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 4788d0e72..d838eda39 100644 --- a/openshift/tests-extension/.openshift-tests-extension/openshift_payload_olmv1.json +++ b/openshift/tests-extension/.openshift-tests-extension/openshift_payload_olmv1.json @@ -79,6 +79,16 @@ "lifecycle": "blocking", "environmentSelector": {} }, + { + "name": "[sig-olmv1][OCPFeatureGate:NewOLMWebhookProviderOpenshiftServiceCA][Skipped:Disconnected][Serial] OLMv1 operator with webhooks should be tolerant to openshift-service-ca certificate rotation", + "labels": {}, + "resources": { + "isolation": {} + }, + "source": "openshift:payload:olmv1", + "lifecycle": "blocking", + "environmentSelector": {} + }, { "name": "[sig-olmv1][OCPFeatureGate:NewOLMWebhookProviderOpenshiftServiceCA][Skipped:Disconnected][Serial] OLMv1 operator with webhooks should be tolerant to tls secret deletion", "labels": {}, diff --git a/openshift/tests-extension/test/webhooks.go b/openshift/tests-extension/test/webhooks.go index f6733a083..53498dc76 100644 --- a/openshift/tests-extension/test/webhooks.go +++ b/openshift/tests-extension/test/webhooks.go @@ -29,10 +29,12 @@ import ( ) const ( - webhookCatalogName = "webhook-operator-catalog" - webhookOperatorPackageName = "webhook-operator" - webhookOperatorCRDName = "webhooktests.webhook.operators.coreos.io" - webhookServiceCert = "webhook-operator-webhook-service-cert" + openshiftServiceCANamespace = "openshift-service-ca" + openshiftServiceCASigningKeySecretName = "signing-key" + webhookCatalogName = "webhook-operator-catalog" + webhookOperatorPackageName = "webhook-operator" + webhookOperatorCRDName = "webhooktests.webhook.operators.coreos.io" + webhookServiceCert = "webhook-operator-webhook-service-cert" ) var _ = Describe("[sig-olmv1][OCPFeatureGate:NewOLMWebhookProviderOpenshiftServiceCA][Skipped:Disconnected][Serial] OLMv1 operator with webhooks", @@ -84,7 +86,6 @@ var _ = Describe("[sig-olmv1][OCPFeatureGate:NewOLMWebhookProviderOpenshiftServi 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") } @@ -167,6 +168,44 @@ var _ = Describe("[sig-olmv1][OCPFeatureGate:NewOLMWebhookProviderOpenshiftServi })) }) + It("should be tolerant to openshift-service-ca certificate rotation", func(ctx SpecContext) { + By("deleting the openshift-service-ca signing-key secret") + signingKeySecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: openshiftServiceCASigningKeySecretName, + Namespace: openshiftServiceCANamespace, + }, + } + err := k8sClient.Delete(ctx, signingKeySecret, client.PropagationPolicy(metav1.DeletePropagationBackground)) + Expect(client.IgnoreNotFound(err)).ToNot(HaveOccurred()) + + By("waiting for the webhook operator's service certificate secret to be recreated and populated") + certificateSecretName := webhookServiceCert + 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("checking webhook is responsive through cert rotation") + Eventually(func(g Gomega) { + resourceName := fmt.Sprintf("cert-rotation-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).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()) + }) + It("should be tolerant to tls secret deletion", func(ctx SpecContext) { certificateSecretName := webhookServiceCert By("ensuring secret exists before deletion attempt")