From 7b17a6ac32c715929b9acba177d13066ef2557ad Mon Sep 17 00:00:00 2001 From: Bruno Andrade Date: Tue, 3 Feb 2026 10:59:27 -0300 Subject: [PATCH] tests: update OLMv0 QE cases --- .../openshift_payload_olmv0.json | 144 ++++++ tests-extension/test/qe/specs/olmv0_common.go | 32 ++ .../test/qe/specs/olmv0_defaultoption.go | 236 ++++++++- .../test/qe/specs/olmv0_nonallns.go | 481 ++++++++++++++++++ 4 files changed, 887 insertions(+), 6 deletions(-) diff --git a/tests-extension/.openshift-tests-extension/openshift_payload_olmv0.json b/tests-extension/.openshift-tests-extension/openshift_payload_olmv0.json index 9050eaa015..b05d2f9421 100644 --- a/tests-extension/.openshift-tests-extension/openshift_payload_olmv0.json +++ b/tests-extension/.openshift-tests-extension/openshift_payload_olmv0.json @@ -117,6 +117,18 @@ "lifecycle": "blocking", "environmentSelector": {} }, + { + "name": "[sig-operator][Jira:OLM] OLMv0 should PolarionID:24818-[OTP]Checking OLM descriptors", + "labels": { + "Extended": {} + }, + "resources": { + "isolation": {} + }, + "source": "openshift:payload:olmv0", + "lifecycle": "blocking", + "environmentSelector": {} + }, { "name": "[sig-operator][Jira:OLM] OLMv0 should PolarionID:22259-[OTP][Skipped:Disconnected]marketplace operator CR status on a running cluster[Serial]", "originalName": "[sig-operator][Jira:OLM] OLMv0 should PolarionID:22259-[Skipped:Disconnected]marketplace operator CR status on a running cluster[Serial]", @@ -1307,6 +1319,54 @@ "lifecycle": "blocking", "environmentSelector": {} }, + { + "name": "[sig-operator][Jira:OLM] OLMv0 optional should PolarionID:43073-[OTP][Skipped:Disconnected]Indicate dependency class in resolution constraint text", + "labels": { + "Extended": {} + }, + "resources": { + "isolation": {} + }, + "source": "openshift:payload:olmv0", + "lifecycle": "blocking", + "environmentSelector": {} + }, + { + "name": "[sig-operator][Jira:OLM] OLMv0 optional should PolarionID:47149-[OTP][Skipped:Disconnected]Conjunctive constraint of one package and one GVK", + "labels": { + "Extended": {} + }, + "resources": { + "isolation": {} + }, + "source": "openshift:payload:olmv0", + "lifecycle": "blocking", + "environmentSelector": {} + }, + { + "name": "[sig-operator][Jira:OLM] OLMv0 optional should PolarionID:47181-[OTP][Skipped:Disconnected]Disjunctive constraint of one package and one GVK", + "labels": { + "Extended": {} + }, + "resources": { + "isolation": {} + }, + "source": "openshift:payload:olmv0", + "lifecycle": "blocking", + "environmentSelector": {} + }, + { + "name": "[sig-operator][Jira:OLM] OLMv0 optional should PolarionID:47179-[OTP][Skipped:Disconnected]Disjunctive constraint of one package and one GVK", + "labels": { + "Extended": {} + }, + "resources": { + "isolation": {} + }, + "source": "openshift:payload:olmv0", + "lifecycle": "blocking", + "environmentSelector": {} + }, { "name": "[sig-operator][Jira:OLM] OLMv0 optional should PolarionID:20981-[OTP]-contain the source commit id", "labels": { @@ -1520,6 +1580,48 @@ "lifecycle": "blocking", "environmentSelector": {} }, + { + "name": "[sig-operator][Jira:OLM] OLMv0 within a namespace PolarionID:24917-[OTP]Operators in SingleNamespace should not be granted namespace list [Serial][Disruptive]", + "labels": { + "Extended": {}, + "NonHyperShiftHOST": {} + }, + "resources": { + "isolation": {} + }, + "source": "openshift:payload:olmv0", + "lifecycle": "blocking", + "environmentSelector": { + "exclude": "topology==\"External\"" + } + }, + { + "name": "[sig-operator][Jira:OLM] OLMv0 within a namespace PolarionID:25644-[OTP]OLM collect CSV health per version", + "labels": { + "Extended": {} + }, + "resources": { + "isolation": {} + }, + "source": "openshift:payload:olmv0", + "lifecycle": "blocking", + "environmentSelector": {} + }, + { + "name": "[sig-operator][Jira:OLM] OLMv0 within a namespace PolarionID:27680-[OTP][Skipped:Disconnected]OLM Bundle support for Prometheus Types [Serial]", + "labels": { + "Extended": {}, + "NonHyperShiftHOST": {} + }, + "resources": { + "isolation": {} + }, + "source": "openshift:payload:olmv0", + "lifecycle": "blocking", + "environmentSelector": { + "exclude": "topology==\"External\"" + } + }, { "name": "[sig-operator][Jira:OLM] OLMv0 within a namespace PolarionID:22200-[OTP][Skipped:Disconnected]add minimum kube version to CSV [Slow]", "originalName": "[sig-operator][Jira:OLM] OLMv0 within a namespace PolarionID:22200-[Skipped:Disconnected]add minimum kube version to CSV [Slow]", @@ -2444,6 +2546,48 @@ "lifecycle": "blocking", "environmentSelector": {} }, + { + "name": "[sig-operator][Jira:OLM] OLMv0 within a namespace PolarionID:41026-[Level0][OTP]OCS should only one installplan generated when creating subscription", + "labels": { + "Extended": {} + }, + "resources": { + "isolation": {} + }, + "source": "openshift:payload:olmv0", + "lifecycle": "blocking", + "environmentSelector": {} + }, + { + "name": "[sig-operator][Jira:OLM] OLMv0 within a namespace PolarionID:68521-[OTP][Skipped:Disconnected]Check failureThreshold of redhat-operators catalog", + "labels": { + "Extended": {}, + "NonHyperShiftHOST": {} + }, + "resources": { + "isolation": {} + }, + "source": "openshift:payload:olmv0", + "lifecycle": "blocking", + "environmentSelector": { + "exclude": "topology==\"External\"" + } + }, + { + "name": "[sig-operator][Jira:OLM] OLMv0 within a namespace PolarionID:68901-[OTP][Skipped:Disconnected]Packageserver pod should not crash if updateStrategy is incorrect", + "labels": { + "Extended": {}, + "NonHyperShiftHOST": {} + }, + "resources": { + "isolation": {} + }, + "source": "openshift:payload:olmv0", + "lifecycle": "blocking", + "environmentSelector": { + "exclude": "topology==\"External\"" + } + }, { "name": "[sig-operator][Jira:OLM] OLMv0 opm should PolarionID:43185-[OTP]DC based opm subcommands out of alpha", "originalName": "[sig-operator][Jira:OLM] OLMv0 opm should PolarionID:43185-DC based opm subcommands out of alpha", diff --git a/tests-extension/test/qe/specs/olmv0_common.go b/tests-extension/test/qe/specs/olmv0_common.go index 38d4b1f954..01bac537c6 100644 --- a/tests-extension/test/qe/specs/olmv0_common.go +++ b/tests-extension/test/qe/specs/olmv0_common.go @@ -49,6 +49,38 @@ var _ = g.Describe("[sig-operator][Jira:OLM] OLMv0 should", func() { dr.RmIr(itName) }) + g.It("PolarionID:24818-[OTP]Checking OLM descriptors", func() { + olmErr := 0 + olmErrDescriptor := []string{""} + hasOperatorSource := true + if output, err := oc.AsAdmin().WithoutNamespace().Run("api-resources").Args("--no-headers").Output(); err == nil { + if !strings.Contains(output, "operatorsources") { + hasOperatorSource = false + } + } + olmExplains := []string{"InstallPlan", "ClusterServiceVersion", "Subscription", "CatalogSource", "OperatorSource", "OperatorGroup", "PackageManifest"} + for _, olmExplain := range olmExplains { + if olmExplain == "OperatorSource" && !hasOperatorSource { + e2e.Logf("skip explain for %s: resource type not found", olmExplain) + continue + } + msg, err := oc.AsAdmin().WithoutNamespace().Run("explain").Args(olmExplain).Output() + if err != nil { + olmErr++ + olmErrDescriptor = append(olmErrDescriptor, olmExplain) + } + o.Expect(err).NotTo(o.HaveOccurred()) + if strings.Contains(msg, "") { + olmErr++ + olmErrDescriptor = append(olmErrDescriptor, olmExplain) + } + } + if olmErr != 0 { + // fmt.Printf("explain errors: %d\n", olmErr) + e2e.Failf("%v errors in explaining the following OLM descriptors: %v", olmErr, olmErrDescriptor) + } + }) + g.It("PolarionID:22259-[OTP][Skipped:Disconnected]marketplace operator CR status on a running cluster[Serial]", g.Label("NonHyperShiftHOST"), g.Label("original-name:[sig-operator][Jira:OLM] OLMv0 should PolarionID:22259-[Skipped:Disconnected]marketplace operator CR status on a running cluster[Serial]"), func() { exutil.SkipForSNOCluster(oc) diff --git a/tests-extension/test/qe/specs/olmv0_defaultoption.go b/tests-extension/test/qe/specs/olmv0_defaultoption.go index 67e7c7bd64..2164e22a7c 100644 --- a/tests-extension/test/qe/specs/olmv0_defaultoption.go +++ b/tests-extension/test/qe/specs/olmv0_defaultoption.go @@ -541,15 +541,15 @@ var _ = g.Describe("[sig-operator][Jira:OLM] OLMv0 optional should", func() { olmv0util.NewCheck("expect", exutil.AsAdmin, exutil.WithoutNamespace, exutil.Compare, "Succeeded", exutil.Ok, []string{"csv", sub.InstalledCSV, "-n", oc.Namespace(), "-o=jsonpath={.status.phase}"}).Check(oc) g.By("Get pod name") - podName, err := oc.AsAdmin().Run("get").Args("pods", "-l", "name=learn-operator", "-n", oc.Namespace(), "-o=jsonpath={.items..metadata.name}").Output() + podName, err := oc.WithoutNamespace().Run("get").Args("pods", "-l", "name=learn-operator", "-n", oc.Namespace(), "-o=jsonpath={.items..metadata.name}").Output() o.Expect(err).NotTo(o.HaveOccurred()) g.By("Patch the deploy object by adding an environment variable") - _, err = oc.AsAdmin().WithoutNamespace().Run("set").Args("env", "deploy/learn-operator", "A=B", "-n", oc.Namespace()).Output() + _, err = oc.WithoutNamespace().Run("set").Args("env", "deploy/learn-operator", "A=B", "-n", oc.Namespace()).Output() o.Expect(err).NotTo(o.HaveOccurred()) g.By("Get restarted pod name") - podNameAfterPatch, err := oc.AsAdmin().Run("get").Args("pods", "-l", "name=learn-operator", "-n", oc.Namespace(), "-o=jsonpath={.items..metadata.name}").Output() + podNameAfterPatch, err := oc.WithoutNamespace().Run("get").Args("pods", "-l", "name=learn-operator", "-n", oc.Namespace(), "-o=jsonpath={.items..metadata.name}").Output() o.Expect(err).NotTo(o.HaveOccurred()) o.Expect(podName).NotTo(o.Equal(podNameAfterPatch)) }) @@ -1099,12 +1099,28 @@ var _ = g.Describe("[sig-operator][Jira:OLM] OLMv0 optional should", func() { } }) + // Polarion ID: 59413 // Polarion ID: 59413 g.It("PolarionID:59413-[OTP][Skipped:Disconnected]Default CatalogSource aren't created in restricted mode [Serial]", g.Label("NonHyperShiftHOST"), func() { exutil.SkipIfDisableDefaultCatalogsource(oc) defaultCatalogSources := []string{"certified-operators", "community-operators", "redhat-operators"} - g.By("step 1 -> check if the SCC is restricted") + existingCatalogSources, err := oc.AsAdmin().WithoutNamespace().Run("get").Args("catalogsource", "-n", "openshift-marketplace", "-o=jsonpath={.items[*].metadata.name}").Output() + o.Expect(err).NotTo(o.HaveOccurred()) + existingSet := map[string]bool{} + for _, name := range strings.Fields(existingCatalogSources) { + existingSet[name] = true + } + availableCatalogSources := []string{} for _, cs := range defaultCatalogSources { + if existingSet[cs] { + availableCatalogSources = append(availableCatalogSources, cs) + } + } + if len(availableCatalogSources) == 0 { + g.Skip("no default CatalogSources found in openshift-marketplace") + } + g.By("step 1 -> check if the SCC is restricted") + for _, cs := range availableCatalogSources { SCC, err := oc.AsAdmin().WithoutNamespace().Run("get").Args("catalogsource", cs, "-o=jsonpath={.spec.grpcPodConfig.securityContextConfig}", "-n", "openshift-marketplace").Output() if err != nil { e2e.Failf("fail to get %s's SCC, error:%v", cs, err) @@ -1114,11 +1130,11 @@ var _ = g.Describe("[sig-operator][Jira:OLM] OLMv0 optional should", func() { } } g.By("step 2 -> change the default SCC to legacy") - for _, cs := range defaultCatalogSources { + for _, cs := range availableCatalogSources { olmv0util.PatchResource(oc, exutil.AsAdmin, exutil.WithoutNamespace, "-n", "openshift-marketplace", "catalogsource", cs, "-p", "{\"spec\":{\"grpcPodConfig\": {\"securityContextConfig\": \"legacy\"}}}", "--type=merge") } g.By("step 3 -> check if SCC reset the restricted") - for _, cs := range defaultCatalogSources { + for _, cs := range availableCatalogSources { SCC, err := oc.AsAdmin().WithoutNamespace().Run("get").Args("catalogsource", cs, "-o=jsonpath={.spec.grpcPodConfig.securityContextConfig}", "-n", "openshift-marketplace").Output() if err != nil { e2e.Failf("fail to get %s's SCC, error:%v", cs, err) @@ -3118,6 +3134,214 @@ var _ = g.Describe("[sig-operator][Jira:OLM] OLMv0 optional should", func() { o.Expect(msg).To(o.ContainSubstring("learn-operator.v0.0.3")) }) + // Polarion ID: 43073 + g.It("PolarionID:43073-[OTP][Skipped:Disconnected]Indicate dependency class in resolution constraint text", func() { + olmv0util.ValidateAccessEnvironment(oc) + architecture.SkipNonAmd64SingleArch(oc) + + dr := make(olmv0util.DescriberResrouce) + itName := g.CurrentSpecReport().FullText() + dr.AddIr(itName) + buildPruningBaseDir := exutil.FixturePath("testdata", "olm") + csImageTemplate := filepath.Join(buildPruningBaseDir, "catalogsource-image.yaml") + subTemplate := filepath.Join(buildPruningBaseDir, "olm-subscription.yaml") + ogSingleTemplate := filepath.Join(buildPruningBaseDir, "operatorgroup.yaml") + + catsrc := olmv0util.CatalogSourceDescription{ + Name: "cs-43073", + Namespace: oc.Namespace(), + DisplayName: "OLM QE Operators", + Publisher: "bandrade", + SourceType: "grpc", + Address: "quay.io/olmqe/bundle-with-dep-error-index:4.0", + Template: csImageTemplate, + } + defer catsrc.Delete(itName, dr) + catsrc.CreateWithCheck(oc, itName, dr) + + og := olmv0util.OperatorGroupDescription{ + Name: "og-43073", + Namespace: oc.Namespace(), + Template: ogSingleTemplate, + } + og.CreateWithCheck(oc, itName, dr) + + sub := olmv0util.SubscriptionDescription{ + SubName: "lib-bucket-provisioner-43073", + Namespace: oc.Namespace(), + CatalogSourceName: catsrc.Name, + CatalogSourceNamespace: catsrc.Namespace, + Channel: "alpha", + IpApproval: "Automatic", + OperatorPackage: "lib-bucket-provisioner", + SingleNamespace: true, + Template: subTemplate, + } + defer sub.Delete(itName, dr) + defer sub.DeleteCSV(itName, dr) + sub.CreateWithoutCheck(oc, itName, dr) + + olmv0util.NewCheck("expect", exutil.AsAdmin, exutil.WithoutNamespace, exutil.Compare, "ConstraintsNotSatisfiable", exutil.Ok, []string{"subs", sub.SubName, "-n", oc.Namespace(), "-o=jsonpath={.status.conditions[?(@.type==\"ResolutionFailed\")].reason}"}).Check(oc) + }) + + // Polarion ID: 47149 + g.It("PolarionID:47149-[OTP][Skipped:Disconnected]Conjunctive constraint of one package and one GVK", func() { + olmv0util.ValidateAccessEnvironment(oc) + architecture.SkipNonAmd64SingleArch(oc) + exutil.SkipIfDisableDefaultCatalogsource(oc) + + dr := make(olmv0util.DescriberResrouce) + itName := g.CurrentSpecReport().FullText() + dr.AddIr(itName) + + buildPruningBaseDir := exutil.FixturePath("testdata", "olm") + subTemplate := filepath.Join(buildPruningBaseDir, "olm-subscription.yaml") + ogTemplate := filepath.Join(buildPruningBaseDir, "operatorgroup.yaml") + csImageTemplate := filepath.Join(buildPruningBaseDir, "catalogsource-image.yaml") + cs := olmv0util.CatalogSourceDescription{ + Name: "ocp-47149", + Namespace: oc.Namespace(), + DisplayName: "ocp-47149", + Publisher: "OLM QE", + SourceType: "grpc", + Address: "quay.io/olmqe/etcd-47149:1.0", + Template: csImageTemplate, + } + cs.CreateWithCheck(oc, itName, dr) + + og := olmv0util.OperatorGroupDescription{ + Name: "test-og-47149", + Namespace: oc.Namespace(), + Template: ogTemplate, + } + og.CreateWithCheck(oc, itName, dr) + + sub := olmv0util.SubscriptionDescription{ + SubName: "etcd", + Namespace: oc.Namespace(), + CatalogSourceName: cs.Name, + CatalogSourceNamespace: cs.Namespace, + Channel: "singlenamespace-alpha", + IpApproval: "Automatic", + OperatorPackage: "etcd", + SingleNamespace: true, + Template: subTemplate, + } + sub.Create(oc, itName, dr) + + waitErr := wait.PollUntilContextTimeout(context.TODO(), 15*time.Second, 360*time.Second, false, func(ctx context.Context) (bool, error) { + csvList, err := oc.AsAdmin().WithoutNamespace().Run("get").Args("csv", "-n", sub.Namespace).Output() + o.Expect(err).NotTo(o.HaveOccurred()) + lines := strings.Split(csvList, "\n") + for _, line := range lines { + if strings.Contains(line, "prometheusoperator") { + if strings.Contains(line, "Succeeded") { + return true, nil + } + return false, nil + } + } + return false, nil + }) + exutil.AssertWaitPollNoErr(waitErr, "csv prometheusoperator is not Succeeded") + olmv0util.NewCheck("expect", exutil.AsUser, exutil.WithoutNamespace, exutil.Compare, "Succeeded", exutil.Ok, []string{"csv", "etcdoperator.v0.9.4", "-n", sub.Namespace, "-o=jsonpath={.status.phase}"}).Check(oc) + }) + + // Polarion ID: 47181 + g.It("PolarionID:47181-[OTP][Skipped:Disconnected]Disjunctive constraint of one package and one GVK", func() { + olmv0util.ValidateAccessEnvironment(oc) + architecture.SkipNonAmd64SingleArch(oc) + + dr := make(olmv0util.DescriberResrouce) + itName := g.CurrentSpecReport().FullText() + dr.AddIr(itName) + + buildPruningBaseDir := exutil.FixturePath("testdata", "olm") + subTemplate := filepath.Join(buildPruningBaseDir, "olm-subscription.yaml") + ogTemplate := filepath.Join(buildPruningBaseDir, "operatorgroup.yaml") + csImageTemplate := filepath.Join(buildPruningBaseDir, "catalogsource-image.yaml") + cs := olmv0util.CatalogSourceDescription{ + Name: "ocp-47181", + Namespace: oc.Namespace(), + DisplayName: "ocp-47181", + Publisher: "OLM QE", + SourceType: "grpc", + Address: "quay.io/olmqe/etcd-47181:1.0", + Template: csImageTemplate, + } + cs.CreateWithCheck(oc, itName, dr) + + og := olmv0util.OperatorGroupDescription{ + Name: "test-og-47181", + Namespace: oc.Namespace(), + Template: ogTemplate, + } + og.CreateWithCheck(oc, itName, dr) + + sub := olmv0util.SubscriptionDescription{ + SubName: "etcd", + Namespace: oc.Namespace(), + CatalogSourceName: cs.Name, + CatalogSourceNamespace: cs.Namespace, + Channel: "singlenamespace-alpha", + IpApproval: "Automatic", + OperatorPackage: "etcd", + SingleNamespace: true, + Template: subTemplate, + } + sub.Create(oc, itName, dr) + + olmv0util.NewCheck("expect", exutil.AsUser, exutil.WithoutNamespace, exutil.Compare, "Succeeded", exutil.Ok, []string{"csv", sub.InstalledCSV, "-n", sub.Namespace, "-o=jsonpath={.status.phase}"}).Check(oc) + }) + + // Polarion ID: 47179 + g.It("PolarionID:47179-[OTP][Skipped:Disconnected]Disjunctive constraint of one package and one GVK", func() { + olmv0util.ValidateAccessEnvironment(oc) + architecture.SkipNonAmd64SingleArch(oc) + exutil.SkipIfDisableDefaultCatalogsource(oc) + + dr := make(olmv0util.DescriberResrouce) + itName := g.CurrentSpecReport().FullText() + dr.AddIr(itName) + + buildPruningBaseDir := exutil.FixturePath("testdata", "olm") + subTemplate := filepath.Join(buildPruningBaseDir, "olm-subscription.yaml") + ogTemplate := filepath.Join(buildPruningBaseDir, "operatorgroup.yaml") + csImageTemplate := filepath.Join(buildPruningBaseDir, "catalogsource-image.yaml") + cs := olmv0util.CatalogSourceDescription{ + Name: "ocp-47179", + Namespace: oc.Namespace(), + DisplayName: "ocp-47179", + Publisher: "OLM QE", + SourceType: "grpc", + Address: "quay.io/olmqe/etcd-47179:1.0", + Template: csImageTemplate, + } + cs.CreateWithCheck(oc, itName, dr) + + og := olmv0util.OperatorGroupDescription{ + Name: "test-og-47179", + Namespace: oc.Namespace(), + Template: ogTemplate, + } + og.CreateWithCheck(oc, itName, dr) + + sub := olmv0util.SubscriptionDescription{ + SubName: "etcd", + Namespace: oc.Namespace(), + CatalogSourceName: cs.Name, + CatalogSourceNamespace: cs.Namespace, + Channel: "singlenamespace-alpha", + IpApproval: "Automatic", + OperatorPackage: "etcd", + SingleNamespace: true, + Template: subTemplate, + } + sub.Create(oc, itName, dr) + + olmv0util.NewCheck("expect", exutil.AsUser, exutil.WithoutNamespace, exutil.Contain, "red-hat-camel-k-operator", exutil.Ok, []string{"csv", "-n", sub.Namespace}).Check(oc) + }) + // Polarion ID: 20981 g.It("PolarionID:20981-[OTP]-contain the source commit id", g.Label("NonHyperShiftHOST"), func() { if os.Getenv("GITHUB_TOKEN") == "" { diff --git a/tests-extension/test/qe/specs/olmv0_nonallns.go b/tests-extension/test/qe/specs/olmv0_nonallns.go index 0b9b387f36..387690d345 100644 --- a/tests-extension/test/qe/specs/olmv0_nonallns.go +++ b/tests-extension/test/qe/specs/olmv0_nonallns.go @@ -106,6 +106,352 @@ var _ = g.Describe("[sig-operator][Jira:OLM] OLMv0 within a namespace", func() { olmv0util.NewCheck("expect", exutil.AsUser, exutil.WithNamespace, exutil.Compare, "Succeeded"+"InstallSucceeded", exutil.Ok, []string{"csv", sub.InstalledCSV, "-o=jsonpath={.status.phase}{.status.reason}"}).Check(oc) }) + g.It("PolarionID:24917-[OTP]Operators in SingleNamespace should not be granted namespace list [Disruptive]", g.Label("NonHyperShiftHOST"), func() { + olmv0util.ValidateAccessEnvironment(oc) + itName := g.CurrentSpecReport().FullText() + ns := oc.Namespace() + buildPruningBaseDir := exutil.FixturePath("testdata", "olm") + ogSingleTemplate := filepath.Join(buildPruningBaseDir, "operatorgroup.yaml") + catsrcImageTemplate := filepath.Join(buildPruningBaseDir, "catalogsource-image.yaml") + subTemplate := filepath.Join(buildPruningBaseDir, "olm-subscription.yaml") + + g.By("Install the OperatorGroup in a random project") + og := olmv0util.OperatorGroupDescription{ + Name: "og-24917", + Namespace: ns, + Template: ogSingleTemplate, + } + defer og.Delete(itName, dr) + og.CreateWithCheck(oc, itName, dr) + + g.By("create the learn-operator CatalogSource") + catsrc := olmv0util.CatalogSourceDescription{ + Name: "catsrc-24917", + Namespace: ns, + DisplayName: "QE Operators", + Publisher: "OpenShift QE", + SourceType: "grpc", + Address: "quay.io/olmqe/learn-operator-index:v25", + Template: catsrcImageTemplate, + } + defer catsrc.Delete(itName, dr) + catsrc.CreateWithCheck(oc, itName, dr) + + g.By("Install the learn-operator with Automatic approval") + sub := olmv0util.SubscriptionDescription{ + SubName: "sub-24917", + Namespace: ns, + CatalogSourceName: catsrc.Name, + CatalogSourceNamespace: ns, + IpApproval: "Automatic", + Channel: "beta", + OperatorPackage: "learn", + SingleNamespace: true, + Template: subTemplate, + } + defer sub.Delete(itName, dr) + defer sub.DeleteCSV(itName, dr) + sub.Create(oc, itName, dr) + olmv0util.NewCheck("expect", exutil.AsAdmin, exutil.WithoutNamespace, exutil.Compare, "Succeeded", exutil.Ok, []string{"csv", sub.InstalledCSV, "-n", ns, "-o=jsonpath={.status.phase}"}).Check(oc) + + g.By("check if this operator's SA can list all namespaces") + expectedSA := fmt.Sprintf("system:serviceaccount:%s:learn-operator", ns) + msg, err := oc.AsAdmin().WithoutNamespace().Run("policy").Args("who-can", "list", "namespaces").Output() + o.Expect(err).NotTo(o.HaveOccurred()) + o.Expect(strings.Contains(msg, expectedSA)).To(o.BeFalse()) + + g.By("verify this SA user should NOT have the permission to list all namespaces") + canI, err := oc.AsAdmin().WithoutNamespace().Run("auth").Args("can-i", "list", "namespaces", "--as", expectedSA).Output() + if err != nil { + if exitErr, ok := err.(*exutil.ExitError); ok { + canI = exitErr.StdErr + } else { + o.Expect(err).NotTo(o.HaveOccurred()) + } + } + o.Expect(strings.Contains(canI, "no")).To(o.BeTrue()) + }) + + g.It("PolarionID:25644-[OTP]OLM collect CSV health per version", func() { + olmv0util.ValidateAccessEnvironment(oc) + var ( + caseID = "25644" + itName = g.CurrentSpecReport().FullText() + ns = oc.Namespace() + buildPruningBaseDir = exutil.FixturePath("testdata", "olm") + ogSingleTemplate = filepath.Join(buildPruningBaseDir, "operatorgroup.yaml") + catsrcImageTemplate = filepath.Join(buildPruningBaseDir, "catalogsource-image.yaml") + subTemplate = filepath.Join(buildPruningBaseDir, "olm-subscription.yaml") + csvName = "" + metricsURL = "https://localhost:8443/metrics" + ) + + og := olmv0util.OperatorGroupDescription{ + Name: "og-" + caseID, + Namespace: ns, + Template: ogSingleTemplate, + } + catsrc := olmv0util.CatalogSourceDescription{ + Name: "catsrc-" + caseID, + Namespace: ns, + DisplayName: "QE Operators", + Publisher: "OpenShift QE", + SourceType: "grpc", + Address: "quay.io/olmqe/learn-operator-index:v25", + Template: catsrcImageTemplate, + } + sub := olmv0util.SubscriptionDescription{ + SubName: "sub-" + caseID, + Namespace: ns, + CatalogSourceName: catsrc.Name, + CatalogSourceNamespace: ns, + IpApproval: "Automatic", + Channel: "beta", + OperatorPackage: "learn", + StartingCSV: csvName, + SingleNamespace: true, + Template: subTemplate, + } + + g.By("create the learn-operator CatalogSource") + defer catsrc.Delete(itName, dr) + catsrc.CreateWithCheck(oc, itName, dr) + + g.By("Get currentCSV from packagemanifest") + err := wait.PollUntilContextTimeout(context.TODO(), 5*time.Second, 180*time.Second, false, func(ctx context.Context) (bool, error) { + currentCSV, err := oc.AsAdmin().WithoutNamespace().Run("get").Args("packagemanifest", sub.OperatorPackage, "-n", ns, "-o=jsonpath={.status.channels[?(@.name==\""+sub.Channel+"\")].currentCSV}").Output() + if err != nil { + e2e.Logf("waiting for packagemanifest %s currentCSV: %v", sub.OperatorPackage, err) + return false, nil + } + currentCSV = strings.TrimSpace(currentCSV) + if currentCSV == "" { + e2e.Logf("packagemanifest %s currentCSV is empty", sub.OperatorPackage) + return false, nil + } + csvName = currentCSV + return true, nil + }) + exutil.AssertWaitPollNoErr(err, fmt.Sprintf("failed to get currentCSV for %s from packagemanifest", sub.OperatorPackage)) + sub.StartingCSV = csvName + sub.InstalledCSV = csvName + + g.By("Create subscription without OperatorGroup to trigger NoOperatorGroup") + defer sub.Delete(itName, dr) + defer sub.DeleteCSV(itName, dr) + sub.CreateWithoutCheck(oc, itName, dr) + + g.By("Wait for CSV to be created") + csvCreated := false + err = wait.PollUntilContextTimeout(context.TODO(), 5*time.Second, 180*time.Second, false, func(ctx context.Context) (bool, error) { + err := oc.AsAdmin().WithoutNamespace().Run("get").Args("csv", csvName, "-n", ns).Execute() + if err != nil { + e2e.Logf("waiting for csv %s to be created: %v", csvName, err) + return false, nil + } + return true, nil + }) + if err == nil { + csvCreated = true + } else { + reason, _ := oc.AsAdmin().WithoutNamespace().Run("get").Args("sub", sub.SubName, "-n", ns, "-o=jsonpath={.status.conditions..reason}").Output() + message, _ := oc.AsAdmin().WithoutNamespace().Run("get").Args("sub", sub.SubName, "-n", ns, "-o=jsonpath={.status.conditions..message}").Output() + e2e.Logf("subscription conditions reason: %q, message: %q", strings.TrimSpace(reason), strings.TrimSpace(message)) + e2e.Logf("csv %s not created without OperatorGroup, proceed with OperatorGroup creation", csvName) + } + + abnormalReason := "" + skipAbnormalMetric := false + if csvCreated { + err = wait.PollUntilContextTimeout(context.TODO(), 10*time.Second, 180*time.Second, false, func(ctx context.Context) (bool, error) { + reason, err := oc.AsAdmin().WithoutNamespace().Run("get").Args("csv", csvName, "-n", ns, "-o=jsonpath={.status.conditions..reason}").Output() + if err != nil { + e2e.Logf("waiting for csv %s to report NoOperatorGroup: %v", csvName, err) + return false, nil + } + if strings.Contains(strings.ToLower(reason), "operatorgroup") { + abnormalReason = strings.TrimSpace(reason) + return true, nil + } + return false, nil + }) + exutil.AssertWaitPollNoErr(err, fmt.Sprintf("csv %s did not report OperatorGroup-related reason", csvName)) + + o.Expect(abnormalReason).NotTo(o.BeEmpty()) + o.Expect(strings.Contains(abnormalReason, "OperatorGroup")).To(o.BeTrue()) + + sub.InstalledCSV = csvName + dr.GetIr(itName).Add(olmv0util.NewResource(oc, "csv", csvName, exutil.RequireNS, ns)) + } else { + skipAbnormalMetric = true + } + + olmToken, err := exutil.GetSAToken(oc, "prometheus-k8s", "openshift-monitoring") + o.Expect(err).NotTo(o.HaveOccurred()) + olmToken = strings.TrimSpace(olmToken) + + olmPodname, err := oc.AsAdmin().WithoutNamespace().Run("get").Args("pods", "-n", "openshift-operator-lifecycle-manager", "--selector=app=olm-operator", "-o=jsonpath={.items[0].metadata.name}").Output() + o.Expect(err).NotTo(o.HaveOccurred()) + olmPodname = strings.TrimSpace(olmPodname) + o.Expect(olmPodname).NotTo(o.BeEmpty()) + + if skipAbnormalMetric { + g.By("Skip csv_abnormal metric check because CSV was not created without OperatorGroup") + } else { + g.By("Check csv_abnormal metric for OperatorGroup-related reason") + err = wait.PollUntilContextTimeout(context.TODO(), 10*time.Second, 150*time.Second, false, func(ctx context.Context) (bool, error) { + metrics, err := oc.AsAdmin().WithoutNamespace().Run("exec").Args(olmPodname, "-n", "openshift-operator-lifecycle-manager", "-i", "--", "curl", "-k", "-H", fmt.Sprintf("Authorization: Bearer %s", olmToken), metricsURL).Output() + if err != nil { + e2e.Logf("failed to get olm-operator metrics: %v", err) + return false, nil + } + for _, line := range strings.Split(metrics, "\n") { + if strings.HasPrefix(line, "csv_abnormal{") && + strings.Contains(line, csvName) && + strings.Contains(line, fmt.Sprintf("namespace=\"%s\"", ns)) && + strings.Contains(line, fmt.Sprintf("reason=\"%s\"", abnormalReason)) { + fields := strings.Fields(line) + if len(fields) > 1 && fields[1] != "" { + e2e.Logf("csv_abnormal metric found: %s", line) + return true, nil + } + } + } + return false, nil + }) + exutil.AssertWaitPollNoErr(err, "csv_abnormal metric is not created") + } + + g.By("Create OperatorGroup for single namespace") + defer og.Delete(itName, dr) + og.Create(oc, itName, dr) + + g.By("Wait for subscription to report installedCSV") + sub.FindInstalledCSVWithSkip(oc, itName, dr, true) + if sub.InstalledCSV != "" { + csvName = sub.InstalledCSV + } + + g.By("Wait for CSV to become Succeeded") + err = wait.PollUntilContextTimeout(context.TODO(), 10*time.Second, 300*time.Second, false, func(ctx context.Context) (bool, error) { + phase, err := oc.AsAdmin().WithoutNamespace().Run("get").Args("csv", csvName, "-n", ns, "-o=jsonpath={.status.phase}").Output() + if err != nil { + e2e.Logf("waiting for csv %s to succeed: %v", csvName, err) + return false, nil + } + if strings.Contains(phase, "Succeeded") { + return true, nil + } + return false, nil + }) + exutil.AssertWaitPollNoErr(err, fmt.Sprintf("csv %s did not reach Succeeded phase", csvName)) + + g.By("Check csv_succeeded metric") + err = wait.PollUntilContextTimeout(context.TODO(), 10*time.Second, 150*time.Second, false, func(ctx context.Context) (bool, error) { + metrics, err := oc.AsAdmin().WithoutNamespace().Run("exec").Args(olmPodname, "-n", "openshift-operator-lifecycle-manager", "-i", "--", "curl", "-k", "-H", fmt.Sprintf("Authorization: Bearer %s", olmToken), metricsURL).Output() + if err != nil { + e2e.Logf("failed to get olm-operator metrics: %v", err) + return false, nil + } + for _, line := range strings.Split(metrics, "\n") { + if strings.HasPrefix(line, "csv_succeeded{") && + strings.Contains(line, csvName) && + strings.Contains(line, fmt.Sprintf("namespace=\"%s\"", ns)) { + fields := strings.Fields(line) + if len(fields) > 1 && fields[1] != "" { + e2e.Logf("csv_succeeded metric found: %s", line) + return true, nil + } + } + } + return false, nil + }) + exutil.AssertWaitPollNoErr(err, "csv_succeeded metric is not created") + }) + + g.It("PolarionID:27680-[OTP][Skipped:Disconnected]OLM Bundle support for Prometheus Types [Serial]", g.Label("NonHyperShiftHOST"), func() { + architecture.SkipNonAmd64SingleArch(oc) + exutil.SkipIfDisableDefaultCatalogsource(oc) + exutil.SkipBaselineCaps(oc, "None") + + var ( + itName = g.CurrentSpecReport().FullText() + ns = oc.Namespace() + buildPruningBaseDir = exutil.FixturePath("testdata", "olm") + csImageTemplate = filepath.Join(buildPruningBaseDir, "catalogsource-image.yaml") + ogSingleTemplate = filepath.Join(buildPruningBaseDir, "operatorgroup.yaml") + subTemplate = filepath.Join(buildPruningBaseDir, "olm-subscription.yaml") + csvName = "etcdoperator.v0.9.4" + ) + + g.By("Start to create the CatalogSource CR") + catsrc := olmv0util.CatalogSourceDescription{ + Name: "prometheus-dependency-27680", + Namespace: oc.Namespace(), + DisplayName: "OLM QE", + Publisher: "OLM QE", + SourceType: "grpc", + Address: "quay.io/olmqe/etcd-prometheus-dependency-index:11.0", + Template: csImageTemplate, + } + defer catsrc.Delete(itName, dr) + catsrc.CreateWithCheck(oc, itName, dr) + + g.By("Install the OperatorGroup in a random project") + og := olmv0util.OperatorGroupDescription{ + Name: "og-27680", + Namespace: ns, + Template: ogSingleTemplate, + } + defer og.Delete(itName, dr) + og.CreateWithCheck(oc, itName, dr) + + g.By("Install the etcdoperator v0.9.4 with Automatic approval") + sub := olmv0util.SubscriptionDescription{ + SubName: "sub-27680", + Namespace: ns, + CatalogSourceName: catsrc.Name, + CatalogSourceNamespace: catsrc.Namespace, + Channel: "singlenamespace-alpha", + IpApproval: "Automatic", + OperatorPackage: "etcd-service-monitor", + StartingCSV: csvName, + SingleNamespace: true, + Template: subTemplate, + } + defer sub.Delete(itName, dr) + defer sub.DeleteCSV(itName, dr) + sub.Create(oc, itName, dr) + olmv0util.NewCheck("expect", exutil.AsAdmin, exutil.WithoutNamespace, exutil.Compare, "Succeeded", exutil.Ok, []string{"csv", csvName, "-n", ns, "-o=jsonpath={.status.phase}"}).Check(oc) + + g.By("Assert that prometheus dependency is resolved") + msg, err := oc.AsAdmin().WithoutNamespace().Run("get").Args("csv", "-n", ns).Output() + o.Expect(err).NotTo(o.HaveOccurred()) + o.Expect(msg).To(o.ContainSubstring("prometheus")) + + g.By("Assert that ServiceMonitor is created") + err = wait.PollUntilContextTimeout(context.TODO(), 10*time.Second, 2*time.Minute, false, func(ctx context.Context) (bool, error) { + output, err := oc.AsAdmin().WithoutNamespace().Run("get").Args("ServiceMonitor", "my-servicemonitor", "-n", ns).Output() + if err != nil { + e2e.Logf("waiting for ServiceMonitor: %v", err) + return false, nil + } + return strings.Contains(output, "my-servicemonitor"), nil + }) + exutil.AssertWaitPollNoErr(err, "ServiceMonitor was not created") + + g.By("Assert that PrometheusRule is created") + err = wait.PollUntilContextTimeout(context.TODO(), 10*time.Second, 2*time.Minute, false, func(ctx context.Context) (bool, error) { + output, err := oc.AsAdmin().WithoutNamespace().Run("get").Args("PrometheusRule", "my-prometheusrule", "-n", ns).Output() + if err != nil { + e2e.Logf("waiting for PrometheusRule: %v", err) + return false, nil + } + return strings.Contains(output, "my-prometheusrule"), nil + }) + exutil.AssertWaitPollNoErr(err, "PrometheusRule was not created") + }) + g.It("PolarionID:22200-[OTP][Skipped:Disconnected]add minimum kube version to CSV [Slow]", g.Label("NonHyperShiftHOST"), g.Label("original-name:[sig-operator][Jira:OLM] OLMv0 within a namespace PolarionID:22200-[Skipped:Disconnected]add minimum kube version to CSV [Slow]"), func() { checkArch := architecture.ClusterArchitecture(oc) e2e.Logf("the curent arch is %v", checkArch.String()) @@ -6193,4 +6539,139 @@ var _ = g.Describe("[sig-operator][Jira:OLM] OLMv0 within a namespace", func() { }) + g.It("PolarionID:41026-[Level0][OTP]OCS should only one installplan generated when creating subscription", func() { + olmv0util.ValidateAccessEnvironment(oc) + var ( + itName = g.CurrentSpecReport().FullText() + buildPruningBaseDir = exutil.FixturePath("testdata", "olm") + ogSingleTemplate = filepath.Join(buildPruningBaseDir, "operatorgroup.yaml") + subTemplate = filepath.Join(buildPruningBaseDir, "olm-subscription.yaml") + catsrcImageTemplate = filepath.Join(buildPruningBaseDir, "catalogsource-image.yaml") + ns = oc.Namespace() + + og = olmv0util.OperatorGroupDescription{ + Name: "og-41026", + Namespace: ns, + Template: ogSingleTemplate, + } + catsrc = olmv0util.CatalogSourceDescription{ + Name: "catsrc-41026", + Namespace: ns, + DisplayName: "QE Operators", + Publisher: "OpenShift QE", + SourceType: "grpc", + Address: "quay.io/olmqe/learn-operator-index:v25", + Template: catsrcImageTemplate, + } + sub = olmv0util.SubscriptionDescription{ + SubName: "sub-41026", + Namespace: ns, + CatalogSourceName: "catsrc-41026", + CatalogSourceNamespace: ns, + Channel: "beta", + IpApproval: "Automatic", + OperatorPackage: "learn", + StartingCSV: "learn-operator.v0.0.3", + SingleNamespace: true, + Template: subTemplate, + } + ) + + og.CreateWithCheck(oc, itName, dr) + + defer catsrc.Delete(itName, dr) + catsrc.CreateWithCheck(oc, itName, dr) + + defer sub.Delete(itName, dr) + defer sub.DeleteCSV(itName, dr) + sub.Create(oc, itName, dr) + olmv0util.NewCheck("expect", exutil.AsAdmin, exutil.WithoutNamespace, exutil.Compare, "Succeeded", exutil.Ok, []string{"csv", "learn-operator.v0.0.3", "-n", ns, "-o=jsonpath={.status.phase}"}).Check(oc) + + g.By("Check there is only one installplan") + err := wait.PollUntilContextTimeout(context.TODO(), 5*time.Second, 30*time.Second, false, func(ctx context.Context) (bool, error) { + ips := olmv0util.GetResource(oc, exutil.AsAdmin, exutil.WithoutNamespace, "installplan", "-n", ns, "--no-headers") + if strings.TrimSpace(ips) == "" { + return false, nil + } + ipList := strings.Split(strings.TrimSpace(ips), "\n") + count := 0 + for _, ip := range ipList { + name := strings.Fields(ip) + if len(name) == 0 { + continue + } + csvs := olmv0util.GetResource(oc, exutil.AsAdmin, exutil.WithoutNamespace, "installplan", name[0], "-n", ns, "-o=jsonpath={.spec.clusterServiceVersionNames}") + if strings.Contains(csvs, sub.StartingCSV) { + count++ + } + } + if count != 1 { + return false, nil + } + return true, nil + }) + exutil.AssertWaitPollNoErr(err, "the generated InstallPlan != 1") + + g.By("Waiting for install plan Complete") + installPlan := sub.GetIP(oc) + o.Expect(installPlan).NotTo(o.BeEmpty()) + olmv0util.NewCheck("expect", exutil.AsAdmin, exutil.WithoutNamespace, exutil.Compare, "Complete", exutil.Ok, []string{"installplan", installPlan, "-n", ns, "-o=jsonpath={.status.phase}"}).Check(oc) + }) + + g.It("PolarionID:68521-[OTP][Skipped:Disconnected]Check failureThreshold of redhat-operators catalog", g.Label("NonHyperShiftHOST"), func() { + olmv0util.ValidateAccessEnvironment(oc) + architecture.SkipNonAmd64SingleArch(oc) + redhatOperators, err := oc.AsAdmin().WithoutNamespace().Run("get").Args("catalogsource", "redhat-operators", "-n", "openshift-marketplace").Output() + if err != nil { + if strings.Contains(redhatOperators, "not found") { + g.Skip("redhat-operators catalog does not exist in the cluster") + } + o.Expect(err).NotTo(o.HaveOccurred()) + } + olmv0util.NewCheck("expect", exutil.AsAdmin, exutil.WithoutNamespace, exutil.Contain, "10", exutil.Ok, []string{"pods", "-n", "openshift-marketplace", "-l", "olm.catalogSource=redhat-operators", "-o=jsonpath={..spec.containers[0].startupProbe.failureThreshold}"}).Check(oc) + }) + + g.It("PolarionID:68901-[OTP][Skipped:Disconnected]Packageserver pod should not crash if updateStrategy is incorrect", g.Label("NonHyperShiftHOST"), func() { + olmv0util.ValidateAccessEnvironment(oc) + var ( + itName = g.CurrentSpecReport().FullText() + buildPruningBaseDir = exutil.FixturePath("testdata", "olm") + catsrcImageTemplate = filepath.Join(buildPruningBaseDir, "catalogsource-image-incorrect-updatestrategy.yaml") + catsrc = olmv0util.CatalogSourceDescription{ + Name: "catsrc-68901", + Namespace: oc.Namespace(), + DisplayName: "Test Catsrc Operators", + Publisher: "Red Hat", + SourceType: "grpc", + Address: "quay.io/olmqe/nginxolm-operator-index:v1", + Template: catsrcImageTemplate, + } + ) + + defer catsrc.Delete(itName, dr) + catsrc.Create(oc, itName, dr) + olmv0util.NewCheck("expect", exutil.AsAdmin, exutil.WithoutNamespace, exutil.Compare, "InvalidIntervalError", exutil.Ok, []string{"catsrc", catsrc.Name, "-n", catsrc.Namespace, "-o=jsonpath={.status.reason}"}).Check(oc) + + waitErr := wait.PollUntilContextTimeout(context.TODO(), 5*time.Second, 20*time.Second, false, func(ctx context.Context) (bool, error) { + status, _ := oc.AsAdmin().WithoutNamespace().Run("get").Args("pods", "-n", "openshift-operator-lifecycle-manager", "-l", "app=catalog-operator", "-o=jsonpath={..status.phase}").Output() + if strings.Compare(status, "Running") != 0 { + return true, nil + } + return false, nil + }) + exutil.AssertWaitPollWithErr(waitErr, "catalog-operator pod crash") + + replicasStr, _ := oc.AsAdmin().WithoutNamespace().Run("get").Args("deployment", "packageserver", "-n", "openshift-operator-lifecycle-manager", "-o=jsonpath={.status.replicas}").Output() + replicas, _ := strconv.Atoi(strings.TrimSpace(replicasStr)) + err := wait.PollUntilContextTimeout(context.TODO(), 5*time.Second, 30*time.Second, false, func(ctx context.Context) (bool, error) { + status, _ := oc.AsAdmin().WithoutNamespace().Run("get").Args("pods", "-n", "openshift-operator-lifecycle-manager", "-l", "app=packageserver", "-o=jsonpath={..status.phase}").Output() + expected := strings.TrimSpace(strings.Repeat("Running ", replicas)) + if strings.TrimSpace(status) == expected { + return true, nil + } + return false, nil + }) + exutil.AssertWaitPollNoErr(err, "packageserver pods not all running") + }) + })