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 ac181102a..d1db3dc1f 100644 --- a/openshift/tests-extension/.openshift-tests-extension/openshift_payload_olmv1.json +++ b/openshift/tests-extension/.openshift-tests-extension/openshift_payload_olmv1.json @@ -527,7 +527,7 @@ "environmentSelector": {} }, { - "name": "[sig-olmv1][OCPFeatureGate:NewOLMWebhookProviderOpenshiftServiceCA] OLMv1 operator with webhooks should have a working mutating webhook [Serial]", + "name": "[sig-olmv1][OCPFeatureGate:NewOLMWebhookProviderOpenshiftServiceCA] OLMv1 operator with webhooks should have a working mutating webhook", "originalName": "[sig-olmv1][OCPFeatureGate:NewOLMWebhookProviderOpenshiftServiceCA][Skipped:Disconnected][Serial] OLMv1 operator with webhooks should have a working mutating webhook", "labels": { "original-name:[sig-olmv1][OCPFeatureGate:NewOLMWebhookProviderOpenshiftServiceCA][Skipped:Disconnected][Serial] OLMv1 operator with webhooks should have a working mutating webhook": {} @@ -540,7 +540,7 @@ "environmentSelector": {} }, { - "name": "[sig-olmv1][OCPFeatureGate:NewOLMWebhookProviderOpenshiftServiceCA] OLMv1 operator with webhooks should have a working conversion webhook [Serial]", + "name": "[sig-olmv1][OCPFeatureGate:NewOLMWebhookProviderOpenshiftServiceCA] OLMv1 operator with webhooks should have a working conversion webhook", "originalName": "[sig-olmv1][OCPFeatureGate:NewOLMWebhookProviderOpenshiftServiceCA][Skipped:Disconnected][Serial] OLMv1 operator with webhooks should have a working conversion webhook", "labels": { "original-name:[sig-olmv1][OCPFeatureGate:NewOLMWebhookProviderOpenshiftServiceCA][Skipped:Disconnected][Serial] OLMv1 operator with webhooks should have a working conversion webhook": {} @@ -553,7 +553,7 @@ "environmentSelector": {} }, { - "name": "[sig-olmv1][OCPFeatureGate:NewOLMWebhookProviderOpenshiftServiceCA] OLMv1 operator with webhooks should be tolerant to tls secret deletion [Serial]", + "name": "[sig-olmv1][OCPFeatureGate:NewOLMWebhookProviderOpenshiftServiceCA] OLMv1 operator with webhooks should be tolerant to tls secret deletion", "originalName": "[sig-olmv1][OCPFeatureGate:NewOLMWebhookProviderOpenshiftServiceCA][Skipped:Disconnected][Serial] OLMv1 operator with webhooks should be tolerant to tls secret deletion", "labels": { "original-name:[sig-olmv1][OCPFeatureGate:NewOLMWebhookProviderOpenshiftServiceCA][Skipped:Disconnected][Serial] OLMv1 operator with webhooks should be tolerant to tls secret deletion": {} diff --git a/openshift/tests-extension/pkg/bindata/webhook/bundle/bundle.go b/openshift/tests-extension/pkg/bindata/webhook/bundle/bundle.go index 1acc74d13..e09306a39 100644 --- a/openshift/tests-extension/pkg/bindata/webhook/bundle/bundle.go +++ b/openshift/tests-extension/pkg/bindata/webhook/bundle/bundle.go @@ -102,7 +102,7 @@ func dockerfile() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "Dockerfile", size: 888, mode: os.FileMode(420), modTime: time.Unix(1760231456, 0)} + info := bindataFileInfo{name: "Dockerfile", size: 888, mode: os.FileMode(420), modTime: time.Unix(1760370604, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -122,7 +122,7 @@ func manifestsWebhookOperatorControllerManagerMetricsService_v1_serviceYaml() (* return nil, err } - info := bindataFileInfo{name: "manifests/webhook-operator-controller-manager-metrics-service_v1_service.yaml", size: 469, mode: os.FileMode(420), modTime: time.Unix(1760231456, 0)} + info := bindataFileInfo{name: "manifests/webhook-operator-controller-manager-metrics-service_v1_service.yaml", size: 469, mode: os.FileMode(420), modTime: time.Unix(1760370604, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -142,7 +142,7 @@ func manifestsWebhookOperatorMetricsReader_rbacAuthorizationK8sIo_v1_clusterrole return nil, err } - info := bindataFileInfo{name: "manifests/webhook-operator-metrics-reader_rbac.authorization.k8s.io_v1_clusterrole.yaml", size: 191, mode: os.FileMode(420), modTime: time.Unix(1760231456, 0)} + info := bindataFileInfo{name: "manifests/webhook-operator-metrics-reader_rbac.authorization.k8s.io_v1_clusterrole.yaml", size: 191, mode: os.FileMode(420), modTime: time.Unix(1760370604, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -162,7 +162,7 @@ func manifestsWebhookOperatorWebhookService_v1_serviceYaml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "manifests/webhook-operator-webhook-service_v1_service.yaml", size: 395, mode: os.FileMode(420), modTime: time.Unix(1760231456, 0)} + info := bindataFileInfo{name: "manifests/webhook-operator-webhook-service_v1_service.yaml", size: 395, mode: os.FileMode(420), modTime: time.Unix(1760370604, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -182,7 +182,7 @@ func manifestsWebhookOperatorWebhooktestAdminRole_rbacAuthorizationK8sIo_v1_clus return nil, err } - info := bindataFileInfo{name: "manifests/webhook-operator-webhooktest-admin-role_rbac.authorization.k8s.io_v1_clusterrole.yaml", size: 440, mode: os.FileMode(420), modTime: time.Unix(1760231456, 0)} + info := bindataFileInfo{name: "manifests/webhook-operator-webhooktest-admin-role_rbac.authorization.k8s.io_v1_clusterrole.yaml", size: 440, mode: os.FileMode(420), modTime: time.Unix(1760370604, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -202,7 +202,7 @@ func manifestsWebhookOperatorWebhooktestEditorRole_rbacAuthorizationK8sIo_v1_clu return nil, err } - info := bindataFileInfo{name: "manifests/webhook-operator-webhooktest-editor-role_rbac.authorization.k8s.io_v1_clusterrole.yaml", size: 503, mode: os.FileMode(420), modTime: time.Unix(1760231456, 0)} + info := bindataFileInfo{name: "manifests/webhook-operator-webhooktest-editor-role_rbac.authorization.k8s.io_v1_clusterrole.yaml", size: 503, mode: os.FileMode(420), modTime: time.Unix(1760370604, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -222,7 +222,7 @@ func manifestsWebhookOperatorWebhooktestViewerRole_rbacAuthorizationK8sIo_v1_clu return nil, err } - info := bindataFileInfo{name: "manifests/webhook-operator-webhooktest-viewer-role_rbac.authorization.k8s.io_v1_clusterrole.yaml", size: 460, mode: os.FileMode(420), modTime: time.Unix(1760231456, 0)} + info := bindataFileInfo{name: "manifests/webhook-operator-webhooktest-viewer-role_rbac.authorization.k8s.io_v1_clusterrole.yaml", size: 460, mode: os.FileMode(420), modTime: time.Unix(1760370604, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -242,7 +242,7 @@ func manifestsWebhookOperatorClusterserviceversionYaml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "manifests/webhook-operator.clusterserviceversion.yaml", size: 8694, mode: os.FileMode(420), modTime: time.Unix(1760231456, 0)} + info := bindataFileInfo{name: "manifests/webhook-operator.clusterserviceversion.yaml", size: 8694, mode: os.FileMode(420), modTime: time.Unix(1760370604, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -262,7 +262,7 @@ func manifestsWebhookOperatorsCoreosIo_webhooktestsYaml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "manifests/webhook.operators.coreos.io_webhooktests.yaml", size: 12240, mode: os.FileMode(420), modTime: time.Unix(1760231456, 0)} + info := bindataFileInfo{name: "manifests/webhook.operators.coreos.io_webhooktests.yaml", size: 12240, mode: os.FileMode(420), modTime: time.Unix(1760370604, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -282,7 +282,7 @@ func metadataAnnotationsYaml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "metadata/annotations.yaml", size: 740, mode: os.FileMode(420), modTime: time.Unix(1760231456, 0)} + info := bindataFileInfo{name: "metadata/annotations.yaml", size: 740, mode: os.FileMode(420), modTime: time.Unix(1760370604, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -302,7 +302,7 @@ func testsScorecardConfigYaml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "tests/scorecard/config.yaml", size: 1614, mode: os.FileMode(420), modTime: time.Unix(1760231456, 0)} + info := bindataFileInfo{name: "tests/scorecard/config.yaml", size: 1614, mode: os.FileMode(420), modTime: time.Unix(1760370604, 0)} a := &asset{bytes: bytes, info: info} return a, nil } diff --git a/openshift/tests-extension/pkg/bindata/webhook/index/index.go b/openshift/tests-extension/pkg/bindata/webhook/index/index.go index 7eff667c9..9a8849082 100644 --- a/openshift/tests-extension/pkg/bindata/webhook/index/index.go +++ b/openshift/tests-extension/pkg/bindata/webhook/index/index.go @@ -93,7 +93,7 @@ func dockerfile() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "Dockerfile", size: 97, mode: os.FileMode(420), modTime: time.Unix(1760231456, 0)} + info := bindataFileInfo{name: "Dockerfile", size: 97, mode: os.FileMode(420), modTime: time.Unix(1760370604, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -113,7 +113,7 @@ func configsIndexYaml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "configs/index.yaml", size: 1122, mode: os.FileMode(420), modTime: time.Unix(1760231456, 0)} + info := bindataFileInfo{name: "configs/index.yaml", size: 1122, mode: os.FileMode(420), modTime: time.Unix(1760370604, 0)} a := &asset{bytes: bytes, info: info} return a, nil } diff --git a/openshift/tests-extension/test/webhooks.go b/openshift/tests-extension/test/webhooks.go index ef7e2cf28..48ed08a93 100644 --- a/openshift/tests-extension/test/webhooks.go +++ b/openshift/tests-extension/test/webhooks.go @@ -39,205 +39,204 @@ const ( webhookServiceCert = "webhook-operator-controller-manager-service-cert" ) -var _ = Describe("[sig-olmv1][OCPFeatureGate:NewOLMWebhookProviderOpenshiftServiceCA] OLMv1 operator with webhooks", - Ordered, Serial, func() { - var ( - k8sClient client.Client - dynamicClient dynamic.Interface - webhookOperatorInstallNamespace string - catalogName string - ) - - BeforeEach(func(ctx SpecContext) { - By("initializing Kubernetes client") - k8sClient = env.Get().K8sClient - restCfg := env.Get().RestCfg - var err error - dynamicClient, err = dynamic.NewForConfig(restCfg) - Expect(err).ToNot(HaveOccurred(), "failed to create dynamic client") - - By("requiring OLMv1 capability on OpenShift") - helpers.RequireOLMv1CapabilityOnOpenshift() - - By("requiring image-registry to be available") - helpers.RequireImageRegistry(ctx) - - By("ensuring no ClusterExtension and CRD from a previous run") - helpers.EnsureCleanupClusterExtension(ctx, webhookOperatorPackageName, webhookOperatorCRDName) +var _ = Describe("[sig-olmv1][OCPFeatureGate:NewOLMWebhookProviderOpenshiftServiceCA] OLMv1 operator with webhooks", func() { + var ( + k8sClient client.Client + dynamicClient dynamic.Interface + webhookOperatorInstallNamespace string + catalogName string + ) + + BeforeEach(func(ctx SpecContext) { + By("initializing Kubernetes client") + k8sClient = env.Get().K8sClient + restCfg := env.Get().RestCfg + var err error + dynamicClient, err = dynamic.NewForConfig(restCfg) + Expect(err).ToNot(HaveOccurred(), "failed to create dynamic client") + + By("requiring OLMv1 capability on OpenShift") + helpers.RequireOLMv1CapabilityOnOpenshift() + + By("requiring image-registry to be available") + helpers.RequireImageRegistry(ctx) + + By("ensuring no ClusterExtension and CRD from a previous run") + helpers.EnsureCleanupClusterExtension(ctx, webhookOperatorPackageName, webhookOperatorCRDName) + + // Build webhook operator bundle and catalog using the consolidated helper + // Note: {{ TEST-BUNDLE }} and {{ NAMESPACE }} will be auto-filled + + replacements := map[string]string{ + "{{ TEST-BUNDLE }}": "", // Auto-filled + "{{ NAMESPACE }}": "", // Auto-filled + "{{ TEST-CONTROLLER }}": image.LocationFor("quay.io/olmtest/webhook-operator:v0.0.5"), + } - // Build webhook operator bundle and catalog using the consolidated helper - // Note: {{ TEST-BUNDLE }} and {{ NAMESPACE }} will be auto-filled + var nsName, opName string + _, nsName, catalogName, opName = helpers.NewCatalogAndClusterBundles(ctx, replacements, + webhookindex.AssetNames, webhookindex.Asset, + webhookbundle.AssetNames, webhookbundle.Asset, + ) + By(fmt.Sprintf("webhook bundle %q and catalog %q built successfully in namespace %q", opName, catalogName, nsName)) - replacements := map[string]string{ - "{{ TEST-BUNDLE }}": "", // Auto-filled - "{{ NAMESPACE }}": "", // Auto-filled - "{{ TEST-CONTROLLER }}": image.LocationFor("quay.io/olmtest/webhook-operator:v0.0.5"), - } + // Create ClusterExtension in a separate namespace + // setupWebhookOperator now registers its own DeferCleanup handlers internally + webhookOperatorInstallNamespace = fmt.Sprintf("webhook-operator-%s", rand.String(5)) + setupWebhookOperator(ctx, k8sClient, webhookOperatorInstallNamespace, catalogName) + }) - var nsName, opName string - _, nsName, catalogName, opName = helpers.NewCatalogAndClusterBundles(ctx, replacements, - webhookindex.AssetNames, webhookindex.Asset, - webhookbundle.AssetNames, webhookbundle.Asset, - ) - By(fmt.Sprintf("webhook bundle %q and catalog %q built successfully in namespace %q", opName, catalogName, nsName)) - - // Create ClusterExtension in a separate namespace - // setupWebhookOperator now registers its own DeferCleanup handlers internally - webhookOperatorInstallNamespace = fmt.Sprintf("webhook-operator-%s", rand.String(5)) - setupWebhookOperator(ctx, k8sClient, webhookOperatorInstallNamespace, catalogName) - }) + AfterEach(func(ctx SpecContext) { + if CurrentSpecReport().Failed() { + By("dumping pod logs for debugging") + helpers.GetAllPodLogs(ctx, webhookOperatorInstallNamespace) + helpers.DescribePods(ctx, webhookOperatorInstallNamespace) + helpers.DescribeAllClusterCatalogs(ctx) + helpers.DescribeAllClusterExtensions(ctx, webhookOperatorInstallNamespace) + By("dumping webhook diagnostics") + // Additional diagnostics specific for this test + helpers.RunAndPrint(ctx, "get", "mutatingwebhookconfigurations.admissionregistration.k8s.io", "-oyaml") + helpers.RunAndPrint(ctx, "get", "validatingwebhookconfigurations.admissionregistration.k8s.io", "-oyaml") + } + // Note: cleanup is now handled by DeferCleanup in BeforeEach, which ensures + // cleanup runs even if BeforeEach or the test fails + }) - AfterEach(func(ctx SpecContext) { - if CurrentSpecReport().Failed() { - By("dumping pod logs for debugging") - helpers.GetAllPodLogs(ctx, webhookOperatorInstallNamespace) - helpers.DescribePods(ctx, webhookOperatorInstallNamespace) - helpers.DescribeAllClusterCatalogs(ctx) - helpers.DescribeAllClusterExtensions(ctx, webhookOperatorInstallNamespace) - By("dumping webhook diagnostics") - // Additional diagnostics specific for this test - helpers.RunAndPrint(ctx, "get", "mutatingwebhookconfigurations.admissionregistration.k8s.io", "-oyaml") - helpers.RunAndPrint(ctx, "get", "validatingwebhookconfigurations.admissionregistration.k8s.io", "-oyaml") + It("should have a working validating webhook", Label("original-name:[sig-olmv1][OCPFeatureGate:NewOLMWebhookProviderOpenshiftServiceCA][Skipped:Disconnected][Serial] OLMv1 operator with webhooks should have a working validating webhook"), func(ctx SpecContext) { + By("creating a webhook test resource that will be rejected by the validating webhook") + Eventually(func() error { + name := fmt.Sprintf("validating-webhook-test-%s", rand.String(5)) + obj := newWebhookTest(name, webhookOperatorInstallNamespace, false) + + _, err := dynamicClient. + Resource(webhookTestV1). + Namespace(webhookOperatorInstallNamespace). + Create(ctx, obj, metav1.CreateOptions{}) + + switch { + case err == nil: + // Webhook not ready yet; clean up and keep polling. + _ = dynamicClient.Resource(webhookTestV1). + Namespace(webhookOperatorInstallNamespace). + Delete(ctx, name, metav1.DeleteOptions{}) + return fmt.Errorf("webhook not rejecting yet") + case strings.Contains(err.Error(), "Invalid value: false: Spec.Valid must be true"): + return nil // got the expected validating-webhook rejection + default: + return fmt.Errorf("unexpected error: %v", err) } - // Note: cleanup is now handled by DeferCleanup in BeforeEach, which ensures - // cleanup runs even if BeforeEach or the test fails - }) - - It("should have a working validating webhook", Label("original-name:[sig-olmv1][OCPFeatureGate:NewOLMWebhookProviderOpenshiftServiceCA][Skipped:Disconnected][Serial] OLMv1 operator with webhooks should have a working validating webhook"), func(ctx SpecContext) { - By("creating a webhook test resource that will be rejected by the validating webhook") - Eventually(func() error { - name := fmt.Sprintf("validating-webhook-test-%s", rand.String(5)) - obj := newWebhookTest(name, webhookOperatorInstallNamespace, false) + }).WithTimeout(2 * time.Minute).WithPolling(5 * time.Second).Should(Succeed()) + }) - _, err := dynamicClient. - Resource(webhookTestV1). - Namespace(webhookOperatorInstallNamespace). - Create(ctx, obj, metav1.CreateOptions{}) - - switch { - case err == nil: - // Webhook not ready yet; clean up and keep polling. - _ = dynamicClient.Resource(webhookTestV1). - Namespace(webhookOperatorInstallNamespace). - Delete(ctx, name, metav1.DeleteOptions{}) - return fmt.Errorf("webhook not rejecting yet") - case strings.Contains(err.Error(), "Invalid value: false: Spec.Valid must be true"): - return nil // got the expected validating-webhook rejection - default: - return fmt.Errorf("unexpected error: %v", err) - } - }).WithTimeout(2 * time.Minute).WithPolling(5 * time.Second).Should(Succeed()) - }) + It("should have a working mutating webhook", Label("original-name:[sig-olmv1][OCPFeatureGate:NewOLMWebhookProviderOpenshiftServiceCA][Skipped:Disconnected][Serial] OLMv1 operator with webhooks should have a working mutating webhook"), func(ctx SpecContext) { + By("creating a valid webhook") + mutatingWebhookResourceName := "mutating-webhook-test" + resource := newWebhookTest(mutatingWebhookResourceName, webhookOperatorInstallNamespace, true) + Eventually(func(g Gomega) { + _, err := dynamicClient.Resource(webhookTestV1).Namespace(webhookOperatorInstallNamespace).Create(ctx, resource, metav1.CreateOptions{}) + g.Expect(err).ToNot(HaveOccurred()) + }).WithTimeout(1 * time.Minute).WithPolling(5 * time.Second).Should(Succeed()) + + By("getting the created resource in v1 schema") + obj, err := dynamicClient.Resource(webhookTestV1).Namespace(webhookOperatorInstallNamespace).Get(ctx, mutatingWebhookResourceName, metav1.GetOptions{}) + Expect(err).ToNot(HaveOccurred()) + Expect(obj).ToNot(BeNil()) + + By("validating the resource spec") + spec := obj.Object["spec"].(map[string]interface{}) + Expect(spec).To(Equal(map[string]interface{}{ + "valid": true, + "mutate": true, + })) + }) - It("should have a working mutating webhook [Serial]", Label("original-name:[sig-olmv1][OCPFeatureGate:NewOLMWebhookProviderOpenshiftServiceCA][Skipped:Disconnected][Serial] OLMv1 operator with webhooks should have a working mutating webhook"), func(ctx SpecContext) { - By("creating a valid webhook") - mutatingWebhookResourceName := "mutating-webhook-test" - resource := newWebhookTest(mutatingWebhookResourceName, webhookOperatorInstallNamespace, true) - Eventually(func(g Gomega) { - _, err := dynamicClient.Resource(webhookTestV1).Namespace(webhookOperatorInstallNamespace).Create(ctx, resource, metav1.CreateOptions{}) - g.Expect(err).ToNot(HaveOccurred()) - }).WithTimeout(1 * time.Minute).WithPolling(5 * time.Second).Should(Succeed()) - - By("getting the created resource in v1 schema") - obj, err := dynamicClient.Resource(webhookTestV1).Namespace(webhookOperatorInstallNamespace).Get(ctx, mutatingWebhookResourceName, metav1.GetOptions{}) - Expect(err).ToNot(HaveOccurred()) - Expect(obj).ToNot(BeNil()) - - By("validating the resource spec") - spec := obj.Object["spec"].(map[string]interface{}) - Expect(spec).To(Equal(map[string]interface{}{ + It("should have a working conversion webhook", Label("original-name:[sig-olmv1][OCPFeatureGate:NewOLMWebhookProviderOpenshiftServiceCA][Skipped:Disconnected][Serial] OLMv1 operator with webhooks should have a working conversion webhook"), func(ctx SpecContext) { + By("creating a conversion webhook test resource") + conversionWebhookResourceName := "conversion-webhook-test" + resourceV1 := newWebhookTest(conversionWebhookResourceName, webhookOperatorInstallNamespace, true) + Eventually(func(g Gomega) { + _, err := dynamicClient.Resource(webhookTestV1).Namespace(webhookOperatorInstallNamespace).Create(ctx, resourceV1, metav1.CreateOptions{}) + g.Expect(err).ToNot(HaveOccurred()) + }).WithTimeout(1 * time.Minute).WithPolling(5 * time.Second).Should(Succeed()) + + By("getting the created resource in v2 schema") + obj, err := dynamicClient.Resource(webhookTestV2).Namespace(webhookOperatorInstallNamespace).Get(ctx, conversionWebhookResourceName, metav1.GetOptions{}) + Expect(err).ToNot(HaveOccurred()) + Expect(obj).ToNot(BeNil()) + + By("validating the resource spec") + spec := obj.Object["spec"].(map[string]interface{}) + Expect(spec).To(Equal(map[string]interface{}{ + "conversion": map[string]interface{}{ "valid": true, "mutate": true, - })) - }) + }, + })) + }) - It("should have a working conversion webhook [Serial]", Label("original-name:[sig-olmv1][OCPFeatureGate:NewOLMWebhookProviderOpenshiftServiceCA][Skipped:Disconnected][Serial] OLMv1 operator with webhooks should have a working conversion webhook"), func(ctx SpecContext) { - By("creating a conversion webhook test resource") - conversionWebhookResourceName := "conversion-webhook-test" - resourceV1 := newWebhookTest(conversionWebhookResourceName, webhookOperatorInstallNamespace, true) - Eventually(func(g Gomega) { - _, err := dynamicClient.Resource(webhookTestV1).Namespace(webhookOperatorInstallNamespace).Create(ctx, resourceV1, metav1.CreateOptions{}) - g.Expect(err).ToNot(HaveOccurred()) - }).WithTimeout(1 * time.Minute).WithPolling(5 * time.Second).Should(Succeed()) - - By("getting the created resource in v2 schema") - obj, err := dynamicClient.Resource(webhookTestV2).Namespace(webhookOperatorInstallNamespace).Get(ctx, conversionWebhookResourceName, metav1.GetOptions{}) - Expect(err).ToNot(HaveOccurred()) - Expect(obj).ToNot(BeNil()) - - By("validating the resource spec") - spec := obj.Object["spec"].(map[string]interface{}) - Expect(spec).To(Equal(map[string]interface{}{ - "conversion": map[string]interface{}{ - "valid": true, - "mutate": true, - }, - })) - }) + It("should be tolerant to tls secret deletion", Label("original-name:[sig-olmv1][OCPFeatureGate:NewOLMWebhookProviderOpenshiftServiceCA][Skipped:Disconnected][Serial] OLMv1 operator with webhooks should be tolerant to tls secret deletion"), func(ctx SpecContext) { + certificateSecretName := webhookServiceCert + By("ensuring secret exists before deletion attempt") + Eventually(func(g Gomega) { + secret := &corev1.Secret{} + err := k8sClient.Get(ctx, client.ObjectKey{Name: certificateSecretName, Namespace: webhookOperatorInstallNamespace}, secret) + g.Expect(err).ToNot(HaveOccurred(), fmt.Sprintf("failed to get secret %s/%s", webhookOperatorInstallNamespace, certificateSecretName)) + }).WithTimeout(1 * time.Minute).WithPolling(5 * time.Second).Should(Succeed()) + + By("checking webhook is responsive through secret recreation after manual deletion") + tlsSecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: certificateSecretName, + Namespace: webhookOperatorInstallNamespace, + }, + } + err := k8sClient.Delete(ctx, tlsSecret, client.PropagationPolicy(metav1.DeletePropagationBackground)) + Expect(client.IgnoreNotFound(err)).ToNot(HaveOccurred()) - It("should be tolerant to tls secret deletion [Serial]", Label("original-name:[sig-olmv1][OCPFeatureGate:NewOLMWebhookProviderOpenshiftServiceCA][Skipped:Disconnected][Serial] OLMv1 operator with webhooks should be tolerant to tls secret deletion"), func(ctx SpecContext) { - certificateSecretName := webhookServiceCert - By("ensuring secret exists before deletion attempt") - Eventually(func(g Gomega) { + DeferCleanup(func() { + // Specific check for this test + if CurrentSpecReport().Failed() { + By("dumping certificate details for debugging") secret := &corev1.Secret{} - err := k8sClient.Get(ctx, client.ObjectKey{Name: certificateSecretName, Namespace: webhookOperatorInstallNamespace}, secret) - g.Expect(err).ToNot(HaveOccurred(), fmt.Sprintf("failed to get secret %s/%s", webhookOperatorInstallNamespace, certificateSecretName)) - }).WithTimeout(1 * time.Minute).WithPolling(5 * time.Second).Should(Succeed()) - - By("checking webhook is responsive through secret recreation after manual deletion") - tlsSecret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: certificateSecretName, + if err := k8sClient.Get(ctx, client.ObjectKey{ + Name: webhookServiceCert, Namespace: webhookOperatorInstallNamespace, - }, - } - err := k8sClient.Delete(ctx, tlsSecret, client.PropagationPolicy(metav1.DeletePropagationBackground)) - Expect(client.IgnoreNotFound(err)).ToNot(HaveOccurred()) - - DeferCleanup(func() { - // Specific check for this test - if CurrentSpecReport().Failed() { - By("dumping certificate details for debugging") - secret := &corev1.Secret{} - if err := k8sClient.Get(ctx, client.ObjectKey{ - Name: webhookServiceCert, - Namespace: webhookOperatorInstallNamespace, - }, secret); err == nil { - if crt, ok := secret.Data["tls.crt"]; ok && len(crt) > 0 { - printTLSCertInfo(crt) - } else { - _, _ = GinkgoWriter.Write([]byte("[diag] tls.crt key not found or empty in secret\n")) - } + }, secret); err == nil { + if crt, ok := secret.Data["tls.crt"]; ok && len(crt) > 0 { + printTLSCertInfo(crt) } else { - fmt.Fprintf(GinkgoWriter, "[diag] failed to get secret for cert dump: %v\n", err) + _, _ = GinkgoWriter.Write([]byte("[diag] tls.crt key not found or empty in secret\n")) } + } else { + fmt.Fprintf(GinkgoWriter, "[diag] failed to get secret for cert dump: %v\n", err) } - }) + } + }) - By("waiting for the webhook operator's service certificate secret to be recreated and populated") - Eventually(func(g Gomega) { - secret := &corev1.Secret{} - err := k8sClient.Get(ctx, client.ObjectKey{Name: certificateSecretName, Namespace: webhookOperatorInstallNamespace}, secret) - if apierrors.IsNotFound(err) { - GinkgoLogr.Info(fmt.Sprintf("Secret %s/%s not found yet (still polling for recreation)", webhookOperatorInstallNamespace, certificateSecretName)) - return - } - g.Expect(err).ToNot(HaveOccurred(), fmt.Sprintf("failed to get webhook service certificate secret %s/%s: %v", webhookOperatorInstallNamespace, certificateSecretName, err)) - g.Expect(secret.Data).ToNot(BeEmpty(), "expected webhook service certificate secret data to not be empty after recreation") - }).WithTimeout(5*time.Minute).WithPolling(10*time.Second).Should(Succeed(), "webhook service certificate secret did not get recreated and populated within timeout") + By("waiting for the webhook operator's service certificate secret to be recreated and populated") + Eventually(func(g Gomega) { + secret := &corev1.Secret{} + err := k8sClient.Get(ctx, client.ObjectKey{Name: certificateSecretName, Namespace: webhookOperatorInstallNamespace}, secret) + if apierrors.IsNotFound(err) { + GinkgoLogr.Info(fmt.Sprintf("Secret %s/%s not found yet (still polling for recreation)", webhookOperatorInstallNamespace, certificateSecretName)) + return + } + g.Expect(err).ToNot(HaveOccurred(), fmt.Sprintf("failed to get webhook service certificate secret %s/%s: %v", webhookOperatorInstallNamespace, certificateSecretName, err)) + g.Expect(secret.Data).ToNot(BeEmpty(), "expected webhook service certificate secret data to not be empty after recreation") + }).WithTimeout(5*time.Minute).WithPolling(10*time.Second).Should(Succeed(), "webhook service certificate secret did not get recreated and populated within timeout") - Eventually(func(g Gomega) { - resourceName := fmt.Sprintf("tls-deletion-test-%s", rand.String(5)) - resource := newWebhookTest(resourceName, webhookOperatorInstallNamespace, true) + Eventually(func(g Gomega) { + resourceName := fmt.Sprintf("tls-deletion-test-%s", rand.String(5)) + resource := newWebhookTest(resourceName, webhookOperatorInstallNamespace, true) - _, err := dynamicClient.Resource(webhookTestV1).Namespace(webhookOperatorInstallNamespace).Create(ctx, resource, metav1.CreateOptions{}) - g.Expect(err).ToNot(HaveOccurred(), fmt.Sprintf("failed to create test resource %s: %v", resourceName, err)) + _, err := dynamicClient.Resource(webhookTestV1).Namespace(webhookOperatorInstallNamespace).Create(ctx, resource, metav1.CreateOptions{}) + g.Expect(err).ToNot(HaveOccurred(), fmt.Sprintf("failed to create test resource %s: %v", resourceName, err)) - err = dynamicClient.Resource(webhookTestV1).Namespace(webhookOperatorInstallNamespace).Delete(ctx, resource.GetName(), metav1.DeleteOptions{}) - g.Expect(client.IgnoreNotFound(err)).ToNot(HaveOccurred(), fmt.Sprintf("failed to delete test resource %s: %v", resourceName, err)) - }).WithTimeout(5 * time.Minute).WithPolling(10 * time.Second).Should(Succeed()) - }) + err = dynamicClient.Resource(webhookTestV1).Namespace(webhookOperatorInstallNamespace).Delete(ctx, resource.GetName(), metav1.DeleteOptions{}) + g.Expect(client.IgnoreNotFound(err)).ToNot(HaveOccurred(), fmt.Sprintf("failed to delete test resource %s: %v", resourceName, err)) + }).WithTimeout(5 * time.Minute).WithPolling(10 * time.Second).Should(Succeed()) }) +}) var webhookTestV1 = schema.GroupVersionResource{ Group: "webhook.operators.coreos.io",