diff --git a/pkg/addon/controllers/addonprogressing/controller.go b/pkg/addon/controllers/addonprogressing/controller.go index 9d0119ff8..f5b83a065 100644 --- a/pkg/addon/controllers/addonprogressing/controller.go +++ b/pkg/addon/controllers/addonprogressing/controller.go @@ -38,7 +38,7 @@ const ( ProgressingFailed string = "Failed" ) -// addonProgressingController reconciles instances of managedclusteradd on the hub +// addonProgressingController reconciles instances of managedclusteraddon on the hub // based to update the status progressing condition and last applied config type addonProgressingController struct { addonClient addonv1alpha1client.Interface @@ -67,11 +67,18 @@ func NewAddonProgressingController( return factory.New().WithInformersQueueKeysFunc( queue.QueueKeyByMetaNamespaceName, addonInformers.Informer(), clusterManagementAddonInformers.Informer()). - // TODO: consider hosted manifestwork - WithInformersQueueKeysFunc( + WithFilteredEventsInformersQueueKeysFunc( func(obj runtime.Object) []string { accessor, _ := meta.Accessor(obj) - return []string{fmt.Sprintf("%s/%s", accessor.GetNamespace(), accessor.GetLabels()[addonapiv1alpha1.AddonLabelKey])} + namespace := accessor.GetNamespace() + if len(accessor.GetLabels()[addonapiv1alpha1.AddonNamespaceLabelKey]) > 0 { + namespace = accessor.GetLabels()[addonapiv1alpha1.AddonNamespaceLabelKey] + } + return []string{fmt.Sprintf("%s/%s", namespace, accessor.GetLabels()[addonapiv1alpha1.AddonLabelKey])} + }, + func(obj interface{}) bool { + accessor, _ := meta.Accessor(obj) + return len(accessor.GetLabels()) > 0 && len(accessor.GetLabels()[addonapiv1alpha1.AddonLabelKey]) > 0 }, workInformers.Informer()). WithSync(c.sync).ToController("addon-progressing-controller", recorder) @@ -117,22 +124,26 @@ func (c *addonProgressingController) sync(ctx context.Context, syncCtx factory.S func (c *addonProgressingController) updateAddonProgressingAndLastApplied( ctx context.Context, newaddon, oldaddon *addonapiv1alpha1.ManagedClusterAddOn) (bool, error) { patcher := patcher.NewPatcher[ - *addonapiv1alpha1.ManagedClusterAddOn, addonapiv1alpha1.ManagedClusterAddOnSpec, addonapiv1alpha1.ManagedClusterAddOnStatus]( + *addonapiv1alpha1.ManagedClusterAddOn, + addonapiv1alpha1.ManagedClusterAddOnSpec, + addonapiv1alpha1.ManagedClusterAddOnStatus]( c.addonClient.AddonV1alpha1().ManagedClusterAddOns(newaddon.Namespace)) // check config references if supported, config := isConfigurationSupported(newaddon); !supported { meta.SetStatusCondition(&newaddon.Status.Conditions, metav1.Condition{ - Type: addonapiv1alpha1.ManagedClusterAddOnConditionProgressing, - Status: metav1.ConditionFalse, - Reason: addonapiv1alpha1.ProgressingReasonConfigurationUnsupported, - Message: fmt.Sprintf("Configuration with gvr %s/%s is not supported for this addon", config.Group, config.Resource), + Type: addonapiv1alpha1.ManagedClusterAddOnConditionProgressing, + Status: metav1.ConditionFalse, + Reason: addonapiv1alpha1.ProgressingReasonConfigurationUnsupported, + Message: fmt.Sprintf("Configuration with gvr %s/%s is not supported for this addon", + config.Group, config.Resource), }) return patcher.PatchStatus(ctx, newaddon, newaddon.Status, oldaddon.Status) } // wait until addon has ManifestApplied condition - if cond := meta.FindStatusCondition(newaddon.Status.Conditions, addonapiv1alpha1.ManagedClusterAddOnManifestApplied); cond == nil { + if cond := meta.FindStatusCondition( + newaddon.Status.Conditions, addonapiv1alpha1.ManagedClusterAddOnManifestApplied); cond == nil { meta.SetStatusCondition(&newaddon.Status.Conditions, metav1.Condition{ Type: addonapiv1alpha1.ManagedClusterAddOnConditionProgressing, Status: metav1.ConditionFalse, @@ -142,6 +153,25 @@ func (c *addonProgressingController) updateAddonProgressingAndLastApplied( return patcher.PatchStatus(ctx, newaddon, newaddon.Status, oldaddon.Status) } + var hostingClusterName string = "" + if newaddon.Annotations != nil && len(newaddon.Annotations[addonapiv1alpha1.HostingClusterNameAnnotationKey]) > 0 { + hostingClusterName = newaddon.Annotations[addonapiv1alpha1.HostingClusterNameAnnotationKey] + } + + if len(hostingClusterName) > 0 { + // wait until addon has HostingManifestApplied condition + if cond := meta.FindStatusCondition( + newaddon.Status.Conditions, addonapiv1alpha1.ManagedClusterAddOnHostingManifestApplied); cond == nil { + meta.SetStatusCondition(&newaddon.Status.Conditions, metav1.Condition{ + Type: addonapiv1alpha1.ManagedClusterAddOnConditionProgressing, + Status: metav1.ConditionFalse, + Reason: "WaitingForHostingManifestApplied", + Message: "Waiting for ManagedClusterAddOn HostingManifestApplied condition", + }) + return patcher.PatchStatus(ctx, newaddon, newaddon.Status, oldaddon.Status) + } + } + // set upgrade flag isUpgrade := false for _, configReference := range newaddon.Status.ConfigReferences { @@ -152,7 +182,6 @@ func (c *addonProgressingController) updateAddonProgressingAndLastApplied( } // get addon works - // TODO: consider hosted manifestwork requirement, _ := labels.NewRequirement(addonapiv1alpha1.AddonLabelKey, selection.Equals, []string{newaddon.Name}) selector := labels.NewSelector().Add(*requirement) addonWorks, err := c.workLister.ManifestWorks(newaddon.Namespace).List(selector) @@ -161,6 +190,16 @@ func (c *addonProgressingController) updateAddonProgressingAndLastApplied( return patcher.PatchStatus(ctx, newaddon, newaddon.Status, oldaddon.Status) } + if len(hostingClusterName) > 0 { + // get hosted addon works + hostedAddonWorks, err := c.workLister.ManifestWorks(hostingClusterName).List(selector) + if err != nil { + setAddOnProgressingAndLastApplied(isUpgrade, ProgressingFailed, err.Error(), newaddon) + return patcher.PatchStatus(ctx, newaddon, newaddon.Status, oldaddon.Status) + } + addonWorks = append(addonWorks, hostedAddonWorks...) + } + if len(addonWorks) == 0 { setAddOnProgressingAndLastApplied(isUpgrade, ProgressingDoing, "no addon works", newaddon) return patcher.PatchStatus(ctx, newaddon, newaddon.Status, oldaddon.Status) diff --git a/pkg/addon/controllers/addonprogressing/controller_test.go b/pkg/addon/controllers/addonprogressing/controller_test.go index d19cd23f4..bb8ee4793 100644 --- a/pkg/addon/controllers/addonprogressing/controller_test.go +++ b/pkg/addon/controllers/addonprogressing/controller_test.go @@ -52,13 +52,29 @@ func TestReconcile(t *testing.T) { }, { name: "no work applied condition", - syncKey: "test/test", + syncKey: "cluster1/test", managedClusteraddon: []runtime.Object{ addontesting.NewAddon("test", "cluster1"), }, clusterManagementAddon: []runtime.Object{addontesting.NewClusterManagementAddon("test", "testcrd", "testcr").Build()}, work: []runtime.Object{}, - validateAddonActions: testingcommon.AssertNoActions, + validateAddonActions: func(t *testing.T, actions []clienttesting.Action) { + testingcommon.AssertActions(t, actions, "patch") + actual := actions[0].(clienttesting.PatchActionImpl).Patch + + addOn := &addonapiv1alpha1.ManagedClusterAddOn{} + err := json.Unmarshal(actual, addOn) + if err != nil { + t.Fatal(err) + } + configCond := meta.FindStatusCondition( + addOn.Status.Conditions, addonapiv1alpha1.ManagedClusterAddOnConditionProgressing) + if !(configCond != nil && + configCond.Reason == "WaitingForManifestApplied" && + configCond.Status == metav1.ConditionFalse) { + t.Errorf("Condition Progressing is incorrect") + } + }, }, { name: "update managedclusteraddon to installing when no work", @@ -612,3 +628,731 @@ func TestReconcile(t *testing.T) { }) } } + +func TestReconcileHostedAddons(t *testing.T) { + cases := []struct { + name string + syncKey string + managedClusteraddon []runtime.Object + clusterManagementAddon []runtime.Object + work []runtime.Object + validateAddonActions func(t *testing.T, actions []clienttesting.Action) + }{ + { + name: "no work applied condition", + syncKey: "cluster1/test", + managedClusteraddon: []runtime.Object{ + addontesting.NewHostedModeAddon("test", "cluster1", "hosting-cluster"), + }, + clusterManagementAddon: []runtime.Object{ + addontesting.NewClusterManagementAddon("test", "testcrd", "testcr").Build(), + }, + work: []runtime.Object{}, + validateAddonActions: func(t *testing.T, actions []clienttesting.Action) { + testingcommon.AssertActions(t, actions, "patch") + actual := actions[0].(clienttesting.PatchActionImpl).Patch + + addOn := &addonapiv1alpha1.ManagedClusterAddOn{} + err := json.Unmarshal(actual, addOn) + if err != nil { + t.Fatal(err) + } + configCond := meta.FindStatusCondition( + addOn.Status.Conditions, addonapiv1alpha1.ManagedClusterAddOnConditionProgressing) + if !(configCond != nil && + configCond.Reason == "WaitingForManifestApplied" && + configCond.Status == metav1.ConditionFalse) { + t.Errorf("Condition Progressing is incorrect") + } + }, + }, + { + name: "no hosting work applied condition", + syncKey: "cluster1/test", + managedClusteraddon: []runtime.Object{ + addontesting.NewHostedModeAddon("test", "cluster1", "hosting-cluster", + metav1.Condition{ + Type: addonapiv1alpha1.ManagedClusterAddOnManifestApplied, + Status: metav1.ConditionTrue, + Reason: addonapiv1alpha1.AddonManifestAppliedReasonManifestsApplied, + Message: "manifests of addon are applied successfully", + }, + ), + }, + clusterManagementAddon: []runtime.Object{ + addontesting.NewClusterManagementAddon("test", "testcrd", "testcr").Build(), + }, + work: []runtime.Object{}, + validateAddonActions: func(t *testing.T, actions []clienttesting.Action) { + testingcommon.AssertActions(t, actions, "patch") + actual := actions[0].(clienttesting.PatchActionImpl).Patch + + addOn := &addonapiv1alpha1.ManagedClusterAddOn{} + err := json.Unmarshal(actual, addOn) + if err != nil { + t.Fatal(err) + } + configCond := meta.FindStatusCondition( + addOn.Status.Conditions, addonapiv1alpha1.ManagedClusterAddOnConditionProgressing) + if !(configCond != nil && + configCond.Reason == "WaitingForHostingManifestApplied" && + configCond.Status == metav1.ConditionFalse) { + t.Errorf("Condition Progressing is incorrect") + } + }, + }, + { + name: "update managedclusteraddon to installing when no work", + syncKey: "cluster1/test", + managedClusteraddon: []runtime.Object{ + addontesting.NewHostedModeAddon("test", "cluster1", "hosting-cluster", + metav1.Condition{ + Type: addonapiv1alpha1.ManagedClusterAddOnManifestApplied, + Status: metav1.ConditionTrue, + Reason: addonapiv1alpha1.AddonManifestAppliedReasonManifestsApplied, + Message: "manifests of addon are applied successfully", + }, + metav1.Condition{ + Type: addonapiv1alpha1.ManagedClusterAddOnHostingManifestApplied, + Status: metav1.ConditionTrue, + Reason: addonapiv1alpha1.AddonManifestAppliedReasonManifestsApplied, + Message: "hosting manifests of addon are applied successfully", + }, + ), + }, + clusterManagementAddon: []runtime.Object{ + addontesting.NewClusterManagementAddon("test", "testcrd", "testcr").Build(), + }, + work: []runtime.Object{}, + validateAddonActions: func(t *testing.T, actions []clienttesting.Action) { + testingcommon.AssertActions(t, actions, "patch") + actual := actions[0].(clienttesting.PatchActionImpl).Patch + + addOn := &addonapiv1alpha1.ManagedClusterAddOn{} + err := json.Unmarshal(actual, addOn) + if err != nil { + t.Fatal(err) + } + configCond := meta.FindStatusCondition( + addOn.Status.Conditions, addonapiv1alpha1.ManagedClusterAddOnConditionProgressing) + if !(configCond != nil && + configCond.Reason == addonapiv1alpha1.ProgressingReasonInstalling && + configCond.Status == metav1.ConditionTrue) { + t.Errorf("Condition Progressing is incorrect") + } + }, + }, + { + name: "update managedclusteraddon to installing when work config spec not match", + syncKey: "cluster1/test", + managedClusteraddon: []runtime.Object{func() *addonapiv1alpha1.ManagedClusterAddOn { + addon := addontesting.NewHostedModeAddon("test", "cluster1", "hosting-cluster", + metav1.Condition{ + Type: addonapiv1alpha1.ManagedClusterAddOnManifestApplied, + Status: metav1.ConditionTrue, + Reason: addonapiv1alpha1.AddonManifestAppliedReasonManifestsApplied, + Message: "manifests of addon are applied successfully", + }, + metav1.Condition{ + Type: addonapiv1alpha1.ManagedClusterAddOnHostingManifestApplied, + Status: metav1.ConditionTrue, + Reason: addonapiv1alpha1.AddonManifestAppliedReasonManifestsApplied, + Message: "hosting manifests of addon are applied successfully", + }, + ) + addon.Status.ConfigReferences = []addonapiv1alpha1.ConfigReference{ + { + ConfigGroupResource: v1alpha1.ConfigGroupResource{Group: "core", Resource: "foo"}, + DesiredConfig: &v1alpha1.ConfigSpecHash{ + ConfigReferent: v1alpha1.ConfigReferent{Name: "test", Namespace: "open-cluster-management"}, + SpecHash: "hashnew", + }, + LastAppliedConfig: &v1alpha1.ConfigSpecHash{ + ConfigReferent: v1alpha1.ConfigReferent{Name: "test", Namespace: "open-cluster-management"}, + SpecHash: "", + }, + }, + } + return addon + }()}, + clusterManagementAddon: []runtime.Object{ + addontesting.NewClusterManagementAddon("test", "testcrd", "testcr").Build(), + }, + work: []runtime.Object{ + func() *workapiv1.ManifestWork { + work := addontesting.NewManifestWork( + "addon-test-deploy", + "hosting-cluster", + testingcommon.NewUnstructured("v1", "ConfigMap", "default", "test1"), + testingcommon.NewUnstructured("v1", "Deployment", "default", "test1"), + ) + work.SetLabels(map[string]string{ + addonapiv1alpha1.AddonLabelKey: "test", + addonapiv1alpha1.AddonNamespaceLabelKey: "cluster1", + }) + work.SetAnnotations(map[string]string{ + workapiv1.ManifestConfigSpecHashAnnotationKey: "{\"foo.core/open-cluster-management/test\":\"hash\"}", + }) + work.Status.Conditions = []metav1.Condition{ + { + Type: workapiv1.WorkApplied, + Status: metav1.ConditionTrue, + }, + { + Type: workapiv1.WorkAvailable, + Status: metav1.ConditionTrue, + }, + } + return work + }(), + }, + validateAddonActions: func(t *testing.T, actions []clienttesting.Action) { + testingcommon.AssertActions(t, actions, "patch") + actual := actions[0].(clienttesting.PatchActionImpl).Patch + + addOn := &addonapiv1alpha1.ManagedClusterAddOn{} + err := json.Unmarshal(actual, addOn) + if err != nil { + t.Fatal(err) + } + configCond := meta.FindStatusCondition( + addOn.Status.Conditions, addonapiv1alpha1.ManagedClusterAddOnConditionProgressing) + if !(configCond != nil && + configCond.Reason == addonapiv1alpha1.ProgressingReasonInstalling && + configCond.Status == metav1.ConditionTrue) { + t.Errorf("Condition Progressing is incorrect") + } + if len(addOn.Status.ConfigReferences) != 0 { + t.Errorf("ConfigReferences object is not correct: %v", addOn.Status.ConfigReferences) + } + }, + }, + { + name: "update managedclusteraddon to installing when work is not ready", + syncKey: "cluster1/test", + managedClusteraddon: []runtime.Object{func() *addonapiv1alpha1.ManagedClusterAddOn { + addon := addontesting.NewHostedModeAddon("test", "cluster1", "hosting-cluster", + metav1.Condition{ + Type: addonapiv1alpha1.ManagedClusterAddOnManifestApplied, + Status: metav1.ConditionTrue, + Reason: addonapiv1alpha1.AddonManifestAppliedReasonManifestsApplied, + Message: "manifests of addon are applied successfully", + }, + metav1.Condition{ + Type: addonapiv1alpha1.ManagedClusterAddOnHostingManifestApplied, + Status: metav1.ConditionTrue, + Reason: addonapiv1alpha1.AddonManifestAppliedReasonManifestsApplied, + Message: "hosting manifests of addon are applied successfully", + }, + ) + addon.Status.ConfigReferences = []addonapiv1alpha1.ConfigReference{ + { + ConfigGroupResource: v1alpha1.ConfigGroupResource{Group: "core", Resource: "foo"}, + DesiredConfig: &v1alpha1.ConfigSpecHash{ + ConfigReferent: v1alpha1.ConfigReferent{Name: "test", Namespace: "open-cluster-management"}, + SpecHash: "hashnew", + }, + LastAppliedConfig: &v1alpha1.ConfigSpecHash{ + ConfigReferent: v1alpha1.ConfigReferent{Name: "test", Namespace: "open-cluster-management"}, + SpecHash: "", + }, + }, + } + return addon + }()}, + clusterManagementAddon: []runtime.Object{ + addontesting.NewClusterManagementAddon("test", "testcrd", "testcr").Build(), + }, + work: []runtime.Object{func() *workapiv1.ManifestWork { + work := addontesting.NewManifestWork( + "addon-test-deploy", + "hosting-cluster", + testingcommon.NewUnstructured("v1", "ConfigMap", "default", "test1"), + testingcommon.NewUnstructured("v1", "Deployment", "default", "test1"), + ) + work.SetLabels(map[string]string{ + addonapiv1alpha1.AddonLabelKey: "test", + addonapiv1alpha1.AddonNamespaceLabelKey: "cluster1", + }) + work.SetAnnotations(map[string]string{ + workapiv1.ManifestConfigSpecHashAnnotationKey: "{\"foo.core/open-cluster-management/test\":\"hashnew\"}", + }) + work.Status.Conditions = []metav1.Condition{ + { + Type: workapiv1.WorkApplied, + Status: metav1.ConditionFalse, + }, + { + Type: workapiv1.WorkAvailable, + Status: metav1.ConditionTrue, + }, + } + return work + }()}, + validateAddonActions: func(t *testing.T, actions []clienttesting.Action) { + testingcommon.AssertActions(t, actions, "patch") + actual := actions[0].(clienttesting.PatchActionImpl).Patch + + addOn := &addonapiv1alpha1.ManagedClusterAddOn{} + err := json.Unmarshal(actual, addOn) + if err != nil { + t.Fatal(err) + } + configCond := meta.FindStatusCondition(addOn.Status.Conditions, addonapiv1alpha1.ManagedClusterAddOnConditionProgressing) + if !(configCond != nil && configCond.Reason == addonapiv1alpha1.ProgressingReasonInstalling && configCond.Status == metav1.ConditionTrue) { + t.Errorf("Condition Progressing is incorrect") + } + if len(addOn.Status.ConfigReferences) != 0 { + t.Errorf("ConfigReferences object is not correct: %v", addOn.Status.ConfigReferences) + } + }, + }, + { + name: "update managedclusteraddon to uprading when work config spec not match", + syncKey: "cluster1/test", + managedClusteraddon: []runtime.Object{func() *addonapiv1alpha1.ManagedClusterAddOn { + addon := addontesting.NewHostedModeAddon("test", "cluster1", "hosting-cluster", + metav1.Condition{ + Type: addonapiv1alpha1.ManagedClusterAddOnManifestApplied, + Status: metav1.ConditionTrue, + Reason: addonapiv1alpha1.AddonManifestAppliedReasonManifestsApplied, + Message: "manifests of addon are applied successfully", + }, + metav1.Condition{ + Type: addonapiv1alpha1.ManagedClusterAddOnHostingManifestApplied, + Status: metav1.ConditionTrue, + Reason: addonapiv1alpha1.AddonManifestAppliedReasonManifestsApplied, + Message: "hosting manifests of addon are applied successfully", + }, + ) + addon.Status.ConfigReferences = []addonapiv1alpha1.ConfigReference{ + { + ConfigGroupResource: v1alpha1.ConfigGroupResource{Group: "core", Resource: "foo"}, + DesiredConfig: &v1alpha1.ConfigSpecHash{ + ConfigReferent: v1alpha1.ConfigReferent{Name: "test", Namespace: "open-cluster-management"}, + SpecHash: "hashnew", + }, + LastAppliedConfig: &v1alpha1.ConfigSpecHash{ + ConfigReferent: v1alpha1.ConfigReferent{Name: "test", Namespace: "open-cluster-management"}, + SpecHash: "hash", + }, + }, + } + return addon + }()}, + clusterManagementAddon: []runtime.Object{ + addontesting.NewClusterManagementAddon("test", "testcrd", "testcr").Build(), + }, + work: []runtime.Object{func() *workapiv1.ManifestWork { + work := addontesting.NewManifestWork( + "addon-test-deploy", + "hosting-cluster", + testingcommon.NewUnstructured("v1", "ConfigMap", "default", "test1"), + testingcommon.NewUnstructured("v1", "Deployment", "default", "test1"), + ) + work.SetLabels(map[string]string{ + addonapiv1alpha1.AddonLabelKey: "test", + addonapiv1alpha1.AddonNamespaceLabelKey: "cluster1", + }) + work.SetAnnotations(map[string]string{ + workapiv1.ManifestConfigSpecHashAnnotationKey: "{\"foo.core/open-cluster-management/test\":\"hash\"}", + }) + work.Status.Conditions = []metav1.Condition{ + { + Type: workapiv1.WorkApplied, + Status: metav1.ConditionTrue, + }, + { + Type: workapiv1.WorkAvailable, + Status: metav1.ConditionTrue, + }, + } + return work + }()}, + validateAddonActions: func(t *testing.T, actions []clienttesting.Action) { + testingcommon.AssertActions(t, actions, "patch") + actual := actions[0].(clienttesting.PatchActionImpl).Patch + + addOn := &addonapiv1alpha1.ManagedClusterAddOn{} + err := json.Unmarshal(actual, addOn) + if err != nil { + t.Fatal(err) + } + configCond := meta.FindStatusCondition( + addOn.Status.Conditions, addonapiv1alpha1.ManagedClusterAddOnConditionProgressing) + if !(configCond != nil && + configCond.Reason == addonapiv1alpha1.ProgressingReasonUpgrading && + configCond.Status == metav1.ConditionTrue) { + t.Errorf("Condition Progressing is incorrect") + } + if len(addOn.Status.ConfigReferences) != 0 { + t.Errorf("ConfigReferences object is not correct: %v", addOn.Status.ConfigReferences) + } + }, + }, + { + name: "update managedclusteraddon to uprading when work is not ready", + syncKey: "cluster1/test", + managedClusteraddon: []runtime.Object{func() *addonapiv1alpha1.ManagedClusterAddOn { + addon := addontesting.NewHostedModeAddon("test", "cluster1", "hosting-cluster", + metav1.Condition{ + Type: addonapiv1alpha1.ManagedClusterAddOnManifestApplied, + Status: metav1.ConditionTrue, + Reason: addonapiv1alpha1.AddonManifestAppliedReasonManifestsApplied, + Message: "manifests of addon are applied successfully", + }, + metav1.Condition{ + Type: addonapiv1alpha1.ManagedClusterAddOnHostingManifestApplied, + Status: metav1.ConditionTrue, + Reason: addonapiv1alpha1.AddonManifestAppliedReasonManifestsApplied, + Message: "hosting manifests of addon are applied successfully", + }, + ) + addon.Status.ConfigReferences = []addonapiv1alpha1.ConfigReference{ + { + ConfigGroupResource: v1alpha1.ConfigGroupResource{Group: "core", Resource: "foo"}, + DesiredConfig: &v1alpha1.ConfigSpecHash{ + ConfigReferent: v1alpha1.ConfigReferent{Name: "test", Namespace: "open-cluster-management"}, + SpecHash: "hashnew", + }, + LastAppliedConfig: &v1alpha1.ConfigSpecHash{ + ConfigReferent: v1alpha1.ConfigReferent{Name: "test", Namespace: "open-cluster-management"}, + SpecHash: "hash", + }, + }, + } + return addon + }()}, + clusterManagementAddon: []runtime.Object{ + addontesting.NewClusterManagementAddon("test", "testcrd", "testcr").Build(), + }, + work: []runtime.Object{func() *workapiv1.ManifestWork { + work := addontesting.NewManifestWork( + "addon-test-deploy", + "hosting-cluster", + testingcommon.NewUnstructured("v1", "ConfigMap", "default", "test1"), + testingcommon.NewUnstructured("v1", "Deployment", "default", "test1"), + ) + work.SetLabels(map[string]string{ + addonapiv1alpha1.AddonLabelKey: "test", + addonapiv1alpha1.AddonNamespaceLabelKey: "cluster1", + }) + work.SetAnnotations(map[string]string{ + workapiv1.ManifestConfigSpecHashAnnotationKey: "{\"foo.core/open-cluster-management/test\":\"hashnew\"}", + }) + work.Status.Conditions = []metav1.Condition{ + { + Type: workapiv1.WorkApplied, + Status: metav1.ConditionTrue, + }, + { + Type: workapiv1.WorkAvailable, + Status: metav1.ConditionFalse, + }, + } + return work + }()}, + validateAddonActions: func(t *testing.T, actions []clienttesting.Action) { + testingcommon.AssertActions(t, actions, "patch") + actual := actions[0].(clienttesting.PatchActionImpl).Patch + + addOn := &addonapiv1alpha1.ManagedClusterAddOn{} + err := json.Unmarshal(actual, addOn) + if err != nil { + t.Fatal(err) + } + configCond := meta.FindStatusCondition( + addOn.Status.Conditions, addonapiv1alpha1.ManagedClusterAddOnConditionProgressing) + if !(configCond != nil && + configCond.Reason == addonapiv1alpha1.ProgressingReasonUpgrading && + configCond.Status == metav1.ConditionTrue) { + t.Errorf("Condition Progressing is incorrect") + } + if len(addOn.Status.ConfigReferences) != 0 { + t.Errorf("ConfigReferences object is not correct: %v", addOn.Status.ConfigReferences) + } + }, + }, + { + name: "update managedclusteraddon to install succeed", + syncKey: "cluster1/test", + managedClusteraddon: []runtime.Object{func() *addonapiv1alpha1.ManagedClusterAddOn { + addon := addontesting.NewHostedModeAddon("test", "cluster1", "hosting-cluster", + metav1.Condition{ + Type: addonapiv1alpha1.ManagedClusterAddOnManifestApplied, + Status: metav1.ConditionTrue, + Reason: addonapiv1alpha1.AddonManifestAppliedReasonManifestsApplied, + Message: "manifests of addon are applied successfully", + }, + metav1.Condition{ + Type: addonapiv1alpha1.ManagedClusterAddOnHostingManifestApplied, + Status: metav1.ConditionTrue, + Reason: addonapiv1alpha1.AddonManifestAppliedReasonManifestsApplied, + Message: "hosting manifests of addon are applied successfully", + }, + ) + addon.Status.ConfigReferences = []addonapiv1alpha1.ConfigReference{ + { + ConfigGroupResource: v1alpha1.ConfigGroupResource{Group: "core", Resource: "foo"}, + DesiredConfig: &v1alpha1.ConfigSpecHash{ + ConfigReferent: v1alpha1.ConfigReferent{Name: "test", Namespace: "open-cluster-management"}, + SpecHash: "hashnew", + }, + LastAppliedConfig: &v1alpha1.ConfigSpecHash{ + ConfigReferent: v1alpha1.ConfigReferent{Name: "test", Namespace: "open-cluster-management"}, + SpecHash: "", + }, + }, + } + return addon + }()}, + clusterManagementAddon: []runtime.Object{ + addontesting.NewClusterManagementAddon("test", "testcrd", "testcr").Build(), + }, + work: []runtime.Object{func() *workapiv1.ManifestWork { + work := addontesting.NewManifestWork( + "addon-test-deploy", + "hosting-cluster", + testingcommon.NewUnstructured("v1", "ConfigMap", "default", "test1"), + testingcommon.NewUnstructured("v1", "Deployment", "default", "test1"), + ) + work.SetLabels(map[string]string{ + addonapiv1alpha1.AddonLabelKey: "test", + addonapiv1alpha1.AddonNamespaceLabelKey: "cluster1", + }) + work.SetAnnotations(map[string]string{ + workapiv1.ManifestConfigSpecHashAnnotationKey: "{\"foo.core/open-cluster-management/test\":\"hashnew\"}", + }) + work.Status.Conditions = []metav1.Condition{ + { + Type: workapiv1.WorkApplied, + Status: metav1.ConditionTrue, + }, + { + Type: workapiv1.WorkAvailable, + Status: metav1.ConditionTrue, + }, + } + return work + }()}, + validateAddonActions: func(t *testing.T, actions []clienttesting.Action) { + testingcommon.AssertActions(t, actions, "patch") + actual := actions[0].(clienttesting.PatchActionImpl).Patch + + addOn := &addonapiv1alpha1.ManagedClusterAddOn{} + err := json.Unmarshal(actual, addOn) + if err != nil { + t.Fatal(err) + } + configCond := meta.FindStatusCondition( + addOn.Status.Conditions, addonapiv1alpha1.ManagedClusterAddOnConditionProgressing) + if !(configCond != nil && + configCond.Reason == addonapiv1alpha1.ProgressingReasonInstallSucceed && + configCond.Status == metav1.ConditionFalse) { + t.Errorf("Condition Progressing is incorrect") + } + if len(addOn.Status.ConfigReferences) != 1 { + t.Errorf("ConfigReferences object is not correct: %v", addOn.Status.ConfigReferences) + } + if addOn.Status.ConfigReferences[0].LastAppliedConfig.SpecHash != + addOn.Status.ConfigReferences[0].DesiredConfig.SpecHash { + t.Errorf("LastAppliedConfig object is not correct: %v", + addOn.Status.ConfigReferences[0].LastAppliedConfig.SpecHash) + } + }, + }, + { + name: "update managedclusteraddon to upgrade succeed", + syncKey: "cluster1/test", + managedClusteraddon: []runtime.Object{func() *addonapiv1alpha1.ManagedClusterAddOn { + addon := addontesting.NewHostedModeAddon("test", "cluster1", "hosting-cluster", + metav1.Condition{ + Type: addonapiv1alpha1.ManagedClusterAddOnManifestApplied, + Status: metav1.ConditionTrue, + Reason: addonapiv1alpha1.AddonManifestAppliedReasonManifestsApplied, + Message: "manifests of addon are applied successfully", + }, + metav1.Condition{ + Type: addonapiv1alpha1.ManagedClusterAddOnHostingManifestApplied, + Status: metav1.ConditionTrue, + Reason: addonapiv1alpha1.AddonManifestAppliedReasonManifestsApplied, + Message: "hosting manifests of addon are applied successfully", + }, + ) + addon.Status.ConfigReferences = []addonapiv1alpha1.ConfigReference{ + { + ConfigGroupResource: v1alpha1.ConfigGroupResource{Group: "core", Resource: "foo"}, + DesiredConfig: &v1alpha1.ConfigSpecHash{ + ConfigReferent: v1alpha1.ConfigReferent{Name: "test", Namespace: "open-cluster-management"}, + SpecHash: "hashnew", + }, + LastAppliedConfig: &v1alpha1.ConfigSpecHash{ + ConfigReferent: v1alpha1.ConfigReferent{Name: "test", Namespace: "open-cluster-management"}, + SpecHash: "hash", + }, + }, + } + return addon + }()}, + clusterManagementAddon: []runtime.Object{ + addontesting.NewClusterManagementAddon("test", "testcrd", "testcr").Build(), + }, + work: []runtime.Object{func() *workapiv1.ManifestWork { + work := addontesting.NewManifestWork( + "addon-test-deploy", + "hosting-cluster", + testingcommon.NewUnstructured("v1", "ConfigMap", "default", "test1"), + testingcommon.NewUnstructured("v1", "Deployment", "default", "test1"), + ) + work.SetLabels(map[string]string{ + addonapiv1alpha1.AddonLabelKey: "test", + addonapiv1alpha1.AddonNamespaceLabelKey: "cluster1", + }) + work.SetAnnotations(map[string]string{ + workapiv1.ManifestConfigSpecHashAnnotationKey: "{\"foo.core/open-cluster-management/test\":\"hashnew\"}", + }) + work.Status.Conditions = []metav1.Condition{ + { + Type: workapiv1.WorkApplied, + Status: metav1.ConditionTrue, + }, + { + Type: workapiv1.WorkAvailable, + Status: metav1.ConditionTrue, + }, + } + return work + }()}, + validateAddonActions: func(t *testing.T, actions []clienttesting.Action) { + testingcommon.AssertActions(t, actions, "patch") + actual := actions[0].(clienttesting.PatchActionImpl).Patch + + addOn := &addonapiv1alpha1.ManagedClusterAddOn{} + err := json.Unmarshal(actual, addOn) + if err != nil { + t.Fatal(err) + } + configCond := meta.FindStatusCondition( + addOn.Status.Conditions, addonapiv1alpha1.ManagedClusterAddOnConditionProgressing) + if !(configCond != nil && + configCond.Reason == addonapiv1alpha1.ProgressingReasonUpgradeSucceed && + configCond.Status == metav1.ConditionFalse) { + t.Errorf("Condition Progressing is incorrect") + } + if len(addOn.Status.ConfigReferences) != 1 { + t.Errorf("ConfigReferences object is not correct: %v", addOn.Status.ConfigReferences) + } + if addOn.Status.ConfigReferences[0].LastAppliedConfig.SpecHash != + addOn.Status.ConfigReferences[0].DesiredConfig.SpecHash { + t.Errorf("LastAppliedConfig object is not correct: %v", + addOn.Status.ConfigReferences[0].LastAppliedConfig.SpecHash) + } + }, + }, + { + name: "update managedclusteraddon to configuration unsupported...", + syncKey: "cluster1/test", + managedClusteraddon: []runtime.Object{ + func() *addonapiv1alpha1.ManagedClusterAddOn { + addon := addontesting.NewHostedModeAddon("test", "cluster1", "hosting-cluster", + metav1.Condition{ + Type: addonapiv1alpha1.ManagedClusterAddOnManifestApplied, + Status: metav1.ConditionTrue, + Reason: addonapiv1alpha1.AddonManifestAppliedReasonManifestsApplied, + Message: "manifests of addon are applied successfully", + }, + metav1.Condition{ + Type: addonapiv1alpha1.ManagedClusterAddOnHostingManifestApplied, + Status: metav1.ConditionTrue, + Reason: addonapiv1alpha1.AddonManifestAppliedReasonManifestsApplied, + Message: "hosting manifests of addon are applied successfully", + }, + ) + addon.Spec.Configs = []addonapiv1alpha1.AddOnConfig{ + { + ConfigGroupResource: addonapiv1alpha1.ConfigGroupResource{ + Group: "config1.test", + Resource: "config1", + }, + ConfigReferent: addonapiv1alpha1.ConfigReferent{ + Namespace: "cluster1", + Name: "override", + }, + }, + } + addon.Status.SupportedConfigs = []addonapiv1alpha1.ConfigGroupResource{ + { + Group: "configs.test", + Resource: "testconfigs", + }, + } + return addon + }(), + }, + clusterManagementAddon: []runtime.Object{ + addontesting.NewClusterManagementAddon("test", "testcrd", "testcr").Build(), + }, + work: []runtime.Object{}, + validateAddonActions: func(t *testing.T, actions []clienttesting.Action) { + testingcommon.AssertActions(t, actions, "patch") + patch := actions[0].(clienttesting.PatchAction).GetPatch() + addOn := &addonapiv1alpha1.ManagedClusterAddOn{} + err := json.Unmarshal(patch, addOn) + if err != nil { + t.Fatal(err) + } + + configCond := meta.FindStatusCondition(addOn.Status.Conditions, addonapiv1alpha1.ManagedClusterAddOnConditionProgressing) + if !(configCond != nil && configCond.Reason == addonapiv1alpha1.ProgressingReasonConfigurationUnsupported && configCond.Status == metav1.ConditionFalse) { + t.Errorf("Condition Progressing is incorrect") + } + }, + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + fakeAddonClient := fakeaddon.NewSimpleClientset(c.managedClusteraddon...) + fakeWorkClient := fakework.NewSimpleClientset() + + addonInformers := addoninformers.NewSharedInformerFactory(fakeAddonClient, 10*time.Minute) + workInformers := workinformers.NewSharedInformerFactory(fakeWorkClient, 10*time.Minute) + + for _, obj := range c.managedClusteraddon { + if err := addonInformers.Addon().V1alpha1().ManagedClusterAddOns().Informer().GetStore(). + Add(obj); err != nil { + t.Fatal(err) + } + } + for _, obj := range c.clusterManagementAddon { + if err := addonInformers.Addon().V1alpha1().ClusterManagementAddOns().Informer().GetStore(). + Add(obj); err != nil { + t.Fatal(err) + } + } + for _, obj := range c.work { + if err := workInformers.Work().V1().ManifestWorks().Informer().GetStore().Add(obj); err != nil { + t.Fatal(err) + } + } + + syncContext := testingcommon.NewFakeSyncContext(t, c.syncKey) + recorder := syncContext.Recorder() + + controller := NewAddonProgressingController( + fakeAddonClient, + addonInformers.Addon().V1alpha1().ManagedClusterAddOns(), + addonInformers.Addon().V1alpha1().ClusterManagementAddOns(), + workInformers.Work().V1().ManifestWorks(), + utils.ManagedBySelf(map[string]agent.AgentAddon{"test": nil}), + recorder, + ) + + err := controller.Sync(context.TODO(), syncContext) + if err != nil { + t.Errorf("expected no error when sync: %v", err) + } + c.validateAddonActions(t, fakeAddonClient.Actions()) + }) + } +} diff --git a/pkg/registration/spoke/addon/configuration.go b/pkg/registration/spoke/addon/configuration.go index 6e34ac986..495da2736 100644 --- a/pkg/registration/spoke/addon/configuration.go +++ b/pkg/registration/spoke/addon/configuration.go @@ -15,8 +15,6 @@ import ( const ( defaultAddOnInstallationNamespace = "open-cluster-management-agent-addon" - // hostingClusterNameAnnotation is the annotation for indicating the hosting cluster name - hostingClusterNameAnnotation = "addon.open-cluster-management.io/hosting-cluster-name" ) // registrationConfig contains necessary information for addon registration @@ -76,7 +74,7 @@ func getAddOnInstallationNamespace(addOn *addonv1alpha1.ManagedClusterAddOn) str // isAddonRunningOutsideManagedCluster returns whether the addon agent is running on the managed cluster func isAddonRunningOutsideManagedCluster(addOn *addonv1alpha1.ManagedClusterAddOn) bool { - hostingCluster, ok := addOn.Annotations[hostingClusterNameAnnotation] + hostingCluster, ok := addOn.Annotations[addonv1alpha1.HostingClusterNameAnnotationKey] if ok && len(hostingCluster) != 0 { return true } diff --git a/pkg/registration/spoke/addon/configuration_test.go b/pkg/registration/spoke/addon/configuration_test.go index f853d099a..2a985e25b 100644 --- a/pkg/registration/spoke/addon/configuration_test.go +++ b/pkg/registration/spoke/addon/configuration_test.go @@ -85,7 +85,7 @@ func TestGetRegistrationConfigs(t *testing.T) { Namespace: testinghelpers.TestManagedClusterName, Name: addOnName, Annotations: map[string]string{ - hostingClusterNameAnnotation: "test", + addonv1alpha1.HostingClusterNameAnnotationKey: "test", }, }, Spec: addonv1alpha1.ManagedClusterAddOnSpec{ diff --git a/pkg/registration/spoke/addon/lease_controller_test.go b/pkg/registration/spoke/addon/lease_controller_test.go index 045cf6ffe..227425dc7 100644 --- a/pkg/registration/spoke/addon/lease_controller_test.go +++ b/pkg/registration/spoke/addon/lease_controller_test.go @@ -296,7 +296,7 @@ func TestSync(t *testing.T) { Namespace: testinghelpers.TestManagedClusterName, Name: "test", Annotations: map[string]string{ - hostingClusterNameAnnotation: "cluster1", + addonv1alpha1.HostingClusterNameAnnotationKey: "cluster1", }, }, Spec: addonv1alpha1.ManagedClusterAddOnSpec{ diff --git a/pkg/registration/spoke/addon/registration_controller_test.go b/pkg/registration/spoke/addon/registration_controller_test.go index 5cebc7178..da6249129 100644 --- a/pkg/registration/spoke/addon/registration_controller_test.go +++ b/pkg/registration/spoke/addon/registration_controller_test.go @@ -437,7 +437,7 @@ func newManagedClusterAddOn(namespace, name string, registrations []addonv1alpha } if hostedMode { - addon.SetAnnotations(map[string]string{hostingClusterNameAnnotation: "test"}) + addon.SetAnnotations(map[string]string{addonv1alpha1.HostingClusterNameAnnotationKey: "test"}) } return addon }