diff --git a/test/e2e/addon_conversion_webhook_test.go b/test/e2e/addon_conversion_webhook_test.go index a31fca26c..4b334eac9 100644 --- a/test/e2e/addon_conversion_webhook_test.go +++ b/test/e2e/addon_conversion_webhook_test.go @@ -464,9 +464,9 @@ var _ = ginkgo.Describe("Create v1alpha1 ClusterManagementAddOn", ginkgo.Label(" _, err := hub.CreateClusterManagementAddOnV1Alpha1(addonName, addon) gomega.Expect(err).NotTo(gomega.HaveOccurred()) - ginkgo.By("Update v1alpha1 ClusterManagementAddOn status using v1alpha1 client") + ginkgo.By("Update v1alpha1 ClusterManagementAddOn status and verify v1beta1 conversion") gomega.Eventually(func() error { - addon, err = hub.GetClusterManagementAddOnV1Alpha1(addonName) + addon, err := hub.GetClusterManagementAddOnV1Alpha1(addonName) if err != nil { return err } @@ -485,28 +485,28 @@ var _ = ginkgo.Describe("Create v1alpha1 ClusterManagementAddOn", ginkgo.Label(" } _, err = hub.AddonClient.AddonV1alpha1().ClusterManagementAddOns().UpdateStatus( context.Background(), addon, metav1.UpdateOptions{}) - return err - }).Should(gomega.Succeed()) + if err != nil { + return err + } - ginkgo.By("Get v1alpha1 ClusterManagementAddOn using v1beta1 client and verify conversion") - gomega.Eventually(func() error { v1beta1Addon, err := hub.GetClusterManagementAddOnV1Beta1(addonName) if err != nil { return err } - // Verify spec.supportedConfigs → spec.defaultConfigs conversion if len(v1beta1Addon.Spec.DefaultConfigs) != 1 { - return fmt.Errorf("expected 1 default config but got %v", len(v1beta1Addon.Spec.DefaultConfigs)) + return fmt.Errorf("expected 1 default config but got %d", len(v1beta1Addon.Spec.DefaultConfigs)) } if v1beta1Addon.Spec.DefaultConfigs[0].Name != "test-config" { - return fmt.Errorf("expected test-config but got %v", v1beta1Addon.Spec.DefaultConfigs[0].Name) + return fmt.Errorf("expected test-config but got %s", v1beta1Addon.Spec.DefaultConfigs[0].Name) } - // Verify status.DefaultConfigReferences conversion if len(v1beta1Addon.Status.DefaultConfigReferences) != 1 { - return fmt.Errorf("expected 1 default config but got %v", len(v1beta1Addon.Status.DefaultConfigReferences)) + return fmt.Errorf("expected 1 default config reference but got %d", len(v1beta1Addon.Status.DefaultConfigReferences)) + } + if v1beta1Addon.Status.DefaultConfigReferences[0].DesiredConfig == nil { + return fmt.Errorf("expected desired config in status") } if v1beta1Addon.Status.DefaultConfigReferences[0].DesiredConfig.Name != "test-config" { - return fmt.Errorf("expected status-config but got %v", v1beta1Addon.Status.DefaultConfigReferences[0].DesiredConfig.Name) + return fmt.Errorf("expected test-config in status but got %s", v1beta1Addon.Status.DefaultConfigReferences[0].DesiredConfig.Name) } return nil }).Should(gomega.Succeed()) diff --git a/test/framework/klusterlet.go b/test/framework/klusterlet.go index 0672d7192..6e5c6fb8d 100644 --- a/test/framework/klusterlet.go +++ b/test/framework/klusterlet.go @@ -288,12 +288,16 @@ func (spoke *Spoke) RemoveRegistrationFeature(klusterletName string, feature str return err } -// CleanKlusterletRelatedResources needs both hub side and spoke side operations +// CleanKlusterletRelatedResources needs both hub side and spoke side operations. func CleanKlusterletRelatedResources( hub *Hub, spoke *Spoke, klusterletName, managedClusterName string) { Expect(klusterletName).NotTo(Equal("")) + // Remove addons and manifest works first; leftover ManifestWorks (e.g. pre-delete hooks) block ManagedCluster deletion. + hub.DeleteAllManagedClusterAddOnsInCluster(managedClusterName) + hub.WaitUntilNoManifestWorks(managedClusterName) + // clean the managed clusters at first. err := hub.ClusterClient.ClusterV1().ManagedClusters().Delete(context.TODO(), managedClusterName, metav1.DeleteOptions{}) if apierrors.IsNotFound(err) { diff --git a/test/framework/managedclusteraddon.go b/test/framework/managedclusteraddon.go index c448d97aa..b7b67937e 100644 --- a/test/framework/managedclusteraddon.go +++ b/test/framework/managedclusteraddon.go @@ -5,11 +5,14 @@ import ( "fmt" "time" + . "github.com/onsi/gomega" coordv1 "k8s.io/api/coordination/v1" corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/util/retry" + "k8s.io/klog/v2" addonv1alpha1 "open-cluster-management.io/api/addon/v1alpha1" addonv1beta1 "open-cluster-management.io/api/addon/v1beta1" @@ -175,3 +178,31 @@ func (hub *Hub) UpdateManagedClusterAddOnV1Alpha1(addon *addonv1alpha1.ManagedCl return hub.AddonClient.AddonV1alpha1().ManagedClusterAddOns(addon.Namespace).Update( context.TODO(), addon, metav1.UpdateOptions{}) } + +// DeleteAllManagedClusterAddOnsInCluster deletes every ManagedClusterAddOn in the cluster namespace. +// A single API version is enough because both versions share the same storage. +func (hub *Hub) DeleteAllManagedClusterAddOnsInCluster(clusterName string) { + addons, err := hub.AddonClient.AddonV1beta1().ManagedClusterAddOns(clusterName).List(context.TODO(), metav1.ListOptions{}) + if err != nil && !apierrors.IsNotFound(err) { + klog.Errorf("failed to list managed cluster addons in %s: %v", clusterName, err) + } else { + for _, addon := range addons.Items { + err := hub.AddonClient.AddonV1beta1().ManagedClusterAddOns(clusterName).Delete( + context.TODO(), addon.Name, metav1.DeleteOptions{}) + if err != nil && !apierrors.IsNotFound(err) { + klog.Errorf("failed to delete managed cluster addon %s/%s: %v", clusterName, addon.Name, err) + } + } + } + + Eventually(func() error { + addons, err := hub.AddonClient.AddonV1beta1().ManagedClusterAddOns(clusterName).List(context.TODO(), metav1.ListOptions{}) + if err != nil && !apierrors.IsNotFound(err) { + return err + } + if len(addons.Items) > 0 { + return fmt.Errorf("managed cluster addons still exist in %s", clusterName) + } + return nil + }).Should(Succeed()) +} diff --git a/test/framework/manifestwork.go b/test/framework/manifestwork.go index 3fd1a6478..4c17b2bab 100644 --- a/test/framework/manifestwork.go +++ b/test/framework/manifestwork.go @@ -2,6 +2,7 @@ package framework import ( "context" + "fmt" . "github.com/onsi/gomega" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -24,3 +25,20 @@ func (hub *Hub) CleanManifestWorks(clusterName, workName string) error { return nil } + +// WaitUntilNoManifestWorks waits until all ManifestWorks are removed from the managed cluster namespace. +func (hub *Hub) WaitUntilNoManifestWorks(clusterName string) { + Eventually(func() error { + works, err := hub.WorkClient.WorkV1().ManifestWorks(clusterName).List(context.Background(), metav1.ListOptions{}) + if err != nil { + if apierrors.IsNotFound(err) { + return nil + } + return err + } + if len(works.Items) != 0 { + return fmt.Errorf("expected no manifest works, but got %d", len(works.Items)) + } + return nil + }).Should(Succeed()) +}