diff --git a/openshift/tests-extension/.openshift-tests-extension/openshift_payload_olmv1.json b/openshift/tests-extension/.openshift-tests-extension/openshift_payload_olmv1.json index 5599657b0..ad0f6e49f 100644 --- a/openshift/tests-extension/.openshift-tests-extension/openshift_payload_olmv1.json +++ b/openshift/tests-extension/.openshift-tests-extension/openshift_payload_olmv1.json @@ -475,7 +475,7 @@ "environmentSelector": {} }, { - "name": "[sig-olmv1][OCPFeatureGate:NewOLM][Skipped:Disconnected] OLMv1 operator installation should install a cluster extension", + "name": "[sig-olmv1][OCPFeatureGate:NewOLM][Skipped:Disconnected] OLMv1 operator installation should install an openshift catalog cluster extension", "labels": {}, "resources": { "isolation": {} @@ -485,8 +485,24 @@ "environmentSelector": {} }, { - "name": "[sig-olmv1][OCPFeatureGate:NewOLM][Skipped:Disconnected] OLMv1 operator installation should fail to install a non-existing cluster extension", - "labels": {}, + "name": "[sig-olmv1][OCPFeatureGate:NewOLM] OLMv1 operator installation should install a cluster extension", + "originalName": "[sig-olmv1][OCPFeatureGate:NewOLM][Skipped:Disconnected] OLMv1 operator installation should install a cluster extension", + "labels": { + "original-name:[sig-olmv1][OCPFeatureGate:NewOLM][Skipped:Disconnected] OLMv1 operator installation should install a cluster extension": {} + }, + "resources": { + "isolation": {} + }, + "source": "openshift:payload:olmv1", + "lifecycle": "blocking", + "environmentSelector": {} + }, + { + "name": "[sig-olmv1][OCPFeatureGate:NewOLM] OLMv1 operator installation should fail to install a non-existing cluster extension", + "originalName": "[sig-olmv1][OCPFeatureGate:NewOLM][Skipped:Disconnected] OLMv1 operator installation should fail to install a non-existing cluster extension", + "labels": { + "original-name:[sig-olmv1][OCPFeatureGate:NewOLM][Skipped:Disconnected] OLMv1 operator installation should fail to install a non-existing cluster extension": {} + }, "resources": { "isolation": {} }, diff --git a/openshift/tests-extension/pkg/bindata/operator/operator.go b/openshift/tests-extension/pkg/bindata/operator/operator.go index 30c6f5ffd..d4a997490 100644 --- a/openshift/tests-extension/pkg/bindata/operator/operator.go +++ b/openshift/tests-extension/pkg/bindata/operator/operator.go @@ -96,7 +96,7 @@ func dockerfile() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "Dockerfile", size: 888, mode: os.FileMode(420), modTime: time.Unix(1756998653, 0)} + info := bindataFileInfo{name: "Dockerfile", size: 888, mode: os.FileMode(420), modTime: time.Unix(1760017176, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -116,7 +116,7 @@ func manifestsRegistryClusterserviceversionYaml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "manifests/registry.clusterserviceversion.yaml", size: 3959, mode: os.FileMode(420), modTime: time.Unix(1759218321, 0)} + info := bindataFileInfo{name: "manifests/registry.clusterserviceversion.yaml", size: 3959, mode: os.FileMode(420), modTime: time.Unix(1760017176, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -136,7 +136,7 @@ func metadataAnnotationsYaml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "metadata/annotations.yaml", size: 732, mode: os.FileMode(420), modTime: time.Unix(1756998653, 0)} + info := bindataFileInfo{name: "metadata/annotations.yaml", size: 732, mode: os.FileMode(420), modTime: time.Unix(1760017176, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -156,7 +156,7 @@ func metadataPropertiesYaml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "metadata/properties.yaml", size: 73, mode: os.FileMode(420), modTime: time.Unix(1759218275, 0)} + info := bindataFileInfo{name: "metadata/properties.yaml", size: 73, mode: os.FileMode(420), modTime: time.Unix(1760017176, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -176,7 +176,7 @@ func testsScorecardConfigYaml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "tests/scorecard/config.yaml", size: 1614, mode: os.FileMode(420), modTime: time.Unix(1756998653, 0)} + info := bindataFileInfo{name: "tests/scorecard/config.yaml", size: 1614, mode: os.FileMode(420), modTime: time.Unix(1760017176, 0)} a := &asset{bytes: bytes, info: info} return a, nil } diff --git a/openshift/tests-extension/test/olmv1.go b/openshift/tests-extension/test/olmv1.go index b172bd672..c4c3e2623 100644 --- a/openshift/tests-extension/test/olmv1.go +++ b/openshift/tests-extension/test/olmv1.go @@ -10,6 +10,7 @@ import ( //nolint:staticcheck // ST1001: dot-imports for readability . "github.com/onsi/gomega" + "github.com/openshift/origin/test/extended/util/image" corev1 "k8s.io/api/core/v1" apiextclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" "k8s.io/apimachinery/pkg/api/meta" @@ -19,6 +20,8 @@ import ( olmv1 "github.com/operator-framework/operator-controller/api/v1" + catalogdata "github.com/openshift/operator-framework-operator-controller/openshift/tests-extension/pkg/bindata/catalog" + operatordata "github.com/openshift/operator-framework-operator-controller/openshift/tests-extension/pkg/bindata/operator" "github.com/openshift/operator-framework-operator-controller/openshift/tests-extension/pkg/env" "github.com/openshift/operator-framework-operator-controller/openshift/tests-extension/pkg/helpers" ) @@ -71,6 +74,8 @@ var _ = Describe("[sig-olmv1][OCPFeatureGate:NewOLM] OLMv1 CRDs", func() { }) }) +// Keeping this test as skip:disconnected, so we can attempt to install a "real" operator, rather than a generated one +// There is an equivalent in-cluster positive test below var _ = Describe("[sig-olmv1][OCPFeatureGate:NewOLM][Skipped:Disconnected] OLMv1 operator installation", func() { var ( namespace string @@ -102,7 +107,7 @@ var _ = Describe("[sig-olmv1][OCPFeatureGate:NewOLM][Skipped:Disconnected] OLMv1 } }) - It("should install a cluster extension", func(ctx SpecContext) { + It("should install an openshift catalog cluster extension", func(ctx SpecContext) { if !env.Get().IsOpenShift { Skip("Requires OCP Catalogs: not OpenShift") } @@ -117,38 +122,170 @@ var _ = Describe("[sig-olmv1][OCPFeatureGate:NewOLM][Skipped:Disconnected] OLMv1 By("waiting for the quay-operator ClusterExtension to be installed") helpers.ExpectClusterExtensionToBeInstalled(ctx, name) }) +}) - It("should fail to install a non-existing cluster extension", func(ctx SpecContext) { - By("ensuring no ClusterExtension and CRD for non-existing operator") - helpers.EnsureCleanupClusterExtension(context.Background(), "does-not-exist", "") // No CRD expected for non-existing operator +var _ = Describe("[sig-olmv1][OCPFeatureGate:NewOLM] OLMv1 operator installation", func() { + var unique, nsName, ccName, rbName, opName string + BeforeEach(func(ctx SpecContext) { + helpers.RequireOLMv1CapabilityOnOpenshift() + unique = rand.String(8) + nsName = "install-test-ns-" + unique + ccName = "install-test-cc-" + unique + rbName = "install-test-rb-" + unique + opName = "install-test-op-" + unique - By("applying the ClusterExtension resource") - name, cleanup := helpers.CreateClusterExtension("does-not-exist", "99.99.99", namespace, "") - DeferCleanup(cleanup) + By(fmt.Sprintf("setting a unique value: %q", unique)) + + testVersion := env.Get().OpenShiftVersion + replacements := map[string]string{ + "{{ TEST-BUNDLE }}": opName, + "{{ NAMESPACE }}": nsName, + "{{ VERSION }}": testVersion, + + // Using the shell image provided by origin as the controller image. + // The image is mirrored into disconnected environments for testing. + "{{ TEST-CONTROLLER }}": image.ShellImage(), + } + + By("creating a new Namespace") + nsCleanup := createNamespace(nsName) + DeferCleanup(nsCleanup) + + // The builder (and deployer) service accounts are created by OpenShift itself which injects them in the NS. + By(fmt.Sprintf("waiting for builder serviceaccount in %s", nsName)) + helpers.ExpectServiceAccountExists(ctx, "builder", nsName) + + By(fmt.Sprintf("waiting for deployer serviceaccount in %s", nsName)) + helpers.ExpectServiceAccountExists(ctx, "deployer", nsName) + + By("applying image-puller RoleBinding") + rbCleanup := createImagePullerRoleBinding(rbName, nsName) + DeferCleanup(rbCleanup) + + By("creating the operator BuildConfig") + bcCleanup := createBuildConfig(opName, nsName) + DeferCleanup(bcCleanup) + + By("creating the operator ImageStream") + isCleanup := createImageStream(opName, nsName) + DeferCleanup(isCleanup) + + By("creating the operator tarball") + fileOperator, fileCleanup := createTempTarBall(replacements, operatordata.AssetNames, operatordata.Asset) + DeferCleanup(fileCleanup) + By(fmt.Sprintf("created operator tarball %q", fileOperator)) + + By("starting the operator build via RAW URL") + opArgs := []string{ + "create", + "--raw", + fmt.Sprintf( + "/apis/build.openshift.io/v1/namespaces/%s/buildconfigs/%s/instantiatebinary?name=%s&namespace=%s", + nsName, opName, opName, nsName, + ), + "-f", + fileOperator, + } + buildOperator := startBuild(opArgs...) + + By(fmt.Sprintf("waiting for the build %q to finish", buildOperator.Name)) + waitForBuildToFinish(ctx, buildOperator.Name, nsName) + + By("creating the catalog BuildConfig") + bcCleanup = createBuildConfig(ccName, nsName) + DeferCleanup(bcCleanup) + + By("creating the catalog ImageStream") + isCleanup = createImageStream(ccName, nsName) + DeferCleanup(isCleanup) + + By("creating the catalog tarball") + fileCatalog, fileCleanup := createTempTarBall(replacements, catalogdata.AssetNames, catalogdata.Asset) + DeferCleanup(fileCleanup) + By(fmt.Sprintf("created catalog tarball %q", fileCatalog)) + + By("starting the catalog build via RAW URL") + catalogArgs := []string{ + "create", + "--raw", + fmt.Sprintf( + "/apis/build.openshift.io/v1/namespaces/%s/buildconfigs/%s/instantiatebinary?name=%s&namespace=%s", + nsName, ccName, ccName, nsName, + ), + "-f", + fileCatalog, + } + buildCatalog := startBuild(catalogArgs...) + + By(fmt.Sprintf("waiting for the build %q to finish", buildCatalog.Name)) + waitForBuildToFinish(ctx, buildCatalog.Name, nsName) - By("waiting for the ClusterExtension to exist") - ce := &olmv1.ClusterExtension{} - Eventually(func() error { - return env.Get().K8sClient.Get(ctx, client.ObjectKey{Name: name}, ce) - }).WithTimeout(5 * time.Minute).WithPolling(1 * time.Second).Should(Succeed()) - - By("waiting up to 2 minutes for ClusterExtension to report failure") - Eventually(func(g Gomega) { - k8sClient := env.Get().K8sClient - err := k8sClient.Get(ctx, client.ObjectKey{Name: name}, ce) - g.Expect(err).ToNot(HaveOccurred()) - - progressing := meta.FindStatusCondition(ce.Status.Conditions, olmv1.TypeProgressing) - g.Expect(progressing).ToNot(BeNil()) - g.Expect(progressing.Status).To(Equal(metav1.ConditionTrue)) - g.Expect(progressing.Reason).To(Equal("Retrying")) - g.Expect(progressing.Message).To(ContainSubstring(`no bundles found`)) - - installed := meta.FindStatusCondition(ce.Status.Conditions, olmv1.TypeInstalled) - g.Expect(installed).ToNot(BeNil()) - g.Expect(installed.Status).To(Equal(metav1.ConditionFalse)) - g.Expect(installed.Reason).To(Equal("Failed")) - g.Expect(installed.Message).To(Equal("No bundle installed")) - }).WithTimeout(5 * time.Minute).WithPolling(1 * time.Second).Should(Succeed()) + By("creating the ClusterCatalog") + ccCleanup := createClusterCatalog(ccName, nsName) + DeferCleanup(ccCleanup) }) + + AfterEach(func(ctx SpecContext) { + if CurrentSpecReport().Failed() { + By("dumping for debugging") + helpers.DescribeAllClusterCatalogs(context.Background()) + helpers.DescribeAllClusterExtensions(context.Background(), nsName) + } + }) + + It("should install a cluster extension", + Label("original-name:[sig-olmv1][OCPFeatureGate:NewOLM][Skipped:Disconnected] OLMv1 operator installation should install a cluster extension"), func(ctx SpecContext) { + if !env.Get().IsOpenShift { + Skip("Requires OCP Catalogs: not OpenShift") + } + + By("ensuring no ClusterExtension and CRD for the operator") + helpers.EnsureCleanupClusterExtension(context.Background(), opName, "") + + By("applying the ClusterExtension resource") + name, cleanup := helpers.CreateClusterExtension(opName, "", nsName, unique, helpers.WithCatalogNameSelector(ccName)) + DeferCleanup(cleanup) + + By("waiting for the ClusterExtension to be installed") + helpers.ExpectClusterExtensionToBeInstalled(ctx, name) + }) + + It("should fail to install a non-existing cluster extension", + Label("original-name:[sig-olmv1][OCPFeatureGate:NewOLM][Skipped:Disconnected] OLMv1 operator installation should fail to install a non-existing cluster extension"), func(ctx SpecContext) { + if !env.Get().IsOpenShift { + Skip("Requires OCP APIs: not OpenShift") + } + + By("ensuring no ClusterExtension and CRD for non-existing operator") + helpers.EnsureCleanupClusterExtension(context.Background(), "does-not-exist", "") // No CRD expected for non-existing operator + + By("applying the ClusterExtension resource") + name, cleanup := helpers.CreateClusterExtension("does-not-exist", "99.99.99", nsName, unique, helpers.WithCatalogNameSelector(ccName)) + DeferCleanup(cleanup) + + By("waiting for the ClusterExtension to exist") + ce := &olmv1.ClusterExtension{} + Eventually(func() error { + return env.Get().K8sClient.Get(ctx, client.ObjectKey{Name: name}, ce) + }).WithTimeout(5 * time.Minute).WithPolling(5 * time.Second).Should(Succeed()) + + By("waiting up to 2 minutes for ClusterExtension to report failure") + Eventually(func(g Gomega) { + k8sClient := env.Get().K8sClient + err := k8sClient.Get(ctx, client.ObjectKey{Name: name}, ce) + g.Expect(err).ToNot(HaveOccurred()) + + progressing := meta.FindStatusCondition(ce.Status.Conditions, olmv1.TypeProgressing) + g.Expect(progressing).ToNot(BeNil()) + g.Expect(progressing.Status).To(Equal(metav1.ConditionTrue)) + g.Expect(progressing.Reason).To(Equal("Retrying")) + g.Expect(progressing.Message).To(ContainSubstring(`no bundles found`)) + + installed := meta.FindStatusCondition(ce.Status.Conditions, olmv1.TypeInstalled) + g.Expect(installed).ToNot(BeNil()) + g.Expect(installed.Status).To(Equal(metav1.ConditionFalse)) + g.Expect(installed.Reason).To(Equal("Failed")) + g.Expect(installed.Message).To(Equal("No bundle installed")) + }).WithTimeout(5 * time.Minute).WithPolling(5 * time.Second).Should(Succeed()) + }) })