diff --git a/openshift/tests-extension/test/olmv1-preflight.go b/openshift/tests-extension/test/olmv1-preflight.go index b130e225d..dfe2edf51 100644 --- a/openshift/tests-extension/test/olmv1-preflight.go +++ b/openshift/tests-extension/test/olmv1-preflight.go @@ -10,6 +10,7 @@ import ( . "github.com/onsi/gomega" "github.com/openshift/api/features" + "github.com/openshift/origin/test/extended/util/image" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/api/meta" @@ -19,6 +20,8 @@ import ( olmv1 "github.com/operator-framework/operator-controller/api/v1" + singleownbundle "github.com/openshift/operator-framework-operator-controller/openshift/tests-extension/pkg/bindata/singleown/bundle" + singleownindex "github.com/openshift/operator-framework-operator-controller/openshift/tests-extension/pkg/bindata/singleown/index" "github.com/openshift/operator-framework-operator-controller/openshift/tests-extension/pkg/env" "github.com/openshift/operator-framework-operator-controller/openshift/tests-extension/pkg/helpers" ) @@ -35,16 +38,41 @@ const ( scenarioMissingClusterExtensionRevisionsFinalizerPerms preflightAuthTestScenario = 6 ) +const preflightBundleVersion = "0.0.5" + var _ = Describe("[sig-olmv1][OCPFeatureGate:NewOLMPreflightPermissionChecks][Skipped:Disconnected] OLMv1 operator preflight checks", func() { var ( - namespace string - k8sClient client.Client + namespace string + k8sClient client.Client + catalogName string + packageName string ) - BeforeEach(func() { + BeforeEach(func(ctx SpecContext) { helpers.RequireOLMv1CapabilityOnOpenshift() + helpers.RequireImageRegistry(ctx) k8sClient = env.Get().K8sClient namespace = "preflight-test-ns-" + rand.String(4) + // Build in-cluster bundle and catalog (same approach as single-namespace tests) + crdSuffix := rand.String(4) + packageName = fmt.Sprintf("preflight-operator-%s", crdSuffix) + crdName := fmt.Sprintf("webhooktests-%s.webhook.operators.coreos.io", crdSuffix) + helpers.EnsureCleanupClusterExtension(context.Background(), packageName, crdName) + + singleownImage := image.LocationFor("quay.io/olmtest/webhook-operator:v0.0.5") + replacements := map[string]string{ + "{{ TEST-BUNDLE }}": "", + "{{ NAMESPACE }}": "", + "{{ TEST-CONTROLLER }}": singleownImage, + "{{ CRD-SUFFIX }}": crdSuffix, + "{{ PACKAGE-NAME }}": packageName, + } + _, _, catalogName, _ = helpers.NewCatalogAndClusterBundles(ctx, replacements, + singleownindex.AssetNames, singleownindex.Asset, + singleownbundle.AssetNames, singleownbundle.Asset, + ) + By(fmt.Sprintf("preflight catalog %q and package %q built successfully", catalogName, packageName)) + By(fmt.Sprintf("creating namespace %s", namespace)) ns := &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ @@ -58,39 +86,39 @@ var _ = Describe("[sig-olmv1][OCPFeatureGate:NewOLMPreflightPermissionChecks][Sk }) It("should report error when {services} are not specified", func(ctx SpecContext) { - runNegativePreflightTest(ctx, scenarioMissingServicePerms, namespace) + runNegativePreflightTest(ctx, scenarioMissingServicePerms, namespace, packageName, catalogName) }) It("should report error when {create} verb is not specified", func(ctx SpecContext) { - runNegativePreflightTest(ctx, scenarioMissingCreateVerb, namespace) + runNegativePreflightTest(ctx, scenarioMissingCreateVerb, namespace, packageName, catalogName) }) It("should report error when {ClusterRoleBindings} are not specified", func(ctx SpecContext) { - runNegativePreflightTest(ctx, scenarioMissingClusterRoleBindingsPerms, namespace) + runNegativePreflightTest(ctx, scenarioMissingClusterRoleBindingsPerms, namespace, packageName, catalogName) }) It("should report error when {ConfigMap:resourceNames} are not all specified", func(ctx SpecContext) { - runNegativePreflightTest(ctx, scenarioMissingNamedConfigMapPerms, namespace) + runNegativePreflightTest(ctx, scenarioMissingNamedConfigMapPerms, namespace, packageName, catalogName) }) It("should report error when {clusterextension/finalizer} is not specified", func(ctx SpecContext) { helpers.RequireFeatureGateDisabled(features.FeatureGateNewOLMBoxCutterRuntime) - runNegativePreflightTest(ctx, scenarioMissingClusterExtensionsFinalizerPerms, namespace) + runNegativePreflightTest(ctx, scenarioMissingClusterExtensionsFinalizerPerms, namespace, packageName, catalogName) }) It("should report error when {clusterextensionrevisions/finalizer} is not specified", func(ctx SpecContext) { helpers.RequireFeatureGateEnabled(features.FeatureGateNewOLMBoxCutterRuntime) - runNegativePreflightTest(ctx, scenarioMissingClusterExtensionRevisionsFinalizerPerms, namespace) + runNegativePreflightTest(ctx, scenarioMissingClusterExtensionRevisionsFinalizerPerms, namespace, packageName, catalogName) }) It("should report error when {escalate, bind} is not specified", func(ctx SpecContext) { - runNegativePreflightTest(ctx, scenarioMissingEscalateAndBindPerms, namespace) + runNegativePreflightTest(ctx, scenarioMissingEscalateAndBindPerms, namespace, packageName, catalogName) }) }) // runNegativePreflightTest creates a deficient ClusterRole and a ClusterExtension that -// relies on it, then waits for the expected preflight failure. -func runNegativePreflightTest(ctx context.Context, scenario preflightAuthTestScenario, namespace string) { +// relies on it (using the in-cluster built catalog), then waits for the expected preflight failure. +func runNegativePreflightTest(ctx context.Context, scenario preflightAuthTestScenario, namespace, packageName, catalogName string) { k8sClient := env.Get().K8sClient unique := rand.String(8) @@ -121,14 +149,17 @@ func runNegativePreflightTest(ctx context.Context, scenario preflightAuthTestSce _ = k8sClient.Delete(ctx, crb) }) - // Step 4: Create ClusterExtension referencing that SA - ce := helpers.NewClusterExtensionObject("openshift-pipelines-operator-rh", "1.15.0", ceName, saName, namespace) + // Step 4: Create ClusterExtension referencing that SA, using in-cluster catalog + ce := helpers.NewClusterExtensionObject(packageName, preflightBundleVersion, ceName, saName, namespace, helpers.WithCatalogNameSelector(catalogName)) Expect(k8sClient.Create(ctx, ce)).To(Succeed(), "failed to create ClusterExtension") DeferCleanup(func(ctx SpecContext) { _ = k8sClient.Delete(ctx, ce) }) // Step 5: Wait for failure + // Pre-authorization failure is reported in the Progressing condition. The controller may set + // Progressing=True (Retrying) or Progressing=False (Blocked); we only assert that the + // message indicates pre-authorization failed (do not assert Status to avoid brittle failures). By("waiting for ClusterExtension to report preflight failure") Eventually(func(g Gomega) { latest := &olmv1.ClusterExtension{} @@ -136,9 +167,9 @@ func runNegativePreflightTest(ctx context.Context, scenario preflightAuthTestSce g.Expect(err).NotTo(HaveOccurred()) c := meta.FindStatusCondition(latest.Status.Conditions, "Progressing") - g.Expect(c).NotTo(BeNil()) - g.Expect(c.Status).To(Equal(metav1.ConditionTrue)) - g.Expect(c.Message).To(ContainSubstring("pre-authorization failed")) + g.Expect(c).NotTo(BeNil(), "Progressing condition should be set") + // Assert only on message: controller may set Status True (Retrying) or False (Blocked). + g.Expect(c.Message).To(ContainSubstring("pre-authorization failed"), "Progressing message should report pre-authorization failure") }).WithTimeout(helpers.DefaultTimeout).WithPolling(helpers.DefaultPolling).Should(Succeed()) } @@ -228,22 +259,25 @@ func createDeficientClusterRole(scenario preflightAuthTestScenario, name, ceName } } case scenarioMissingNamedConfigMapPerms: - // Restrict configmaps to named subset (resourceNames) - for i, r := range rules { - if r.APIGroups[0] == "" { + // Restrict ClusterRoles to a named subset so the SA cannot manage all roles required by the bundle. + // The webhook/singleown bundle has no ConfigMaps but has ClusterRoles (webhook-operator-*-role, webhook-operator-metrics-reader). + // By allowing only one, pre-authorization fails for the others. + for i := range rules { + if rules[i].APIGroups[0] == "rbac.authorization.k8s.io" { filtered := []string{} - for _, res := range r.Resources { - if res != "configmaps" && res != "configmaps/finalizers" { + for _, res := range rules[i].Resources { + if res != "clusterroles" && res != "clusterroles/finalizers" { filtered = append(filtered, res) } } rules[i].Resources = filtered rules = append(rules, rbacv1.PolicyRule{ - APIGroups: []string{""}, - Resources: []string{"configmaps"}, - Verbs: r.Verbs, - ResourceNames: []string{"config-logging", "tekton-config-defaults", "tekton-config-observability"}, + APIGroups: []string{"rbac.authorization.k8s.io"}, + Resources: []string{"clusterroles", "clusterroles/finalizers"}, + Verbs: []string{"delete", "deletecollection", "create", "patch", "get", "list", "update", "watch"}, + ResourceNames: []string{"webhook-operator-metrics-reader"}, }) + break } } case scenarioMissingClusterExtensionsFinalizerPerms: