From 0321b8816d817b786da5a480e02454aba5ca996d Mon Sep 17 00:00:00 2001 From: Bryce Palmer Date: Tue, 20 Jun 2023 14:16:40 -0400 Subject: [PATCH] externalize & improve source package Signed-off-by: Bryce Palmer --- Makefile | 1 + api/v1alpha1/bundle_types.go | 105 +------- api/v1alpha1/zz_generated.deepcopy.go | 168 +------------ cmd/core/main.go | 53 +++- cmd/helm/main.go | 55 ++++- internal/controllers/bundle/bundle.go | 6 +- internal/rukpakctl/run.go | 5 +- internal/source/unpacker.go | 142 ----------- internal/source/upload.go | 59 ----- internal/uploadmgr/gc.go | 5 +- internal/uploadmgr/handler.go | 5 +- internal/util/util_test.go | 7 +- internal/webhook/bundle.go | 7 +- internal/webhook/configmaps.go | 3 +- .../core.rukpak.io_bundledeployments.yaml | 14 +- .../apis/crds/core.rukpak.io_bundles.yaml | 28 +-- {internal => pkg}/source/common.go | 3 +- {internal => pkg}/source/configmaps.go | 50 ++-- {internal => pkg}/source/git.go | 85 ++++--- {internal => pkg}/source/http.go | 61 +++-- {internal => pkg}/source/image.go | 155 ++++++++---- pkg/source/types.go | 117 +++++++++ pkg/source/unpacker.go | 85 +++++++ pkg/source/upload.go | 87 +++++++ pkg/source/zz_generated.deepcopy.go | 189 ++++++++++++++ test/e2e/api_validation_test.go | 57 ++--- test/e2e/helm_provisioner_test.go | 47 ++-- test/e2e/plain_provisioner_test.go | 233 +++++++++--------- test/e2e/registry_provisioner_test.go | 13 +- test/e2e/rukpakctl_test.go | 9 +- test/e2e/webhook_test.go | 21 +- 31 files changed, 1066 insertions(+), 809 deletions(-) delete mode 100644 internal/source/unpacker.go delete mode 100644 internal/source/upload.go rename {internal => pkg}/source/common.go (51%) rename {internal => pkg}/source/configmaps.go (58%) rename {internal => pkg}/source/git.go (72%) rename {internal => pkg}/source/http.go (50%) rename {internal => pkg}/source/image.go (62%) create mode 100644 pkg/source/types.go create mode 100644 pkg/source/unpacker.go create mode 100644 pkg/source/upload.go create mode 100644 pkg/source/zz_generated.deepcopy.go diff --git a/Makefile b/Makefile index a65c7d52..58edfe11 100644 --- a/Makefile +++ b/Makefile @@ -76,6 +76,7 @@ generate: $(CONTROLLER_GEN) ## Generate code and manifests $(Q)$(CONTROLLER_GEN) crd:crdVersions=v1,generateEmbeddedObjectMeta=true output:crd:dir=./manifests/apis/crds paths=./api/... $(Q)$(CONTROLLER_GEN) webhook paths=./api/... paths=./internal/webhook/... output:stdout > ./manifests/apis/webhooks/resources/webhook.yaml $(Q)$(CONTROLLER_GEN) object:headerFile=./hack/boilerplate.go.txt paths=./api/... + $(Q)$(CONTROLLER_GEN) object:headerFile=./hack/boilerplate.go.txt paths=./pkg/source/... $(Q)$(CONTROLLER_GEN) rbac:roleName=core-admin \ paths=./internal/controllers/bundle/... \ paths=./internal/controllers/bundledeployment/... \ diff --git a/api/v1alpha1/bundle_types.go b/api/v1alpha1/bundle_types.go index cf4c3923..0af99142 100644 --- a/api/v1alpha1/bundle_types.go +++ b/api/v1alpha1/bundle_types.go @@ -17,8 +17,9 @@ limitations under the License. package v1alpha1 import ( - corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/operator-framework/rukpak/pkg/source" ) var ( @@ -26,15 +27,7 @@ var ( BundleKind = BundleGVK.Kind ) -type SourceType string - const ( - SourceTypeImage SourceType = "image" - SourceTypeGit SourceType = "git" - SourceTypeConfigMaps SourceType = "configMaps" - SourceTypeUpload SourceType = "upload" - SourceTypeHTTP SourceType = "http" - TypeUnpacked = "Unpacked" ReasonUnpackPending = "UnpackPending" @@ -55,105 +48,15 @@ type BundleSpec struct { // ProvisionerClassName sets the name of the provisioner that should reconcile this BundleDeployment. ProvisionerClassName string `json:"provisionerClassName"` // Source defines the configuration for the underlying Bundle content. - Source BundleSource `json:"source"` -} - -type BundleSource struct { - // Type defines the kind of Bundle content being sourced. - Type SourceType `json:"type"` - // Image is the bundle image that backs the content of this bundle. - Image *ImageSource `json:"image,omitempty"` - // Git is the git repository that backs the content of this Bundle. - Git *GitSource `json:"git,omitempty"` - // ConfigMaps is a list of config map references and their relative - // directory paths that represent a bundle filesystem. - ConfigMaps []ConfigMapSource `json:"configMaps,omitempty"` - // Upload is a source that enables this Bundle's content to be uploaded - // via Rukpak's bundle upload service. This source type is primarily useful - // with bundle development workflows because it enables bundle developers - // to inject a local bundle directly into the cluster. - Upload *UploadSource `json:"upload,omitempty"` - // HTTP is the remote location that backs the content of this Bundle. - HTTP *HTTPSource `json:"http,omitempty"` -} - -type ImageSource struct { - // Ref contains the reference to a container image containing Bundle contents. - Ref string `json:"ref"` - // ImagePullSecretName contains the name of the image pull secret in the namespace that the provisioner is deployed. - ImagePullSecretName string `json:"pullSecret,omitempty"` -} - -type GitSource struct { - // Repository is a URL link to the git repository containing the bundle. - // Repository is required and the URL should be parsable by a standard git tool. - Repository string `json:"repository"` - // Directory refers to the location of the bundle within the git repository. - // Directory is optional and if not set defaults to ./manifests. - Directory string `json:"directory,omitempty"` - // Ref configures the git source to clone a specific branch, tag, or commit - // from the specified repo. Ref is required, and exactly one field within Ref - // is required. Setting more than one field or zero fields will result in an - // error. - Ref GitRef `json:"ref"` - // Auth configures the authorization method if necessary. - Auth Authorization `json:"auth,omitempty"` -} - -type ConfigMapSource struct { - // ConfigMap is a reference to a configmap in the rukpak system namespace - ConfigMap corev1.LocalObjectReference `json:"configMap"` - // Path is the relative directory path within the bundle where the files - // from the configmap will be present when the bundle is unpacked. - Path string `json:"path,omitempty"` -} - -type HTTPSource struct { - // URL is where the bundle contents is. - URL string `json:"url"` - // Auth configures the authorization method if necessary. - Auth Authorization `json:"auth,omitempty"` -} - -type ConfigMapRef struct { - Name string `json:"name"` - Namespace string `json:"namespace"` + Source source.Source `json:"source"` } -type GitRef struct { - // Branch refers to the branch to checkout from the repository. - // The Branch should contain the bundle manifests in the specified directory. - Branch string `json:"branch,omitempty"` - // Tag refers to the tag to checkout from the repository. - // The Tag should contain the bundle manifests in the specified directory. - Tag string `json:"tag,omitempty"` - // Commit refers to the commit to checkout from the repository. - // The Commit should contain the bundle manifests in the specified directory. - Commit string `json:"commit,omitempty"` -} - -type Authorization struct { - // Secret contains reference to the secret that has authorization information and is in the namespace that the provisioner is deployed. - // The secret is expected to contain `data.username` and `data.password` for the username and password, respectively for http(s) scheme. - // Refer to https://kubernetes.io/docs/concepts/configuration/secret/#basic-authentication-secret - // For the ssh authorization of the GitSource, the secret is expected to contain `data.ssh-privatekey` and `data.ssh-knownhosts` for the ssh privatekey and the host entry in the known_hosts file respectively. - // Refer to https://kubernetes.io/docs/concepts/configuration/secret/#ssh-authentication-secrets - Secret corev1.LocalObjectReference `json:"secret,omitempty"` - // InsecureSkipVerify controls whether a client verifies the server's certificate chain and host name. If InsecureSkipVerify - // is true, the clone operation will accept any certificate presented by the server and any host name in that - // certificate. In this mode, TLS is susceptible to machine-in-the-middle attacks unless custom verification is - // used. This should be used only for testing. - InsecureSkipVerify bool `json:"insecureSkipVerify,omitempty"` -} - -type UploadSource struct{} - type ProvisionerID string // BundleStatus defines the observed state of Bundle type BundleStatus struct { Phase string `json:"phase,omitempty"` - ResolvedSource *BundleSource `json:"resolvedSource,omitempty"` + ResolvedSource *source.Source `json:"resolvedSource,omitempty"` ObservedGeneration int64 `json:"observedGeneration,omitempty"` Conditions []metav1.Condition `json:"conditions,omitempty"` ContentURL string `json:"contentURL,omitempty"` diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index c8c665bb..8c338a78 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -22,26 +22,11 @@ limitations under the License. package v1alpha1 import ( + "github.com/operator-framework/rukpak/pkg/source" "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" ) -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Authorization) DeepCopyInto(out *Authorization) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Authorization. -func (in *Authorization) DeepCopy() *Authorization { - if in == nil { - return nil - } - out := new(Authorization) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Bundle) DeepCopyInto(out *Bundle) { *out = *in @@ -203,46 +188,6 @@ func (in *BundleList) DeepCopyObject() runtime.Object { return nil } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *BundleSource) DeepCopyInto(out *BundleSource) { - *out = *in - if in.Image != nil { - in, out := &in.Image, &out.Image - *out = new(ImageSource) - **out = **in - } - if in.Git != nil { - in, out := &in.Git, &out.Git - *out = new(GitSource) - **out = **in - } - if in.ConfigMaps != nil { - in, out := &in.ConfigMaps, &out.ConfigMaps - *out = make([]ConfigMapSource, len(*in)) - copy(*out, *in) - } - if in.Upload != nil { - in, out := &in.Upload, &out.Upload - *out = new(UploadSource) - **out = **in - } - if in.HTTP != nil { - in, out := &in.HTTP, &out.HTTP - *out = new(HTTPSource) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BundleSource. -func (in *BundleSource) DeepCopy() *BundleSource { - if in == nil { - return nil - } - out := new(BundleSource) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *BundleSpec) DeepCopyInto(out *BundleSpec) { *out = *in @@ -264,7 +209,7 @@ func (in *BundleStatus) DeepCopyInto(out *BundleStatus) { *out = *in if in.ResolvedSource != nil { in, out := &in.ResolvedSource, &out.ResolvedSource - *out = new(BundleSource) + *out = new(source.Source) (*in).DeepCopyInto(*out) } if in.Conditions != nil { @@ -302,112 +247,3 @@ func (in *BundleTemplate) DeepCopy() *BundleTemplate { in.DeepCopyInto(out) return out } - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ConfigMapRef) DeepCopyInto(out *ConfigMapRef) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigMapRef. -func (in *ConfigMapRef) DeepCopy() *ConfigMapRef { - if in == nil { - return nil - } - out := new(ConfigMapRef) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ConfigMapSource) DeepCopyInto(out *ConfigMapSource) { - *out = *in - out.ConfigMap = in.ConfigMap -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigMapSource. -func (in *ConfigMapSource) DeepCopy() *ConfigMapSource { - if in == nil { - return nil - } - out := new(ConfigMapSource) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *GitRef) DeepCopyInto(out *GitRef) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GitRef. -func (in *GitRef) DeepCopy() *GitRef { - if in == nil { - return nil - } - out := new(GitRef) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *GitSource) DeepCopyInto(out *GitSource) { - *out = *in - out.Ref = in.Ref - out.Auth = in.Auth -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GitSource. -func (in *GitSource) DeepCopy() *GitSource { - if in == nil { - return nil - } - out := new(GitSource) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *HTTPSource) DeepCopyInto(out *HTTPSource) { - *out = *in - out.Auth = in.Auth -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPSource. -func (in *HTTPSource) DeepCopy() *HTTPSource { - if in == nil { - return nil - } - out := new(HTTPSource) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ImageSource) DeepCopyInto(out *ImageSource) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ImageSource. -func (in *ImageSource) DeepCopy() *ImageSource { - if in == nil { - return nil - } - out := new(ImageSource) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *UploadSource) DeepCopyInto(out *UploadSource) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UploadSource. -func (in *UploadSource) DeepCopy() *UploadSource { - if in == nil { - return nil - } - out := new(UploadSource) - in.DeepCopyInto(out) - return out -} diff --git a/cmd/core/main.go b/cmd/core/main.go index 50c57e22..42721ca1 100644 --- a/cmd/core/main.go +++ b/cmd/core/main.go @@ -17,6 +17,7 @@ limitations under the License. package main import ( + "crypto/tls" "crypto/x509" "flag" "fmt" @@ -33,9 +34,11 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/selection" utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/client-go/kubernetes" clientgoscheme "k8s.io/client-go/kubernetes/scheme" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/cache" + "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/cluster" crfinalizer "sigs.k8s.io/controller-runtime/pkg/finalizer" "sigs.k8s.io/controller-runtime/pkg/healthz" @@ -47,11 +50,11 @@ import ( "github.com/operator-framework/rukpak/internal/finalizer" "github.com/operator-framework/rukpak/internal/provisioner/plain" "github.com/operator-framework/rukpak/internal/provisioner/registry" - "github.com/operator-framework/rukpak/internal/source" "github.com/operator-framework/rukpak/internal/storage" "github.com/operator-framework/rukpak/internal/uploadmgr" "github.com/operator-framework/rukpak/internal/util" "github.com/operator-framework/rukpak/internal/version" + "github.com/operator-framework/rukpak/pkg/source" ) var ( @@ -59,6 +62,9 @@ var ( setupLog = ctrl.Log.WithName("setup") ) +// uploadClientTimeout is the timeout to be used with http connections to upload manager. +const uploadClientTimeout = time.Second * 10 + func init() { utilruntime.Must(clientgoscheme.AddToScheme(scheme)) utilruntime.Must(apiextensionsv1.AddToScheme(scheme)) @@ -215,12 +221,53 @@ func main() { os.Exit(1) } - unpacker, err := source.NewDefaultUnpacker(systemNsCluster, systemNs, unpackImage, baseUploadManagerURL, rootCAs) + kubeClient, err := kubernetes.NewForConfig(cfg) if err != nil { - setupLog.Error(err, "unable to setup bundle unpacker") + setupLog.Error(err, "unable to setup kube client") os.Exit(1) } + httpTransport := http.DefaultTransport.(*http.Transport).Clone() + if httpTransport.TLSClientConfig == nil { + httpTransport.TLSClientConfig = &tls.Config{ + MinVersion: tls.VersionTLS12, + } + } + httpTransport.TLSClientConfig.RootCAs = rootCAs + + unpacker := source.NewUnpacker(map[source.SourceType]source.Unpacker{ + source.SourceTypeImage: source.NewImageUnpacker( + systemNsCluster.GetClient(), + kubeClient, + source.WithPodNamespace(systemNs), + source.WithFieldManager("rukpak-core"), + source.WithUnpackImage(unpackImage), + source.WithLabelsFunc(func(o client.Object) map[string]string { + return map[string]string{ + util.CoreOwnerKindKey: o.GetObjectKind().GroupVersionKind().Kind, + util.CoreOwnerNameKey: o.GetName(), + } + }), + ), + source.SourceTypeGit: source.NewGitUnpacker( + systemNsCluster.GetClient(), + source.WithGitSecretNamespace(systemNs), + ), + source.SourceTypeConfigMaps: source.NewConfigMapUnpacker( + systemNsCluster.GetClient(), + source.WithConfigMapNamespace(systemNs), + ), + source.SourceTypeUpload: source.NewUploadUnpacker( + http.Client{Timeout: uploadClientTimeout, Transport: httpTransport}, + source.WithBaseDownloadURL(baseUploadManagerURL), + source.WithBearerToken(systemNsCluster.GetConfig().BearerToken), + ), + source.SourceTypeHTTP: source.NewHTTPUnpacker( + systemNsCluster.GetClient(), + source.WithHTTPSecretNamespace(systemNs), + ), + }) + commonBundleProvisionerOptions := []bundle.Option{ bundle.WithUnpacker(unpacker), bundle.WithFinalizers(bundleFinalizers), diff --git a/cmd/helm/main.go b/cmd/helm/main.go index c2d4decf..4d61be60 100644 --- a/cmd/helm/main.go +++ b/cmd/helm/main.go @@ -17,11 +17,14 @@ limitations under the License. package main import ( + "crypto/tls" "crypto/x509" "flag" "fmt" + "net/http" "net/url" "os" + "time" helmclient "github.com/operator-framework/helm-operator-plugins/pkg/client" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" @@ -29,9 +32,11 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/selection" utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/client-go/kubernetes" clientgoscheme "k8s.io/client-go/kubernetes/scheme" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/cache" + "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/cluster" crfinalizer "sigs.k8s.io/controller-runtime/pkg/finalizer" "sigs.k8s.io/controller-runtime/pkg/healthz" @@ -42,10 +47,10 @@ import ( "github.com/operator-framework/rukpak/internal/controllers/bundledeployment" "github.com/operator-framework/rukpak/internal/finalizer" "github.com/operator-framework/rukpak/internal/provisioner/helm" - "github.com/operator-framework/rukpak/internal/source" "github.com/operator-framework/rukpak/internal/storage" "github.com/operator-framework/rukpak/internal/util" "github.com/operator-framework/rukpak/internal/version" + "github.com/operator-framework/rukpak/pkg/source" ) var ( @@ -53,6 +58,9 @@ var ( setupLog = ctrl.Log.WithName("setup") ) +// uploadClientTimeout is the timeout to be used with http connections to upload manager. +const uploadClientTimeout = time.Second * 10 + func init() { utilruntime.Must(clientgoscheme.AddToScheme(scheme)) utilruntime.Must(apiextensionsv1.AddToScheme(scheme)) @@ -197,12 +205,53 @@ func main() { os.Exit(1) } - unpacker, err := source.NewDefaultUnpacker(systemNsCluster, systemNs, unpackImage, baseUploadManagerURL, rootCAs) + kubeClient, err := kubernetes.NewForConfig(cfg) if err != nil { - setupLog.Error(err, "unable to setup bundle unpacker") + setupLog.Error(err, "unable to setup kube client") os.Exit(1) } + httpTransport := http.DefaultTransport.(*http.Transport).Clone() + if httpTransport.TLSClientConfig == nil { + httpTransport.TLSClientConfig = &tls.Config{ + MinVersion: tls.VersionTLS12, + } + } + httpTransport.TLSClientConfig.RootCAs = rootCAs + + unpacker := source.NewUnpacker(map[source.SourceType]source.Unpacker{ + source.SourceTypeImage: source.NewImageUnpacker( + systemNsCluster.GetClient(), + kubeClient, + source.WithPodNamespace(systemNs), + source.WithFieldManager("rukpak-core"), + source.WithUnpackImage(unpackImage), + source.WithLabelsFunc(func(o client.Object) map[string]string { + return map[string]string{ + util.CoreOwnerKindKey: o.GetObjectKind().GroupVersionKind().Kind, + util.CoreOwnerNameKey: o.GetName(), + } + }), + ), + source.SourceTypeGit: source.NewGitUnpacker( + systemNsCluster.GetClient(), + source.WithGitSecretNamespace(systemNs), + ), + source.SourceTypeConfigMaps: source.NewConfigMapUnpacker( + systemNsCluster.GetClient(), + source.WithConfigMapNamespace(systemNs), + ), + source.SourceTypeUpload: source.NewUploadUnpacker( + http.Client{Timeout: uploadClientTimeout, Transport: httpTransport}, + source.WithBaseDownloadURL(baseUploadManagerURL), + source.WithBearerToken(systemNsCluster.GetConfig().BearerToken), + ), + source.SourceTypeHTTP: source.NewHTTPUnpacker( + systemNsCluster.GetClient(), + source.WithHTTPSecretNamespace(systemNs), + ), + }) + commonBundleProvisionerOptions := []bundle.Option{ bundle.WithUnpacker(unpacker), bundle.WithFinalizers(bundleFinalizers), diff --git a/internal/controllers/bundle/bundle.go b/internal/controllers/bundle/bundle.go index acbaa218..8d1b2ecc 100644 --- a/internal/controllers/bundle/bundle.go +++ b/internal/controllers/bundle/bundle.go @@ -21,9 +21,9 @@ import ( crsource "sigs.k8s.io/controller-runtime/pkg/source" rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1" - "github.com/operator-framework/rukpak/internal/source" "github.com/operator-framework/rukpak/internal/storage" "github.com/operator-framework/rukpak/internal/util" + "github.com/operator-framework/rukpak/pkg/source" ) type Option func(*controller) @@ -205,7 +205,7 @@ func (c *controller) reconcile(ctx context.Context, bundle *rukpakv1alpha1.Bundl return ctrl.Result{}, nil } - unpackResult, err := c.unpacker.Unpack(ctx, bundle) + unpackResult, err := c.unpacker.Unpack(ctx, &bundle.Spec.Source, bundle) if err != nil { return ctrl.Result{}, updateStatusUnpackFailing(&bundle.Status, fmt.Errorf("source bundle content: %v", err)) } @@ -217,7 +217,7 @@ func (c *controller) reconcile(ctx context.Context, bundle *rukpakv1alpha1.Bundl updateStatusUnpacking(&bundle.Status, unpackResult) return ctrl.Result{}, nil case source.StateUnpacked: - storeFS, err := c.handler.Handle(ctx, unpackResult.Bundle, bundle) + storeFS, err := c.handler.Handle(ctx, unpackResult.FS, bundle) if err != nil { return ctrl.Result{}, updateStatusUnpackFailing(&bundle.Status, err) } diff --git a/internal/rukpakctl/run.go b/internal/rukpakctl/run.go index 2655107c..83c3bf55 100644 --- a/internal/rukpakctl/run.go +++ b/internal/rukpakctl/run.go @@ -18,6 +18,7 @@ import ( rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1" "github.com/operator-framework/rukpak/internal/provisioner/plain" "github.com/operator-framework/rukpak/internal/util" + "github.com/operator-framework/rukpak/pkg/source" ) // Run implements rukpakctl's `run` subcommand @@ -132,8 +133,8 @@ func buildBundleDeployment(bdName string, bundleLabels map[string]string, biPCN, "spec": map[string]interface{}{ "provisionerClassName": bPNC, "source": map[string]interface{}{ - "type": rukpakv1alpha1.SourceTypeUpload, - "upload": &rukpakv1alpha1.UploadSource{}, + "type": source.SourceTypeUpload, + "upload": &source.UploadSource{}, }, }, }, diff --git a/internal/source/unpacker.go b/internal/source/unpacker.go deleted file mode 100644 index f68af841..00000000 --- a/internal/source/unpacker.go +++ /dev/null @@ -1,142 +0,0 @@ -package source - -import ( - "context" - "crypto/tls" - "crypto/x509" - "fmt" - "io/fs" - "net/http" - "time" - - "k8s.io/client-go/kubernetes" - "sigs.k8s.io/controller-runtime/pkg/cluster" - - rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1" -) - -const ( - // uploadClientTimeout is the timeout to be used with http connections to upload manager. - uploadClientTimeout = time.Second * 10 -) - -// Unpacker unpacks bundle content, either synchronously or asynchronously and -// returns a Result, which conveys information about the progress of unpacking -// the bundle content. -// -// If a Source unpacks content asynchronously, it should register one or more -// watches with a controller to ensure that Bundles referencing this source -// can be reconciled as progress updates are available. -// -// For asynchronous Sources, multiple calls to Unpack should be made until the -// returned result includes state StateUnpacked. -// -// NOTE: A source is meant to be agnostic to specific bundle formats and -// specifications. A source should treat a bundle root directory as an opaque -// file tree and delegate bundle format concerns to bundle parsers. -type Unpacker interface { - Unpack(context.Context, *rukpakv1alpha1.Bundle) (*Result, error) -} - -// Result conveys progress information about unpacking bundle content. -type Result struct { - // Bundle contains the full filesystem of a bundle's root directory. - Bundle fs.FS - - // ResolvedSource is a reproducible view of a Bundle's Source. - // When possible, source implementations should return a ResolvedSource - // that pins the Source such that future fetches of the bundle content can - // be guaranteed to fetch the exact same bundle content as the original - // unpack. - // - // For example, resolved image sources should reference a container image - // digest rather than an image tag, and git sources should reference a - // commit hash rather than a branch or tag. - ResolvedSource *rukpakv1alpha1.BundleSource - - // State is the current state of unpacking the bundle content. - State State - - // Message is contextual information about the progress of unpacking the - // bundle content. - Message string -} - -type State string - -const ( - // StatePending conveys that a request for unpacking a bundle has been - // acknowledged, but not yet started. - StatePending State = "Pending" - - // StateUnpacking conveys that the source is currently unpacking a bundle. - // This state should be used when the bundle contents are being downloaded - // and processed. - StateUnpacking State = "Unpacking" - - // StateUnpacked conveys that the bundle has been successfully unpacked. - StateUnpacked State = "Unpacked" -) - -type unpacker struct { - sources map[rukpakv1alpha1.SourceType]Unpacker -} - -// NewUnpacker returns a new composite Source that unpacks bundles using the source -// mapping provided by the configured sources. -func NewUnpacker(sources map[rukpakv1alpha1.SourceType]Unpacker) Unpacker { - return &unpacker{sources: sources} -} - -func (s *unpacker) Unpack(ctx context.Context, bundle *rukpakv1alpha1.Bundle) (*Result, error) { - source, ok := s.sources[bundle.Spec.Source.Type] - if !ok { - return nil, fmt.Errorf("source type %q not supported", bundle.Spec.Source.Type) - } - return source.Unpack(ctx, bundle) -} - -// NewDefaultUnpacker returns a new composite Source that unpacks bundles using -// a default source mapping with built-in implementations of all of the supported -// source types. -// -// TODO: refactor NewDefaultUnpacker due to growing parameter list -func NewDefaultUnpacker(systemNsCluster cluster.Cluster, namespace, unpackImage string, baseUploadManagerURL string, rootCAs *x509.CertPool) (Unpacker, error) { - cfg := systemNsCluster.GetConfig() - kubeClient, err := kubernetes.NewForConfig(cfg) - if err != nil { - return nil, err - } - httpTransport := http.DefaultTransport.(*http.Transport).Clone() - if httpTransport.TLSClientConfig == nil { - httpTransport.TLSClientConfig = &tls.Config{ - MinVersion: tls.VersionTLS12, - } - } - httpTransport.TLSClientConfig.RootCAs = rootCAs - return NewUnpacker(map[rukpakv1alpha1.SourceType]Unpacker{ - rukpakv1alpha1.SourceTypeImage: &Image{ - Client: systemNsCluster.GetClient(), - KubeClient: kubeClient, - PodNamespace: namespace, - UnpackImage: unpackImage, - }, - rukpakv1alpha1.SourceTypeGit: &Git{ - Reader: systemNsCluster.GetClient(), - SecretNamespace: namespace, - }, - rukpakv1alpha1.SourceTypeConfigMaps: &ConfigMaps{ - Reader: systemNsCluster.GetClient(), - ConfigMapNamespace: namespace, - }, - rukpakv1alpha1.SourceTypeUpload: &Upload{ - baseDownloadURL: baseUploadManagerURL, - bearerToken: systemNsCluster.GetConfig().BearerToken, - client: http.Client{Timeout: uploadClientTimeout, Transport: httpTransport}, - }, - rukpakv1alpha1.SourceTypeHTTP: &HTTP{ - Reader: systemNsCluster.GetClient(), - SecretNamespace: namespace, - }, - }), nil -} diff --git a/internal/source/upload.go b/internal/source/upload.go deleted file mode 100644 index 40f4d688..00000000 --- a/internal/source/upload.go +++ /dev/null @@ -1,59 +0,0 @@ -package source - -import ( - "compress/gzip" - "context" - "fmt" - "net/http" - - "github.com/nlepage/go-tarfs" - - rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1" -) - -// Upload is a bundle source that sources bundles from the rukpak upload service. -type Upload struct { - baseDownloadURL string - bearerToken string - client http.Client -} - -// Unpack unpacks an uploaded bundle by requesting the bundle contents from a web server hosted -// by rukpak's upload service. -func (b *Upload) Unpack(ctx context.Context, bundle *rukpakv1alpha1.Bundle) (*Result, error) { - if bundle.Spec.Source.Type != rukpakv1alpha1.SourceTypeUpload { - return nil, fmt.Errorf("cannot unpack source type %q with %q unpacker", bundle.Spec.Source.Type, rukpakv1alpha1.SourceTypeUpload) - } - - url := fmt.Sprintf("%s/uploads/%s.tgz", b.baseDownloadURL, bundle.Name) - action := fmt.Sprintf("%s %s", http.MethodGet, url) - - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - return nil, fmt.Errorf("create http request %q for bundle content: %v", action, err) - } - req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", b.bearerToken)) - resp, err := b.client.Do(req) - if err != nil { - return nil, fmt.Errorf("%s: http request for bundle content failed: %v", action, err) - } - defer resp.Body.Close() - if resp.StatusCode == http.StatusNotFound { - return &Result{State: StatePending, Message: "waiting for bundle to be uploaded"}, nil - } - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("%s: unexpected status %q", action, resp.Status) - } - gzipReader, err := gzip.NewReader(resp.Body) - if err != nil { - return nil, fmt.Errorf("read response as gzip: %v", err) - } - bundleFS, err := tarfs.New(gzipReader) - if err != nil { - return nil, fmt.Errorf("untar bundle contents from response: %v", err) - } - - message := generateMessage("upload") - - return &Result{Bundle: bundleFS, ResolvedSource: bundle.Spec.Source.DeepCopy(), State: StateUnpacked, Message: message}, nil -} diff --git a/internal/uploadmgr/gc.go b/internal/uploadmgr/gc.go index b53c3e0a..f4818261 100644 --- a/internal/uploadmgr/gc.go +++ b/internal/uploadmgr/gc.go @@ -15,6 +15,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/manager" rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1" + "github.com/operator-framework/rukpak/pkg/source" ) type bundleGC struct { @@ -43,7 +44,7 @@ func (gc *bundleGC) Start(ctx context.Context) error { _, err = bundleInformer.AddEventHandler(toolscache.ResourceEventHandlerFuncs{ DeleteFunc: func(obj interface{}) { bundle := obj.(*rukpakv1alpha1.Bundle) - if bundle.Spec.Source.Type != rukpakv1alpha1.SourceTypeUpload { + if bundle.Spec.Source.Type != source.SourceTypeUpload { return } filename := bundlePath(gc.storageDir, bundle.Name) @@ -89,7 +90,7 @@ func (gc *bundleGC) Start(ctx context.Context) error { continue } for _, bundle := range bundles.Items { - if bundle.Spec.Source.Type != rukpakv1alpha1.SourceTypeUpload { + if bundle.Spec.Source.Type != source.SourceTypeUpload { continue } existingFiles.Delete(filepath.Base(bundlePath(gc.storageDir, bundle.Name))) diff --git a/internal/uploadmgr/handler.go b/internal/uploadmgr/handler.go index f689d47c..c3403768 100644 --- a/internal/uploadmgr/handler.go +++ b/internal/uploadmgr/handler.go @@ -19,6 +19,7 @@ import ( rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1" "github.com/operator-framework/rukpak/internal/util" + "github.com/operator-framework/rukpak/pkg/source" ) const DefaultBundleCacheDir = "/var/cache/uploads" @@ -44,8 +45,8 @@ func newPutHandler(cl client.Client, storageDir string) http.Handler { http.Error(w, err.Error(), int(getCode(err))) return } - if bundle.Spec.Source.Type != rukpakv1alpha1.SourceTypeUpload { - http.Error(w, fmt.Sprintf("bundle source type is %q; expected %q", bundle.Spec.Source.Type, rukpakv1alpha1.SourceTypeUpload), http.StatusConflict) + if bundle.Spec.Source.Type != source.SourceTypeUpload { + http.Error(w, fmt.Sprintf("bundle source type is %q; expected %q", bundle.Spec.Source.Type, source.SourceTypeUpload), http.StatusConflict) return } diff --git a/internal/util/util_test.go b/internal/util/util_test.go index 4f2ecf69..b9275b8a 100644 --- a/internal/util/util_test.go +++ b/internal/util/util_test.go @@ -6,13 +6,14 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1" + "github.com/operator-framework/rukpak/pkg/source" ) var sampleSpec = rukpakv1alpha1.BundleSpec{ ProvisionerClassName: "sample", - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeImage, - Image: &rukpakv1alpha1.ImageSource{ + Source: source.Source{ + Type: source.SourceTypeImage, + Image: &source.ImageSource{ Ref: "non-existent", }, }, diff --git a/internal/webhook/bundle.go b/internal/webhook/bundle.go index 5c393925..df0e69e1 100644 --- a/internal/webhook/bundle.go +++ b/internal/webhook/bundle.go @@ -33,6 +33,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/webhook/admission" rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1" + "github.com/operator-framework/rukpak/pkg/source" ) type Bundle struct { @@ -67,18 +68,18 @@ func (b *Bundle) ValidateDelete(_ context.Context, _ runtime.Object) error { func (b *Bundle) checkBundleSource(ctx context.Context, bundle *rukpakv1alpha1.Bundle) error { switch typ := bundle.Spec.Source.Type; typ { - case rukpakv1alpha1.SourceTypeImage: + case source.SourceTypeImage: if bundle.Spec.Source.Image == nil { return fmt.Errorf("bundle.spec.source.image must be set for source type \"image\"") } - case rukpakv1alpha1.SourceTypeGit: + case source.SourceTypeGit: if bundle.Spec.Source.Git == nil { return fmt.Errorf("bundle.spec.source.git must be set for source type \"git\"") } if strings.HasPrefix(filepath.Clean(bundle.Spec.Source.Git.Directory), "../") { return fmt.Errorf(`bundle.spec.source.git.directory begins with "../": directory must define path within the repository`) } - case rukpakv1alpha1.SourceTypeConfigMaps: + case source.SourceTypeConfigMaps: if len(bundle.Spec.Source.ConfigMaps) == 0 { return fmt.Errorf(`bundle.spec.source.configmaps must be set for source type "configmaps"`) } diff --git a/internal/webhook/configmaps.go b/internal/webhook/configmaps.go index 24689a22..e9cd9fd8 100644 --- a/internal/webhook/configmaps.go +++ b/internal/webhook/configmaps.go @@ -12,6 +12,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/webhook/admission" rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1" + "github.com/operator-framework/rukpak/pkg/source" ) //+kubebuilder:rbac:groups=core.rukpak.io,resources=bundles,verbs=list;watch @@ -37,7 +38,7 @@ func (w *ConfigMap) ValidateCreate(ctx context.Context, obj runtime.Object) erro } bundleReferrers := []string{} for _, bundle := range bundleList.Items { - if bundle.Spec.Source.Type == rukpakv1alpha1.SourceTypeConfigMaps { + if bundle.Spec.Source.Type == source.SourceTypeConfigMaps { for _, bundleConfigMapRef := range bundle.Spec.Source.ConfigMaps { if bundleConfigMapRef.ConfigMap.Name == cm.Name { bundleReferrers = append(bundleReferrers, bundle.Name) diff --git a/manifests/apis/crds/core.rukpak.io_bundledeployments.yaml b/manifests/apis/crds/core.rukpak.io_bundledeployments.yaml index 94bf7310..55c4facd 100644 --- a/manifests/apis/crds/core.rukpak.io_bundledeployments.yaml +++ b/manifests/apis/crds/core.rukpak.io_bundledeployments.yaml @@ -100,7 +100,7 @@ spec: configMaps: description: ConfigMaps is a list of config map references and their relative directory paths that represent a - bundle filesystem. + source filesystem. items: properties: configMap: @@ -126,7 +126,7 @@ spec: type: array git: description: Git is the git repository that backs the - content of this Bundle. + content of this Source. properties: auth: description: Auth configures the authorization method @@ -205,7 +205,7 @@ spec: type: object http: description: HTTP is the remote location that backs the - content of this Bundle. + content of this Source. properties: auth: description: Auth configures the authorization method @@ -252,8 +252,8 @@ spec: - url type: object image: - description: Image is the bundle image that backs the - content of this bundle. + description: Image is the image that backs the content + of this Source. properties: pullSecret: description: ImagePullSecretName contains the name @@ -268,11 +268,11 @@ spec: - ref type: object type: - description: Type defines the kind of Bundle content being + description: Type defines the kind of Source content being sourced. type: string upload: - description: Upload is a source that enables this Bundle's + description: Upload is a source that enables this Source's content to be uploaded via Rukpak's bundle upload service. This source type is primarily useful with bundle development workflows because it enables bundle developers to inject diff --git a/manifests/apis/crds/core.rukpak.io_bundles.yaml b/manifests/apis/crds/core.rukpak.io_bundles.yaml index 2f4d2131..467e0849 100644 --- a/manifests/apis/crds/core.rukpak.io_bundles.yaml +++ b/manifests/apis/crds/core.rukpak.io_bundles.yaml @@ -63,7 +63,7 @@ spec: properties: configMaps: description: ConfigMaps is a list of config map references and - their relative directory paths that represent a bundle filesystem. + their relative directory paths that represent a source filesystem. items: properties: configMap: @@ -87,7 +87,7 @@ spec: type: array git: description: Git is the git repository that backs the content - of this Bundle. + of this Source. properties: auth: description: Auth configures the authorization method if necessary. @@ -160,7 +160,7 @@ spec: type: object http: description: HTTP is the remote location that backs the content - of this Bundle. + of this Source. properties: auth: description: Auth configures the authorization method if necessary. @@ -202,8 +202,8 @@ spec: - url type: object image: - description: Image is the bundle image that backs the content - of this bundle. + description: Image is the image that backs the content of this + Source. properties: pullSecret: description: ImagePullSecretName contains the name of the @@ -218,10 +218,10 @@ spec: - ref type: object type: - description: Type defines the kind of Bundle content being sourced. + description: Type defines the kind of Source content being sourced. type: string upload: - description: Upload is a source that enables this Bundle's content + description: Upload is a source that enables this Source's content to be uploaded via Rukpak's bundle upload service. This source type is primarily useful with bundle development workflows because it enables bundle developers to inject a local bundle directly @@ -316,7 +316,7 @@ spec: properties: configMaps: description: ConfigMaps is a list of config map references and - their relative directory paths that represent a bundle filesystem. + their relative directory paths that represent a source filesystem. items: properties: configMap: @@ -340,7 +340,7 @@ spec: type: array git: description: Git is the git repository that backs the content - of this Bundle. + of this Source. properties: auth: description: Auth configures the authorization method if necessary. @@ -413,7 +413,7 @@ spec: type: object http: description: HTTP is the remote location that backs the content - of this Bundle. + of this Source. properties: auth: description: Auth configures the authorization method if necessary. @@ -455,8 +455,8 @@ spec: - url type: object image: - description: Image is the bundle image that backs the content - of this bundle. + description: Image is the image that backs the content of this + Source. properties: pullSecret: description: ImagePullSecretName contains the name of the @@ -471,10 +471,10 @@ spec: - ref type: object type: - description: Type defines the kind of Bundle content being sourced. + description: Type defines the kind of Source content being sourced. type: string upload: - description: Upload is a source that enables this Bundle's content + description: Upload is a source that enables this Source's content to be uploaded via Rukpak's bundle upload service. This source type is primarily useful with bundle development workflows because it enables bundle developers to inject a local bundle directly diff --git a/internal/source/common.go b/pkg/source/common.go similarity index 51% rename from internal/source/common.go rename to pkg/source/common.go index 100bceff..445565a8 100644 --- a/internal/source/common.go +++ b/pkg/source/common.go @@ -4,6 +4,7 @@ import ( "fmt" ) +// TODO: Remove this? func generateMessage(bundleName string) string { - return fmt.Sprintf("Successfully unpacked the %s Bundle", bundleName) + return fmt.Sprintf("Successfully unpacked the %s source", bundleName) } diff --git a/internal/source/configmaps.go b/pkg/source/configmaps.go similarity index 58% rename from internal/source/configmaps.go rename to pkg/source/configmaps.go index d6716d0c..12c1803c 100644 --- a/internal/source/configmaps.go +++ b/pkg/source/configmaps.go @@ -10,24 +10,46 @@ import ( utilerrors "k8s.io/apimachinery/pkg/util/errors" "k8s.io/apimachinery/pkg/util/sets" "sigs.k8s.io/controller-runtime/pkg/client" - - rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1" ) -type ConfigMaps struct { +type ConfigMapUnpackerOption func(c *configMaps) + +// WithConfigMapNamespace configures the namespace used by the +// ConfigMap Unpacker to look for ConfigMaps +func WithConfigMapNamespace(ns string) ConfigMapUnpackerOption { + return func(c *configMaps) { + c.ConfigMapNamespace = ns + } +} + +// NewConfigMapUnpacker returns a new Unpacker for unpacking sources of type "configmap" +func NewConfigMapUnpacker(reader client.Reader, opts ...ConfigMapUnpackerOption) Unpacker { + cm := &configMaps{ + Reader: reader, + ConfigMapNamespace: "default", + } + + for _, opt := range opts { + opt(cm) + } + + return cm +} + +type configMaps struct { Reader client.Reader ConfigMapNamespace string } -func (o *ConfigMaps) Unpack(ctx context.Context, bundle *rukpakv1alpha1.Bundle) (*Result, error) { - if bundle.Spec.Source.Type != rukpakv1alpha1.SourceTypeConfigMaps { - return nil, fmt.Errorf("bundle source type %q not supported", bundle.Spec.Source.Type) +func (o *configMaps) Unpack(ctx context.Context, src *Source, _ client.Object) (*Result, error) { + if src.Type != SourceTypeConfigMaps { + return nil, fmt.Errorf("source type %q not supported", src.Type) } - if bundle.Spec.Source.ConfigMaps == nil { - return nil, fmt.Errorf("bundle source configmaps configuration is unset") + if src.ConfigMaps == nil { + return nil, fmt.Errorf("source configmaps configuration is unset") } - configMapSources := bundle.Spec.Source.ConfigMaps + configMapSources := src.ConfigMaps bundleFS := fstest.MapFS{} seenFilepaths := map[string]sets.Set[string]{} @@ -37,7 +59,7 @@ func (o *ConfigMaps) Unpack(ctx context.Context, bundle *rukpakv1alpha1.Bundle) dir := filepath.Clean(cmSource.Path) // Validating admission webhook handles validation for: - // - paths outside the bundle root + // - paths outside the src root // - configmaps referenced by bundles must be immutable var cm corev1.ConfigMap @@ -74,11 +96,11 @@ func (o *ConfigMaps) Unpack(ctx context.Context, bundle *rukpakv1alpha1.Bundle) return nil, utilerrors.NewAggregate(errs) } - resolvedSource := &rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeConfigMaps, - ConfigMaps: bundle.Spec.Source.DeepCopy().ConfigMaps, + resolvedSource := &Source{ + Type: SourceTypeConfigMaps, + ConfigMaps: src.DeepCopy().ConfigMaps, } message := generateMessage("configMaps") - return &Result{Bundle: bundleFS, ResolvedSource: resolvedSource, State: StateUnpacked, Message: message}, nil + return &Result{FS: bundleFS, ResolvedSource: resolvedSource, State: StateUnpacked, Message: message}, nil } diff --git a/internal/source/git.go b/pkg/source/git.go similarity index 72% rename from internal/source/git.go rename to pkg/source/git.go index 1aa3ab5d..23bd0f73 100644 --- a/internal/source/git.go +++ b/pkg/source/git.go @@ -23,23 +23,46 @@ import ( "golang.org/x/crypto/ssh" corev1 "k8s.io/api/core/v1" "sigs.k8s.io/controller-runtime/pkg/client" - - rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1" ) -type Git struct { - client.Reader +type GitUnpackerOption func(g *gitUnpacker) + +// WithGitSecretNamespace configures the namespace that the +// Git Unpacker uses to find the Secret used for authorization +// if authorization is specified in the GitSource +func WithGitSecretNamespace(ns string) GitUnpackerOption { + return func(g *gitUnpacker) { + g.SecretNamespace = ns + } +} + +// NewGitUnpacker returns a new Unpacker for unpacking sources of type "git" +func NewGitUnpacker(reader client.Reader, opts ...GitUnpackerOption) Unpacker { + g := &gitUnpacker{ + Reader: reader, + SecretNamespace: "default", + } + + for _, opt := range opts { + opt(g) + } + + return g +} + +type gitUnpacker struct { + Reader client.Reader SecretNamespace string } -func (r *Git) Unpack(ctx context.Context, bundle *rukpakv1alpha1.Bundle) (*Result, error) { - if bundle.Spec.Source.Type != rukpakv1alpha1.SourceTypeGit { - return nil, fmt.Errorf("bundle source type %q not supported", bundle.Spec.Source.Type) +func (r *gitUnpacker) Unpack(ctx context.Context, src *Source, _ client.Object) (*Result, error) { + if src.Type != SourceTypeGit { + return nil, fmt.Errorf("source type %q not supported", src.Type) } - if bundle.Spec.Source.Git == nil { - return nil, fmt.Errorf("bundle source git configuration is unset") + if src.Git == nil { + return nil, fmt.Errorf("source git configuration is unset") } - gitsource := bundle.Spec.Source.Git + gitsource := src.Git if gitsource.Repository == "" { // This should never happen because the validation webhook rejects git bundles without repository return nil, errors.New("missing git source information: repository must be provided") @@ -51,11 +74,11 @@ func (r *Git) Unpack(ctx context.Context, bundle *rukpakv1alpha1.Bundle) (*Resul URL: gitsource.Repository, Progress: &progress, Tags: git.NoTags, - InsecureSkipTLS: bundle.Spec.Source.Git.Auth.InsecureSkipVerify, + InsecureSkipTLS: src.Git.Auth.InsecureSkipVerify, } - if bundle.Spec.Source.Git.Auth.Secret.Name != "" { - auth, err := r.configAuth(ctx, bundle) + if src.Git.Auth.Secret.Name != "" { + auth, err := r.configAuth(ctx, src) if err != nil { return nil, fmt.Errorf("configuring Auth error: %w", err) } @@ -75,11 +98,11 @@ func (r *Git) Unpack(ctx context.Context, bundle *rukpakv1alpha1.Bundle) (*Resul // Clone repo, err := git.CloneContext(ctx, memory.NewStorage(), memfs.New(), &cloneOpts) if err != nil { - return nil, fmt.Errorf("bundle unpack git clone error: %v - %s", err, progress.String()) + return nil, fmt.Errorf("src unpack git clone error: %v - %s", err, progress.String()) } wt, err := repo.Worktree() if err != nil { - return nil, fmt.Errorf("bundle unpack error: %v", err) + return nil, fmt.Errorf("src unpack error: %v", err) } // Checkout commit @@ -113,31 +136,31 @@ func (r *Git) Unpack(ctx context.Context, bundle *rukpakv1alpha1.Bundle) (*Resul return nil, fmt.Errorf("resolve commit hash: %v", err) } - resolvedGit := bundle.Spec.Source.Git.DeepCopy() - resolvedGit.Ref = rukpakv1alpha1.GitRef{ + resolvedGit := src.Git.DeepCopy() + resolvedGit.Ref = GitRef{ Commit: commitHash.String(), } - resolvedSource := &rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeGit, + resolvedSource := &Source{ + Type: SourceTypeGit, Git: resolvedGit, } message := generateMessage("git") - return &Result{Bundle: bundleFS, ResolvedSource: resolvedSource, State: StateUnpacked, Message: message}, nil + return &Result{FS: bundleFS, ResolvedSource: resolvedSource, State: StateUnpacked, Message: message}, nil } -func (r *Git) configAuth(ctx context.Context, bundle *rukpakv1alpha1.Bundle) (transport.AuthMethod, error) { +func (r *gitUnpacker) configAuth(ctx context.Context, src *Source) (transport.AuthMethod, error) { var auth transport.AuthMethod - if strings.HasPrefix(bundle.Spec.Source.Git.Repository, "http") { - userName, password, err := r.getCredentials(ctx, bundle) + if strings.HasPrefix(src.Git.Repository, "http") { + userName, password, err := r.getCredentials(ctx, src) if err != nil { return nil, err } return &http.BasicAuth{Username: userName, Password: password}, nil } - privatekey, host, err := r.getCertificate(ctx, bundle) + privatekey, host, err := r.getCertificate(ctx, src) if err != nil { return nil, err } @@ -150,7 +173,7 @@ func (r *Git) configAuth(ctx context.Context, bundle *rukpakv1alpha1.Bundle) (tr User: "git", Signer: signer, } - if bundle.Spec.Source.Git.Auth.InsecureSkipVerify { + if src.Git.Auth.InsecureSkipVerify { auth = &sshgit.PublicKeys{ User: "git", Signer: signer, @@ -174,11 +197,11 @@ func (r *Git) configAuth(ctx context.Context, bundle *rukpakv1alpha1.Bundle) (tr return auth, nil } -// getCredentials reads credentials from the secret specified in the bundle +// getCredentials reads credentials from the secret specified in the src // It returns the username ane password when they are in the secret -func (r *Git) getCredentials(ctx context.Context, bundle *rukpakv1alpha1.Bundle) (string, string, error) { +func (r *gitUnpacker) getCredentials(ctx context.Context, src *Source) (string, string, error) { secret := &corev1.Secret{} - err := r.Get(ctx, client.ObjectKey{Namespace: r.SecretNamespace, Name: bundle.Spec.Source.Git.Auth.Secret.Name}, secret) + err := r.Reader.Get(ctx, client.ObjectKey{Namespace: r.SecretNamespace, Name: src.Git.Auth.Secret.Name}, secret) if err != nil { return "", "", err } @@ -188,11 +211,11 @@ func (r *Git) getCredentials(ctx context.Context, bundle *rukpakv1alpha1.Bundle) return userName, password, nil } -// getCertificate reads certificate from the secret specified in the bundle +// getCertificate reads certificate from the secret specified in the src // It returns the privatekey and the entry of the host in known_hosts when they are in the secret -func (r *Git) getCertificate(ctx context.Context, bundle *rukpakv1alpha1.Bundle) ([]byte, []byte, error) { +func (r *gitUnpacker) getCertificate(ctx context.Context, src *Source) ([]byte, []byte, error) { secret := &corev1.Secret{} - err := r.Get(ctx, client.ObjectKey{Namespace: r.SecretNamespace, Name: bundle.Spec.Source.Git.Auth.Secret.Name}, secret) + err := r.Reader.Get(ctx, client.ObjectKey{Namespace: r.SecretNamespace, Name: src.Git.Auth.Secret.Name}, secret) if err != nil { return nil, nil, err } diff --git a/internal/source/http.go b/pkg/source/http.go similarity index 50% rename from internal/source/http.go rename to pkg/source/http.go index 6ed3403d..8af6e483 100644 --- a/internal/source/http.go +++ b/pkg/source/http.go @@ -11,32 +11,55 @@ import ( "github.com/nlepage/go-tarfs" corev1 "k8s.io/api/core/v1" "sigs.k8s.io/controller-runtime/pkg/client" - - rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1" ) -// http is a bundle source that sources bundles from the specified url. -type HTTP struct { - client.Reader +type HTTPUnpackerOption func(h *hTTP) + +// WithHTTPSecretNamespace configures the namespace that the +// HTTP Unpacker uses to find the Secret used for authorization +// if authorization is specified in the HTTPSource +func WithHTTPSecretNamespace(ns string) HTTPUnpackerOption { + return func(h *hTTP) { + h.SecretNamespace = ns + } +} + +// NewHTTPUnpacker returns a new Unpacker for unpacking sources of type "http" +func NewHTTPUnpacker(reader client.Reader, opts ...HTTPUnpackerOption) Unpacker { + h := &hTTP{ + Reader: reader, + SecretNamespace: "default", + } + + for _, opt := range opts { + opt(h) + } + + return h +} + +// http is a src source that sources bundles from the specified url. +type hTTP struct { + Reader client.Reader SecretNamespace string } -// Unpack unpacks a bundle by requesting the bundle contents from a specified URL -func (b *HTTP) Unpack(ctx context.Context, bundle *rukpakv1alpha1.Bundle) (*Result, error) { - if bundle.Spec.Source.Type != rukpakv1alpha1.SourceTypeHTTP { - return nil, fmt.Errorf("cannot unpack source type %q with %q unpacker", bundle.Spec.Source.Type, rukpakv1alpha1.SourceTypeHTTP) +// Unpack unpacks a src by requesting the src contents from a specified URL +func (b *hTTP) Unpack(ctx context.Context, src *Source, _ client.Object) (*Result, error) { + if src.Type != SourceTypeHTTP { + return nil, fmt.Errorf("cannot unpack source type %q with %q unpacker", src.Type, SourceTypeHTTP) } - url := bundle.Spec.Source.HTTP.URL + url := src.HTTP.URL action := fmt.Sprintf("%s %s", http.MethodGet, url) req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) if err != nil { - return nil, fmt.Errorf("create http request %q for bundle content: %v", action, err) + return nil, fmt.Errorf("create http request %q for source content: %v", action, err) } var userName, password string - if bundle.Spec.Source.HTTP.Auth.Secret.Name != "" { - userName, password, err = b.getCredentials(ctx, bundle) + if src.HTTP.Auth.Secret.Name != "" { + userName, password, err = b.getCredentials(ctx, src) if err != nil { return nil, err } @@ -44,7 +67,7 @@ func (b *HTTP) Unpack(ctx context.Context, bundle *rukpakv1alpha1.Bundle) (*Resu } httpClient := http.Client{Timeout: 10 * time.Second} - if bundle.Spec.Source.HTTP.Auth.InsecureSkipVerify { + if src.HTTP.Auth.InsecureSkipVerify { tr := http.DefaultTransport.(*http.Transport).Clone() tr.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} // nolint:gosec httpClient.Transport = tr @@ -52,7 +75,7 @@ func (b *HTTP) Unpack(ctx context.Context, bundle *rukpakv1alpha1.Bundle) (*Resu resp, err := httpClient.Do(req) if err != nil { - return nil, fmt.Errorf("%s: http request for bundle content failed: %v", action, err) + return nil, fmt.Errorf("%s: http request for source content failed: %v", action, err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { @@ -70,14 +93,14 @@ func (b *HTTP) Unpack(ctx context.Context, bundle *rukpakv1alpha1.Bundle) (*Resu message := generateMessage("http") - return &Result{Bundle: fs, ResolvedSource: bundle.Spec.Source.DeepCopy(), State: StateUnpacked, Message: message}, nil + return &Result{FS: fs, ResolvedSource: src.DeepCopy(), State: StateUnpacked, Message: message}, nil } -// getCredentials reads credentials from the secret specified in the bundle +// getCredentials reads credentials from the secret specified in the src // It returns the username ane password when they are in the secret -func (b *HTTP) getCredentials(ctx context.Context, bundle *rukpakv1alpha1.Bundle) (string, string, error) { +func (b *hTTP) getCredentials(ctx context.Context, src *Source) (string, string, error) { secret := &corev1.Secret{} - err := b.Get(ctx, client.ObjectKey{Namespace: b.SecretNamespace, Name: bundle.Spec.Source.HTTP.Auth.Secret.Name}, secret) + err := b.Reader.Get(ctx, client.ObjectKey{Namespace: b.SecretNamespace, Name: src.HTTP.Auth.Secret.Name}, secret) if err != nil { return "", "", err } diff --git a/internal/source/image.go b/pkg/source/image.go similarity index 62% rename from internal/source/image.go rename to pkg/source/image.go index e0b34a36..7bf17864 100644 --- a/internal/source/image.go +++ b/pkg/source/image.go @@ -20,30 +20,96 @@ import ( "k8s.io/client-go/kubernetes" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - - rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1" - "github.com/operator-framework/rukpak/internal/util" ) -type Image struct { +//TODO: Add comments to improve the godocs + +type ImageUnpackerOption func(i *image) + +// WithPodNamespace configures the namespace used +// by the Image Unpacker when creating a Pod +func WithPodNamespace(ns string) ImageUnpackerOption { + return func(i *image) { + i.PodNamespace = ns + } +} + +// WithUnpackImage configures the image used by +// the Image Unpacker Pod to unpack an image source +func WithUnpackImage(img string) ImageUnpackerOption { + return func(i *image) { + i.UnpackImage = img + } +} + +// WithFieldManager configures the field manager used +// by the Image Unpacker to set the Pod field manager +func WithFieldManager(manager string) ImageUnpackerOption { + return func(i *image) { + i.FieldManager = manager + } +} + +// WithDir configures the directory passed to the +// Unpack Pod by the Image Unpacker. This dictates +// which directory of information should be unpacked +func WithDir(dir string) ImageUnpackerOption { + return func(i *image) { + i.BundleDir = dir + } +} + +type LabelFunc func(client.Object) map[string]string + +// WithLabelsFunc configures the function used by the Image +// Unpacker to add labels to the Unpacker Pod. +func WithLabelsFunc(labelFunc LabelFunc) ImageUnpackerOption { + return func(i *image) { + i.LabelsFunc = labelFunc + } +} + +// NewImageUnpacker returns a new Unpacker for unpacking sources of type "image" +func NewImageUnpacker(cli client.Client, kubeCli kubernetes.Interface, opts ...ImageUnpackerOption) Unpacker { + image := &image{ + Client: cli, + KubeClient: kubeCli, + PodNamespace: "default", + UnpackImage: "quay.io/operator-framework/rukpak:main", + FieldManager: "unpacker", + BundleDir: "/", + LabelsFunc: func(o client.Object) map[string]string { return map[string]string{} }, + } + + for _, opt := range opts { + opt(image) + } + + return image +} + +type image struct { Client client.Client KubeClient kubernetes.Interface PodNamespace string UnpackImage string + FieldManager string + BundleDir string + LabelsFunc LabelFunc } -const imageBundleUnpackContainerName = "bundle" +const imageBundleUnpackContainerName = "src" -func (i *Image) Unpack(ctx context.Context, bundle *rukpakv1alpha1.Bundle) (*Result, error) { - if bundle.Spec.Source.Type != rukpakv1alpha1.SourceTypeImage { - return nil, fmt.Errorf("bundle source type %q not supported", bundle.Spec.Source.Type) +func (i *image) Unpack(ctx context.Context, src *Source, obj client.Object) (*Result, error) { + if src.Type != SourceTypeImage { + return nil, fmt.Errorf("source type %q not supported", src.Type) } - if bundle.Spec.Source.Image == nil { - return nil, fmt.Errorf("bundle source image configuration is unset") + if src.Image == nil { + return nil, fmt.Errorf("source image configuration is unset") } pod := &corev1.Pod{} - op, err := i.ensureUnpackPod(ctx, bundle, pod) + op, err := i.ensureUnpackPod(ctx, src, pod, obj) if err != nil { return nil, err } else if op == controllerutil.OperationResultCreated || op == controllerutil.OperationResultUpdated || pod.DeletionTimestamp != nil { @@ -64,14 +130,14 @@ func (i *Image) Unpack(ctx context.Context, bundle *rukpakv1alpha1.Bundle) (*Res } } -func (i *Image) ensureUnpackPod(ctx context.Context, bundle *rukpakv1alpha1.Bundle, pod *corev1.Pod) (controllerutil.OperationResult, error) { - existingPod := &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: i.PodNamespace, Name: bundle.Name}} +func (i *image) ensureUnpackPod(ctx context.Context, src *Source, pod *corev1.Pod, obj client.Object) (controllerutil.OperationResult, error) { + existingPod := &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: i.PodNamespace, Name: obj.GetName()}} if err := i.Client.Get(ctx, client.ObjectKeyFromObject(existingPod), existingPod); client.IgnoreNotFound(err) != nil { return controllerutil.OperationResultNone, err } - podApplyConfig := i.getDesiredPodApplyConfig(bundle) - updatedPod, err := i.KubeClient.CoreV1().Pods(i.PodNamespace).Apply(ctx, podApplyConfig, metav1.ApplyOptions{Force: true, FieldManager: "rukpak-core"}) + podApplyConfig := i.getDesiredPodApplyConfig(src, obj) + updatedPod, err := i.KubeClient.CoreV1().Pods(i.PodNamespace).Apply(ctx, podApplyConfig, metav1.ApplyOptions{Force: true, FieldManager: i.FieldManager}) if err != nil { if !apierrors.IsInvalid(err) { return controllerutil.OperationResultNone, err @@ -79,7 +145,7 @@ func (i *Image) ensureUnpackPod(ctx context.Context, bundle *rukpakv1alpha1.Bund if err := i.Client.Delete(ctx, existingPod); err != nil { return controllerutil.OperationResultNone, err } - updatedPod, err = i.KubeClient.CoreV1().Pods(i.PodNamespace).Apply(ctx, podApplyConfig, metav1.ApplyOptions{Force: true, FieldManager: "rukpak-core"}) + updatedPod, err = i.KubeClient.CoreV1().Pods(i.PodNamespace).Apply(ctx, podApplyConfig, metav1.ApplyOptions{Force: true, FieldManager: i.FieldManager}) if err != nil { return controllerutil.OperationResultNone, err } @@ -99,7 +165,7 @@ func (i *Image) ensureUnpackPod(ctx context.Context, bundle *rukpakv1alpha1.Bund return controllerutil.OperationResultUpdated, nil } -func (i *Image) getDesiredPodApplyConfig(bundle *rukpakv1alpha1.Bundle) *applyconfigurationcorev1.PodApplyConfiguration { +func (i *image) getDesiredPodApplyConfig(src *Source, obj client.Object) *applyconfigurationcorev1.PodApplyConfiguration { // TODO (tyslaton): Address unpacker pod allowing root users for image sources // // In our current implementation, we are creating a pod that uses the image @@ -118,16 +184,13 @@ func (i *Image) getDesiredPodApplyConfig(bundle *rukpakv1alpha1.Bundle) *applyco WithDrop("ALL"), ) - podApply := applyconfigurationcorev1.Pod(bundle.Name, i.PodNamespace). - WithLabels(map[string]string{ - util.CoreOwnerKindKey: bundle.Kind, - util.CoreOwnerNameKey: bundle.Name, - }). + podApply := applyconfigurationcorev1.Pod(obj.GetName(), i.PodNamespace). + WithLabels(i.LabelsFunc(obj)). WithOwnerReferences(v1.OwnerReference(). - WithName(bundle.Name). - WithKind(bundle.Kind). - WithAPIVersion(bundle.APIVersion). - WithUID(bundle.UID). + WithName(obj.GetName()). + WithKind(obj.GetObjectKind().GroupVersionKind().Kind). + WithAPIVersion(obj.GetObjectKind().GroupVersionKind().GroupVersion().String()). + WithUID(obj.GetUID()). WithController(true). WithBlockOwnerDeletion(true), ). @@ -147,8 +210,8 @@ func (i *Image) getDesiredPodApplyConfig(bundle *rukpakv1alpha1.Bundle) *applyco ). WithContainers(applyconfigurationcorev1.Container(). WithName(imageBundleUnpackContainerName). - WithImage(bundle.Spec.Source.Image.Ref). - WithCommand("/bin/unpack", "--bundle-dir", "/"). + WithImage(src.Image.Ref). + WithCommand("/bin/unpack", "--bundle-dir", i.BundleDir). WithVolumeMounts(applyconfigurationcorev1.VolumeMount(). WithName("util"). WithMountPath("/bin"), @@ -167,9 +230,9 @@ func (i *Image) getDesiredPodApplyConfig(bundle *rukpakv1alpha1.Bundle) *applyco ), ) - if bundle.Spec.Source.Image.ImagePullSecretName != "" { + if src.Image.ImagePullSecretName != "" { podApply.Spec = podApply.Spec.WithImagePullSecrets( - applyconfigurationcorev1.LocalObjectReference().WithName(bundle.Spec.Source.Image.ImagePullSecretName), + applyconfigurationcorev1.LocalObjectReference().WithName(src.Image.ImagePullSecretName), ) } return podApply @@ -183,7 +246,7 @@ func unsetNonComparedPodFields(pods ...*corev1.Pod) { } } -func (i *Image) failedPodResult(ctx context.Context, pod *corev1.Pod) error { +func (i *image) failedPodResult(ctx context.Context, pod *corev1.Pod) error { logs, err := i.getPodLogs(ctx, pod) if err != nil { return fmt.Errorf("unpack failed: failed to retrieve failed pod logs: %v", err) @@ -192,57 +255,57 @@ func (i *Image) failedPodResult(ctx context.Context, pod *corev1.Pod) error { return fmt.Errorf("unpack failed: %v", string(logs)) } -func (i *Image) succeededPodResult(ctx context.Context, pod *corev1.Pod) (*Result, error) { +func (i *image) succeededPodResult(ctx context.Context, pod *corev1.Pod) (*Result, error) { bundleFS, err := i.getBundleContents(ctx, pod) if err != nil { - return nil, fmt.Errorf("get bundle contents: %v", err) + return nil, fmt.Errorf("get src contents: %v", err) } digest, err := i.getBundleImageDigest(pod) if err != nil { - return nil, fmt.Errorf("get bundle image digest: %v", err) + return nil, fmt.Errorf("get src image digest: %v", err) } - resolvedSource := &rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeImage, - Image: &rukpakv1alpha1.ImageSource{Ref: digest}, + resolvedSource := &Source{ + Type: SourceTypeImage, + Image: &ImageSource{Ref: digest}, } message := generateMessage("image") - return &Result{Bundle: bundleFS, ResolvedSource: resolvedSource, State: StateUnpacked, Message: message}, nil + return &Result{FS: bundleFS, ResolvedSource: resolvedSource, State: StateUnpacked, Message: message}, nil } -func (i *Image) getBundleContents(ctx context.Context, pod *corev1.Pod) (fs.FS, error) { +func (i *image) getBundleContents(ctx context.Context, pod *corev1.Pod) (fs.FS, error) { bundleData, err := i.getPodLogs(ctx, pod) if err != nil { - return nil, fmt.Errorf("get bundle contents: %v", err) + return nil, fmt.Errorf("get src contents: %v", err) } bd := struct { Content []byte `json:"content"` }{} if err := json.Unmarshal(bundleData, &bd); err != nil { - return nil, fmt.Errorf("parse bundle data: %v", err) + return nil, fmt.Errorf("parse src data: %v", err) } gzr, err := gzip.NewReader(bytes.NewReader(bd.Content)) if err != nil { - return nil, fmt.Errorf("read bundle content gzip: %v", err) + return nil, fmt.Errorf("read src content gzip: %v", err) } return tarfs.New(gzr) } -func (i *Image) getBundleImageDigest(pod *corev1.Pod) (string, error) { +func (i *image) getBundleImageDigest(pod *corev1.Pod) (string, error) { for _, ps := range pod.Status.ContainerStatuses { if ps.Name == imageBundleUnpackContainerName && ps.ImageID != "" { return ps.ImageID, nil } } - return "", fmt.Errorf("bundle image digest not found") + return "", fmt.Errorf("src image digest not found") } -func (i *Image) getPodLogs(ctx context.Context, pod *corev1.Pod) ([]byte, error) { +func (i *image) getPodLogs(ctx context.Context, pod *corev1.Pod) ([]byte, error) { logReader, err := i.KubeClient.CoreV1().Pods(pod.Namespace).GetLogs(pod.Name, &corev1.PodLogOptions{}).Stream(ctx) if err != nil { return nil, fmt.Errorf("get pod logs: %v", err) @@ -255,7 +318,7 @@ func (i *Image) getPodLogs(ctx context.Context, pod *corev1.Pod) ([]byte, error) return buf.Bytes(), nil } -func (i *Image) handleUnexpectedPod(ctx context.Context, pod *corev1.Pod) error { +func (i *image) handleUnexpectedPod(ctx context.Context, pod *corev1.Pod) error { _ = i.Client.Delete(ctx, pod) return fmt.Errorf("unexpected pod phase: %v", pod.Status.Phase) } diff --git a/pkg/source/types.go b/pkg/source/types.go new file mode 100644 index 00000000..baea8ed4 --- /dev/null +++ b/pkg/source/types.go @@ -0,0 +1,117 @@ +package source + +import ( + corev1 "k8s.io/api/core/v1" +) + +// nolint:revive +type SourceType string + +const ( + SourceTypeImage SourceType = "image" + SourceTypeGit SourceType = "git" + SourceTypeConfigMaps SourceType = "configMaps" + SourceTypeUpload SourceType = "upload" + SourceTypeHTTP SourceType = "http" +) + +// TODO: Can we make source a bit more generic and composable/extensible? + +// +kubebuilder:object:generate=true +type Source struct { + // Type defines the kind of Source content being sourced. + Type SourceType `json:"type"` + // Image is the image that backs the content of this Source. + Image *ImageSource `json:"image,omitempty"` + // Git is the git repository that backs the content of this Source. + Git *GitSource `json:"git,omitempty"` + // ConfigMaps is a list of config map references and their relative + // directory paths that represent a source filesystem. + ConfigMaps []ConfigMapSource `json:"configMaps,omitempty"` + // Upload is a source that enables this Source's content to be uploaded + // via Rukpak's bundle upload service. This source type is primarily useful + // with bundle development workflows because it enables bundle developers + // to inject a local bundle directly into the cluster. + Upload *UploadSource `json:"upload,omitempty"` + // HTTP is the remote location that backs the content of this Source. + HTTP *HTTPSource `json:"http,omitempty"` +} + +// +kubebuilder:object:generate=true +type ImageSource struct { + // Ref contains the reference to a container image containing Bundle contents. + Ref string `json:"ref"` + // ImagePullSecretName contains the name of the image pull secret in the namespace that the provisioner is deployed. + ImagePullSecretName string `json:"pullSecret,omitempty"` +} + +// +kubebuilder:object:generate=true +type GitSource struct { + // Repository is a URL link to the git repository containing the bundle. + // Repository is required and the URL should be parsable by a standard git tool. + Repository string `json:"repository"` + // Directory refers to the location of the bundle within the git repository. + // Directory is optional and if not set defaults to ./manifests. + Directory string `json:"directory,omitempty"` + // Ref configures the git source to clone a specific branch, tag, or commit + // from the specified repo. Ref is required, and exactly one field within Ref + // is required. Setting more than one field or zero fields will result in an + // error. + Ref GitRef `json:"ref"` + // Auth configures the authorization method if necessary. + Auth Authorization `json:"auth,omitempty"` +} + +// +kubebuilder:object:generate=true +type ConfigMapSource struct { + // ConfigMap is a reference to a configmap in the rukpak system namespace + ConfigMap corev1.LocalObjectReference `json:"configMap"` + // Path is the relative directory path within the bundle where the files + // from the configmap will be present when the bundle is unpacked. + Path string `json:"path,omitempty"` +} + +// +kubebuilder:object:generate=true +type HTTPSource struct { + // URL is where the bundle contents is. + URL string `json:"url"` + // Auth configures the authorization method if necessary. + Auth Authorization `json:"auth,omitempty"` +} + +// +kubebuilder:object:generate=true +type ConfigMapRef struct { + Name string `json:"name"` + Namespace string `json:"namespace"` +} + +// +kubebuilder:object:generate=true +type GitRef struct { + // Branch refers to the branch to checkout from the repository. + // The Branch should contain the bundle manifests in the specified directory. + Branch string `json:"branch,omitempty"` + // Tag refers to the tag to checkout from the repository. + // The Tag should contain the bundle manifests in the specified directory. + Tag string `json:"tag,omitempty"` + // Commit refers to the commit to checkout from the repository. + // The Commit should contain the bundle manifests in the specified directory. + Commit string `json:"commit,omitempty"` +} + +// +kubebuilder:object:generate=true +type Authorization struct { + // Secret contains reference to the secret that has authorization information and is in the namespace that the provisioner is deployed. + // The secret is expected to contain `data.username` and `data.password` for the username and password, respectively for http(s) scheme. + // Refer to https://kubernetes.io/docs/concepts/configuration/secret/#basic-authentication-secret + // For the ssh authorization of the GitSource, the secret is expected to contain `data.ssh-privatekey` and `data.ssh-knownhosts` for the ssh privatekey and the host entry in the known_hosts file respectively. + // Refer to https://kubernetes.io/docs/concepts/configuration/secret/#ssh-authentication-secrets + Secret corev1.LocalObjectReference `json:"secret,omitempty"` + // InsecureSkipVerify controls whether a client verifies the server's certificate chain and host name. If InsecureSkipVerify + // is true, the clone operation will accept any certificate presented by the server and any host name in that + // certificate. In this mode, TLS is susceptible to machine-in-the-middle attacks unless custom verification is + // used. This should be used only for testing. + InsecureSkipVerify bool `json:"insecureSkipVerify,omitempty"` +} + +// +kubebuilder:object:generate=true +type UploadSource struct{} diff --git a/pkg/source/unpacker.go b/pkg/source/unpacker.go new file mode 100644 index 00000000..a5efbf0e --- /dev/null +++ b/pkg/source/unpacker.go @@ -0,0 +1,85 @@ +package source + +import ( + "context" + "fmt" + "io/fs" + + "sigs.k8s.io/controller-runtime/pkg/client" +) + +// Unpacker unpacks source content, either synchronously or asynchronously and +// returns a Result, which conveys information about the progress of unpacking +// the source content. +// +// If a Source unpacks content asynchronously, it should register one or more +// watches with a controller to ensure that sources referencing this source +// can be reconciled as progress updates are available. +// +// For asynchronous Sources, multiple calls to Unpack should be made until the +// returned result includes state StateUnpacked. +// +// NOTE: A source is meant to be agnostic to specific source formats and +// specifications. A source should treat a source root directory as an opaque +// file tree and delegate source format concerns to source parsers. +type Unpacker interface { + Unpack(context.Context, *Source, client.Object) (*Result, error) +} + +// Result conveys progress information about unpacking source content. +type Result struct { + // FS contains the full filesystem of a source's root directory. + FS fs.FS + + // ResolvedSource is a reproducible view of a Source. + // When possible, source implementations should return a ResolvedSource + // that pins the Source such that future fetches of the source content can + // be guaranteed to fetch the exact same source content as the original + // unpack. + // + // For example, resolved image sources should reference a container image + // digest rather than an image tag, and git sources should reference a + // commit hash rather than a branch or tag. + ResolvedSource *Source + + // State is the current state of unpacking the source content. + State State + + // Message is contextual information about the progress of unpacking the + // source content. + Message string +} + +type State string + +const ( + // StatePending conveys that a request for unpacking a source has been + // acknowledged, but not yet started. + StatePending State = "Pending" + + // StateUnpacking conveys that the source is currently unpacking a source. + // This state should be used when the source contents are being downloaded + // and processed. + StateUnpacking State = "Unpacking" + + // StateUnpacked conveys that the source has been successfully unpacked. + StateUnpacked State = "Unpacked" +) + +type unpacker struct { + sources map[SourceType]Unpacker +} + +// NewUnpacker returns a new composite Source that unpacks sources using the source +// mapping provided by the configured sources. +func NewUnpacker(sources map[SourceType]Unpacker) Unpacker { + return &unpacker{sources: sources} +} + +func (s *unpacker) Unpack(ctx context.Context, src *Source, obj client.Object) (*Result, error) { + source, ok := s.sources[src.Type] + if !ok { + return nil, fmt.Errorf("source type %q not supported", src.Type) + } + return source.Unpack(ctx, src, obj) +} diff --git a/pkg/source/upload.go b/pkg/source/upload.go new file mode 100644 index 00000000..d2bbd342 --- /dev/null +++ b/pkg/source/upload.go @@ -0,0 +1,87 @@ +package source + +import ( + "compress/gzip" + "context" + "fmt" + "net/http" + + "github.com/nlepage/go-tarfs" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +//TODO: Add comments to improve the godocs + +type UploadUnpackerOption func(u *upload) + +func WithBaseDownloadURL(url string) UploadUnpackerOption { + return func(u *upload) { + u.baseDownloadURL = url + } +} + +func WithBearerToken(token string) UploadUnpackerOption { + return func(u *upload) { + u.bearerToken = token + } +} + +// NewUploadUnpacker returns a new Unpacker for unpacking sources of type "upload" +func NewUploadUnpacker(cli http.Client, opts ...UploadUnpackerOption) Unpacker { + u := &upload{ + client: cli, + } + + for _, opt := range opts { + opt(u) + } + + return u +} + +// upload is a source that sources from the rukpak upload service. +type upload struct { + baseDownloadURL string + bearerToken string + client http.Client +} + +// Unpack unpacks an uploaded source by requesting the source contents from a web server hosted +// by rukpak's upload service. +func (b *upload) Unpack(ctx context.Context, src *Source, obj client.Object) (*Result, error) { + if src.Type != SourceTypeUpload { + return nil, fmt.Errorf("cannot unpack source type %q with %q unpacker", src.Type, SourceTypeUpload) + } + + url := fmt.Sprintf("%s/uploads/%s.tgz", b.baseDownloadURL, obj.GetName()) + action := fmt.Sprintf("%s %s", http.MethodGet, url) + + req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) + if err != nil { + return nil, fmt.Errorf("create http request %q for src content: %v", action, err) + } + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", b.bearerToken)) + resp, err := b.client.Do(req) + if err != nil { + return nil, fmt.Errorf("%s: http request for src content failed: %v", action, err) + } + defer resp.Body.Close() + if resp.StatusCode == http.StatusNotFound { + return &Result{State: StatePending, Message: "waiting for src to be uploaded"}, nil + } + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("%s: unexpected status %q", action, resp.Status) + } + gzipReader, err := gzip.NewReader(resp.Body) + if err != nil { + return nil, fmt.Errorf("read response as gzip: %v", err) + } + bundleFS, err := tarfs.New(gzipReader) + if err != nil { + return nil, fmt.Errorf("untar src contents from response: %v", err) + } + + message := generateMessage("upload") + + return &Result{FS: bundleFS, ResolvedSource: src.DeepCopy(), State: StateUnpacked, Message: message}, nil +} diff --git a/pkg/source/zz_generated.deepcopy.go b/pkg/source/zz_generated.deepcopy.go new file mode 100644 index 00000000..d94db5f2 --- /dev/null +++ b/pkg/source/zz_generated.deepcopy.go @@ -0,0 +1,189 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package source + +import () + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Authorization) DeepCopyInto(out *Authorization) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Authorization. +func (in *Authorization) DeepCopy() *Authorization { + if in == nil { + return nil + } + out := new(Authorization) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConfigMapRef) DeepCopyInto(out *ConfigMapRef) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigMapRef. +func (in *ConfigMapRef) DeepCopy() *ConfigMapRef { + if in == nil { + return nil + } + out := new(ConfigMapRef) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConfigMapSource) DeepCopyInto(out *ConfigMapSource) { + *out = *in + out.ConfigMap = in.ConfigMap +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigMapSource. +func (in *ConfigMapSource) DeepCopy() *ConfigMapSource { + if in == nil { + return nil + } + out := new(ConfigMapSource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GitRef) DeepCopyInto(out *GitRef) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GitRef. +func (in *GitRef) DeepCopy() *GitRef { + if in == nil { + return nil + } + out := new(GitRef) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GitSource) DeepCopyInto(out *GitSource) { + *out = *in + out.Ref = in.Ref + out.Auth = in.Auth +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GitSource. +func (in *GitSource) DeepCopy() *GitSource { + if in == nil { + return nil + } + out := new(GitSource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPSource) DeepCopyInto(out *HTTPSource) { + *out = *in + out.Auth = in.Auth +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPSource. +func (in *HTTPSource) DeepCopy() *HTTPSource { + if in == nil { + return nil + } + out := new(HTTPSource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ImageSource) DeepCopyInto(out *ImageSource) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ImageSource. +func (in *ImageSource) DeepCopy() *ImageSource { + if in == nil { + return nil + } + out := new(ImageSource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Source) DeepCopyInto(out *Source) { + *out = *in + if in.Image != nil { + in, out := &in.Image, &out.Image + *out = new(ImageSource) + **out = **in + } + if in.Git != nil { + in, out := &in.Git, &out.Git + *out = new(GitSource) + **out = **in + } + if in.ConfigMaps != nil { + in, out := &in.ConfigMaps, &out.ConfigMaps + *out = make([]ConfigMapSource, len(*in)) + copy(*out, *in) + } + if in.Upload != nil { + in, out := &in.Upload, &out.Upload + *out = new(UploadSource) + **out = **in + } + if in.HTTP != nil { + in, out := &in.HTTP, &out.HTTP + *out = new(HTTPSource) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Source. +func (in *Source) DeepCopy() *Source { + if in == nil { + return nil + } + out := new(Source) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *UploadSource) DeepCopyInto(out *UploadSource) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UploadSource. +func (in *UploadSource) DeepCopy() *UploadSource { + if in == nil { + return nil + } + out := new(UploadSource) + in.DeepCopyInto(out) + return out +} diff --git a/test/e2e/api_validation_test.go b/test/e2e/api_validation_test.go index 7a866b36..033a5363 100644 --- a/test/e2e/api_validation_test.go +++ b/test/e2e/api_validation_test.go @@ -13,6 +13,7 @@ import ( rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1" "github.com/operator-framework/rukpak/internal/provisioner/plain" + "github.com/operator-framework/rukpak/pkg/source" ) var _ = Describe("bundle api validation", func() { @@ -31,9 +32,9 @@ var _ = Describe("bundle api validation", func() { }, Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: plain.ProvisionerID, - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeImage, - Image: &rukpakv1alpha1.ImageSource{ + Source: source.Source{ + Type: source.SourceTypeImage, + Image: &source.ImageSource{ Ref: "localhost/testdata/bundles/plain-v0:valid", }, }, @@ -66,14 +67,14 @@ var _ = Describe("bundle api validation", func() { }, Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: plain.ProvisionerID, - Source: rukpakv1alpha1.BundleSource{ + Source: source.Source{ Type: "invalid source", - Image: &rukpakv1alpha1.ImageSource{ + Image: &source.ImageSource{ Ref: "localhost/testdata/bundles/plain-v0:valid", }, - Git: &rukpakv1alpha1.GitSource{ + Git: &source.GitSource{ Repository: "https://github.com/exdx/combo-bundle", - Ref: rukpakv1alpha1.GitRef{ + Ref: source.GitRef{ Commit: "9e3ab7f1a36302ef512294d5c9f2e9b9566b811e", Tag: "v0.0.1", }, @@ -109,7 +110,7 @@ var _ = Describe("bundle api validation", func() { }, Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: plain.ProvisionerID, - Source: rukpakv1alpha1.BundleSource{ + Source: source.Source{ Type: "invalid source", }, }, @@ -142,11 +143,11 @@ var _ = Describe("bundle api validation", func() { }, Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: plain.ProvisionerID, - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeGit, - Git: &rukpakv1alpha1.GitSource{ + Source: source.Source{ + Type: source.SourceTypeGit, + Git: &source.GitSource{ Repository: "https://github.com/exdx/combo-bundle", - Ref: rukpakv1alpha1.GitRef{ + Ref: source.GitRef{ Commit: "9e3ab7f1a36302ef512294d5c9f2e9b9566b811e", Tag: "v0.0.1", }, @@ -182,11 +183,11 @@ var _ = Describe("bundle api validation", func() { }, Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: plain.ProvisionerID, - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeGit, - Git: &rukpakv1alpha1.GitSource{ + Source: source.Source{ + Type: source.SourceTypeGit, + Git: &source.GitSource{ Repository: "https://github.com/exdx/combo-bundle", - Ref: rukpakv1alpha1.GitRef{}, + Ref: source.GitRef{}, }, }, }, @@ -223,9 +224,9 @@ var _ = Describe("bundle api validation", func() { }, Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: "invalid/class-name", - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeImage, - Image: &rukpakv1alpha1.ImageSource{ + Source: source.Source{ + Type: source.SourceTypeImage, + Image: &source.ImageSource{ Ref: "localhost/testdata/bundles/plain-v0:valid", }, }, @@ -260,9 +261,9 @@ var _ = Describe("bundle deployment api validation", func() { Template: &rukpakv1alpha1.BundleTemplate{ Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: plain.ProvisionerID, - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeImage, - Image: &rukpakv1alpha1.ImageSource{ + Source: source.Source{ + Type: source.SourceTypeImage, + Image: &source.ImageSource{ Ref: "localhost/testdata/bundles/plain-v0:valid", }, }, @@ -305,9 +306,9 @@ var _ = Describe("bundle deployment api validation", func() { Template: &rukpakv1alpha1.BundleTemplate{ Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: plain.ProvisionerID, - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeImage, - Image: &rukpakv1alpha1.ImageSource{ + Source: source.Source{ + Type: source.SourceTypeImage, + Image: &source.ImageSource{ Ref: "localhost/testdata/bundles/plain-v0:valid", }, }, @@ -347,9 +348,9 @@ var _ = Describe("bundle deployment api validation", func() { Template: &rukpakv1alpha1.BundleTemplate{ Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: "invalid/class-name", - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeImage, - Image: &rukpakv1alpha1.ImageSource{ + Source: source.Source{ + Type: source.SourceTypeImage, + Image: &source.ImageSource{ Ref: "localhost/testdata/bundles/plain-v0:valid", }, }, diff --git a/test/e2e/helm_provisioner_test.go b/test/e2e/helm_provisioner_test.go index b99ccd19..7963ddaf 100644 --- a/test/e2e/helm_provisioner_test.go +++ b/test/e2e/helm_provisioner_test.go @@ -16,6 +16,7 @@ import ( rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1" "github.com/operator-framework/rukpak/internal/provisioner/helm" + "github.com/operator-framework/rukpak/pkg/source" ) var _ = Describe("helm provisioner bundledeployment", func() { @@ -41,9 +42,9 @@ var _ = Describe("helm provisioner bundledeployment", func() { }, Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: helm.ProvisionerID, - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeHTTP, - HTTP: &rukpakv1alpha1.HTTPSource{ + Source: source.Source{ + Type: source.SourceTypeHTTP, + HTTP: &source.HTTPSource{ URL: "https://github.com/helm/examples/releases/download/hello-world-0.1.0/hello-world-0.1.0.tgz", }, }, @@ -155,9 +156,9 @@ var _ = Describe("helm provisioner bundledeployment", func() { }, Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: helm.ProvisionerID, - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeHTTP, - HTTP: &rukpakv1alpha1.HTTPSource{ + Source: source.Source{ + Type: source.SourceTypeHTTP, + HTTP: &source.HTTPSource{ URL: "https://github.com/helm/examples/releases/download/hello-world-0.1.0/xxx", }, }, @@ -211,9 +212,9 @@ var _ = Describe("helm provisioner bundledeployment", func() { }, Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: helm.ProvisionerID, - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeHTTP, - HTTP: &rukpakv1alpha1.HTTPSource{ + Source: source.Source{ + Type: source.SourceTypeHTTP, + HTTP: &source.HTTPSource{ URL: "https://raw.githubusercontent.com/helm/examples/main/LICENSE", }, }, @@ -267,9 +268,9 @@ var _ = Describe("helm provisioner bundledeployment", func() { }, Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: helm.ProvisionerID, - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeHTTP, - HTTP: &rukpakv1alpha1.HTTPSource{ + Source: source.Source{ + Type: source.SourceTypeHTTP, + HTTP: &source.HTTPSource{ URL: "https://github.com/helm/examples/archive/refs/tags/hello-world-0.1.0.tar.gz", }, }, @@ -323,12 +324,12 @@ var _ = Describe("helm provisioner bundledeployment", func() { }, Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: helm.ProvisionerID, - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeGit, - Git: &rukpakv1alpha1.GitSource{ + Source: source.Source{ + Type: source.SourceTypeGit, + Git: &source.GitSource{ Repository: "https://github.com/helm/examples", Directory: "./charts", - Ref: rukpakv1alpha1.GitRef{ + Ref: source.GitRef{ Branch: "main", }, }, @@ -447,12 +448,12 @@ var _ = Describe("helm provisioner bundledeployment", func() { }, Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: helm.ProvisionerID, - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeGit, - Git: &rukpakv1alpha1.GitSource{ + Source: source.Source{ + Type: source.SourceTypeGit, + Git: &source.GitSource{ Repository: "https://github.com/helm/examples", Directory: "./charts/hello-world", - Ref: rukpakv1alpha1.GitRef{ + Ref: source.GitRef{ Branch: "main", }, }, @@ -511,9 +512,9 @@ var _ = Describe("helm provisioner bundledeployment", func() { }, Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: helm.ProvisionerID, - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeHTTP, - HTTP: &rukpakv1alpha1.HTTPSource{ + Source: source.Source{ + Type: source.SourceTypeHTTP, + HTTP: &source.HTTPSource{ URL: "https://github.com/helm/examples/releases/download/hello-world-0.1.0/hello-world-0.1.0.tgz", }, }, diff --git a/test/e2e/plain_provisioner_test.go b/test/e2e/plain_provisioner_test.go index fe83b2e2..73042588 100644 --- a/test/e2e/plain_provisioner_test.go +++ b/test/e2e/plain_provisioner_test.go @@ -34,6 +34,7 @@ import ( "github.com/operator-framework/rukpak/internal/rukpakctl" "github.com/operator-framework/rukpak/internal/storage" "github.com/operator-framework/rukpak/internal/util" + "github.com/operator-framework/rukpak/pkg/source" ) const ( @@ -65,9 +66,9 @@ var _ = Describe("plain provisioner bundle", func() { }, Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: "non-existent-class-name", - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeImage, - Image: &rukpakv1alpha1.ImageSource{ + Source: source.Source{ + Type: source.SourceTypeImage, + Image: &source.ImageSource{ Ref: fmt.Sprintf("%v/%v", ImageRepo, "plain-v0:valid"), }, }, @@ -105,9 +106,9 @@ var _ = Describe("plain provisioner bundle", func() { }, Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: plain.ProvisionerID, - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeImage, - Image: &rukpakv1alpha1.ImageSource{ + Source: source.Source{ + Type: source.SourceTypeImage, + Image: &source.ImageSource{ Ref: fmt.Sprintf("%v/%v", ImageRepo, "plain-v0:valid"), }, }, @@ -133,17 +134,17 @@ var _ = Describe("plain provisioner bundle", func() { }) By("eventually writing a non-empty image digest to the status", func() { - Eventually(func() (*rukpakv1alpha1.BundleSource, error) { + Eventually(func() (*source.Source, error) { if err := c.Get(ctx, client.ObjectKeyFromObject(bundle), bundle); err != nil { return nil, err } return bundle.Status.ResolvedSource, nil }).Should(And( Not(BeNil()), - WithTransform(func(s *rukpakv1alpha1.BundleSource) rukpakv1alpha1.SourceType { return s.Type }, Equal(rukpakv1alpha1.SourceTypeImage)), - WithTransform(func(s *rukpakv1alpha1.BundleSource) *rukpakv1alpha1.ImageSource { return s.Image }, And( + WithTransform(func(s *source.Source) source.SourceType { return s.Type }, Equal(source.SourceTypeImage)), + WithTransform(func(s *source.Source) *source.ImageSource { return s.Image }, And( Not(BeNil()), - WithTransform(func(i *rukpakv1alpha1.ImageSource) string { return i.Ref }, Not(Equal(""))), + WithTransform(func(i *source.ImageSource) string { return i.Ref }, Not(Equal(""))), )), )) }) @@ -223,9 +224,9 @@ var _ = Describe("plain provisioner bundle", func() { }, Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: plain.ProvisionerID, - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeImage, - Image: &rukpakv1alpha1.ImageSource{ + Source: source.Source{ + Type: source.SourceTypeImage, + Image: &source.ImageSource{ Ref: "docker-registry.rukpak-e2e.svc.cluster.local:5000/bundles/plain-v0:valid", ImagePullSecretName: "registrysecret", }, @@ -252,17 +253,17 @@ var _ = Describe("plain provisioner bundle", func() { }) By("eventually writing a non-empty image digest to the status", func() { - Eventually(func() (*rukpakv1alpha1.BundleSource, error) { + Eventually(func() (*source.Source, error) { if err := c.Get(ctx, client.ObjectKeyFromObject(bundle), bundle); err != nil { return nil, err } return bundle.Status.ResolvedSource, nil }).Should(And( Not(BeNil()), - WithTransform(func(s *rukpakv1alpha1.BundleSource) rukpakv1alpha1.SourceType { return s.Type }, Equal(rukpakv1alpha1.SourceTypeImage)), - WithTransform(func(s *rukpakv1alpha1.BundleSource) *rukpakv1alpha1.ImageSource { return s.Image }, And( + WithTransform(func(s *source.Source) source.SourceType { return s.Type }, Equal(source.SourceTypeImage)), + WithTransform(func(s *source.Source) *source.ImageSource { return s.Image }, And( Not(BeNil()), - WithTransform(func(i *rukpakv1alpha1.ImageSource) string { return i.Ref }, Not(Equal(""))), + WithTransform(func(i *source.ImageSource) string { return i.Ref }, Not(Equal(""))), )), )) }) @@ -284,9 +285,9 @@ var _ = Describe("plain provisioner bundle", func() { }, Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: plain.ProvisionerID, - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeImage, - Image: &rukpakv1alpha1.ImageSource{ + Source: source.Source{ + Type: source.SourceTypeImage, + Image: &source.ImageSource{ Ref: fmt.Sprintf("%v/%v", ImageRepo, "plain-v0:non-existent-tag"), }, }, @@ -358,9 +359,9 @@ var _ = Describe("plain provisioner bundle", func() { }, Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: plain.ProvisionerID, - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeImage, - Image: &rukpakv1alpha1.ImageSource{ + Source: source.Source{ + Type: source.SourceTypeImage, + Image: &source.ImageSource{ Ref: fmt.Sprintf("%v/%v", ImageRepo, "plain-v0:empty"), }, }, @@ -407,9 +408,9 @@ var _ = Describe("plain provisioner bundle", func() { }, Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: plain.ProvisionerID, - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeImage, - Image: &rukpakv1alpha1.ImageSource{ + Source: source.Source{ + Type: source.SourceTypeImage, + Image: &source.ImageSource{ Ref: fmt.Sprintf("%v/%v", ImageRepo, "plain-v0:no-manifests"), }, }, @@ -461,11 +462,11 @@ var _ = Describe("plain provisioner bundle", func() { }, Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: plain.ProvisionerID, - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeGit, - Git: &rukpakv1alpha1.GitSource{ + Source: source.Source{ + Type: source.SourceTypeGit, + Git: &source.GitSource{ Repository: "https://github.com/exdx/combo-bundle", - Ref: rukpakv1alpha1.GitRef{ + Ref: source.GitRef{ Commit: "9e3ab7f1a36302ef512294d5c9f2e9b9566b811e", }, }, @@ -513,11 +514,11 @@ var _ = Describe("plain provisioner bundle", func() { }, Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: plain.ProvisionerID, - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeGit, - Git: &rukpakv1alpha1.GitSource{ + Source: source.Source{ + Type: source.SourceTypeGit, + Git: &source.GitSource{ Repository: "https://github.com/exdx/combo-bundle", - Ref: rukpakv1alpha1.GitRef{ + Ref: source.GitRef{ Tag: "v0.0.1", }, }, @@ -556,17 +557,17 @@ var _ = Describe("plain provisioner bundle", func() { }) By("eventually writing a non-empty commit hash to the status", func() { - Eventually(func() (*rukpakv1alpha1.BundleSource, error) { + Eventually(func() (*source.Source, error) { if err := c.Get(ctx, client.ObjectKeyFromObject(bundle), bundle); err != nil { return nil, err } return bundle.Status.ResolvedSource, nil }).Should(And( Not(BeNil()), - WithTransform(func(s *rukpakv1alpha1.BundleSource) rukpakv1alpha1.SourceType { return s.Type }, Equal(rukpakv1alpha1.SourceTypeGit)), - WithTransform(func(s *rukpakv1alpha1.BundleSource) *rukpakv1alpha1.GitSource { return s.Git }, And( + WithTransform(func(s *source.Source) source.SourceType { return s.Type }, Equal(source.SourceTypeGit)), + WithTransform(func(s *source.Source) *source.GitSource { return s.Git }, And( Not(BeNil()), - WithTransform(func(i *rukpakv1alpha1.GitSource) string { return i.Ref.Commit }, Not(Equal(""))), + WithTransform(func(i *source.GitSource) string { return i.Ref.Commit }, Not(Equal(""))), )), )) }) @@ -584,11 +585,11 @@ var _ = Describe("plain provisioner bundle", func() { }, Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: plain.ProvisionerID, - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeGit, - Git: &rukpakv1alpha1.GitSource{ + Source: source.Source{ + Type: source.SourceTypeGit, + Git: &source.GitSource{ Repository: "https://github.com/exdx/combo-bundle.git", - Ref: rukpakv1alpha1.GitRef{ + Ref: source.GitRef{ Branch: "main", }, }, @@ -627,17 +628,17 @@ var _ = Describe("plain provisioner bundle", func() { }) By("eventually writing a non-empty commit hash to the status", func() { - Eventually(func() (*rukpakv1alpha1.BundleSource, error) { + Eventually(func() (*source.Source, error) { if err := c.Get(ctx, client.ObjectKeyFromObject(bundle), bundle); err != nil { return nil, err } return bundle.Status.ResolvedSource, nil }).Should(And( Not(BeNil()), - WithTransform(func(s *rukpakv1alpha1.BundleSource) rukpakv1alpha1.SourceType { return s.Type }, Equal(rukpakv1alpha1.SourceTypeGit)), - WithTransform(func(s *rukpakv1alpha1.BundleSource) *rukpakv1alpha1.GitSource { return s.Git }, And( + WithTransform(func(s *source.Source) source.SourceType { return s.Type }, Equal(source.SourceTypeGit)), + WithTransform(func(s *source.Source) *source.GitSource { return s.Git }, And( Not(BeNil()), - WithTransform(func(i *rukpakv1alpha1.GitSource) string { return i.Ref.Commit }, Not(Equal(""))), + WithTransform(func(i *source.GitSource) string { return i.Ref.Commit }, Not(Equal(""))), )), )) }) @@ -655,12 +656,12 @@ var _ = Describe("plain provisioner bundle", func() { }, Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: plain.ProvisionerID, - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeGit, - Git: &rukpakv1alpha1.GitSource{ + Source: source.Source{ + Type: source.SourceTypeGit, + Git: &source.GitSource{ Repository: "https://github.com/exdx/combo-bundle", Directory: "./dev/deploy", - Ref: rukpakv1alpha1.GitRef{ + Ref: source.GitRef{ Branch: "main", }, }, @@ -729,14 +730,14 @@ var _ = Describe("plain provisioner bundle", func() { }, Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: plain.ProvisionerID, - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeGit, - Git: &rukpakv1alpha1.GitSource{ + Source: source.Source{ + Type: source.SourceTypeGit, + Git: &source.GitSource{ Repository: privateRepo, - Ref: rukpakv1alpha1.GitRef{ + Ref: source.GitRef{ Branch: "main", }, - Auth: rukpakv1alpha1.Authorization{ + Auth: source.Authorization{ Secret: corev1.LocalObjectReference{ Name: secret.Name, }, @@ -791,14 +792,14 @@ var _ = Describe("plain provisioner bundle", func() { }, Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: plain.ProvisionerID, - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeGit, - Git: &rukpakv1alpha1.GitSource{ + Source: source.Source{ + Type: source.SourceTypeGit, + Git: &source.GitSource{ Repository: privateRepo, - Ref: rukpakv1alpha1.GitRef{ + Ref: source.GitRef{ Branch: "main", }, - Auth: rukpakv1alpha1.Authorization{ + Auth: source.Authorization{ Secret: corev1.LocalObjectReference{ Name: "gitsecret", }, @@ -886,9 +887,9 @@ var _ = Describe("plain provisioner bundle", func() { }, Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: plain.ProvisionerID, - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeConfigMaps, - ConfigMaps: []rukpakv1alpha1.ConfigMapSource{{ + Source: source.Source{ + Type: source.SourceTypeConfigMaps, + ConfigMaps: []source.ConfigMapSource{{ ConfigMap: corev1.LocalObjectReference{Name: configmap.ObjectMeta.Name}, Path: "manifests", }}, @@ -937,9 +938,9 @@ var _ = Describe("plain provisioner bundle", func() { }, Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: plain.ProvisionerID, - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeConfigMaps, - ConfigMaps: []rukpakv1alpha1.ConfigMapSource{{ + Source: source.Source{ + Type: source.SourceTypeConfigMaps, + ConfigMaps: []source.ConfigMapSource{{ ConfigMap: corev1.LocalObjectReference{Name: "non-exist"}, Path: "manifests", }}, @@ -1013,9 +1014,9 @@ var _ = Describe("plain provisioner bundle", func() { }, Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: plain.ProvisionerID, - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeConfigMaps, - ConfigMaps: []rukpakv1alpha1.ConfigMapSource{{ + Source: source.Source{ + Type: source.SourceTypeConfigMaps, + ConfigMaps: []source.ConfigMapSource{{ ConfigMap: corev1.LocalObjectReference{Name: configmap.ObjectMeta.Name}, Path: "manifests", }}, @@ -1065,9 +1066,9 @@ var _ = Describe("plain provisioner bundle", func() { }, Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: plain.ProvisionerID, - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeUpload, - Upload: &rukpakv1alpha1.UploadSource{}, + Source: source.Source{ + Type: source.SourceTypeUpload, + Upload: &source.UploadSource{}, }, }, } @@ -1123,9 +1124,9 @@ var _ = Describe("plain provisioner bundle", func() { }, Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: plain.ProvisionerID, - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeUpload, - Upload: &rukpakv1alpha1.UploadSource{}, + Source: source.Source{ + Type: source.SourceTypeUpload, + Upload: &source.UploadSource{}, }, }, } @@ -1187,9 +1188,9 @@ var _ = Describe("plain provisioner bundle", func() { }, Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: plain.ProvisionerID, - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeImage, - Image: &rukpakv1alpha1.ImageSource{ + Source: source.Source{ + Type: source.SourceTypeImage, + Image: &source.ImageSource{ Ref: fmt.Sprintf("%v/%v", ImageRepo, "plain-v0:subdir"), }, }, @@ -1235,11 +1236,11 @@ var _ = Describe("plain provisioner bundle", func() { }, Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: plain.ProvisionerID, - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeGit, - Git: &rukpakv1alpha1.GitSource{ + Source: source.Source{ + Type: source.SourceTypeGit, + Git: &source.GitSource{ Repository: "https://github.com/exdx/combo-bundle", - Ref: rukpakv1alpha1.GitRef{ + Ref: source.GitRef{ Commit: "9e3ab7f1a36302ef512294d5c9f2e9b9566b811e", }, }, @@ -1397,9 +1398,9 @@ var _ = Describe("plain provisioner bundledeployment", func() { Template: &rukpakv1alpha1.BundleTemplate{ Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: plain.ProvisionerID, - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeImage, - Image: &rukpakv1alpha1.ImageSource{ + Source: source.Source{ + Type: source.SourceTypeImage, + Image: &source.ImageSource{ Ref: fmt.Sprintf("%v/%v", ImageRepo, "plain-v0:valid"), }, }, @@ -1482,11 +1483,11 @@ var _ = Describe("plain provisioner bundledeployment", func() { } bd.Spec.Template.Spec = rukpakv1alpha1.BundleSpec{ ProvisionerClassName: plain.ProvisionerID, - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeGit, - Git: &rukpakv1alpha1.GitSource{ + Source: source.Source{ + Type: source.SourceTypeGit, + Git: &source.GitSource{ Repository: "github.com/operator-framework/combo", - Ref: rukpakv1alpha1.GitRef{ + Ref: source.GitRef{ Tag: "non-existent-tag", }, }, @@ -1633,9 +1634,9 @@ var _ = Describe("plain provisioner bundledeployment", func() { }, Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: plain.ProvisionerID, - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeImage, - Image: &rukpakv1alpha1.ImageSource{ + Source: source.Source{ + Type: source.SourceTypeImage, + Image: &source.ImageSource{ Ref: fmt.Sprintf("%v/%v", ImageRepo, "plain-v0:valid"), }, }, @@ -1692,9 +1693,9 @@ var _ = Describe("plain provisioner bundledeployment", func() { }, Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: plain.ProvisionerID, - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeImage, - Image: &rukpakv1alpha1.ImageSource{ + Source: source.Source{ + Type: source.SourceTypeImage, + Image: &source.ImageSource{ Ref: fmt.Sprintf("%v/%v", ImageRepo, "plain-v0:invalid-missing-crds"), }, }, @@ -1759,9 +1760,9 @@ var _ = Describe("plain provisioner bundledeployment", func() { }, Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: plain.ProvisionerID, - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeImage, - Image: &rukpakv1alpha1.ImageSource{ + Source: source.Source{ + Type: source.SourceTypeImage, + Image: &source.ImageSource{ Ref: fmt.Sprintf("%v/%v", ImageRepo, "plain-v0:subdir"), }, }, @@ -1823,9 +1824,9 @@ var _ = Describe("plain provisioner bundledeployment", func() { }, Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: plain.ProvisionerID, - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeImage, - Image: &rukpakv1alpha1.ImageSource{ + Source: source.Source{ + Type: source.SourceTypeImage, + Image: &source.ImageSource{ Ref: fmt.Sprintf("%v/%v", ImageRepo, "plain-v0:dependent"), }, }, @@ -1880,9 +1881,9 @@ var _ = Describe("plain provisioner bundledeployment", func() { }, Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: plain.ProvisionerID, - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeImage, - Image: &rukpakv1alpha1.ImageSource{ + Source: source.Source{ + Type: source.SourceTypeImage, + Image: &source.ImageSource{ Ref: fmt.Sprintf("%v/%v", ImageRepo, "plain-v0:provides"), }, }, @@ -1941,9 +1942,9 @@ var _ = Describe("plain provisioner bundledeployment", func() { }, Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: plain.ProvisionerID, - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeImage, - Image: &rukpakv1alpha1.ImageSource{ + Source: source.Source{ + Type: source.SourceTypeImage, + Image: &source.ImageSource{ Ref: fmt.Sprintf("%v/%v", ImageRepo, "plain-v0:invalid-crds-and-crs"), }, }, @@ -1991,9 +1992,9 @@ var _ = Describe("plain provisioner garbage collection", func() { }, Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: plain.ProvisionerID, - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeImage, - Image: &rukpakv1alpha1.ImageSource{ + Source: source.Source{ + Type: source.SourceTypeImage, + Image: &source.ImageSource{ Ref: fmt.Sprintf("%v/%v", ImageRepo, "plain-v0:valid"), }, }, @@ -2073,9 +2074,9 @@ var _ = Describe("plain provisioner garbage collection", func() { }, Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: plain.ProvisionerID, - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeImage, - Image: &rukpakv1alpha1.ImageSource{ + Source: source.Source{ + Type: source.SourceTypeImage, + Image: &source.ImageSource{ Ref: fmt.Sprintf("%v/%v", ImageRepo, "plain-v0:valid"), }, }, @@ -2167,9 +2168,9 @@ var _ = Describe("plain provisioner garbage collection", func() { }, Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: plain.ProvisionerID, - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeImage, - Image: &rukpakv1alpha1.ImageSource{ + Source: source.Source{ + Type: source.SourceTypeImage, + Image: &source.ImageSource{ Ref: fmt.Sprintf("%v/%v", ImageRepo, "plain-v0:valid"), }, }, diff --git a/test/e2e/registry_provisioner_test.go b/test/e2e/registry_provisioner_test.go index 74bfeaff..ed7343d5 100644 --- a/test/e2e/registry_provisioner_test.go +++ b/test/e2e/registry_provisioner_test.go @@ -13,6 +13,7 @@ import ( rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1" "github.com/operator-framework/rukpak/internal/provisioner/plain" "github.com/operator-framework/rukpak/internal/provisioner/registry" + "github.com/operator-framework/rukpak/pkg/source" ) var _ = Describe("registry provisioner bundle", func() { @@ -38,9 +39,9 @@ var _ = Describe("registry provisioner bundle", func() { }, Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: registry.ProvisionerID, - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeImage, - Image: &rukpakv1alpha1.ImageSource{ + Source: source.Source{ + Type: source.SourceTypeImage, + Image: &source.ImageSource{ Ref: fmt.Sprintf("%v/%v", ImageRepo, "registry:valid"), }, }, @@ -97,9 +98,9 @@ var _ = Describe("registry provisioner bundle", func() { }, Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: registry.ProvisionerID, - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeImage, - Image: &rukpakv1alpha1.ImageSource{ + Source: source.Source{ + Type: source.SourceTypeImage, + Image: &source.ImageSource{ Ref: fmt.Sprintf("%v/%v", ImageRepo, "registry:invalid"), }, }, diff --git a/test/e2e/rukpakctl_test.go b/test/e2e/rukpakctl_test.go index a668e721..21d71e19 100644 --- a/test/e2e/rukpakctl_test.go +++ b/test/e2e/rukpakctl_test.go @@ -14,6 +14,7 @@ import ( rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1" "github.com/operator-framework/rukpak/internal/provisioner/plain" + "github.com/operator-framework/rukpak/pkg/source" ) const ( @@ -189,11 +190,11 @@ var _ = Describe("rukpakctl content subcommand", func() { }, Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: plain.ProvisionerID, - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeGit, - Git: &rukpakv1alpha1.GitSource{ + Source: source.Source{ + Type: source.SourceTypeGit, + Git: &source.GitSource{ Repository: "https://github.com/exdx/combo-bundle", - Ref: rukpakv1alpha1.GitRef{ + Ref: source.GitRef{ Commit: "9e3ab7f1a36302ef512294d5c9f2e9b9566b811e", }, }, diff --git a/test/e2e/webhook_test.go b/test/e2e/webhook_test.go index 7b6f7799..e107b52e 100644 --- a/test/e2e/webhook_test.go +++ b/test/e2e/webhook_test.go @@ -10,6 +10,7 @@ import ( rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1" "github.com/operator-framework/rukpak/internal/provisioner/plain" + "github.com/operator-framework/rukpak/pkg/source" ) var _ = Describe("bundle api validating webhook", func() { @@ -30,9 +31,9 @@ var _ = Describe("bundle api validating webhook", func() { }, Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: plain.ProvisionerID, - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeImage, - Image: &rukpakv1alpha1.ImageSource{ + Source: source.Source{ + Type: source.SourceTypeImage, + Image: &source.ImageSource{ Ref: "localhost/testdata/bundles/plain-v0:valid", }, }, @@ -65,9 +66,9 @@ var _ = Describe("bundle api validating webhook", func() { }, Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: plain.ProvisionerID, - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeGit, - Image: &rukpakv1alpha1.ImageSource{ + Source: source.Source{ + Type: source.SourceTypeGit, + Image: &source.ImageSource{ Ref: "localhost/testdata/bundles/plain-v0:valid", }, }, @@ -100,11 +101,11 @@ var _ = Describe("bundle api validating webhook", func() { }, Spec: rukpakv1alpha1.BundleSpec{ ProvisionerClassName: plain.ProvisionerID, - Source: rukpakv1alpha1.BundleSource{ - Type: rukpakv1alpha1.SourceTypeImage, - Git: &rukpakv1alpha1.GitSource{ + Source: source.Source{ + Type: source.SourceTypeImage, + Git: &source.GitSource{ Repository: "https://github.com/exdx/combo-bundle", - Ref: rukpakv1alpha1.GitRef{ + Ref: source.GitRef{ Commit: "9e3ab7f1a36302ef512294d5c9f2e9b9566b811e", }, },