Skip to content

Commit 2182497

Browse files
camilamacedo86ci-robot
authored andcommitted
UPSTREAM: <carry>: [OTE] Add webhook to validate openshift-service-ca certificate rotation
This change is a refactor of code from openshift/origin#30059. Assisted-by: Gemini
1 parent a2ae884 commit 2182497

File tree

2 files changed

+86
-5
lines changed

2 files changed

+86
-5
lines changed

openshift/tests-extension/.openshift-tests-extension/openshift_payload_olmv1.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,16 @@
139139
"lifecycle": "blocking",
140140
"environmentSelector": {}
141141
},
142+
{
143+
"name": "[sig-olmv1][OCPFeatureGate:NewOLMWebhookProviderOpenshiftServiceCA][Skipped:Disconnected][Serial] OLMv1 operator with webhooks should be tolerant to openshift-service-ca certificate rotation",
144+
"labels": {},
145+
"resources": {
146+
"isolation": {}
147+
},
148+
"source": "openshift:payload:olmv1",
149+
"lifecycle": "blocking",
150+
"environmentSelector": {}
151+
},
142152
{
143153
"name": "[sig-olmv1][OCPFeatureGate:NewOLMWebhookProviderOpenshiftServiceCA][Skipped:Disconnected][Serial] OLMv1 operator with webhooks should be tolerant to tls secret deletion",
144154
"labels": {},

openshift/tests-extension/test/webhooks.go

Lines changed: 76 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,12 @@ import (
2929
)
3030

3131
const (
32-
webhookCatalogName = "webhook-operator-catalog"
33-
webhookOperatorPackageName = "webhook-operator"
34-
webhookOperatorCRDName = "webhooktests.webhook.operators.coreos.io"
35-
webhookServiceCert = "webhook-operator-webhook-service-cert"
32+
openshiftServiceCANamespace = "openshift-service-ca"
33+
openshiftServiceCASigningKeySecretName = "signing-key"
34+
webhookCatalogName = "webhook-operator-catalog"
35+
webhookOperatorPackageName = "webhook-operator"
36+
webhookOperatorCRDName = "webhooktests.webhook.operators.coreos.io"
37+
webhookServiceCert = "webhook-operator-webhook-service-cert"
3638
)
3739

3840
var _ = Describe("[sig-olmv1][OCPFeatureGate:NewOLMWebhookProviderOpenshiftServiceCA][Skipped:Disconnected][Serial] OLMv1 operator with webhooks",
@@ -84,7 +86,6 @@ var _ = Describe("[sig-olmv1][OCPFeatureGate:NewOLMWebhookProviderOpenshiftServi
8486
helpers.DescribeAllClusterCatalogs(ctx)
8587
helpers.DescribeAllClusterExtensions(ctx, webhookOperatorInstallNamespace)
8688
By("dumping webhook diagnostics")
87-
// Additional diagnostics specific for this test
8889
helpers.RunAndPrint(ctx, "get", "mutatingwebhookconfigurations.admissionregistration.k8s.io", "-oyaml")
8990
helpers.RunAndPrint(ctx, "get", "validatingwebhookconfigurations.admissionregistration.k8s.io", "-oyaml")
9091
}
@@ -167,6 +168,76 @@ var _ = Describe("[sig-olmv1][OCPFeatureGate:NewOLMWebhookProviderOpenshiftServi
167168
}))
168169
})
169170

171+
It("should be tolerant to openshift-service-ca certificate rotation", func(ctx SpecContext) {
172+
certificateSecretName := webhookServiceCert
173+
var oldSecretResourceVersion string
174+
175+
By("ensuring the webhook operator's service certificate secret exists and getting its ResourceVersion")
176+
Eventually(func(g Gomega) {
177+
secret := &corev1.Secret{}
178+
err := k8sClient.Get(ctx, client.ObjectKey{Name: certificateSecretName, Namespace: webhookOperatorInstallNamespace}, secret)
179+
g.Expect(err).ToNot(HaveOccurred(), fmt.Sprintf("failed to get webhook service certificate secret %s/%s", webhookOperatorInstallNamespace, certificateSecretName))
180+
g.Expect(secret.Data).ToNot(BeEmpty(), "expected webhook service certificate secret data to not be empty")
181+
oldSecretResourceVersion = secret.ResourceVersion
182+
g.Expect(oldSecretResourceVersion).ToNot(BeEmpty(), "expected secret ResourceVersion to not be empty")
183+
}).WithTimeout(3 * time.Minute).WithPolling(5 * time.Second).Should(Succeed())
184+
185+
By("deleting the openshift-service-ca signing-key secret")
186+
signingKeySecret := &corev1.Secret{
187+
ObjectMeta: metav1.ObjectMeta{
188+
Name: openshiftServiceCASigningKeySecretName,
189+
Namespace: openshiftServiceCANamespace,
190+
},
191+
}
192+
err := k8sClient.Delete(ctx, signingKeySecret, client.PropagationPolicy(metav1.DeletePropagationBackground))
193+
Expect(client.IgnoreNotFound(err)).ToNot(HaveOccurred())
194+
195+
By("waiting for the webhook operator's service certificate secret to be recreated with a new ResourceVersion")
196+
Eventually(func(g Gomega) {
197+
secret := &corev1.Secret{}
198+
err := k8sClient.Get(ctx, client.ObjectKey{Name: certificateSecretName, Namespace: webhookOperatorInstallNamespace}, secret)
199+
if apierrors.IsNotFound(err) {
200+
GinkgoLogr.Info(fmt.Sprintf("Secret %s/%s not found yet (still polling for recreation)", webhookOperatorInstallNamespace, certificateSecretName))
201+
return
202+
}
203+
g.Expect(err).ToNot(HaveOccurred(), fmt.Sprintf("failed to get webhook service certificate secret %s/%s: %v", webhookOperatorInstallNamespace, certificateSecretName, err))
204+
g.Expect(secret.ResourceVersion).ToNot(Equal(oldSecretResourceVersion), "expected secret ResourceVersion to be different from the old one")
205+
g.Expect(secret.Data).ToNot(BeEmpty(), "expected webhook service certificate secret data to not be empty after recreation")
206+
}).WithTimeout(5*time.Minute).WithPolling(10*time.Second).Should(Succeed(), "webhook service certificate secret did not get recreated with a new ResourceVersion and populated within timeout")
207+
208+
By("checking webhook is responsive through cert rotation")
209+
Eventually(func(g Gomega) {
210+
resourceName := fmt.Sprintf("cert-rotation-test-%s", rand.String(5))
211+
resource := newWebhookTest(resourceName, webhookOperatorInstallNamespace, true)
212+
213+
_, err := dynamicClient.Resource(webhookTestV1).Namespace(webhookOperatorInstallNamespace).Create(ctx, resource, metav1.CreateOptions{})
214+
g.Expect(err).ToNot(HaveOccurred(), fmt.Sprintf("failed to create test resource %s: %v", resourceName, err))
215+
216+
err = dynamicClient.Resource(webhookTestV1).Namespace(webhookOperatorInstallNamespace).Delete(ctx, resource.GetName(), metav1.DeleteOptions{})
217+
g.Expect(client.IgnoreNotFound(err)).ToNot(HaveOccurred(), fmt.Sprintf("failed to delete test resource %s: %v", resourceName, err))
218+
}).WithTimeout(5 * time.Minute).WithPolling(10 * time.Second).Should(Succeed())
219+
220+
DeferCleanup(func() {
221+
// Specific check for this test
222+
if CurrentSpecReport().Failed() {
223+
By("dumping certificate details for debugging")
224+
secret := &corev1.Secret{}
225+
if err := k8sClient.Get(ctx, client.ObjectKey{
226+
Name: webhookServiceCert,
227+
Namespace: webhookOperatorInstallNamespace,
228+
}, secret); err == nil {
229+
if crt, ok := secret.Data["tls.crt"]; ok && len(crt) > 0 {
230+
printTLSCertInfo(crt)
231+
} else {
232+
_, _ = GinkgoWriter.Write([]byte("[diag] tls.crt key not found or empty in secret\n"))
233+
}
234+
} else {
235+
fmt.Fprintf(GinkgoWriter, "[diag] failed to get secret for cert dump: %v\n", err)
236+
}
237+
}
238+
})
239+
})
240+
170241
It("should be tolerant to tls secret deletion", func(ctx SpecContext) {
171242
certificateSecretName := webhookServiceCert
172243
By("ensuring secret exists before deletion attempt")

0 commit comments

Comments
 (0)