diff --git a/boilerplate/_data/last-boilerplate-commit b/boilerplate/_data/last-boilerplate-commit index 01f9820c..65923da3 100644 --- a/boilerplate/_data/last-boilerplate-commit +++ b/boilerplate/_data/last-boilerplate-commit @@ -1 +1 @@ -03d10f359a4a9d3b38c4f5e1494f771ef603fc3f +8ca4e1dfcab3443c52acf52b3912b6cf1c5f79a4 diff --git a/test/e2e/certman_operator_tests.go b/test/e2e/certman_operator_tests.go index 11a53108..28840d2b 100644 --- a/test/e2e/certman_operator_tests.go +++ b/test/e2e/certman_operator_tests.go @@ -529,7 +529,7 @@ var _ = ginkgo.Describe("Certman Operator", ginkgo.Ordered, ginkgo.ContinueOnFai gomega.Expect(certRequestsCount).To(gomega.Equal(1), "Should have exactly 1 certificate request") - ginkgo.GinkgoLogr.Info("✅ Metrics verification successful", + ginkgo.GinkgoLogr.Info(" Metrics verification successful", "certificateRequestsCount", certRequestsCount) }) @@ -812,6 +812,100 @@ var _ = ginkgo.Describe("Certman Operator", ginkgo.Ordered, ginkgo.ContinueOnFai }, pollingDuration, 30*time.Second).Should(gomega.BeTrue(), "ClusterDeployment should be automatically added as owner of CertificateRequest by operator") }) + ginkgo.It("Delete a CertificateRequest and ensures it is recreated", func(ctx context.Context) { + + crGVR := schema.GroupVersionResource{ + Group: "certman.managed.openshift.io", + Version: "v1alpha1", + Resource: "certificaterequests", + } + + log.Log.Info("STEP 1: Fetching CertificateRequest owned by our ClusterDeployment", "clusterDeployment", clusterDeploymentName, "namespace", certConfig.TestNamespace) + originalCR, err := utils.FindCertificateRequestForClusterDeployment(ctx, dynamicClient, crGVR, certConfig.TestNamespace, clusterDeploymentName) + if err != nil { + log.Log.Info("No CertificateRequest found for our ClusterDeployment, skipping test", "error", err) + ginkgo.Skip("SKIPPED: No CertificateRequest found for ClusterDeployment. This test runs after \"should create ClusterDeployment and CertificateRequest\" and requires that CR to exist.") + } + + originalCRName := originalCR.GetName() + originalCRUID := originalCR.GetUID() + crList, err := dynamicClient.Resource(crGVR).Namespace(certConfig.TestNamespace).List(ctx, metav1.ListOptions{}) + gomega.Expect(err).ToNot(gomega.HaveOccurred(), "Failed to list CertificateRequests for count") + initialIssuedCertCount := len(crList.Items) + + // Step 2: Delete the CertificateRequest owned by our ClusterDeployment + log.Log.Info("STEP 2: Deleting the CertificateRequest owned by our ClusterDeployment", "name", originalCRName) + err = dynamicClient.Resource(crGVR).Namespace(certConfig.TestNamespace).Delete(ctx, originalCRName, metav1.DeleteOptions{}) + gomega.Expect(err).ToNot(gomega.HaveOccurred(), "Failed to delete CertificateRequest") + + // Step 3: Handle deletion blocked by finalizer + gomega.Eventually(func() bool { + cr, err := dynamicClient.Resource(crGVR).Namespace(certConfig.TestNamespace).Get(ctx, originalCRName, metav1.GetOptions{}) + if err != nil { + log.Log.Info("CR appears to be deleted already", "name", originalCRName) + return true + } + if cr.GetDeletionTimestamp() == nil { + log.Log.Info("CR not marked for deletion yet", "name", cr.GetName()) + return false + } + + finalizers, found, err := unstructured.NestedStringSlice(cr.Object, "metadata", "finalizers") + if err != nil { + log.Log.Error(err, "Error retrieving finalizers") + return false + } + if !found || len(finalizers) == 0 { + log.Log.Info("No finalizers present", "name", cr.GetName()) + return false + } + + crCopy := cr.DeepCopy() + _ = unstructured.SetNestedStringSlice(crCopy.Object, []string{}, "metadata", "finalizers") + + _, err = dynamicClient.Resource(crGVR).Namespace(certConfig.TestNamespace).Update(ctx, crCopy, metav1.UpdateOptions{}) + if err != nil { + log.Log.Error(err, "Failed to remove finalizer") + return false + } + return true + }, 1*time.Minute, 5*time.Second).Should(gomega.BeTrue(), "Finalizer should be removed") + + // Step 4: Wait for new CertificateRequest with new UID (operator recreates it for our ClusterDeployment) + var newCRName string + gomega.Eventually(func() bool { + newList, err := dynamicClient.Resource(crGVR).Namespace(certConfig.TestNamespace).List(ctx, metav1.ListOptions{}) + if err != nil { + log.Log.Error(err, "Failed to list new CertificateRequests") + return false + } + if len(newList.Items) == 0 { + log.Log.Info("Still waiting for new CertificateRequest (none found)") + return false + } + + newCount := len(newList.Items) + logger.Info("CertificateRequest count after reconciliation", "count", newCount) + if newCount != initialIssuedCertCount { + logger.Info("CertificateRequest count mismatch", "expected", initialIssuedCertCount, "got", newCount) + return false + } + + for _, cr := range newList.Items { + log.Log.Info("Found CR candidate", "name", cr.GetName(), "uid", cr.GetUID()) + if cr.GetUID() != originalCRUID { + newCRName = cr.GetName() + log.Log.Info("New CertificateRequest detected", "name", newCRName, "uid", cr.GetUID()) + return true + } + } + return false + }, 2*time.Minute, 10*time.Second).Should(gomega.BeTrue(), "New CertificateRequest should appear") + + secretNameFromCR, _ := utils.GetCertificateSecretNameFromCR(originalCR) + log.Log.Info("Test completed: CertificateRequest recreated by operator", "newCR", newCRName, "certificateSecret", secretNameFromCR) + }) + ginkgo.AfterAll(func(ctx context.Context) { logger.Info("Cleanup: Running AfterAll cleanup")