From 931601284587b71e87f26625ad44153a2679090e Mon Sep 17 00:00:00 2001 From: Joe Lanford Date: Thu, 14 Dec 2023 15:51:00 -0500 Subject: [PATCH] rename Operator API to ClusterExtension API (#568) Signed-off-by: Joe Lanford --- .github/workflows/deploy.yaml | 4 +- .github/workflows/operator-developer-e2e.yaml | 6 +- Makefile | 14 +- PROJECT | 4 +- README.md | 3 +- ...tor_types.go => clusterextension_types.go} | 28 +- ...test.go => clusterextension_types_test.go} | 4 +- api/v1alpha1/groupversion_info.go | 6 +- api/v1alpha1/zz_generated.deepcopy.go | 38 +- cmd/manager/main.go | 8 +- cmd/resolutioncli/main.go | 27 +- ...eratorframework.io_clusterextensions.yaml} | 18 +- config/crd/kustomization.yaml | 2 +- ...yaml => clusterextension_editor_role.yaml} | 14 +- ...yaml => clusterextension_viewer_role.yaml} | 14 +- config/rbac/role.yaml | 12 +- config/samples/kustomization.yaml | 2 +- ...aml => olm_v1alpha1_clusterextension.yaml} | 10 +- docs/Tasks/adding-a-catalog.md | 6 +- ...operator.md => installing-an-extension.md} | 20 +- docs/Tasks/uninstall-an-extension.md | 14 + docs/Tasks/uninstall-an-operator.md | 14 - docs/components.md | 2 +- docs/demos/coast-to-coast-q4-2023.md | 154 +-- docs/drafts/upgrade-support.md | 12 +- docs/drafts/version-ranges.md | 12 +- docs/index.md | 4 +- docs/olmv1_roadmap.md | 18 +- .../catalogmetadata/filter/filter_test.go | 26 +- internal/conditionsets/conditionsets.go | 6 +- internal/controllers/admission_test.go | 46 +- ...ller.go => clusterextension_controller.go} | 206 ++-- ...go => clusterextension_controller_test.go} | 974 +++++++++--------- internal/controllers/suite_test.go | 8 +- internal/controllers/validators/validators.go | 20 +- .../controllers/validators/validators_test.go | 10 +- internal/controllers/variables.go | 8 +- internal/controllers/variables_test.go | 37 +- internal/resolution/variables/bundle.go | 2 +- .../variablesources/fake_object_utils_test.go | 16 +- .../variablesources/installed_package.go | 21 +- .../variablesources/installed_package_test.go | 42 +- .../variablesources/required_package.go | 16 +- .../variablesources/required_package_test.go | 50 +- mkdocs.yml | 8 +- test/e2e/e2e_suite_test.go | 10 +- test/e2e/install_test.go | 257 +++-- .../extension_developer_test.go} | 41 +- .../setup.sh | 32 +- 49 files changed, 1147 insertions(+), 1159 deletions(-) rename api/v1alpha1/{operator_types.go => clusterextension_types.go} (85%) rename api/v1alpha1/{operator_types_test.go => clusterextension_types_test.go} (96%) rename config/crd/bases/{operators.operatorframework.io_operators.yaml => olm.operatorframework.io_clusterextensions.yaml} (93%) rename config/rbac/{operator_editor_role.yaml => clusterextension_editor_role.yaml} (63%) rename config/rbac/{operator_viewer_role.yaml => clusterextension_viewer_role.yaml} (61%) rename config/samples/{operators_v1alpha1_operator.yaml => olm_v1alpha1_clusterextension.yaml} (52%) rename docs/Tasks/{installing-an-operator.md => installing-an-extension.md} (74%) create mode 100644 docs/Tasks/uninstall-an-extension.md delete mode 100644 docs/Tasks/uninstall-an-operator.md rename internal/controllers/{operator_controller.go => clusterextension_controller.go} (64%) rename internal/controllers/{operator_controller_test.go => clusterextension_controller_test.go} (59%) rename test/{operator-framework-e2e/operator_framework_test.go => extension-developer-e2e/extension_developer_test.go} (64%) rename test/{operator-framework-e2e => extension-developer-e2e}/setup.sh (88%) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 30fe38c0..f8ef79c9 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -22,8 +22,8 @@ jobs: run: | make kind-cluster make deploy - kubectl get crds operators.operators.operatorframework.io + kubectl get crds clusterextensions.olm.operatorframework.io kubectl get ns operator-controller-system make undeploy ! kubectl get ns operator-controller-system - ! kubectl get crds operators.operators.operatorframework.io + ! kubectl get crds clusterextensions.olm.operatorframework.io diff --git a/.github/workflows/operator-developer-e2e.yaml b/.github/workflows/operator-developer-e2e.yaml index 5ee6caf3..cc399f19 100644 --- a/.github/workflows/operator-developer-e2e.yaml +++ b/.github/workflows/operator-developer-e2e.yaml @@ -1,4 +1,4 @@ -name: operator-developer-e2e +name: extension-developer-e2e on: workflow_dispatch: @@ -18,6 +18,6 @@ jobs: with: go-version-file: go.mod - - name: Run the operator framework e2e test + - name: Run the extension developer e2e test run: | - make operator-developer-e2e + make extension-developer-e2e diff --git a/Makefile b/Makefile index 8c5a4e01..f7f9afbf 100644 --- a/Makefile +++ b/Makefile @@ -111,10 +111,10 @@ e2e: $(SETUP_ENVTEST) #EXHELP Run the e2e tests. export REG_PKG_NAME=registry-operator export PLAIN_PKG_NAME=plain-operator export CATALOG_IMG=${E2E_REGISTRY_NAME}.${E2E_REGISTRY_NAMESPACE}.svc:5000/test-catalog:e2e -.PHONY: test-op-dev-e2e -test-op-dev-e2e: $(SETUP_ENVTEST) $(OPERATOR_SDK) $(KUSTOMIZE) $(KIND) #HELP Run operator create, upgrade and delete tests. - test/operator-framework-e2e/setup.sh $(OPERATOR_SDK) $(CONTAINER_RUNTIME) $(KUSTOMIZE) $(KIND) $(KIND_CLUSTER_NAME) ${E2E_REGISTRY_NAMESPACE} - eval $$($(SETUP_ENVTEST) use -p env $(ENVTEST_VERSION)) && go test -tags $(GO_BUILD_TAGS) -v ./test/operator-framework-e2e/... +.PHONY: test-ext-dev-e2e +test-ext-dev-e2e: $(SETUP_ENVTEST) $(OPERATOR_SDK) $(KUSTOMIZE) $(KIND) #HELP Run extension create, upgrade and delete tests. + test/extension-developer-e2e/setup.sh $(OPERATOR_SDK) $(CONTAINER_RUNTIME) $(KUSTOMIZE) $(KIND) $(KIND_CLUSTER_NAME) ${E2E_REGISTRY_NAMESPACE} + eval $$($(SETUP_ENVTEST) use -p env $(ENVTEST_VERSION)) && go test -tags $(GO_BUILD_TAGS) -v ./test/extension-developer-e2e/... .PHONY: test-unit ENVTEST_VERSION = $(shell go list -m k8s.io/client-go | cut -d" " -f2 | sed 's/^v0\.\([[:digit:]]\{1,\}\)\.[[:digit:]]\{1,\}$$/1.\1.x/') @@ -136,9 +136,9 @@ test-e2e: KUSTOMIZE_BUILD_DIR=config/e2e test-e2e: GO_BUILD_FLAGS=-cover test-e2e: run image-registry build-push-e2e-catalog kind-load-test-artifacts e2e e2e-coverage undeploy kind-clean #HELP Run e2e test suite on local kind cluster -.PHONY: operator-developer-e2e -operator-developer-e2e: KIND_CLUSTER_NAME=operator-controller-op-dev-e2e #EXHELP Run operator-developer e2e on local kind cluster -operator-developer-e2e: run image-registry test-op-dev-e2e kind-clean +.PHONY: extension-developer-e2e +extension-developer-e2e: KIND_CLUSTER_NAME=operator-controller-ext-dev-e2e #EXHELP Run extension-developer e2e on local kind cluster +extension-developer-e2e: run image-registry test-ext-dev-e2e kind-clean .PHONY: e2e-coverage e2e-coverage: diff --git a/PROJECT b/PROJECT index ae354b24..ed17e0fd 100644 --- a/PROJECT +++ b/PROJECT @@ -9,8 +9,8 @@ resources: namespaced: true controller: true domain: operatorframework.io - group: operators - kind: Operator + group: olm + kind: ClusterExtension path: github.com/operator-framework/operator-controller/api/v1alpha1 version: v1alpha1 version: "3" diff --git a/README.md b/README.md index 0183fce2..9e1efd8a 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # operator-controller -The operator-controller is the central component of Operator Lifecycle Manager (OLM) v1. It extends Kubernetes with an API through which users can install Operators. +The operator-controller is the central component of Operator Lifecycle Manager (OLM) v1. It extends Kubernetes with an API through which users can install extensions. ## Description OLM v1 is the follow-up to OLM v0, located [here](https://github.com/operator-framework/operator-lifecycle-manager). It consists of four different components, including this one, which are as follows: @@ -90,4 +90,3 @@ 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. - diff --git a/api/v1alpha1/operator_types.go b/api/v1alpha1/clusterextension_types.go similarity index 85% rename from api/v1alpha1/operator_types.go rename to api/v1alpha1/clusterextension_types.go index 22d0d2a4..783e0c18 100644 --- a/api/v1alpha1/operator_types.go +++ b/api/v1alpha1/clusterextension_types.go @@ -25,11 +25,11 @@ import ( type UpgradeConstraintPolicy string const ( - // The operator will only upgrade if the new version satisfies + // The extension will only upgrade if the new version satisfies // the upgrade constraints set by the package author. UpgradeConstraintPolicyEnforce UpgradeConstraintPolicy = "Enforce" - // Unsafe option which allows an operator to be + // Unsafe option which allows an extension to be // upgraded or downgraded to any available version of the package and // ignore the upgrade path designed by package authors. // This assumes that users independently verify the outcome of the changes. @@ -38,8 +38,8 @@ const ( UpgradeConstraintPolicyIgnore UpgradeConstraintPolicy = "Ignore" ) -// OperatorSpec defines the desired state of Operator -type OperatorSpec struct { +// ClusterExtensionSpec defines the desired state of ClusterExtension +type ClusterExtensionSpec struct { //+kubebuilder:validation:MaxLength:=48 //+kubebuilder:validation:Pattern:=^[a-z0-9]+(-[a-z0-9]+)*$ PackageName string `json:"packageName"` @@ -101,8 +101,8 @@ func init() { ) } -// OperatorStatus defines the observed state of Operator -type OperatorStatus struct { +// ClusterExtensionStatus defines the observed state of ClusterExtension +type ClusterExtensionStatus struct { // +optional InstalledBundleResource string `json:"installedBundleResource,omitempty"` // +optional @@ -119,24 +119,24 @@ type OperatorStatus struct { //+kubebuilder:resource:scope=Cluster //+kubebuilder:subresource:status -// Operator is the Schema for the operators API -type Operator struct { +// ClusterExtension is the Schema for the clusterextensions API +type ClusterExtension struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - Spec OperatorSpec `json:"spec,omitempty"` - Status OperatorStatus `json:"status,omitempty"` + Spec ClusterExtensionSpec `json:"spec,omitempty"` + Status ClusterExtensionStatus `json:"status,omitempty"` } //+kubebuilder:object:root=true -// OperatorList contains a list of Operator -type OperatorList struct { +// ClusterExtensionList contains a list of ClusterExtension +type ClusterExtensionList struct { metav1.TypeMeta `json:",inline"` metav1.ListMeta `json:"metadata,omitempty"` - Items []Operator `json:"items"` + Items []ClusterExtension `json:"items"` } func init() { - SchemeBuilder.Register(&Operator{}, &OperatorList{}) + SchemeBuilder.Register(&ClusterExtension{}, &ClusterExtensionList{}) } diff --git a/api/v1alpha1/operator_types_test.go b/api/v1alpha1/clusterextension_types_test.go similarity index 96% rename from api/v1alpha1/operator_types_test.go rename to api/v1alpha1/clusterextension_types_test.go index b63b0acf..ce151e3b 100644 --- a/api/v1alpha1/operator_types_test.go +++ b/api/v1alpha1/clusterextension_types_test.go @@ -15,7 +15,7 @@ import ( "golang.org/x/exp/slices" // replace with "slices" in go 1.21 ) -func TestOperatorTypeRegistration(t *testing.T) { +func TestClusterExtensionTypeRegistration(t *testing.T) { types, err := parseConstants("Type") if err != nil { t.Fatalf("unable to parse Type constants %v", err) @@ -34,7 +34,7 @@ func TestOperatorTypeRegistration(t *testing.T) { } } -func TestOperatorReasonRegistration(t *testing.T) { +func TestClusterExtensionReasonRegistration(t *testing.T) { reasons, err := parseConstants("Reason") if err != nil { t.Fatalf("unable to parse Reason constants %v", err) diff --git a/api/v1alpha1/groupversion_info.go b/api/v1alpha1/groupversion_info.go index d65c1b4e..f46abbf3 100644 --- a/api/v1alpha1/groupversion_info.go +++ b/api/v1alpha1/groupversion_info.go @@ -14,9 +14,9 @@ See the License for the specific language governing permissions and limitations under the License. */ -// Package v1alpha1 contains API Schema definitions for the operators v1alpha1 API group +// Package v1alpha1 contains API Schema definitions for the olm v1alpha1 API group // +kubebuilder:object:generate=true -// +groupName=operators.operatorframework.io +// +groupName=olm.operatorframework.io package v1alpha1 import ( @@ -26,7 +26,7 @@ import ( var ( // GroupVersion is group version used to register these objects - GroupVersion = schema.GroupVersion{Group: "operators.operatorframework.io", Version: "v1alpha1"} + GroupVersion = schema.GroupVersion{Group: "olm.operatorframework.io", Version: "v1alpha1"} // SchemeBuilder is used to add go types to the GroupVersionKind scheme SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index f54900db..4c1b7f3e 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -27,7 +27,7 @@ import ( ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Operator) DeepCopyInto(out *Operator) { +func (in *ClusterExtension) DeepCopyInto(out *ClusterExtension) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) @@ -35,18 +35,18 @@ func (in *Operator) DeepCopyInto(out *Operator) { in.Status.DeepCopyInto(&out.Status) } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Operator. -func (in *Operator) DeepCopy() *Operator { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterExtension. +func (in *ClusterExtension) DeepCopy() *ClusterExtension { if in == nil { return nil } - out := new(Operator) + out := new(ClusterExtension) in.DeepCopyInto(out) return out } // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *Operator) DeepCopyObject() runtime.Object { +func (in *ClusterExtension) DeepCopyObject() runtime.Object { if c := in.DeepCopy(); c != nil { return c } @@ -54,31 +54,31 @@ func (in *Operator) DeepCopyObject() runtime.Object { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *OperatorList) DeepCopyInto(out *OperatorList) { +func (in *ClusterExtensionList) DeepCopyInto(out *ClusterExtensionList) { *out = *in out.TypeMeta = in.TypeMeta in.ListMeta.DeepCopyInto(&out.ListMeta) if in.Items != nil { in, out := &in.Items, &out.Items - *out = make([]Operator, len(*in)) + *out = make([]ClusterExtension, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OperatorList. -func (in *OperatorList) DeepCopy() *OperatorList { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterExtensionList. +func (in *ClusterExtensionList) DeepCopy() *ClusterExtensionList { if in == nil { return nil } - out := new(OperatorList) + out := new(ClusterExtensionList) in.DeepCopyInto(out) return out } // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *OperatorList) DeepCopyObject() runtime.Object { +func (in *ClusterExtensionList) DeepCopyObject() runtime.Object { if c := in.DeepCopy(); c != nil { return c } @@ -86,22 +86,22 @@ func (in *OperatorList) DeepCopyObject() runtime.Object { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *OperatorSpec) DeepCopyInto(out *OperatorSpec) { +func (in *ClusterExtensionSpec) DeepCopyInto(out *ClusterExtensionSpec) { *out = *in } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OperatorSpec. -func (in *OperatorSpec) DeepCopy() *OperatorSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterExtensionSpec. +func (in *ClusterExtensionSpec) DeepCopy() *ClusterExtensionSpec { if in == nil { return nil } - out := new(OperatorSpec) + out := new(ClusterExtensionSpec) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *OperatorStatus) DeepCopyInto(out *OperatorStatus) { +func (in *ClusterExtensionStatus) DeepCopyInto(out *ClusterExtensionStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions @@ -112,12 +112,12 @@ func (in *OperatorStatus) DeepCopyInto(out *OperatorStatus) { } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OperatorStatus. -func (in *OperatorStatus) DeepCopy() *OperatorStatus { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterExtensionStatus. +func (in *ClusterExtensionStatus) DeepCopy() *ClusterExtensionStatus { if in == nil { return nil } - out := new(OperatorStatus) + out := new(ClusterExtensionStatus) in.DeepCopyInto(out) return out } diff --git a/cmd/manager/main.go b/cmd/manager/main.go index ada4307f..d61a6f18 100644 --- a/cmd/manager/main.go +++ b/cmd/manager/main.go @@ -36,7 +36,7 @@ import ( "github.com/operator-framework/deppy/pkg/deppy/solver" rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1" - operatorsv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" + ocv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" "github.com/operator-framework/operator-controller/internal/catalogmetadata/cache" catalogclient "github.com/operator-framework/operator-controller/internal/catalogmetadata/client" "github.com/operator-framework/operator-controller/internal/controllers" @@ -51,7 +51,7 @@ var ( func init() { utilruntime.Must(clientgoscheme.AddToScheme(scheme)) - utilruntime.Must(operatorsv1alpha1.AddToScheme(scheme)) + utilruntime.Must(ocv1alpha1.AddToScheme(scheme)) utilruntime.Must(rukpakv1alpha1.AddToScheme(scheme)) utilruntime.Must(catalogd.AddToScheme(scheme)) @@ -115,13 +115,13 @@ func main() { os.Exit(1) } - if err = (&controllers.OperatorReconciler{ + if err = (&controllers.ClusterExtensionReconciler{ Client: cl, BundleProvider: catalogClient, Scheme: mgr.GetScheme(), Resolver: resolver, }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "Operator") + setupLog.Error(err, "unable to create controller", "controller", "ClusterExtension") os.Exit(1) } //+kubebuilder:scaffold:builder diff --git a/cmd/resolutioncli/main.go b/cmd/resolutioncli/main.go index 29395857..ab9f29f0 100644 --- a/cmd/resolutioncli/main.go +++ b/cmd/resolutioncli/main.go @@ -22,6 +22,10 @@ import ( "fmt" "os" + catalogd "github.com/operator-framework/catalogd/api/core/v1alpha1" + "github.com/operator-framework/deppy/pkg/deppy" + "github.com/operator-framework/deppy/pkg/deppy/solver" + rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/serializer" @@ -30,12 +34,7 @@ import ( _ "k8s.io/client-go/plugin/pkg/client/auth" "sigs.k8s.io/controller-runtime/pkg/client/fake" - catalogd "github.com/operator-framework/catalogd/api/core/v1alpha1" - "github.com/operator-framework/deppy/pkg/deppy" - "github.com/operator-framework/deppy/pkg/deppy/solver" - rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1" - - operatorsv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" + ocv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" "github.com/operator-framework/operator-controller/internal/catalogmetadata" "github.com/operator-framework/operator-controller/internal/controllers" olmvariables "github.com/operator-framework/operator-controller/internal/resolution/variables" @@ -61,7 +60,7 @@ var ( func init() { utilruntime.Must(clientgoscheme.AddToScheme(scheme)) - utilruntime.Must(operatorsv1alpha1.AddToScheme(scheme)) + utilruntime.Must(ocv1alpha1.AddToScheme(scheme)) utilruntime.Must(rukpakv1alpha1.AddToScheme(scheme)) utilruntime.Must(catalogd.AddToScheme(scheme)) } @@ -81,7 +80,7 @@ func main() { flag.StringVar(&packageChannel, flagNamePackageChannel, "", "Channel of the package") // TODO: Consider adding support of multiple refs flag.StringVar(&indexRef, flagNameIndexRef, "", "Index reference (FBC image or dir)") - flag.StringVar(&inputDir, flagNameInputDir, "", "Directory containing Kubernetes manifests (such as Operator) to be used as an input for resolution") + flag.StringVar(&inputDir, flagNameInputDir, "", "Directory containing Kubernetes manifests (such as ClusterExtension) to be used as an input for resolution") flag.Parse() if err := validateFlags(packageName, indexRef); err != nil { @@ -133,11 +132,11 @@ func run(ctx context.Context, packageName, packageChannel, packageVersionRange, clientBuilder.WithRuntimeObjects(objects...) } - clientBuilder.WithRuntimeObjects(&operatorsv1alpha1.Operator{ + clientBuilder.WithRuntimeObjects(&ocv1alpha1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{ - Name: "resolutioncli-input-operator", + Name: "resolutioncli-input", }, - Spec: operatorsv1alpha1.OperatorSpec{ + Spec: ocv1alpha1.ClusterExtensionSpec{ PackageName: packageName, Channel: packageChannel, Version: packageVersionRange, @@ -155,15 +154,15 @@ func run(ctx context.Context, packageName, packageChannel, packageVersionRange, if err != nil { return err } - operatorList := operatorsv1alpha1.OperatorList{} - if err := cl.List(ctx, &operatorList); err != nil { + clusterExtensionList := ocv1alpha1.ClusterExtensionList{} + if err := cl.List(ctx, &clusterExtensionList); err != nil { return err } bundleDeploymentList := rukpakv1alpha1.BundleDeploymentList{} if err := cl.List(ctx, &bundleDeploymentList); err != nil { return err } - variables, err := controllers.GenerateVariables(allBundles, operatorList.Items, bundleDeploymentList.Items) + variables, err := controllers.GenerateVariables(allBundles, clusterExtensionList.Items, bundleDeploymentList.Items) if err != nil { return err } diff --git a/config/crd/bases/operators.operatorframework.io_operators.yaml b/config/crd/bases/olm.operatorframework.io_clusterextensions.yaml similarity index 93% rename from config/crd/bases/operators.operatorframework.io_operators.yaml rename to config/crd/bases/olm.operatorframework.io_clusterextensions.yaml index d54e8620..8b59f73d 100644 --- a/config/crd/bases/operators.operatorframework.io_operators.yaml +++ b/config/crd/bases/olm.operatorframework.io_clusterextensions.yaml @@ -4,20 +4,20 @@ kind: CustomResourceDefinition metadata: annotations: controller-gen.kubebuilder.io/version: v0.12.0 - name: operators.operators.operatorframework.io + name: clusterextensions.olm.operatorframework.io spec: - group: operators.operatorframework.io + group: olm.operatorframework.io names: - kind: Operator - listKind: OperatorList - plural: operators - singular: operator + kind: ClusterExtension + listKind: ClusterExtensionList + plural: clusterextensions + singular: clusterextension scope: Cluster versions: - name: v1alpha1 schema: openAPIV3Schema: - description: Operator is the Schema for the operators API + description: ClusterExtension is the Schema for the clusterextensions API properties: apiVersion: description: 'APIVersion defines the versioned schema of this representation @@ -32,7 +32,7 @@ spec: metadata: type: object spec: - description: OperatorSpec defines the desired state of Operator + description: ClusterExtensionSpec defines the desired state of ClusterExtension properties: channel: description: Channel constraint definition @@ -64,7 +64,7 @@ spec: - packageName type: object status: - description: OperatorStatus defines the observed state of Operator + description: ClusterExtensionStatus defines the observed state of ClusterExtension properties: conditions: items: diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index 098dcba4..ec864639 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -2,7 +2,7 @@ # since it depends on service name and namespace that are out of this kustomize package. # It should be run by config/default resources: -- bases/operators.operatorframework.io_operators.yaml +- bases/olm.operatorframework.io_clusterextensions.yaml # the following config is for teaching kustomize how to do kustomization for CRDs. configurations: diff --git a/config/rbac/operator_editor_role.yaml b/config/rbac/clusterextension_editor_role.yaml similarity index 63% rename from config/rbac/operator_editor_role.yaml rename to config/rbac/clusterextension_editor_role.yaml index 640b48b7..4eef877a 100644 --- a/config/rbac/operator_editor_role.yaml +++ b/config/rbac/clusterextension_editor_role.yaml @@ -1,20 +1,20 @@ -# permissions for end users to edit operators. +# permissions for end users to edit cluster extensions. apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: labels: app.kubernetes.io/name: clusterrole - app.kubernetes.io/instance: operator-editor-role + app.kubernetes.io/instance: clusterextension-editor-role app.kubernetes.io/component: rbac app.kubernetes.io/created-by: operator-controller app.kubernetes.io/part-of: operator-controller app.kubernetes.io/managed-by: kustomize - name: operator-editor-role + name: clusterextension-editor-role rules: - apiGroups: - - operators.operatorframework.io + - olm.operatorframework.io resources: - - operators + - clusterextensions verbs: - create - delete @@ -24,8 +24,8 @@ rules: - update - watch - apiGroups: - - operators.operatorframework.io + - olm.operatorframework.io resources: - - operators/status + - clusterextensions/status verbs: - get diff --git a/config/rbac/operator_viewer_role.yaml b/config/rbac/clusterextension_viewer_role.yaml similarity index 61% rename from config/rbac/operator_viewer_role.yaml rename to config/rbac/clusterextension_viewer_role.yaml index 2f32b0e1..5ffd0ecd 100644 --- a/config/rbac/operator_viewer_role.yaml +++ b/config/rbac/clusterextension_viewer_role.yaml @@ -1,27 +1,27 @@ -# permissions for end users to view operators. +# permissions for end users to view cluster extensions. apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: labels: app.kubernetes.io/name: clusterrole - app.kubernetes.io/instance: operator-viewer-role + app.kubernetes.io/instance: clusterextension-viewer-role app.kubernetes.io/component: rbac app.kubernetes.io/created-by: operator-controller app.kubernetes.io/part-of: operator-controller app.kubernetes.io/managed-by: kustomize - name: operator-viewer-role + name: clusterextension-viewer-role rules: - apiGroups: - - operators.operatorframework.io + - olm.operatorframework.io resources: - - operators + - clusterextensions verbs: - get - list - watch - apiGroups: - - operators.operatorframework.io + - olm.operatorframework.io resources: - - operators/status + - clusterextensions/status verbs: - get diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 546ae21b..5c7c712d 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -30,23 +30,23 @@ rules: - update - watch - apiGroups: - - operators.operatorframework.io + - olm.operatorframework.io resources: - - operators + - clusterextensions verbs: - get - list - watch - apiGroups: - - operators.operatorframework.io + - olm.operatorframework.io resources: - - operators/finalizers + - clusterextensions/finalizers verbs: - update - apiGroups: - - operators.operatorframework.io + - olm.operatorframework.io resources: - - operators/status + - clusterextensions/status verbs: - get - patch diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml index 8d15a090..09aabdc5 100644 --- a/config/samples/kustomization.yaml +++ b/config/samples/kustomization.yaml @@ -1,4 +1,4 @@ ## Append samples of your project ## resources: -- operators_v1alpha1_operator.yaml +- olm_v1alpha1_clusterextension.yaml #+kubebuilder:scaffold:manifestskustomizesamples diff --git a/config/samples/operators_v1alpha1_operator.yaml b/config/samples/olm_v1alpha1_clusterextension.yaml similarity index 52% rename from config/samples/operators_v1alpha1_operator.yaml rename to config/samples/olm_v1alpha1_clusterextension.yaml index d4dfb79a..e7585d7f 100644 --- a/config/samples/operators_v1alpha1_operator.yaml +++ b/config/samples/olm_v1alpha1_clusterextension.yaml @@ -1,13 +1,13 @@ -apiVersion: operators.operatorframework.io/v1alpha1 -kind: Operator +apiVersion: olm.operatorframework.io/v1alpha1 +kind: ClusterExtension metadata: labels: - app.kubernetes.io/name: operator - app.kubernetes.io/instance: operator-sample + app.kubernetes.io/name: clusterextension + app.kubernetes.io/instance: clusterextension-sample app.kubernetes.io/part-of: operator-controller app.kubernetes.io/managed-by: kustomize app.kubernetes.io/created-by: operator-controller - name: operator-sample + name: clusterextension-sample spec: packageName: argocd-operator version: 0.6.0 diff --git a/docs/Tasks/adding-a-catalog.md b/docs/Tasks/adding-a-catalog.md index f0694127..ecccf0a6 100644 --- a/docs/Tasks/adding-a-catalog.md +++ b/docs/Tasks/adding-a-catalog.md @@ -1,6 +1,6 @@ -Operator authors have the mechanisms to offer their product as part of a curated catalog of operators, that they can push updates to over-the-air (eg publish new versions, publish patched versions with CVEs, etc). Cluster admins can sign up to receive these updates on clusters, by adding the catalog to the cluster. When a catalog is added to a cluster, the kubernetes extension packages (operators, or any other extension package) in that catalog become available on cluster for installation and receiving updates. +Extension authors have the mechanisms to offer their product as part of a curated catalog of extensions, that they can push updates to over-the-air (eg publish new versions, publish patched versions with CVEs, etc). Cluster admins can sign up to receive these updates on clusters, by adding the catalog to the cluster. When a catalog is added to a cluster, the kubernetes extension packages in that catalog become available on cluster for installation and receiving updates. -For example, the [k8s-operatorhub/community-operators](https://github.com/k8s-operatorhub/community-operators) is a catalog of curated operators that contains a list of operators being developed by the community. The list of operators can be viewed in [Operatorhub.io](https://operatorhub.io). This catalog is distributed as an image [quay.io/operatorhubio/catalog](https://quay.io/repository/operatorhubio/catalog?tag=latest&tab=tags) for consumption on clusters. +For example, the [k8s-operatorhub/community-operators](https://github.com/k8s-operatorhub/community-operators) is a catalog of curated extensions that contains a list of extensions being developed by the community. The list of extensions can be viewed in [Operatorhub.io](https://operatorhub.io). This catalog is distributed as an image [quay.io/operatorhubio/catalog](https://quay.io/repository/operatorhubio/catalog?tag=latest&tab=tags) for consumption on clusters. To consume this catalog on cluster, create a `Catalog` Custom Resource(CR) with the image specified in the `spec.source.image` field: @@ -74,5 +74,3 @@ operatorhubio-ack-apigatewayv2-controller.v0.1.3 3m58s . . ``` - - diff --git a/docs/Tasks/installing-an-operator.md b/docs/Tasks/installing-an-extension.md similarity index 74% rename from docs/Tasks/installing-an-operator.md rename to docs/Tasks/installing-an-extension.md index a213b0da..10202ad0 100644 --- a/docs/Tasks/installing-an-operator.md +++ b/docs/Tasks/installing-an-extension.md @@ -1,4 +1,4 @@ -Creating an Operator CR installs the operator on cluster: +Creating a ClusterExtension CR installs the extension on cluster: ```bash $ kubectl get packages | grep argocd @@ -6,25 +6,25 @@ operatorhubio-argocd-operator 5m19s operatorhubio-argocd-operator-helm 5m19s $ kubectl apply -f - <[!NOTE] ->In this demo, we aren't going to make the Operator do anything. +>In this demo, we aren't going to make the extension do anything. -We will create a new Operator using the `operator-sdk`. +We will create a new extension using the `operator-sdk`. - Create a new directory for the project and `cd` into it: ```sh @@ -53,8 +53,8 @@ operator-sdk create api \ --resource --controller ``` -### Build the Operator and Bundle Images -For this demo we are going to build a several different versions of this Operator: +### Build the extension and bundle images +For this demo we are going to build a several different versions of this extension: - `v1.0.0-alpha1` - `v1.0.0` - `v1.0.1` @@ -201,15 +201,15 @@ EOF ``` ## Only install/upgrade to `v1.0.z` releases -In this section we are going to create an `Operator` resource that attempts to install our Operator +In this section we are going to create a `ClusterExtension` resource that attempts to install our extension with a version range of `1.0.x`. This version range ensures we only install z-stream releases for `v1.0` excluding pre-release versions. -- Create the `Operator` resource: +- Create the `ClusterExtension` resource: ```sh kubectl apply -f - < Example ```yaml -apiVersion: operators.operatorframework.io/v1alpha1 -kind: Operator +apiVersion: olm.operatorframework.io/v1alpha1 +kind: ClusterExtension metadata: annotations: kubectl.kubernetes.io/last-applied-configuration: | - {"apiVersion":"operators.operatorframework.io/v1alpha1","kind":"Operator","metadata":{"annotations":{},"name":"coastal"},"spec":{"packageName":"coastal","version":"1.0.x"}} + {"apiVersion":"olm.operatorframework.io/v1alpha1","kind":"ClusterExtension","metadata":{"annotations":{},"name":"coastal"},"spec":{"packageName":"coastal","version":"1.0.x"}} creationTimestamp: "2023-11-27T20:29:18Z" generation: 1 name: coastal @@ -261,12 +261,12 @@ status: >[!NOTE] ->The above command establishes a watch on the `Operator` resource and blocks. Once you are done verifying +>The above command establishes a watch on the `ClusterExtension` resource and blocks. Once you are done verifying >the resolution status you can exit the command with `ctrl+c` ### Update the FBC Image to contain a bundle for `v1.0.0` To highlight both the polling functionality and the version range constraints, let's add the `v1.0.0` bundle -of our Operator to the catalog image we created in the preparation steps and push the changes. +of our extension to the catalog image we created in the preparation steps and push the changes. - Add the new bundle to the catalog YAML file ```sh @@ -297,23 +297,23 @@ docker build -t quay.io/operator-framework/coastal-catalog:latest -f catalog.Doc docker push quay.io/operator-framework/coastal-catalog:latest ``` -Shortly, we should see that the `Catalog` resource updates to have a new resolved reference and the `Operator` resource we created previously is successfully installed. +Shortly, we should see that the `Catalog` resource updates to have a new resolved reference and the `ClusterExtension` resource we created previously is successfully installed. -Verify this for the `Operator` with: +Verify this for the `ClusterExtension` with: ```sh -kubectl get operator/coastal -o yaml -w +kubectl get clusterextension/coastal -o yaml -w ```
Example ```yaml -apiVersion: operators.operatorframework.io/v1alpha1 -kind: Operator +apiVersion: olm.operatorframework.io/v1alpha1 +kind: ClusterExtension metadata: annotations: kubectl.kubernetes.io/last-applied-configuration: | - {"apiVersion":"operators.operatorframework.io/v1alpha1","kind":"Operator","metadata":{"annotations":{},"name":"coastal"},"spec":{"packageName":"coastal","version":"1.0.x"}} + {"apiVersion":"olm.operatorframework.io/v1alpha1","kind":"ClusterExtension","metadata":{"annotations":{},"name":"coastal"},"spec":{"packageName":"coastal","version":"1.0.x"}} creationTimestamp: "2023-11-27T20:29:18Z" generation: 1 name: coastal @@ -391,7 +391,7 @@ status:
-Once the `Operator` has been successfully installed we can verify that all the resources created are healthy by checking the `BundleDeployment` resource owned by the `Operator` we created. Verify the `BundleDeployment` has a status condition showing whether or not it is healthy with: +Once the `ClusterExtension` has been successfully installed we can verify that all the resources created are healthy by checking the `BundleDeployment` resource owned by the `ClusterExtension` we created. Verify the `BundleDeployment` has a status condition showing whether or not it is healthy with: ```sh kubectl get bundledeployment/coastal -o yaml -w ``` @@ -407,10 +407,10 @@ metadata: generation: 2 name: coastal ownerReferences: - - apiVersion: operators.operatorframework.io/v1alpha1 + - apiVersion: olm.operatorframework.io/v1alpha1 blockOwnerDeletion: true controller: true - kind: Operator + kind: ClusterExtension name: coastal uid: 48d16240-edae-4904-bad8-58137bebcf8a resourceVersion: "3935" @@ -481,7 +481,7 @@ docker push quay.io/operator-framework/coastal-catalog:latest ``` Similar to the previous procedure, the `Catalog` updates its resolved reference, -but the `Operator` resource remains the same and does not +but the `ClusterExtension` resource remains the same and does not automatically upgrade to `v1.1.0` ### Update the FBC Image to contain a bundle for `v1.0.1` @@ -517,7 +517,7 @@ docker build -t quay.io/operator-framework/coastal-catalog:latest -f catalog.Doc docker push quay.io/operator-framework/coastal-catalog:latest ``` -Once again, we should see the `Catalog` update its resolved reference. This time, we expect that the `Operator` resource is automatically upgraded to the new `v1.0.1` bundle we added. +Once again, we should see the `Catalog` update its resolved reference. This time, we expect that the `ClusterExtension` resource is automatically upgraded to the new `v1.0.1` bundle we added.
Example Catalog @@ -563,15 +563,15 @@ status:
-Example Operator +Example ClusterExtension ```yaml -apiVersion: operators.operatorframework.io/v1alpha1 -kind: Operator +apiVersion: olm.operatorframework.io/v1alpha1 +kind: ClusterExtension metadata: annotations: kubectl.kubernetes.io/last-applied-configuration: | - {"apiVersion":"operators.operatorframework.io/v1alpha1","kind":"Operator","metadata":{"annotations":{},"name":"coastal"},"spec":{"packageName":"coastal","version":"1.0.x"}} + {"apiVersion":"olm.operatorframework.io/v1alpha1","kind":"ClusterExtension","metadata":{"annotations":{},"name":"coastal"},"spec":{"packageName":"coastal","version":"1.0.x"}} creationTimestamp: "2023-11-27T20:29:18Z" generation: 1 name: coastal @@ -602,13 +602,13 @@ status:
## Change version range to pin installs/upgrades to `v1.1.z` releases -Making changes to the `Operator` resource should result in a re-reconciliation of the resource +Making changes to the `ClusterExtension` resource should result in a re-reconciliation of the resource which should result in another resolution loop. To see this, let's update the version range -on our `Operator` resource to `1.1.x` with: +on our `ClusterExtension` resource to `1.1.x` with: ```sh kubectl apply -f - < Example ```yaml -apiVersion: operators.operatorframework.io/v1alpha1 -kind: Operator +apiVersion: olm.operatorframework.io/v1alpha1 +kind: ClusterExtension metadata: annotations: kubectl.kubernetes.io/last-applied-configuration: | - {"apiVersion":"operators.operatorframework.io/v1alpha1","kind":"Operator","metadata":{"annotations":{},"name":"coastal"},"spec":{"packageName":"coastal","version":"1.1.x"}} + {"apiVersion":"olm.operatorframework.io/v1alpha1","kind":"ClusterExtension","metadata":{"annotations":{},"name":"coastal"},"spec":{"packageName":"coastal","version":"1.1.x"}} creationTimestamp: "2023-11-27T20:29:18Z" generation: 2 name: coastal @@ -665,9 +665,9 @@ status: ## Attempting a major version upgrade by changing the version range -The Operator Controller follows semver and prevents automatic upgrades to new major versions when an `Operator`'s `spec.upgradeConstraintPolicy` is set to `Enforce`. New major versions might +The operator-controller follows semver and prevents automatic upgrades to new major versions when a `ClusterExtension`'s `spec.upgradeConstraintPolicy` is set to `Enforce`. New major versions might have breaking changes and could cause problems for users. Let's add a new major -version of our bundle to the catalog image and update the version range on the `Operator`. +version of our bundle to the catalog image and update the version range on the `ClusterExtension`. ### Update the FBC Image to contain a bundle for `v2.0.0` - Add the new bundle to the catalog YAML file @@ -699,14 +699,14 @@ docker build -t quay.io/operator-framework/coastal-catalog:latest -f catalog.Doc docker push quay.io/operator-framework/coastal-catalog:latest ``` -The `Catalog` updates its resolved reference and no changes are applied to the `Operator` resource. +The `Catalog` updates its resolved reference and no changes are applied to the `ClusterExtension` resource. -Updating the `Operator` resource's version range will still result in a resolution failure since the version installed +Updating the `ClusterExtension` resource's version range will still result in a resolution failure since the version installed is a lower major version than specified by the version range. Try it by running: ```sh kubectl apply -f - < Example ```yaml -apiVersion: operators.operatorframework.io/v1alpha1 -kind: Operator +apiVersion: olm.operatorframework.io/v1alpha1 +kind: ClusterExtension metadata: annotations: kubectl.kubernetes.io/last-applied-configuration: | - {"apiVersion":"operators.operatorframework.io/v1alpha1","kind":"Operator","metadata":{"annotations":{},"name":"coastal"},"spec":{"packageName":"coastal","version":"2.0.x"}} + {"apiVersion":"olm.operatorframework.io/v1alpha1","kind":"ClusterExtension","metadata":{"annotations":{},"name":"coastal"},"spec":{"packageName":"coastal","version":"2.0.x"}} creationTimestamp: "2023-11-27T20:29:18Z" generation: 3 name: coastal @@ -765,11 +765,11 @@ status: ## To the escape hatch! To tell operator-controller to ignore the semver policies and allow upgrades across major versions, -set the `Operator`'s `spec.upgradeConstraintPolicy` to `Ignore` with: +set the `ClusterExtension`'s `spec.upgradeConstraintPolicy` to `Ignore` with: ```sh kubectl apply -f - < Example ```yaml -apiVersion: operators.operatorframework.io/v1alpha1 -kind: Operator +apiVersion: olm.operatorframework.io/v1alpha1 +kind: ClusterExtension metadata: annotations: kubectl.kubernetes.io/last-applied-configuration: | - {"apiVersion":"operators.operatorframework.io/v1alpha1","kind":"Operator","metadata":{"annotations":{},"name":"coastal"},"spec":{"packageName":"coastal","upgradeConstraintPolicy":"Ignore","version":"2.0.x"}} + {"apiVersion":"olm.operatorframework.io/v1alpha1","kind":"ClusterExtension","metadata":{"annotations":{},"name":"coastal"},"spec":{"packageName":"coastal","upgradeConstraintPolicy":"Ignore","version":"2.0.x"}} creationTimestamp: "2023-11-27T20:29:18Z" generation: 4 name: coastal @@ -825,12 +825,12 @@ status: ## Attempting to downgrade by changing the version range -We can disable downgrades by setting the `Operator` resource's `spec.UpgradeConstraintPolicy` field to `Enforce`. +We can disable downgrades by setting the `ClusterExtension` resource's `spec.UpgradeConstraintPolicy` field to `Enforce`. To see this, run: ```sh kubectl apply -f - < Example ```yaml -apiVersion: operators.operatorframework.io/v1alpha1 -kind: Operator +apiVersion: olm.operatorframework.io/v1alpha1 +kind: ClusterExtension metadata: annotations: kubectl.kubernetes.io/last-applied-configuration: | - {"apiVersion":"operators.operatorframework.io/v1alpha1","kind":"Operator","metadata":{"annotations":{},"name":"coastal"},"spec":{"packageName":"coastal","upgradeConstraintPolicy":"Enforce","version":"1.1.x"}} + {"apiVersion":"olm.operatorframework.io/v1alpha1","kind":"ClusterExtension","metadata":{"annotations":{},"name":"coastal"},"spec":{"packageName":"coastal","upgradeConstraintPolicy":"Enforce","version":"1.1.x"}} creationTimestamp: "2023-11-27T20:29:18Z" generation: 5 name: coastal @@ -889,12 +889,12 @@ status: ## Back to the escape hatch! -To tell operator-controller to ignore the safety mechanisms and downgrade the `Operator` version, -set the `Operator`'s `spec.upgradeConstraintPolicy` to `Ignore` with: +To tell operator-controller to ignore the safety mechanisms and downgrade the `ClusterExtension` version, +set the `ClusterExtension`'s `spec.upgradeConstraintPolicy` to `Ignore` with: ```sh kubectl apply -f - < Example ```yaml -apiVersion: operators.operatorframework.io/v1alpha1 -kind: Operator +apiVersion: olm.operatorframework.io/v1alpha1 +kind: ClusterExtension metadata: annotations: kubectl.kubernetes.io/last-applied-configuration: | - {"apiVersion":"operators.operatorframework.io/v1alpha1","kind":"Operator","metadata":{"annotations":{},"name":"coastal"},"spec":{"packageName":"coastal","upgradeConstraintPolicy":"Ignore","version":"1.1.x"}} + {"apiVersion":"olm.operatorframework.io/v1alpha1","kind":"ClusterExtension","metadata":{"annotations":{},"name":"coastal"},"spec":{"packageName":"coastal","upgradeConstraintPolicy":"Ignore","version":"1.1.x"}} creationTimestamp: "2023-11-27T20:29:18Z" generation: 6 name: coastal @@ -947,4 +947,4 @@ status: resolvedBundleResource: docker.io/bpalmer/coastal-bundle:v1.1.0 ``` - \ No newline at end of file + diff --git a/docs/drafts/upgrade-support.md b/docs/drafts/upgrade-support.md index a0410c9c..f45a386e 100644 --- a/docs/drafts/upgrade-support.md +++ b/docs/drafts/upgrade-support.md @@ -8,7 +8,7 @@ It also introduces an API to enable independently verified upgrades and downgrad ## Upgrade constraint semantics -As of Operator Controller release 0.8.0, OLM 1.0 supports the following upgrade constraint semantics: +As of operator-controller release 0.8.0, OLM 1.0 supports the following upgrade constraint semantics: * [Semantic Versioning](https://semver.org/) (Semver) * The `replaces` directive from the [legacy OLM 0 semantics](https://olm.operatorframework.io/docs/concepts/olm-architecture/operator-catalog/creating-an-update-graph/#methods-for-specifying-updates) @@ -49,17 +49,17 @@ You must verify and perform upgrades manually in cases where automatic upgrades **Warning:** If you want to force an upgrade manually, you must thoroughly verify the outcome before applying any changes to production workloads. Failure to test and verify the upgrade might lead to catastrophic consequences such as data loss. -As a package admin, if you must upgrade or downgrade to version that might be incompatible with the currently installed version, you can set the `.spec.upgradeConstraintPolicy` field to `Ignore` on the relevant `Operator` resource. +As a package admin, if you must upgrade or downgrade to version that might be incompatible with the currently installed version, you can set the `.spec.upgradeConstraintPolicy` field to `Ignore` on the relevant `ClusterExtension` resource. If you set the field to `Ignore`, no upgrade constraints are set on the package. As a result, you can change the version to any version available in the catalogs for a given package. -Example `Operator` with `.spec.upgradeConstraintPolicy` field set to `Ignore`: +Example `ClusterExtension` with `.spec.upgradeConstraintPolicy` field set to `Ignore`: ```yaml -apiVersion: operators.operatorframework.io/v1alpha1 -kind: Operator +apiVersion: olm.operatorframework.io/v1alpha1 +kind: ClusterExtension metadata: - name: operator-sample + name: extension-sample spec: packageName: argocd-operator version: 0.6.0 diff --git a/docs/drafts/version-ranges.md b/docs/drafts/version-ranges.md index 6fabaf8d..9356c906 100644 --- a/docs/drafts/version-ranges.md +++ b/docs/drafts/version-ranges.md @@ -1,14 +1,14 @@ -# Operator version ranges +# Extension version ranges -This document explains how to specify a version range to install or update an Operator with OLM 1.0. +This document explains how to specify a version range to install or update an extension with OLM 1.0. -You define a version range in an Operator's custom resource (CR) file. +You define a version range in a ClusterExtension's custom resource (CR) file. ## Specifying a version range in the CR -If you specify a version range in the Operator's CR, OLM 1.0 installs or updates the latest version of the Operator that can be resolved within the version range. -The resolved version is the latest version of the Operator that satisfies the dependencies and constraints of the Operator and the environment. -Operator updates within the specified range are automatically installed if they can be resolved successfully. +If you specify a version range in the ClusterExtension's CR, OLM 1.0 installs or updates the latest version of the extension that can be resolved within the version range. +The resolved version is the latest version of the extension that satisfies the dependencies and constraints of the extension and the environment. +Extension updates within the specified range are automatically installed if they can be resolved successfully. Updates are not installed if they are outside of the specified range or if they cannot be resolved successfully. For more information about dependency and constraint resolution in OLM 1.0, see the [Deppy introduction](https://github.com/operator-framework/deppy#introductionhttps://github.com/operator-framework/deppy#introductionhttps://github.com/operator-framework/deppy#introduction) diff --git a/docs/index.md b/docs/index.md index b60c9e15..d6f1fb54 100644 --- a/docs/index.md +++ b/docs/index.md @@ -14,7 +14,7 @@ OLM has been helping to define lifecycles for these extensions in which the exte In the dependency model, extensions can rely on each other for required services that are out of scope of the primary purpose of an extension, allowing each extension to focus on a specific purpose. -OLM also prevents conflicting extensions from running on the cluster, either with conflicting dependency constraints or conflicts in ownership of services provided via APIs. Since cluster extensions need to be supported with an enterprise-grade product lifecycle, there has been a growing need for allowing operator authors to limit installation and upgrade of their extension by specifing addtional environmental constraints as dependencies, primarily to align with what was tested by the operator author's QE processes. In other words, there is an evergrowing ask for OLM to allow the author to enforce these support limitations in the form of additional constraints specified by operator authors in their packaging for OLM. +OLM also prevents conflicting extensions from running on the cluster, either with conflicting dependency constraints or conflicts in ownership of services provided via APIs. Since cluster extensions need to be supported with an enterprise-grade product lifecycle, there has been a growing need for allowing extension authors to limit installation and upgrade of their extension by specifying additional environmental constraints as dependencies, primarily to align with what was tested by the extension author's QE processes. In other words, there is an evergrowing ask for OLM to allow the author to enforce these support limitations in the form of additional constraints specified by extension authors in their packaging for OLM. During their lifecycle on the cluster, OLM also manages the permissions and capabilities extensions have on the cluster as well as the permission and access tenants on the cluster have to the extensions. This is done using the Kubernetes RBAC system, in combination with tenant isolation using Kubernetes namespaces. While the interaction surface of the extensions is solely composed of Kubernetes APIs the extensions define, there is an acute need to rethink the way tenant(i.e consumers of extensions) isolation is achieved. The ask from OLM, is to provide tenant isolation in a more intuitive way than [is implemented in OLM v0](https://olm.operatorframework.io/docs/advanced-tasks/operator-scoping-with-operatorgroups/#docs) @@ -31,5 +31,3 @@ You can reach out to the OLM community for feedbacks/discussions/contributions i * Kubernetes slack channel: [#olm-dev](https://kubernetes.slack.com/messages/olm-dev) * [Operator Framework on Google Groups](https://groups.google.com/forum/#!forum/operator-framework) - - diff --git a/docs/olmv1_roadmap.md b/docs/olmv1_roadmap.md index ddad3693..80b79067 100644 --- a/docs/olmv1_roadmap.md +++ b/docs/olmv1_roadmap.md @@ -42,7 +42,7 @@ As extensions get updated, either automatically or manually, OLM replaces the ol To support multi-tenant environments a request / approval flow is desirable for generally available content within default catalogs. In this model any tenant with enough privilege can discover installable content and can trigger an install request, which can in turn be approved or denied by a more privileged administrative role. Such requests should also have timeouts. Administrators should have the ability to define a list of extensions that are automatically approved at the scope of a namespace. Administrators should be able to get aware of unapproved requested via alerts triggered by the platform. ### F12 - Installed extension discovery (P1) -Unprivileged tenants need to be able to discover installed extensions if they are offering services for them to use in their namespace. OLM needs to provide distinct controls for installed operators which administrators can use to regulate in which namespaces extensions are discoverable by tenants, irrespective of the namespaces in which the operator has permissions on cluster APIs (see F13) +Unprivileged tenants need to be able to discover installed extensions if they are offering services for them to use in their namespace. OLM needs to provide distinct controls for installed extensions which administrators can use to regulate in which namespaces extensions are discoverable by tenants, irrespective of the namespaces in which the extension has permissions on cluster APIs (see F13) ### F13 - Extension permissions management (P1) Administrative personas need to be able to configure in which namespaces in the cluster the extension can get the requested permissions the extension author deems required. The control needs to be independent of the controls in F12. Extensions should always be given permissions to update their own APIs (if they define any) to inform users about potential lack of permissions in their namespace. @@ -60,7 +60,7 @@ Administrative personas need to be able to remove an extension including all the OLM needs to be able to cleanly remove an extension entirely, which means deleting CRDs and other resources on the cluster. In particular this means the removal of all custom resource instances of the CRDs to be deleted and all extensions with a hard dependency. A user needs to actively opt-in to this behavior and OLM has to sequence the successful removal of all affected components and the extension itself. ### F18 - Standalone extension bundle installation (P2) -For local development OLM should allow the installation of an extension by directly referring to the bundle / OCI image in a container registry rather than a package name of an operator in a catalog containing the image in order to simplify testing and delivering hotfixes. +For local development OLM should allow the installation of an extension by directly referring to the bundle / OCI image in a container registry rather than a package name of an extension in a catalog containing the image in order to simplify testing and delivering hotfixes. ### F19 - Extension constraints (P1) OLM needs to allow extensions to specify constraints towards aspects of the running cluster and other currently installed or future extensions. Aspects of the running cluster should include software version, resource utilization, overall resource availability and state of configuration settings. These constraints need to be evaluated when extensions are attempted to be installed or updated. @@ -75,7 +75,7 @@ OLM should provide a way for extensions to report an aggregate health state with OLM should always try its best to resolve installation and update requests with the currently available and healthy set catalogs to resolve against. Intermittently or permanently failed catalogs should not block resolution for installation and updates. Fulfilling user requests is valued higher than determinism of results. ### F23 - Opt-in to fallback / rollback (P2) -OLM should allow operator developers to specify whether or not it is safe to rollback from a particular current version of the operator to an author-specified previous version, once an extension update has passed pre-flights checks in F10 but subsequently failed to become available or carry out a migration. In these cases OLM should allow the administrator to downgrade the operator to the specific previous version. OLM should also respect this downgrade path when conducting updates that fail and use it to fail back to the previous version of the operator indicating that the downgrade path is supported. Extension uptime is an important goal of OLM. +OLM should allow extension developers to specify whether or not it is safe to rollback from a particular current version of the extension to an author-specified previous version, once an extension update has passed pre-flights checks in F10 but subsequently failed to become available or carry out a migration. In these cases OLM should allow the administrator to downgrade the extension to the specific previous version. OLM should also respect this downgrade path when conducting updates that fail and use it to fail back to the previous version of the extension indicating that the downgrade path is supported. Extension uptime is an important goal of OLM. ### F24 - Extension Overrides (P2) Components deployed as part of extensions will require user-provided modifications at runtime in order to aid features like placement, networking configuration, proxy configuration etc. that require customization of volume mounts, annotations, labels, environment variables, taints, tolerations, node selectors, affinity, and resources. OLM should support accepting these customizations to the extension as input from the user prior or after the installation of an extension and apply them to applicable objects such as Deployments, StatefulSets, ReplicatSets. @@ -84,7 +84,7 @@ Components deployed as part of extensions will require user-provided modificatio Extensions should be able to control their readiness for updates. An extension could be on a critical path or in a state where updates would lead to disruption or worst-case: outages. OLM should respect these update readiness signals and allow the extension to signal readiness differentiated to what nature the update is based on semantic versioning rules, i.e. patch updates vs. minor or major updates. Once the signal is encountered, OLM should block the update until the signal disappears. ### F26 - Canary Style Rollouts (P3) -OLM should have an opinion about how administrators can carry out roll outs of new extension versions that coexist with already installed versions of the extension, in particular if the extension only ships a controller. While conflicting CRDs cannot co-exist, controllers that only selectively reconcile objects (Ingress operator pattern) can. OLM should support these deployment styles while ensuring integrity of cluster-level extensions like CRDs. +OLM should have an opinion about how administrators can carry out roll outs of new extension versions that coexist with already installed versions of the extension, in particular if the extension only ships a controller. While conflicting CRDs cannot co-exist, controllers that only selectively reconcile objects (Ingress controller pattern) can. OLM should support these deployment styles while ensuring integrity of cluster-level extensions like CRDs. ### F27 - Pluggable certificate management (P2) OLM should rely on other controllers to create and lifecycle TLS certificates required to drive the functionality of certain extensions, like webhooks that need to trust / need to be trusted by the cluster's API server. OLM should not implement any certificate handling itself. In a first implementation support should be established for cert-manager. @@ -100,13 +100,13 @@ While the underlying implementation of the functional requirements can be carrie In many cases OLM APIs will not be used by a human via a CLI or GUI interactively but declaratively through a GitOps pipeline. This means the primary OLM API to lifecycle an extension cannot leak abstractions to other APIs for initial deployment or reconfiguration. Modifications must not require conditional lookup or modifications of other objects on the cluster that are created as part of the declarative intent stored in git in the form of YAML manifest files. ### B3 - Declarative API (P1) -As an operator itself, OLMs API controls have to allow for operations to be carried out solely declaratively. This mandates continuous reconciliation and eventual consistency of desired state. OLM should not conduct one-off operations. OLM should not require either clean up of failed operations or restating intent to retry a failed operation (with the exception of F11). +As an extension itself, OLMs API controls have to allow for operations to be carried out solely declaratively. This mandates continuous reconciliation and eventual consistency of desired state. OLM should not conduct one-off operations. OLM should not require either clean up of failed operations or restating intent to retry a failed operation (with the exception of F11). ### B4 - Event trail (P2) OLM should make heavy use of Kubernetes events to leave an audit trail of tasks and actions carried out on the cluster. For expected failure scenarios administrators should not need to consult the OLM controller logs for debugging but solely rely on events and status conditions (see also B6). ### B5 - Force overrides (P1) -While OLM has a lot of opinions about safe operations with cluster extensions they do not apply all the time since OLM cannot possibly foresee how extensions behave at runtime. It needs to yield to the user in cases where they have more certainty about what's going to happen based on their background knowledge of the cluster or the operator. It should offer ways to force-override decisions that OLM made that block the user from proceeding in a certain direction, especially in the areas of extension installation, removal and updates. +While OLM has a lot of opinions about safe operations with cluster extensions they do not apply all the time since OLM cannot possibly foresee how extensions behave at runtime. It needs to yield to the user in cases where they have more certainty about what's going to happen based on their background knowledge of the cluster or the extension. It should offer ways to force-override decisions that OLM made that block the user from proceeding in a certain direction, especially in the areas of extension installation, removal and updates. ### B6 - Human-readable status extensions information (P2) Whenever OLM is in the process of or having reached or failed to reach a desired state it needs to update the user about what is happening / what has happened without assuming knowledge about OLM internal or implementation details. @@ -133,10 +133,10 @@ OLM 1.0 does not support managing bundles or extension versions that do not supp - OLM will move to a descoped, cluster-wide singleton model for cluster extensions, extension management isn’t namespaced ## Constraints -- Only operator bundles with “AllNamespace” mode installation support can be lifecycled with the new APIs / flows in OLM 1.0 +- Only extension bundles with “AllNamespace” mode installation support can be lifecycled with the new APIs / flows in OLM 1.0 ## Dependencies -- "F13 - Extension permissions management (P1)" and "F12 - Installed extension discovery (P1)" will land prior to the GA of OLM 1.0 to unblock most operators that do not support AllNamespace installation mode today. +- "F13 - Extension permissions management (P1)" and "F12 - Installed extension discovery (P1)" will land prior to the GA of OLM 1.0 to unblock most extensions that do not support AllNamespace installation mode today. ## Migration - A new set of APIs is introduced in parallel to the existing set of APIs @@ -149,7 +149,7 @@ OLM 1.0 does not support managing bundles or extension versions that do not supp - Migration scripting is provided to mass-convert existing installed extensions (“Subscription” / “OperatorGroup” objects) on existing clusters to the new OLM 1.0 model assuming they are compatible -- Operator authors that are also SRE/Managed PaaS administrators are incentivized to make their operator compatible with the requirements of OLM 1.0 to reap the operational benefits +- Extension authors that are also SRE/Managed PaaS administrators are incentivized to make their extension compatible with the requirements of OLM 1.0 to reap the operational benefits # TODO - Definition of "extension" diff --git a/internal/catalogmetadata/filter/filter_test.go b/internal/catalogmetadata/filter/filter_test.go index 942b76fa..b6817009 100644 --- a/internal/catalogmetadata/filter/filter_test.go +++ b/internal/catalogmetadata/filter/filter_test.go @@ -12,9 +12,9 @@ import ( func TestFilter(t *testing.T) { in := []*catalogmetadata.Bundle{ - {Bundle: declcfg.Bundle{Name: "operator1.v1", Package: "operator1", Image: "fake1"}}, - {Bundle: declcfg.Bundle{Name: "operator1.v2", Package: "operator1", Image: "fake2"}}, - {Bundle: declcfg.Bundle{Name: "operator2.v1", Package: "operator2", Image: "fake1"}}, + {Bundle: declcfg.Bundle{Name: "extension1.v1", Package: "extension1", Image: "fake1"}}, + {Bundle: declcfg.Bundle{Name: "extension1.v2", Package: "extension1", Image: "fake2"}}, + {Bundle: declcfg.Bundle{Name: "extension2.v1", Package: "extension2", Image: "fake1"}}, } for _, tt := range []struct { @@ -25,49 +25,49 @@ func TestFilter(t *testing.T) { { name: "simple filter with one predicate", predicate: func(bundle *catalogmetadata.Bundle) bool { - return bundle.Name == "operator1.v1" + return bundle.Name == "extension1.v1" }, want: []*catalogmetadata.Bundle{ - {Bundle: declcfg.Bundle{Name: "operator1.v1", Package: "operator1", Image: "fake1"}}, + {Bundle: declcfg.Bundle{Name: "extension1.v1", Package: "extension1", Image: "fake1"}}, }, }, { name: "filter with Not predicate", predicate: filter.Not(func(bundle *catalogmetadata.Bundle) bool { - return bundle.Name == "operator1.v1" + return bundle.Name == "extension1.v1" }), want: []*catalogmetadata.Bundle{ - {Bundle: declcfg.Bundle{Name: "operator1.v2", Package: "operator1", Image: "fake2"}}, - {Bundle: declcfg.Bundle{Name: "operator2.v1", Package: "operator2", Image: "fake1"}}, + {Bundle: declcfg.Bundle{Name: "extension1.v2", Package: "extension1", Image: "fake2"}}, + {Bundle: declcfg.Bundle{Name: "extension2.v1", Package: "extension2", Image: "fake1"}}, }, }, { name: "filter with And predicate", predicate: filter.And( func(bundle *catalogmetadata.Bundle) bool { - return bundle.Name == "operator1.v1" + return bundle.Name == "extension1.v1" }, func(bundle *catalogmetadata.Bundle) bool { return bundle.Image == "fake1" }, ), want: []*catalogmetadata.Bundle{ - {Bundle: declcfg.Bundle{Name: "operator1.v1", Package: "operator1", Image: "fake1"}}, + {Bundle: declcfg.Bundle{Name: "extension1.v1", Package: "extension1", Image: "fake1"}}, }, }, { name: "filter with Or predicate", predicate: filter.Or( func(bundle *catalogmetadata.Bundle) bool { - return bundle.Name == "operator1.v1" + return bundle.Name == "extension1.v1" }, func(bundle *catalogmetadata.Bundle) bool { return bundle.Image == "fake1" }, ), want: []*catalogmetadata.Bundle{ - {Bundle: declcfg.Bundle{Name: "operator1.v1", Package: "operator1", Image: "fake1"}}, - {Bundle: declcfg.Bundle{Name: "operator2.v1", Package: "operator2", Image: "fake1"}}, + {Bundle: declcfg.Bundle{Name: "extension1.v1", Package: "extension1", Image: "fake1"}}, + {Bundle: declcfg.Bundle{Name: "extension2.v1", Package: "extension2", Image: "fake1"}}, }, }, } { diff --git a/internal/conditionsets/conditionsets.go b/internal/conditionsets/conditionsets.go index 8661b230..fa408714 100644 --- a/internal/conditionsets/conditionsets.go +++ b/internal/conditionsets/conditionsets.go @@ -16,9 +16,9 @@ limitations under the License. package conditionsets -// ConditionTypes is the full set of Operator condition Types. -// ConditionReasons is the full set of Operator condition Reasons. +// ConditionTypes is the full set of ClusterExtension condition Types. +// ConditionReasons is the full set of ClusterExtension condition Reasons. // -// NOTE: These are populated by init() in api/v1alpha1/operator_types.go +// NOTE: These are populated by init() in api/v1alpha1/clusterextension_types.go var ConditionTypes []string var ConditionReasons []string diff --git a/internal/controllers/admission_test.go b/internal/controllers/admission_test.go index 6f0c41c9..bb607b60 100644 --- a/internal/controllers/admission_test.go +++ b/internal/controllers/admission_test.go @@ -7,37 +7,37 @@ import ( "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - operatorsv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" + ocv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" ) -func operator(spec operatorsv1alpha1.OperatorSpec) *operatorsv1alpha1.Operator { - return &operatorsv1alpha1.Operator{ +func buildClusterExtension(spec ocv1alpha1.ClusterExtensionSpec) *ocv1alpha1.ClusterExtension { + return &ocv1alpha1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{ - GenerateName: "test-operator", + GenerateName: "test-extension", }, Spec: spec, } } -var operatorData = []struct { - spec *operatorsv1alpha1.Operator +var clusterExtensionData = []struct { + spec *ocv1alpha1.ClusterExtension comment string errMsg string }{ { - operator(operatorsv1alpha1.OperatorSpec{}), - "operator spec is empty", + buildClusterExtension(ocv1alpha1.ClusterExtensionSpec{}), + "spec is empty", "spec.packageName in body should match '^[a-z0-9]+(-[a-z0-9]+)*$'", }, { - operator(operatorsv1alpha1.OperatorSpec{ + buildClusterExtension(ocv1alpha1.ClusterExtensionSpec{ PackageName: "this-is-a-really-long-package-name-that-is-greater-than-48-characters", }), "long package name", "spec.packageName: Too long: may not be longer than 48", }, { - operator(operatorsv1alpha1.OperatorSpec{ + buildClusterExtension(ocv1alpha1.ClusterExtensionSpec{ PackageName: "package", Version: "1234567890.1234567890.12345678901234567890123456789012345678901234", }), @@ -45,7 +45,7 @@ var operatorData = []struct { "spec.version: Too long: may not be longer than 64", }, { - operator(operatorsv1alpha1.OperatorSpec{ + buildClusterExtension(ocv1alpha1.ClusterExtensionSpec{ PackageName: "package", Channel: "longname01234567890123456789012345678901234567890", }), @@ -54,13 +54,13 @@ var operatorData = []struct { }, } -func TestOperatorSpecs(t *testing.T) { +func TestClusterExtensionSpecs(t *testing.T) { t.Parallel() ctx, cancel := context.WithCancel(context.Background()) t.Cleanup(cancel) - for _, od := range operatorData { - d := od + for _, ed := range clusterExtensionData { + d := ed t.Run(d.comment, func(t *testing.T) { t.Parallel() cl := newClient(t) @@ -71,7 +71,7 @@ func TestOperatorSpecs(t *testing.T) { } } -func TestOperatorInvalidSemver(t *testing.T) { +func TestClusterExtensionInvalidSemver(t *testing.T) { t.Parallel() ctx, cancel := context.WithCancel(context.Background()) t.Cleanup(cancel) @@ -102,7 +102,7 @@ func TestOperatorInvalidSemver(t *testing.T) { t.Run(d, func(t *testing.T) { t.Parallel() cl := newClient(t) - err := cl.Create(ctx, operator(operatorsv1alpha1.OperatorSpec{ + err := cl.Create(ctx, buildClusterExtension(ocv1alpha1.ClusterExtensionSpec{ PackageName: "package", Version: d, })) @@ -113,7 +113,7 @@ func TestOperatorInvalidSemver(t *testing.T) { } } -func TestOperatorValidSemver(t *testing.T) { +func TestClusterExtensionValidSemver(t *testing.T) { t.Parallel() ctx, cancel := context.WithCancel(context.Background()) t.Cleanup(cancel) @@ -157,7 +157,7 @@ func TestOperatorValidSemver(t *testing.T) { t.Run(d, func(t *testing.T) { t.Parallel() cl := newClient(t) - op := operator(operatorsv1alpha1.OperatorSpec{ + op := buildClusterExtension(ocv1alpha1.ClusterExtensionSpec{ PackageName: "package", Version: d, }) @@ -167,7 +167,7 @@ func TestOperatorValidSemver(t *testing.T) { } } -func TestOperatorInvalidChannel(t *testing.T) { +func TestClusterExtensionInvalidChannel(t *testing.T) { t.Parallel() ctx, cancel := context.WithCancel(context.Background()) t.Cleanup(cancel) @@ -187,7 +187,7 @@ func TestOperatorInvalidChannel(t *testing.T) { t.Run(d, func(t *testing.T) { t.Parallel() cl := newClient(t) - err := cl.Create(ctx, operator(operatorsv1alpha1.OperatorSpec{ + err := cl.Create(ctx, buildClusterExtension(ocv1alpha1.ClusterExtensionSpec{ PackageName: "package", Channel: d, })) @@ -197,7 +197,7 @@ func TestOperatorInvalidChannel(t *testing.T) { } } -func TestOperatorValidChannel(t *testing.T) { +func TestClusterExtensionValidChannel(t *testing.T) { t.Parallel() ctx, cancel := context.WithCancel(context.Background()) t.Cleanup(cancel) @@ -212,11 +212,11 @@ func TestOperatorValidChannel(t *testing.T) { t.Run(d, func(t *testing.T) { t.Parallel() cl := newClient(t) - op := operator(operatorsv1alpha1.OperatorSpec{ + ext := buildClusterExtension(ocv1alpha1.ClusterExtensionSpec{ PackageName: "package", Channel: d, }) - err := cl.Create(ctx, op) + err := cl.Create(ctx, ext) require.NoErrorf(t, err, "unexpected error creating valid channel %q: %w", d, err) }) } diff --git a/internal/controllers/operator_controller.go b/internal/controllers/clusterextension_controller.go similarity index 64% rename from internal/controllers/operator_controller.go rename to internal/controllers/clusterextension_controller.go index f9443f83..56c256e3 100644 --- a/internal/controllers/operator_controller.go +++ b/internal/controllers/clusterextension_controller.go @@ -39,7 +39,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/reconcile" - operatorsv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" + ocv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" "github.com/operator-framework/operator-controller/internal/catalogmetadata" "github.com/operator-framework/operator-controller/internal/controllers/validators" olmvariables "github.com/operator-framework/operator-controller/internal/resolution/variables" @@ -51,43 +51,43 @@ type BundleProvider interface { Bundles(ctx context.Context) ([]*catalogmetadata.Bundle, error) } -// OperatorReconciler reconciles a Operator object -type OperatorReconciler struct { +// ClusterExtensionReconciler reconciles a ClusterExtension object +type ClusterExtensionReconciler struct { client.Client BundleProvider BundleProvider Scheme *runtime.Scheme Resolver *solver.Solver } -//+kubebuilder:rbac:groups=operators.operatorframework.io,resources=operators,verbs=get;list;watch -//+kubebuilder:rbac:groups=operators.operatorframework.io,resources=operators/status,verbs=get;update;patch -//+kubebuilder:rbac:groups=operators.operatorframework.io,resources=operators/finalizers,verbs=update +//+kubebuilder:rbac:groups=olm.operatorframework.io,resources=clusterextensions,verbs=get;list;watch +//+kubebuilder:rbac:groups=olm.operatorframework.io,resources=clusterextensions/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=olm.operatorframework.io,resources=clusterextensions/finalizers,verbs=update //+kubebuilder:rbac:groups=core.rukpak.io,resources=bundledeployments,verbs=get;list;watch;create;update;patch //+kubebuilder:rbac:groups=catalogd.operatorframework.io,resources=catalogs,verbs=list;watch //+kubebuilder:rbac:groups=catalogd.operatorframework.io,resources=catalogmetadata,verbs=list;watch -func (r *OperatorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { +func (r *ClusterExtensionReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { l := log.FromContext(ctx).WithName("operator-controller") l.V(1).Info("starting") defer l.V(1).Info("ending") - var existingOp = &operatorsv1alpha1.Operator{} - if err := r.Get(ctx, req.NamespacedName, existingOp); err != nil { + var existingExt = &ocv1alpha1.ClusterExtension{} + if err := r.Get(ctx, req.NamespacedName, existingExt); err != nil { return ctrl.Result{}, client.IgnoreNotFound(err) } - reconciledOp := existingOp.DeepCopy() - res, reconcileErr := r.reconcile(ctx, reconciledOp) + reconciledExt := existingExt.DeepCopy() + res, reconcileErr := r.reconcile(ctx, reconciledExt) // Do checks before any Update()s, as Update() may modify the resource structure! - updateStatus := !equality.Semantic.DeepEqual(existingOp.Status, reconciledOp.Status) - updateFinalizers := !equality.Semantic.DeepEqual(existingOp.Finalizers, reconciledOp.Finalizers) - unexpectedFieldsChanged := checkForUnexpectedFieldChange(*existingOp, *reconciledOp) + updateStatus := !equality.Semantic.DeepEqual(existingExt.Status, reconciledExt.Status) + updateFinalizers := !equality.Semantic.DeepEqual(existingExt.Finalizers, reconciledExt.Finalizers) + unexpectedFieldsChanged := checkForUnexpectedFieldChange(*existingExt, *reconciledExt) if updateStatus { - if updateErr := r.Status().Update(ctx, reconciledOp); updateErr != nil { + if updateErr := r.Status().Update(ctx, reconciledExt); updateErr != nil { return res, utilerrors.NewAggregate([]error{reconcileErr, updateErr}) } } @@ -97,7 +97,7 @@ func (r *OperatorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c } if updateFinalizers { - if updateErr := r.Update(ctx, reconciledOp); updateErr != nil { + if updateErr := r.Update(ctx, reconciledExt); updateErr != nil { return res, utilerrors.NewAggregate([]error{reconcileErr, updateErr}) } } @@ -106,8 +106,8 @@ func (r *OperatorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c } // Compare resources - ignoring status & metadata.finalizers -func checkForUnexpectedFieldChange(a, b operatorsv1alpha1.Operator) bool { - a.Status, b.Status = operatorsv1alpha1.OperatorStatus{}, operatorsv1alpha1.OperatorStatus{} +func checkForUnexpectedFieldChange(a, b ocv1alpha1.ClusterExtension) bool { + a.Status, b.Status = ocv1alpha1.ClusterExtensionStatus{}, ocv1alpha1.ClusterExtensionStatus{} a.Finalizers, b.Finalizers = []string{}, []string{} return !equality.Semantic.DeepEqual(a, b) } @@ -119,99 +119,99 @@ func checkForUnexpectedFieldChange(a, b operatorsv1alpha1.Operator) bool { // to return different results (e.g. requeue). // //nolint:unparam -func (r *OperatorReconciler) reconcile(ctx context.Context, op *operatorsv1alpha1.Operator) (ctrl.Result, error) { +func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alpha1.ClusterExtension) (ctrl.Result, error) { // validate spec - if err := validators.ValidateOperatorSpec(op); err != nil { + if err := validators.ValidateClusterExtensionSpec(ext); err != nil { // Set the TypeInstalled condition to Unknown to indicate that the resolution // hasn't been attempted yet, due to the spec being invalid. - op.Status.InstalledBundleResource = "" - setInstalledStatusConditionUnknown(&op.Status.Conditions, "installation has not been attempted as spec is invalid", op.GetGeneration()) + ext.Status.InstalledBundleResource = "" + setInstalledStatusConditionUnknown(&ext.Status.Conditions, "installation has not been attempted as spec is invalid", ext.GetGeneration()) // Set the TypeResolved condition to Unknown to indicate that the resolution // hasn't been attempted yet, due to the spec being invalid. - op.Status.ResolvedBundleResource = "" - setResolvedStatusConditionUnknown(&op.Status.Conditions, "validation has not been attempted as spec is invalid", op.GetGeneration()) + ext.Status.ResolvedBundleResource = "" + setResolvedStatusConditionUnknown(&ext.Status.Conditions, "validation has not been attempted as spec is invalid", ext.GetGeneration()) return ctrl.Result{}, nil } // gather vars for resolution vars, err := r.variables(ctx) if err != nil { - op.Status.InstalledBundleResource = "" - setInstalledStatusConditionUnknown(&op.Status.Conditions, "installation has not been attempted due to failure to gather data for resolution", op.GetGeneration()) - op.Status.ResolvedBundleResource = "" - setResolvedStatusConditionFailed(&op.Status.Conditions, err.Error(), op.GetGeneration()) + ext.Status.InstalledBundleResource = "" + setInstalledStatusConditionUnknown(&ext.Status.Conditions, "installation has not been attempted due to failure to gather data for resolution", ext.GetGeneration()) + ext.Status.ResolvedBundleResource = "" + setResolvedStatusConditionFailed(&ext.Status.Conditions, err.Error(), ext.GetGeneration()) return ctrl.Result{}, err } // run resolution selection, err := r.Resolver.Solve(vars) if err != nil { - op.Status.InstalledBundleResource = "" - setInstalledStatusConditionUnknown(&op.Status.Conditions, "installation has not been attempted as resolution failed", op.GetGeneration()) - op.Status.ResolvedBundleResource = "" - setResolvedStatusConditionFailed(&op.Status.Conditions, err.Error(), op.GetGeneration()) + ext.Status.InstalledBundleResource = "" + setInstalledStatusConditionUnknown(&ext.Status.Conditions, "installation has not been attempted as resolution failed", ext.GetGeneration()) + ext.Status.ResolvedBundleResource = "" + setResolvedStatusConditionFailed(&ext.Status.Conditions, err.Error(), ext.GetGeneration()) return ctrl.Result{}, err } // lookup the bundle in the solution that corresponds to the - // Operator's desired package name. - bundle, err := r.bundleFromSolution(selection, op.Spec.PackageName) + // ClusterExtension's desired package name. + bundle, err := r.bundleFromSolution(selection, ext.Spec.PackageName) if err != nil { - op.Status.InstalledBundleResource = "" - setInstalledStatusConditionUnknown(&op.Status.Conditions, "installation has not been attempted as resolution failed", op.GetGeneration()) - op.Status.ResolvedBundleResource = "" - setResolvedStatusConditionFailed(&op.Status.Conditions, err.Error(), op.GetGeneration()) + ext.Status.InstalledBundleResource = "" + setInstalledStatusConditionUnknown(&ext.Status.Conditions, "installation has not been attempted as resolution failed", ext.GetGeneration()) + ext.Status.ResolvedBundleResource = "" + setResolvedStatusConditionFailed(&ext.Status.Conditions, err.Error(), ext.GetGeneration()) return ctrl.Result{}, err } // Now we can set the Resolved Condition, and the resolvedBundleSource field to the bundle.Image value. - op.Status.ResolvedBundleResource = bundle.Image - setResolvedStatusConditionSuccess(&op.Status.Conditions, fmt.Sprintf("resolved to %q", bundle.Image), op.GetGeneration()) + ext.Status.ResolvedBundleResource = bundle.Image + setResolvedStatusConditionSuccess(&ext.Status.Conditions, fmt.Sprintf("resolved to %q", bundle.Image), ext.GetGeneration()) mediaType, err := bundle.MediaType() if err != nil { - setInstalledStatusConditionFailed(&op.Status.Conditions, err.Error(), op.GetGeneration()) + setInstalledStatusConditionFailed(&ext.Status.Conditions, err.Error(), ext.GetGeneration()) return ctrl.Result{}, err } bundleProvisioner, err := mapBundleMediaTypeToBundleProvisioner(mediaType) if err != nil { - setInstalledStatusConditionFailed(&op.Status.Conditions, err.Error(), op.GetGeneration()) + setInstalledStatusConditionFailed(&ext.Status.Conditions, err.Error(), ext.GetGeneration()) return ctrl.Result{}, err } // Ensure a BundleDeployment exists with its bundle source from the bundle // image we just looked up in the solution. - dep := r.generateExpectedBundleDeployment(*op, bundle.Image, bundleProvisioner) + dep := r.generateExpectedBundleDeployment(*ext, bundle.Image, bundleProvisioner) if err := r.ensureBundleDeployment(ctx, dep); err != nil { - // originally Reason: operatorsv1alpha1.ReasonInstallationFailed - op.Status.InstalledBundleResource = "" - setInstalledStatusConditionFailed(&op.Status.Conditions, err.Error(), op.GetGeneration()) + // originally Reason: ocv1alpha1.ReasonInstallationFailed + ext.Status.InstalledBundleResource = "" + setInstalledStatusConditionFailed(&ext.Status.Conditions, err.Error(), ext.GetGeneration()) return ctrl.Result{}, err } // convert existing unstructured object into bundleDeployment for easier mapping of status. existingTypedBundleDeployment := &rukpakv1alpha1.BundleDeployment{} if err := runtime.DefaultUnstructuredConverter.FromUnstructured(dep.UnstructuredContent(), existingTypedBundleDeployment); err != nil { - // originally Reason: operatorsv1alpha1.ReasonInstallationStatusUnknown - op.Status.InstalledBundleResource = "" - setInstalledStatusConditionUnknown(&op.Status.Conditions, err.Error(), op.GetGeneration()) + // originally Reason: ocv1alpha1.ReasonInstallationStatusUnknown + ext.Status.InstalledBundleResource = "" + setInstalledStatusConditionUnknown(&ext.Status.Conditions, err.Error(), ext.GetGeneration()) return ctrl.Result{}, err } // Let's set the proper Installed condition and InstalledBundleResource field based on the // existing BundleDeployment object status. - mapBDStatusToInstalledCondition(existingTypedBundleDeployment, op) + mapBDStatusToInstalledCondition(existingTypedBundleDeployment, ext) - // set the status of the operator based on the respective bundle deployment status conditions. + // set the status of the cluster extension based on the respective bundle deployment status conditions. return ctrl.Result{}, nil } -func (r *OperatorReconciler) variables(ctx context.Context) ([]deppy.Variable, error) { +func (r *ClusterExtensionReconciler) variables(ctx context.Context) ([]deppy.Variable, error) { allBundles, err := r.BundleProvider.Bundles(ctx) if err != nil { return nil, err } - operatorList := operatorsv1alpha1.OperatorList{} - if err := r.Client.List(ctx, &operatorList); err != nil { + clusterExtensionList := ocv1alpha1.ClusterExtensionList{} + if err := r.Client.List(ctx, &clusterExtensionList); err != nil { return nil, err } bundleDeploymentList := rukpakv1alpha1.BundleDeploymentList{} @@ -219,23 +219,23 @@ func (r *OperatorReconciler) variables(ctx context.Context) ([]deppy.Variable, e return nil, err } - return GenerateVariables(allBundles, operatorList.Items, bundleDeploymentList.Items) + return GenerateVariables(allBundles, clusterExtensionList.Items, bundleDeploymentList.Items) } -func mapBDStatusToInstalledCondition(existingTypedBundleDeployment *rukpakv1alpha1.BundleDeployment, op *operatorsv1alpha1.Operator) { +func mapBDStatusToInstalledCondition(existingTypedBundleDeployment *rukpakv1alpha1.BundleDeployment, ext *ocv1alpha1.ClusterExtension) { bundleDeploymentReady := apimeta.FindStatusCondition(existingTypedBundleDeployment.Status.Conditions, rukpakv1alpha1.TypeInstalled) if bundleDeploymentReady == nil { - op.Status.InstalledBundleResource = "" - setInstalledStatusConditionUnknown(&op.Status.Conditions, "bundledeployment status is unknown", op.GetGeneration()) + ext.Status.InstalledBundleResource = "" + setInstalledStatusConditionUnknown(&ext.Status.Conditions, "bundledeployment status is unknown", ext.GetGeneration()) return } if bundleDeploymentReady.Status != metav1.ConditionTrue { - op.Status.InstalledBundleResource = "" + ext.Status.InstalledBundleResource = "" setInstalledStatusConditionFailed( - &op.Status.Conditions, + &ext.Status.Conditions, fmt.Sprintf("bundledeployment not ready: %s", bundleDeploymentReady.Message), - op.GetGeneration(), + ext.GetGeneration(), ) return } @@ -243,31 +243,31 @@ func mapBDStatusToInstalledCondition(existingTypedBundleDeployment *rukpakv1alph bundleDeploymentSource := existingTypedBundleDeployment.Spec.Template.Spec.Source switch bundleDeploymentSource.Type { case rukpakv1alpha1.SourceTypeImage: - op.Status.InstalledBundleResource = bundleDeploymentSource.Image.Ref + ext.Status.InstalledBundleResource = bundleDeploymentSource.Image.Ref setInstalledStatusConditionSuccess( - &op.Status.Conditions, + &ext.Status.Conditions, fmt.Sprintf("installed from %q", bundleDeploymentSource.Image.Ref), - op.GetGeneration(), + ext.GetGeneration(), ) case rukpakv1alpha1.SourceTypeGit: resource := bundleDeploymentSource.Git.Repository + "@" + bundleDeploymentSource.Git.Ref.Commit - op.Status.InstalledBundleResource = resource + ext.Status.InstalledBundleResource = resource setInstalledStatusConditionSuccess( - &op.Status.Conditions, + &ext.Status.Conditions, fmt.Sprintf("installed from %q", resource), - op.GetGeneration(), + ext.GetGeneration(), ) default: - op.Status.InstalledBundleResource = "" + ext.Status.InstalledBundleResource = "" setInstalledStatusConditionUnknown( - &op.Status.Conditions, + &ext.Status.Conditions, fmt.Sprintf("unknown bundledeployment source type %q", bundleDeploymentSource.Type), - op.GetGeneration(), + ext.GetGeneration(), ) } } -func (r *OperatorReconciler) bundleFromSolution(selection []deppy.Variable, packageName string) (*catalogmetadata.Bundle, error) { +func (r *ClusterExtensionReconciler) bundleFromSolution(selection []deppy.Variable, packageName string) (*catalogmetadata.Bundle, error) { for _, variable := range selection { switch v := variable.(type) { case *olmvariables.BundleVariable: @@ -280,7 +280,7 @@ func (r *OperatorReconciler) bundleFromSolution(selection []deppy.Variable, pack return nil, fmt.Errorf("bundle for package %q not found in solution", packageName) } -func (r *OperatorReconciler) generateExpectedBundleDeployment(o operatorsv1alpha1.Operator, bundlePath string, bundleProvisioner string) *unstructured.Unstructured { +func (r *ClusterExtensionReconciler) generateExpectedBundleDeployment(o ocv1alpha1.ClusterExtension, bundlePath string, bundleProvisioner string) *unstructured.Unstructured { // We use unstructured here to avoid problems of serializing default values when sending patches to the apiserver. // If you use a typed object, any default values from that struct get serialized into the JSON patch, which could // cause unrelated fields to be patched back to the default value even though that isn't the intention. Using an @@ -312,8 +312,8 @@ func (r *OperatorReconciler) generateExpectedBundleDeployment(o operatorsv1alpha }} bd.SetOwnerReferences([]metav1.OwnerReference{ { - APIVersion: operatorsv1alpha1.GroupVersion.String(), - Kind: "Operator", + APIVersion: ocv1alpha1.GroupVersion.String(), + Kind: "ClusterExtension", Name: o.Name, UID: o.UID, Controller: pointer.Bool(true), @@ -324,11 +324,11 @@ func (r *OperatorReconciler) generateExpectedBundleDeployment(o operatorsv1alpha } // SetupWithManager sets up the controller with the Manager. -func (r *OperatorReconciler) SetupWithManager(mgr ctrl.Manager) error { +func (r *ClusterExtensionReconciler) SetupWithManager(mgr ctrl.Manager) error { err := ctrl.NewControllerManagedBy(mgr). - For(&operatorsv1alpha1.Operator{}). + For(&ocv1alpha1.ClusterExtension{}). Watches(&catalogd.Catalog{}, - handler.EnqueueRequestsFromMapFunc(operatorRequestsForCatalog(mgr.GetClient(), mgr.GetLogger()))). + handler.EnqueueRequestsFromMapFunc(clusterExtensionRequestsForCatalog(mgr.GetClient(), mgr.GetLogger()))). Owns(&rukpakv1alpha1.BundleDeployment{}). Complete(r) @@ -338,12 +338,12 @@ func (r *OperatorReconciler) SetupWithManager(mgr ctrl.Manager) error { return nil } -func (r *OperatorReconciler) ensureBundleDeployment(ctx context.Context, desiredBundleDeployment *unstructured.Unstructured) error { - // TODO: what if there happens to be an unrelated BD with the same name as the Operator? +func (r *ClusterExtensionReconciler) ensureBundleDeployment(ctx context.Context, desiredBundleDeployment *unstructured.Unstructured) error { + // TODO: what if there happens to be an unrelated BD with the same name as the ClusterExtension? // we should probably also check to see if there's an owner reference and/or a label set - // that we expect only to ever be used by the operator controller. That way, we don't + // that we expect only to ever be used by the operator-controller. That way, we don't // automatically and silently adopt and change a BD that the user doens't intend to be - // owned by the Operator. + // owned by the ClusterExtension. existingBundleDeployment, err := r.existingBundleDeploymentUnstructured(ctx, desiredBundleDeployment.GetName()) if client.IgnoreNotFound(err) != nil { return err @@ -359,7 +359,7 @@ func (r *OperatorReconciler) ensureBundleDeployment(ctx context.Context, desired return r.Client.Patch(ctx, desiredBundleDeployment, client.Apply, client.ForceOwnership, client.FieldOwner("operator-controller")) } -func (r *OperatorReconciler) existingBundleDeploymentUnstructured(ctx context.Context, name string) (*unstructured.Unstructured, error) { +func (r *ClusterExtensionReconciler) existingBundleDeploymentUnstructured(ctx context.Context, name string) (*unstructured.Unstructured, error) { existingBundleDeployment := &rukpakv1alpha1.BundleDeployment{} err := r.Client.Get(ctx, types.NamespacedName{Name: name}, existingBundleDeployment) if err != nil { @@ -394,9 +394,9 @@ func mapBundleMediaTypeToBundleProvisioner(mediaType string) (string, error) { // setResolvedStatusConditionSuccess sets the resolved status condition to success. func setResolvedStatusConditionSuccess(conditions *[]metav1.Condition, message string, generation int64) { apimeta.SetStatusCondition(conditions, metav1.Condition{ - Type: operatorsv1alpha1.TypeResolved, + Type: ocv1alpha1.TypeResolved, Status: metav1.ConditionTrue, - Reason: operatorsv1alpha1.ReasonSuccess, + Reason: ocv1alpha1.ReasonSuccess, Message: message, ObservedGeneration: generation, }) @@ -405,9 +405,9 @@ func setResolvedStatusConditionSuccess(conditions *[]metav1.Condition, message s // setResolvedStatusConditionFailed sets the resolved status condition to failed. func setResolvedStatusConditionFailed(conditions *[]metav1.Condition, message string, generation int64) { apimeta.SetStatusCondition(conditions, metav1.Condition{ - Type: operatorsv1alpha1.TypeResolved, + Type: ocv1alpha1.TypeResolved, Status: metav1.ConditionFalse, - Reason: operatorsv1alpha1.ReasonResolutionFailed, + Reason: ocv1alpha1.ReasonResolutionFailed, Message: message, ObservedGeneration: generation, }) @@ -416,9 +416,9 @@ func setResolvedStatusConditionFailed(conditions *[]metav1.Condition, message st // setResolvedStatusConditionUnknown sets the resolved status condition to unknown. func setResolvedStatusConditionUnknown(conditions *[]metav1.Condition, message string, generation int64) { apimeta.SetStatusCondition(conditions, metav1.Condition{ - Type: operatorsv1alpha1.TypeResolved, + Type: ocv1alpha1.TypeResolved, Status: metav1.ConditionUnknown, - Reason: operatorsv1alpha1.ReasonResolutionUnknown, + Reason: ocv1alpha1.ReasonResolutionUnknown, Message: message, ObservedGeneration: generation, }) @@ -427,9 +427,9 @@ func setResolvedStatusConditionUnknown(conditions *[]metav1.Condition, message s // setInstalledStatusConditionSuccess sets the installed status condition to success. func setInstalledStatusConditionSuccess(conditions *[]metav1.Condition, message string, generation int64) { apimeta.SetStatusCondition(conditions, metav1.Condition{ - Type: operatorsv1alpha1.TypeInstalled, + Type: ocv1alpha1.TypeInstalled, Status: metav1.ConditionTrue, - Reason: operatorsv1alpha1.ReasonSuccess, + Reason: ocv1alpha1.ReasonSuccess, Message: message, ObservedGeneration: generation, }) @@ -438,9 +438,9 @@ func setInstalledStatusConditionSuccess(conditions *[]metav1.Condition, message // setInstalledStatusConditionFailed sets the installed status condition to failed. func setInstalledStatusConditionFailed(conditions *[]metav1.Condition, message string, generation int64) { apimeta.SetStatusCondition(conditions, metav1.Condition{ - Type: operatorsv1alpha1.TypeInstalled, + Type: ocv1alpha1.TypeInstalled, Status: metav1.ConditionFalse, - Reason: operatorsv1alpha1.ReasonInstallationFailed, + Reason: ocv1alpha1.ReasonInstallationFailed, Message: message, ObservedGeneration: generation, }) @@ -449,30 +449,30 @@ func setInstalledStatusConditionFailed(conditions *[]metav1.Condition, message s // setInstalledStatusConditionUnknown sets the installed status condition to unknown. func setInstalledStatusConditionUnknown(conditions *[]metav1.Condition, message string, generation int64) { apimeta.SetStatusCondition(conditions, metav1.Condition{ - Type: operatorsv1alpha1.TypeInstalled, + Type: ocv1alpha1.TypeInstalled, Status: metav1.ConditionUnknown, - Reason: operatorsv1alpha1.ReasonInstallationStatusUnknown, + Reason: ocv1alpha1.ReasonInstallationStatusUnknown, Message: message, ObservedGeneration: generation, }) } -// Generate reconcile requests for all operators affected by a catalog change -func operatorRequestsForCatalog(c client.Reader, logger logr.Logger) handler.MapFunc { +// Generate reconcile requests for all cluster extensions affected by a catalog change +func clusterExtensionRequestsForCatalog(c client.Reader, logger logr.Logger) handler.MapFunc { return func(ctx context.Context, _ client.Object) []reconcile.Request { - // no way of associating an operator to a catalog so create reconcile requests for everything - operators := operatorsv1alpha1.OperatorList{} - err := c.List(ctx, &operators) + // no way of associating an extension to a catalog so create reconcile requests for everything + clusterExtensions := ocv1alpha1.ClusterExtensionList{} + err := c.List(ctx, &clusterExtensions) if err != nil { - logger.Error(err, "unable to enqueue operators for catalog reconcile") + logger.Error(err, "unable to enqueue cluster extensions for catalog reconcile") return nil } var requests []reconcile.Request - for _, op := range operators.Items { + for _, ext := range clusterExtensions.Items { requests = append(requests, reconcile.Request{ NamespacedName: types.NamespacedName{ - Namespace: op.GetNamespace(), - Name: op.GetName(), + Namespace: ext.GetNamespace(), + Name: ext.GetName(), }, }) } diff --git a/internal/controllers/operator_controller_test.go b/internal/controllers/clusterextension_controller_test.go similarity index 59% rename from internal/controllers/operator_controller_test.go rename to internal/controllers/clusterextension_controller_test.go index 0d6120cf..3e8b749c 100644 --- a/internal/controllers/operator_controller_test.go +++ b/internal/controllers/clusterextension_controller_test.go @@ -21,135 +21,135 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" - operatorsv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" + ocv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" "github.com/operator-framework/operator-controller/internal/catalogmetadata" "github.com/operator-framework/operator-controller/internal/conditionsets" "github.com/operator-framework/operator-controller/pkg/features" ) -// Describe: Operator Controller Test -func TestOperatorDoesNotExist(t *testing.T) { +// Describe: ClusterExtension Controller Test +func TestClusterExtensionDoesNotExist(t *testing.T) { _, reconciler := newClientAndReconciler(t) - t.Log("When the operator does not exist") + t.Log("When the cluster extension does not exist") t.Log("It returns no error") res, err := reconciler.Reconcile(context.Background(), ctrl.Request{NamespacedName: types.NamespacedName{Name: "non-existent"}}) require.Equal(t, ctrl.Result{}, res) require.NoError(t, err) } -func TestOperatorNonExistantPackage(t *testing.T) { +func TestClusterExtensionNonExistentPackage(t *testing.T) { cl, reconciler := newClientAndReconciler(t) ctx := context.Background() - opKey := types.NamespacedName{Name: fmt.Sprintf("operator-test-%s", rand.String(8))} + extKey := types.NamespacedName{Name: fmt.Sprintf("cluster-extension-test-%s", rand.String(8))} - t.Log("When the operator specifies a non-existent package") + t.Log("When the cluster extension specifies a non-existent package") t.Log("By initializing cluster state") pkgName := fmt.Sprintf("non-existent-%s", rand.String(6)) - operator := &operatorsv1alpha1.Operator{ - ObjectMeta: metav1.ObjectMeta{Name: opKey.Name}, - Spec: operatorsv1alpha1.OperatorSpec{PackageName: pkgName}, + clusterExtension := &ocv1alpha1.ClusterExtension{ + ObjectMeta: metav1.ObjectMeta{Name: extKey.Name}, + Spec: ocv1alpha1.ClusterExtensionSpec{PackageName: pkgName}, } - require.NoError(t, cl.Create(ctx, operator)) + require.NoError(t, cl.Create(ctx, clusterExtension)) t.Log("It sets resolution failure status") t.Log("By running reconcile") - res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) + res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: extKey}) require.Equal(t, ctrl.Result{}, res) require.EqualError(t, err, fmt.Sprintf("no package %q found", pkgName)) - t.Log("By fetching updated operator after reconcile") - require.NoError(t, cl.Get(ctx, opKey, operator)) + t.Log("By fetching updated cluster extension after reconcile") + require.NoError(t, cl.Get(ctx, extKey, clusterExtension)) t.Log("By checking the status fields") - require.Empty(t, operator.Status.ResolvedBundleResource) - require.Empty(t, operator.Status.InstalledBundleResource) + require.Empty(t, clusterExtension.Status.ResolvedBundleResource) + require.Empty(t, clusterExtension.Status.InstalledBundleResource) t.Log("By checking the expected conditions") - cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeResolved) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) require.NotNil(t, cond) require.Equal(t, metav1.ConditionFalse, cond.Status) - require.Equal(t, operatorsv1alpha1.ReasonResolutionFailed, cond.Reason) + require.Equal(t, ocv1alpha1.ReasonResolutionFailed, cond.Reason) require.Equal(t, fmt.Sprintf("no package %q found", pkgName), cond.Message) - verifyInvariants(ctx, t, reconciler.Client, operator) - require.NoError(t, cl.DeleteAllOf(ctx, &operatorsv1alpha1.Operator{})) + verifyInvariants(ctx, t, reconciler.Client, clusterExtension) + require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) require.NoError(t, cl.DeleteAllOf(ctx, &rukpakv1alpha1.BundleDeployment{})) } -func TestOperatorNonExistantVersion(t *testing.T) { +func TestClusterExtensionNonExistentVersion(t *testing.T) { cl, reconciler := newClientAndReconciler(t) ctx := context.Background() - opKey := types.NamespacedName{Name: fmt.Sprintf("operator-test-%s", rand.String(8))} + extKey := types.NamespacedName{Name: fmt.Sprintf("cluster-extension-test-%s", rand.String(8))} - t.Log("When the operator specifies a version that does not exist") + t.Log("When the cluster extension specifies a version that does not exist") t.Log("By initializing cluster state") pkgName := "prometheus" - operator := &operatorsv1alpha1.Operator{ - ObjectMeta: metav1.ObjectMeta{Name: opKey.Name}, - Spec: operatorsv1alpha1.OperatorSpec{ + clusterExtension := &ocv1alpha1.ClusterExtension{ + ObjectMeta: metav1.ObjectMeta{Name: extKey.Name}, + Spec: ocv1alpha1.ClusterExtensionSpec{ PackageName: pkgName, Version: "0.50.0", // this version of the package does not exist }, } - require.NoError(t, cl.Create(ctx, operator)) + require.NoError(t, cl.Create(ctx, clusterExtension)) t.Log("It sets resolution failure status") t.Log("By running reconcile") - res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) + res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: extKey}) require.Equal(t, ctrl.Result{}, res) require.EqualError(t, err, fmt.Sprintf(`no package %q matching version "0.50.0" found`, pkgName)) - t.Log("By fetching updated operator after reconcile") - require.NoError(t, cl.Get(ctx, opKey, operator)) + t.Log("By fetching updated cluster extension after reconcile") + require.NoError(t, cl.Get(ctx, extKey, clusterExtension)) t.Log("By checking the status fields") - require.Empty(t, operator.Status.ResolvedBundleResource) - require.Empty(t, operator.Status.InstalledBundleResource) + require.Empty(t, clusterExtension.Status.ResolvedBundleResource) + require.Empty(t, clusterExtension.Status.InstalledBundleResource) t.Log("By checking the expected conditions") - cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeResolved) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) require.NotNil(t, cond) require.Equal(t, metav1.ConditionFalse, cond.Status) - require.Equal(t, operatorsv1alpha1.ReasonResolutionFailed, cond.Reason) + require.Equal(t, ocv1alpha1.ReasonResolutionFailed, cond.Reason) require.Equal(t, fmt.Sprintf(`no package %q matching version "0.50.0" found`, pkgName), cond.Message) - cond = apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeInstalled) + cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) require.NotNil(t, cond) require.Equal(t, metav1.ConditionUnknown, cond.Status) - require.Equal(t, operatorsv1alpha1.ReasonInstallationStatusUnknown, cond.Reason) + require.Equal(t, ocv1alpha1.ReasonInstallationStatusUnknown, cond.Reason) require.Equal(t, "installation has not been attempted due to failure to gather data for resolution", cond.Message) - verifyInvariants(ctx, t, reconciler.Client, operator) - require.NoError(t, cl.DeleteAllOf(ctx, &operatorsv1alpha1.Operator{})) + verifyInvariants(ctx, t, reconciler.Client, clusterExtension) + require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) require.NoError(t, cl.DeleteAllOf(ctx, &rukpakv1alpha1.BundleDeployment{})) } -func TestOperatorBundleDeploymentDoesNotExist(t *testing.T) { +func TestClusterExtensionBundleDeploymentDoesNotExist(t *testing.T) { cl, reconciler := newClientAndReconciler(t) ctx := context.Background() - opKey := types.NamespacedName{Name: fmt.Sprintf("operator-test-%s", rand.String(8))} + extKey := types.NamespacedName{Name: fmt.Sprintf("cluster-extension-test-%s", rand.String(8))} const pkgName = "prometheus" - t.Log("When the operator specifies a valid available package") + t.Log("When the cluster extension specifies a valid available package") t.Log("By initializing cluster state") - operator := &operatorsv1alpha1.Operator{ - ObjectMeta: metav1.ObjectMeta{Name: opKey.Name}, - Spec: operatorsv1alpha1.OperatorSpec{PackageName: pkgName}, + clusterExtension := &ocv1alpha1.ClusterExtension{ + ObjectMeta: metav1.ObjectMeta{Name: extKey.Name}, + Spec: ocv1alpha1.ClusterExtensionSpec{PackageName: pkgName}, } - require.NoError(t, cl.Create(ctx, operator)) + require.NoError(t, cl.Create(ctx, clusterExtension)) t.Log("When the BundleDeployment does not exist") t.Log("By running reconcile") - res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) + res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: extKey}) require.Equal(t, ctrl.Result{}, res) require.NoError(t, err) - t.Log("By fetching updated operator after reconcile") - require.NoError(t, cl.Get(ctx, opKey, operator)) + t.Log("By fetching updated cluster extension after reconcile") + require.NoError(t, cl.Get(ctx, extKey, clusterExtension)) t.Log("It results in the expected BundleDeployment") bd := &rukpakv1alpha1.BundleDeployment{} - require.NoError(t, cl.Get(ctx, types.NamespacedName{Name: opKey.Name}, bd)) + require.NoError(t, cl.Get(ctx, types.NamespacedName{Name: extKey.Name}, bd)) require.Equal(t, "core-rukpak-io-plain", bd.Spec.ProvisionerClassName) require.Equal(t, "core-rukpak-io-registry", bd.Spec.Template.Spec.ProvisionerClassName) require.Equal(t, rukpakv1alpha1.SourceTypeImage, bd.Spec.Template.Spec.Source.Type) @@ -157,55 +157,55 @@ func TestOperatorBundleDeploymentDoesNotExist(t *testing.T) { require.Equal(t, "quay.io/operatorhubio/prometheus@fake2.0.0", bd.Spec.Template.Spec.Source.Image.Ref) t.Log("It sets the resolvedBundleResource status field") - require.Equal(t, "quay.io/operatorhubio/prometheus@fake2.0.0", operator.Status.ResolvedBundleResource) + require.Equal(t, "quay.io/operatorhubio/prometheus@fake2.0.0", clusterExtension.Status.ResolvedBundleResource) t.Log("It sets the InstalledBundleResource status field") - require.Empty(t, operator.Status.InstalledBundleResource) + require.Empty(t, clusterExtension.Status.InstalledBundleResource) - t.Log("It sets the status on operator") - cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeResolved) + t.Log("It sets the status on the cluster extension") + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) require.NotNil(t, cond) require.Equal(t, metav1.ConditionTrue, cond.Status) - require.Equal(t, operatorsv1alpha1.ReasonSuccess, cond.Reason) + require.Equal(t, ocv1alpha1.ReasonSuccess, cond.Reason) require.Equal(t, "resolved to \"quay.io/operatorhubio/prometheus@fake2.0.0\"", cond.Message) - cond = apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeInstalled) + cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) require.NotNil(t, cond) require.Equal(t, metav1.ConditionUnknown, cond.Status) - require.Equal(t, operatorsv1alpha1.ReasonInstallationStatusUnknown, cond.Reason) + require.Equal(t, ocv1alpha1.ReasonInstallationStatusUnknown, cond.Reason) require.Equal(t, "bundledeployment status is unknown", cond.Message) - verifyInvariants(ctx, t, reconciler.Client, operator) - require.NoError(t, cl.DeleteAllOf(ctx, &operatorsv1alpha1.Operator{})) + verifyInvariants(ctx, t, reconciler.Client, clusterExtension) + require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) require.NoError(t, cl.DeleteAllOf(ctx, &rukpakv1alpha1.BundleDeployment{})) } -func TestOperatorBundleDeploymentOutOfDate(t *testing.T) { +func TestClusterExtensionBundleDeploymentOutOfDate(t *testing.T) { cl, reconciler := newClientAndReconciler(t) ctx := context.Background() - opKey := types.NamespacedName{Name: fmt.Sprintf("operator-test-%s", rand.String(8))} + extKey := types.NamespacedName{Name: fmt.Sprintf("cluster-extension-test-%s", rand.String(8))} const pkgName = "prometheus" - t.Log("When the operator specifies a valid available package") + t.Log("When the cluster extension specifies a valid available package") t.Log("By initializing cluster state") - operator := &operatorsv1alpha1.Operator{ - ObjectMeta: metav1.ObjectMeta{Name: opKey.Name}, - Spec: operatorsv1alpha1.OperatorSpec{PackageName: pkgName}, + clusterExtension := &ocv1alpha1.ClusterExtension{ + ObjectMeta: metav1.ObjectMeta{Name: extKey.Name}, + Spec: ocv1alpha1.ClusterExtensionSpec{PackageName: pkgName}, } - require.NoError(t, cl.Create(ctx, operator)) + require.NoError(t, cl.Create(ctx, clusterExtension)) t.Log("When the expected BundleDeployment already exists") t.Log("When the BundleDeployment spec is out of date") t.Log("By patching the existing BD") bd := &rukpakv1alpha1.BundleDeployment{ ObjectMeta: metav1.ObjectMeta{ - Name: opKey.Name, + Name: extKey.Name, OwnerReferences: []metav1.OwnerReference{ { - APIVersion: operatorsv1alpha1.GroupVersion.String(), - Kind: "Operator", - Name: operator.Name, - UID: operator.UID, + APIVersion: ocv1alpha1.GroupVersion.String(), + Kind: "ClusterExtension", + Name: clusterExtension.Name, + UID: clusterExtension.UID, Controller: pointer.Bool(true), BlockOwnerDeletion: pointer.Bool(true), }, @@ -234,16 +234,16 @@ func TestOperatorBundleDeploymentOutOfDate(t *testing.T) { t.Log("It results in the expected BundleDeployment") t.Log("By running reconcile") - res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) + res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: extKey}) require.Equal(t, ctrl.Result{}, res) require.NoError(t, err) - t.Log("By fetching updated operator after reconcile") - require.NoError(t, cl.Get(ctx, opKey, operator)) + t.Log("By fetching updated cluster extension after reconcile") + require.NoError(t, cl.Get(ctx, extKey, clusterExtension)) t.Log("By checking the expected BD spec") bd = &rukpakv1alpha1.BundleDeployment{} - require.NoError(t, cl.Get(ctx, types.NamespacedName{Name: opKey.Name}, bd)) + require.NoError(t, cl.Get(ctx, types.NamespacedName{Name: extKey.Name}, bd)) require.Equal(t, "core-rukpak-io-plain", bd.Spec.ProvisionerClassName) require.Equal(t, "core-rukpak-io-registry", bd.Spec.Template.Spec.ProvisionerClassName) require.Equal(t, rukpakv1alpha1.SourceTypeImage, bd.Spec.Template.Spec.Source.Type) @@ -251,52 +251,52 @@ func TestOperatorBundleDeploymentOutOfDate(t *testing.T) { require.Equal(t, "quay.io/operatorhubio/prometheus@fake2.0.0", bd.Spec.Template.Spec.Source.Image.Ref) t.Log("By checking the status fields") - require.Equal(t, "quay.io/operatorhubio/prometheus@fake2.0.0", operator.Status.ResolvedBundleResource) - require.Empty(t, operator.Status.InstalledBundleResource) + require.Equal(t, "quay.io/operatorhubio/prometheus@fake2.0.0", clusterExtension.Status.ResolvedBundleResource) + require.Empty(t, clusterExtension.Status.InstalledBundleResource) t.Log("By checking the expected status conditions") - cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeResolved) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) require.NotNil(t, cond) require.Equal(t, metav1.ConditionTrue, cond.Status) - require.Equal(t, operatorsv1alpha1.ReasonSuccess, cond.Reason) + require.Equal(t, ocv1alpha1.ReasonSuccess, cond.Reason) require.Equal(t, "resolved to \"quay.io/operatorhubio/prometheus@fake2.0.0\"", cond.Message) - cond = apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeInstalled) + cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) require.NotNil(t, cond) require.Equal(t, metav1.ConditionUnknown, cond.Status) - require.Equal(t, operatorsv1alpha1.ReasonInstallationStatusUnknown, cond.Reason) + require.Equal(t, ocv1alpha1.ReasonInstallationStatusUnknown, cond.Reason) require.Equal(t, "bundledeployment status is unknown", cond.Message) - verifyInvariants(ctx, t, reconciler.Client, operator) - require.NoError(t, cl.DeleteAllOf(ctx, &operatorsv1alpha1.Operator{})) + verifyInvariants(ctx, t, reconciler.Client, clusterExtension) + require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) require.NoError(t, cl.DeleteAllOf(ctx, &rukpakv1alpha1.BundleDeployment{})) } -func TestOperatorBundleDeploymentUpToDate(t *testing.T) { +func TestClusterExtensionBundleDeploymentUpToDate(t *testing.T) { cl, reconciler := newClientAndReconciler(t) ctx := context.Background() - opKey := types.NamespacedName{Name: fmt.Sprintf("operator-test-%s", rand.String(8))} + extKey := types.NamespacedName{Name: fmt.Sprintf("cluster-extension-test-%s", rand.String(8))} const pkgName = "prometheus" - t.Log("When the operator specifies a valid available package") + t.Log("When the cluster extension specifies a valid available package") t.Log("By initializing cluster state") - operator := &operatorsv1alpha1.Operator{ - ObjectMeta: metav1.ObjectMeta{Name: opKey.Name}, - Spec: operatorsv1alpha1.OperatorSpec{PackageName: pkgName}, + clusterExtension := &ocv1alpha1.ClusterExtension{ + ObjectMeta: metav1.ObjectMeta{Name: extKey.Name}, + Spec: ocv1alpha1.ClusterExtensionSpec{PackageName: pkgName}, } - require.NoError(t, cl.Create(ctx, operator)) + require.NoError(t, cl.Create(ctx, clusterExtension)) t.Log("When the expected BundleDeployment already exists") t.Log("When the BundleDeployment spec is up-to-date") t.Log("By patching the existing BD") bd := &rukpakv1alpha1.BundleDeployment{ ObjectMeta: metav1.ObjectMeta{ - Name: opKey.Name, + Name: extKey.Name, OwnerReferences: []metav1.OwnerReference{ { - APIVersion: operatorsv1alpha1.GroupVersion.String(), - Kind: "Operator", - Name: operator.Name, - UID: operator.UID, + APIVersion: ocv1alpha1.GroupVersion.String(), + Kind: "ClusterExtension", + Name: clusterExtension.Name, + UID: clusterExtension.UID, Controller: pointer.Bool(true), BlockOwnerDeletion: pointer.Bool(true), }, @@ -321,37 +321,37 @@ func TestOperatorBundleDeploymentUpToDate(t *testing.T) { require.NoError(t, cl.Create(ctx, bd)) bd.Status.ObservedGeneration = bd.GetGeneration() - t.Log("When the BundleDeployment status is mapped to the expected Operator status") - t.Log("It verifies operator status when bundle deployment is waiting to be created") + t.Log("When the BundleDeployment status is mapped to the expected ClusterExtension status") + t.Log("It verifies cluster extension status when bundle deployment is waiting to be created") t.Log("By updating the status of bundleDeployment") require.NoError(t, cl.Status().Update(ctx, bd)) t.Log("By running reconcile") - res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) + res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: extKey}) require.Equal(t, ctrl.Result{}, res) require.NoError(t, err) - t.Log("By fetching the updated operator after reconcile") - op := &operatorsv1alpha1.Operator{} - require.NoError(t, cl.Get(ctx, opKey, op)) + t.Log("By fetching the updated cluster extension after reconcile") + ext := &ocv1alpha1.ClusterExtension{} + require.NoError(t, cl.Get(ctx, extKey, ext)) t.Log("By checking the status fields") - require.Equal(t, "quay.io/operatorhubio/prometheus@fake2.0.0", op.Status.ResolvedBundleResource) - require.Empty(t, op.Status.InstalledBundleResource) + require.Equal(t, "quay.io/operatorhubio/prometheus@fake2.0.0", ext.Status.ResolvedBundleResource) + require.Empty(t, ext.Status.InstalledBundleResource) t.Log("By checking the expected conditions") - cond := apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeResolved) + cond := apimeta.FindStatusCondition(ext.Status.Conditions, ocv1alpha1.TypeResolved) require.NotNil(t, cond) require.Equal(t, metav1.ConditionTrue, cond.Status) - require.Equal(t, operatorsv1alpha1.ReasonSuccess, cond.Reason) + require.Equal(t, ocv1alpha1.ReasonSuccess, cond.Reason) require.Equal(t, "resolved to \"quay.io/operatorhubio/prometheus@fake2.0.0\"", cond.Message) - cond = apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeInstalled) + cond = apimeta.FindStatusCondition(ext.Status.Conditions, ocv1alpha1.TypeInstalled) require.NotNil(t, cond) require.Equal(t, metav1.ConditionUnknown, cond.Status) - require.Equal(t, operatorsv1alpha1.ReasonInstallationStatusUnknown, cond.Reason) + require.Equal(t, ocv1alpha1.ReasonInstallationStatusUnknown, cond.Reason) require.Equal(t, "bundledeployment status is unknown", cond.Message) - t.Log("It verifies operator status when `HasValidBundle` condition of rukpak is false") + t.Log("It verifies cluster extension status when `HasValidBundle` condition of rukpak is false") apimeta.SetStatusCondition(&bd.Status.Conditions, metav1.Condition{ Type: rukpakv1alpha1.TypeHasValidBundle, Status: metav1.ConditionFalse, @@ -363,31 +363,31 @@ func TestOperatorBundleDeploymentUpToDate(t *testing.T) { require.NoError(t, cl.Status().Update(ctx, bd)) t.Log("By running reconcile") - res, err = reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) + res, err = reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: extKey}) require.Equal(t, ctrl.Result{}, res) require.NoError(t, err) - t.Log("By fetching the updated operator after reconcile") - op = &operatorsv1alpha1.Operator{} - require.NoError(t, cl.Get(ctx, opKey, op)) + t.Log("By fetching the updated cluster extension after reconcile") + ext = &ocv1alpha1.ClusterExtension{} + require.NoError(t, cl.Get(ctx, extKey, ext)) t.Log("By checking the status fields") - require.Equal(t, "quay.io/operatorhubio/prometheus@fake2.0.0", op.Status.ResolvedBundleResource) - require.Equal(t, "", op.Status.InstalledBundleResource) + require.Equal(t, "quay.io/operatorhubio/prometheus@fake2.0.0", ext.Status.ResolvedBundleResource) + require.Equal(t, "", ext.Status.InstalledBundleResource) t.Log("By checking the expected conditions") - cond = apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeResolved) + cond = apimeta.FindStatusCondition(ext.Status.Conditions, ocv1alpha1.TypeResolved) require.NotNil(t, cond) require.Equal(t, metav1.ConditionTrue, cond.Status) - require.Equal(t, operatorsv1alpha1.ReasonSuccess, cond.Reason) + require.Equal(t, ocv1alpha1.ReasonSuccess, cond.Reason) require.Equal(t, "resolved to \"quay.io/operatorhubio/prometheus@fake2.0.0\"", cond.Message) - cond = apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeInstalled) + cond = apimeta.FindStatusCondition(ext.Status.Conditions, ocv1alpha1.TypeInstalled) require.NotNil(t, cond) require.Equal(t, metav1.ConditionUnknown, cond.Status) - require.Equal(t, operatorsv1alpha1.ReasonInstallationStatusUnknown, cond.Reason) + require.Equal(t, ocv1alpha1.ReasonInstallationStatusUnknown, cond.Reason) require.Equal(t, "bundledeployment status is unknown", cond.Message) - t.Log("It verifies operator status when `InstallReady` condition of rukpak is false") + t.Log("It verifies cluster extension status when `InstallReady` condition of rukpak is false") apimeta.SetStatusCondition(&bd.Status.Conditions, metav1.Condition{ Type: rukpakv1alpha1.TypeInstalled, Status: metav1.ConditionFalse, @@ -399,37 +399,37 @@ func TestOperatorBundleDeploymentUpToDate(t *testing.T) { require.NoError(t, cl.Status().Update(ctx, bd)) t.Log("By running reconcile") - res, err = reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) + res, err = reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: extKey}) require.Equal(t, ctrl.Result{}, res) require.NoError(t, err) - t.Log("By fetching the updated operator after reconcile") - op = &operatorsv1alpha1.Operator{} - err = cl.Get(ctx, opKey, op) + t.Log("By fetching the updated cluster extension after reconcile") + ext = &ocv1alpha1.ClusterExtension{} + err = cl.Get(ctx, extKey, ext) require.NoError(t, err) t.Log("By checking the status fields") - require.Equal(t, "quay.io/operatorhubio/prometheus@fake2.0.0", op.Status.ResolvedBundleResource) - require.Empty(t, op.Status.InstalledBundleResource) + require.Equal(t, "quay.io/operatorhubio/prometheus@fake2.0.0", ext.Status.ResolvedBundleResource) + require.Empty(t, ext.Status.InstalledBundleResource) t.Log("By checking the expected conditions") - cond = apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeResolved) + cond = apimeta.FindStatusCondition(ext.Status.Conditions, ocv1alpha1.TypeResolved) require.NotNil(t, cond) require.Equal(t, metav1.ConditionTrue, cond.Status) - require.Equal(t, operatorsv1alpha1.ReasonSuccess, cond.Reason) + require.Equal(t, ocv1alpha1.ReasonSuccess, cond.Reason) require.Equal(t, "resolved to \"quay.io/operatorhubio/prometheus@fake2.0.0\"", cond.Message) - cond = apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeInstalled) + cond = apimeta.FindStatusCondition(ext.Status.Conditions, ocv1alpha1.TypeInstalled) require.NotNil(t, cond) require.Equal(t, metav1.ConditionFalse, cond.Status) - require.Equal(t, operatorsv1alpha1.ReasonInstallationFailed, cond.Reason) + require.Equal(t, ocv1alpha1.ReasonInstallationFailed, cond.Reason) require.Contains(t, cond.Message, `failed to install`) - t.Log("It verifies operator status when `InstallReady` condition of rukpak is true") + t.Log("It verifies cluster extension status when `InstallReady` condition of rukpak is true") apimeta.SetStatusCondition(&bd.Status.Conditions, metav1.Condition{ Type: rukpakv1alpha1.TypeInstalled, Status: metav1.ConditionTrue, - Message: "operator installed successfully", + Message: "cluster extension installed successfully", Reason: rukpakv1alpha1.ReasonInstallationSucceeded, }) @@ -437,28 +437,28 @@ func TestOperatorBundleDeploymentUpToDate(t *testing.T) { require.NoError(t, cl.Status().Update(ctx, bd)) t.Log("By running reconcile") - res, err = reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) + res, err = reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: extKey}) require.Equal(t, ctrl.Result{}, res) require.NoError(t, err) - t.Log("By fetching the updated operator after reconcile") - op = &operatorsv1alpha1.Operator{} - require.NoError(t, cl.Get(ctx, opKey, op)) + t.Log("By fetching the updated cluster extension after reconcile") + ext = &ocv1alpha1.ClusterExtension{} + require.NoError(t, cl.Get(ctx, extKey, ext)) t.Log("By checking the status fields") - require.Equal(t, "quay.io/operatorhubio/prometheus@fake2.0.0", op.Status.ResolvedBundleResource) - require.Equal(t, "quay.io/operatorhubio/prometheus@fake2.0.0", op.Status.InstalledBundleResource) + require.Equal(t, "quay.io/operatorhubio/prometheus@fake2.0.0", ext.Status.ResolvedBundleResource) + require.Equal(t, "quay.io/operatorhubio/prometheus@fake2.0.0", ext.Status.InstalledBundleResource) t.Log("By checking the expected conditions") - cond = apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeResolved) + cond = apimeta.FindStatusCondition(ext.Status.Conditions, ocv1alpha1.TypeResolved) require.NotNil(t, cond) require.Equal(t, metav1.ConditionTrue, cond.Status) - require.Equal(t, operatorsv1alpha1.ReasonSuccess, cond.Reason) + require.Equal(t, ocv1alpha1.ReasonSuccess, cond.Reason) require.Equal(t, "resolved to \"quay.io/operatorhubio/prometheus@fake2.0.0\"", cond.Message) - cond = apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeInstalled) + cond = apimeta.FindStatusCondition(ext.Status.Conditions, ocv1alpha1.TypeInstalled) require.NotNil(t, cond) require.Equal(t, metav1.ConditionTrue, cond.Status) - require.Equal(t, operatorsv1alpha1.ReasonSuccess, cond.Reason) + require.Equal(t, ocv1alpha1.ReasonSuccess, cond.Reason) require.Equal(t, "installed from \"quay.io/operatorhubio/prometheus@fake2.0.0\"", cond.Message) t.Log("It verifies any other unknown status of bundledeployment") @@ -480,32 +480,32 @@ func TestOperatorBundleDeploymentUpToDate(t *testing.T) { require.NoError(t, cl.Status().Update(ctx, bd)) t.Log("By running reconcile") - res, err = reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) + res, err = reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: extKey}) require.Equal(t, ctrl.Result{}, res) require.NoError(t, err) - t.Log("By fetching the updated operator after reconcile") - op = &operatorsv1alpha1.Operator{} - require.NoError(t, cl.Get(ctx, opKey, op)) + t.Log("By fetching the updated cluster extension after reconcile") + ext = &ocv1alpha1.ClusterExtension{} + require.NoError(t, cl.Get(ctx, extKey, ext)) t.Log("By checking the status fields") - require.Equal(t, "quay.io/operatorhubio/prometheus@fake2.0.0", op.Status.ResolvedBundleResource) - require.Empty(t, op.Status.InstalledBundleResource) + require.Equal(t, "quay.io/operatorhubio/prometheus@fake2.0.0", ext.Status.ResolvedBundleResource) + require.Empty(t, ext.Status.InstalledBundleResource) t.Log("By checking the expected conditions") - cond = apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeResolved) + cond = apimeta.FindStatusCondition(ext.Status.Conditions, ocv1alpha1.TypeResolved) require.NotNil(t, cond) require.Equal(t, metav1.ConditionTrue, cond.Status) - require.Equal(t, operatorsv1alpha1.ReasonSuccess, cond.Reason) + require.Equal(t, ocv1alpha1.ReasonSuccess, cond.Reason) require.Equal(t, "resolved to \"quay.io/operatorhubio/prometheus@fake2.0.0\"", cond.Message) - cond = apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeInstalled) + cond = apimeta.FindStatusCondition(ext.Status.Conditions, ocv1alpha1.TypeInstalled) require.NotNil(t, cond) require.Equal(t, metav1.ConditionFalse, cond.Status) - require.Equal(t, operatorsv1alpha1.ReasonInstallationFailed, cond.Reason) + require.Equal(t, ocv1alpha1.ReasonInstallationFailed, cond.Reason) require.Equal(t, "bundledeployment not ready: installing", cond.Message) - t.Log("It verifies operator status when bundleDeployment installation status is unknown") + t.Log("It verifies cluster extension status when bundleDeployment installation status is unknown") apimeta.SetStatusCondition(&bd.Status.Conditions, metav1.Condition{ Type: rukpakv1alpha1.TypeInstalled, Status: metav1.ConditionUnknown, @@ -517,53 +517,53 @@ func TestOperatorBundleDeploymentUpToDate(t *testing.T) { require.NoError(t, cl.Status().Update(ctx, bd)) t.Log("running reconcile") - res, err = reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) + res, err = reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: extKey}) require.Equal(t, ctrl.Result{}, res) require.NoError(t, err) - t.Log("By fetching the updated operator after reconcile") - op = &operatorsv1alpha1.Operator{} - require.NoError(t, cl.Get(ctx, opKey, op)) + t.Log("By fetching the updated cluster extension after reconcile") + ext = &ocv1alpha1.ClusterExtension{} + require.NoError(t, cl.Get(ctx, extKey, ext)) t.Log("By checking the status fields") - require.Equal(t, "quay.io/operatorhubio/prometheus@fake2.0.0", op.Status.ResolvedBundleResource) - require.Empty(t, op.Status.InstalledBundleResource) + require.Equal(t, "quay.io/operatorhubio/prometheus@fake2.0.0", ext.Status.ResolvedBundleResource) + require.Empty(t, ext.Status.InstalledBundleResource) t.Log("By cchecking the expected conditions") - cond = apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeResolved) + cond = apimeta.FindStatusCondition(ext.Status.Conditions, ocv1alpha1.TypeResolved) require.NotNil(t, cond) require.Equal(t, metav1.ConditionTrue, cond.Status) - require.Equal(t, operatorsv1alpha1.ReasonSuccess, cond.Reason) + require.Equal(t, ocv1alpha1.ReasonSuccess, cond.Reason) require.Equal(t, "resolved to \"quay.io/operatorhubio/prometheus@fake2.0.0\"", cond.Message) - cond = apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeInstalled) + cond = apimeta.FindStatusCondition(ext.Status.Conditions, ocv1alpha1.TypeInstalled) require.NotNil(t, cond) require.Equal(t, metav1.ConditionFalse, cond.Status) - require.Equal(t, operatorsv1alpha1.ReasonInstallationFailed, cond.Reason) + require.Equal(t, ocv1alpha1.ReasonInstallationFailed, cond.Reason) require.Equal(t, "bundledeployment not ready: installing", cond.Message) - verifyInvariants(ctx, t, reconciler.Client, operator) - require.NoError(t, cl.DeleteAllOf(ctx, &operatorsv1alpha1.Operator{})) + verifyInvariants(ctx, t, reconciler.Client, clusterExtension) + require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) require.NoError(t, cl.DeleteAllOf(ctx, &rukpakv1alpha1.BundleDeployment{})) } -func TestOperatorExpectedBundleDeployment(t *testing.T) { +func TestClusterExtensionExpectedBundleDeployment(t *testing.T) { cl, reconciler := newClientAndReconciler(t) ctx := context.Background() - opKey := types.NamespacedName{Name: fmt.Sprintf("operator-test-%s", rand.String(8))} + extKey := types.NamespacedName{Name: fmt.Sprintf("cluster-extension-test-%s", rand.String(8))} const pkgName = "prometheus" - t.Log("When the operator specifies a valid available package") + t.Log("When the cluster extension specifies a valid available package") t.Log("By initializing cluster state") - operator := &operatorsv1alpha1.Operator{ - ObjectMeta: metav1.ObjectMeta{Name: opKey.Name}, - Spec: operatorsv1alpha1.OperatorSpec{PackageName: pkgName}, + clusterExtension := &ocv1alpha1.ClusterExtension{ + ObjectMeta: metav1.ObjectMeta{Name: extKey.Name}, + Spec: ocv1alpha1.ClusterExtensionSpec{PackageName: pkgName}, } - require.NoError(t, cl.Create(ctx, operator)) + require.NoError(t, cl.Create(ctx, clusterExtension)) t.Log("When an out-of-date BundleDeployment exists") t.Log("By creating the expected BD") bd := &rukpakv1alpha1.BundleDeployment{ - ObjectMeta: metav1.ObjectMeta{Name: opKey.Name}, + ObjectMeta: metav1.ObjectMeta{Name: extKey.Name}, Spec: rukpakv1alpha1.BundleDeploymentSpec{ ProvisionerClassName: "foo", Template: rukpakv1alpha1.BundleTemplate{ @@ -582,16 +582,16 @@ func TestOperatorExpectedBundleDeployment(t *testing.T) { require.NoError(t, cl.Create(ctx, bd)) t.Log("By running reconcile") - res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) + res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: extKey}) require.Equal(t, ctrl.Result{}, res) require.NoError(t, err) - t.Log("By fetching updated operator after reconcile") - require.NoError(t, cl.Get(ctx, opKey, operator)) + t.Log("By fetching updated cluster extension after reconcile") + require.NoError(t, cl.Get(ctx, extKey, clusterExtension)) t.Log("It results in the expected BundleDeployment") bd = &rukpakv1alpha1.BundleDeployment{} - require.NoError(t, cl.Get(ctx, types.NamespacedName{Name: opKey.Name}, bd)) + require.NoError(t, cl.Get(ctx, types.NamespacedName{Name: extKey.Name}, bd)) require.Equal(t, "core-rukpak-io-plain", bd.Spec.ProvisionerClassName) require.Equal(t, "core-rukpak-io-registry", bd.Spec.Template.Spec.ProvisionerClassName) require.Equal(t, rukpakv1alpha1.SourceTypeImage, bd.Spec.Template.Spec.Source.Type) @@ -599,716 +599,716 @@ func TestOperatorExpectedBundleDeployment(t *testing.T) { require.Equal(t, "quay.io/operatorhubio/prometheus@fake2.0.0", bd.Spec.Template.Spec.Source.Image.Ref) t.Log("It sets the resolvedBundleResource status field") - require.Equal(t, "quay.io/operatorhubio/prometheus@fake2.0.0", operator.Status.ResolvedBundleResource) + require.Equal(t, "quay.io/operatorhubio/prometheus@fake2.0.0", clusterExtension.Status.ResolvedBundleResource) t.Log("It sets the InstalledBundleResource status field") - require.Empty(t, operator.Status.InstalledBundleResource) + require.Empty(t, clusterExtension.Status.InstalledBundleResource) t.Log("It sets resolution to unknown status") - cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeResolved) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) require.NotNil(t, cond) require.Equal(t, metav1.ConditionTrue, cond.Status) - require.Equal(t, operatorsv1alpha1.ReasonSuccess, cond.Reason) + require.Equal(t, ocv1alpha1.ReasonSuccess, cond.Reason) require.Equal(t, "resolved to \"quay.io/operatorhubio/prometheus@fake2.0.0\"", cond.Message) - cond = apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeInstalled) + cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) require.NotNil(t, cond) require.Equal(t, metav1.ConditionUnknown, cond.Status) - require.Equal(t, operatorsv1alpha1.ReasonInstallationStatusUnknown, cond.Reason) + require.Equal(t, ocv1alpha1.ReasonInstallationStatusUnknown, cond.Reason) require.Equal(t, "bundledeployment status is unknown", cond.Message) - verifyInvariants(ctx, t, reconciler.Client, operator) - require.NoError(t, cl.DeleteAllOf(ctx, &operatorsv1alpha1.Operator{})) + verifyInvariants(ctx, t, reconciler.Client, clusterExtension) + require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) require.NoError(t, cl.DeleteAllOf(ctx, &rukpakv1alpha1.BundleDeployment{})) } -func TestOperatorDuplicatePackage(t *testing.T) { +func TestClusterExtensionDuplicatePackage(t *testing.T) { cl, reconciler := newClientAndReconciler(t) ctx := context.Background() - opKey := types.NamespacedName{Name: fmt.Sprintf("operator-test-%s", rand.String(8))} + extKey := types.NamespacedName{Name: fmt.Sprintf("cluster-extension-test-%s", rand.String(8))} const pkgName = "prometheus" - t.Log("When the operator specifies a duplicate package") + t.Log("When the cluster extension specifies a duplicate package") t.Log("By initializing cluster state") - dupOperator := &operatorsv1alpha1.Operator{ - ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("orig-%s", opKey.Name)}, - Spec: operatorsv1alpha1.OperatorSpec{PackageName: pkgName}, + dupClusterExtension := &ocv1alpha1.ClusterExtension{ + ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("orig-%s", extKey.Name)}, + Spec: ocv1alpha1.ClusterExtensionSpec{PackageName: pkgName}, } - require.NoError(t, cl.Create(ctx, dupOperator)) + require.NoError(t, cl.Create(ctx, dupClusterExtension)) - operator := &operatorsv1alpha1.Operator{ - ObjectMeta: metav1.ObjectMeta{Name: opKey.Name}, - Spec: operatorsv1alpha1.OperatorSpec{PackageName: pkgName}, + clusterExtension := &ocv1alpha1.ClusterExtension{ + ObjectMeta: metav1.ObjectMeta{Name: extKey.Name}, + Spec: ocv1alpha1.ClusterExtensionSpec{PackageName: pkgName}, } - require.NoError(t, cl.Create(ctx, operator)) + require.NoError(t, cl.Create(ctx, clusterExtension)) t.Log("It sets resolution failure status") t.Log("By running reconcile") - res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) + res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: extKey}) require.Equal(t, ctrl.Result{}, res) require.EqualError(t, err, `duplicate identifier "required package prometheus" in input`) - t.Log("By fetching updated operator after reconcile") - require.NoError(t, cl.Get(ctx, opKey, operator)) + t.Log("By fetching updated cluster extension after reconcile") + require.NoError(t, cl.Get(ctx, extKey, clusterExtension)) t.Log("By checking the status fields") - require.Empty(t, operator.Status.ResolvedBundleResource) - require.Empty(t, operator.Status.InstalledBundleResource) + require.Empty(t, clusterExtension.Status.ResolvedBundleResource) + require.Empty(t, clusterExtension.Status.InstalledBundleResource) t.Log("By checking the expected conditions") - cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeResolved) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) require.NotNil(t, cond) require.Equal(t, metav1.ConditionFalse, cond.Status) - require.Equal(t, operatorsv1alpha1.ReasonResolutionFailed, cond.Reason) + require.Equal(t, ocv1alpha1.ReasonResolutionFailed, cond.Reason) require.Equal(t, `duplicate identifier "required package prometheus" in input`, cond.Message) - cond = apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeInstalled) + cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) require.NotNil(t, cond) require.Equal(t, metav1.ConditionUnknown, cond.Status) - require.Equal(t, operatorsv1alpha1.ReasonInstallationStatusUnknown, cond.Reason) + require.Equal(t, ocv1alpha1.ReasonInstallationStatusUnknown, cond.Reason) require.Equal(t, "installation has not been attempted as resolution failed", cond.Message) - verifyInvariants(ctx, t, reconciler.Client, operator) - require.NoError(t, cl.DeleteAllOf(ctx, &operatorsv1alpha1.Operator{})) + verifyInvariants(ctx, t, reconciler.Client, clusterExtension) + require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) require.NoError(t, cl.DeleteAllOf(ctx, &rukpakv1alpha1.BundleDeployment{})) } -func TestOperatorChannelVersionExists(t *testing.T) { +func TestClusterExtensionChannelVersionExists(t *testing.T) { cl, reconciler := newClientAndReconciler(t) ctx := context.Background() - opKey := types.NamespacedName{Name: fmt.Sprintf("operator-test-%s", rand.String(8))} + extKey := types.NamespacedName{Name: fmt.Sprintf("cluster-extension-test-%s", rand.String(8))} - t.Log("When the operator specifies a channel with version that exist") + t.Log("When the cluster extension specifies a channel with version that exist") t.Log("By initializing cluster state") pkgName := "prometheus" pkgVer := "1.0.0" pkgChan := "beta" - operator := &operatorsv1alpha1.Operator{ - ObjectMeta: metav1.ObjectMeta{Name: opKey.Name}, - Spec: operatorsv1alpha1.OperatorSpec{ + clusterExtension := &ocv1alpha1.ClusterExtension{ + ObjectMeta: metav1.ObjectMeta{Name: extKey.Name}, + Spec: ocv1alpha1.ClusterExtensionSpec{ PackageName: pkgName, Version: pkgVer, Channel: pkgChan, }, } - err := cl.Create(ctx, operator) + err := cl.Create(ctx, clusterExtension) require.NoError(t, err) t.Log("It sets resolution success status") t.Log("By running reconcile") - res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) + res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: extKey}) require.Equal(t, ctrl.Result{}, res) require.NoError(t, err) - t.Log("By fetching updated operator after reconcile") - require.NoError(t, cl.Get(ctx, opKey, operator)) + t.Log("By fetching updated cluster extension after reconcile") + require.NoError(t, cl.Get(ctx, extKey, clusterExtension)) t.Log("By checking the status fields") - require.Equal(t, "quay.io/operatorhubio/prometheus@fake1.0.0", operator.Status.ResolvedBundleResource) - require.Empty(t, operator.Status.InstalledBundleResource) + require.Equal(t, "quay.io/operatorhubio/prometheus@fake1.0.0", clusterExtension.Status.ResolvedBundleResource) + require.Empty(t, clusterExtension.Status.InstalledBundleResource) t.Log("By checking the expected conditions") - cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeResolved) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) require.NotNil(t, cond) require.Equal(t, metav1.ConditionTrue, cond.Status) - require.Equal(t, operatorsv1alpha1.ReasonSuccess, cond.Reason) + require.Equal(t, ocv1alpha1.ReasonSuccess, cond.Reason) require.Equal(t, "resolved to \"quay.io/operatorhubio/prometheus@fake1.0.0\"", cond.Message) - cond = apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeInstalled) + cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) require.NotNil(t, cond) require.Equal(t, metav1.ConditionUnknown, cond.Status) - require.Equal(t, operatorsv1alpha1.ReasonInstallationStatusUnknown, cond.Reason) + require.Equal(t, ocv1alpha1.ReasonInstallationStatusUnknown, cond.Reason) require.Equal(t, "bundledeployment status is unknown", cond.Message) t.Log("By fetching the bundled deployment") bd := &rukpakv1alpha1.BundleDeployment{} - require.NoError(t, cl.Get(ctx, types.NamespacedName{Name: opKey.Name}, bd)) + require.NoError(t, cl.Get(ctx, types.NamespacedName{Name: extKey.Name}, bd)) require.Equal(t, "core-rukpak-io-plain", bd.Spec.ProvisionerClassName) require.Equal(t, "core-rukpak-io-registry", bd.Spec.Template.Spec.ProvisionerClassName) require.Equal(t, rukpakv1alpha1.SourceTypeImage, bd.Spec.Template.Spec.Source.Type) require.NotNil(t, bd.Spec.Template.Spec.Source.Image) require.Equal(t, "quay.io/operatorhubio/prometheus@fake1.0.0", bd.Spec.Template.Spec.Source.Image.Ref) - verifyInvariants(ctx, t, reconciler.Client, operator) - require.NoError(t, cl.DeleteAllOf(ctx, &operatorsv1alpha1.Operator{})) + verifyInvariants(ctx, t, reconciler.Client, clusterExtension) + require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) require.NoError(t, cl.DeleteAllOf(ctx, &rukpakv1alpha1.BundleDeployment{})) } -func TestOperatorChannelExistsNoVersion(t *testing.T) { +func TestClusterExtensionChannelExistsNoVersion(t *testing.T) { cl, reconciler := newClientAndReconciler(t) ctx := context.Background() - opKey := types.NamespacedName{Name: fmt.Sprintf("operator-test-%s", rand.String(8))} + extKey := types.NamespacedName{Name: fmt.Sprintf("cluster-extension-test-%s", rand.String(8))} - t.Log("When the operator specifies a package that exists within a channel but no version specified") + t.Log("When the cluster extension specifies a package that exists within a channel but no version specified") t.Log("By initializing cluster state") pkgName := "prometheus" pkgVer := "" pkgChan := "beta" - operator := &operatorsv1alpha1.Operator{ - ObjectMeta: metav1.ObjectMeta{Name: opKey.Name}, - Spec: operatorsv1alpha1.OperatorSpec{ + clusterExtension := &ocv1alpha1.ClusterExtension{ + ObjectMeta: metav1.ObjectMeta{Name: extKey.Name}, + Spec: ocv1alpha1.ClusterExtensionSpec{ PackageName: pkgName, Version: pkgVer, Channel: pkgChan, }, } - require.NoError(t, cl.Create(ctx, operator)) + require.NoError(t, cl.Create(ctx, clusterExtension)) t.Log("It sets resolution success status") t.Log("By running reconcile") - res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) + res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: extKey}) require.Equal(t, ctrl.Result{}, res) require.NoError(t, err) - t.Log("By fetching updated operator after reconcile") - require.NoError(t, cl.Get(ctx, opKey, operator)) + t.Log("By fetching updated cluster extension after reconcile") + require.NoError(t, cl.Get(ctx, extKey, clusterExtension)) t.Log("By checking the status fields") - require.Equal(t, "quay.io/operatorhubio/prometheus@fake2.0.0", operator.Status.ResolvedBundleResource) - require.Empty(t, operator.Status.InstalledBundleResource) + require.Equal(t, "quay.io/operatorhubio/prometheus@fake2.0.0", clusterExtension.Status.ResolvedBundleResource) + require.Empty(t, clusterExtension.Status.InstalledBundleResource) t.Log("By checking the expected conditions") - cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeResolved) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) require.NotNil(t, cond) require.Equal(t, metav1.ConditionTrue, cond.Status) - require.Equal(t, operatorsv1alpha1.ReasonSuccess, cond.Reason) + require.Equal(t, ocv1alpha1.ReasonSuccess, cond.Reason) require.Equal(t, "resolved to \"quay.io/operatorhubio/prometheus@fake2.0.0\"", cond.Message) - cond = apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeInstalled) + cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) require.NotNil(t, cond) require.Equal(t, metav1.ConditionUnknown, cond.Status) - require.Equal(t, operatorsv1alpha1.ReasonInstallationStatusUnknown, cond.Reason) + require.Equal(t, ocv1alpha1.ReasonInstallationStatusUnknown, cond.Reason) require.Equal(t, "bundledeployment status is unknown", cond.Message) t.Log("By fetching the bundledeployment") bd := &rukpakv1alpha1.BundleDeployment{} - require.NoError(t, cl.Get(ctx, types.NamespacedName{Name: opKey.Name}, bd)) + require.NoError(t, cl.Get(ctx, types.NamespacedName{Name: extKey.Name}, bd)) require.Equal(t, "core-rukpak-io-plain", bd.Spec.ProvisionerClassName) require.Equal(t, "core-rukpak-io-registry", bd.Spec.Template.Spec.ProvisionerClassName) require.Equal(t, rukpakv1alpha1.SourceTypeImage, bd.Spec.Template.Spec.Source.Type) require.NotNil(t, bd.Spec.Template.Spec.Source.Image) require.Equal(t, "quay.io/operatorhubio/prometheus@fake2.0.0", bd.Spec.Template.Spec.Source.Image.Ref) - verifyInvariants(ctx, t, reconciler.Client, operator) - require.NoError(t, cl.DeleteAllOf(ctx, &operatorsv1alpha1.Operator{})) + verifyInvariants(ctx, t, reconciler.Client, clusterExtension) + require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) require.NoError(t, cl.DeleteAllOf(ctx, &rukpakv1alpha1.BundleDeployment{})) } -func TestOperatorVersionNoChannel(t *testing.T) { +func TestClusterExtensionVersionNoChannel(t *testing.T) { cl, reconciler := newClientAndReconciler(t) ctx := context.Background() - opKey := types.NamespacedName{Name: fmt.Sprintf("operator-test-%s", rand.String(8))} + extKey := types.NamespacedName{Name: fmt.Sprintf("cluster-extension-test-%s", rand.String(8))} - t.Log("When the operator specifies a package version in a channel that does not exist") + t.Log("When the cluster extension specifies a package version in a channel that does not exist") t.Log("By initializing cluster state") pkgName := "prometheus" pkgVer := "0.47.0" pkgChan := "alpha" - operator := &operatorsv1alpha1.Operator{ - ObjectMeta: metav1.ObjectMeta{Name: opKey.Name}, - Spec: operatorsv1alpha1.OperatorSpec{ + clusterExtension := &ocv1alpha1.ClusterExtension{ + ObjectMeta: metav1.ObjectMeta{Name: extKey.Name}, + Spec: ocv1alpha1.ClusterExtensionSpec{ PackageName: pkgName, Version: pkgVer, Channel: pkgChan, }, } - require.NoError(t, cl.Create(ctx, operator)) + require.NoError(t, cl.Create(ctx, clusterExtension)) t.Log("It sets resolution failure status") t.Log("By running reconcile") - res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) + res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: extKey}) require.Equal(t, ctrl.Result{}, res) require.EqualError(t, err, fmt.Sprintf("no package %q matching version %q found in channel %q", pkgName, pkgVer, pkgChan)) - t.Log("By fetching updated operator after reconcile") - require.NoError(t, cl.Get(ctx, opKey, operator)) + t.Log("By fetching updated cluster extension after reconcile") + require.NoError(t, cl.Get(ctx, extKey, clusterExtension)) t.Log("By checking the status fields") - require.Empty(t, operator.Status.ResolvedBundleResource) - require.Empty(t, operator.Status.InstalledBundleResource) + require.Empty(t, clusterExtension.Status.ResolvedBundleResource) + require.Empty(t, clusterExtension.Status.InstalledBundleResource) t.Log("By checking the expected conditions") - cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeResolved) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) require.NotNil(t, cond) require.Equal(t, metav1.ConditionFalse, cond.Status) - require.Equal(t, operatorsv1alpha1.ReasonResolutionFailed, cond.Reason) + require.Equal(t, ocv1alpha1.ReasonResolutionFailed, cond.Reason) require.Equal(t, fmt.Sprintf("no package %q matching version %q found in channel %q", pkgName, pkgVer, pkgChan), cond.Message) - cond = apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeInstalled) + cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) require.NotNil(t, cond) require.Equal(t, metav1.ConditionUnknown, cond.Status) - require.Equal(t, operatorsv1alpha1.ReasonInstallationStatusUnknown, cond.Reason) + require.Equal(t, ocv1alpha1.ReasonInstallationStatusUnknown, cond.Reason) require.Equal(t, "installation has not been attempted due to failure to gather data for resolution", cond.Message) - verifyInvariants(ctx, t, reconciler.Client, operator) - require.NoError(t, cl.DeleteAllOf(ctx, &operatorsv1alpha1.Operator{})) + verifyInvariants(ctx, t, reconciler.Client, clusterExtension) + require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) require.NoError(t, cl.DeleteAllOf(ctx, &rukpakv1alpha1.BundleDeployment{})) } -func TestOperatorNoChannel(t *testing.T) { +func TestClusterExtensionNoChannel(t *testing.T) { cl, reconciler := newClientAndReconciler(t) ctx := context.Background() - opKey := types.NamespacedName{Name: fmt.Sprintf("operator-test-%s", rand.String(8))} + extKey := types.NamespacedName{Name: fmt.Sprintf("cluster-extension-test-%s", rand.String(8))} - t.Log("When the operator specifies a package in a channel that does not exist") + t.Log("When the cluster extension specifies a package in a channel that does not exist") t.Log("By initializing cluster state") pkgName := "prometheus" pkgChan := "non-existent" - operator := &operatorsv1alpha1.Operator{ - ObjectMeta: metav1.ObjectMeta{Name: opKey.Name}, - Spec: operatorsv1alpha1.OperatorSpec{ + clusterExtension := &ocv1alpha1.ClusterExtension{ + ObjectMeta: metav1.ObjectMeta{Name: extKey.Name}, + Spec: ocv1alpha1.ClusterExtensionSpec{ PackageName: pkgName, Channel: pkgChan, }, } - require.NoError(t, cl.Create(ctx, operator)) + require.NoError(t, cl.Create(ctx, clusterExtension)) t.Log("It sets resolution failure status") t.Log("By running reconcile") - res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) + res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: extKey}) require.Equal(t, ctrl.Result{}, res) require.EqualError(t, err, fmt.Sprintf("no package %q found in channel %q", pkgName, pkgChan)) - t.Log("By fetching updated operator after reconcile") - require.NoError(t, cl.Get(ctx, opKey, operator)) + t.Log("By fetching updated cluster extension after reconcile") + require.NoError(t, cl.Get(ctx, extKey, clusterExtension)) t.Log("By checking the status fields") - require.Empty(t, operator.Status.ResolvedBundleResource) - require.Empty(t, operator.Status.InstalledBundleResource) + require.Empty(t, clusterExtension.Status.ResolvedBundleResource) + require.Empty(t, clusterExtension.Status.InstalledBundleResource) t.Log("By checking the expected conditions") - cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeResolved) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) require.NotNil(t, cond) require.Equal(t, metav1.ConditionFalse, cond.Status) - require.Equal(t, operatorsv1alpha1.ReasonResolutionFailed, cond.Reason) + require.Equal(t, ocv1alpha1.ReasonResolutionFailed, cond.Reason) require.Equal(t, fmt.Sprintf("no package %q found in channel %q", pkgName, pkgChan), cond.Message) - cond = apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeInstalled) + cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) require.NotNil(t, cond) require.Equal(t, metav1.ConditionUnknown, cond.Status) - require.Equal(t, operatorsv1alpha1.ReasonInstallationStatusUnknown, cond.Reason) + require.Equal(t, ocv1alpha1.ReasonInstallationStatusUnknown, cond.Reason) require.Equal(t, "installation has not been attempted due to failure to gather data for resolution", cond.Message) - verifyInvariants(ctx, t, reconciler.Client, operator) - require.NoError(t, cl.DeleteAllOf(ctx, &operatorsv1alpha1.Operator{})) + verifyInvariants(ctx, t, reconciler.Client, clusterExtension) + require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) require.NoError(t, cl.DeleteAllOf(ctx, &rukpakv1alpha1.BundleDeployment{})) } -func TestOperatorNoVersion(t *testing.T) { +func TestClusterExtensionNoVersion(t *testing.T) { cl, reconciler := newClientAndReconciler(t) ctx := context.Background() - opKey := types.NamespacedName{Name: fmt.Sprintf("operator-test-%s", rand.String(8))} + extKey := types.NamespacedName{Name: fmt.Sprintf("cluster-extension-test-%s", rand.String(8))} - t.Log("When the operator specifies a package version that does not exist in the channel") + t.Log("When the cluster extension specifies a package version that does not exist in the channel") t.Log("By initializing cluster state") pkgName := "prometheus" pkgVer := "0.57.0" pkgChan := "non-existent" - operator := &operatorsv1alpha1.Operator{ - ObjectMeta: metav1.ObjectMeta{Name: opKey.Name}, - Spec: operatorsv1alpha1.OperatorSpec{ + clusterExtension := &ocv1alpha1.ClusterExtension{ + ObjectMeta: metav1.ObjectMeta{Name: extKey.Name}, + Spec: ocv1alpha1.ClusterExtensionSpec{ PackageName: pkgName, Version: pkgVer, Channel: pkgChan, }, } - require.NoError(t, cl.Create(ctx, operator)) + require.NoError(t, cl.Create(ctx, clusterExtension)) t.Log("It sets resolution failure status") t.Log("By running reconcile") - res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) + res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: extKey}) require.Equal(t, ctrl.Result{}, res) require.EqualError(t, err, fmt.Sprintf("no package %q matching version %q found in channel %q", pkgName, pkgVer, pkgChan)) - t.Log("By fetching updated operator after reconcile") - require.NoError(t, cl.Get(ctx, opKey, operator)) + t.Log("By fetching updated cluster extension after reconcile") + require.NoError(t, cl.Get(ctx, extKey, clusterExtension)) t.Log("By checking the status fields") - require.Empty(t, operator.Status.ResolvedBundleResource) - require.Empty(t, operator.Status.InstalledBundleResource) + require.Empty(t, clusterExtension.Status.ResolvedBundleResource) + require.Empty(t, clusterExtension.Status.InstalledBundleResource) t.Log("By checking the expected conditions") - cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeResolved) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) require.NotNil(t, cond) require.Equal(t, metav1.ConditionFalse, cond.Status) - require.Equal(t, operatorsv1alpha1.ReasonResolutionFailed, cond.Reason) + require.Equal(t, ocv1alpha1.ReasonResolutionFailed, cond.Reason) require.Equal(t, fmt.Sprintf("no package %q matching version %q found in channel %q", pkgName, pkgVer, pkgChan), cond.Message) - cond = apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeInstalled) + cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) require.NotNil(t, cond) require.Equal(t, metav1.ConditionUnknown, cond.Status) - require.Equal(t, operatorsv1alpha1.ReasonInstallationStatusUnknown, cond.Reason) + require.Equal(t, ocv1alpha1.ReasonInstallationStatusUnknown, cond.Reason) require.Equal(t, "installation has not been attempted due to failure to gather data for resolution", cond.Message) - verifyInvariants(ctx, t, reconciler.Client, operator) - require.NoError(t, cl.DeleteAllOf(ctx, &operatorsv1alpha1.Operator{})) + verifyInvariants(ctx, t, reconciler.Client, clusterExtension) + require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) require.NoError(t, cl.DeleteAllOf(ctx, &rukpakv1alpha1.BundleDeployment{})) } -func TestOperatorPlainV0Bundle(t *testing.T) { +func TestClusterExtensionPlainV0Bundle(t *testing.T) { cl, reconciler := newClientAndReconciler(t) ctx := context.Background() - opKey := types.NamespacedName{Name: fmt.Sprintf("operator-test-%s", rand.String(8))} + extKey := types.NamespacedName{Name: fmt.Sprintf("cluster-extension-test-%s", rand.String(8))} - t.Log("When the operator specifies a package with a plain+v0 bundle") + t.Log("When the cluster extension specifies a package with a plain+v0 bundle") t.Log("By initializing cluster state") pkgName := "plain" pkgVer := "0.1.0" pkgChan := "beta" - operator := &operatorsv1alpha1.Operator{ - ObjectMeta: metav1.ObjectMeta{Name: opKey.Name}, - Spec: operatorsv1alpha1.OperatorSpec{ + clusterExtension := &ocv1alpha1.ClusterExtension{ + ObjectMeta: metav1.ObjectMeta{Name: extKey.Name}, + Spec: ocv1alpha1.ClusterExtensionSpec{ PackageName: pkgName, Version: pkgVer, Channel: pkgChan, }, } - require.NoError(t, cl.Create(ctx, operator)) + require.NoError(t, cl.Create(ctx, clusterExtension)) t.Log("It sets resolution success status") t.Log("By running reconcile") - res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) + res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: extKey}) require.Equal(t, ctrl.Result{}, res) require.NoError(t, err) - t.Log("By fetching updated operator after reconcile") - require.NoError(t, cl.Get(ctx, opKey, operator)) + t.Log("By fetching updated cluster extension after reconcile") + require.NoError(t, cl.Get(ctx, extKey, clusterExtension)) t.Log("By checking the status fields") - require.Equal(t, "quay.io/operatorhub/plain@sha256:plain", operator.Status.ResolvedBundleResource) - require.Empty(t, operator.Status.InstalledBundleResource) + require.Equal(t, "quay.io/operatorhub/plain@sha256:plain", clusterExtension.Status.ResolvedBundleResource) + require.Empty(t, clusterExtension.Status.InstalledBundleResource) t.Log("By checking the expected conditions") - cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeResolved) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) require.NotNil(t, cond) require.Equal(t, metav1.ConditionTrue, cond.Status) - require.Equal(t, operatorsv1alpha1.ReasonSuccess, cond.Reason) + require.Equal(t, ocv1alpha1.ReasonSuccess, cond.Reason) require.Equal(t, "resolved to \"quay.io/operatorhub/plain@sha256:plain\"", cond.Message) - cond = apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeInstalled) + cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) require.NotNil(t, cond) require.Equal(t, metav1.ConditionUnknown, cond.Status) - require.Equal(t, operatorsv1alpha1.ReasonInstallationStatusUnknown, cond.Reason) + require.Equal(t, ocv1alpha1.ReasonInstallationStatusUnknown, cond.Reason) require.Equal(t, "bundledeployment status is unknown", cond.Message) t.Log("By fetching the bundled deployment") bd := &rukpakv1alpha1.BundleDeployment{} - require.NoError(t, cl.Get(ctx, types.NamespacedName{Name: opKey.Name}, bd)) + require.NoError(t, cl.Get(ctx, types.NamespacedName{Name: extKey.Name}, bd)) require.Equal(t, "core-rukpak-io-plain", bd.Spec.ProvisionerClassName) require.Equal(t, "core-rukpak-io-plain", bd.Spec.Template.Spec.ProvisionerClassName) require.Equal(t, rukpakv1alpha1.SourceTypeImage, bd.Spec.Template.Spec.Source.Type) require.NotNil(t, bd.Spec.Template.Spec.Source.Image) require.Equal(t, "quay.io/operatorhub/plain@sha256:plain", bd.Spec.Template.Spec.Source.Image.Ref) - verifyInvariants(ctx, t, reconciler.Client, operator) - require.NoError(t, cl.DeleteAllOf(ctx, &operatorsv1alpha1.Operator{})) + verifyInvariants(ctx, t, reconciler.Client, clusterExtension) + require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) require.NoError(t, cl.DeleteAllOf(ctx, &rukpakv1alpha1.BundleDeployment{})) } -func TestOperatorBadBundleMediaType(t *testing.T) { +func TestClusterExtensionBadBundleMediaType(t *testing.T) { cl, reconciler := newClientAndReconciler(t) ctx := context.Background() - opKey := types.NamespacedName{Name: fmt.Sprintf("operator-test-%s", rand.String(8))} + extKey := types.NamespacedName{Name: fmt.Sprintf("cluster-extension-test-%s", rand.String(8))} - t.Log("When the operator specifies a package with a bad bundle mediatype") + t.Log("When the cluster extension specifies a package with a bad bundle mediatype") t.Log("By initializing cluster state") pkgName := "badmedia" pkgVer := "0.1.0" pkgChan := "beta" - operator := &operatorsv1alpha1.Operator{ - ObjectMeta: metav1.ObjectMeta{Name: opKey.Name}, - Spec: operatorsv1alpha1.OperatorSpec{ + clusterExtension := &ocv1alpha1.ClusterExtension{ + ObjectMeta: metav1.ObjectMeta{Name: extKey.Name}, + Spec: ocv1alpha1.ClusterExtensionSpec{ PackageName: pkgName, Version: pkgVer, Channel: pkgChan, }, } - require.NoError(t, cl.Create(ctx, operator)) + require.NoError(t, cl.Create(ctx, clusterExtension)) t.Log("It sets resolution success status") t.Log("By running reconcile") - res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) + res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: extKey}) require.Equal(t, ctrl.Result{}, res) require.Error(t, err) require.ErrorContains(t, err, "unknown bundle mediatype: badmedia+v1") - t.Log("By fetching updated operator after reconcile") - require.NoError(t, cl.Get(ctx, opKey, operator)) + t.Log("By fetching updated cluster extension after reconcile") + require.NoError(t, cl.Get(ctx, extKey, clusterExtension)) t.Log("By checking the status fields") - require.Equal(t, "quay.io/operatorhub/badmedia@sha256:badmedia", operator.Status.ResolvedBundleResource) - require.Empty(t, operator.Status.InstalledBundleResource) + require.Equal(t, "quay.io/operatorhub/badmedia@sha256:badmedia", clusterExtension.Status.ResolvedBundleResource) + require.Empty(t, clusterExtension.Status.InstalledBundleResource) t.Log("By checking the expected conditions") - cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeResolved) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) require.NotNil(t, cond) require.Equal(t, metav1.ConditionTrue, cond.Status) - require.Equal(t, operatorsv1alpha1.ReasonSuccess, cond.Reason) + require.Equal(t, ocv1alpha1.ReasonSuccess, cond.Reason) require.Equal(t, "resolved to \"quay.io/operatorhub/badmedia@sha256:badmedia\"", cond.Message) - cond = apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeInstalled) + cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) require.NotNil(t, cond) require.Equal(t, metav1.ConditionFalse, cond.Status) - require.Equal(t, operatorsv1alpha1.ReasonInstallationFailed, cond.Reason) + require.Equal(t, ocv1alpha1.ReasonInstallationFailed, cond.Reason) require.Equal(t, "unknown bundle mediatype: badmedia+v1", cond.Message) - verifyInvariants(ctx, t, reconciler.Client, operator) - require.NoError(t, cl.DeleteAllOf(ctx, &operatorsv1alpha1.Operator{})) + verifyInvariants(ctx, t, reconciler.Client, clusterExtension) + require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) require.NoError(t, cl.DeleteAllOf(ctx, &rukpakv1alpha1.BundleDeployment{})) } -func TestOperatorInvalidSemverPastRegex(t *testing.T) { +func TestClusterExtensionInvalidSemverPastRegex(t *testing.T) { cl, reconciler := newClientAndReconciler(t) ctx := context.Background() t.Log("When an invalid semver is provided that bypasses the regex validation") - opKey := types.NamespacedName{Name: fmt.Sprintf("operator-validation-test-%s", rand.String(8))} + extKey := types.NamespacedName{Name: fmt.Sprintf("clusterextension-validation-test-%s", rand.String(8))} - t.Log("By injecting creating a client with the bad operator CR") + t.Log("By injecting creating a client with the bad clusterextension CR") pkgName := fmt.Sprintf("exists-%s", rand.String(6)) - operator := &operatorsv1alpha1.Operator{ - ObjectMeta: metav1.ObjectMeta{Name: opKey.Name}, - Spec: operatorsv1alpha1.OperatorSpec{ + clusterExtension := &ocv1alpha1.ClusterExtension{ + ObjectMeta: metav1.ObjectMeta{Name: extKey.Name}, + Spec: ocv1alpha1.ClusterExtensionSpec{ PackageName: pkgName, Version: "1.2.3-123abc_def", // bad semver that matches the regex on the CR validation }, } // this bypasses client/server-side CR validation and allows us to test the reconciler's validation - fakeClient := fake.NewClientBuilder().WithScheme(sch).WithObjects(operator).WithStatusSubresource(operator).Build() + fakeClient := fake.NewClientBuilder().WithScheme(sch).WithObjects(clusterExtension).WithStatusSubresource(clusterExtension).Build() t.Log("By changing the reconciler client to the fake client") reconciler.Client = fakeClient t.Log("It should add an invalid spec condition and *not* re-enqueue for reconciliation") t.Log("By running reconcile") - res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) + res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: extKey}) require.Equal(t, ctrl.Result{}, res) require.NoError(t, err) - t.Log("By fetching updated operator after reconcile") - require.NoError(t, fakeClient.Get(ctx, opKey, operator)) + t.Log("By fetching updated cluster extension after reconcile") + require.NoError(t, fakeClient.Get(ctx, extKey, clusterExtension)) t.Log("By checking the status fields") - require.Empty(t, operator.Status.ResolvedBundleResource) - require.Empty(t, operator.Status.InstalledBundleResource) + require.Empty(t, clusterExtension.Status.ResolvedBundleResource) + require.Empty(t, clusterExtension.Status.InstalledBundleResource) t.Log("By checking the expected conditions") - cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeResolved) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) require.NotNil(t, cond) require.Equal(t, metav1.ConditionUnknown, cond.Status) - require.Equal(t, operatorsv1alpha1.ReasonResolutionUnknown, cond.Reason) + require.Equal(t, ocv1alpha1.ReasonResolutionUnknown, cond.Reason) require.Equal(t, "validation has not been attempted as spec is invalid", cond.Message) - cond = apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeInstalled) + cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) require.NotNil(t, cond) require.Equal(t, metav1.ConditionUnknown, cond.Status) - require.Equal(t, operatorsv1alpha1.ReasonInstallationStatusUnknown, cond.Reason) + require.Equal(t, ocv1alpha1.ReasonInstallationStatusUnknown, cond.Reason) require.Equal(t, "installation has not been attempted as spec is invalid", cond.Message) - verifyInvariants(ctx, t, reconciler.Client, operator) - require.NoError(t, cl.DeleteAllOf(ctx, &operatorsv1alpha1.Operator{})) + verifyInvariants(ctx, t, reconciler.Client, clusterExtension) + require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) require.NoError(t, cl.DeleteAllOf(ctx, &rukpakv1alpha1.BundleDeployment{})) } -func verifyInvariants(ctx context.Context, t *testing.T, c client.Client, op *operatorsv1alpha1.Operator) { - key := client.ObjectKeyFromObject(op) - require.NoError(t, c.Get(ctx, key, op)) +func verifyInvariants(ctx context.Context, t *testing.T, c client.Client, ext *ocv1alpha1.ClusterExtension) { + key := client.ObjectKeyFromObject(ext) + require.NoError(t, c.Get(ctx, key, ext)) - verifyConditionsInvariants(t, op) + verifyConditionsInvariants(t, ext) } -func verifyConditionsInvariants(t *testing.T, op *operatorsv1alpha1.Operator) { - // Expect that the operator's set of conditions contains all defined - // condition types for the Operator API. Every reconcile should always +func verifyConditionsInvariants(t *testing.T, ext *ocv1alpha1.ClusterExtension) { + // Expect that the cluster extension's set of conditions contains all defined + // condition types for the ClusterExtension API. Every reconcile should always // ensure every condition type's status/reason/message reflects the state // read during _this_ reconcile call. - require.Len(t, op.Status.Conditions, len(conditionsets.ConditionTypes)) + require.Len(t, ext.Status.Conditions, len(conditionsets.ConditionTypes)) for _, tt := range conditionsets.ConditionTypes { - cond := apimeta.FindStatusCondition(op.Status.Conditions, tt) + cond := apimeta.FindStatusCondition(ext.Status.Conditions, tt) require.NotNil(t, cond) require.NotEmpty(t, cond.Status) require.Contains(t, conditionsets.ConditionReasons, cond.Reason) - require.Equal(t, op.GetGeneration(), cond.ObservedGeneration) + require.Equal(t, ext.GetGeneration(), cond.ObservedGeneration) } } -func TestOperatorUpgrade(t *testing.T) { +func TestClusterExtensionUpgrade(t *testing.T) { cl, reconciler := newClientAndReconciler(t) ctx := context.Background() t.Run("semver upgrade constraints enforcement of upgrades within major version", func(t *testing.T) { defer featuregatetesting.SetFeatureGateDuringTest(t, features.OperatorControllerFeatureGate, features.ForceSemverUpgradeConstraints, true)() defer func() { - require.NoError(t, cl.DeleteAllOf(ctx, &operatorsv1alpha1.Operator{})) + require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) require.NoError(t, cl.DeleteAllOf(ctx, &rukpakv1alpha1.BundleDeployment{})) }() pkgName := "prometheus" pkgVer := "1.0.0" pkgChan := "beta" - opKey := types.NamespacedName{Name: fmt.Sprintf("operator-test-%s", rand.String(8))} - operator := &operatorsv1alpha1.Operator{ - ObjectMeta: metav1.ObjectMeta{Name: opKey.Name}, - Spec: operatorsv1alpha1.OperatorSpec{ + extKey := types.NamespacedName{Name: fmt.Sprintf("cluster-extension-test-%s", rand.String(8))} + clusterExtension := &ocv1alpha1.ClusterExtension{ + ObjectMeta: metav1.ObjectMeta{Name: extKey.Name}, + Spec: ocv1alpha1.ClusterExtensionSpec{ PackageName: pkgName, Version: pkgVer, Channel: pkgChan, }, } - // Create an operator - err := cl.Create(ctx, operator) + // Create a cluster extension + err := cl.Create(ctx, clusterExtension) require.NoError(t, err) // Run reconcile - res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) + res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: extKey}) require.NoError(t, err) assert.Equal(t, ctrl.Result{}, res) - // Refresh the operator after reconcile - err = cl.Get(ctx, opKey, operator) + // Refresh the cluster extension after reconcile + err = cl.Get(ctx, extKey, clusterExtension) require.NoError(t, err) // Checking the status fields - assert.Equal(t, "quay.io/operatorhubio/prometheus@fake1.0.0", operator.Status.ResolvedBundleResource) + assert.Equal(t, "quay.io/operatorhubio/prometheus@fake1.0.0", clusterExtension.Status.ResolvedBundleResource) // checking the expected conditions - cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeResolved) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) require.NotNil(t, cond) assert.Equal(t, metav1.ConditionTrue, cond.Status) - assert.Equal(t, operatorsv1alpha1.ReasonSuccess, cond.Reason) + assert.Equal(t, ocv1alpha1.ReasonSuccess, cond.Reason) assert.Equal(t, `resolved to "quay.io/operatorhubio/prometheus@fake1.0.0"`, cond.Message) // Invalid update: can not go to the next major version - operator.Spec.Version = "2.0.0" - err = cl.Update(ctx, operator) + clusterExtension.Spec.Version = "2.0.0" + err = cl.Update(ctx, clusterExtension) require.NoError(t, err) // Run reconcile again - res, err = reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) + res, err = reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: extKey}) require.Error(t, err) assert.Equal(t, ctrl.Result{}, res) - // Refresh the operator after reconcile - err = cl.Get(ctx, opKey, operator) + // Refresh the cluster extension after reconcile + err = cl.Get(ctx, extKey, clusterExtension) require.NoError(t, err) // Checking the status fields // TODO: https://github.com/operator-framework/operator-controller/issues/320 - assert.Equal(t, "", operator.Status.ResolvedBundleResource) + assert.Equal(t, "", clusterExtension.Status.ResolvedBundleResource) // checking the expected conditions - cond = apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeResolved) + cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) require.NotNil(t, cond) assert.Equal(t, metav1.ConditionFalse, cond.Status) - assert.Equal(t, operatorsv1alpha1.ReasonResolutionFailed, cond.Reason) + assert.Equal(t, ocv1alpha1.ReasonResolutionFailed, cond.Reason) assert.Contains(t, cond.Message, "constraints not satisfiable") assert.Regexp(t, "installed package prometheus requires at least one of fake-catalog-prometheus-operatorhub/prometheus/beta/1.2.0, fake-catalog-prometheus-operatorhub/prometheus/beta/1.0.1, fake-catalog-prometheus-operatorhub/prometheus/beta/1.0.0$", cond.Message) // Valid update skipping one version - operator.Spec.Version = "1.2.0" - err = cl.Update(ctx, operator) + clusterExtension.Spec.Version = "1.2.0" + err = cl.Update(ctx, clusterExtension) require.NoError(t, err) // Run reconcile again - res, err = reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) + res, err = reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: extKey}) require.NoError(t, err) assert.Equal(t, ctrl.Result{}, res) - // Refresh the operator after reconcile - err = cl.Get(ctx, opKey, operator) + // Refresh the cluster extension after reconcile + err = cl.Get(ctx, extKey, clusterExtension) require.NoError(t, err) // Checking the status fields - assert.Equal(t, "quay.io/operatorhubio/prometheus@fake1.2.0", operator.Status.ResolvedBundleResource) + assert.Equal(t, "quay.io/operatorhubio/prometheus@fake1.2.0", clusterExtension.Status.ResolvedBundleResource) // checking the expected conditions - cond = apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeResolved) + cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) require.NotNil(t, cond) assert.Equal(t, metav1.ConditionTrue, cond.Status) - assert.Equal(t, operatorsv1alpha1.ReasonSuccess, cond.Reason) + assert.Equal(t, ocv1alpha1.ReasonSuccess, cond.Reason) assert.Equal(t, `resolved to "quay.io/operatorhubio/prometheus@fake1.2.0"`, cond.Message) }) t.Run("legacy semantics upgrade constraints enforcement", func(t *testing.T) { defer featuregatetesting.SetFeatureGateDuringTest(t, features.OperatorControllerFeatureGate, features.ForceSemverUpgradeConstraints, false)() defer func() { - require.NoError(t, cl.DeleteAllOf(ctx, &operatorsv1alpha1.Operator{})) + require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) require.NoError(t, cl.DeleteAllOf(ctx, &rukpakv1alpha1.BundleDeployment{})) }() pkgName := "prometheus" pkgVer := "1.0.0" pkgChan := "beta" - opKey := types.NamespacedName{Name: fmt.Sprintf("operator-test-%s", rand.String(8))} - operator := &operatorsv1alpha1.Operator{ - ObjectMeta: metav1.ObjectMeta{Name: opKey.Name}, - Spec: operatorsv1alpha1.OperatorSpec{ + extKey := types.NamespacedName{Name: fmt.Sprintf("cluster-extension-test-%s", rand.String(8))} + clusterExtension := &ocv1alpha1.ClusterExtension{ + ObjectMeta: metav1.ObjectMeta{Name: extKey.Name}, + Spec: ocv1alpha1.ClusterExtensionSpec{ PackageName: pkgName, Version: pkgVer, Channel: pkgChan, }, } - // Create an operator - err := cl.Create(ctx, operator) + // Create a cluster extension + err := cl.Create(ctx, clusterExtension) require.NoError(t, err) // Run reconcile - res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) + res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: extKey}) require.NoError(t, err) assert.Equal(t, ctrl.Result{}, res) - // Refresh the operator after reconcile - err = cl.Get(ctx, opKey, operator) + // Refresh the cluster extension after reconcile + err = cl.Get(ctx, extKey, clusterExtension) require.NoError(t, err) // Checking the status fields - assert.Equal(t, "quay.io/operatorhubio/prometheus@fake1.0.0", operator.Status.ResolvedBundleResource) + assert.Equal(t, "quay.io/operatorhubio/prometheus@fake1.0.0", clusterExtension.Status.ResolvedBundleResource) // checking the expected conditions - cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeResolved) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) require.NotNil(t, cond) assert.Equal(t, metav1.ConditionTrue, cond.Status) - assert.Equal(t, operatorsv1alpha1.ReasonSuccess, cond.Reason) + assert.Equal(t, ocv1alpha1.ReasonSuccess, cond.Reason) assert.Equal(t, `resolved to "quay.io/operatorhubio/prometheus@fake1.0.0"`, cond.Message) // Invalid update: can not upgrade by skipping a version in the replaces chain - operator.Spec.Version = "1.2.0" - err = cl.Update(ctx, operator) + clusterExtension.Spec.Version = "1.2.0" + err = cl.Update(ctx, clusterExtension) require.NoError(t, err) // Run reconcile again - res, err = reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) + res, err = reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: extKey}) require.Error(t, err) assert.Equal(t, ctrl.Result{}, res) - // Refresh the operator after reconcile - err = cl.Get(ctx, opKey, operator) + // Refresh the cluster extension after reconcile + err = cl.Get(ctx, extKey, clusterExtension) require.NoError(t, err) // Checking the status fields // TODO: https://github.com/operator-framework/operator-controller/issues/320 - assert.Equal(t, "", operator.Status.ResolvedBundleResource) + assert.Equal(t, "", clusterExtension.Status.ResolvedBundleResource) // checking the expected conditions - cond = apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeResolved) + cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) require.NotNil(t, cond) assert.Equal(t, metav1.ConditionFalse, cond.Status) - assert.Equal(t, operatorsv1alpha1.ReasonResolutionFailed, cond.Reason) + assert.Equal(t, ocv1alpha1.ReasonResolutionFailed, cond.Reason) assert.Contains(t, cond.Message, "constraints not satisfiable") assert.Contains(t, cond.Message, "installed package prometheus requires at least one of fake-catalog-prometheus-operatorhub/prometheus/beta/1.0.1, fake-catalog-prometheus-operatorhub/prometheus/beta/1.0.0\n") // Valid update skipping one version - operator.Spec.Version = "1.0.1" - err = cl.Update(ctx, operator) + clusterExtension.Spec.Version = "1.0.1" + err = cl.Update(ctx, clusterExtension) require.NoError(t, err) // Run reconcile again - res, err = reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) + res, err = reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: extKey}) require.NoError(t, err) assert.Equal(t, ctrl.Result{}, res) - // Refresh the operator after reconcile - err = cl.Get(ctx, opKey, operator) + // Refresh the cluster extension after reconcile + err = cl.Get(ctx, extKey, clusterExtension) require.NoError(t, err) // Checking the status fields - assert.Equal(t, "quay.io/operatorhubio/prometheus@fake1.0.1", operator.Status.ResolvedBundleResource) + assert.Equal(t, "quay.io/operatorhubio/prometheus@fake1.0.1", clusterExtension.Status.ResolvedBundleResource) // checking the expected conditions - cond = apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeResolved) + cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) require.NotNil(t, cond) assert.Equal(t, metav1.ConditionTrue, cond.Status) - assert.Equal(t, operatorsv1alpha1.ReasonSuccess, cond.Reason) + assert.Equal(t, ocv1alpha1.ReasonSuccess, cond.Reason) assert.Equal(t, `resolved to "quay.io/operatorhubio/prometheus@fake1.0.1"`, cond.Message) }) @@ -1329,74 +1329,74 @@ func TestOperatorUpgrade(t *testing.T) { t.Run(tt.name, func(t *testing.T) { defer featuregatetesting.SetFeatureGateDuringTest(t, features.OperatorControllerFeatureGate, features.ForceSemverUpgradeConstraints, tt.flagState)() defer func() { - require.NoError(t, cl.DeleteAllOf(ctx, &operatorsv1alpha1.Operator{})) + require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) require.NoError(t, cl.DeleteAllOf(ctx, &rukpakv1alpha1.BundleDeployment{})) }() - opKey := types.NamespacedName{Name: fmt.Sprintf("operator-test-%s", rand.String(8))} - operator := &operatorsv1alpha1.Operator{ - ObjectMeta: metav1.ObjectMeta{Name: opKey.Name}, - Spec: operatorsv1alpha1.OperatorSpec{ + extKey := types.NamespacedName{Name: fmt.Sprintf("cluster-extension-test-%s", rand.String(8))} + clusterExtension := &ocv1alpha1.ClusterExtension{ + ObjectMeta: metav1.ObjectMeta{Name: extKey.Name}, + Spec: ocv1alpha1.ClusterExtensionSpec{ PackageName: "prometheus", Version: "1.0.0", Channel: "beta", - UpgradeConstraintPolicy: operatorsv1alpha1.UpgradeConstraintPolicyIgnore, + UpgradeConstraintPolicy: ocv1alpha1.UpgradeConstraintPolicyIgnore, }, } - // Create an operator - err := cl.Create(ctx, operator) + // Create a cluster extension + err := cl.Create(ctx, clusterExtension) require.NoError(t, err) // Run reconcile - res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) + res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: extKey}) require.NoError(t, err) assert.Equal(t, ctrl.Result{}, res) - // Refresh the operator after reconcile - err = cl.Get(ctx, opKey, operator) + // Refresh the cluster extension after reconcile + err = cl.Get(ctx, extKey, clusterExtension) require.NoError(t, err) // Checking the status fields - assert.Equal(t, "quay.io/operatorhubio/prometheus@fake1.0.0", operator.Status.ResolvedBundleResource) + assert.Equal(t, "quay.io/operatorhubio/prometheus@fake1.0.0", clusterExtension.Status.ResolvedBundleResource) // checking the expected conditions - cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeResolved) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) require.NotNil(t, cond) assert.Equal(t, metav1.ConditionTrue, cond.Status) - assert.Equal(t, operatorsv1alpha1.ReasonSuccess, cond.Reason) + assert.Equal(t, ocv1alpha1.ReasonSuccess, cond.Reason) assert.Equal(t, `resolved to "quay.io/operatorhubio/prometheus@fake1.0.0"`, cond.Message) // We can go to the next major version when using semver // as well as to the version which is not next in the channel // when using legacy constraints - operator.Spec.Version = "2.0.0" - err = cl.Update(ctx, operator) + clusterExtension.Spec.Version = "2.0.0" + err = cl.Update(ctx, clusterExtension) require.NoError(t, err) // Run reconcile again - res, err = reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) + res, err = reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: extKey}) require.NoError(t, err) assert.Equal(t, ctrl.Result{}, res) - // Refresh the operator after reconcile - err = cl.Get(ctx, opKey, operator) + // Refresh the cluster extension after reconcile + err = cl.Get(ctx, extKey, clusterExtension) require.NoError(t, err) // Checking the status fields - assert.Equal(t, "quay.io/operatorhubio/prometheus@fake2.0.0", operator.Status.ResolvedBundleResource) + assert.Equal(t, "quay.io/operatorhubio/prometheus@fake2.0.0", clusterExtension.Status.ResolvedBundleResource) // checking the expected conditions - cond = apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeResolved) + cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) require.NotNil(t, cond) assert.Equal(t, metav1.ConditionTrue, cond.Status) - assert.Equal(t, operatorsv1alpha1.ReasonSuccess, cond.Reason) + assert.Equal(t, ocv1alpha1.ReasonSuccess, cond.Reason) assert.Equal(t, `resolved to "quay.io/operatorhubio/prometheus@fake2.0.0"`, cond.Message) }) } }) } -func TestOperatorDowngrade(t *testing.T) { +func TestClusterExtensionDowngrade(t *testing.T) { cl, reconciler := newClientAndReconciler(t) ctx := context.Background() @@ -1417,65 +1417,65 @@ func TestOperatorDowngrade(t *testing.T) { t.Run(tt.name, func(t *testing.T) { defer featuregatetesting.SetFeatureGateDuringTest(t, features.OperatorControllerFeatureGate, features.ForceSemverUpgradeConstraints, tt.flagState)() defer func() { - require.NoError(t, cl.DeleteAllOf(ctx, &operatorsv1alpha1.Operator{})) + require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) require.NoError(t, cl.DeleteAllOf(ctx, &rukpakv1alpha1.BundleDeployment{})) }() - opKey := types.NamespacedName{Name: fmt.Sprintf("operator-test-%s", rand.String(8))} - operator := &operatorsv1alpha1.Operator{ - ObjectMeta: metav1.ObjectMeta{Name: opKey.Name}, - Spec: operatorsv1alpha1.OperatorSpec{ + extKey := types.NamespacedName{Name: fmt.Sprintf("cluster-extension-test-%s", rand.String(8))} + clusterExtension := &ocv1alpha1.ClusterExtension{ + ObjectMeta: metav1.ObjectMeta{Name: extKey.Name}, + Spec: ocv1alpha1.ClusterExtensionSpec{ PackageName: "prometheus", Version: "1.0.1", Channel: "beta", }, } - // Create an operator - err := cl.Create(ctx, operator) + // Create a cluster extension + err := cl.Create(ctx, clusterExtension) require.NoError(t, err) // Run reconcile - res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) + res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: extKey}) require.NoError(t, err) assert.Equal(t, ctrl.Result{}, res) - // Refresh the operator after reconcile - err = cl.Get(ctx, opKey, operator) + // Refresh the cluster extension after reconcile + err = cl.Get(ctx, extKey, clusterExtension) require.NoError(t, err) // Checking the status fields - assert.Equal(t, "quay.io/operatorhubio/prometheus@fake1.0.1", operator.Status.ResolvedBundleResource) + assert.Equal(t, "quay.io/operatorhubio/prometheus@fake1.0.1", clusterExtension.Status.ResolvedBundleResource) // checking the expected conditions - cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeResolved) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) require.NotNil(t, cond) assert.Equal(t, metav1.ConditionTrue, cond.Status) - assert.Equal(t, operatorsv1alpha1.ReasonSuccess, cond.Reason) + assert.Equal(t, ocv1alpha1.ReasonSuccess, cond.Reason) assert.Equal(t, `resolved to "quay.io/operatorhubio/prometheus@fake1.0.1"`, cond.Message) // Invalid operation: can not downgrade - operator.Spec.Version = "1.0.0" - err = cl.Update(ctx, operator) + clusterExtension.Spec.Version = "1.0.0" + err = cl.Update(ctx, clusterExtension) require.NoError(t, err) // Run reconcile again - res, err = reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) + res, err = reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: extKey}) require.Error(t, err) assert.Equal(t, ctrl.Result{}, res) - // Refresh the operator after reconcile - err = cl.Get(ctx, opKey, operator) + // Refresh the cluster extension after reconcile + err = cl.Get(ctx, extKey, clusterExtension) require.NoError(t, err) // Checking the status fields // TODO: https://github.com/operator-framework/operator-controller/issues/320 - assert.Equal(t, "", operator.Status.ResolvedBundleResource) + assert.Equal(t, "", clusterExtension.Status.ResolvedBundleResource) // checking the expected conditions - cond = apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeResolved) + cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) require.NotNil(t, cond) assert.Equal(t, metav1.ConditionFalse, cond.Status) - assert.Equal(t, operatorsv1alpha1.ReasonResolutionFailed, cond.Reason) + assert.Equal(t, ocv1alpha1.ReasonResolutionFailed, cond.Reason) assert.Contains(t, cond.Message, "constraints not satisfiable") assert.Contains(t, cond.Message, "installed package prometheus requires at least one of fake-catalog-prometheus-operatorhub/prometheus/beta/1.2.0, fake-catalog-prometheus-operatorhub/prometheus/beta/1.0.1\n") }) @@ -1499,65 +1499,65 @@ func TestOperatorDowngrade(t *testing.T) { t.Run(tt.name, func(t *testing.T) { defer featuregatetesting.SetFeatureGateDuringTest(t, features.OperatorControllerFeatureGate, features.ForceSemverUpgradeConstraints, tt.flagState)() defer func() { - require.NoError(t, cl.DeleteAllOf(ctx, &operatorsv1alpha1.Operator{})) + require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) require.NoError(t, cl.DeleteAllOf(ctx, &rukpakv1alpha1.BundleDeployment{})) }() - opKey := types.NamespacedName{Name: fmt.Sprintf("operator-test-%s", rand.String(8))} - operator := &operatorsv1alpha1.Operator{ - ObjectMeta: metav1.ObjectMeta{Name: opKey.Name}, - Spec: operatorsv1alpha1.OperatorSpec{ + extKey := types.NamespacedName{Name: fmt.Sprintf("cluster-extension-test-%s", rand.String(8))} + clusterExtension := &ocv1alpha1.ClusterExtension{ + ObjectMeta: metav1.ObjectMeta{Name: extKey.Name}, + Spec: ocv1alpha1.ClusterExtensionSpec{ PackageName: "prometheus", Version: "2.0.0", Channel: "beta", - UpgradeConstraintPolicy: operatorsv1alpha1.UpgradeConstraintPolicyIgnore, + UpgradeConstraintPolicy: ocv1alpha1.UpgradeConstraintPolicyIgnore, }, } - // Create an operator - err := cl.Create(ctx, operator) + // Create a cluster extension + err := cl.Create(ctx, clusterExtension) require.NoError(t, err) // Run reconcile - res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) + res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: extKey}) require.NoError(t, err) assert.Equal(t, ctrl.Result{}, res) - // Refresh the operator after reconcile - err = cl.Get(ctx, opKey, operator) + // Refresh the cluster extension after reconcile + err = cl.Get(ctx, extKey, clusterExtension) require.NoError(t, err) // Checking the status fields - assert.Equal(t, "quay.io/operatorhubio/prometheus@fake2.0.0", operator.Status.ResolvedBundleResource) + assert.Equal(t, "quay.io/operatorhubio/prometheus@fake2.0.0", clusterExtension.Status.ResolvedBundleResource) // checking the expected conditions - cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeResolved) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) require.NotNil(t, cond) assert.Equal(t, metav1.ConditionTrue, cond.Status) - assert.Equal(t, operatorsv1alpha1.ReasonSuccess, cond.Reason) + assert.Equal(t, ocv1alpha1.ReasonSuccess, cond.Reason) assert.Equal(t, `resolved to "quay.io/operatorhubio/prometheus@fake2.0.0"`, cond.Message) // We downgrade - operator.Spec.Version = "1.0.0" - err = cl.Update(ctx, operator) + clusterExtension.Spec.Version = "1.0.0" + err = cl.Update(ctx, clusterExtension) require.NoError(t, err) // Run reconcile again - res, err = reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) + res, err = reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: extKey}) require.NoError(t, err) assert.Equal(t, ctrl.Result{}, res) - // Refresh the operator after reconcile - err = cl.Get(ctx, opKey, operator) + // Refresh the cluster extension after reconcile + err = cl.Get(ctx, extKey, clusterExtension) require.NoError(t, err) // Checking the status fields - assert.Equal(t, "quay.io/operatorhubio/prometheus@fake1.0.0", operator.Status.ResolvedBundleResource) + assert.Equal(t, "quay.io/operatorhubio/prometheus@fake1.0.0", clusterExtension.Status.ResolvedBundleResource) // checking the expected conditions - cond = apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeResolved) + cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) require.NotNil(t, cond) assert.Equal(t, metav1.ConditionTrue, cond.Status) - assert.Equal(t, operatorsv1alpha1.ReasonSuccess, cond.Reason) + assert.Equal(t, ocv1alpha1.ReasonSuccess, cond.Reason) assert.Equal(t, `resolved to "quay.io/operatorhubio/prometheus@fake1.0.0"`, cond.Message) }) } diff --git a/internal/controllers/suite_test.go b/internal/controllers/suite_test.go index 2661e58d..f802573a 100644 --- a/internal/controllers/suite_test.go +++ b/internal/controllers/suite_test.go @@ -32,7 +32,7 @@ import ( rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1" "github.com/stretchr/testify/require" - operatorsv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" + ocv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" "github.com/operator-framework/operator-controller/internal/controllers" testutil "github.com/operator-framework/operator-controller/test/util" ) @@ -44,13 +44,13 @@ func newClient(t *testing.T) client.Client { return cl } -func newClientAndReconciler(t *testing.T) (client.Client, *controllers.OperatorReconciler) { +func newClientAndReconciler(t *testing.T) (client.Client, *controllers.ClusterExtensionReconciler) { resolver, err := solver.New() require.NoError(t, err) cl := newClient(t) fakeCatalogClient := testutil.NewFakeCatalogClient(testBundleList) - reconciler := &controllers.OperatorReconciler{ + reconciler := &controllers.ClusterExtensionReconciler{ Client: cl, BundleProvider: &fakeCatalogClient, Scheme: sch, @@ -80,7 +80,7 @@ func TestMain(m *testing.M) { } sch = runtime.NewScheme() - utilruntime.Must(operatorsv1alpha1.AddToScheme(sch)) + utilruntime.Must(ocv1alpha1.AddToScheme(sch)) utilruntime.Must(rukpakv1alpha1.AddToScheme(sch)) code := m.Run() diff --git a/internal/controllers/validators/validators.go b/internal/controllers/validators/validators.go index f71a7897..4a64cd7f 100644 --- a/internal/controllers/validators/validators.go +++ b/internal/controllers/validators/validators.go @@ -5,28 +5,28 @@ import ( mmsemver "github.com/Masterminds/semver/v3" - operatorsv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" + ocv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" ) -type operatorCRValidatorFunc func(operator *operatorsv1alpha1.Operator) error +type clusterExtensionCRValidatorFunc func(clusterExtension *ocv1alpha1.ClusterExtension) error -// validateSemver validates that the operator's version is a valid SemVer. +// validateSemver validates that the clusterExtension's version is a valid SemVer. // this validation should already be happening at the CRD level. But, it depends // on a regex that could possibly fail to validate a valid SemVer. This is added as an // extra measure to ensure a valid spec before the CR is processed for resolution -func validateSemver(operator *operatorsv1alpha1.Operator) error { - if operator.Spec.Version == "" { +func validateSemver(clusterExtension *ocv1alpha1.ClusterExtension) error { + if clusterExtension.Spec.Version == "" { return nil } - if _, err := mmsemver.NewConstraint(operator.Spec.Version); err != nil { + if _, err := mmsemver.NewConstraint(clusterExtension.Spec.Version); err != nil { return fmt.Errorf("invalid .spec.version: %w", err) } return nil } -// ValidateOperatorSpec validates the operator spec, e.g. ensuring that .spec.version, if provided, is a valid SemVer -func ValidateOperatorSpec(operator *operatorsv1alpha1.Operator) error { - validators := []operatorCRValidatorFunc{ +// ValidateClusterExtensionSpec validates the clusterExtension spec, e.g. ensuring that .spec.version, if provided, is a valid SemVer +func ValidateClusterExtensionSpec(clusterExtension *ocv1alpha1.ClusterExtension) error { + validators := []clusterExtensionCRValidatorFunc{ validateSemver, } @@ -35,7 +35,7 @@ func ValidateOperatorSpec(operator *operatorsv1alpha1.Operator) error { // we should consider how to present this to the user in a way that is easy to understand and fix. // this issue is tracked here: https://github.com/operator-framework/operator-controller/issues/167 for _, validator := range validators { - if err := validator(operator); err != nil { + if err := validator(clusterExtension); err != nil { return err } } diff --git a/internal/controllers/validators/validators_test.go b/internal/controllers/validators/validators_test.go index 7eaf41e7..de1c9c6e 100644 --- a/internal/controllers/validators/validators_test.go +++ b/internal/controllers/validators/validators_test.go @@ -74,21 +74,21 @@ var semVers = []struct { {"1.2.3 - 2.3.4", "unsupported hyphen (range) operator - FALSE POSITIVE", true}, } -func TestValidateOperatorSpecSemVer(t *testing.T) { +func TestValidateClusterExtensionSpecSemVer(t *testing.T) { t.Parallel() for _, s := range semVers { d := s t.Run(d.comment, func(t *testing.T) { t.Parallel() - operator := &v1alpha1.Operator{ - Spec: v1alpha1.OperatorSpec{ + clusterExtension := &v1alpha1.ClusterExtension{ + Spec: v1alpha1.ClusterExtensionSpec{ Version: d.data, }, } if d.result { - require.NoError(t, validators.ValidateOperatorSpec(operator)) + require.NoError(t, validators.ValidateClusterExtensionSpec(clusterExtension)) } else { - require.Error(t, validators.ValidateOperatorSpec(operator)) + require.Error(t, validators.ValidateClusterExtensionSpec(clusterExtension)) } }) } diff --git a/internal/controllers/variables.go b/internal/controllers/variables.go index 0c10c2a0..a8765926 100644 --- a/internal/controllers/variables.go +++ b/internal/controllers/variables.go @@ -20,18 +20,18 @@ import ( "github.com/operator-framework/deppy/pkg/deppy" rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1" - operatorsv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" + ocv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" "github.com/operator-framework/operator-controller/internal/catalogmetadata" "github.com/operator-framework/operator-controller/internal/resolution/variablesources" ) -func GenerateVariables(allBundles []*catalogmetadata.Bundle, operators []operatorsv1alpha1.Operator, bundleDeployments []rukpakv1alpha1.BundleDeployment) ([]deppy.Variable, error) { - requiredPackages, err := variablesources.MakeRequiredPackageVariables(allBundles, operators) +func GenerateVariables(allBundles []*catalogmetadata.Bundle, clusterExtensions []ocv1alpha1.ClusterExtension, bundleDeployments []rukpakv1alpha1.BundleDeployment) ([]deppy.Variable, error) { + requiredPackages, err := variablesources.MakeRequiredPackageVariables(allBundles, clusterExtensions) if err != nil { return nil, err } - installedPackages, err := variablesources.MakeInstalledPackageVariables(allBundles, operators, bundleDeployments) + installedPackages, err := variablesources.MakeInstalledPackageVariables(allBundles, clusterExtensions, bundleDeployments) if err != nil { return nil, err } diff --git a/internal/controllers/variables_test.go b/internal/controllers/variables_test.go index 9014533c..b173aa80 100644 --- a/internal/controllers/variables_test.go +++ b/internal/controllers/variables_test.go @@ -7,21 +7,20 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" - "github.com/stretchr/testify/require" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/util/rand" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" - "k8s.io/utils/pointer" - "github.com/operator-framework/deppy/pkg/deppy" "github.com/operator-framework/deppy/pkg/deppy/constraint" "github.com/operator-framework/deppy/pkg/deppy/input" "github.com/operator-framework/operator-registry/alpha/declcfg" "github.com/operator-framework/operator-registry/alpha/property" rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/rand" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/utils/pointer" - operatorsv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" + ocv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" "github.com/operator-framework/operator-controller/internal/catalogmetadata" "github.com/operator-framework/operator-controller/internal/controllers" olmvariables "github.com/operator-framework/operator-controller/internal/resolution/variables" @@ -29,7 +28,7 @@ import ( func TestVariableSource(t *testing.T) { sch := runtime.NewScheme() - utilruntime.Must(operatorsv1alpha1.AddToScheme(sch)) + utilruntime.Must(ocv1alpha1.AddToScheme(sch)) utilruntime.Must(rukpakv1alpha1.AddToScheme(sch)) stableChannel := catalogmetadata.Channel{Channel: declcfg.Channel{ @@ -60,10 +59,10 @@ func TestVariableSource(t *testing.T) { } pkgName := "packageA" - opName := fmt.Sprintf("operator-test-%s", rand.String(8)) - operator := operatorsv1alpha1.Operator{ - ObjectMeta: metav1.ObjectMeta{Name: opName}, - Spec: operatorsv1alpha1.OperatorSpec{ + clusterExtensionName := fmt.Sprintf("clusterextension-test-%s", rand.String(8)) + clusterExtension := ocv1alpha1.ClusterExtension{ + ObjectMeta: metav1.ObjectMeta{Name: clusterExtensionName}, + Spec: ocv1alpha1.ClusterExtensionSpec{ PackageName: pkgName, Channel: "stable", Version: "2.0.0", @@ -72,13 +71,13 @@ func TestVariableSource(t *testing.T) { bd := rukpakv1alpha1.BundleDeployment{ ObjectMeta: metav1.ObjectMeta{ - Name: opName, + Name: clusterExtensionName, OwnerReferences: []metav1.OwnerReference{ { - APIVersion: operatorsv1alpha1.GroupVersion.String(), - Kind: "Operator", - Name: operator.Name, - UID: operator.UID, + APIVersion: ocv1alpha1.GroupVersion.String(), + Kind: "ClusterExtension", + Name: clusterExtension.Name, + UID: clusterExtension.UID, Controller: pointer.Bool(true), BlockOwnerDeletion: pointer.Bool(true), }, @@ -100,7 +99,7 @@ func TestVariableSource(t *testing.T) { }, } - vars, err := controllers.GenerateVariables(allBundles, []operatorsv1alpha1.Operator{operator}, []rukpakv1alpha1.BundleDeployment{bd}) + vars, err := controllers.GenerateVariables(allBundles, []ocv1alpha1.ClusterExtension{clusterExtension}, []rukpakv1alpha1.BundleDeployment{bd}) require.NoError(t, err) expectedVars := []deppy.Variable{ diff --git a/internal/resolution/variables/bundle.go b/internal/resolution/variables/bundle.go index 8c238cb1..51723fb1 100644 --- a/internal/resolution/variables/bundle.go +++ b/internal/resolution/variables/bundle.go @@ -52,7 +52,7 @@ type BundleUniquenessVariable struct { // from the input 'atMostID'. Examples: // 1. restrict the solution to at most a single bundle per package // 2. restrict the solution to at most a single bundler per provided gvk -// this guarantees that no two operators provide the same gvk and no two version of the same operator are running at the same time +// this guarantees that no two extensions provide the same gvk and no two version of the same extension are running at the same time func NewBundleUniquenessVariable(id deppy.Identifier, atMostIDs ...deppy.Identifier) *BundleUniquenessVariable { return &BundleUniquenessVariable{ SimpleVariable: input.NewSimpleVariable(id, constraint.AtMost(1, atMostIDs...)), diff --git a/internal/resolution/variablesources/fake_object_utils_test.go b/internal/resolution/variablesources/fake_object_utils_test.go index 7a1fc762..1288838d 100644 --- a/internal/resolution/variablesources/fake_object_utils_test.go +++ b/internal/resolution/variablesources/fake_object_utils_test.go @@ -1,7 +1,7 @@ package variablesources_test import ( - operatorsv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" + ocv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/uuid" @@ -10,23 +10,23 @@ import ( rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1" ) -func fakeOperator(name, packageName string, upgradeConstraintPolicy operatorsv1alpha1.UpgradeConstraintPolicy) operatorsv1alpha1.Operator { - return operatorsv1alpha1.Operator{ +func fakeClusterExtension(name, packageName string, upgradeConstraintPolicy ocv1alpha1.UpgradeConstraintPolicy) ocv1alpha1.ClusterExtension { + return ocv1alpha1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{ Name: name, // We manually set a fake UID here because the code we test - // uses UID to determine Operator CR which + // uses UID to determine ClusterExtension CR which // owns `BundleDeployment` UID: uuid.NewUUID(), }, - Spec: operatorsv1alpha1.OperatorSpec{ + Spec: ocv1alpha1.ClusterExtensionSpec{ PackageName: packageName, UpgradeConstraintPolicy: upgradeConstraintPolicy, }, } } -func fakeBundleDeployment(name, bundleImage string, owner *operatorsv1alpha1.Operator) rukpakv1alpha1.BundleDeployment { +func fakeBundleDeployment(name, bundleImage string, owner *ocv1alpha1.ClusterExtension) rukpakv1alpha1.BundleDeployment { bd := rukpakv1alpha1.BundleDeployment{ ObjectMeta: metav1.ObjectMeta{ Name: name, @@ -49,8 +49,8 @@ func fakeBundleDeployment(name, bundleImage string, owner *operatorsv1alpha1.Ope if owner != nil { bd.SetOwnerReferences([]metav1.OwnerReference{ { - APIVersion: operatorsv1alpha1.GroupVersion.String(), - Kind: "Operator", + APIVersion: ocv1alpha1.GroupVersion.String(), + Kind: "ClusterExtension", Name: owner.Name, UID: owner.UID, Controller: pointer.Bool(true), diff --git a/internal/resolution/variablesources/installed_package.go b/internal/resolution/variablesources/installed_package.go index 6b0f4427..49bf84a4 100644 --- a/internal/resolution/variablesources/installed_package.go +++ b/internal/resolution/variablesources/installed_package.go @@ -5,12 +5,11 @@ import ( "sort" mmsemver "github.com/Masterminds/semver/v3" + rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/sets" - rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1" - - operatorsv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" + ocv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" "github.com/operator-framework/operator-controller/internal/catalogmetadata" catalogfilter "github.com/operator-framework/operator-controller/internal/catalogmetadata/filter" catalogsort "github.com/operator-framework/operator-controller/internal/catalogmetadata/sort" @@ -24,7 +23,7 @@ import ( // has own variable. func MakeInstalledPackageVariables( allBundles []*catalogmetadata.Bundle, - operators []operatorsv1alpha1.Operator, + clusterExtensions []ocv1alpha1.ClusterExtension, bundleDeployments []rukpakv1alpha1.BundleDeployment, ) ([]*olmvariables.InstalledPackageVariable, error) { var successors successorsFunc = legacySemanticsSuccessors @@ -34,16 +33,16 @@ func MakeInstalledPackageVariables( ownerIDToBundleDeployment := mapOwnerIDToBundleDeployment(bundleDeployments) - result := make([]*olmvariables.InstalledPackageVariable, 0, len(operators)) + result := make([]*olmvariables.InstalledPackageVariable, 0, len(clusterExtensions)) processed := sets.Set[string]{} - for _, operator := range operators { - if operator.Spec.UpgradeConstraintPolicy == operatorsv1alpha1.UpgradeConstraintPolicyIgnore { + for _, clusterExtension := range clusterExtensions { + if clusterExtension.Spec.UpgradeConstraintPolicy == ocv1alpha1.UpgradeConstraintPolicyIgnore { continue } - bundleDeployment, ok := ownerIDToBundleDeployment[operator.UID] + bundleDeployment, ok := ownerIDToBundleDeployment[clusterExtension.UID] if !ok { - // This can happen when an Operator is requested, + // This can happen when an ClusterExtension is requested, // but not yet installed (e.g. no BundleDeployment created for it) continue } @@ -62,11 +61,11 @@ func MakeInstalledPackageVariables( // find corresponding bundle for the installed content resultSet := catalogfilter.Filter(allBundles, catalogfilter.And( - catalogfilter.WithPackageName(operator.Spec.PackageName), + catalogfilter.WithPackageName(clusterExtension.Spec.PackageName), catalogfilter.WithBundleImage(bundleImage), )) if len(resultSet) == 0 { - return nil, fmt.Errorf("bundle with image %q for package %q not found in available catalogs but is currently installed via BundleDeployment %q", bundleImage, operator.Spec.PackageName, bundleDeployment.Name) + return nil, fmt.Errorf("bundle with image %q for package %q not found in available catalogs but is currently installed via BundleDeployment %q", bundleImage, clusterExtension.Spec.PackageName, bundleDeployment.Name) } sort.SliceStable(resultSet, func(i, j int) bool { diff --git a/internal/resolution/variablesources/installed_package_test.go b/internal/resolution/variablesources/installed_package_test.go index 87489c15..37ddce7b 100644 --- a/internal/resolution/variablesources/installed_package_test.go +++ b/internal/resolution/variablesources/installed_package_test.go @@ -15,7 +15,7 @@ import ( "github.com/stretchr/testify/require" featuregatetesting "k8s.io/component-base/featuregate/testing" - operatorsv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" + ocv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" "github.com/operator-framework/operator-controller/internal/catalogmetadata" olmvariables "github.com/operator-framework/operator-controller/internal/resolution/variables" "github.com/operator-framework/operator-controller/internal/resolution/variablesources" @@ -173,14 +173,14 @@ func TestMakeInstalledPackageVariablesWithForceSemverUpgradeConstraintsEnabled(t for _, tt := range []struct { name string - upgradeConstraintPolicy operatorsv1alpha1.UpgradeConstraintPolicy + upgradeConstraintPolicy ocv1alpha1.UpgradeConstraintPolicy installedBundle *catalogmetadata.Bundle expectedResult []*olmvariables.InstalledPackageVariable expectedError string }{ { name: "with non-zero major version", - upgradeConstraintPolicy: operatorsv1alpha1.UpgradeConstraintPolicyEnforce, + upgradeConstraintPolicy: ocv1alpha1.UpgradeConstraintPolicyEnforce, installedBundle: bundleSet["test-package.v2.0.0"], expectedResult: []*olmvariables.InstalledPackageVariable{ olmvariables.NewInstalledPackageVariable(testPackageName, []*catalogmetadata.Bundle{ @@ -195,7 +195,7 @@ func TestMakeInstalledPackageVariablesWithForceSemverUpgradeConstraintsEnabled(t }, { name: "with zero major and zero minor version", - upgradeConstraintPolicy: operatorsv1alpha1.UpgradeConstraintPolicyEnforce, + upgradeConstraintPolicy: ocv1alpha1.UpgradeConstraintPolicyEnforce, installedBundle: bundleSet["test-package.v0.0.1"], expectedResult: []*olmvariables.InstalledPackageVariable{ olmvariables.NewInstalledPackageVariable(testPackageName, []*catalogmetadata.Bundle{ @@ -206,7 +206,7 @@ func TestMakeInstalledPackageVariablesWithForceSemverUpgradeConstraintsEnabled(t }, { name: "with zero major and non-zero minor version", - upgradeConstraintPolicy: operatorsv1alpha1.UpgradeConstraintPolicyEnforce, + upgradeConstraintPolicy: ocv1alpha1.UpgradeConstraintPolicyEnforce, installedBundle: bundleSet["test-package.v0.1.0"], expectedResult: []*olmvariables.InstalledPackageVariable{ olmvariables.NewInstalledPackageVariable(testPackageName, []*catalogmetadata.Bundle{ @@ -221,18 +221,18 @@ func TestMakeInstalledPackageVariablesWithForceSemverUpgradeConstraintsEnabled(t }, { name: "UpgradeConstraintPolicy is set to Ignore", - upgradeConstraintPolicy: operatorsv1alpha1.UpgradeConstraintPolicyIgnore, + upgradeConstraintPolicy: ocv1alpha1.UpgradeConstraintPolicyIgnore, installedBundle: bundleSet["test-package.v2.0.0"], expectedResult: []*olmvariables.InstalledPackageVariable{}, }, { - name: "no BundleDeployment for an Operator", - upgradeConstraintPolicy: operatorsv1alpha1.UpgradeConstraintPolicyEnforce, + name: "no BundleDeployment for an ClusterExtension", + upgradeConstraintPolicy: ocv1alpha1.UpgradeConstraintPolicyEnforce, expectedResult: []*olmvariables.InstalledPackageVariable{}, }, { name: "installed bundle not found", - upgradeConstraintPolicy: operatorsv1alpha1.UpgradeConstraintPolicyEnforce, + upgradeConstraintPolicy: ocv1alpha1.UpgradeConstraintPolicyEnforce, installedBundle: &catalogmetadata.Bundle{ Bundle: declcfg.Bundle{ Name: "test-package.v9.0.0", @@ -248,15 +248,15 @@ func TestMakeInstalledPackageVariablesWithForceSemverUpgradeConstraintsEnabled(t }, } { t.Run(tt.name, func(t *testing.T) { - fakeOwnerOperator := fakeOperator("test-operator-semver", testPackageName, tt.upgradeConstraintPolicy) + fakeOwnerClusterExtension := fakeClusterExtension("test-extension-semver", testPackageName, tt.upgradeConstraintPolicy) bundleDeployments := []rukpakv1alpha1.BundleDeployment{} if tt.installedBundle != nil { - bundleDeployments = append(bundleDeployments, fakeBundleDeployment("test-package-bd", tt.installedBundle.Image, &fakeOwnerOperator)) + bundleDeployments = append(bundleDeployments, fakeBundleDeployment("test-package-bd", tt.installedBundle.Image, &fakeOwnerClusterExtension)) } installedPackages, err := variablesources.MakeInstalledPackageVariables( allBundles, - []operatorsv1alpha1.Operator{fakeOwnerOperator}, + []ocv1alpha1.ClusterExtension{fakeOwnerClusterExtension}, bundleDeployments, ) if tt.expectedError == "" { @@ -363,14 +363,14 @@ func TestMakeInstalledPackageVariablesWithForceSemverUpgradeConstraintsDisabled( for _, tt := range []struct { name string - upgradeConstraintPolicy operatorsv1alpha1.UpgradeConstraintPolicy + upgradeConstraintPolicy ocv1alpha1.UpgradeConstraintPolicy installedBundle *catalogmetadata.Bundle expectedResult []*olmvariables.InstalledPackageVariable expectedError string }{ { name: "respect replaces directive from catalog", - upgradeConstraintPolicy: operatorsv1alpha1.UpgradeConstraintPolicyEnforce, + upgradeConstraintPolicy: ocv1alpha1.UpgradeConstraintPolicyEnforce, installedBundle: bundleSet["test-package.v2.0.0"], expectedResult: []*olmvariables.InstalledPackageVariable{ olmvariables.NewInstalledPackageVariable(testPackageName, []*catalogmetadata.Bundle{ @@ -384,18 +384,18 @@ func TestMakeInstalledPackageVariablesWithForceSemverUpgradeConstraintsDisabled( }, { name: "UpgradeConstraintPolicy is set to Ignore", - upgradeConstraintPolicy: operatorsv1alpha1.UpgradeConstraintPolicyIgnore, + upgradeConstraintPolicy: ocv1alpha1.UpgradeConstraintPolicyIgnore, installedBundle: bundleSet["test-package.v2.0.0"], expectedResult: []*olmvariables.InstalledPackageVariable{}, }, { - name: "no BundleDeployment for an Operator", - upgradeConstraintPolicy: operatorsv1alpha1.UpgradeConstraintPolicyEnforce, + name: "no BundleDeployment for an ClusterExtension", + upgradeConstraintPolicy: ocv1alpha1.UpgradeConstraintPolicyEnforce, expectedResult: []*olmvariables.InstalledPackageVariable{}, }, { name: "installed bundle not found", - upgradeConstraintPolicy: operatorsv1alpha1.UpgradeConstraintPolicyEnforce, + upgradeConstraintPolicy: ocv1alpha1.UpgradeConstraintPolicyEnforce, installedBundle: &catalogmetadata.Bundle{ Bundle: declcfg.Bundle{ Name: "test-package.v9.0.0", @@ -411,15 +411,15 @@ func TestMakeInstalledPackageVariablesWithForceSemverUpgradeConstraintsDisabled( }, } { t.Run(tt.name, func(t *testing.T) { - fakeOwnerOperator := fakeOperator("test-operator-legacy", testPackageName, tt.upgradeConstraintPolicy) + fakeOwnerClusterExtension := fakeClusterExtension("test-extension-legacy", testPackageName, tt.upgradeConstraintPolicy) bundleDeployments := []rukpakv1alpha1.BundleDeployment{} if tt.installedBundle != nil { - bundleDeployments = append(bundleDeployments, fakeBundleDeployment("test-package-bd", tt.installedBundle.Image, &fakeOwnerOperator)) + bundleDeployments = append(bundleDeployments, fakeBundleDeployment("test-package-bd", tt.installedBundle.Image, &fakeOwnerClusterExtension)) } installedPackages, err := variablesources.MakeInstalledPackageVariables( allBundles, - []operatorsv1alpha1.Operator{fakeOwnerOperator}, + []ocv1alpha1.ClusterExtension{fakeOwnerClusterExtension}, bundleDeployments, ) if tt.expectedError == "" { diff --git a/internal/resolution/variablesources/required_package.go b/internal/resolution/variablesources/required_package.go index de735eb1..a06f5006 100644 --- a/internal/resolution/variablesources/required_package.go +++ b/internal/resolution/variablesources/required_package.go @@ -6,7 +6,7 @@ import ( mmsemver "github.com/Masterminds/semver/v3" - operatorsv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" + ocv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" "github.com/operator-framework/operator-controller/internal/catalogmetadata" catalogfilter "github.com/operator-framework/operator-controller/internal/catalogmetadata/filter" catalogsort "github.com/operator-framework/operator-controller/internal/catalogmetadata/sort" @@ -15,14 +15,14 @@ import ( // MakeRequiredPackageVariables returns a variable which represent // explicit requirement for a package from an user. -// This is when an user explicitly asks "install this" via Operator API. -func MakeRequiredPackageVariables(allBundles []*catalogmetadata.Bundle, operators []operatorsv1alpha1.Operator) ([]*olmvariables.RequiredPackageVariable, error) { - result := make([]*olmvariables.RequiredPackageVariable, 0, len(operators)) +// This is when a user explicitly asks "install this" via ClusterExtension API. +func MakeRequiredPackageVariables(allBundles []*catalogmetadata.Bundle, clusterExtensions []ocv1alpha1.ClusterExtension) ([]*olmvariables.RequiredPackageVariable, error) { + result := make([]*olmvariables.RequiredPackageVariable, 0, len(clusterExtensions)) - for _, operator := range operators { - packageName := operator.Spec.PackageName - channelName := operator.Spec.Channel - versionRange := operator.Spec.Version + for _, clusterExtension := range clusterExtensions { + packageName := clusterExtension.Spec.PackageName + channelName := clusterExtension.Spec.Channel + versionRange := clusterExtension.Spec.Version predicates := []catalogfilter.Predicate[catalogmetadata.Bundle]{ catalogfilter.WithPackageName(packageName), diff --git a/internal/resolution/variablesources/required_package_test.go b/internal/resolution/variablesources/required_package_test.go index 78ef9828..b510b5be 100644 --- a/internal/resolution/variablesources/required_package_test.go +++ b/internal/resolution/variablesources/required_package_test.go @@ -16,7 +16,7 @@ import ( "github.com/operator-framework/operator-registry/alpha/declcfg" "github.com/operator-framework/operator-registry/alpha/property" - operatorsv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" + ocv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" "github.com/operator-framework/operator-controller/internal/catalogmetadata" olmvariables "github.com/operator-framework/operator-controller/internal/resolution/variables" "github.com/operator-framework/operator-controller/internal/resolution/variablesources" @@ -82,12 +82,12 @@ func TestMakeRequiredPackageVariables(t *testing.T) { allBundles = append(allBundles, bundle) } - fakeOperator := func(packageName, channelName, versionRange string) operatorsv1alpha1.Operator { - return operatorsv1alpha1.Operator{ + fakeClusterExtension := func(packageName, channelName, versionRange string) ocv1alpha1.ClusterExtension { + return ocv1alpha1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{ Name: fmt.Sprintf("op-%s-%s-%s", packageName, channelName, versionRange), }, - Spec: operatorsv1alpha1.OperatorSpec{ + Spec: ocv1alpha1.ClusterExtensionSpec{ PackageName: packageName, Version: versionRange, Channel: channelName, @@ -96,15 +96,15 @@ func TestMakeRequiredPackageVariables(t *testing.T) { } for _, tt := range []struct { - name string - operators []operatorsv1alpha1.Operator - expectedResult []*olmvariables.RequiredPackageVariable - expectedError string + name string + clusterExtensions []ocv1alpha1.ClusterExtension + expectedResult []*olmvariables.RequiredPackageVariable + expectedError string }{ { name: "package name only", - operators: []operatorsv1alpha1.Operator{ - fakeOperator("test-package", "", ""), + clusterExtensions: []ocv1alpha1.ClusterExtension{ + fakeClusterExtension("test-package", "", ""), }, expectedResult: []*olmvariables.RequiredPackageVariable{ olmvariables.NewRequiredPackageVariable("test-package", []*catalogmetadata.Bundle{ @@ -116,8 +116,8 @@ func TestMakeRequiredPackageVariables(t *testing.T) { }, { name: "package name and channel", - operators: []operatorsv1alpha1.Operator{ - fakeOperator("test-package", "beta", ""), + clusterExtensions: []ocv1alpha1.ClusterExtension{ + fakeClusterExtension("test-package", "beta", ""), }, expectedResult: []*olmvariables.RequiredPackageVariable{ olmvariables.NewRequiredPackageVariable("test-package", []*catalogmetadata.Bundle{ @@ -127,8 +127,8 @@ func TestMakeRequiredPackageVariables(t *testing.T) { }, { name: "package name and version range", - operators: []operatorsv1alpha1.Operator{ - fakeOperator("test-package", "", ">=1.0.0 !=2.0.0 <3.0.0"), + clusterExtensions: []ocv1alpha1.ClusterExtension{ + fakeClusterExtension("test-package", "", ">=1.0.0 !=2.0.0 <3.0.0"), }, expectedResult: []*olmvariables.RequiredPackageVariable{ olmvariables.NewRequiredPackageVariable("test-package", []*catalogmetadata.Bundle{ @@ -138,42 +138,42 @@ func TestMakeRequiredPackageVariables(t *testing.T) { }, { name: "package name and invalid version range", - operators: []operatorsv1alpha1.Operator{ - fakeOperator("test-package", "", "not a valid semver"), + clusterExtensions: []ocv1alpha1.ClusterExtension{ + fakeClusterExtension("test-package", "", "not a valid semver"), }, expectedError: `invalid version range "not a valid semver"`, }, { name: "not found: package name only", - operators: []operatorsv1alpha1.Operator{ - fakeOperator("non-existent-test-package", "", ""), + clusterExtensions: []ocv1alpha1.ClusterExtension{ + fakeClusterExtension("non-existent-test-package", "", ""), }, expectedError: `no package "non-existent-test-package" found`, }, { name: "not found: package name and channel", - operators: []operatorsv1alpha1.Operator{ - fakeOperator("non-existent-test-package", "stable", ""), + clusterExtensions: []ocv1alpha1.ClusterExtension{ + fakeClusterExtension("non-existent-test-package", "stable", ""), }, expectedError: `no package "non-existent-test-package" found in channel "stable"`, }, { name: "not found: package name and version range", - operators: []operatorsv1alpha1.Operator{ - fakeOperator("non-existent-test-package", "", "1.0.0"), + clusterExtensions: []ocv1alpha1.ClusterExtension{ + fakeClusterExtension("non-existent-test-package", "", "1.0.0"), }, expectedError: `no package "non-existent-test-package" matching version "1.0.0" found`, }, { name: "not found: package name with channel and version range", - operators: []operatorsv1alpha1.Operator{ - fakeOperator("non-existent-test-package", "stable", "1.0.0"), + clusterExtensions: []ocv1alpha1.ClusterExtension{ + fakeClusterExtension("non-existent-test-package", "stable", "1.0.0"), }, expectedError: `no package "non-existent-test-package" matching version "1.0.0" found in channel "stable"`, }, } { t.Run(tt.name, func(t *testing.T) { - vars, err := variablesources.MakeRequiredPackageVariables(allBundles, tt.operators) + vars, err := variablesources.MakeRequiredPackageVariables(allBundles, tt.clusterExtensions) if tt.expectedError == "" { assert.NoError(t, err) } else { diff --git a/mkdocs.yml b/mkdocs.yml index ed9ee043..ae496c0c 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -10,7 +10,7 @@ nav: - Home: 'index.md' - Components: 'components.md' - Tasks: - - Adding a catalog of operators: 'Tasks/adding-a-catalog.md' - - Explore operators available for installation: 'Tasks/explore-available-packages.md' - - Installing an operator: 'Tasks/installing-an-operator.md' - - Deleting an operator: 'Tasks/uninstall-an-operator.md' + - Adding a catalog of extensions: 'Tasks/adding-a-catalog.md' + - Explore extensions available for installation: 'Tasks/explore-available-packages.md' + - Installing an extension: 'Tasks/installing-an-extension.md' + - Deleting an extension: 'Tasks/uninstall-an-extension.md' diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index f4fed4dd..07eca66e 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -5,9 +5,10 @@ import ( "os" "testing" + catalogd "github.com/operator-framework/catalogd/api/core/v1alpha1" + rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" @@ -15,10 +16,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" - catalogd "github.com/operator-framework/catalogd/api/core/v1alpha1" - rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1" - - operatorv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" + ocv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" ) var ( @@ -36,7 +34,7 @@ func TestMain(m *testing.M) { scheme := runtime.NewScheme() - utilruntime.Must(operatorv1alpha1.AddToScheme(scheme)) + utilruntime.Must(ocv1alpha1.AddToScheme(scheme)) utilruntime.Must(rukpakv1alpha1.AddToScheme(scheme)) utilruntime.Must(catalogd.AddToScheme(scheme)) utilruntime.Must(appsv1.AddToScheme(scheme)) diff --git a/test/e2e/install_test.go b/test/e2e/install_test.go index 76647b5d..fae5385b 100644 --- a/test/e2e/install_test.go +++ b/test/e2e/install_test.go @@ -24,10 +24,9 @@ import ( "k8s.io/apimachinery/pkg/util/rand" kubeclient "k8s.io/client-go/kubernetes" "k8s.io/utils/env" - "sigs.k8s.io/controller-runtime/pkg/client" - operatorv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" + ocv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" ) const ( @@ -37,76 +36,76 @@ const ( var pollDuration = time.Minute var pollInterval = time.Second -func testInit(t *testing.T) (*operatorv1alpha1.Operator, string, *catalogd.Catalog) { +func testInit(t *testing.T) (*ocv1alpha1.ClusterExtension, string, *catalogd.Catalog) { var err error - operatorCatalog, err := createTestCatalog(context.Background(), testCatalogName, os.Getenv(testCatalogRefEnvVar)) + extensionCatalog, err := createTestCatalog(context.Background(), testCatalogName, os.Getenv(testCatalogRefEnvVar)) require.NoError(t, err) - operatorName := fmt.Sprintf("operator-%s", rand.String(8)) - operator := &operatorv1alpha1.Operator{ + clusterExtensionName := fmt.Sprintf("clusterextension-%s", rand.String(8)) + clusterExtension := &ocv1alpha1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{ - Name: operatorName, + Name: clusterExtensionName, }, } - return operator, operatorName, operatorCatalog + return clusterExtension, clusterExtensionName, extensionCatalog } -func testCleanup(t *testing.T, cat *catalogd.Catalog, operator *operatorv1alpha1.Operator) { +func testCleanup(t *testing.T, cat *catalogd.Catalog, clusterExtension *ocv1alpha1.ClusterExtension) { require.NoError(t, c.Delete(context.Background(), cat)) require.Eventually(t, func() bool { err := c.Get(context.Background(), types.NamespacedName{Name: cat.Name}, &catalogd.Catalog{}) return errors.IsNotFound(err) }, pollDuration, pollInterval) - require.NoError(t, c.Delete(context.Background(), operator)) + require.NoError(t, c.Delete(context.Background(), clusterExtension)) require.Eventually(t, func() bool { - err := c.Get(context.Background(), types.NamespacedName{Name: operator.Name}, &operatorv1alpha1.Operator{}) + err := c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, &ocv1alpha1.ClusterExtension{}) return errors.IsNotFound(err) }, pollDuration, pollInterval) } -func TestOperatorInstallRegistry(t *testing.T) { - t.Log("When an operator is installed from an operator catalog") - t.Log("When the operator bundle format is registry+v1") +func TestClusterExtensionInstallRegistry(t *testing.T) { + t.Log("When a cluster extension is installed from a catalog") + t.Log("When the extension bundle format is registry+v1") - operator, operatorName, operatorCatalog := testInit(t) - defer testCleanup(t, operatorCatalog, operator) + clusterExtension, clusterExtensionName, extensionCatalog := testInit(t) + defer testCleanup(t, extensionCatalog, clusterExtension) defer getArtifactsOutput(t) - operator.Spec = operatorv1alpha1.OperatorSpec{ + clusterExtension.Spec = ocv1alpha1.ClusterExtensionSpec{ PackageName: "prometheus", } t.Log("It resolves the specified package with correct bundle path") - t.Log("By creating the Operator resource") - require.NoError(t, c.Create(context.Background(), operator)) + t.Log("By creating the ClusterExtension resource") + require.NoError(t, c.Create(context.Background(), clusterExtension)) t.Log("By eventually reporting a successful resolution and bundle path") require.EventuallyWithT(t, func(ct *assert.CollectT) { - assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: operator.Name}, operator)) - assert.Len(ct, operator.Status.Conditions, 2) - cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorv1alpha1.TypeResolved) + assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) + assert.Len(ct, clusterExtension.Status.Conditions, 2) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) if !assert.NotNil(ct, cond) { return } assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, operatorv1alpha1.ReasonSuccess, cond.Reason) + assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) assert.Contains(ct, cond.Message, "resolved to") - assert.Equal(ct, "localhost/testdata/bundles/registry-v1/prometheus-operator:v2.0.0", operator.Status.ResolvedBundleResource) + assert.Equal(ct, "localhost/testdata/bundles/registry-v1/prometheus-operator:v2.0.0", clusterExtension.Status.ResolvedBundleResource) }, pollDuration, pollInterval) t.Log("By eventually installing the package successfully") require.EventuallyWithT(t, func(ct *assert.CollectT) { - assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: operator.Name}, operator)) - cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorv1alpha1.TypeInstalled) + assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) if !assert.NotNil(ct, cond) { return } assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, operatorv1alpha1.ReasonSuccess, cond.Reason) + assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) assert.Contains(ct, cond.Message, "installed from") - assert.NotEmpty(ct, operator.Status.InstalledBundleResource) + assert.NotEmpty(ct, clusterExtension.Status.InstalledBundleResource) bd := rukpakv1alpha1.BundleDeployment{} - assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: operatorName}, &bd)) + assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtensionName}, &bd)) hasValidBundle := apimeta.FindStatusCondition(bd.Status.Conditions, rukpakv1alpha1.TypeHasValidBundle) if !assert.NotNil(ct, hasValidBundle) { return @@ -120,49 +119,49 @@ func TestOperatorInstallRegistry(t *testing.T) { }, pollDuration, pollInterval) } -func TestOperatorInstallPlain(t *testing.T) { - t.Log("When an operator is installed from an operator catalog") - t.Log("When the operator bundle format is plain+v0") +func TestClusterExtensionInstallPlain(t *testing.T) { + t.Log("When a cluster extension is installed from a catalog") + t.Log("When the cluster extension bundle format is plain+v0") - operator, operatorName, operatorCatalog := testInit(t) - defer testCleanup(t, operatorCatalog, operator) + clusterExtension, clusterExtensionName, extensionCatalog := testInit(t) + defer testCleanup(t, extensionCatalog, clusterExtension) defer getArtifactsOutput(t) - operator.Spec = operatorv1alpha1.OperatorSpec{ + clusterExtension.Spec = ocv1alpha1.ClusterExtensionSpec{ PackageName: "plain", } t.Log("It resolves the specified package with correct bundle path") - t.Log("By creating the Operator resource") - require.NoError(t, c.Create(context.Background(), operator)) + t.Log("By creating the ClusterExtension resource") + require.NoError(t, c.Create(context.Background(), clusterExtension)) t.Log("By eventually reporting a successful resolution and bundle path") require.EventuallyWithT(t, func(ct *assert.CollectT) { - assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: operator.Name}, operator)) - assert.Len(ct, operator.Status.Conditions, 2) - cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorv1alpha1.TypeResolved) + assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) + assert.Len(ct, clusterExtension.Status.Conditions, 2) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) if !assert.NotNil(ct, cond) { return } assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, operatorv1alpha1.ReasonSuccess, cond.Reason) + assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) assert.Contains(ct, cond.Message, "resolved to") - assert.NotEmpty(ct, operator.Status.ResolvedBundleResource) + assert.NotEmpty(ct, clusterExtension.Status.ResolvedBundleResource) }, pollDuration, pollInterval) t.Log("By eventually installing the package successfully") require.EventuallyWithT(t, func(ct *assert.CollectT) { - assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: operator.Name}, operator)) - cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorv1alpha1.TypeInstalled) + assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) if !assert.NotNil(ct, cond) { return } assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, operatorv1alpha1.ReasonSuccess, cond.Reason) + assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) assert.Contains(ct, cond.Message, "installed from") - assert.NotEmpty(ct, operator.Status.InstalledBundleResource) + assert.NotEmpty(ct, clusterExtension.Status.InstalledBundleResource) bd := rukpakv1alpha1.BundleDeployment{} - assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: operatorName}, &bd)) + assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtensionName}, &bd)) hasValidBundle := apimeta.FindStatusCondition(bd.Status.Conditions, rukpakv1alpha1.TypeHasValidBundle) if !assert.NotNil(ct, hasValidBundle) { return @@ -176,48 +175,48 @@ func TestOperatorInstallPlain(t *testing.T) { }, pollDuration, pollInterval) } -func TestOperatorInstallReResolvesWhenNewCatalog(t *testing.T) { - t.Log("When an operator is installed from an operator catalog") +func TestClusterExtensionInstallReResolvesWhenNewCatalog(t *testing.T) { + t.Log("When a cluster extension is installed from a catalog") t.Log("It resolves again when a new catalog is available") - operator, _, operatorCatalog := testInit(t) - defer testCleanup(t, operatorCatalog, operator) + clusterExtension, _, extensionCatalog := testInit(t) + defer testCleanup(t, extensionCatalog, clusterExtension) defer getArtifactsOutput(t) pkgName := "prometheus" - operator.Spec = operatorv1alpha1.OperatorSpec{ + clusterExtension.Spec = ocv1alpha1.ClusterExtensionSpec{ PackageName: pkgName, } t.Log("By deleting the catalog first") - require.NoError(t, c.Delete(context.Background(), operatorCatalog)) + require.NoError(t, c.Delete(context.Background(), extensionCatalog)) require.EventuallyWithT(t, func(ct *assert.CollectT) { - err := c.Get(context.Background(), types.NamespacedName{Name: operatorCatalog.Name}, &catalogd.Catalog{}) + err := c.Get(context.Background(), types.NamespacedName{Name: extensionCatalog.Name}, &catalogd.Catalog{}) assert.True(ct, errors.IsNotFound(err)) }, pollDuration, pollInterval) - t.Log("By creating the Operator resource") - require.NoError(t, c.Create(context.Background(), operator)) + t.Log("By creating the ClusterExtension resource") + require.NoError(t, c.Create(context.Background(), clusterExtension)) - t.Log("By failing to find Operator during resolution") + t.Log("By failing to find ClusterExtension during resolution") require.EventuallyWithT(t, func(ct *assert.CollectT) { - assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: operator.Name}, operator)) - cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorv1alpha1.TypeResolved) + assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) if !assert.NotNil(ct, cond) { return } assert.Equal(ct, metav1.ConditionFalse, cond.Status) - assert.Equal(ct, operatorv1alpha1.ReasonResolutionFailed, cond.Reason) + assert.Equal(ct, ocv1alpha1.ReasonResolutionFailed, cond.Reason) assert.Equal(ct, fmt.Sprintf("no package %q found", pkgName), cond.Message) }, pollDuration, pollInterval) - t.Log("By creating an Operator catalog with the desired package") + t.Log("By creating an ClusterExtension catalog with the desired package") var err error - operatorCatalog, err = createTestCatalog(context.Background(), testCatalogName, os.Getenv(testCatalogRefEnvVar)) + extensionCatalog, err = createTestCatalog(context.Background(), testCatalogName, os.Getenv(testCatalogRefEnvVar)) require.NoError(t, err) require.EventuallyWithT(t, func(ct *assert.CollectT) { - assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: operatorCatalog.Name}, operatorCatalog)) - cond := apimeta.FindStatusCondition(operatorCatalog.Status.Conditions, catalogd.TypeUnpacked) + assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: extensionCatalog.Name}, extensionCatalog)) + cond := apimeta.FindStatusCondition(extensionCatalog.Status.Conditions, catalogd.TypeUnpacked) if !assert.NotNil(ct, cond) { return } @@ -227,107 +226,107 @@ func TestOperatorInstallReResolvesWhenNewCatalog(t *testing.T) { t.Log("By eventually resolving the package successfully") require.EventuallyWithT(t, func(ct *assert.CollectT) { - assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: operator.Name}, operator)) - cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorv1alpha1.TypeResolved) + assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) if !assert.NotNil(ct, cond) { return } assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, operatorv1alpha1.ReasonSuccess, cond.Reason) + assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) }, pollDuration, pollInterval) } -func TestOperatorInstallNonSuccessorVersion(t *testing.T) { - t.Log("When an operator is installed from an operator catalog") +func TestClusterExtensionInstallNonSuccessorVersion(t *testing.T) { + t.Log("When a cluster extension is installed from a catalog") t.Log("When resolving upgrade edges") - operator, _, operatorCatalog := testInit(t) - defer testCleanup(t, operatorCatalog, operator) + clusterExtension, _, extensionCatalog := testInit(t) + defer testCleanup(t, extensionCatalog, clusterExtension) defer getArtifactsOutput(t) - t.Log("By creating an Operator at a specified version") - operator.Spec = operatorv1alpha1.OperatorSpec{ + t.Log("By creating an ClusterExtension at a specified version") + clusterExtension.Spec = ocv1alpha1.ClusterExtensionSpec{ PackageName: "prometheus", Version: "1.0.0", } - require.NoError(t, c.Create(context.Background(), operator)) + require.NoError(t, c.Create(context.Background(), clusterExtension)) t.Log("By eventually reporting a successful resolution") require.EventuallyWithT(t, func(ct *assert.CollectT) { - assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: operator.Name}, operator)) - cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorv1alpha1.TypeResolved) + assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) if !assert.NotNil(ct, cond) { return } - assert.Equal(ct, operatorv1alpha1.ReasonSuccess, cond.Reason) + assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) assert.Contains(ct, cond.Message, "resolved to") - assert.Equal(ct, "localhost/testdata/bundles/registry-v1/prometheus-operator:v1.0.0", operator.Status.ResolvedBundleResource) + assert.Equal(ct, "localhost/testdata/bundles/registry-v1/prometheus-operator:v1.0.0", clusterExtension.Status.ResolvedBundleResource) }, pollDuration, pollInterval) - t.Log("It does not allow to upgrade the Operator to a non-successor version") - t.Log("By updating the Operator resource to a non-successor version") + t.Log("It does not allow to upgrade the ClusterExtension to a non-successor version") + t.Log("By updating the ClusterExtension resource to a non-successor version") // Semver only allows upgrades within major version at the moment. - operator.Spec.Version = "2.0.0" - require.NoError(t, c.Update(context.Background(), operator)) + clusterExtension.Spec.Version = "2.0.0" + require.NoError(t, c.Update(context.Background(), clusterExtension)) t.Log("By eventually reporting an unsatisfiable resolution") require.EventuallyWithT(t, func(ct *assert.CollectT) { - assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: operator.Name}, operator)) - cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorv1alpha1.TypeResolved) + assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) if !assert.NotNil(ct, cond) { return } - assert.Equal(ct, operatorv1alpha1.ReasonResolutionFailed, cond.Reason) + assert.Equal(ct, ocv1alpha1.ReasonResolutionFailed, cond.Reason) assert.Contains(ct, cond.Message, "constraints not satisfiable") assert.Contains(ct, cond.Message, "installed package prometheus requires at least one of test-catalog-prometheus-prometheus-operator.1.2.0, test-catalog-prometheus-prometheus-operator.1.0.1, test-catalog-prometheus-prometheus-operator.1.0.0") - assert.Empty(ct, operator.Status.ResolvedBundleResource) + assert.Empty(ct, clusterExtension.Status.ResolvedBundleResource) }, pollDuration, pollInterval) } -func TestOperatorInstallSuccessorVersion(t *testing.T) { - t.Log("When an operator is installed from an operator catalog") +func TestClusterExtensionInstallSuccessorVersion(t *testing.T) { + t.Log("When a cluster extension is installed from a catalog") t.Log("When resolving upgrade edges") - operator, _, operatorCatalog := testInit(t) - defer testCleanup(t, operatorCatalog, operator) + clusterExtension, _, extensionCatalog := testInit(t) + defer testCleanup(t, extensionCatalog, clusterExtension) defer getArtifactsOutput(t) - t.Log("By creating an Operator at a specified version") - operator.Spec = operatorv1alpha1.OperatorSpec{ + t.Log("By creating an ClusterExtension at a specified version") + clusterExtension.Spec = ocv1alpha1.ClusterExtensionSpec{ PackageName: "prometheus", Version: "1.0.0", } - require.NoError(t, c.Create(context.Background(), operator)) + require.NoError(t, c.Create(context.Background(), clusterExtension)) t.Log("By eventually reporting a successful resolution") require.EventuallyWithT(t, func(ct *assert.CollectT) { - assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: operator.Name}, operator)) - cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorv1alpha1.TypeResolved) + assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) if !assert.NotNil(ct, cond) { return } - assert.Equal(ct, operatorv1alpha1.ReasonSuccess, cond.Reason) + assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) assert.Contains(ct, cond.Message, "resolved to") - assert.Equal(ct, "localhost/testdata/bundles/registry-v1/prometheus-operator:v1.0.0", operator.Status.ResolvedBundleResource) + assert.Equal(ct, "localhost/testdata/bundles/registry-v1/prometheus-operator:v1.0.0", clusterExtension.Status.ResolvedBundleResource) }, pollDuration, pollInterval) - t.Log("It does allow to upgrade the Operator to any of the successor versions within non-zero major version") - t.Log("By updating the Operator resource by skipping versions") + t.Log("It does allow to upgrade the ClusterExtension to any of the successor versions within non-zero major version") + t.Log("By updating the ClusterExtension resource by skipping versions") // Test catalog has versions between the initial version and new version - operator.Spec.Version = "1.2.0" - require.NoError(t, c.Update(context.Background(), operator)) + clusterExtension.Spec.Version = "1.2.0" + require.NoError(t, c.Update(context.Background(), clusterExtension)) t.Log("By eventually reporting a successful resolution and bundle path") require.EventuallyWithT(t, func(ct *assert.CollectT) { - assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: operator.Name}, operator)) - cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorv1alpha1.TypeResolved) + assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) if !assert.NotNil(ct, cond) { return } - assert.Equal(ct, operatorv1alpha1.ReasonSuccess, cond.Reason) + assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) assert.Contains(ct, cond.Message, "resolved to") - assert.Equal(ct, "localhost/testdata/bundles/registry-v1/prometheus-operator:v1.2.0", operator.Status.ResolvedBundleResource) + assert.Equal(ct, "localhost/testdata/bundles/registry-v1/prometheus-operator:v1.2.0", clusterExtension.Status.ResolvedBundleResource) }, pollDuration, pollInterval) } // getArtifactsOutput gets all the artifacts from the test run and saves them to the artifact path. // Currently it saves: -// - operators +// - clusterextensions // - pods logs // - deployments // - bundle @@ -354,74 +353,74 @@ func getArtifactsOutput(t *testing.T) { // Get all namespaces namespaces := corev1.NamespaceList{} if err := c.List(context.Background(), &namespaces); err != nil { - fmt.Printf("Failed to list namespaces %v", err) + fmt.Printf("Failed to list namespaces: %v", err) } - // get all operators save them to the artifact path. - operators := operatorv1alpha1.OperatorList{} - if err := c.List(context.Background(), &operators, client.InNamespace("")); err != nil { - fmt.Printf("Failed to list operators %v", err) + // get all cluster extensions save them to the artifact path. + clusterExtensions := ocv1alpha1.ClusterExtensionList{} + if err := c.List(context.Background(), &clusterExtensions, client.InNamespace("")); err != nil { + fmt.Printf("Failed to list cluster extensions: %v", err) } - for _, operator := range operators.Items { - // Save operator to artifact path - operatorYaml, err := yaml.Marshal(operator) + for _, clusterExtension := range clusterExtensions.Items { + // Save cluster extension to artifact path + clusterExtensionYaml, err := yaml.Marshal(clusterExtension) if err != nil { - fmt.Printf("Failed to marshal operator %v", err) + fmt.Printf("Failed to marshal cluster extension: %v", err) continue } - if err := os.WriteFile(filepath.Join(artifactPath, operator.Name+"-operator.yaml"), operatorYaml, 0600); err != nil { - fmt.Printf("Failed to write operator to file %v", err) + if err := os.WriteFile(filepath.Join(artifactPath, clusterExtension.Name+"-clusterextension.yaml"), clusterExtensionYaml, 0600); err != nil { + fmt.Printf("Failed to write cluster extension to file: %v", err) } } // get all catalogsources save them to the artifact path. catalogsources := catalogd.CatalogList{} if err := c.List(context.Background(), &catalogsources, client.InNamespace("")); err != nil { - fmt.Printf("Failed to list catalogsources %v", err) + fmt.Printf("Failed to list catalogsources: %v", err) } for _, catalogsource := range catalogsources.Items { // Save catalogsource to artifact path catalogsourceYaml, err := yaml.Marshal(catalogsource) if err != nil { - fmt.Printf("Failed to marshal catalogsource %v", err) + fmt.Printf("Failed to marshal catalogsource: %v", err) continue } if err := os.WriteFile(filepath.Join(artifactPath, catalogsource.Name+"-catalogsource.yaml"), catalogsourceYaml, 0600); err != nil { - fmt.Printf("Failed to write catalogsource to file %v", err) + fmt.Printf("Failed to write catalogsource to file: %v", err) } } // Get all Bundles in the namespace and save them to the artifact path. bundles := rukpakv1alpha1.BundleList{} if err := c.List(context.Background(), &bundles, client.InNamespace("")); err != nil { - fmt.Printf("Failed to list bundles %v", err) + fmt.Printf("Failed to list bundles: %v", err) } for _, bundle := range bundles.Items { // Save bundle to artifact path bundleYaml, err := yaml.Marshal(bundle) if err != nil { - fmt.Printf("Failed to marshal bundle %v", err) + fmt.Printf("Failed to marshal bundle: %v", err) continue } if err := os.WriteFile(filepath.Join(artifactPath, bundle.Name+"-bundle.yaml"), bundleYaml, 0600); err != nil { - fmt.Printf("Failed to write bundle to file %v", err) + fmt.Printf("Failed to write bundle to file: %v", err) } } // Get all BundleDeployments in the namespace and save them to the artifact path. bundleDeployments := rukpakv1alpha1.BundleDeploymentList{} if err := c.List(context.Background(), &bundleDeployments, client.InNamespace("")); err != nil { - fmt.Printf("Failed to list bundleDeployments %v", err) + fmt.Printf("Failed to list bundleDeployments: %v", err) } for _, bundleDeployment := range bundleDeployments.Items { // Save bundleDeployment to artifact path bundleDeploymentYaml, err := yaml.Marshal(bundleDeployment) if err != nil { - fmt.Printf("Failed to marshal bundleDeployment %v", err) + fmt.Printf("Failed to marshal bundleDeployment: %v", err) continue } if err := os.WriteFile(filepath.Join(artifactPath, bundleDeployment.Name+"-bundleDeployment.yaml"), bundleDeploymentYaml, 0600); err != nil { - fmt.Printf("Failed to write bundleDeployment to file %v", err) + fmt.Printf("Failed to write bundleDeployment to file: %v", err) } } @@ -433,7 +432,7 @@ func getArtifactsOutput(t *testing.T) { namespacedArtifactPath := filepath.Join(artifactPath, namespace.Name) if err := os.Mkdir(namespacedArtifactPath, 0755); err != nil { - fmt.Printf("Failed to create namespaced artifact path %v", err) + fmt.Printf("Failed to create namespaced artifact path: %v", err) continue } @@ -448,11 +447,11 @@ func getArtifactsOutput(t *testing.T) { // Save deployment to artifact path deploymentYaml, err := yaml.Marshal(deployment) if err != nil { - fmt.Printf("Failed to marshal deployment %v", err) + fmt.Printf("Failed to marshal deployment: %v", err) continue } if err := os.WriteFile(filepath.Join(namespacedArtifactPath, deployment.Name+"-deployment.yaml"), deploymentYaml, 0600); err != nil { - fmt.Printf("Failed to write deployment to file %v", err) + fmt.Printf("Failed to write deployment to file: %v", err) } } diff --git a/test/operator-framework-e2e/operator_framework_test.go b/test/extension-developer-e2e/extension_developer_test.go similarity index 64% rename from test/operator-framework-e2e/operator_framework_test.go rename to test/extension-developer-e2e/extension_developer_test.go index fa7112ab..c82beade 100644 --- a/test/operator-framework-e2e/operator_framework_test.go +++ b/test/extension-developer-e2e/extension_developer_test.go @@ -1,4 +1,4 @@ -package operatore2e +package extensione2e import ( "context" @@ -6,37 +6,36 @@ import ( "testing" "time" + catalogd "github.com/operator-framework/catalogd/api/core/v1alpha1" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" - catalogd "github.com/operator-framework/catalogd/api/core/v1alpha1" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - operatorv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" + ocv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" ) -func TestOperatorFramework(t *testing.T) { +func TestExtensionDeveloper(t *testing.T) { t.Parallel() cfg := ctrl.GetConfigOrDie() scheme := runtime.NewScheme() require.NoError(t, catalogd.AddToScheme(scheme)) - require.NoError(t, operatorv1alpha1.AddToScheme(scheme)) + require.NoError(t, ocv1alpha1.AddToScheme(scheme)) c, err := client.New(cfg, client.Options{Scheme: scheme}) require.NoError(t, err) - var operators = []*operatorv1alpha1.Operator{ + var clusterExtensions = []*ocv1alpha1.ClusterExtension{ { ObjectMeta: metav1.ObjectMeta{ Name: "plainv0", }, - Spec: operatorv1alpha1.OperatorSpec{ + Spec: ocv1alpha1.ClusterExtensionSpec{ PackageName: os.Getenv("PLAIN_PKG_NAME"), }, }, @@ -44,15 +43,15 @@ func TestOperatorFramework(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "registryv1", }, - Spec: operatorv1alpha1.OperatorSpec{ + Spec: ocv1alpha1.ClusterExtensionSpec{ PackageName: os.Getenv("REG_PKG_NAME"), }, }, } - for _, op := range operators { - operator := op - t.Run(operator.ObjectMeta.Name, func(t *testing.T) { + for _, ce := range clusterExtensions { + clusterExtension := ce + t.Run(clusterExtension.ObjectMeta.Name, func(t *testing.T) { t.Parallel() catalog := &catalogd.Catalog{ ObjectMeta: metav1.ObjectMeta{ @@ -68,22 +67,22 @@ func TestOperatorFramework(t *testing.T) { }, }, } - t.Logf("When creating an Operator that references a package with a %q bundle type", operator.ObjectMeta.Name) + t.Logf("When creating an ClusterExtension that references a package with a %q bundle type", clusterExtension.ObjectMeta.Name) require.NoError(t, c.Create(context.Background(), catalog)) - require.NoError(t, c.Create(context.Background(), operator)) + require.NoError(t, c.Create(context.Background(), clusterExtension)) t.Log("It should have a status condition type of Installed with a status of True and a reason of Success") require.EventuallyWithT(t, func(ct *assert.CollectT) { - op := &operatorv1alpha1.Operator{} - assert.NoError(ct, c.Get(context.Background(), client.ObjectKeyFromObject(operator), op)) - cond := meta.FindStatusCondition(op.Status.Conditions, operatorv1alpha1.TypeInstalled) + ext := &ocv1alpha1.ClusterExtension{} + assert.NoError(ct, c.Get(context.Background(), client.ObjectKeyFromObject(clusterExtension), ext)) + cond := meta.FindStatusCondition(ext.Status.Conditions, ocv1alpha1.TypeInstalled) if !assert.NotNil(ct, cond) { return } assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, operatorv1alpha1.ReasonSuccess, cond.Reason) + assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) }, 2*time.Minute, time.Second) require.NoError(t, c.Delete(context.Background(), catalog)) - require.NoError(t, c.Delete(context.Background(), operator)) + require.NoError(t, c.Delete(context.Background(), clusterExtension)) }) } } diff --git a/test/operator-framework-e2e/setup.sh b/test/extension-developer-e2e/setup.sh similarity index 88% rename from test/operator-framework-e2e/setup.sh rename to test/extension-developer-e2e/setup.sh index c2f42c3e..d6fb7489 100755 --- a/test/operator-framework-e2e/setup.sh +++ b/test/extension-developer-e2e/setup.sh @@ -4,7 +4,7 @@ set -o errexit set -o nounset set -o pipefail -help="setup.sh is used to build operators using the operator-sdk and +help="setup.sh is used to build extensions using the operator-sdk and build the image + bundle image, and create a FBC image for the following bundle formats: - registry+v1 @@ -12,10 +12,10 @@ following bundle formats: This script will ensure that all images built are loaded onto a KinD cluster with the name specified in the arguments. The following environment variables are required for configuring this script: -- \$CATALOG_IMG - the tag for the catalog image that contains the plain+v0 and registry+v1 operator bundle. -- \$REG_PKG_NAME - the name of the package for the operator that uses the registry+v1 bundle format. -- \$PLAIN_PKG_NAME - the name of the package for the operator that uses the plain+v0 bundle format. -setup.sh also takes 5 arguments. +- \$CATALOG_IMG - the tag for the catalog image that contains the plain+v0 and registry+v1 bundle. +- \$REG_PKG_NAME - the name of the package for the extension that uses the registry+v1 bundle format. +- \$PLAIN_PKG_NAME - the name of the package for the extension that uses the plain+v0 bundle format. +setup.sh also takes 5 arguments. Usage: setup.sh [OPERATOR_SDK] [CONTAINER_RUNTIME] [KUSTOMIZE] [KIND] [KIND_CLUSTER_NAME] [NAMESPACE] @@ -31,19 +31,19 @@ if [[ "$#" -ne 6 ]]; then exit 1 fi -if [[ -z "${CATALOG_IMG}" ]]; then +if [[ -z "${CATALOG_IMG}" ]]; then echo "\$CATALOG_IMG is required to be set" echo "${help}" exit 1 fi -if [[ -z "${REG_PKG_NAME}" ]]; then +if [[ -z "${REG_PKG_NAME}" ]]; then echo "\$REG_PKG_NAME is required to be set" echo "${help}" exit 1 fi -if [[ -z "${PLAIN_PKG_NAME}" ]]; then +if [[ -z "${PLAIN_PKG_NAME}" ]]; then echo "\$PLAIN_PKG_NAME is required to be set" echo "${help}" exit 1 @@ -82,7 +82,7 @@ reg_pkg_name="${REG_PKG_NAME}" plain_pkg_name="${PLAIN_PKG_NAME}" ######################################## -# Create the registry+v1 based operator +# Create the registry+v1 based extension # and build + load images ######################################## @@ -105,7 +105,7 @@ $kind load docker-image "${reg_img}" --name "${kcluster_name}" $kind load docker-image "${reg_bundle_img}" --name "${kcluster_name}" ##################################### -# Create the plain+v0 based operator +# Create the plain+v0 based extension # and build + load images ##################################### @@ -134,8 +134,8 @@ $kind load docker-image "${plain_img}" --name "${kcluster_name}" $kind load docker-image "${plain_bundle_img}" --name "${kcluster_name}" ##################################### -# Create the FBC that contains both -# the plain+v0 and registry+v1 operators +# Create the FBC that contains both +# the plain+v0 and registry+v1 extensions ##################################### cat << EOF > "${TMP_ROOT}"/catalog.Dockerfile @@ -217,8 +217,8 @@ cat < "${TMP_ROOT}"/catalog/.indexignore ..* EOF -kubectl create configmap -n "${namespace}" --from-file="${TMP_ROOT}"/catalog.Dockerfile operator-dev-e2e.dockerfile -kubectl create configmap -n "${namespace}" --from-file="${TMP_ROOT}"/catalog operator-dev-e2e.build-contents +kubectl create configmap -n "${namespace}" --from-file="${TMP_ROOT}"/catalog.Dockerfile extension-dev-e2e.dockerfile +kubectl create configmap -n "${namespace}" --from-file="${TMP_ROOT}"/catalog extension-dev-e2e.build-contents kubectl apply -f - << EOF apiVersion: batch/v1 @@ -245,13 +245,13 @@ spec: volumes: - name: dockerfile configMap: - name: operator-dev-e2e.dockerfile + name: extension-dev-e2e.dockerfile items: - key: catalog.Dockerfile path: catalog.Dockerfile - name: build-contents configMap: - name: operator-dev-e2e.build-contents + name: extension-dev-e2e.build-contents EOF kubectl wait --for=condition=Complete -n "${namespace}" jobs/kaniko --timeout=60s