From 0aabccae096050d614dc2342fbdea298ab6d80da Mon Sep 17 00:00:00 2001 From: haoqing0110 Date: Wed, 13 Mar 2024 09:04:22 +0000 Subject: [PATCH 1/4] remove duplicate controller and installstrategy Signed-off-by: haoqing0110 --- pkg/addonfactory/addonfactory.go | 10 +- .../controllers/addoninstall/controller.go | 138 --- .../addoninstall/controller_test.go | 235 ----- .../controllers/managementaddon/controller.go | 95 -- .../managementaddon/controller_test.go | 151 --- pkg/addonmanager/manager.go | 49 +- pkg/agent/inteface.go | 121 ++- .../addon_configuration_reconciler.go | 114 --- .../addon_configuration_reconciler_test.go | 941 ------------------ .../addonconfiguration/controller.go | 196 ---- .../controllers/addonconfiguration/graph.go | 417 -------- .../addonconfiguration/graph_test.go | 711 ------------- .../mgmt_addon_progressing_reconciler.go | 143 --- .../mgmt_addon_progressing_reconciler_test.go | 677 ------------- .../controllers/addonowner/controller.go | 100 -- .../controllers/addonowner/controller_test.go | 89 -- test/integration/suite_test.go | 31 +- 17 files changed, 80 insertions(+), 4138 deletions(-) delete mode 100644 pkg/addonmanager/controllers/addoninstall/controller.go delete mode 100644 pkg/addonmanager/controllers/addoninstall/controller_test.go delete mode 100644 pkg/addonmanager/controllers/managementaddon/controller.go delete mode 100644 pkg/addonmanager/controllers/managementaddon/controller_test.go delete mode 100644 pkg/manager/controllers/addonconfiguration/addon_configuration_reconciler.go delete mode 100644 pkg/manager/controllers/addonconfiguration/addon_configuration_reconciler_test.go delete mode 100644 pkg/manager/controllers/addonconfiguration/controller.go delete mode 100644 pkg/manager/controllers/addonconfiguration/graph.go delete mode 100644 pkg/manager/controllers/addonconfiguration/graph_test.go delete mode 100644 pkg/manager/controllers/addonconfiguration/mgmt_addon_progressing_reconciler.go delete mode 100644 pkg/manager/controllers/addonconfiguration/mgmt_addon_progressing_reconciler_test.go delete mode 100644 pkg/manager/controllers/addonowner/controller.go delete mode 100644 pkg/manager/controllers/addonowner/controller_test.go diff --git a/pkg/addonfactory/addonfactory.go b/pkg/addonfactory/addonfactory.go index d4413377a..8098d9584 100644 --- a/pkg/addonfactory/addonfactory.go +++ b/pkg/addonfactory/addonfactory.go @@ -55,9 +55,9 @@ func NewAgentAddonFactory(addonName string, fs embed.FS, dir string) *AgentAddon fs: fs, dir: dir, agentAddonOptions: agent.AgentAddonOptions{ - AddonName: addonName, - Registration: nil, - InstallStrategy: nil, + AddonName: addonName, + Registration: nil, + // InstallStrategy: nil, HealthProber: nil, SupportedConfigGVRs: []schema.GroupVersionResource{}, }, @@ -87,14 +87,14 @@ func (f *AgentAddonFactory) WithGetValuesFuncs(getValuesFuncs ...GetValuesFunc) // Deprecated: add annotation "addon.open-cluster-management.io/lifecycle: addon-manager" to ClusterManagementAddon // and define install strategy in ClusterManagementAddon spec.installStrategy instead. // The migration plan refer to https://github.com/open-cluster-management-io/ocm/issues/355. -func (f *AgentAddonFactory) WithInstallStrategy(strategy *agent.InstallStrategy) *AgentAddonFactory { +/*func (f *AgentAddonFactory) WithInstallStrategy(strategy *agent.InstallStrategy) *AgentAddonFactory { if strategy.InstallNamespace == "" { strategy.InstallNamespace = AddonDefaultInstallNamespace } f.agentAddonOptions.InstallStrategy = strategy return f -} +}*/ // WithAgentRegistrationOption defines how agent is registered to the hub cluster. func (f *AgentAddonFactory) WithAgentRegistrationOption(option *agent.RegistrationOption) *AgentAddonFactory { diff --git a/pkg/addonmanager/controllers/addoninstall/controller.go b/pkg/addonmanager/controllers/addoninstall/controller.go deleted file mode 100644 index 4c57759e7..000000000 --- a/pkg/addonmanager/controllers/addoninstall/controller.go +++ /dev/null @@ -1,138 +0,0 @@ -package addoninstall - -import ( - "context" - "strings" - - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/api/meta" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - errorsutil "k8s.io/apimachinery/pkg/util/errors" - "k8s.io/klog/v2" - addonapiv1alpha1 "open-cluster-management.io/api/addon/v1alpha1" - addonv1alpha1client "open-cluster-management.io/api/client/addon/clientset/versioned" - addoninformerv1alpha1 "open-cluster-management.io/api/client/addon/informers/externalversions/addon/v1alpha1" - addonlisterv1alpha1 "open-cluster-management.io/api/client/addon/listers/addon/v1alpha1" - clusterinformers "open-cluster-management.io/api/client/cluster/informers/externalversions/cluster/v1" - clusterlister "open-cluster-management.io/api/client/cluster/listers/cluster/v1" - - "open-cluster-management.io/addon-framework/pkg/agent" - "open-cluster-management.io/addon-framework/pkg/basecontroller/factory" -) - -// managedClusterController reconciles instances of ManagedCluster on the hub. -type addonInstallController struct { - addonClient addonv1alpha1client.Interface - managedClusterLister clusterlister.ManagedClusterLister - managedClusterAddonLister addonlisterv1alpha1.ManagedClusterAddOnLister - agentAddons map[string]agent.AgentAddon -} - -func NewAddonInstallController( - addonClient addonv1alpha1client.Interface, - clusterInformers clusterinformers.ManagedClusterInformer, - addonInformers addoninformerv1alpha1.ManagedClusterAddOnInformer, - agentAddons map[string]agent.AgentAddon, -) factory.Controller { - c := &addonInstallController{ - addonClient: addonClient, - managedClusterLister: clusterInformers.Lister(), - managedClusterAddonLister: addonInformers.Lister(), - agentAddons: agentAddons, - } - - return factory.New().WithFilteredEventsInformersQueueKeysFunc( - func(obj runtime.Object) []string { - accessor, _ := meta.Accessor(obj) - return []string{accessor.GetNamespace()} - }, - func(obj interface{}) bool { - accessor, _ := meta.Accessor(obj) - if _, ok := c.agentAddons[accessor.GetName()]; !ok { - return false - } - - return true - }, - addonInformers.Informer()). - WithInformersQueueKeysFunc( - func(obj runtime.Object) []string { - accessor, _ := meta.Accessor(obj) - return []string{accessor.GetName()} - }, - clusterInformers.Informer(), - ). - WithSync(c.sync).ToController("addon-install-controller") -} - -func (c *addonInstallController) sync(ctx context.Context, syncCtx factory.SyncContext, clusterName string) error { - klog.V(4).Infof("Reconciling addon deploy on cluster %q", clusterName) - - cluster, err := c.managedClusterLister.Get(clusterName) - if errors.IsNotFound(err) { - return nil - } - if err != nil { - return err - } - - // if cluster is deleting, do not install addon - if !cluster.DeletionTimestamp.IsZero() { - klog.V(4).Infof("Cluster %q is deleting, skip addon deploy", clusterName) - return nil - } - - if value, ok := cluster.Annotations[addonapiv1alpha1.DisableAddonAutomaticInstallationAnnotationKey]; ok && - strings.EqualFold(value, "true") { - - klog.V(4).Infof("Cluster %q has annotation %q, skip addon deploy", - clusterName, addonapiv1alpha1.DisableAddonAutomaticInstallationAnnotationKey) - return nil - } - - var errs []error - - for addonName, addon := range c.agentAddons { - if addon.GetAgentAddonOptions().InstallStrategy == nil { - continue - } - - managedClusterFilter := addon.GetAgentAddonOptions().InstallStrategy.GetManagedClusterFilter() - if managedClusterFilter == nil { - continue - } - if !managedClusterFilter(cluster) { - klog.V(4).Infof("managed cluster filter is not match for addon %s on %s", addonName, clusterName) - continue - } - - err = c.applyAddon(ctx, addonName, clusterName, addon.GetAgentAddonOptions().InstallStrategy.InstallNamespace) - if err != nil { - errs = append(errs, err) - } - } - - return errorsutil.NewAggregate(errs) -} - -func (c *addonInstallController) applyAddon(ctx context.Context, addonName, clusterName, installNamespace string) error { - _, err := c.managedClusterAddonLister.ManagedClusterAddOns(clusterName).Get(addonName) - - // only create addon when it is missing, if user update the addon resource ,it should not be reverted - if errors.IsNotFound(err) { - addon := &addonapiv1alpha1.ManagedClusterAddOn{ - ObjectMeta: metav1.ObjectMeta{ - Name: addonName, - Namespace: clusterName, - }, - Spec: addonapiv1alpha1.ManagedClusterAddOnSpec{ - InstallNamespace: installNamespace, - }, - } - _, err = c.addonClient.AddonV1alpha1().ManagedClusterAddOns(clusterName).Create(ctx, addon, metav1.CreateOptions{}) - return err - } - - return err -} diff --git a/pkg/addonmanager/controllers/addoninstall/controller_test.go b/pkg/addonmanager/controllers/addoninstall/controller_test.go deleted file mode 100644 index bffeb9478..000000000 --- a/pkg/addonmanager/controllers/addoninstall/controller_test.go +++ /dev/null @@ -1,235 +0,0 @@ -package addoninstall - -import ( - "context" - "testing" - "time" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - clienttesting "k8s.io/client-go/testing" - "open-cluster-management.io/addon-framework/pkg/addonmanager/addontesting" - "open-cluster-management.io/addon-framework/pkg/agent" - addonapiv1alpha1 "open-cluster-management.io/api/addon/v1alpha1" - fakeaddon "open-cluster-management.io/api/client/addon/clientset/versioned/fake" - addoninformers "open-cluster-management.io/api/client/addon/informers/externalversions" - fakecluster "open-cluster-management.io/api/client/cluster/clientset/versioned/fake" - clusterv1informers "open-cluster-management.io/api/client/cluster/informers/externalversions" - clusterv1 "open-cluster-management.io/api/cluster/v1" -) - -type testAgent struct { - name string - strategy *agent.InstallStrategy -} - -func (t *testAgent) Manifests(cluster *clusterv1.ManagedCluster, addon *addonapiv1alpha1.ManagedClusterAddOn) ([]runtime.Object, error) { - return nil, nil -} - -func (t *testAgent) GetAgentAddonOptions() agent.AgentAddonOptions { - return agent.AgentAddonOptions{ - AddonName: t.name, - InstallStrategy: t.strategy, - } -} - -func newManagedClusterWithLabel(name, key, value string) *clusterv1.ManagedCluster { - cluster := addontesting.NewManagedCluster(name) - cluster.Labels = map[string]string{key: value} - - return cluster -} - -func newManagedClusterWithAnnotation(name, key, value string) *clusterv1.ManagedCluster { - cluster := addontesting.NewManagedCluster(name) - cluster.Annotations = map[string]string{key: value} - return cluster -} - -func TestReconcile(t *testing.T) { - cases := []struct { - name string - addon []runtime.Object - testaddons map[string]agent.AgentAddon - cluster []runtime.Object - validateAddonActions func(t *testing.T, actions []clienttesting.Action) - }{ - { - name: "no install strategy", - addon: []runtime.Object{}, - cluster: []runtime.Object{addontesting.NewManagedCluster("cluster1")}, - validateAddonActions: addontesting.AssertNoActions, - testaddons: map[string]agent.AgentAddon{ - "test": &testAgent{name: "test", strategy: nil}, - }, - }, - { - name: "all install strategy", - addon: []runtime.Object{}, - cluster: []runtime.Object{addontesting.NewManagedCluster("cluster1")}, - validateAddonActions: func(t *testing.T, actions []clienttesting.Action) { - addontesting.AssertActions(t, actions, "create") - actual := actions[0].(clienttesting.CreateActionImpl).Object - addOn := actual.(*addonapiv1alpha1.ManagedClusterAddOn) - if addOn.Spec.InstallNamespace != "test" { - t.Errorf("Install namespace is not correct, expected test but got %s", addOn.Spec.InstallNamespace) - } - }, - testaddons: map[string]agent.AgentAddon{ - "test": &testAgent{name: "test", strategy: agent.InstallAllStrategy("test")}, - }, - }, - { - name: "install addon when cluster is deleting", - addon: []runtime.Object{addontesting.NewAddon("test", "cluster1")}, - cluster: []runtime.Object{addontesting.DeleteManagedCluster(addontesting.NewManagedCluster("cluster1"))}, - validateAddonActions: func(t *testing.T, actions []clienttesting.Action) { - if len(actions) != 0 { - t.Errorf("Should not install addon when controller is deleting") - } - }, - testaddons: map[string]agent.AgentAddon{ - "test": &testAgent{name: "test", strategy: agent.InstallAllStrategy("test")}, - }, - }, - { - name: "cluster has addon disable automatic installation annotation", - addon: []runtime.Object{}, - cluster: []runtime.Object{addontesting.SetManagedClusterAnnotation( - addontesting.NewManagedCluster("cluster1"), - map[string]string{addonapiv1alpha1.DisableAddonAutomaticInstallationAnnotationKey: "true"})}, - validateAddonActions: func(t *testing.T, actions []clienttesting.Action) { - if len(actions) != 0 { - t.Errorf("Should not install addon when cluster has disable automatic installation annotation") - } - }, - testaddons: map[string]agent.AgentAddon{ - "test": &testAgent{name: "test", strategy: agent.InstallAllStrategy("test")}, - }, - }, - { - name: "selector install strategy with unmatched cluster", - addon: []runtime.Object{}, - cluster: []runtime.Object{addontesting.NewManagedCluster("cluster1")}, - validateAddonActions: addontesting.AssertNoActions, - testaddons: map[string]agent.AgentAddon{ - "test": &testAgent{name: "test", strategy: agent.InstallByLabelStrategy("test", metav1.LabelSelector{ - MatchLabels: map[string]string{"mode": "dev"}, - })}, - }, - }, - { - name: "selector install strategy with matched cluster", - addon: []runtime.Object{}, - cluster: []runtime.Object{newManagedClusterWithLabel("cluster1", "mode", "dev")}, - validateAddonActions: func(t *testing.T, actions []clienttesting.Action) { - addontesting.AssertActions(t, actions, "create") - actual := actions[0].(clienttesting.CreateActionImpl).Object - addOn := actual.(*addonapiv1alpha1.ManagedClusterAddOn) - if addOn.Spec.InstallNamespace != "test" { - t.Errorf("Install namespace is not correct, expected test but got %s", addOn.Spec.InstallNamespace) - } - }, - testaddons: map[string]agent.AgentAddon{ - "test": &testAgent{name: "test", strategy: agent.InstallByLabelStrategy("test", metav1.LabelSelector{ - MatchLabels: map[string]string{"mode": "dev"}, - })}, - }, - }, - { - name: "multi addons on a cluster", - addon: []runtime.Object{}, - cluster: []runtime.Object{newManagedClusterWithLabel("cluster1", "mode", "dev")}, - validateAddonActions: func(t *testing.T, actions []clienttesting.Action) { - // expect 2 create actions for each addons - addontesting.AssertActions(t, actions, "create", "create") - - for i := 0; i < 2; i++ { - actual := actions[i].(clienttesting.CreateActionImpl).Object - addOn := actual.(*addonapiv1alpha1.ManagedClusterAddOn) - switch addOn.Name { - case "test1": - if addOn.Spec.InstallNamespace != "test1" { - t.Errorf("Install namespace is not correct, expected test1 but got %s", addOn.Spec.InstallNamespace) - } - case "test2": - if addOn.Spec.InstallNamespace != "test2" { - t.Errorf("Install namespace is not correct, expected test2 but got %s", addOn.Spec.InstallNamespace) - } - default: - t.Errorf("invalid addon %v", addOn.Name) - } - } - }, - testaddons: map[string]agent.AgentAddon{ - "test1": &testAgent{name: "test1", strategy: agent.InstallAllStrategy("test1")}, - "test2": &testAgent{name: "test2", strategy: agent.InstallAllStrategy("test2")}, - }, - }, - { - name: "managed cluster filter install strategy", - addon: []runtime.Object{}, - cluster: []runtime.Object{ - newManagedClusterWithAnnotation("hosted-1", "mode", "hosted"), - newManagedClusterWithAnnotation("hosted-2", "mode", "hosted"), - addontesting.NewManagedCluster("default"), - }, - validateAddonActions: func(t *testing.T, actions []clienttesting.Action) { - // expect one create on trigger by cluster "default" - addontesting.AssertActions(t, actions, "create") - actual := actions[0].(clienttesting.CreateActionImpl).Object - addOn := actual.(*addonapiv1alpha1.ManagedClusterAddOn) - if addOn.Spec.InstallNamespace != "test" { - t.Errorf("Install namespace is not correct, expected test but got %s", addOn.Spec.InstallNamespace) - } - }, - testaddons: map[string]agent.AgentAddon{ - "test": &testAgent{name: "test", strategy: agent.InstallByFilterFunctionStrategy("test", func(cluster *clusterv1.ManagedCluster) bool { - if v, ok := cluster.Annotations["mode"]; ok && v == "hosted" { - return false - } - return true - })}, - }, - }, - } - - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - fakeClusterClient := fakecluster.NewSimpleClientset(c.cluster...) - fakeAddonClient := fakeaddon.NewSimpleClientset(c.addon...) - - addonInformers := addoninformers.NewSharedInformerFactory(fakeAddonClient, 10*time.Minute) - clusterInformers := clusterv1informers.NewSharedInformerFactory(fakeClusterClient, 10*time.Minute) - - for _, obj := range c.cluster { - if err := clusterInformers.Cluster().V1().ManagedClusters().Informer().GetStore().Add(obj); err != nil { - t.Fatal(err) - } - } - for _, obj := range c.addon { - if err := addonInformers.Addon().V1alpha1().ManagedClusterAddOns().Informer().GetStore().Add(obj); err != nil { - t.Fatal(err) - } - } - - controller := addonInstallController{ - addonClient: fakeAddonClient, - managedClusterLister: clusterInformers.Cluster().V1().ManagedClusters().Lister(), - managedClusterAddonLister: addonInformers.Addon().V1alpha1().ManagedClusterAddOns().Lister(), - agentAddons: c.testaddons, - } - - for _, obj := range c.cluster { - mc := obj.(*clusterv1.ManagedCluster) - syncContext := addontesting.NewFakeSyncContext(t) - err := controller.sync(context.TODO(), syncContext, mc.Name) - if err != nil { - t.Errorf("expected no error when sync: %v", err) - } - } - c.validateAddonActions(t, fakeAddonClient.Actions()) - }) - } -} diff --git a/pkg/addonmanager/controllers/managementaddon/controller.go b/pkg/addonmanager/controllers/managementaddon/controller.go deleted file mode 100644 index 47ef7b862..000000000 --- a/pkg/addonmanager/controllers/managementaddon/controller.go +++ /dev/null @@ -1,95 +0,0 @@ -package managementaddon - -import ( - "context" - - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/client-go/tools/cache" - addonapiv1alpha1 "open-cluster-management.io/api/addon/v1alpha1" - addonv1alpha1client "open-cluster-management.io/api/client/addon/clientset/versioned" - addoninformerv1alpha1 "open-cluster-management.io/api/client/addon/informers/externalversions/addon/v1alpha1" - addonlisterv1alpha1 "open-cluster-management.io/api/client/addon/listers/addon/v1alpha1" - "open-cluster-management.io/sdk-go/pkg/patcher" - - "open-cluster-management.io/addon-framework/pkg/agent" - "open-cluster-management.io/addon-framework/pkg/basecontroller/factory" -) - -const ( - controllerName = "management-addon-controller" -) - -// clusterManagementAddonController reconciles cma on the hub. -type clusterManagementAddonController struct { - addonClient addonv1alpha1client.Interface - clusterManagementAddonLister addonlisterv1alpha1.ClusterManagementAddOnLister - agentAddons map[string]agent.AgentAddon - addonFilterFunc factory.EventFilterFunc - addonPatcher patcher.Patcher[*addonapiv1alpha1.ClusterManagementAddOn, - addonapiv1alpha1.ClusterManagementAddOnSpec, - addonapiv1alpha1.ClusterManagementAddOnStatus] -} - -func NewManagementAddonController( - addonClient addonv1alpha1client.Interface, - clusterManagementAddonInformers addoninformerv1alpha1.ClusterManagementAddOnInformer, - agentAddons map[string]agent.AgentAddon, - addonFilterFunc factory.EventFilterFunc, -) factory.Controller { - syncCtx := factory.NewSyncContext(controllerName) - - c := &clusterManagementAddonController{ - addonClient: addonClient, - clusterManagementAddonLister: clusterManagementAddonInformers.Lister(), - agentAddons: agentAddons, - addonFilterFunc: addonFilterFunc, - addonPatcher: patcher.NewPatcher[*addonapiv1alpha1.ClusterManagementAddOn, - addonapiv1alpha1.ClusterManagementAddOnSpec, - addonapiv1alpha1.ClusterManagementAddOnStatus](addonClient.AddonV1alpha1().ClusterManagementAddOns()), - } - - return factory.New(). - WithSyncContext(syncCtx). - WithFilteredEventsInformersQueueKeysFunc( - func(obj runtime.Object) []string { - key, _ := cache.DeletionHandlingMetaNamespaceKeyFunc(obj) - return []string{key} - }, - c.addonFilterFunc, clusterManagementAddonInformers.Informer()). - WithSync(c.sync).ToController(controllerName) -} - -func (c *clusterManagementAddonController) sync(ctx context.Context, syncCtx factory.SyncContext, key string) error { - _, addonName, err := cache.SplitMetaNamespaceKey(key) - if err != nil { - // ignore addon whose key is invalid - return nil - } - - cma, err := c.clusterManagementAddonLister.Get(addonName) - if errors.IsNotFound(err) { - // addon cloud be deleted, ignore - return nil - } - if err != nil { - return err - } - - addon := c.agentAddons[cma.GetName()] - if addon.GetAgentAddonOptions().InstallStrategy == nil { - return nil - } - - // If the addon defines install strategy via WithInstallStrategy(), force add annotation "addon.open-cluster-management.io/lifecycle: self" to cma. - // The annotation with value "self" will be removed when remove WithInstallStrategy() in addon-framework. - // The migration plan refer to https://github.com/open-cluster-management-io/ocm/issues/355. - cmaCopy := cma.DeepCopy() - if cmaCopy.Annotations == nil { - cmaCopy.Annotations = map[string]string{} - } - cmaCopy.Annotations[addonapiv1alpha1.AddonLifecycleAnnotationKey] = addonapiv1alpha1.AddonLifecycleSelfManageAnnotationValue - - _, err = c.addonPatcher.PatchLabelAnnotations(ctx, cmaCopy, cmaCopy.ObjectMeta, cma.ObjectMeta) - return err -} diff --git a/pkg/addonmanager/controllers/managementaddon/controller_test.go b/pkg/addonmanager/controllers/managementaddon/controller_test.go deleted file mode 100644 index 8a3c3ee96..000000000 --- a/pkg/addonmanager/controllers/managementaddon/controller_test.go +++ /dev/null @@ -1,151 +0,0 @@ -package managementaddon - -import ( - "context" - "encoding/json" - "testing" - "time" - - "k8s.io/apimachinery/pkg/runtime" - clienttesting "k8s.io/client-go/testing" - "open-cluster-management.io/addon-framework/pkg/addonmanager/addontesting" - "open-cluster-management.io/addon-framework/pkg/agent" - "open-cluster-management.io/addon-framework/pkg/utils" - addonapiv1alpha1 "open-cluster-management.io/api/addon/v1alpha1" - fakeaddon "open-cluster-management.io/api/client/addon/clientset/versioned/fake" - addoninformers "open-cluster-management.io/api/client/addon/informers/externalversions" - clusterv1 "open-cluster-management.io/api/cluster/v1" - "open-cluster-management.io/sdk-go/pkg/patcher" -) - -type testAgent struct { - name string - strategy *agent.InstallStrategy -} - -func (t *testAgent) Manifests(cluster *clusterv1.ManagedCluster, addon *addonapiv1alpha1.ManagedClusterAddOn) ([]runtime.Object, error) { - return nil, nil -} - -func (t *testAgent) GetAgentAddonOptions() agent.AgentAddonOptions { - return agent.AgentAddonOptions{ - AddonName: t.name, - InstallStrategy: t.strategy, - } -} - -func newClusterManagementAddonWithAnnotation(name string, annotations map[string]string) *addonapiv1alpha1.ClusterManagementAddOn { - cma := addontesting.NewClusterManagementAddon(name, "", "").Build() - cma.Annotations = annotations - return cma -} - -func TestReconcile(t *testing.T) { - cases := []struct { - name string - cma []runtime.Object - testaddons map[string]agent.AgentAddon - validateAddonActions func(t *testing.T, actions []clienttesting.Action) - }{ - { - name: "add annotation when uses install strategy", - cma: []runtime.Object{newClusterManagementAddonWithAnnotation("test", map[string]string{ - "test": "test", - })}, - validateAddonActions: func(t *testing.T, actions []clienttesting.Action) { - addontesting.AssertActions(t, actions, "patch") - patch := actions[0].(clienttesting.PatchActionImpl).Patch - cma := &addonapiv1alpha1.ClusterManagementAddOn{} - err := json.Unmarshal(patch, cma) - if err != nil { - t.Fatal(err) - } - - if len(cma.Annotations) != 1 || cma.Annotations[addonapiv1alpha1.AddonLifecycleAnnotationKey] != addonapiv1alpha1.AddonLifecycleSelfManageAnnotationValue { - t.Errorf("cma annotation is not correct, expected self but got %s", cma.Annotations[addonapiv1alpha1.AddonLifecycleAnnotationKey]) - } - }, - testaddons: map[string]agent.AgentAddon{ - "test": &testAgent{name: "test", strategy: agent.InstallAllStrategy("test")}, - }, - }, - { - name: "override annotation when uses install strategy", - cma: []runtime.Object{newClusterManagementAddonWithAnnotation("test", map[string]string{ - "test": "test", - addonapiv1alpha1.AddonLifecycleAnnotationKey: addonapiv1alpha1.AddonLifecycleAddonManagerAnnotationValue, - })}, - validateAddonActions: func(t *testing.T, actions []clienttesting.Action) { - addontesting.AssertActions(t, actions, "patch") - patch := actions[0].(clienttesting.PatchActionImpl).Patch - cma := &addonapiv1alpha1.ClusterManagementAddOn{} - err := json.Unmarshal(patch, cma) - if err != nil { - t.Fatal(err) - } - - if len(cma.Annotations) != 1 || cma.Annotations[addonapiv1alpha1.AddonLifecycleAnnotationKey] != addonapiv1alpha1.AddonLifecycleSelfManageAnnotationValue { - t.Errorf("cma annotation is not correct, expected self but got %s", cma.Annotations[addonapiv1alpha1.AddonLifecycleAnnotationKey]) - } - }, - testaddons: map[string]agent.AgentAddon{ - "test": &testAgent{name: "test", strategy: agent.InstallAllStrategy("test")}, - }, - }, - { - name: "no patch annotation if managed by self", - cma: []runtime.Object{newClusterManagementAddonWithAnnotation("test", map[string]string{ - "test": "test", - addonapiv1alpha1.AddonLifecycleAnnotationKey: addonapiv1alpha1.AddonLifecycleSelfManageAnnotationValue, - })}, - validateAddonActions: addontesting.AssertNoActions, - testaddons: map[string]agent.AgentAddon{ - "test": &testAgent{name: "test", strategy: agent.InstallAllStrategy("test")}, - }, - }, - { - name: "no patch annotation if no install strategy", - cma: []runtime.Object{newClusterManagementAddonWithAnnotation("test", map[string]string{ - "test": "test", - addonapiv1alpha1.AddonLifecycleAnnotationKey: addonapiv1alpha1.AddonLifecycleAddonManagerAnnotationValue, - })}, - validateAddonActions: addontesting.AssertNoActions, - testaddons: map[string]agent.AgentAddon{ - "test": &testAgent{name: "test"}, - }, - }, - } - - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - fakeAddonClient := fakeaddon.NewSimpleClientset(c.cma...) - addonInformers := addoninformers.NewSharedInformerFactory(fakeAddonClient, 10*time.Minute) - - for _, obj := range c.cma { - if err := addonInformers.Addon().V1alpha1().ClusterManagementAddOns().Informer().GetStore().Add(obj); err != nil { - t.Fatal(err) - } - } - - controller := clusterManagementAddonController{ - addonClient: fakeAddonClient, - clusterManagementAddonLister: addonInformers.Addon().V1alpha1().ClusterManagementAddOns().Lister(), - agentAddons: c.testaddons, - addonFilterFunc: utils.FilterByAddonName(c.testaddons), - addonPatcher: patcher.NewPatcher[*addonapiv1alpha1.ClusterManagementAddOn, - addonapiv1alpha1.ClusterManagementAddOnSpec, - addonapiv1alpha1.ClusterManagementAddOnStatus](fakeAddonClient.AddonV1alpha1().ClusterManagementAddOns()), - } - - for _, obj := range c.cma { - cma := obj.(*addonapiv1alpha1.ClusterManagementAddOn) - syncContext := addontesting.NewFakeSyncContext(t) - err := controller.sync(context.TODO(), syncContext, cma.Name) - if err != nil { - t.Errorf("expected no error when sync: %v", err) - } - } - c.validateAddonActions(t, fakeAddonClient.Actions()) - }) - } -} diff --git a/pkg/addonmanager/manager.go b/pkg/addonmanager/manager.go index b2f3575e5..a6a121d2e 100644 --- a/pkg/addonmanager/manager.go +++ b/pkg/addonmanager/manager.go @@ -22,17 +22,13 @@ import ( workv1informers "open-cluster-management.io/api/client/work/informers/externalversions" "open-cluster-management.io/addon-framework/pkg/addonmanager/controllers/addonconfig" - "open-cluster-management.io/addon-framework/pkg/addonmanager/controllers/addoninstall" "open-cluster-management.io/addon-framework/pkg/addonmanager/controllers/agentdeploy" "open-cluster-management.io/addon-framework/pkg/addonmanager/controllers/certificate" - "open-cluster-management.io/addon-framework/pkg/addonmanager/controllers/managementaddon" "open-cluster-management.io/addon-framework/pkg/addonmanager/controllers/managementaddonconfig" "open-cluster-management.io/addon-framework/pkg/addonmanager/controllers/registration" "open-cluster-management.io/addon-framework/pkg/agent" "open-cluster-management.io/addon-framework/pkg/basecontroller/factory" "open-cluster-management.io/addon-framework/pkg/index" - "open-cluster-management.io/addon-framework/pkg/manager/controllers/addonconfiguration" - "open-cluster-management.io/addon-framework/pkg/manager/controllers/addonowner" "open-cluster-management.io/addon-framework/pkg/utils" ) @@ -241,33 +237,7 @@ func (a *addonManager) StartWithInformers(ctx context.Context, a.addonAgents, ) - addonInstallController := addoninstall.NewAddonInstallController( - addonClient, - clusterInformers.Cluster().V1().ManagedClusters(), - addonInformers.Addon().V1alpha1().ManagedClusterAddOns(), - a.addonAgents, - ) - - // This controller is used during migrating addons to be managed by addon-manager. - // This should be removed when the migration is done. - // The migration plan refer to https://github.com/open-cluster-management-io/ocm/issues/355. - managementAddonController := managementaddon.NewManagementAddonController( - addonClient, - addonInformers.Addon().V1alpha1().ClusterManagementAddOns(), - a.addonAgents, - utils.FilterByAddonName(a.addonAgents), - ) - - // This is a duplicate controller in general addon-manager. This should be removed when we - // alway enable the addon-manager - addonOwnerController := addonowner.NewAddonOwnerController( - addonClient, - addonInformers.Addon().V1alpha1().ManagedClusterAddOns(), - addonInformers.Addon().V1alpha1().ClusterManagementAddOns(), - utils.ManagedBySelf(a.addonAgents), - ) - - var addonConfigController, managementAddonConfigController, addonConfigurationController factory.Controller + var addonConfigController, managementAddonConfigController factory.Controller if len(a.addonConfigs) != 0 { addonConfigController = addonconfig.NewAddonConfigController( addonClient, @@ -284,17 +254,6 @@ func (a *addonManager) StartWithInformers(ctx context.Context, a.addonConfigs, utils.FilterByAddonName(a.addonAgents), ) - - // start addonConfiguration controller, note this is to handle the case when the general addon-manager - // is not started, we should consider to remove this when the general addon-manager are always started. - // This controller will also ignore the installStrategy part. - addonConfigurationController = addonconfiguration.NewAddonConfigurationController( - addonClient, - addonInformers.Addon().V1alpha1().ManagedClusterAddOns(), - addonInformers.Addon().V1alpha1().ClusterManagementAddOns(), - nil, nil, - utils.ManagedBySelf(a.addonAgents), - ) } var csrApproveController factory.Controller @@ -334,19 +293,13 @@ func (a *addonManager) StartWithInformers(ctx context.Context, go deployController.Run(ctx, 1) go registrationController.Run(ctx, 1) - go addonInstallController.Run(ctx, 1) - go managementAddonController.Run(ctx, 1) - go addonOwnerController.Run(ctx, 1) if addonConfigController != nil { go addonConfigController.Run(ctx, 1) } if managementAddonConfigController != nil { go managementAddonConfigController.Run(ctx, 1) } - if addonConfigurationController != nil { - go addonConfigurationController.Run(ctx, 1) - } if csrApproveController != nil { go csrApproveController.Run(ctx, 1) } diff --git a/pkg/agent/inteface.go b/pkg/agent/inteface.go index 1dd5d7b00..03890f198 100644 --- a/pkg/agent/inteface.go +++ b/pkg/agent/inteface.go @@ -4,11 +4,8 @@ import ( "fmt" certificatesv1 "k8s.io/api/certificates/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/klog/v2" addonapiv1alpha1 "open-cluster-management.io/api/addon/v1alpha1" clusterv1 "open-cluster-management.io/api/cluster/v1" workapiv1 "open-cluster-management.io/api/work/v1" @@ -55,7 +52,7 @@ type AgentAddonOptions struct { // Deprecated: use installStrategy config in ClusterManagementAddOn API instead // The migration plan refer to https://github.com/open-cluster-management-io/ocm/issues/355. // +optional - InstallStrategy *InstallStrategy + // InstallStrategy *InstallStrategy // Updaters select a set of resources and define the strategies to update them. // UpdateStrategy is Update if no Updater is defined for a resource. @@ -158,21 +155,21 @@ type RegistrationOption struct { } // InstallStrategy is the installation strategy of the manifests prescribed by Manifests(..). -type InstallStrategy struct { - *installStrategy -} - -type installStrategy struct { - // InstallNamespace is target deploying namespace in the managed cluster upon automatic addon installation. - InstallNamespace string - - // managedClusterFilter will filter the clusters to install the addon to. - managedClusterFilter func(cluster *clusterv1.ManagedCluster) bool -} - -func (s *InstallStrategy) GetManagedClusterFilter() func(cluster *clusterv1.ManagedCluster) bool { - return s.managedClusterFilter -} +// type InstallStrategy struct { +// *installStrategy +// } + +// type installStrategy struct { +// // InstallNamespace is target deploying namespace in the managed cluster upon automatic addon installation. +// InstallNamespace string +// +// // managedClusterFilter will filter the clusters to install the addon to. +// managedClusterFilter func(cluster *clusterv1.ManagedCluster) bool +// } + +// func (s *InstallStrategy) GetManagedClusterFilter() func(cluster *clusterv1.ManagedCluster) bool { +// return s.managedClusterFilter +// } type Updater struct { // ResourceIdentifier sets what resources the strategy applies to @@ -258,53 +255,53 @@ func DefaultGroups(clusterName, addonName string) []string { } } -// InstallAllStrategy indicate to install addon to all clusters -func InstallAllStrategy(installNamespace string) *InstallStrategy { - return &InstallStrategy{ - &installStrategy{ - InstallNamespace: installNamespace, - managedClusterFilter: func(cluster *clusterv1.ManagedCluster) bool { - return true - }, - }, - } -} +// // InstallAllStrategy indicate to install addon to all clusters +// func InstallAllStrategy(installNamespace string) *InstallStrategy { +// return &InstallStrategy{ +// &installStrategy{ +// InstallNamespace: installNamespace, +// managedClusterFilter: func(cluster *clusterv1.ManagedCluster) bool { +// return true +// }, +// }, +// } +// } // InstallByLabelStrategy indicate to install addon based on clusters' label -func InstallByLabelStrategy(installNamespace string, selector metav1.LabelSelector) *InstallStrategy { - return &InstallStrategy{ - &installStrategy{ - InstallNamespace: installNamespace, - managedClusterFilter: func(cluster *clusterv1.ManagedCluster) bool { - selector, err := metav1.LabelSelectorAsSelector(&selector) - if err != nil { - klog.Warningf("labels selector is not correct: %v", err) - return false - } - - if !selector.Matches(labels.Set(cluster.Labels)) { - return false - } - return true - }, - }, - } -} +// func InstallByLabelStrategy(installNamespace string, selector metav1.LabelSelector) *InstallStrategy { +// return &InstallStrategy{ +// &installStrategy{ +// InstallNamespace: installNamespace, +// managedClusterFilter: func(cluster *clusterv1.ManagedCluster) bool { +// selector, err := metav1.LabelSelectorAsSelector(&selector) +// if err != nil { +// klog.Warningf("labels selector is not correct: %v", err) +// return false +// } +// +// if !selector.Matches(labels.Set(cluster.Labels)) { +// return false +// } +// return true +// }, +// }, +// } +// } // InstallByFilterFunctionStrategy indicate to install addon based on a filter function, and it will also install addons if the filter function is nil. -func InstallByFilterFunctionStrategy(installNamespace string, f func(cluster *clusterv1.ManagedCluster) bool) *InstallStrategy { - if f == nil { - f = func(cluster *clusterv1.ManagedCluster) bool { - return true - } - } - return &InstallStrategy{ - &installStrategy{ - InstallNamespace: installNamespace, - managedClusterFilter: f, - }, - } -} +// func InstallByFilterFunctionStrategy(installNamespace string, f func(cluster *clusterv1.ManagedCluster) bool) *InstallStrategy { +// if f == nil { +// f = func(cluster *clusterv1.ManagedCluster) bool { +// return true +// } +// } +// return &InstallStrategy{ +// &installStrategy{ +// InstallNamespace: installNamespace, +// managedClusterFilter: f, +// }, +// } +// } // ApprovalAllCSRs returns true for all csrs. func ApprovalAllCSRs(cluster *clusterv1.ManagedCluster, addon *addonapiv1alpha1.ManagedClusterAddOn, csr *certificatesv1.CertificateSigningRequest) bool { diff --git a/pkg/manager/controllers/addonconfiguration/addon_configuration_reconciler.go b/pkg/manager/controllers/addonconfiguration/addon_configuration_reconciler.go deleted file mode 100644 index 5e38bfde4..000000000 --- a/pkg/manager/controllers/addonconfiguration/addon_configuration_reconciler.go +++ /dev/null @@ -1,114 +0,0 @@ -package addonconfiguration - -import ( - "context" - "encoding/json" - "fmt" - - jsonpatch "github.com/evanphx/json-patch" - "k8s.io/apimachinery/pkg/api/equality" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - utilerrors "k8s.io/apimachinery/pkg/util/errors" - "k8s.io/klog/v2" - - addonv1alpha1 "open-cluster-management.io/api/addon/v1alpha1" - addonv1alpha1client "open-cluster-management.io/api/client/addon/clientset/versioned" -) - -type managedClusterAddonConfigurationReconciler struct { - addonClient addonv1alpha1client.Interface -} - -func (d *managedClusterAddonConfigurationReconciler) reconcile( - ctx context.Context, cma *addonv1alpha1.ClusterManagementAddOn, graph *configurationGraph) (*addonv1alpha1.ClusterManagementAddOn, reconcileState, error) { - var errs []error - - for _, addon := range graph.getAddonsToUpdate() { - mca := d.mergeAddonConfig(addon.mca, addon.desiredConfigs) - err := d.patchAddonStatus(ctx, mca, addon.mca) - if err != nil { - errs = append(errs, err) - } - } - - return cma, reconcileContinue, utilerrors.NewAggregate(errs) -} - -func (d *managedClusterAddonConfigurationReconciler) mergeAddonConfig( - mca *addonv1alpha1.ManagedClusterAddOn, desiredConfigMap addonConfigMap) *addonv1alpha1.ManagedClusterAddOn { - mcaCopy := mca.DeepCopy() - - var mergedConfigs []addonv1alpha1.ConfigReference - // remove configs that are not desired - for _, config := range mcaCopy.Status.ConfigReferences { - if _, ok := desiredConfigMap[config.ConfigGroupResource]; ok { - mergedConfigs = append(mergedConfigs, config) - } - } - - // append or update configs - for _, config := range desiredConfigMap { - var match bool - for i := range mergedConfigs { - if mergedConfigs[i].ConfigGroupResource != config.ConfigGroupResource { - continue - } - - match = true - // set LastObservedGeneration to 0 when config name/namespace changes - if mergedConfigs[i].DesiredConfig != nil && (mergedConfigs[i].DesiredConfig.ConfigReferent != config.DesiredConfig.ConfigReferent) { - mergedConfigs[i].LastObservedGeneration = 0 - } - mergedConfigs[i].ConfigReferent = config.ConfigReferent - mergedConfigs[i].DesiredConfig = config.DesiredConfig.DeepCopy() - } - - if !match { - mergedConfigs = append(mergedConfigs, config) - } - } - - mcaCopy.Status.ConfigReferences = mergedConfigs - return mcaCopy -} - -func (d *managedClusterAddonConfigurationReconciler) patchAddonStatus(ctx context.Context, new, old *addonv1alpha1.ManagedClusterAddOn) error { - if equality.Semantic.DeepEqual(new.Status, old.Status) { - return nil - } - - oldData, err := json.Marshal(&addonv1alpha1.ManagedClusterAddOn{ - Status: addonv1alpha1.ManagedClusterAddOnStatus{ - Namespace: old.Status.Namespace, - ConfigReferences: old.Status.ConfigReferences, - }, - }) - if err != nil { - return err - } - - newData, err := json.Marshal(&addonv1alpha1.ManagedClusterAddOn{ - ObjectMeta: metav1.ObjectMeta{ - UID: new.UID, - ResourceVersion: new.ResourceVersion, - }, - Status: addonv1alpha1.ManagedClusterAddOnStatus{ - Namespace: new.Status.Namespace, - ConfigReferences: new.Status.ConfigReferences, - }, - }) - if err != nil { - return err - } - - patchBytes, err := jsonpatch.CreateMergePatch(oldData, newData) - if err != nil { - return fmt.Errorf("failed to create patch for addon %s: %w", new.Name, err) - } - - klog.V(2).Infof("Patching addon %s/%s status with %s", new.Namespace, new.Name, string(patchBytes)) - _, err = d.addonClient.AddonV1alpha1().ManagedClusterAddOns(new.Namespace).Patch( - ctx, new.Name, types.MergePatchType, patchBytes, metav1.PatchOptions{}, "status") - return err -} diff --git a/pkg/manager/controllers/addonconfiguration/addon_configuration_reconciler_test.go b/pkg/manager/controllers/addonconfiguration/addon_configuration_reconciler_test.go deleted file mode 100644 index 626f5c011..000000000 --- a/pkg/manager/controllers/addonconfiguration/addon_configuration_reconciler_test.go +++ /dev/null @@ -1,941 +0,0 @@ -package addonconfiguration - -import ( - "context" - "encoding/json" - "sort" - "testing" - "time" - - apiequality "k8s.io/apimachinery/pkg/api/equality" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/util/intstr" - clienttesting "k8s.io/client-go/testing" - "k8s.io/client-go/tools/cache" - - "open-cluster-management.io/addon-framework/pkg/addonmanager/addontesting" - "open-cluster-management.io/addon-framework/pkg/index" - "open-cluster-management.io/api/addon/v1alpha1" - addonv1alpha1 "open-cluster-management.io/api/addon/v1alpha1" - fakeaddon "open-cluster-management.io/api/client/addon/clientset/versioned/fake" - addoninformers "open-cluster-management.io/api/client/addon/informers/externalversions" - fakecluster "open-cluster-management.io/api/client/cluster/clientset/versioned/fake" - clusterv1informers "open-cluster-management.io/api/client/cluster/informers/externalversions" - clusterv1alpha1 "open-cluster-management.io/api/cluster/v1alpha1" - clusterv1beta1 "open-cluster-management.io/api/cluster/v1beta1" -) - -func TestAddonConfigReconcile(t *testing.T) { - cases := []struct { - name string - managedClusteraddon []runtime.Object - clusterManagementAddon *addonv1alpha1.ClusterManagementAddOn - placements []runtime.Object - placementDecisions []runtime.Object - validateAddonActions func(t *testing.T, actions []clienttesting.Action) - expectErr bool - }{ - { - name: "no configuration", - managedClusteraddon: []runtime.Object{ - addontesting.NewAddon("test", "cluster1"), - addontesting.NewAddon("test", "cluster2"), - }, - clusterManagementAddon: addontesting.NewClusterManagementAddon("test", "", "").Build(), - placements: []runtime.Object{}, - placementDecisions: []runtime.Object{}, - validateAddonActions: addontesting.AssertNoActions, - }, - { - name: "manual installStrategy", - managedClusteraddon: []runtime.Object{ - addontesting.NewAddon("test", "cluster1"), - addontesting.NewAddon("test", "cluster2"), - }, - clusterManagementAddon: addontesting.NewClusterManagementAddon("test", "", "").WithSupportedConfigs(addonv1alpha1.ConfigMeta{ - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - DefaultConfig: &addonv1alpha1.ConfigReferent{Name: "test"}, - }).WithDefaultConfigReferences(addonv1alpha1.DefaultConfigReference{ - ConfigGroupResource: v1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - DesiredConfig: &v1alpha1.ConfigSpecHash{ - ConfigReferent: v1alpha1.ConfigReferent{Name: "test"}, - SpecHash: "hash", - }, - }).Build(), - placements: []runtime.Object{}, - placementDecisions: []runtime.Object{}, - validateAddonActions: func(t *testing.T, actions []clienttesting.Action) { - addontesting.AssertActions(t, actions, "patch", "patch") - sort.Sort(byPatchName(actions)) - expectPatchConfigurationAction(t, actions[0], []addonv1alpha1.ConfigReference{{ - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test"}, - SpecHash: "hash", - }, - LastObservedGeneration: 0, - }}) - }, - }, - { - name: "placement installStrategy", - managedClusteraddon: []runtime.Object{ - addontesting.NewAddon("test", "cluster1"), - addontesting.NewAddon("test", "cluster2"), - }, - placements: []runtime.Object{ - &clusterv1beta1.Placement{ObjectMeta: metav1.ObjectMeta{Name: "test-placement", Namespace: "default"}}, - }, - placementDecisions: []runtime.Object{ - &clusterv1beta1.PlacementDecision{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-placement", - Namespace: "default", - Labels: map[string]string{ - clusterv1beta1.PlacementLabel: "test-placement", - clusterv1beta1.DecisionGroupIndexLabel: "0", - }, - }, - Status: clusterv1beta1.PlacementDecisionStatus{ - Decisions: []clusterv1beta1.ClusterDecision{{ClusterName: "cluster2"}}, - }, - }, - }, - clusterManagementAddon: addontesting.NewClusterManagementAddon("test", "", "").WithSupportedConfigs(addonv1alpha1.ConfigMeta{ - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - DefaultConfig: &addonv1alpha1.ConfigReferent{Name: "test"}, - }).WithDefaultConfigReferences(addonv1alpha1.DefaultConfigReference{ - ConfigGroupResource: v1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - DesiredConfig: &v1alpha1.ConfigSpecHash{ - ConfigReferent: v1alpha1.ConfigReferent{Name: "test"}, - SpecHash: "hash", - }, - }).WithPlacementStrategy(addonv1alpha1.PlacementStrategy{ - PlacementRef: addonv1alpha1.PlacementRef{Name: "test-placement", Namespace: "default"}, - RolloutStrategy: clusterv1alpha1.RolloutStrategy{Type: clusterv1alpha1.All}, - }).WithInstallProgression(addonv1alpha1.InstallProgression{ - PlacementRef: addonv1alpha1.PlacementRef{Name: "test-placement", Namespace: "default"}, - ConfigReferences: []addonv1alpha1.InstallConfigReference{ - { - ConfigGroupResource: v1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - DesiredConfig: &v1alpha1.ConfigSpecHash{ - ConfigReferent: v1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "hash1", - }, - }, - }, - }).Build(), - validateAddonActions: func(t *testing.T, actions []clienttesting.Action) { - addontesting.AssertActions(t, actions, "patch", "patch") - sort.Sort(byPatchName(actions)) - expectPatchConfigurationAction(t, actions[0], []addonv1alpha1.ConfigReference{{ - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test"}, - SpecHash: "hash", - }, - LastObservedGeneration: 0, - }}) - expectPatchConfigurationAction(t, actions[1], []addonv1alpha1.ConfigReference{{ - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "hash1", - }, - LastObservedGeneration: 0, - }}) - }, - }, - { - name: "mca override", - managedClusteraddon: []runtime.Object{ - newManagedClusterAddon("test", "cluster1", []addonv1alpha1.AddOnConfig{{ - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test2"}, - }}, nil, nil), - addontesting.NewAddon("test", "cluster2"), - }, - placements: []runtime.Object{ - &clusterv1beta1.Placement{ObjectMeta: metav1.ObjectMeta{Name: "test-placement", Namespace: "default"}}, - }, - placementDecisions: []runtime.Object{ - &clusterv1beta1.PlacementDecision{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-placement", - Namespace: "default", - Labels: map[string]string{ - clusterv1beta1.PlacementLabel: "test-placement", - clusterv1beta1.DecisionGroupIndexLabel: "0", - }, - }, - Status: clusterv1beta1.PlacementDecisionStatus{ - Decisions: []clusterv1beta1.ClusterDecision{{ClusterName: "cluster1"}, {ClusterName: "cluster2"}}, - }, - }, - }, - clusterManagementAddon: addontesting.NewClusterManagementAddon("test", "", "").WithSupportedConfigs(addonv1alpha1.ConfigMeta{ - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - DefaultConfig: &addonv1alpha1.ConfigReferent{Name: "test"}, - }).WithDefaultConfigReferences(addonv1alpha1.DefaultConfigReference{ - ConfigGroupResource: v1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - DesiredConfig: &v1alpha1.ConfigSpecHash{ - ConfigReferent: v1alpha1.ConfigReferent{Name: "test"}, - SpecHash: "hash", - }, - }).WithPlacementStrategy(addonv1alpha1.PlacementStrategy{ - PlacementRef: addonv1alpha1.PlacementRef{Name: "test-placement", Namespace: "default"}, - RolloutStrategy: clusterv1alpha1.RolloutStrategy{Type: clusterv1alpha1.All}, - }).WithInstallProgression(addonv1alpha1.InstallProgression{ - PlacementRef: addonv1alpha1.PlacementRef{Name: "test-placement", Namespace: "default"}, - ConfigReferences: []addonv1alpha1.InstallConfigReference{ - { - ConfigGroupResource: v1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - DesiredConfig: &v1alpha1.ConfigSpecHash{ - ConfigReferent: v1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "hash1", - }, - }, - }, - }).Build(), - validateAddonActions: func(t *testing.T, actions []clienttesting.Action) { - addontesting.AssertActions(t, actions, "patch", "patch") - sort.Sort(byPatchName(actions)) - expectPatchConfigurationAction(t, actions[0], []addonv1alpha1.ConfigReference{{ - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test2"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test2"}, - SpecHash: "", - }, - LastObservedGeneration: 0, - }}) - expectPatchConfigurationAction(t, actions[1], []addonv1alpha1.ConfigReference{{ - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "hash1", - }, - LastObservedGeneration: 0, - }}) - }, - }, - { - name: "config name/namespce change", - managedClusteraddon: []runtime.Object{ - newManagedClusterAddon("test", "cluster1", []addonv1alpha1.AddOnConfig{}, []addonv1alpha1.ConfigReference{{ - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "hash1", - }, - LastObservedGeneration: 1, - }}, nil), - }, - placements: []runtime.Object{ - &clusterv1beta1.Placement{ObjectMeta: metav1.ObjectMeta{Name: "test-placement", Namespace: "default"}}, - }, - placementDecisions: []runtime.Object{ - &clusterv1beta1.PlacementDecision{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-placement", - Namespace: "default", - Labels: map[string]string{ - clusterv1beta1.PlacementLabel: "test-placement", - clusterv1beta1.DecisionGroupIndexLabel: "0", - }, - }, - Status: clusterv1beta1.PlacementDecisionStatus{ - Decisions: []clusterv1beta1.ClusterDecision{{ClusterName: "cluster1"}}, - }, - }, - }, - clusterManagementAddon: addontesting.NewClusterManagementAddon("test", "", "").WithSupportedConfigs(addonv1alpha1.ConfigMeta{ - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - DefaultConfig: &addonv1alpha1.ConfigReferent{Name: "test"}, - }).WithPlacementStrategy(addonv1alpha1.PlacementStrategy{ - PlacementRef: addonv1alpha1.PlacementRef{Name: "test-placement", Namespace: "default"}, - RolloutStrategy: clusterv1alpha1.RolloutStrategy{Type: clusterv1alpha1.All}, - }).WithInstallProgression(addonv1alpha1.InstallProgression{ - PlacementRef: addonv1alpha1.PlacementRef{Name: "test-placement", Namespace: "default"}, - ConfigReferences: []addonv1alpha1.InstallConfigReference{ - { - ConfigGroupResource: v1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - DesiredConfig: &v1alpha1.ConfigSpecHash{ - ConfigReferent: v1alpha1.ConfigReferent{Name: "test2"}, - SpecHash: "hash2", - }, - }, - }, - }).Build(), - validateAddonActions: func(t *testing.T, actions []clienttesting.Action) { - addontesting.AssertActions(t, actions, "patch") - expectPatchConfigurationAction(t, actions[0], []addonv1alpha1.ConfigReference{{ - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test2"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test2"}, - SpecHash: "hash2", - }, - LastObservedGeneration: 0, - }}) - }, - }, - { - name: "config spec hash change", - managedClusteraddon: []runtime.Object{ - newManagedClusterAddon("test", "cluster1", []addonv1alpha1.AddOnConfig{}, []addonv1alpha1.ConfigReference{{ - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "hash1", - }, - LastObservedGeneration: 1, - }}, nil), - }, - placements: []runtime.Object{ - &clusterv1beta1.Placement{ObjectMeta: metav1.ObjectMeta{Name: "test-placement", Namespace: "default"}}, - }, - placementDecisions: []runtime.Object{ - &clusterv1beta1.PlacementDecision{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-placement", - Namespace: "default", - Labels: map[string]string{ - clusterv1beta1.PlacementLabel: "test-placement", - clusterv1beta1.DecisionGroupIndexLabel: "0", - }, - }, - Status: clusterv1beta1.PlacementDecisionStatus{ - Decisions: []clusterv1beta1.ClusterDecision{{ClusterName: "cluster1"}}, - }, - }, - }, - clusterManagementAddon: addontesting.NewClusterManagementAddon("test", "", "").WithSupportedConfigs(addonv1alpha1.ConfigMeta{ - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - DefaultConfig: &addonv1alpha1.ConfigReferent{Name: "test"}, - }).WithPlacementStrategy(addonv1alpha1.PlacementStrategy{ - PlacementRef: addonv1alpha1.PlacementRef{Name: "test-placement", Namespace: "default"}, - RolloutStrategy: clusterv1alpha1.RolloutStrategy{Type: clusterv1alpha1.All}, - Configs: []addonv1alpha1.AddOnConfig{v1alpha1.AddOnConfig{ - ConfigGroupResource: v1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - ConfigReferent: v1alpha1.ConfigReferent{Name: "test1"}}}, - }).WithInstallProgression(addonv1alpha1.InstallProgression{ - PlacementRef: addonv1alpha1.PlacementRef{Name: "test-placement", Namespace: "default"}, - ConfigReferences: []addonv1alpha1.InstallConfigReference{ - { - ConfigGroupResource: v1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - DesiredConfig: &v1alpha1.ConfigSpecHash{ - ConfigReferent: v1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "hash1new", - }, - }, - }, - }).Build(), - validateAddonActions: func(t *testing.T, actions []clienttesting.Action) { - addontesting.AssertActions(t, actions, "patch") - expectPatchConfigurationAction(t, actions[0], []addonv1alpha1.ConfigReference{{ - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "hash1new", - }, - LastObservedGeneration: 1, - }}) - }, - }, - { - name: "mca noop", - managedClusteraddon: []runtime.Object{ - newManagedClusterAddon("test", "cluster1", []addonv1alpha1.AddOnConfig{}, []addonv1alpha1.ConfigReference{{ - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "hash1", - }, - LastObservedGeneration: 1, - }}, nil), - }, - placements: []runtime.Object{ - &clusterv1beta1.Placement{ObjectMeta: metav1.ObjectMeta{Name: "test-placement", Namespace: "default"}}, - }, - placementDecisions: []runtime.Object{ - &clusterv1beta1.PlacementDecision{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-placement", - Namespace: "default", - Labels: map[string]string{ - clusterv1beta1.PlacementLabel: "test-placement", - clusterv1beta1.DecisionGroupIndexLabel: "0", - }, - }, - Status: clusterv1beta1.PlacementDecisionStatus{ - Decisions: []clusterv1beta1.ClusterDecision{{ClusterName: "cluster1"}}, - }, - }, - }, - clusterManagementAddon: addontesting.NewClusterManagementAddon("test", "", "").WithSupportedConfigs(addonv1alpha1.ConfigMeta{ - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - DefaultConfig: &addonv1alpha1.ConfigReferent{Name: "test"}, - }).WithPlacementStrategy(addonv1alpha1.PlacementStrategy{ - PlacementRef: addonv1alpha1.PlacementRef{Name: "test-placement", Namespace: "default"}, - Configs: []addonv1alpha1.AddOnConfig{v1alpha1.AddOnConfig{ - ConfigGroupResource: v1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - ConfigReferent: v1alpha1.ConfigReferent{Name: "test1"}}}, - RolloutStrategy: clusterv1alpha1.RolloutStrategy{Type: clusterv1alpha1.All}, - }).WithInstallProgression(addonv1alpha1.InstallProgression{ - PlacementRef: addonv1alpha1.PlacementRef{Name: "test-placement", Namespace: "default"}, - ConfigReferences: []addonv1alpha1.InstallConfigReference{ - { - ConfigGroupResource: v1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - DesiredConfig: &v1alpha1.ConfigSpecHash{ - ConfigReferent: v1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "hash1", - }, - }, - }, - }).Build(), - validateAddonActions: addontesting.AssertNoActions, - }, - { - name: "placement rollout progressive with MaxConcurrency 1", - managedClusteraddon: []runtime.Object{ - addontesting.NewAddon("test", "cluster1"), - addontesting.NewAddon("test", "cluster2"), - addontesting.NewAddon("test", "cluster3"), - }, - placements: []runtime.Object{ - &clusterv1beta1.Placement{ObjectMeta: metav1.ObjectMeta{Name: "test-placement", Namespace: "default"}}, - }, - placementDecisions: []runtime.Object{ - &clusterv1beta1.PlacementDecision{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-placement", - Namespace: "default", - Labels: map[string]string{ - clusterv1beta1.PlacementLabel: "test-placement", - clusterv1beta1.DecisionGroupIndexLabel: "0", - }, - }, - Status: clusterv1beta1.PlacementDecisionStatus{ - Decisions: []clusterv1beta1.ClusterDecision{{ClusterName: "cluster2"}, {ClusterName: "cluster3"}}, - }, - }, - }, - clusterManagementAddon: addontesting.NewClusterManagementAddon("test", "", "").WithPlacementStrategy(addonv1alpha1.PlacementStrategy{ - PlacementRef: addonv1alpha1.PlacementRef{Name: "test-placement", Namespace: "default"}, - RolloutStrategy: clusterv1alpha1.RolloutStrategy{ - Type: clusterv1alpha1.Progressive, - Progressive: &clusterv1alpha1.RolloutProgressive{MaxConcurrency: intstr.FromInt(1)}}, - }).WithInstallProgression(addonv1alpha1.InstallProgression{ - PlacementRef: addonv1alpha1.PlacementRef{Name: "test-placement", Namespace: "default"}, - ConfigReferences: []addonv1alpha1.InstallConfigReference{ - { - ConfigGroupResource: v1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - DesiredConfig: &v1alpha1.ConfigSpecHash{ - ConfigReferent: v1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "hash1", - }, - }, - }, - }).Build(), - validateAddonActions: func(t *testing.T, actions []clienttesting.Action) { - addontesting.AssertActions(t, actions, "patch") - sort.Sort(byPatchName(actions)) - expectPatchConfigurationAction(t, actions[0], []addonv1alpha1.ConfigReference{{ - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "hash1", - }, - LastObservedGeneration: 0, - }}) - }, - }, - { - name: "placement rollout progressive with MaxConcurrency 50%", - managedClusteraddon: []runtime.Object{ - addontesting.NewAddon("test", "cluster1"), - addontesting.NewAddon("test", "cluster2"), - addontesting.NewAddon("test", "cluster3"), - }, - placements: []runtime.Object{ - &clusterv1beta1.Placement{ObjectMeta: metav1.ObjectMeta{Name: "test-placement", Namespace: "default"}}, - }, - placementDecisions: []runtime.Object{ - &clusterv1beta1.PlacementDecision{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-placement", - Namespace: "default", - Labels: map[string]string{ - clusterv1beta1.PlacementLabel: "test-placement", - clusterv1beta1.DecisionGroupIndexLabel: "0", - }, - }, - Status: clusterv1beta1.PlacementDecisionStatus{ - Decisions: []clusterv1beta1.ClusterDecision{{ClusterName: "cluster2"}, {ClusterName: "cluster3"}}, - }, - }, - }, - clusterManagementAddon: addontesting.NewClusterManagementAddon("test", "", "").WithPlacementStrategy(addonv1alpha1.PlacementStrategy{ - PlacementRef: addonv1alpha1.PlacementRef{Name: "test-placement", Namespace: "default"}, - RolloutStrategy: clusterv1alpha1.RolloutStrategy{ - Type: clusterv1alpha1.Progressive, - Progressive: &clusterv1alpha1.RolloutProgressive{MaxConcurrency: intstr.FromString("50%")}}, - }).WithInstallProgression(addonv1alpha1.InstallProgression{ - PlacementRef: addonv1alpha1.PlacementRef{Name: "test-placement", Namespace: "default"}, - ConfigReferences: []addonv1alpha1.InstallConfigReference{ - { - ConfigGroupResource: v1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - DesiredConfig: &v1alpha1.ConfigSpecHash{ - ConfigReferent: v1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "hash1", - }, - }, - }, - }).Build(), - validateAddonActions: func(t *testing.T, actions []clienttesting.Action) { - addontesting.AssertActions(t, actions, "patch") - sort.Sort(byPatchName(actions)) - expectPatchConfigurationAction(t, actions[0], []addonv1alpha1.ConfigReference{{ - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "hash1", - }, - LastObservedGeneration: 0, - }}) - }, - }, - { - name: "placement rollout progressive with default MaxConcurrency 100%", - managedClusteraddon: []runtime.Object{ - addontesting.NewAddon("test", "cluster1"), - addontesting.NewAddon("test", "cluster2"), - addontesting.NewAddon("test", "cluster3"), - }, - placements: []runtime.Object{ - &clusterv1beta1.Placement{ObjectMeta: metav1.ObjectMeta{Name: "test-placement", Namespace: "default"}}, - }, - placementDecisions: []runtime.Object{ - &clusterv1beta1.PlacementDecision{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-placement", - Namespace: "default", - Labels: map[string]string{ - clusterv1beta1.PlacementLabel: "test-placement", - clusterv1beta1.DecisionGroupIndexLabel: "0", - }, - }, - Status: clusterv1beta1.PlacementDecisionStatus{ - Decisions: []clusterv1beta1.ClusterDecision{{ClusterName: "cluster2"}, {ClusterName: "cluster3"}}, - }, - }, - }, - clusterManagementAddon: addontesting.NewClusterManagementAddon("test", "", "").WithPlacementStrategy(addonv1alpha1.PlacementStrategy{ - PlacementRef: addonv1alpha1.PlacementRef{Name: "test-placement", Namespace: "default"}, - RolloutStrategy: clusterv1alpha1.RolloutStrategy{ - Type: clusterv1alpha1.Progressive, - Progressive: &clusterv1alpha1.RolloutProgressive{}}, - }).WithInstallProgression(addonv1alpha1.InstallProgression{ - PlacementRef: addonv1alpha1.PlacementRef{Name: "test-placement", Namespace: "default"}, - ConfigReferences: []addonv1alpha1.InstallConfigReference{ - { - ConfigGroupResource: v1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - DesiredConfig: &v1alpha1.ConfigSpecHash{ - ConfigReferent: v1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "hash1", - }, - }, - }, - }).Build(), - validateAddonActions: func(t *testing.T, actions []clienttesting.Action) { - addontesting.AssertActions(t, actions, "patch", "patch") - sort.Sort(byPatchName(actions)) - expectPatchConfigurationAction(t, actions[0], []addonv1alpha1.ConfigReference{{ - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "hash1", - }, - LastObservedGeneration: 0, - }}) - expectPatchConfigurationAction(t, actions[1], []addonv1alpha1.ConfigReference{{ - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "hash1", - }, - LastObservedGeneration: 0, - }}) - }, - }, - { - name: "placement rollout progressive with mandatory decision groups", - managedClusteraddon: []runtime.Object{ - addontesting.NewAddon("test", "cluster1"), - addontesting.NewAddon("test", "cluster2"), - addontesting.NewAddon("test", "cluster3"), - }, - placements: []runtime.Object{ - &clusterv1beta1.Placement{ObjectMeta: metav1.ObjectMeta{Name: "test-placement", Namespace: "default"}}, - }, - placementDecisions: []runtime.Object{ - &clusterv1beta1.PlacementDecision{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-placement-0", - Namespace: "default", - Labels: map[string]string{ - clusterv1beta1.PlacementLabel: "test-placement", - clusterv1beta1.DecisionGroupNameLabel: "group1", - clusterv1beta1.DecisionGroupIndexLabel: "0", - }, - }, - Status: clusterv1beta1.PlacementDecisionStatus{ - Decisions: []clusterv1beta1.ClusterDecision{{ClusterName: "cluster1"}, {ClusterName: "cluster2"}}, - }, - }, - &clusterv1beta1.PlacementDecision{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-placement-1", - Namespace: "default", - Labels: map[string]string{ - clusterv1beta1.PlacementLabel: "test-placement", - clusterv1beta1.DecisionGroupIndexLabel: "1", - }, - }, - Status: clusterv1beta1.PlacementDecisionStatus{ - Decisions: []clusterv1beta1.ClusterDecision{{ClusterName: "cluster3"}}, - }, - }, - }, - clusterManagementAddon: addontesting.NewClusterManagementAddon("test", "", "").WithPlacementStrategy( - addonv1alpha1.PlacementStrategy{ - PlacementRef: addonv1alpha1.PlacementRef{Name: "test-placement", Namespace: "default"}, - RolloutStrategy: clusterv1alpha1.RolloutStrategy{ - Type: clusterv1alpha1.Progressive, - Progressive: &clusterv1alpha1.RolloutProgressive{ - MandatoryDecisionGroups: clusterv1alpha1.MandatoryDecisionGroups{ - MandatoryDecisionGroups: []clusterv1alpha1.MandatoryDecisionGroup{ - {GroupName: "group1"}, - }, - }, - }, - }}).WithInstallProgression(addonv1alpha1.InstallProgression{ - PlacementRef: addonv1alpha1.PlacementRef{Name: "test-placement", Namespace: "default"}, - ConfigReferences: []addonv1alpha1.InstallConfigReference{ - { - ConfigGroupResource: v1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - DesiredConfig: &v1alpha1.ConfigSpecHash{ - ConfigReferent: v1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "hash1", - }, - }, - }, - }).Build(), - validateAddonActions: func(t *testing.T, actions []clienttesting.Action) { - addontesting.AssertActions(t, actions, "patch", "patch") - sort.Sort(byPatchName(actions)) - expectPatchConfigurationAction(t, actions[0], []addonv1alpha1.ConfigReference{{ - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "hash1", - }, - LastObservedGeneration: 0, - }}) - expectPatchConfigurationAction(t, actions[1], []addonv1alpha1.ConfigReference{{ - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "hash1", - }, - LastObservedGeneration: 0, - }}) - }, - }, - { - name: "placement rollout progressive per group", - managedClusteraddon: []runtime.Object{ - addontesting.NewAddon("test", "cluster1"), - addontesting.NewAddon("test", "cluster2"), - addontesting.NewAddon("test", "cluster3"), - }, - placements: []runtime.Object{ - &clusterv1beta1.Placement{ObjectMeta: metav1.ObjectMeta{Name: "test-placement", Namespace: "default"}}, - }, - placementDecisions: []runtime.Object{ - &clusterv1beta1.PlacementDecision{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-placement-0", - Namespace: "default", - Labels: map[string]string{ - clusterv1beta1.PlacementLabel: "test-placement", - clusterv1beta1.DecisionGroupIndexLabel: "0", - }, - }, - Status: clusterv1beta1.PlacementDecisionStatus{ - Decisions: []clusterv1beta1.ClusterDecision{{ClusterName: "cluster1"}, {ClusterName: "cluster2"}}, - }, - }, - &clusterv1beta1.PlacementDecision{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-placement-1", - Namespace: "default", - Labels: map[string]string{ - clusterv1beta1.PlacementLabel: "test-placement", - clusterv1beta1.DecisionGroupIndexLabel: "1", - }, - }, - Status: clusterv1beta1.PlacementDecisionStatus{ - Decisions: []clusterv1beta1.ClusterDecision{{ClusterName: "cluster3"}}, - }, - }, - }, - clusterManagementAddon: addontesting.NewClusterManagementAddon("test", "", "").WithPlacementStrategy( - addonv1alpha1.PlacementStrategy{ - PlacementRef: addonv1alpha1.PlacementRef{Name: "test-placement", Namespace: "default"}, - RolloutStrategy: clusterv1alpha1.RolloutStrategy{ - Type: clusterv1alpha1.ProgressivePerGroup, - }}).WithInstallProgression(addonv1alpha1.InstallProgression{ - PlacementRef: addonv1alpha1.PlacementRef{Name: "test-placement", Namespace: "default"}, - ConfigReferences: []addonv1alpha1.InstallConfigReference{ - { - ConfigGroupResource: v1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - DesiredConfig: &v1alpha1.ConfigSpecHash{ - ConfigReferent: v1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "hash1", - }, - }, - }, - }).Build(), - validateAddonActions: func(t *testing.T, actions []clienttesting.Action) { - addontesting.AssertActions(t, actions, "patch", "patch") - sort.Sort(byPatchName(actions)) - expectPatchConfigurationAction(t, actions[0], []addonv1alpha1.ConfigReference{{ - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "hash1", - }, - LastObservedGeneration: 0, - }}) - expectPatchConfigurationAction(t, actions[1], []addonv1alpha1.ConfigReference{{ - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "hash1", - }, - LastObservedGeneration: 0, - }}) - }, - }, - { - name: "placement rollout progressive per group with mandatory decision groups", - managedClusteraddon: []runtime.Object{ - addontesting.NewAddon("test", "cluster1"), - addontesting.NewAddon("test", "cluster2"), - addontesting.NewAddon("test", "cluster3"), - }, - placements: []runtime.Object{ - &clusterv1beta1.Placement{ObjectMeta: metav1.ObjectMeta{Name: "test-placement", Namespace: "default"}}, - }, - placementDecisions: []runtime.Object{ - &clusterv1beta1.PlacementDecision{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-placement-0", - Namespace: "default", - Labels: map[string]string{ - clusterv1beta1.PlacementLabel: "test-placement", - clusterv1beta1.DecisionGroupNameLabel: "group1", - clusterv1beta1.DecisionGroupIndexLabel: "0", - }, - }, - Status: clusterv1beta1.PlacementDecisionStatus{ - Decisions: []clusterv1beta1.ClusterDecision{{ClusterName: "cluster1"}, {ClusterName: "cluster2"}}, - }, - }, - &clusterv1beta1.PlacementDecision{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-placement-1", - Namespace: "default", - Labels: map[string]string{ - clusterv1beta1.PlacementLabel: "test-placement", - clusterv1beta1.DecisionGroupIndexLabel: "1", - }, - }, - Status: clusterv1beta1.PlacementDecisionStatus{ - Decisions: []clusterv1beta1.ClusterDecision{{ClusterName: "cluster3"}}, - }, - }, - }, - clusterManagementAddon: addontesting.NewClusterManagementAddon("test", "", "").WithPlacementStrategy( - addonv1alpha1.PlacementStrategy{ - PlacementRef: addonv1alpha1.PlacementRef{Name: "test-placement", Namespace: "default"}, - RolloutStrategy: clusterv1alpha1.RolloutStrategy{ - Type: clusterv1alpha1.ProgressivePerGroup, - ProgressivePerGroup: &clusterv1alpha1.RolloutProgressivePerGroup{ - MandatoryDecisionGroups: clusterv1alpha1.MandatoryDecisionGroups{ - MandatoryDecisionGroups: []clusterv1alpha1.MandatoryDecisionGroup{ - {GroupName: "group1"}, - }, - }, - }, - }}).WithInstallProgression(addonv1alpha1.InstallProgression{ - PlacementRef: addonv1alpha1.PlacementRef{Name: "test-placement", Namespace: "default"}, - ConfigReferences: []addonv1alpha1.InstallConfigReference{ - { - ConfigGroupResource: v1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - DesiredConfig: &v1alpha1.ConfigSpecHash{ - ConfigReferent: v1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "hash1", - }, - }, - }, - }).Build(), - validateAddonActions: func(t *testing.T, actions []clienttesting.Action) { - addontesting.AssertActions(t, actions, "patch", "patch") - sort.Sort(byPatchName(actions)) - expectPatchConfigurationAction(t, actions[0], []addonv1alpha1.ConfigReference{{ - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "hash1", - }, - LastObservedGeneration: 0, - }}) - expectPatchConfigurationAction(t, actions[1], []addonv1alpha1.ConfigReference{{ - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "hash1", - }, - LastObservedGeneration: 0, - }}) - }, - }, - } - - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - clusterObj := append(c.placements, c.placementDecisions...) - fakeClusterClient := fakecluster.NewSimpleClientset(clusterObj...) - fakeAddonClient := fakeaddon.NewSimpleClientset(c.managedClusteraddon...) - - addonInformers := addoninformers.NewSharedInformerFactory(fakeAddonClient, 10*time.Minute) - clusterInformers := clusterv1informers.NewSharedInformerFactory(fakeClusterClient, 10*time.Minute) - - err := addonInformers.Addon().V1alpha1().ManagedClusterAddOns().Informer().AddIndexers( - cache.Indexers{ - index.ManagedClusterAddonByName: index.IndexManagedClusterAddonByName, - }) - if err != nil { - t.Fatal(err) - } - - for _, obj := range c.placements { - if err := clusterInformers.Cluster().V1beta1().Placements().Informer().GetStore().Add(obj); err != nil { - t.Fatal(err) - } - } - - for _, obj := range c.placementDecisions { - if err := clusterInformers.Cluster().V1beta1().PlacementDecisions().Informer().GetStore().Add(obj); err != nil { - t.Fatal(err) - } - } - - for _, obj := range c.managedClusteraddon { - if err := addonInformers.Addon().V1alpha1().ManagedClusterAddOns().Informer().GetStore().Add(obj); err != nil { - t.Fatal(err) - } - } - - controller := &addonConfigurationController{ - addonClient: fakeAddonClient, - placementDecisionGetter: PlacementDecisionGetter{Client: clusterInformers.Cluster().V1beta1().PlacementDecisions().Lister()}, - placementLister: clusterInformers.Cluster().V1beta1().Placements().Lister(), - clusterManagementAddonLister: addonInformers.Addon().V1alpha1().ClusterManagementAddOns().Lister(), - managedClusterAddonIndexer: addonInformers.Addon().V1alpha1().ManagedClusterAddOns().Informer().GetIndexer(), - } - - reconcile := &managedClusterAddonConfigurationReconciler{ - addonClient: fakeAddonClient, - } - - graph, err := controller.buildConfigurationGraph(c.clusterManagementAddon) - if err != nil { - t.Errorf("expected no error when build graph: %v", err) - } - err = graph.generateRolloutResult() - if err != nil { - t.Errorf("expected no error when refresh rollout result: %v", err) - } - - _, _, err = reconcile.reconcile(context.TODO(), c.clusterManagementAddon, graph) - if err != nil && !c.expectErr { - t.Errorf("expected no error when sync: %v", err) - } - if err == nil && c.expectErr { - t.Errorf("Expect error but got no error") - } - - c.validateAddonActions(t, fakeAddonClient.Actions()) - }) - } -} - -// the Age field. -type byPatchName []clienttesting.Action - -func (a byPatchName) Len() int { return len(a) } -func (a byPatchName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } -func (a byPatchName) Less(i, j int) bool { - patchi := a[i].(clienttesting.PatchActionImpl) - patchj := a[j].(clienttesting.PatchActionImpl) - return patchi.Namespace < patchj.Namespace -} - -func newManagedClusterAddon( - name, namespace string, - configs []addonv1alpha1.AddOnConfig, - configStatus []addonv1alpha1.ConfigReference, - conditions []metav1.Condition, -) *addonv1alpha1.ManagedClusterAddOn { - mca := addontesting.NewAddon(name, namespace) - mca.Spec.Configs = configs - mca.Status.ConfigReferences = configStatus - mca.Status.Conditions = conditions - return mca -} - -func expectPatchConfigurationAction(t *testing.T, action clienttesting.Action, expected []addonv1alpha1.ConfigReference) { - patch := action.(clienttesting.PatchActionImpl).GetPatch() - mca := &addonv1alpha1.ManagedClusterAddOn{} - err := json.Unmarshal(patch, mca) - if err != nil { - t.Fatal(err) - } - - if !apiequality.Semantic.DeepEqual(mca.Status.ConfigReferences, expected) { - t.Errorf("Configuration not correctly patched, expected %v, actual %v", expected, mca.Status.ConfigReferences) - } -} diff --git a/pkg/manager/controllers/addonconfiguration/controller.go b/pkg/manager/controllers/addonconfiguration/controller.go deleted file mode 100644 index a51c304c9..000000000 --- a/pkg/manager/controllers/addonconfiguration/controller.go +++ /dev/null @@ -1,196 +0,0 @@ -package addonconfiguration - -import ( - "context" - - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/runtime" - utilerrors "k8s.io/apimachinery/pkg/util/errors" - "k8s.io/client-go/tools/cache" - "k8s.io/klog/v2" - addonv1alpha1 "open-cluster-management.io/api/addon/v1alpha1" - addonv1alpha1client "open-cluster-management.io/api/client/addon/clientset/versioned" - addoninformerv1alpha1 "open-cluster-management.io/api/client/addon/informers/externalversions/addon/v1alpha1" - addonlisterv1alpha1 "open-cluster-management.io/api/client/addon/listers/addon/v1alpha1" - clusterinformersv1beta1 "open-cluster-management.io/api/client/cluster/informers/externalversions/cluster/v1beta1" - clusterlister "open-cluster-management.io/api/client/cluster/listers/cluster/v1beta1" - clusterlisterv1beta1 "open-cluster-management.io/api/client/cluster/listers/cluster/v1beta1" - clusterv1beta1 "open-cluster-management.io/api/cluster/v1beta1" - - "open-cluster-management.io/addon-framework/pkg/basecontroller/factory" - "open-cluster-management.io/addon-framework/pkg/index" -) - -// addonConfigurationController is a controller to update configuration of mca with the following order -// 1. use configuration in mca spec if it is set -// 2. use configuration in install strategy -// 3. use configuration in the default configuration in cma -type addonConfigurationController struct { - addonClient addonv1alpha1client.Interface - clusterManagementAddonLister addonlisterv1alpha1.ClusterManagementAddOnLister - managedClusterAddonIndexer cache.Indexer - addonFilterFunc factory.EventFilterFunc - placementLister clusterlisterv1beta1.PlacementLister - placementDecisionLister clusterlisterv1beta1.PlacementDecisionLister - placementDecisionGetter PlacementDecisionGetter - - reconcilers []addonConfigurationReconcile -} - -type addonConfigurationReconcile interface { - reconcile(ctx context.Context, cma *addonv1alpha1.ClusterManagementAddOn, - graph *configurationGraph) (*addonv1alpha1.ClusterManagementAddOn, reconcileState, error) -} - -type reconcileState int64 - -const ( - reconcileStop reconcileState = iota - reconcileContinue -) - -func NewAddonConfigurationController( - addonClient addonv1alpha1client.Interface, - addonInformers addoninformerv1alpha1.ManagedClusterAddOnInformer, - clusterManagementAddonInformers addoninformerv1alpha1.ClusterManagementAddOnInformer, - placementInformer clusterinformersv1beta1.PlacementInformer, - placementDecisionInformer clusterinformersv1beta1.PlacementDecisionInformer, - addonFilterFunc factory.EventFilterFunc, -) factory.Controller { - c := &addonConfigurationController{ - addonClient: addonClient, - clusterManagementAddonLister: clusterManagementAddonInformers.Lister(), - managedClusterAddonIndexer: addonInformers.Informer().GetIndexer(), - addonFilterFunc: addonFilterFunc, - } - - c.reconcilers = []addonConfigurationReconcile{ - &managedClusterAddonConfigurationReconciler{ - addonClient: addonClient, - }, - &clusterManagementAddonProgressingReconciler{ - addonClient: addonClient, - }, - } - - controllerFactory := factory.New().WithFilteredEventsInformersQueueKeysFunc( - func(obj runtime.Object) []string { - key, _ := cache.DeletionHandlingMetaNamespaceKeyFunc(obj) - return []string{key} - }, - c.addonFilterFunc, - clusterManagementAddonInformers.Informer()).WithInformersQueueKeysFunc( - func(obj runtime.Object) []string { - key, _ := cache.DeletionHandlingMetaNamespaceKeyFunc(obj) - return []string{key} - }, - addonInformers.Informer()) - - // This is to handle the case the self managed addon-manager does not have placementInformer/placementDecisionInformer. - // we will not consider installStrategy related placement for self managed addon-manager. - if placementInformer != nil && placementDecisionInformer != nil { - controllerFactory = controllerFactory.WithInformersQueueKeysFunc( - index.ClusterManagementAddonByPlacementDecisionQueueKey(clusterManagementAddonInformers), placementDecisionInformer.Informer()). - WithInformersQueueKeysFunc(index.ClusterManagementAddonByPlacementQueueKey(clusterManagementAddonInformers), placementInformer.Informer()) - c.placementLister = placementInformer.Lister() - c.placementDecisionLister = placementDecisionInformer.Lister() - c.placementDecisionGetter = PlacementDecisionGetter{Client: placementDecisionInformer.Lister()} - } - - return controllerFactory.WithSync(c.sync).ToController("addon-configuration-controller") -} - -func (c *addonConfigurationController) sync(ctx context.Context, syncCtx factory.SyncContext, key string) error { - _, addonName, err := cache.SplitMetaNamespaceKey(key) - if err != nil { - // ignore addon whose key is invalid - return nil - } - - klog.V(4).Infof("Reconciling addon %q", addonName) - - cma, err := c.clusterManagementAddonLister.Get(addonName) - switch { - case errors.IsNotFound(err): - return nil - case err != nil: - return err - } - - if !c.addonFilterFunc(cma) { - return nil - } - - cma = cma.DeepCopy() - graph, err := c.buildConfigurationGraph(cma) - if err != nil { - return err - } - - // generate the rollout result before calling reconcile() - // so that all the reconcilers are using the same rollout result - err = graph.generateRolloutResult() - if err != nil { - return err - } - - var state reconcileState - var errs []error - for _, reconciler := range c.reconcilers { - cma, state, err = reconciler.reconcile(ctx, cma, graph) - if err != nil { - errs = append(errs, err) - } - if state == reconcileStop { - break - } - } - - return utilerrors.NewAggregate(errs) -} - -func (c *addonConfigurationController) buildConfigurationGraph(cma *addonv1alpha1.ClusterManagementAddOn) (*configurationGraph, error) { - graph := newGraph(cma.Spec.SupportedConfigs, cma.Status.DefaultConfigReferences) - addons, err := c.managedClusterAddonIndexer.ByIndex(index.ManagedClusterAddonByName, cma.Name) - if err != nil { - return graph, err - } - - // add all existing addons to the default at first - for _, addonObject := range addons { - addon := addonObject.(*addonv1alpha1.ManagedClusterAddOn) - graph.addAddonNode(addon) - } - - if cma.Spec.InstallStrategy.Type == "" || cma.Spec.InstallStrategy.Type == addonv1alpha1.AddonInstallStrategyManual { - return graph, nil - } - - // check each install strategy in status - var errs []error - for _, installProgression := range cma.Status.InstallProgressions { - for _, installStrategy := range cma.Spec.InstallStrategy.Placements { - if installStrategy.PlacementRef != installProgression.PlacementRef { - continue - } - - // add placement node - err = graph.addPlacementNode(installStrategy, installProgression, c.placementLister, c.placementDecisionGetter) - if err != nil { - errs = append(errs, err) - continue - } - } - } - - return graph, utilerrors.NewAggregate(errs) -} - -type PlacementDecisionGetter struct { - Client clusterlister.PlacementDecisionLister -} - -func (pdl PlacementDecisionGetter) List(selector labels.Selector, namespace string) ([]*clusterv1beta1.PlacementDecision, error) { - return pdl.Client.PlacementDecisions(namespace).List(selector) -} diff --git a/pkg/manager/controllers/addonconfiguration/graph.go b/pkg/manager/controllers/addonconfiguration/graph.go deleted file mode 100644 index d0b381e25..000000000 --- a/pkg/manager/controllers/addonconfiguration/graph.go +++ /dev/null @@ -1,417 +0,0 @@ -package addonconfiguration - -import ( - "fmt" - "sort" - - "k8s.io/apimachinery/pkg/api/equality" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/sets" - - addonv1alpha1 "open-cluster-management.io/api/addon/v1alpha1" - clusterlisterv1beta1 "open-cluster-management.io/api/client/cluster/listers/cluster/v1beta1" - clusterv1alpha1 "open-cluster-management.io/api/cluster/v1alpha1" - clusterv1sdkalpha1 "open-cluster-management.io/sdk-go/pkg/apis/cluster/v1alpha1" - clustersdkv1beta1 "open-cluster-management.io/sdk-go/pkg/apis/cluster/v1beta1" -) - -// configurationTree is a 2 level snapshot tree on the configuration of addons -// the first level is a list of nodes that represents a install strategy and a desired configuration for this install -// strategy. The second level is a list of nodes that represent each mca and its desired configuration -type configurationGraph struct { - // nodes maintains a list between a installStrategy and its related mcas - nodes []*installStrategyNode - // defaults is the nodes with no install strategy - defaults *installStrategyNode -} - -// installStrategyNode is a node in configurationGraph defined by a install strategy -type installStrategyNode struct { - placementRef addonv1alpha1.PlacementRef - pdTracker *clustersdkv1beta1.PlacementDecisionClustersTracker - rolloutStrategy clusterv1alpha1.RolloutStrategy - rolloutResult clusterv1sdkalpha1.RolloutResult - desiredConfigs addonConfigMap - // children keeps a map of addons node as the children of this node - children map[string]*addonNode - clusters sets.Set[string] -} - -// addonNode is node as a child of installStrategy node represting a mca -// addonnode -type addonNode struct { - desiredConfigs addonConfigMap - mca *addonv1alpha1.ManagedClusterAddOn - status *clusterv1sdkalpha1.ClusterRolloutStatus -} - -type addonConfigMap map[addonv1alpha1.ConfigGroupResource]addonv1alpha1.ConfigReference - -// set addon rollout status -func (n *addonNode) setRolloutStatus() { - n.status = &clusterv1sdkalpha1.ClusterRolloutStatus{ClusterName: n.mca.Namespace} - - // desired configs doesn't match actual configs, set to ToApply - if len(n.mca.Status.ConfigReferences) != len(n.desiredConfigs) { - n.status.Status = clusterv1sdkalpha1.ToApply - return - } - - var progressingCond metav1.Condition - for _, cond := range n.mca.Status.Conditions { - if cond.Type == addonv1alpha1.ManagedClusterAddOnConditionProgressing { - progressingCond = cond - break - } - } - - for _, actual := range n.mca.Status.ConfigReferences { - if desired, ok := n.desiredConfigs[actual.ConfigGroupResource]; ok { - // desired config spec hash doesn't match actual, set to ToApply - if !equality.Semantic.DeepEqual(desired.DesiredConfig, actual.DesiredConfig) { - n.status.Status = clusterv1sdkalpha1.ToApply - return - // desired config spec hash matches actual, but last applied config spec hash doesn't match actual - } else if !equality.Semantic.DeepEqual(actual.LastAppliedConfig, actual.DesiredConfig) { - switch progressingCond.Reason { - case addonv1alpha1.ProgressingReasonInstallFailed, addonv1alpha1.ProgressingReasonUpgradeFailed: - n.status.Status = clusterv1sdkalpha1.Failed - n.status.LastTransitionTime = &progressingCond.LastTransitionTime - case addonv1alpha1.ProgressingReasonInstalling, addonv1alpha1.ProgressingReasonUpgrading: - n.status.Status = clusterv1sdkalpha1.Progressing - n.status.LastTransitionTime = &progressingCond.LastTransitionTime - default: - n.status.Status = clusterv1sdkalpha1.Progressing - } - return - } - } else { - n.status.Status = clusterv1sdkalpha1.ToApply - return - } - } - - // succeed - n.status.Status = clusterv1sdkalpha1.Succeeded - if progressingCond.Reason == addonv1alpha1.ProgressingReasonInstallSucceed || progressingCond.Reason == addonv1alpha1.ProgressingReasonUpgradeSucceed { - n.status.LastTransitionTime = &progressingCond.LastTransitionTime - } -} - -func (d addonConfigMap) copy() addonConfigMap { - output := addonConfigMap{} - for k, v := range d { - output[k] = v - } - return output -} - -func newGraph(supportedConfigs []addonv1alpha1.ConfigMeta, defaultConfigReferences []addonv1alpha1.DefaultConfigReference) *configurationGraph { - graph := &configurationGraph{ - nodes: []*installStrategyNode{}, - defaults: &installStrategyNode{ - desiredConfigs: map[addonv1alpha1.ConfigGroupResource]addonv1alpha1.ConfigReference{}, - children: map[string]*addonNode{}, - }, - } - - // init graph.defaults.desiredConfigs with supportedConfigs - for _, config := range supportedConfigs { - if config.DefaultConfig != nil { - graph.defaults.desiredConfigs[config.ConfigGroupResource] = addonv1alpha1.ConfigReference{ - ConfigGroupResource: config.ConfigGroupResource, - ConfigReferent: *config.DefaultConfig, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: *config.DefaultConfig, - }, - } - } - } - // copy the spechash from cma status defaultConfigReferences - for _, configRef := range defaultConfigReferences { - if configRef.DesiredConfig == nil { - continue - } - defaultsDesiredConfig, ok := graph.defaults.desiredConfigs[configRef.ConfigGroupResource] - if ok && (defaultsDesiredConfig.DesiredConfig.ConfigReferent == configRef.DesiredConfig.ConfigReferent) { - defaultsDesiredConfig.DesiredConfig.SpecHash = configRef.DesiredConfig.SpecHash - } - } - - return graph -} - -// addAddonNode to the graph, starting from placement with the highest order -func (g *configurationGraph) addAddonNode(mca *addonv1alpha1.ManagedClusterAddOn) { - for i := len(g.nodes) - 1; i >= 0; i-- { - if g.nodes[i].clusters.Has(mca.Namespace) { - g.nodes[i].addNode(mca) - return - } - } - - g.defaults.addNode(mca) -} - -// addNode delete clusters on existing graph so the new configuration overrides the previous -func (g *configurationGraph) addPlacementNode( - installStrategy addonv1alpha1.PlacementStrategy, - installProgression addonv1alpha1.InstallProgression, - placementLister clusterlisterv1beta1.PlacementLister, - placementDecisionGetter PlacementDecisionGetter, -) error { - placementRef := installProgression.PlacementRef - installConfigReference := installProgression.ConfigReferences - - // get placement - if placementLister == nil { - return fmt.Errorf("invalid placement lister %v", placementLister) - } - placement, err := placementLister.Placements(placementRef.Namespace).Get(placementRef.Name) - if err != nil { - return err - } - - // new decision tracker - pdTracker := clustersdkv1beta1.NewPlacementDecisionClustersTracker(placement, placementDecisionGetter, nil) - - // refresh and get existing decision clusters - err = pdTracker.Refresh() - if err != nil { - return err - } - clusters := pdTracker.ExistingClusterGroupsBesides().GetClusters() - - node := &installStrategyNode{ - placementRef: placementRef, - pdTracker: pdTracker, - rolloutStrategy: installStrategy.RolloutStrategy, - desiredConfigs: g.defaults.desiredConfigs, - children: map[string]*addonNode{}, - clusters: clusters, - } - - // Set MaxConcurrency - // If progressive strategy is not initialized or MaxConcurrency is not specified, set MaxConcurrency to the default value - if node.rolloutStrategy.Type == clusterv1alpha1.Progressive { - progressiveStrategy := node.rolloutStrategy.Progressive - - if progressiveStrategy == nil { - progressiveStrategy = &clusterv1alpha1.RolloutProgressive{} - } - if progressiveStrategy.MaxConcurrency.StrVal == "" && progressiveStrategy.MaxConcurrency.IntVal == 0 { - progressiveStrategy.MaxConcurrency = placement.Spec.DecisionStrategy.GroupStrategy.ClustersPerDecisionGroup - } - - node.rolloutStrategy.Progressive = progressiveStrategy - } - - // overrides configuration by install strategy - if len(installConfigReference) > 0 { - node.desiredConfigs = node.desiredConfigs.copy() - for _, configRef := range installConfigReference { - if configRef.DesiredConfig == nil { - continue - } - node.desiredConfigs[configRef.ConfigGroupResource] = addonv1alpha1.ConfigReference{ - ConfigGroupResource: configRef.ConfigGroupResource, - ConfigReferent: configRef.DesiredConfig.ConfigReferent, - DesiredConfig: configRef.DesiredConfig.DeepCopy(), - } - } - } - - // remove addon in defaults and other placements. - for _, cluster := range node.clusters.UnsortedList() { - if _, ok := g.defaults.children[cluster]; ok { - node.addNode(g.defaults.children[cluster].mca) - delete(g.defaults.children, cluster) - } - for _, placementNode := range g.nodes { - if _, ok := placementNode.children[cluster]; ok { - node.addNode(placementNode.children[cluster].mca) - delete(placementNode.children, cluster) - } - } - } - g.nodes = append(g.nodes, node) - return nil -} - -func (g *configurationGraph) generateRolloutResult() error { - for _, node := range g.nodes { - if err := node.generateRolloutResult(); err != nil { - return err - } - } - if err := g.defaults.generateRolloutResult(); err != nil { - return err - } - return nil -} - -func (g *configurationGraph) getPlacementNodes() map[addonv1alpha1.PlacementRef]*installStrategyNode { - placementNodeMap := map[addonv1alpha1.PlacementRef]*installStrategyNode{} - for _, node := range g.nodes { - placementNodeMap[node.placementRef] = node - } - - return placementNodeMap -} - -func (g *configurationGraph) getAddonsToUpdate() []*addonNode { - var addons []*addonNode - for _, node := range g.nodes { - addons = append(addons, node.getAddonsToUpdate()...) - } - - addons = append(addons, g.defaults.getAddonsToUpdate()...) - - return addons -} - -func (n *installStrategyNode) addNode(addon *addonv1alpha1.ManagedClusterAddOn) { - n.children[addon.Namespace] = &addonNode{ - mca: addon, - desiredConfigs: n.desiredConfigs, - } - - // override configuration by mca spec - if len(addon.Spec.Configs) > 0 { - n.children[addon.Namespace].desiredConfigs = n.children[addon.Namespace].desiredConfigs.copy() - // TODO we should also filter out the configs which are not supported configs. - for _, config := range addon.Spec.Configs { - n.children[addon.Namespace].desiredConfigs[config.ConfigGroupResource] = addonv1alpha1.ConfigReference{ - ConfigGroupResource: config.ConfigGroupResource, - ConfigReferent: config.ConfigReferent, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: config.ConfigReferent, - }, - } - // copy the spechash from mca status - for _, configRef := range addon.Status.ConfigReferences { - if configRef.DesiredConfig == nil { - continue - } - nodeDesiredConfig, ok := n.children[addon.Namespace].desiredConfigs[configRef.ConfigGroupResource] - if ok && (nodeDesiredConfig.DesiredConfig.ConfigReferent == configRef.DesiredConfig.ConfigReferent) { - nodeDesiredConfig.DesiredConfig.SpecHash = configRef.DesiredConfig.SpecHash - } - } - } - } - - // set addon node rollout status - n.children[addon.Namespace].setRolloutStatus() -} - -func (n *installStrategyNode) generateRolloutResult() error { - if n.placementRef.Name == "" { - // default addons - rolloutResult := clusterv1sdkalpha1.RolloutResult{} - rolloutResult.ClustersToRollout = []clusterv1sdkalpha1.ClusterRolloutStatus{} - for name, addon := range n.children { - if addon.status == nil { - return fmt.Errorf("failed to get rollout status on cluster %v", name) - } - if addon.status.Status != clusterv1sdkalpha1.Succeeded { - rolloutResult.ClustersToRollout = append(rolloutResult.ClustersToRollout, *addon.status) - } - } - n.rolloutResult = rolloutResult - } else { - // placement addons - rolloutHandler, err := clusterv1sdkalpha1.NewRolloutHandler(n.pdTracker, getClusterRolloutStatus) - if err != nil { - return err - } - - // get existing addons - existingRolloutClusters := []clusterv1sdkalpha1.ClusterRolloutStatus{} - for name, addon := range n.children { - clsRolloutStatus, err := getClusterRolloutStatus(name, addon) - if err != nil { - return err - } - existingRolloutClusters = append(existingRolloutClusters, clsRolloutStatus) - } - - // sort by cluster name - sort.SliceStable(existingRolloutClusters, func(i, j int) bool { - return existingRolloutClusters[i].ClusterName < existingRolloutClusters[j].ClusterName - }) - - _, rolloutResult, err := rolloutHandler.GetRolloutCluster(n.rolloutStrategy, existingRolloutClusters) - if err != nil { - return err - } - n.rolloutResult = rolloutResult - } - - return nil -} - -// addonToUpdate finds the addons to be updated by placement -func (n *installStrategyNode) getAddonsToUpdate() []*addonNode { - var addons []*addonNode - var clusters []string - - // get addon to update from rollout result - for _, c := range n.rolloutResult.ClustersToRollout { - if _, exist := n.children[c.ClusterName]; exist { - clusters = append(clusters, c.ClusterName) - } - } - - // sort addons by name - sort.Strings(clusters) - for _, k := range clusters { - addons = append(addons, n.children[k]) - } - return addons -} - -func (n *installStrategyNode) countAddonUpgradeSucceed() int { - count := 0 - for _, addon := range n.children { - if desiredConfigsEqual(addon.desiredConfigs, n.desiredConfigs) && addon.status.Status == clusterv1sdkalpha1.Succeeded { - count += 1 - } - } - return count -} - -func (n *installStrategyNode) countAddonUpgrading() int { - count := 0 - for _, addon := range n.children { - if desiredConfigsEqual(addon.desiredConfigs, n.desiredConfigs) && addon.status.Status == clusterv1sdkalpha1.Progressing { - count += 1 - } - } - return count -} - -func (n *installStrategyNode) countAddonTimeOut() int { - return len(n.rolloutResult.ClustersTimeOut) -} - -func getClusterRolloutStatus(clusterName string, addonNode *addonNode) (clusterv1sdkalpha1.ClusterRolloutStatus, error) { - if addonNode.status == nil { - return clusterv1sdkalpha1.ClusterRolloutStatus{}, fmt.Errorf("failed to get rollout status on cluster %v", clusterName) - } - return *addonNode.status, nil -} - -func desiredConfigsEqual(a, b addonConfigMap) bool { - if len(a) != len(b) { - return false - } - - for configgrA := range a { - if a[configgrA] != b[configgrA] { - return false - } - } - - return true -} diff --git a/pkg/manager/controllers/addonconfiguration/graph_test.go b/pkg/manager/controllers/addonconfiguration/graph_test.go deleted file mode 100644 index 246482570..000000000 --- a/pkg/manager/controllers/addonconfiguration/graph_test.go +++ /dev/null @@ -1,711 +0,0 @@ -package addonconfiguration - -import ( - "reflect" - "testing" - "time" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - clusterv1sdkalpha1 "open-cluster-management.io/sdk-go/pkg/apis/cluster/v1alpha1" - - "open-cluster-management.io/addon-framework/pkg/addonmanager/addontesting" - addonv1alpha1 "open-cluster-management.io/api/addon/v1alpha1" - fakecluster "open-cluster-management.io/api/client/cluster/clientset/versioned/fake" - clusterv1informers "open-cluster-management.io/api/client/cluster/informers/externalversions" - clusterv1alpha1 "open-cluster-management.io/api/cluster/v1alpha1" - clusterv1beta1 "open-cluster-management.io/api/cluster/v1beta1" -) - -var fakeTime = metav1.NewTime(time.Date(2022, time.January, 01, 0, 0, 0, 0, time.UTC)) - -type placementDesicion struct { - addonv1alpha1.PlacementRef - clusters []clusterv1beta1.ClusterDecision -} - -func TestConfigurationGraph(t *testing.T) { - cases := []struct { - name string - defaultConfigs []addonv1alpha1.ConfigMeta - defaultConfigReference []addonv1alpha1.DefaultConfigReference - addons []*addonv1alpha1.ManagedClusterAddOn - placementDesicions []placementDesicion - placementStrategies []addonv1alpha1.PlacementStrategy - installProgressions []addonv1alpha1.InstallProgression - expected []*addonNode - }{ - { - name: "no output", - expected: nil, - }, - { - name: "default config only", - defaultConfigs: []addonv1alpha1.ConfigMeta{ - {ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Bar"}}, - {ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - DefaultConfig: &addonv1alpha1.ConfigReferent{Name: "test"}}, - }, - defaultConfigReference: []addonv1alpha1.DefaultConfigReference{ - newDefaultConfigReference("core", "Foo", "test", ""), - }, - addons: []*addonv1alpha1.ManagedClusterAddOn{ - addontesting.NewAddon("test", "cluster1"), - addontesting.NewAddon("test", "cluster2"), - }, - expected: []*addonNode{ - { - desiredConfigs: map[addonv1alpha1.ConfigGroupResource]addonv1alpha1.ConfigReference{ - {Group: "core", Resource: "Foo"}: { - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test"}, - SpecHash: "", - }, - }, - }, - mca: addontesting.NewAddon("test", "cluster1"), - status: &clusterv1sdkalpha1.ClusterRolloutStatus{ - ClusterName: "cluster1", - Status: clusterv1sdkalpha1.ToApply}, - }, - { - desiredConfigs: map[addonv1alpha1.ConfigGroupResource]addonv1alpha1.ConfigReference{ - {Group: "core", Resource: "Foo"}: { - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test"}, - SpecHash: "", - }, - }, - }, - mca: addontesting.NewAddon("test", "cluster2"), - status: &clusterv1sdkalpha1.ClusterRolloutStatus{ - ClusterName: "cluster2", - Status: clusterv1sdkalpha1.ToApply}, - }, - }, - }, - { - name: "with placement strategy", - defaultConfigs: []addonv1alpha1.ConfigMeta{ - {ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Bar"}, - DefaultConfig: &addonv1alpha1.ConfigReferent{Name: "test"}}, - {ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - DefaultConfig: &addonv1alpha1.ConfigReferent{Name: "test"}}, - }, - defaultConfigReference: []addonv1alpha1.DefaultConfigReference{ - newDefaultConfigReference("core", "Bar", "test", ""), - newDefaultConfigReference("core", "Foo", "test", ""), - }, - addons: []*addonv1alpha1.ManagedClusterAddOn{ - addontesting.NewAddon("test", "cluster1"), - addontesting.NewAddon("test", "cluster2"), - addontesting.NewAddon("test", "cluster3"), - }, - placementDesicions: []placementDesicion{ - {PlacementRef: addonv1alpha1.PlacementRef{Name: "placement1", Namespace: "test"}, - clusters: []clusterv1beta1.ClusterDecision{{ClusterName: "cluster1"}}}, - {PlacementRef: addonv1alpha1.PlacementRef{Name: "placement2", Namespace: "test"}, - clusters: []clusterv1beta1.ClusterDecision{{ClusterName: "cluster2"}}}, - }, - placementStrategies: []addonv1alpha1.PlacementStrategy{ - {PlacementRef: addonv1alpha1.PlacementRef{Name: "placement1", Namespace: "test"}, - RolloutStrategy: clusterv1alpha1.RolloutStrategy{Type: clusterv1alpha1.All}}, - {PlacementRef: addonv1alpha1.PlacementRef{Name: "placement2", Namespace: "test"}, - RolloutStrategy: clusterv1alpha1.RolloutStrategy{Type: clusterv1alpha1.All}}, - }, - installProgressions: []addonv1alpha1.InstallProgression{ - { - PlacementRef: addonv1alpha1.PlacementRef{Name: "placement1", Namespace: "test"}, - ConfigReferences: []addonv1alpha1.InstallConfigReference{ - newInstallConfigReference("core", "Bar", "test1", ""), - }, - }, - { - PlacementRef: addonv1alpha1.PlacementRef{Name: "placement2", Namespace: "test"}, - ConfigReferences: []addonv1alpha1.InstallConfigReference{ - newInstallConfigReference("core", "Bar", "test2", ""), - newInstallConfigReference("core", "Foo", "test2", ""), - }, - }, - }, - expected: []*addonNode{ - { - desiredConfigs: map[addonv1alpha1.ConfigGroupResource]addonv1alpha1.ConfigReference{ - {Group: "core", Resource: "Bar"}: { - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Bar"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "", - }, - }, - {Group: "core", Resource: "Foo"}: { - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test"}, - SpecHash: "", - }, - }, - }, - mca: addontesting.NewAddon("test", "cluster1"), - status: &clusterv1sdkalpha1.ClusterRolloutStatus{ - ClusterName: "cluster1", - Status: clusterv1sdkalpha1.ToApply}, - }, - { - desiredConfigs: map[addonv1alpha1.ConfigGroupResource]addonv1alpha1.ConfigReference{ - {Group: "core", Resource: "Bar"}: { - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Bar"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test2"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test2"}, - SpecHash: "", - }, - }, - {Group: "core", Resource: "Foo"}: { - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test2"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test2"}, - SpecHash: "", - }, - }, - }, - mca: addontesting.NewAddon("test", "cluster2"), - status: &clusterv1sdkalpha1.ClusterRolloutStatus{ - ClusterName: "cluster2", - Status: clusterv1sdkalpha1.ToApply, - }, - }, - { - desiredConfigs: map[addonv1alpha1.ConfigGroupResource]addonv1alpha1.ConfigReference{ - {Group: "core", Resource: "Bar"}: { - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Bar"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test"}, - SpecHash: "", - }, - }, - {Group: "core", Resource: "Foo"}: { - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test"}, - SpecHash: "", - }, - }, - }, - mca: addontesting.NewAddon("test", "cluster3"), - status: &clusterv1sdkalpha1.ClusterRolloutStatus{ - ClusterName: "cluster3", - Status: clusterv1sdkalpha1.ToApply, - }, - }, - }, - }, - { - name: "mca progressing/failed/succeed", - defaultConfigs: []addonv1alpha1.ConfigMeta{}, - defaultConfigReference: []addonv1alpha1.DefaultConfigReference{}, - addons: []*addonv1alpha1.ManagedClusterAddOn{ - newManagedClusterAddon("test", "cluster1", []addonv1alpha1.AddOnConfig{}, []addonv1alpha1.ConfigReference{ - { - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Bar"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "", - }, - LastObservedGeneration: 1, - }, - }, []metav1.Condition{ - { - Type: addonv1alpha1.ManagedClusterAddOnConditionProgressing, - Reason: addonv1alpha1.ProgressingReasonUpgradeFailed, - LastTransitionTime: fakeTime, - }, - }), - newManagedClusterAddon("test", "cluster2", []addonv1alpha1.AddOnConfig{}, []addonv1alpha1.ConfigReference{ - { - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Bar"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "", - }, - LastObservedGeneration: 1, - }, - }, []metav1.Condition{ - { - Type: addonv1alpha1.ManagedClusterAddOnConditionProgressing, - Reason: addonv1alpha1.ProgressingReasonUpgrading, - LastTransitionTime: fakeTime, - }, - }), - newManagedClusterAddon("test", "cluster3", []addonv1alpha1.AddOnConfig{}, []addonv1alpha1.ConfigReference{ - { - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Bar"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "", - }, - LastAppliedConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "", - }, - LastObservedGeneration: 1, - }, - }, []metav1.Condition{ - { - Type: addonv1alpha1.ManagedClusterAddOnConditionProgressing, - Reason: addonv1alpha1.ProgressingReasonUpgradeSucceed, - LastTransitionTime: fakeTime, - }, - }), - newManagedClusterAddon("test", "cluster4", []addonv1alpha1.AddOnConfig{}, []addonv1alpha1.ConfigReference{ - { - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "testx"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "testx"}, - SpecHash: "", - }, - LastAppliedConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "testx"}, - SpecHash: "", - }, - LastObservedGeneration: 1, - }, - }, []metav1.Condition{ - { - Type: addonv1alpha1.ManagedClusterAddOnConditionProgressing, - Reason: addonv1alpha1.ProgressingReasonUpgradeSucceed, - LastTransitionTime: fakeTime, - }, - }), - }, - placementDesicions: []placementDesicion{ - {PlacementRef: addonv1alpha1.PlacementRef{Name: "placement1", Namespace: "test"}, - clusters: []clusterv1beta1.ClusterDecision{{ClusterName: "cluster1"}, {ClusterName: "cluster2"}, - {ClusterName: "cluster3"}, {ClusterName: "cluster4"}}}, - }, - placementStrategies: []addonv1alpha1.PlacementStrategy{ - {PlacementRef: addonv1alpha1.PlacementRef{Name: "placement1", Namespace: "test"}, - RolloutStrategy: clusterv1alpha1.RolloutStrategy{Type: clusterv1alpha1.All}}, - }, - installProgressions: []addonv1alpha1.InstallProgression{ - { - PlacementRef: addonv1alpha1.PlacementRef{Name: "placement1", Namespace: "test"}, - ConfigReferences: []addonv1alpha1.InstallConfigReference{ - newInstallConfigReference("core", "Bar", "test1", ""), - }, - }, - }, - expected: []*addonNode{ - { - desiredConfigs: map[addonv1alpha1.ConfigGroupResource]addonv1alpha1.ConfigReference{ - {Group: "core", Resource: "Bar"}: { - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Bar"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "", - }, - }, - }, - mca: addontesting.NewAddon("test", "cluster1"), - status: &clusterv1sdkalpha1.ClusterRolloutStatus{ - ClusterName: "cluster1", - Status: clusterv1sdkalpha1.Failed, - LastTransitionTime: &fakeTime, - }, - }, - { - desiredConfigs: map[addonv1alpha1.ConfigGroupResource]addonv1alpha1.ConfigReference{ - {Group: "core", Resource: "Bar"}: { - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Bar"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "", - }, - }, - }, - mca: addontesting.NewAddon("test", "cluster2"), - status: &clusterv1sdkalpha1.ClusterRolloutStatus{ - ClusterName: "cluster2", - Status: clusterv1sdkalpha1.Progressing, - LastTransitionTime: &fakeTime, - }, - }, - { - desiredConfigs: map[addonv1alpha1.ConfigGroupResource]addonv1alpha1.ConfigReference{ - {Group: "core", Resource: "Bar"}: { - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Bar"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "", - }, - }, - }, - mca: addontesting.NewAddon("test", "cluster4"), - status: &clusterv1sdkalpha1.ClusterRolloutStatus{ - ClusterName: "cluster4", - Status: clusterv1sdkalpha1.ToApply, - }, - }, - }, - }, - { - name: "placement overlap", - defaultConfigs: []addonv1alpha1.ConfigMeta{ - {ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Bar"}, - DefaultConfig: &addonv1alpha1.ConfigReferent{Name: "test"}}, - {ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - DefaultConfig: &addonv1alpha1.ConfigReferent{Name: "test"}}, - }, - defaultConfigReference: []addonv1alpha1.DefaultConfigReference{ - newDefaultConfigReference("core", "Bar", "test", ""), - newDefaultConfigReference("core", "Foo", "test", ""), - }, - addons: []*addonv1alpha1.ManagedClusterAddOn{ - addontesting.NewAddon("test", "cluster1"), - addontesting.NewAddon("test", "cluster2"), - addontesting.NewAddon("test", "cluster3"), - }, - placementStrategies: []addonv1alpha1.PlacementStrategy{ - {PlacementRef: addonv1alpha1.PlacementRef{Name: "placement1", Namespace: "test"}, - RolloutStrategy: clusterv1alpha1.RolloutStrategy{Type: clusterv1alpha1.All}}, - {PlacementRef: addonv1alpha1.PlacementRef{Name: "placement2", Namespace: "test"}, - RolloutStrategy: clusterv1alpha1.RolloutStrategy{Type: clusterv1alpha1.All}}, - }, - placementDesicions: []placementDesicion{ - {PlacementRef: addonv1alpha1.PlacementRef{Name: "placement1", Namespace: "test"}, - clusters: []clusterv1beta1.ClusterDecision{{ClusterName: "cluster1"}, {ClusterName: "cluster2"}}}, - {PlacementRef: addonv1alpha1.PlacementRef{Name: "placement2", Namespace: "test"}, - clusters: []clusterv1beta1.ClusterDecision{{ClusterName: "cluster2"}, {ClusterName: "cluster3"}}}, - }, - installProgressions: []addonv1alpha1.InstallProgression{ - { - PlacementRef: addonv1alpha1.PlacementRef{Name: "placement1", Namespace: "test"}, - ConfigReferences: []addonv1alpha1.InstallConfigReference{ - newInstallConfigReference("core", "Bar", "test1", ""), - }, - }, - { - PlacementRef: addonv1alpha1.PlacementRef{Name: "placement2", Namespace: "test"}, - ConfigReferences: []addonv1alpha1.InstallConfigReference{ - newInstallConfigReference("core", "Bar", "test2", ""), - newInstallConfigReference("core", "Foo", "test2", ""), - }, - }, - }, - expected: []*addonNode{ - { - desiredConfigs: map[addonv1alpha1.ConfigGroupResource]addonv1alpha1.ConfigReference{ - {Group: "core", Resource: "Bar"}: { - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Bar"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "", - }, - }, - {Group: "core", Resource: "Foo"}: { - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test"}, - SpecHash: "", - }, - }, - }, - mca: addontesting.NewAddon("test", "cluster1"), - status: &clusterv1sdkalpha1.ClusterRolloutStatus{ - ClusterName: "cluster1", - Status: clusterv1sdkalpha1.ToApply}, - }, - { - desiredConfigs: map[addonv1alpha1.ConfigGroupResource]addonv1alpha1.ConfigReference{ - {Group: "core", Resource: "Bar"}: { - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Bar"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test2"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test2"}, - SpecHash: "", - }, - }, - {Group: "core", Resource: "Foo"}: { - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test2"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test2"}, - SpecHash: "", - }, - }, - }, - mca: addontesting.NewAddon("test", "cluster2"), - status: &clusterv1sdkalpha1.ClusterRolloutStatus{ - ClusterName: "cluster2", - Status: clusterv1sdkalpha1.ToApply}, - }, - { - desiredConfigs: map[addonv1alpha1.ConfigGroupResource]addonv1alpha1.ConfigReference{ - {Group: "core", Resource: "Bar"}: { - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Bar"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test2"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test2"}, - SpecHash: "", - }, - }, - {Group: "core", Resource: "Foo"}: { - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test2"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test2"}, - SpecHash: "", - }, - }, - }, - mca: addontesting.NewAddon("test", "cluster3"), - status: &clusterv1sdkalpha1.ClusterRolloutStatus{ - ClusterName: "cluster3", - Status: clusterv1sdkalpha1.ToApply}, - }, - }, - }, - { - name: "mca override", - defaultConfigs: []addonv1alpha1.ConfigMeta{ - {ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Bar"}, - DefaultConfig: &addonv1alpha1.ConfigReferent{Name: "test"}}, - {ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - DefaultConfig: &addonv1alpha1.ConfigReferent{Name: "test"}}, - }, - defaultConfigReference: []addonv1alpha1.DefaultConfigReference{ - newDefaultConfigReference("core", "Bar", "test", ""), - newDefaultConfigReference("core", "Foo", "test", ""), - }, - addons: []*addonv1alpha1.ManagedClusterAddOn{ - newManagedClusterAddon("test", "cluster1", []addonv1alpha1.AddOnConfig{ - {ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Bar"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}}, - }, nil, nil), - addontesting.NewAddon("test", "cluster2"), - addontesting.NewAddon("test", "cluster3"), - }, - placementStrategies: []addonv1alpha1.PlacementStrategy{ - {PlacementRef: addonv1alpha1.PlacementRef{Name: "placement1", Namespace: "test"}, - RolloutStrategy: clusterv1alpha1.RolloutStrategy{Type: clusterv1alpha1.All}}, - {PlacementRef: addonv1alpha1.PlacementRef{Name: "placement2", Namespace: "test"}, - RolloutStrategy: clusterv1alpha1.RolloutStrategy{Type: clusterv1alpha1.All}}, - }, - placementDesicions: []placementDesicion{ - {PlacementRef: addonv1alpha1.PlacementRef{Name: "placement1", Namespace: "test"}, - clusters: []clusterv1beta1.ClusterDecision{{ClusterName: "cluster1"}}}, - {PlacementRef: addonv1alpha1.PlacementRef{Name: "placement2", Namespace: "test"}, - clusters: []clusterv1beta1.ClusterDecision{{ClusterName: "cluster2"}}}, - }, - installProgressions: []addonv1alpha1.InstallProgression{ - { - PlacementRef: addonv1alpha1.PlacementRef{Name: "placement1", Namespace: "test"}, - ConfigReferences: []addonv1alpha1.InstallConfigReference{ - newInstallConfigReference("core", "Foo", "test1", ""), - }, - }, - { - PlacementRef: addonv1alpha1.PlacementRef{Name: "placement2", Namespace: "test"}, - ConfigReferences: []addonv1alpha1.InstallConfigReference{ - newInstallConfigReference("core", "Bar", "test2", ""), - newInstallConfigReference("core", "Foo", "test2", ""), - }, - }, - }, - expected: []*addonNode{ - { - desiredConfigs: map[addonv1alpha1.ConfigGroupResource]addonv1alpha1.ConfigReference{ - {Group: "core", Resource: "Bar"}: { - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Bar"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "", - }, - }, - {Group: "core", Resource: "Foo"}: { - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "", - }, - }, - }, - mca: newManagedClusterAddon("test", "cluster1", []addonv1alpha1.AddOnConfig{ - {ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Bar"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}}, - }, nil, nil), - status: &clusterv1sdkalpha1.ClusterRolloutStatus{ - ClusterName: "cluster1", - Status: clusterv1sdkalpha1.ToApply}, - }, - { - desiredConfigs: map[addonv1alpha1.ConfigGroupResource]addonv1alpha1.ConfigReference{ - {Group: "core", Resource: "Bar"}: { - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Bar"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test2"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test2"}, - SpecHash: "", - }, - }, - {Group: "core", Resource: "Foo"}: { - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test2"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test2"}, - SpecHash: "", - }, - }, - }, - mca: addontesting.NewAddon("test", "cluster2"), - status: &clusterv1sdkalpha1.ClusterRolloutStatus{ - ClusterName: "cluster2", - Status: clusterv1sdkalpha1.ToApply}, - }, - { - desiredConfigs: map[addonv1alpha1.ConfigGroupResource]addonv1alpha1.ConfigReference{ - {Group: "core", Resource: "Bar"}: { - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Bar"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test"}, - SpecHash: "", - }, - }, - {Group: "core", Resource: "Foo"}: { - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test"}, - SpecHash: "", - }, - }, - }, - mca: addontesting.NewAddon("test", "cluster3"), - status: &clusterv1sdkalpha1.ClusterRolloutStatus{ - ClusterName: "cluster3", - Status: clusterv1sdkalpha1.ToApply}, - }, - }, - }, - } - - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - fakeClusterClient := fakecluster.NewSimpleClientset() - clusterInformers := clusterv1informers.NewSharedInformerFactory(fakeClusterClient, 10*time.Minute) - placementDecisionGetter := PlacementDecisionGetter{Client: clusterInformers.Cluster().V1beta1().PlacementDecisions().Lister()} - placementLister := clusterInformers.Cluster().V1beta1().Placements().Lister() - - for _, strategy := range c.placementStrategies { - obj := &clusterv1beta1.Placement{ObjectMeta: metav1.ObjectMeta{Name: strategy.Name, Namespace: strategy.Namespace}} - if err := clusterInformers.Cluster().V1beta1().Placements().Informer().GetStore().Add(obj); err != nil { - t.Fatal(err) - } - } - - for _, decision := range c.placementDesicions { - obj := &clusterv1beta1.PlacementDecision{ - ObjectMeta: metav1.ObjectMeta{Name: decision.Name, Namespace: decision.Namespace, - Labels: map[string]string{ - clusterv1beta1.PlacementLabel: decision.Name, - clusterv1beta1.DecisionGroupIndexLabel: "0", - }}, - Status: clusterv1beta1.PlacementDecisionStatus{Decisions: decision.clusters}, - } - if err := clusterInformers.Cluster().V1beta1().PlacementDecisions().Informer().GetStore().Add(obj); err != nil { - t.Fatal(err) - } - } - - graph := newGraph(c.defaultConfigs, c.defaultConfigReference) - for _, addon := range c.addons { - graph.addAddonNode(addon) - } - - for i := range c.placementStrategies { - graph.addPlacementNode(c.placementStrategies[i], c.installProgressions[i], placementLister, placementDecisionGetter) - } - - err := graph.generateRolloutResult() - if err != nil { - t.Errorf("expected no error when refresh rollout result: %v", err) - } - - actual := graph.getAddonsToUpdate() - if len(actual) != len(c.expected) { - t.Errorf("output length is not correct, expected %v, got %v", len(c.expected), len(actual)) - } - - for _, ev := range c.expected { - compared := false - for _, v := range actual { - if v == nil || ev == nil { - t.Errorf("addonNode should not be nil") - } - if ev.mca != nil && v.mca != nil && ev.mca.Namespace == v.mca.Namespace { - if !reflect.DeepEqual(v.mca.Name, ev.mca.Name) { - t.Errorf("output mca name is not correct, cluster %s, expected %v, got %v", v.mca.Namespace, ev.mca.Name, v.mca.Name) - } - if !reflect.DeepEqual(v.desiredConfigs, ev.desiredConfigs) { - t.Errorf("output desiredConfigs is not correct, cluster %s, expected %v, got %v", v.mca.Namespace, ev.desiredConfigs, v.desiredConfigs) - } - if !reflect.DeepEqual(v.status, ev.status) { - t.Errorf("output status is not correct, cluster %s, expected %v, got %v", v.mca.Namespace, ev.status, v.status) - } - compared = true - } - } - - if !compared { - t.Errorf("not found addonNode %v", ev.mca) - } - } - }) - } -} - -func newInstallConfigReference(group, resource, name, hash string) addonv1alpha1.InstallConfigReference { - return addonv1alpha1.InstallConfigReference{ - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{ - Group: group, - Resource: resource, - }, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: name}, - SpecHash: hash, - }, - } -} - -func newDefaultConfigReference(group, resource, name, hash string) addonv1alpha1.DefaultConfigReference { - return addonv1alpha1.DefaultConfigReference{ - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{ - Group: group, - Resource: resource, - }, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: name}, - SpecHash: hash, - }, - } -} diff --git a/pkg/manager/controllers/addonconfiguration/mgmt_addon_progressing_reconciler.go b/pkg/manager/controllers/addonconfiguration/mgmt_addon_progressing_reconciler.go deleted file mode 100644 index 36d712383..000000000 --- a/pkg/manager/controllers/addonconfiguration/mgmt_addon_progressing_reconciler.go +++ /dev/null @@ -1,143 +0,0 @@ -package addonconfiguration - -import ( - "context" - "encoding/json" - "fmt" - - jsonpatch "github.com/evanphx/json-patch" - "k8s.io/apimachinery/pkg/api/equality" - "k8s.io/apimachinery/pkg/api/meta" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - utilerrors "k8s.io/apimachinery/pkg/util/errors" - "k8s.io/klog/v2" - - addonv1alpha1 "open-cluster-management.io/api/addon/v1alpha1" - addonv1alpha1client "open-cluster-management.io/api/client/addon/clientset/versioned" -) - -type clusterManagementAddonProgressingReconciler struct { - addonClient addonv1alpha1client.Interface -} - -func (d *clusterManagementAddonProgressingReconciler) reconcile( - ctx context.Context, cma *addonv1alpha1.ClusterManagementAddOn, graph *configurationGraph) (*addonv1alpha1.ClusterManagementAddOn, reconcileState, error) { - var errs []error - cmaCopy := cma.DeepCopy() - placementNodes := graph.getPlacementNodes() - - // go through addons and update condition per install progression - for i, installProgression := range cmaCopy.Status.InstallProgressions { - placementNode, exist := placementNodes[installProgression.PlacementRef] - if !exist { - continue - } - - isUpgrade := false - - for _, configReference := range installProgression.ConfigReferences { - if configReference.LastAppliedConfig != nil { - isUpgrade = true - break - } - } - - setAddOnInstallProgressionsAndLastApplied(&cmaCopy.Status.InstallProgressions[i], - isUpgrade, - placementNode.countAddonUpgrading(), - placementNode.countAddonUpgradeSucceed(), - placementNode.countAddonTimeOut(), - len(placementNode.clusters), - ) - } - - err := d.patchMgmtAddonStatus(ctx, cmaCopy, cma) - if err != nil { - errs = append(errs, err) - } - return cmaCopy, reconcileContinue, utilerrors.NewAggregate(errs) -} - -func (d *clusterManagementAddonProgressingReconciler) patchMgmtAddonStatus(ctx context.Context, new, old *addonv1alpha1.ClusterManagementAddOn) error { - if equality.Semantic.DeepEqual(new.Status, old.Status) { - return nil - } - - oldData, err := json.Marshal(&addonv1alpha1.ClusterManagementAddOn{ - Status: addonv1alpha1.ClusterManagementAddOnStatus{ - InstallProgressions: old.Status.InstallProgressions, - }, - }) - if err != nil { - return err - } - - newData, err := json.Marshal(&addonv1alpha1.ClusterManagementAddOn{ - ObjectMeta: metav1.ObjectMeta{ - UID: new.UID, - ResourceVersion: new.ResourceVersion, - }, - Status: addonv1alpha1.ClusterManagementAddOnStatus{ - InstallProgressions: new.Status.InstallProgressions, - }, - }) - if err != nil { - return err - } - - patchBytes, err := jsonpatch.CreateMergePatch(oldData, newData) - if err != nil { - return fmt.Errorf("failed to create patch for addon %s: %w", new.Name, err) - } - - klog.V(2).Infof("Patching clustermanagementaddon %s status with %s", new.Name, string(patchBytes)) - _, err = d.addonClient.AddonV1alpha1().ClusterManagementAddOns().Patch( - ctx, new.Name, types.MergePatchType, patchBytes, metav1.PatchOptions{}, "status") - return err -} - -func setAddOnInstallProgressionsAndLastApplied( - installProgression *addonv1alpha1.InstallProgression, - isUpgrade bool, - progressing, done, timeout, total int) { - // always update progressing condition when there is no config - // skip update progressing condition when last applied config already the same as desired - skip := len(installProgression.ConfigReferences) > 0 - for _, configReference := range installProgression.ConfigReferences { - if !equality.Semantic.DeepEqual(configReference.LastAppliedConfig, configReference.DesiredConfig) && - !equality.Semantic.DeepEqual(configReference.LastKnownGoodConfig, configReference.DesiredConfig) { - skip = false - } - } - if skip { - return - } - condition := metav1.Condition{ - Type: addonv1alpha1.ManagedClusterAddOnConditionProgressing, - } - if (total == 0 && done == 0) || (done != total) { - condition.Status = metav1.ConditionTrue - if isUpgrade { - condition.Reason = addonv1alpha1.ProgressingReasonUpgrading - condition.Message = fmt.Sprintf("%d/%d upgrading..., %d timeout.", progressing+done, total, timeout) - } else { - condition.Reason = addonv1alpha1.ProgressingReasonInstalling - condition.Message = fmt.Sprintf("%d/%d installing..., %d timeout.", progressing+done, total, timeout) - } - } else { - for i, configRef := range installProgression.ConfigReferences { - installProgression.ConfigReferences[i].LastAppliedConfig = configRef.DesiredConfig.DeepCopy() - installProgression.ConfigReferences[i].LastKnownGoodConfig = configRef.DesiredConfig.DeepCopy() - } - condition.Status = metav1.ConditionFalse - if isUpgrade { - condition.Reason = addonv1alpha1.ProgressingReasonUpgradeSucceed - condition.Message = fmt.Sprintf("%d/%d upgrade completed with no errors, %d timeout.", done, total, timeout) - } else { - condition.Reason = addonv1alpha1.ProgressingReasonInstallSucceed - condition.Message = fmt.Sprintf("%d/%d install completed with no errors, %d timeout.", done, total, timeout) - } - } - meta.SetStatusCondition(&installProgression.Conditions, condition) -} diff --git a/pkg/manager/controllers/addonconfiguration/mgmt_addon_progressing_reconciler_test.go b/pkg/manager/controllers/addonconfiguration/mgmt_addon_progressing_reconciler_test.go deleted file mode 100644 index 934bcdf27..000000000 --- a/pkg/manager/controllers/addonconfiguration/mgmt_addon_progressing_reconciler_test.go +++ /dev/null @@ -1,677 +0,0 @@ -package addonconfiguration - -import ( - "context" - "encoding/json" - "testing" - "time" - - apiequality "k8s.io/apimachinery/pkg/api/equality" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - clienttesting "k8s.io/client-go/testing" - "k8s.io/client-go/tools/cache" - - "open-cluster-management.io/addon-framework/pkg/addonmanager/addontesting" - "open-cluster-management.io/addon-framework/pkg/index" - addonv1alpha1 "open-cluster-management.io/api/addon/v1alpha1" - fakeaddon "open-cluster-management.io/api/client/addon/clientset/versioned/fake" - addoninformers "open-cluster-management.io/api/client/addon/informers/externalversions" - fakecluster "open-cluster-management.io/api/client/cluster/clientset/versioned/fake" - clusterv1informers "open-cluster-management.io/api/client/cluster/informers/externalversions" - clusterv1alpha1 "open-cluster-management.io/api/cluster/v1alpha1" - clusterv1beta1 "open-cluster-management.io/api/cluster/v1beta1" -) - -func TestMgmtAddonProgressingReconcile(t *testing.T) { - cases := []struct { - name string - managedClusteraddon []runtime.Object - clusterManagementAddon []runtime.Object - placements []runtime.Object - placementDecisions []runtime.Object - validateAddonActions func(t *testing.T, actions []clienttesting.Action) - expectErr bool - }{ - { - name: "no managedClusteraddon", - managedClusteraddon: []runtime.Object{}, - clusterManagementAddon: []runtime.Object{addontesting.NewClusterManagementAddon("test", "", ""). - WithPlacementStrategy(addonv1alpha1.PlacementStrategy{ - PlacementRef: addonv1alpha1.PlacementRef{Name: "placement1", Namespace: "test"}, - RolloutStrategy: clusterv1alpha1.RolloutStrategy{Type: clusterv1alpha1.All}, - }).WithInstallProgression(addonv1alpha1.InstallProgression{ - PlacementRef: addonv1alpha1.PlacementRef{Name: "placement1", Namespace: "test"}, - ConfigReferences: []addonv1alpha1.InstallConfigReference{ - { - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "hash1", - }, - }, - }, - }).Build()}, - placements: []runtime.Object{ - &clusterv1beta1.Placement{ObjectMeta: metav1.ObjectMeta{Name: "placement1", Namespace: "test"}}, - }, - placementDecisions: []runtime.Object{ - &clusterv1beta1.PlacementDecision{ - ObjectMeta: metav1.ObjectMeta{ - Name: "placement1", - Namespace: "test", - Labels: map[string]string{ - clusterv1beta1.PlacementLabel: "placement1", - clusterv1beta1.DecisionGroupIndexLabel: "0", - }, - }, - Status: clusterv1beta1.PlacementDecisionStatus{ - Decisions: []clusterv1beta1.ClusterDecision{{ClusterName: "cluster1"}, {ClusterName: "cluster2"}}, - }, - }, - }, - validateAddonActions: func(t *testing.T, actions []clienttesting.Action) { - addontesting.AssertActions(t, actions, "patch") - actual := actions[0].(clienttesting.PatchActionImpl).Patch - cma := &addonv1alpha1.ClusterManagementAddOn{} - err := json.Unmarshal(actual, cma) - if err != nil { - t.Fatal(err) - } - - if len(cma.Status.DefaultConfigReferences) != 0 { - t.Errorf("DefaultConfigReferences object is not correct: %v", cma.Status.DefaultConfigReferences) - } - if cma.Status.InstallProgressions[0].ConfigReferences[0].LastAppliedConfig != nil { - t.Errorf("InstallProgressions LastAppliedConfig is not correct: %v", cma.Status.InstallProgressions[0].ConfigReferences[0]) - } - if cma.Status.InstallProgressions[0].ConfigReferences[0].LastKnownGoodConfig != nil { - t.Errorf("InstallProgressions LastKnownGoodConfig is not correct: %v", cma.Status.InstallProgressions[0].ConfigReferences[0]) - } - if cma.Status.InstallProgressions[0].Conditions[0].Reason != addonv1alpha1.ProgressingReasonInstalling { - t.Errorf("InstallProgressions condition is not correct: %v", cma.Status.InstallProgressions[0].Conditions[0].Reason) - } - if cma.Status.InstallProgressions[0].Conditions[0].Message != "0/2 installing..., 0 timeout." { - t.Errorf("InstallProgressions condition is not correct: %v", cma.Status.InstallProgressions[0].Conditions[0].Message) - } - }, - }, - { - name: "no placement", - managedClusteraddon: []runtime.Object{}, - clusterManagementAddon: []runtime.Object{addontesting.NewClusterManagementAddon("test", "", "").WithPlacementStrategy(). - WithInstallProgression(addonv1alpha1.InstallProgression{ - PlacementRef: addonv1alpha1.PlacementRef{Name: "placement1", Namespace: "test"}, - ConfigReferences: []addonv1alpha1.InstallConfigReference{ - { - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "hash1", - }, - }, - }, - }).Build()}, - validateAddonActions: addontesting.AssertNoActions, - }, - { - name: "update clustermanagementaddon status with condition Progressing installing", - managedClusteraddon: []runtime.Object{func() *addonv1alpha1.ManagedClusterAddOn { - addon := addontesting.NewAddon("test", "cluster1") - addon.Status.ConfigReferences = []addonv1alpha1.ConfigReference{ - { - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "hash1", - }, - }, - } - return addon - }()}, - clusterManagementAddon: []runtime.Object{addontesting.NewClusterManagementAddon("test", "", ""). - WithPlacementStrategy(addonv1alpha1.PlacementStrategy{ - PlacementRef: addonv1alpha1.PlacementRef{Name: "placement1", Namespace: "test"}, - RolloutStrategy: clusterv1alpha1.RolloutStrategy{Type: clusterv1alpha1.All}, - }).WithInstallProgression(addonv1alpha1.InstallProgression{ - PlacementRef: addonv1alpha1.PlacementRef{Name: "placement1", Namespace: "test"}, - ConfigReferences: []addonv1alpha1.InstallConfigReference{ - { - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "hash1", - }, - }, - }, - }).Build()}, - placements: []runtime.Object{ - &clusterv1beta1.Placement{ObjectMeta: metav1.ObjectMeta{Name: "placement1", Namespace: "test"}}, - }, - placementDecisions: []runtime.Object{ - &clusterv1beta1.PlacementDecision{ - ObjectMeta: metav1.ObjectMeta{ - Name: "placement1", - Namespace: "test", - Labels: map[string]string{ - clusterv1beta1.PlacementLabel: "placement1", - clusterv1beta1.DecisionGroupIndexLabel: "0", - }, - }, - Status: clusterv1beta1.PlacementDecisionStatus{ - Decisions: []clusterv1beta1.ClusterDecision{{ClusterName: "cluster1"}, {ClusterName: "cluster2"}}, - }, - }, - }, - validateAddonActions: func(t *testing.T, actions []clienttesting.Action) { - addontesting.AssertActions(t, actions, "patch") - actual := actions[0].(clienttesting.PatchActionImpl).Patch - cma := &addonv1alpha1.ClusterManagementAddOn{} - err := json.Unmarshal(actual, cma) - if err != nil { - t.Fatal(err) - } - - if len(cma.Status.DefaultConfigReferences) != 0 { - t.Errorf("DefaultConfigReferences object is not correct: %v", cma.Status.DefaultConfigReferences) - } - if cma.Status.InstallProgressions[0].ConfigReferences[0].LastAppliedConfig != nil { - t.Errorf("InstallProgressions LastAppliedConfig is not correct: %v", cma.Status.InstallProgressions[0].ConfigReferences[0]) - } - if cma.Status.InstallProgressions[0].ConfigReferences[0].LastKnownGoodConfig != nil { - t.Errorf("InstallProgressions LastKnownGoodConfig is not correct: %v", cma.Status.InstallProgressions[0].ConfigReferences[0]) - } - if cma.Status.InstallProgressions[0].Conditions[0].Reason != addonv1alpha1.ProgressingReasonInstalling { - t.Errorf("InstallProgressions condition is not correct: %v", cma.Status.InstallProgressions[0].Conditions[0].Reason) - } - if cma.Status.InstallProgressions[0].Conditions[0].Message != "1/2 installing..., 0 timeout." { - t.Errorf("InstallProgressions condition is not correct: %v", cma.Status.InstallProgressions[0].Conditions[0].Message) - } - }, - }, - { - name: "update clustermanagementaddon status with condition Progressing install succeed", - managedClusteraddon: []runtime.Object{func() *addonv1alpha1.ManagedClusterAddOn { - addon := addontesting.NewAddon("test", "cluster1") - addon.Status.ConfigReferences = []addonv1alpha1.ConfigReference{ - { - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "hash1", - }, - LastAppliedConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "hash1", - }, - }, - } - return addon - }()}, - clusterManagementAddon: []runtime.Object{addontesting.NewClusterManagementAddon("test", "", ""). - WithPlacementStrategy(addonv1alpha1.PlacementStrategy{ - PlacementRef: addonv1alpha1.PlacementRef{Name: "placement1", Namespace: "test"}, - RolloutStrategy: clusterv1alpha1.RolloutStrategy{Type: clusterv1alpha1.All}, - }).WithInstallProgression(addonv1alpha1.InstallProgression{ - PlacementRef: addonv1alpha1.PlacementRef{Name: "placement1", Namespace: "test"}, - ConfigReferences: []addonv1alpha1.InstallConfigReference{ - { - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "hash1", - }, - }, - }, - }).Build()}, - placements: []runtime.Object{ - &clusterv1beta1.Placement{ObjectMeta: metav1.ObjectMeta{Name: "placement1", Namespace: "test"}}, - }, - placementDecisions: []runtime.Object{ - &clusterv1beta1.PlacementDecision{ - ObjectMeta: metav1.ObjectMeta{ - Name: "placement1", - Namespace: "test", - Labels: map[string]string{ - clusterv1beta1.PlacementLabel: "placement1", - clusterv1beta1.DecisionGroupIndexLabel: "0", - }, - }, - Status: clusterv1beta1.PlacementDecisionStatus{ - Decisions: []clusterv1beta1.ClusterDecision{{ClusterName: "cluster1"}}, - }, - }, - }, - validateAddonActions: func(t *testing.T, actions []clienttesting.Action) { - addontesting.AssertActions(t, actions, "patch") - actual := actions[0].(clienttesting.PatchActionImpl).Patch - cma := &addonv1alpha1.ClusterManagementAddOn{} - err := json.Unmarshal(actual, cma) - if err != nil { - t.Fatal(err) - } - - if len(cma.Status.DefaultConfigReferences) != 0 { - t.Errorf("DefaultConfigReferences object is not correct: %v", cma.Status.DefaultConfigReferences) - } - if !apiequality.Semantic.DeepEqual( - cma.Status.InstallProgressions[0].ConfigReferences[0].LastAppliedConfig, - cma.Status.InstallProgressions[0].ConfigReferences[0].DesiredConfig) { - t.Errorf("InstallProgressions LastAppliedConfig is not correct: %v", cma.Status.InstallProgressions[0].ConfigReferences[0]) - } - if !apiequality.Semantic.DeepEqual( - cma.Status.InstallProgressions[0].ConfigReferences[0].LastKnownGoodConfig, - cma.Status.InstallProgressions[0].ConfigReferences[0].DesiredConfig) { - t.Errorf("InstallProgressions LastKnownGoodConfig is not correct: %v", cma.Status.InstallProgressions[0].ConfigReferences[0]) - } - if cma.Status.InstallProgressions[0].Conditions[0].Reason != addonv1alpha1.ProgressingReasonInstallSucceed { - t.Errorf("InstallProgressions condition is not correct: %v", cma.Status.InstallProgressions[0].Conditions) - } - if cma.Status.InstallProgressions[0].Conditions[0].Message != "1/1 install completed with no errors, 0 timeout." { - t.Errorf("InstallProgressions condition is not correct: %v", cma.Status.InstallProgressions[0].Conditions) - } - }, - }, - { - name: "update clustermanagementaddon status with condition Progressing upgrading", - managedClusteraddon: []runtime.Object{func() *addonv1alpha1.ManagedClusterAddOn { - addon := addontesting.NewAddon("test", "cluster1") - addon.Status.ConfigReferences = []addonv1alpha1.ConfigReference{ - { - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "hash1", - }, - }, - } - return addon - }()}, - clusterManagementAddon: []runtime.Object{addontesting.NewClusterManagementAddon("test", "", ""). - WithPlacementStrategy(addonv1alpha1.PlacementStrategy{ - PlacementRef: addonv1alpha1.PlacementRef{Name: "placement1", Namespace: "test"}, - RolloutStrategy: clusterv1alpha1.RolloutStrategy{Type: clusterv1alpha1.All}, - }).WithInstallProgression(addonv1alpha1.InstallProgression{ - PlacementRef: addonv1alpha1.PlacementRef{Name: "placement1", Namespace: "test"}, - ConfigReferences: []addonv1alpha1.InstallConfigReference{ - { - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "hash1", - }, - LastAppliedConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "hash", - }, - }, - }, - }).Build()}, - placements: []runtime.Object{ - &clusterv1beta1.Placement{ObjectMeta: metav1.ObjectMeta{Name: "placement1", Namespace: "test"}}, - }, - placementDecisions: []runtime.Object{ - &clusterv1beta1.PlacementDecision{ - ObjectMeta: metav1.ObjectMeta{ - Name: "placement1", - Namespace: "test", - Labels: map[string]string{ - clusterv1beta1.PlacementLabel: "placement1", - clusterv1beta1.DecisionGroupIndexLabel: "0", - }, - }, - Status: clusterv1beta1.PlacementDecisionStatus{ - Decisions: []clusterv1beta1.ClusterDecision{{ClusterName: "cluster1"}, {ClusterName: "cluster2"}}, - }, - }, - }, - validateAddonActions: func(t *testing.T, actions []clienttesting.Action) { - addontesting.AssertActions(t, actions, "patch") - actual := actions[0].(clienttesting.PatchActionImpl).Patch - cma := &addonv1alpha1.ClusterManagementAddOn{} - err := json.Unmarshal(actual, cma) - if err != nil { - t.Fatal(err) - } - - if len(cma.Status.DefaultConfigReferences) != 0 { - t.Errorf("DefaultConfigReferences object is not correct: %v", cma.Status.DefaultConfigReferences) - } - if cma.Status.InstallProgressions[0].ConfigReferences[0].LastKnownGoodConfig != nil { - t.Errorf("InstallProgressions LastKnownGoodConfig is not correct: %v", cma.Status.InstallProgressions[0].ConfigReferences[0]) - } - if cma.Status.InstallProgressions[0].Conditions[0].Reason != addonv1alpha1.ProgressingReasonUpgrading { - t.Errorf("InstallProgressions condition is not correct: %v", cma.Status.InstallProgressions[0].Conditions) - } - if cma.Status.InstallProgressions[0].Conditions[0].Message != "1/2 upgrading..., 0 timeout." { - t.Errorf("InstallProgressions condition is not correct: %v", cma.Status.InstallProgressions[0].Conditions) - } - }, - }, - { - name: "update clustermanagementaddon status with condition Progressing upgrade succeed", - managedClusteraddon: []runtime.Object{func() *addonv1alpha1.ManagedClusterAddOn { - addon := addontesting.NewAddon("test", "cluster1") - addon.Status.ConfigReferences = []addonv1alpha1.ConfigReference{ - { - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "hash1", - }, - LastAppliedConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "hash1", - }, - }, - } - return addon - }()}, - clusterManagementAddon: []runtime.Object{addontesting.NewClusterManagementAddon("test", "", ""). - WithPlacementStrategy(addonv1alpha1.PlacementStrategy{ - PlacementRef: addonv1alpha1.PlacementRef{Name: "placement1", Namespace: "test"}, - RolloutStrategy: clusterv1alpha1.RolloutStrategy{Type: clusterv1alpha1.All}, - }).WithInstallProgression(addonv1alpha1.InstallProgression{ - PlacementRef: addonv1alpha1.PlacementRef{Name: "placement1", Namespace: "test"}, - ConfigReferences: []addonv1alpha1.InstallConfigReference{ - { - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "hash1", - }, - LastAppliedConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "hash", - }, - }, - }, - }).Build()}, - placements: []runtime.Object{ - &clusterv1beta1.Placement{ObjectMeta: metav1.ObjectMeta{Name: "placement1", Namespace: "test"}}, - }, - placementDecisions: []runtime.Object{ - &clusterv1beta1.PlacementDecision{ - ObjectMeta: metav1.ObjectMeta{ - Name: "placement1", - Namespace: "test", - Labels: map[string]string{ - clusterv1beta1.PlacementLabel: "placement1", - clusterv1beta1.DecisionGroupIndexLabel: "0", - }, - }, - Status: clusterv1beta1.PlacementDecisionStatus{ - Decisions: []clusterv1beta1.ClusterDecision{{ClusterName: "cluster1"}}, - }, - }, - }, - validateAddonActions: func(t *testing.T, actions []clienttesting.Action) { - addontesting.AssertActions(t, actions, "patch") - actual := actions[0].(clienttesting.PatchActionImpl).Patch - cma := &addonv1alpha1.ClusterManagementAddOn{} - err := json.Unmarshal(actual, cma) - if err != nil { - t.Fatal(err) - } - - if len(cma.Status.DefaultConfigReferences) != 0 { - t.Errorf("DefaultConfigReferences object is not correct: %v", cma.Status.DefaultConfigReferences) - } - if !apiequality.Semantic.DeepEqual( - cma.Status.InstallProgressions[0].ConfigReferences[0].LastAppliedConfig, - cma.Status.InstallProgressions[0].ConfigReferences[0].DesiredConfig) { - t.Errorf("InstallProgressions LastAppliedConfig is not correct: %v", cma.Status.InstallProgressions[0].ConfigReferences[0]) - } - if !apiequality.Semantic.DeepEqual( - cma.Status.InstallProgressions[0].ConfigReferences[0].LastKnownGoodConfig, - cma.Status.InstallProgressions[0].ConfigReferences[0].DesiredConfig) { - t.Errorf("InstallProgressions LastKnownGoodConfig is not correct: %v", cma.Status.InstallProgressions[0].ConfigReferences[0]) - } - if cma.Status.InstallProgressions[0].Conditions[0].Reason != addonv1alpha1.ProgressingReasonUpgradeSucceed { - t.Errorf("InstallProgressions condition is not correct: %v", cma.Status.InstallProgressions[0].Conditions) - } - if cma.Status.InstallProgressions[0].Conditions[0].Message != "1/1 upgrade completed with no errors, 0 timeout." { - t.Errorf("InstallProgressions condition is not correct: %v", cma.Status.InstallProgressions[0].Conditions) - } - }, - }, - { - name: "mca override cma configs", - managedClusteraddon: []runtime.Object{func() *addonv1alpha1.ManagedClusterAddOn { - addon := addontesting.NewAddon("test", "cluster1") - addon.Spec.Configs = []addonv1alpha1.AddOnConfig{ - { - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "testmca"}, - }, - } - addon.Status.ConfigReferences = []addonv1alpha1.ConfigReference{ - { - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "testmca"}, - SpecHash: "hashmca", - }, - LastAppliedConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "testmca"}, - SpecHash: "hashmca", - }, - }, - } - return addon - }()}, - clusterManagementAddon: []runtime.Object{addontesting.NewClusterManagementAddon("test", "", ""). - WithPlacementStrategy(addonv1alpha1.PlacementStrategy{ - PlacementRef: addonv1alpha1.PlacementRef{Name: "placement1", Namespace: "test"}, - RolloutStrategy: clusterv1alpha1.RolloutStrategy{Type: clusterv1alpha1.All}, - }).WithInstallProgression(addonv1alpha1.InstallProgression{ - PlacementRef: addonv1alpha1.PlacementRef{Name: "placement1", Namespace: "test"}, - ConfigReferences: []addonv1alpha1.InstallConfigReference{ - { - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "hash1", - }, - LastAppliedConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "hash", - }, - }, - }, - }).Build()}, - placements: []runtime.Object{ - &clusterv1beta1.Placement{ObjectMeta: metav1.ObjectMeta{Name: "placement1", Namespace: "test"}}, - }, - placementDecisions: []runtime.Object{ - &clusterv1beta1.PlacementDecision{ - ObjectMeta: metav1.ObjectMeta{ - Name: "placement1", - Namespace: "test", - Labels: map[string]string{ - clusterv1beta1.PlacementLabel: "placement1", - clusterv1beta1.DecisionGroupIndexLabel: "0", - }, - }, - Status: clusterv1beta1.PlacementDecisionStatus{ - Decisions: []clusterv1beta1.ClusterDecision{{ClusterName: "cluster1"}}, - }, - }, - }, - validateAddonActions: func(t *testing.T, actions []clienttesting.Action) { - addontesting.AssertActions(t, actions, "patch") - actual := actions[0].(clienttesting.PatchActionImpl).Patch - cma := &addonv1alpha1.ClusterManagementAddOn{} - err := json.Unmarshal(actual, cma) - if err != nil { - t.Fatal(err) - } - - if len(cma.Status.DefaultConfigReferences) != 0 { - t.Errorf("DefaultConfigReferences object is not correct: %v", cma.Status.DefaultConfigReferences) - } - if cma.Status.InstallProgressions[0].ConfigReferences[0].LastKnownGoodConfig != nil { - t.Errorf("InstallProgressions LastKnownGoodConfig is not correct: %v", cma.Status.InstallProgressions[0].ConfigReferences[0]) - } - if cma.Status.InstallProgressions[0].Conditions[0].Reason != addonv1alpha1.ProgressingReasonUpgrading { - t.Errorf("InstallProgressions condition is not correct: %v", cma.Status.InstallProgressions[0].Conditions) - } - if cma.Status.InstallProgressions[0].Conditions[0].Message != "0/1 upgrading..., 0 timeout." { - t.Errorf("InstallProgressions condition is not correct: %v", cma.Status.InstallProgressions[0].Conditions) - } - }, - }, - { - name: "update clustermanagementaddon status with condition Progressing ConfigurationUnsupported", - managedClusteraddon: []runtime.Object{func() *addonv1alpha1.ManagedClusterAddOn { - addon := addontesting.NewAddon("test", "cluster1") - addon.Status.ConfigReferences = []addonv1alpha1.ConfigReference{ - { - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "hash1", - }, - }, - } - return addon - }()}, - clusterManagementAddon: []runtime.Object{addontesting.NewClusterManagementAddon("test", "", ""). - WithPlacementStrategy(addonv1alpha1.PlacementStrategy{ - PlacementRef: addonv1alpha1.PlacementRef{Name: "placement1", Namespace: "test"}, - RolloutStrategy: clusterv1alpha1.RolloutStrategy{Type: clusterv1alpha1.All}, - }).WithInstallProgression(addonv1alpha1.InstallProgression{ - PlacementRef: addonv1alpha1.PlacementRef{Name: "placement1", Namespace: "test"}, - ConfigReferences: []addonv1alpha1.InstallConfigReference{ - { - ConfigGroupResource: addonv1alpha1.ConfigGroupResource{Group: "core", Resource: "Foo"}, - DesiredConfig: &addonv1alpha1.ConfigSpecHash{ - ConfigReferent: addonv1alpha1.ConfigReferent{Name: "test1"}, - SpecHash: "hash1", - }, - }, - }, - }).Build()}, - placements: []runtime.Object{ - &clusterv1beta1.Placement{ObjectMeta: metav1.ObjectMeta{Name: "placement1", Namespace: "test"}}, - }, - placementDecisions: []runtime.Object{ - &clusterv1beta1.PlacementDecision{ - ObjectMeta: metav1.ObjectMeta{ - Name: "placement1", - Namespace: "test", - Labels: map[string]string{ - clusterv1beta1.PlacementLabel: "placement1", - clusterv1beta1.DecisionGroupIndexLabel: "0", - }, - }, - Status: clusterv1beta1.PlacementDecisionStatus{ - Decisions: []clusterv1beta1.ClusterDecision{{ClusterName: "cluster1"}, {ClusterName: "cluster2"}}, - }, - }, - }, - validateAddonActions: func(t *testing.T, actions []clienttesting.Action) { - addontesting.AssertActions(t, actions, "patch") - actual := actions[0].(clienttesting.PatchActionImpl).Patch - cma := &addonv1alpha1.ClusterManagementAddOn{} - err := json.Unmarshal(actual, cma) - if err != nil { - t.Fatal(err) - } - - if len(cma.Status.DefaultConfigReferences) != 0 { - t.Errorf("DefaultConfigReferences object is not correct: %v", cma.Status.DefaultConfigReferences) - } - if cma.Status.InstallProgressions[0].ConfigReferences[0].LastAppliedConfig != nil { - t.Errorf("InstallProgressions LastAppliedConfig is not correct: %v", cma.Status.InstallProgressions[0].ConfigReferences[0]) - } - if cma.Status.InstallProgressions[0].ConfigReferences[0].LastKnownGoodConfig != nil { - t.Errorf("InstallProgressions LastKnownGoodConfig is not correct: %v", cma.Status.InstallProgressions[0].ConfigReferences[0]) - } - if cma.Status.InstallProgressions[0].Conditions[0].Reason != addonv1alpha1.ProgressingReasonInstalling { - t.Errorf("InstallProgressions condition is not correct: %v", cma.Status.InstallProgressions[0].Conditions) - } - if cma.Status.InstallProgressions[0].Conditions[0].Message != "1/2 installing..., 0 timeout." { - t.Errorf("InstallProgressions condition is not correct: %v", cma.Status.InstallProgressions[0].Conditions[0].Message) - } - }, - }, - } - - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - obj := append(c.clusterManagementAddon, c.managedClusteraddon...) - clusterObj := append(c.placements, c.placementDecisions...) - fakeClusterClient := fakecluster.NewSimpleClientset(clusterObj...) - fakeAddonClient := fakeaddon.NewSimpleClientset(obj...) - - addonInformers := addoninformers.NewSharedInformerFactory(fakeAddonClient, 10*time.Minute) - clusterInformers := clusterv1informers.NewSharedInformerFactory(fakeClusterClient, 10*time.Minute) - - err := addonInformers.Addon().V1alpha1().ManagedClusterAddOns().Informer().AddIndexers( - cache.Indexers{ - index.ManagedClusterAddonByName: index.IndexManagedClusterAddonByName, - }) - if err != nil { - t.Fatal(err) - } - - for _, obj := range c.placements { - if err := clusterInformers.Cluster().V1beta1().Placements().Informer().GetStore().Add(obj); err != nil { - t.Fatal(err) - } - } - - for _, obj := range c.placementDecisions { - if err := clusterInformers.Cluster().V1beta1().PlacementDecisions().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.managedClusteraddon { - if err := addonInformers.Addon().V1alpha1().ManagedClusterAddOns().Informer().GetStore().Add(obj); err != nil { - t.Fatal(err) - } - } - - controller := &addonConfigurationController{ - addonClient: fakeAddonClient, - placementDecisionGetter: PlacementDecisionGetter{Client: clusterInformers.Cluster().V1beta1().PlacementDecisions().Lister()}, - placementLister: clusterInformers.Cluster().V1beta1().Placements().Lister(), - clusterManagementAddonLister: addonInformers.Addon().V1alpha1().ClusterManagementAddOns().Lister(), - managedClusterAddonIndexer: addonInformers.Addon().V1alpha1().ManagedClusterAddOns().Informer().GetIndexer(), - } - - reconcile := &clusterManagementAddonProgressingReconciler{ - addonClient: fakeAddonClient, - } - - for _, obj := range c.clusterManagementAddon { - graph, err := controller.buildConfigurationGraph(obj.(*addonv1alpha1.ClusterManagementAddOn)) - if err != nil { - t.Errorf("expected no error when build graph: %v", err) - } - err = graph.generateRolloutResult() - if err != nil { - t.Errorf("expected no error when refresh rollout result: %v", err) - } - - _, _, err = reconcile.reconcile(context.TODO(), obj.(*addonv1alpha1.ClusterManagementAddOn), graph) - if err != nil && !c.expectErr { - t.Errorf("expected no error when sync: %v", err) - } - if err == nil && c.expectErr { - t.Errorf("Expect error but got no error") - } - } - - c.validateAddonActions(t, fakeAddonClient.Actions()) - }) - } -} diff --git a/pkg/manager/controllers/addonowner/controller.go b/pkg/manager/controllers/addonowner/controller.go deleted file mode 100644 index beff5b4bd..000000000 --- a/pkg/manager/controllers/addonowner/controller.go +++ /dev/null @@ -1,100 +0,0 @@ -package addonowner - -import ( - "context" - - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/client-go/tools/cache" - "k8s.io/klog/v2" - - addonapiv1alpha1 "open-cluster-management.io/api/addon/v1alpha1" - addonv1alpha1client "open-cluster-management.io/api/client/addon/clientset/versioned" - addoninformerv1alpha1 "open-cluster-management.io/api/client/addon/informers/externalversions/addon/v1alpha1" - addonlisterv1alpha1 "open-cluster-management.io/api/client/addon/listers/addon/v1alpha1" - - "open-cluster-management.io/addon-framework/pkg/basecontroller/factory" - "open-cluster-management.io/addon-framework/pkg/utils" -) - -const UnsupportedConfigurationType = "UnsupportedConfiguration" - -// addonOwnerController reconciles instances of managedclusteradd on the hub -// to add related ClusterManagementAddon as the owner. -type addonOwnerController struct { - addonClient addonv1alpha1client.Interface - managedClusterAddonLister addonlisterv1alpha1.ManagedClusterAddOnLister - clusterManagementAddonLister addonlisterv1alpha1.ClusterManagementAddOnLister - addonFilterFunc factory.EventFilterFunc -} - -func NewAddonOwnerController( - addonClient addonv1alpha1client.Interface, - addonInformers addoninformerv1alpha1.ManagedClusterAddOnInformer, - clusterManagementAddonInformers addoninformerv1alpha1.ClusterManagementAddOnInformer, - addonFilterFunc factory.EventFilterFunc, -) factory.Controller { - c := &addonOwnerController{ - addonClient: addonClient, - managedClusterAddonLister: addonInformers.Lister(), - clusterManagementAddonLister: clusterManagementAddonInformers.Lister(), - addonFilterFunc: addonFilterFunc, - } - - return factory.New().WithFilteredEventsInformersQueueKeysFunc( - func(obj runtime.Object) []string { - key, _ := cache.DeletionHandlingMetaNamespaceKeyFunc(obj) - return []string{key} - }, - c.addonFilterFunc, clusterManagementAddonInformers.Informer()). - WithInformersQueueKeysFunc( - func(obj runtime.Object) []string { - key, _ := cache.DeletionHandlingMetaNamespaceKeyFunc(obj) - return []string{key} - }, - addonInformers.Informer()).WithSync(c.sync).ToController("addon-owner-controller") -} - -func (c *addonOwnerController) sync(ctx context.Context, syncCtx factory.SyncContext, key string) error { - klog.V(4).Infof("Reconciling addon %q", key) - - namespace, addonName, err := cache.SplitMetaNamespaceKey(key) - if err != nil { - // ignore addon whose key is invalid - return nil - } - - addon, err := c.managedClusterAddonLister.ManagedClusterAddOns(namespace).Get(addonName) - switch { - case errors.IsNotFound(err): - return nil - case err != nil: - return err - } - - addonCopy := addon.DeepCopy() - modified := false - - clusterManagementAddon, err := c.clusterManagementAddonLister.Get(addonName) - if errors.IsNotFound(err) { - return nil - } - - if err != nil { - return err - } - - if !c.addonFilterFunc(clusterManagementAddon) { - return nil - } - - owner := metav1.NewControllerRef(clusterManagementAddon, addonapiv1alpha1.GroupVersion.WithKind("ClusterManagementAddOn")) - modified = utils.MergeOwnerRefs(&addonCopy.OwnerReferences, *owner, false) - if modified { - _, err = c.addonClient.AddonV1alpha1().ManagedClusterAddOns(namespace).Update(ctx, addonCopy, metav1.UpdateOptions{}) - return err - } - - return nil -} diff --git a/pkg/manager/controllers/addonowner/controller_test.go b/pkg/manager/controllers/addonowner/controller_test.go deleted file mode 100644 index 666caefaa..000000000 --- a/pkg/manager/controllers/addonowner/controller_test.go +++ /dev/null @@ -1,89 +0,0 @@ -package addonowner - -import ( - "context" - "open-cluster-management.io/addon-framework/pkg/utils" - "testing" - "time" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - clienttesting "k8s.io/client-go/testing" - "open-cluster-management.io/addon-framework/pkg/addonmanager/addontesting" - addonapiv1alpha1 "open-cluster-management.io/api/addon/v1alpha1" - fakeaddon "open-cluster-management.io/api/client/addon/clientset/versioned/fake" - addoninformers "open-cluster-management.io/api/client/addon/informers/externalversions" -) - -func newClusterManagementOwner(name string) metav1.OwnerReference { - clusterManagementAddon := addontesting.NewClusterManagementAddon(name, "testcrd", "testcr").Build() - return *metav1.NewControllerRef(clusterManagementAddon, addonapiv1alpha1.GroupVersion.WithKind("ClusterManagementAddOn")) -} - -func TestReconcile(t *testing.T) { - cases := []struct { - name string - syncKey string - managedClusteraddon []runtime.Object - clusterManagementAddon []runtime.Object - validateAddonActions func(t *testing.T, actions []clienttesting.Action) - }{ - { - name: "no clustermanagementaddon", - syncKey: "test/test", - managedClusteraddon: []runtime.Object{}, - clusterManagementAddon: []runtime.Object{}, - validateAddonActions: addontesting.AssertNoActions, - }, - { - name: "no managedclusteraddon to sync", - syncKey: "cluster1/test", - managedClusteraddon: []runtime.Object{}, - clusterManagementAddon: []runtime.Object{addontesting.NewClusterManagementAddon("test", "testcrd", "testcr").Build()}, - validateAddonActions: addontesting.AssertNoActions, - }, - { - name: "update managedclusteraddon", - syncKey: "cluster1/test", - managedClusteraddon: []runtime.Object{ - addontesting.NewAddon("test", "cluster1", newClusterManagementOwner("test")), - }, - clusterManagementAddon: []runtime.Object{addontesting.NewClusterManagementAddon("test", "testcrd", "testcr").Build()}, - validateAddonActions: addontesting.AssertNoActions, - }, - } - - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - obj := append(c.clusterManagementAddon, c.managedClusteraddon...) - fakeAddonClient := fakeaddon.NewSimpleClientset(obj...) - - addonInformers := addoninformers.NewSharedInformerFactory(fakeAddonClient, 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) - } - } - - controller := addonOwnerController{ - addonClient: fakeAddonClient, - clusterManagementAddonLister: addonInformers.Addon().V1alpha1().ClusterManagementAddOns().Lister(), - managedClusterAddonLister: addonInformers.Addon().V1alpha1().ManagedClusterAddOns().Lister(), - addonFilterFunc: utils.ManagedByAddonManager, - } - - syncContext := addontesting.NewFakeSyncContext(t) - err := controller.sync(context.TODO(), syncContext, c.syncKey) - if err != nil { - t.Errorf("expected no error when sync: %v", err) - } - c.validateAddonActions(t, fakeAddonClient.Actions()) - }) - } -} diff --git a/test/integration/suite_test.go b/test/integration/suite_test.go index 0181cf1ea..73a9b7aaf 100644 --- a/test/integration/suite_test.go +++ b/test/integration/suite_test.go @@ -9,7 +9,6 @@ import ( "github.com/onsi/gomega" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/kubernetes" @@ -98,11 +97,11 @@ var _ = ginkgo.BeforeSuite(func(done ginkgo.Done) { name: "test-install-all", manifests: map[string][]runtime.Object{}, registrations: map[string][]addonapiv1alpha1.RegistrationConfig{}, - installStrategy: agent.InstallByLabelStrategy("default", v1.LabelSelector{ - MatchLabels: map[string]string{ - "test": "test", - }, - }), + // installStrategy: agent.InstallByLabelStrategy("default", v1.LabelSelector{ + // MatchLabels: map[string]string{ + // "test": "test", + // }, + // }), } testMultiWorksAddonImpl = &testAddon{ @@ -140,13 +139,13 @@ var _ = ginkgo.AfterSuite(func() { }) type testAddon struct { - name string - manifests map[string][]runtime.Object - registrations map[string][]addonapiv1alpha1.RegistrationConfig - approveCSR bool - cert []byte - prober *agent.HealthProber - installStrategy *agent.InstallStrategy + name string + manifests map[string][]runtime.Object + registrations map[string][]addonapiv1alpha1.RegistrationConfig + approveCSR bool + cert []byte + prober *agent.HealthProber + // installStrategy *agent.InstallStrategy hostedModeEnabled bool supportedConfigGVRs []schema.GroupVersionResource } @@ -157,9 +156,9 @@ func (t *testAddon) Manifests(cluster *clusterv1.ManagedCluster, addon *addonapi func (t *testAddon) GetAgentAddonOptions() agent.AgentAddonOptions { option := agent.AgentAddonOptions{ - AddonName: t.name, - HealthProber: t.prober, - InstallStrategy: t.installStrategy, + AddonName: t.name, + HealthProber: t.prober, + // InstallStrategy: t.installStrategy, HostedModeEnabled: t.hostedModeEnabled, SupportedConfigGVRs: t.supportedConfigGVRs, } From bf8e063d9d6f0c52caca72cce2badadc1307be8e Mon Sep 17 00:00:00 2001 From: haoqing0110 Date: Thu, 14 Mar 2024 09:43:01 +0000 Subject: [PATCH 2/4] addon ownerref to pass integration tests Signed-off-by: haoqing0110 --- pkg/addonfactory/addonfactory.go | 18 +---- pkg/agent/inteface.go | 73 ------------------- test/integration/agent_deploy_test.go | 20 ++--- test/integration/agent_hook_deploy_test.go | 9 ++- test/integration/agent_hosting_deploy_test.go | 13 ++-- .../agent_hosting_hook_deploy_test.go | 9 +-- test/integration/assertion_test.go | 16 ++++ .../cluster_management_addon_test.go | 7 +- test/integration/multiworks_test.go | 11 ++- test/integration/registration_test.go | 11 ++- test/integration/suite_test.go | 7 -- 11 files changed, 53 insertions(+), 141 deletions(-) diff --git a/pkg/addonfactory/addonfactory.go b/pkg/addonfactory/addonfactory.go index 8098d9584..173b168cd 100644 --- a/pkg/addonfactory/addonfactory.go +++ b/pkg/addonfactory/addonfactory.go @@ -55,9 +55,8 @@ func NewAgentAddonFactory(addonName string, fs embed.FS, dir string) *AgentAddon fs: fs, dir: dir, agentAddonOptions: agent.AgentAddonOptions{ - AddonName: addonName, - Registration: nil, - // InstallStrategy: nil, + AddonName: addonName, + Registration: nil, HealthProber: nil, SupportedConfigGVRs: []schema.GroupVersionResource{}, }, @@ -83,19 +82,6 @@ func (f *AgentAddonFactory) WithGetValuesFuncs(getValuesFuncs ...GetValuesFunc) return f } -// WithInstallStrategy defines the installation strategy of the manifests prescribed by Manifests(..). -// Deprecated: add annotation "addon.open-cluster-management.io/lifecycle: addon-manager" to ClusterManagementAddon -// and define install strategy in ClusterManagementAddon spec.installStrategy instead. -// The migration plan refer to https://github.com/open-cluster-management-io/ocm/issues/355. -/*func (f *AgentAddonFactory) WithInstallStrategy(strategy *agent.InstallStrategy) *AgentAddonFactory { - if strategy.InstallNamespace == "" { - strategy.InstallNamespace = AddonDefaultInstallNamespace - } - f.agentAddonOptions.InstallStrategy = strategy - - return f -}*/ - // WithAgentRegistrationOption defines how agent is registered to the hub cluster. func (f *AgentAddonFactory) WithAgentRegistrationOption(option *agent.RegistrationOption) *AgentAddonFactory { f.agentAddonOptions.Registration = option diff --git a/pkg/agent/inteface.go b/pkg/agent/inteface.go index 03890f198..3229237b0 100644 --- a/pkg/agent/inteface.go +++ b/pkg/agent/inteface.go @@ -46,14 +46,6 @@ type AgentAddonOptions struct { // +optional Registration *RegistrationOption - // InstallStrategy defines that addon should be created in which clusters. - // Addon will not be installed automatically until a ManagedClusterAddon is applied to the cluster's - // namespace if InstallStrategy is nil. - // Deprecated: use installStrategy config in ClusterManagementAddOn API instead - // The migration plan refer to https://github.com/open-cluster-management-io/ocm/issues/355. - // +optional - // InstallStrategy *InstallStrategy - // Updaters select a set of resources and define the strategies to update them. // UpdateStrategy is Update if no Updater is defined for a resource. // +optional @@ -154,23 +146,6 @@ type RegistrationOption struct { CSRSign CSRSignerFunc } -// InstallStrategy is the installation strategy of the manifests prescribed by Manifests(..). -// type InstallStrategy struct { -// *installStrategy -// } - -// type installStrategy struct { -// // InstallNamespace is target deploying namespace in the managed cluster upon automatic addon installation. -// InstallNamespace string -// -// // managedClusterFilter will filter the clusters to install the addon to. -// managedClusterFilter func(cluster *clusterv1.ManagedCluster) bool -// } - -// func (s *InstallStrategy) GetManagedClusterFilter() func(cluster *clusterv1.ManagedCluster) bool { -// return s.managedClusterFilter -// } - type Updater struct { // ResourceIdentifier sets what resources the strategy applies to ResourceIdentifier workapiv1.ResourceIdentifier @@ -255,54 +230,6 @@ func DefaultGroups(clusterName, addonName string) []string { } } -// // InstallAllStrategy indicate to install addon to all clusters -// func InstallAllStrategy(installNamespace string) *InstallStrategy { -// return &InstallStrategy{ -// &installStrategy{ -// InstallNamespace: installNamespace, -// managedClusterFilter: func(cluster *clusterv1.ManagedCluster) bool { -// return true -// }, -// }, -// } -// } - -// InstallByLabelStrategy indicate to install addon based on clusters' label -// func InstallByLabelStrategy(installNamespace string, selector metav1.LabelSelector) *InstallStrategy { -// return &InstallStrategy{ -// &installStrategy{ -// InstallNamespace: installNamespace, -// managedClusterFilter: func(cluster *clusterv1.ManagedCluster) bool { -// selector, err := metav1.LabelSelectorAsSelector(&selector) -// if err != nil { -// klog.Warningf("labels selector is not correct: %v", err) -// return false -// } -// -// if !selector.Matches(labels.Set(cluster.Labels)) { -// return false -// } -// return true -// }, -// }, -// } -// } - -// InstallByFilterFunctionStrategy indicate to install addon based on a filter function, and it will also install addons if the filter function is nil. -// func InstallByFilterFunctionStrategy(installNamespace string, f func(cluster *clusterv1.ManagedCluster) bool) *InstallStrategy { -// if f == nil { -// f = func(cluster *clusterv1.ManagedCluster) bool { -// return true -// } -// } -// return &InstallStrategy{ -// &installStrategy{ -// InstallNamespace: installNamespace, -// managedClusterFilter: f, -// }, -// } -// } - // ApprovalAllCSRs returns true for all csrs. func ApprovalAllCSRs(cluster *clusterv1.ManagedCluster, addon *addonapiv1alpha1.ManagedClusterAddOn, csr *certificatesv1.CertificateSigningRequest) bool { return true diff --git a/test/integration/agent_deploy_test.go b/test/integration/agent_deploy_test.go index 9eedbd9a9..a02d40185 100644 --- a/test/integration/agent_deploy_test.go +++ b/test/integration/agent_deploy_test.go @@ -120,6 +120,7 @@ var _ = ginkgo.Describe("Agent deploy", func() { var managedClusterName string var err error var manifestWorkName string + var cma *addonapiv1alpha1.ClusterManagementAddOn ginkgo.BeforeEach(func() { suffix := rand.String(5) managedClusterName = fmt.Sprintf("managedcluster-%s", suffix) @@ -140,8 +141,8 @@ var _ = ginkgo.Describe("Agent deploy", func() { _, err = hubKubeClient.CoreV1().Namespaces().Create(context.Background(), ns, metav1.CreateOptions{}) gomega.Expect(err).ToNot(gomega.HaveOccurred()) - cma := newClusterManagementAddon(testAddonImpl.name) - _, err = hubAddonClient.AddonV1alpha1().ClusterManagementAddOns().Create(context.Background(), + cma = newClusterManagementAddon(testAddonImpl.name) + cma, err = hubAddonClient.AddonV1alpha1().ClusterManagementAddOns().Create(context.Background(), cma, metav1.CreateOptions{}) gomega.Expect(err).ToNot(gomega.HaveOccurred()) @@ -175,8 +176,7 @@ var _ = ginkgo.Describe("Agent deploy", func() { InstallNamespace: "default", }, } - _, err = hubAddonClient.AddonV1alpha1().ManagedClusterAddOns(managedClusterName).Create(context.Background(), addon, metav1.CreateOptions{}) - gomega.Expect(err).ToNot(gomega.HaveOccurred()) + createManagedClusterAddOnwithOwnerRefs(managedClusterName, addon, cma) gomega.Eventually(func() error { work, err := hubWorkClient.WorkV1().ManifestWorks(managedClusterName).Get(context.Background(), manifestWorkName, metav1.GetOptions{}) @@ -270,8 +270,7 @@ var _ = ginkgo.Describe("Agent deploy", func() { InstallNamespace: "default", }, } - _, err = hubAddonClient.AddonV1alpha1().ManagedClusterAddOns(managedClusterName).Create(context.Background(), addon, metav1.CreateOptions{}) - gomega.Expect(err).ToNot(gomega.HaveOccurred()) + createManagedClusterAddOnwithOwnerRefs(managedClusterName, addon, cma) gomega.Eventually(func() error { work, err := hubWorkClient.WorkV1().ManifestWorks(managedClusterName).Get(context.Background(), manifestWorkName, metav1.GetOptions{}) @@ -422,8 +421,7 @@ var _ = ginkgo.Describe("Agent deploy", func() { InstallNamespace: "default", }, } - _, err = hubAddonClient.AddonV1alpha1().ManagedClusterAddOns(managedClusterName).Create(context.Background(), addon, metav1.CreateOptions{}) - gomega.Expect(err).ToNot(gomega.HaveOccurred()) + createManagedClusterAddOnwithOwnerRefs(managedClusterName, addon, cma) gomega.Eventually(func() error { work, err := hubWorkClient.WorkV1().ManifestWorks(managedClusterName).Get(context.Background(), manifestWorkName, metav1.GetOptions{}) @@ -571,8 +569,7 @@ var _ = ginkgo.Describe("Agent deploy", func() { InstallNamespace: "default", }, } - _, err = hubAddonClient.AddonV1alpha1().ManagedClusterAddOns(managedClusterName).Create(context.Background(), addon, metav1.CreateOptions{}) - gomega.Expect(err).ToNot(gomega.HaveOccurred()) + createManagedClusterAddOnwithOwnerRefs(managedClusterName, addon, cma) gomega.Eventually(func() error { work, err := hubWorkClient.WorkV1().ManifestWorks(managedClusterName).Get(context.Background(), manifestWorkName, metav1.GetOptions{}) @@ -637,8 +634,7 @@ var _ = ginkgo.Describe("Agent deploy", func() { InstallNamespace: "default", }, } - _, err = hubAddonClient.AddonV1alpha1().ManagedClusterAddOns(managedClusterName).Create(context.Background(), addon, metav1.CreateOptions{}) - gomega.Expect(err).ToNot(gomega.HaveOccurred()) + createManagedClusterAddOnwithOwnerRefs(managedClusterName, addon, cma) var work *workapiv1.ManifestWork gomega.Eventually(func() error { diff --git a/test/integration/agent_hook_deploy_test.go b/test/integration/agent_hook_deploy_test.go index 1b4dd449e..809a3891b 100644 --- a/test/integration/agent_hook_deploy_test.go +++ b/test/integration/agent_hook_deploy_test.go @@ -72,6 +72,7 @@ var jobCompleteValue = "True" var _ = ginkgo.Describe("Agent hook deploy", func() { var managedClusterName string + var cma *addonapiv1alpha1.ClusterManagementAddOn var err error ginkgo.BeforeEach(func() { @@ -93,8 +94,8 @@ var _ = ginkgo.Describe("Agent hook deploy", func() { _, err = hubKubeClient.CoreV1().Namespaces().Create(context.Background(), ns, metav1.CreateOptions{}) gomega.Expect(err).ToNot(gomega.HaveOccurred()) - cma := newClusterManagementAddon(testAddonImpl.name) - _, err = hubAddonClient.AddonV1alpha1().ClusterManagementAddOns().Create(context.Background(), + cma = newClusterManagementAddon(testAddonImpl.name) + cma, err = hubAddonClient.AddonV1alpha1().ClusterManagementAddOns().Create(context.Background(), cma, metav1.CreateOptions{}) gomega.Expect(err).ToNot(gomega.HaveOccurred()) }) @@ -126,8 +127,8 @@ var _ = ginkgo.Describe("Agent hook deploy", func() { InstallNamespace: "default", }, } - _, err = hubAddonClient.AddonV1alpha1().ManagedClusterAddOns(managedClusterName).Create(context.Background(), addon, metav1.CreateOptions{}) - gomega.Expect(err).ToNot(gomega.HaveOccurred()) + createManagedClusterAddOnwithOwnerRefs(managedClusterName, addon, cma) + // deploy manifest is deployed gomega.Eventually(func() error { work, err := hubWorkClient.WorkV1().ManifestWorks(managedClusterName).Get(context.Background(), fmt.Sprintf("addon-%s-deploy-0", testAddonImpl.name), metav1.GetOptions{}) diff --git a/test/integration/agent_hosting_deploy_test.go b/test/integration/agent_hosting_deploy_test.go index cf2587afe..6b22aba59 100644 --- a/test/integration/agent_hosting_deploy_test.go +++ b/test/integration/agent_hosting_deploy_test.go @@ -72,6 +72,7 @@ var _ = ginkgo.Describe("Agent deploy", func() { var managedClusterName, hostingClusterName string var err error var hostingManifestWorkName string + var cma *addonapiv1alpha1.ClusterManagementAddOn ginkgo.BeforeEach(func() { suffix := rand.String(5) managedClusterName = fmt.Sprintf("managedcluster-%s", suffix) @@ -110,8 +111,8 @@ var _ = ginkgo.Describe("Agent deploy", func() { _, err = hubKubeClient.CoreV1().Namespaces().Create(context.Background(), ns, metav1.CreateOptions{}) gomega.Expect(err).ToNot(gomega.HaveOccurred()) - cma := newClusterManagementAddon(testHostedAddonImpl.name) - _, err = hubAddonClient.AddonV1alpha1().ClusterManagementAddOns().Create(context.Background(), + cma = newClusterManagementAddon(testHostedAddonImpl.name) + cma, err = hubAddonClient.AddonV1alpha1().ClusterManagementAddOns().Create(context.Background(), cma, metav1.CreateOptions{}) gomega.Expect(err).ToNot(gomega.HaveOccurred()) }) @@ -156,9 +157,7 @@ var _ = ginkgo.Describe("Agent deploy", func() { InstallNamespace: "default", }, } - _, err = hubAddonClient.AddonV1alpha1().ManagedClusterAddOns(managedClusterName).Create( - context.Background(), addon, metav1.CreateOptions{}) - gomega.Expect(err).ToNot(gomega.HaveOccurred()) + createManagedClusterAddOnwithOwnerRefs(managedClusterName, addon, cma) gomega.Eventually(func() error { work, err := hubWorkClient.WorkV1().ManifestWorks(hostingClusterName).Get(context.Background(), @@ -278,9 +277,7 @@ var _ = ginkgo.Describe("Agent deploy", func() { InstallNamespace: "default", }, } - _, err = hubAddonClient.AddonV1alpha1().ManagedClusterAddOns(managedClusterName).Create( - context.Background(), addon, metav1.CreateOptions{}) - gomega.Expect(err).ToNot(gomega.HaveOccurred()) + createManagedClusterAddOnwithOwnerRefs(managedClusterName, addon, cma) gomega.Eventually(func() error { work, err := hubWorkClient.WorkV1().ManifestWorks(hostingClusterName).Get(context.Background(), diff --git a/test/integration/agent_hosting_hook_deploy_test.go b/test/integration/agent_hosting_hook_deploy_test.go index cda0507a5..afbd9a0d1 100644 --- a/test/integration/agent_hosting_hook_deploy_test.go +++ b/test/integration/agent_hosting_hook_deploy_test.go @@ -75,6 +75,7 @@ var _ = ginkgo.Describe("Agent hook deploy", func() { var err error var hostingManifestWorkName string var hostingJobCompleteValue = "True" + var cma *addonapiv1alpha1.ClusterManagementAddOn ginkgo.BeforeEach(func() { suffix := rand.String(5) managedClusterName = fmt.Sprintf("managedcluster-%s", suffix) @@ -113,8 +114,8 @@ var _ = ginkgo.Describe("Agent hook deploy", func() { _, err = hubKubeClient.CoreV1().Namespaces().Create(context.Background(), ns, metav1.CreateOptions{}) gomega.Expect(err).ToNot(gomega.HaveOccurred()) - cma := newClusterManagementAddon(testHostedAddonImpl.name) - _, err = hubAddonClient.AddonV1alpha1().ClusterManagementAddOns().Create(context.Background(), + cma = newClusterManagementAddon(testHostedAddonImpl.name) + cma, err = hubAddonClient.AddonV1alpha1().ClusterManagementAddOns().Create(context.Background(), cma, metav1.CreateOptions{}) gomega.Expect(err).ToNot(gomega.HaveOccurred()) }) @@ -163,9 +164,7 @@ var _ = ginkgo.Describe("Agent hook deploy", func() { InstallNamespace: "default", }, } - _, err = hubAddonClient.AddonV1alpha1().ManagedClusterAddOns(managedClusterName).Create( - context.Background(), addon, metav1.CreateOptions{}) - gomega.Expect(err).ToNot(gomega.HaveOccurred()) + createManagedClusterAddOnwithOwnerRefs(managedClusterName, addon, cma) gomega.Eventually(func() error { work, err := hubWorkClient.WorkV1().ManifestWorks(hostingClusterName).Get(context.Background(), diff --git a/test/integration/assertion_test.go b/test/integration/assertion_test.go index 14fa1189f..ee7b99b78 100644 --- a/test/integration/assertion_test.go +++ b/test/integration/assertion_test.go @@ -12,6 +12,7 @@ import ( "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "open-cluster-management.io/addon-framework/pkg/utils" addonapiv1alpha1 "open-cluster-management.io/api/addon/v1alpha1" ) @@ -102,6 +103,21 @@ func updateClusterManagementAddOn(ctx context.Context, new *addonapiv1alpha1.Clu }, eventuallyTimeout, eventuallyInterval).Should(gomega.BeTrue()) } +func createManagedClusterAddOnwithOwnerRefs(namespace string, addon *addonapiv1alpha1.ManagedClusterAddOn, cma *addonapiv1alpha1.ClusterManagementAddOn) { + addon, err := hubAddonClient.AddonV1alpha1().ManagedClusterAddOns(namespace).Create(context.Background(), addon, metav1.CreateOptions{}) + gomega.Expect(err).ToNot(gomega.HaveOccurred()) + + addonCopy := addon.DeepCopy() + + // This is to assume that addon-manager has already added the OwnerReferences. + owner := metav1.NewControllerRef(cma, addonapiv1alpha1.GroupVersion.WithKind("ClusterManagementAddOn")) + modified := utils.MergeOwnerRefs(&addonCopy.OwnerReferences, *owner, false) + if modified { + _, err = hubAddonClient.AddonV1alpha1().ManagedClusterAddOns(addonCopy.Namespace).Update(context.Background(), addonCopy, metav1.UpdateOptions{}) + gomega.Expect(err).ToNot(gomega.HaveOccurred()) + } +} + func updateManagedClusterAddOnStatus(ctx context.Context, new *addonapiv1alpha1.ManagedClusterAddOn) { gomega.Eventually(func() bool { old, err := hubAddonClient.AddonV1alpha1().ManagedClusterAddOns(new.Namespace).Get(context.Background(), new.Name, metav1.GetOptions{}) diff --git a/test/integration/cluster_management_addon_test.go b/test/integration/cluster_management_addon_test.go index 4fd737582..2658655ee 100644 --- a/test/integration/cluster_management_addon_test.go +++ b/test/integration/cluster_management_addon_test.go @@ -57,7 +57,7 @@ var _ = ginkgo.Describe("ClusterManagementAddon", func() { ginkgo.It("Should update config related object successfully", func() { // Create clustermanagement addon - clusterManagementAddon := &addonapiv1alpha1.ClusterManagementAddOn{ + cma := &addonapiv1alpha1.ClusterManagementAddOn{ ObjectMeta: metav1.ObjectMeta{ Name: testAddonImpl.name, }, @@ -67,7 +67,7 @@ var _ = ginkgo.Describe("ClusterManagementAddon", func() { }, }, } - _, err = hubAddonClient.AddonV1alpha1().ClusterManagementAddOns().Create(context.Background(), clusterManagementAddon, metav1.CreateOptions{}) + cma, err := hubAddonClient.AddonV1alpha1().ClusterManagementAddOns().Create(context.Background(), cma, metav1.CreateOptions{}) gomega.Expect(err).ToNot(gomega.HaveOccurred()) // Create managed cluster addon @@ -79,8 +79,7 @@ var _ = ginkgo.Describe("ClusterManagementAddon", func() { InstallNamespace: "test", }, } - _, err = hubAddonClient.AddonV1alpha1().ManagedClusterAddOns(managedClusterName).Create(context.Background(), addon, metav1.CreateOptions{}) - gomega.Expect(err).ToNot(gomega.HaveOccurred()) + createManagedClusterAddOnwithOwnerRefs(managedClusterName, addon, cma) gomega.Eventually(func() error { actual, err := hubAddonClient.AddonV1alpha1().ManagedClusterAddOns(managedClusterName).Get(context.Background(), testAddonImpl.name, metav1.GetOptions{}) diff --git a/test/integration/multiworks_test.go b/test/integration/multiworks_test.go index c939dac3b..293cfeb50 100644 --- a/test/integration/multiworks_test.go +++ b/test/integration/multiworks_test.go @@ -93,6 +93,7 @@ var _ = ginkgo.Describe("Agent deploy multi works", func() { var managedClusterName string var err error var manifestWorkName0, manifestWorkName1 string + var cma *addonapiv1alpha1.ClusterManagementAddOn ginkgo.BeforeEach(func() { suffix := rand.String(5) managedClusterName = fmt.Sprintf("managedcluster-%s", suffix) @@ -113,8 +114,8 @@ var _ = ginkgo.Describe("Agent deploy multi works", func() { _, err = hubKubeClient.CoreV1().Namespaces().Create(context.Background(), ns, metav1.CreateOptions{}) gomega.Expect(err).ToNot(gomega.HaveOccurred()) - cma := newClusterManagementAddon(testMultiWorksAddonImpl.name) - _, err = hubAddonClient.AddonV1alpha1().ClusterManagementAddOns().Create(context.Background(), + cma = newClusterManagementAddon(testMultiWorksAddonImpl.name) + cma, err = hubAddonClient.AddonV1alpha1().ClusterManagementAddOns().Create(context.Background(), cma, metav1.CreateOptions{}) gomega.Expect(err).ToNot(gomega.HaveOccurred()) }) @@ -157,8 +158,7 @@ var _ = ginkgo.Describe("Agent deploy multi works", func() { InstallNamespace: "default", }, } - _, err = hubAddonClient.AddonV1alpha1().ManagedClusterAddOns(managedClusterName).Create(context.Background(), addon, metav1.CreateOptions{}) - gomega.Expect(err).ToNot(gomega.HaveOccurred()) + createManagedClusterAddOnwithOwnerRefs(managedClusterName, addon, cma) gomega.Eventually(func() error { works, err := hubWorkClient.WorkV1().ManifestWorks(managedClusterName).List(context.Background(), metav1.ListOptions{ @@ -347,8 +347,7 @@ var _ = ginkgo.Describe("Agent deploy multi works", func() { InstallNamespace: "default", }, } - _, err = hubAddonClient.AddonV1alpha1().ManagedClusterAddOns(managedClusterName).Create(context.Background(), addon, metav1.CreateOptions{}) - gomega.Expect(err).ToNot(gomega.HaveOccurred()) + createManagedClusterAddOnwithOwnerRefs(managedClusterName, addon, cma) gomega.Eventually(func() error { works, err := hubWorkClient.WorkV1().ManifestWorks(managedClusterName).List(context.Background(), metav1.ListOptions{ diff --git a/test/integration/registration_test.go b/test/integration/registration_test.go index 18eaa9add..a243defc4 100644 --- a/test/integration/registration_test.go +++ b/test/integration/registration_test.go @@ -18,6 +18,7 @@ import ( var _ = ginkgo.Describe("Addon Registration", func() { var managedClusterName string + var cma *addonapiv1alpha1.ClusterManagementAddOn var err error ginkgo.BeforeEach(func() { @@ -40,7 +41,7 @@ var _ = ginkgo.Describe("Addon Registration", func() { gomega.Expect(err).ToNot(gomega.HaveOccurred()) // Create clustermanagement addon - clusterManagementAddon := &addonapiv1alpha1.ClusterManagementAddOn{ + cma = &addonapiv1alpha1.ClusterManagementAddOn{ ObjectMeta: metav1.ObjectMeta{ Name: testAddonImpl.name, }, @@ -50,7 +51,7 @@ var _ = ginkgo.Describe("Addon Registration", func() { }, }, } - _, err = hubAddonClient.AddonV1alpha1().ClusterManagementAddOns().Create(context.Background(), clusterManagementAddon, metav1.CreateOptions{}) + cma, err = hubAddonClient.AddonV1alpha1().ClusterManagementAddOns().Create(context.Background(), cma, metav1.CreateOptions{}) gomega.Expect(err).ToNot(gomega.HaveOccurred()) }) @@ -79,8 +80,7 @@ var _ = ginkgo.Describe("Addon Registration", func() { InstallNamespace: "test", }, } - _, err = hubAddonClient.AddonV1alpha1().ManagedClusterAddOns(managedClusterName).Create(context.Background(), addon, metav1.CreateOptions{}) - gomega.Expect(err).ToNot(gomega.HaveOccurred()) + createManagedClusterAddOnwithOwnerRefs(managedClusterName, addon, cma) gomega.Eventually(func() error { actual, err := hubAddonClient.AddonV1alpha1().ManagedClusterAddOns(managedClusterName).Get(context.Background(), testAddonImpl.name, metav1.GetOptions{}) @@ -112,8 +112,7 @@ var _ = ginkgo.Describe("Addon Registration", func() { InstallNamespace: "test", }, } - _, err = hubAddonClient.AddonV1alpha1().ManagedClusterAddOns(managedClusterName).Create(context.Background(), addon, metav1.CreateOptions{}) - gomega.Expect(err).ToNot(gomega.HaveOccurred()) + createManagedClusterAddOnwithOwnerRefs(managedClusterName, addon, cma) gomega.Eventually(func() error { actual, err := hubAddonClient.AddonV1alpha1().ManagedClusterAddOns(managedClusterName).Get(context.Background(), testAddonImpl.name, metav1.GetOptions{}) diff --git a/test/integration/suite_test.go b/test/integration/suite_test.go index 73a9b7aaf..806ebfc7a 100644 --- a/test/integration/suite_test.go +++ b/test/integration/suite_test.go @@ -97,11 +97,6 @@ var _ = ginkgo.BeforeSuite(func(done ginkgo.Done) { name: "test-install-all", manifests: map[string][]runtime.Object{}, registrations: map[string][]addonapiv1alpha1.RegistrationConfig{}, - // installStrategy: agent.InstallByLabelStrategy("default", v1.LabelSelector{ - // MatchLabels: map[string]string{ - // "test": "test", - // }, - // }), } testMultiWorksAddonImpl = &testAddon{ @@ -145,7 +140,6 @@ type testAddon struct { approveCSR bool cert []byte prober *agent.HealthProber - // installStrategy *agent.InstallStrategy hostedModeEnabled bool supportedConfigGVRs []schema.GroupVersionResource } @@ -158,7 +152,6 @@ func (t *testAddon) GetAgentAddonOptions() agent.AgentAddonOptions { option := agent.AgentAddonOptions{ AddonName: t.name, HealthProber: t.prober, - // InstallStrategy: t.installStrategy, HostedModeEnabled: t.hostedModeEnabled, SupportedConfigGVRs: t.supportedConfigGVRs, } From 4eca494c94a99cc880cc09734cbaa772cce6c58f Mon Sep 17 00:00:00 2001 From: haoqing0110 Date: Tue, 19 Mar 2024 15:10:25 +0000 Subject: [PATCH 3/4] modify helloworld addon Signed-off-by: haoqing0110 --- .../resources/cluster_management_addon.yaml | 2 - .../helloworld_clustermanagementaddon.yaml | 2 - .../resources/cluster_management_addon.yaml | 2 - test/e2e/helloworld_helm_test.go | 6 +- test/e2e/helloworld_test.go | 2 +- test/integration/assertion_test.go | 2 + test/integration/suite_test.go | 16 +- vendor/modules.txt | 2 - .../pkg/apis/cluster/v1alpha1/rollout.go | 616 ------------------ .../pkg/apis/cluster/v1beta1/placement.go | 273 -------- 10 files changed, 14 insertions(+), 909 deletions(-) delete mode 100644 vendor/open-cluster-management.io/sdk-go/pkg/apis/cluster/v1alpha1/rollout.go delete mode 100644 vendor/open-cluster-management.io/sdk-go/pkg/apis/cluster/v1beta1/placement.go diff --git a/examples/deploy/addon/helloworld-template/resources/cluster_management_addon.yaml b/examples/deploy/addon/helloworld-template/resources/cluster_management_addon.yaml index 3b728cec1..e46709884 100644 --- a/examples/deploy/addon/helloworld-template/resources/cluster_management_addon.yaml +++ b/examples/deploy/addon/helloworld-template/resources/cluster_management_addon.yaml @@ -2,8 +2,6 @@ apiVersion: addon.open-cluster-management.io/v1alpha1 kind: ClusterManagementAddOn metadata: name: hello-template - annotations: - addon.open-cluster-management.io/lifecycle: "addon-manager" spec: addOnMeta: description: hello-template diff --git a/examples/deploy/addon/helloworld/resources/helloworld_clustermanagementaddon.yaml b/examples/deploy/addon/helloworld/resources/helloworld_clustermanagementaddon.yaml index 77bf16d68..ebc5acac4 100644 --- a/examples/deploy/addon/helloworld/resources/helloworld_clustermanagementaddon.yaml +++ b/examples/deploy/addon/helloworld/resources/helloworld_clustermanagementaddon.yaml @@ -2,8 +2,6 @@ apiVersion: addon.open-cluster-management.io/v1alpha1 kind: ClusterManagementAddOn metadata: name: helloworld - annotations: - "addon.open-cluster-management.io/lifecycle": "addon-manager" spec: addOnMeta: displayName: helloworld diff --git a/examples/deploy/addon/kubernetes-dashboard/resources/cluster_management_addon.yaml b/examples/deploy/addon/kubernetes-dashboard/resources/cluster_management_addon.yaml index f27b126ad..5663fda95 100644 --- a/examples/deploy/addon/kubernetes-dashboard/resources/cluster_management_addon.yaml +++ b/examples/deploy/addon/kubernetes-dashboard/resources/cluster_management_addon.yaml @@ -2,8 +2,6 @@ apiVersion: addon.open-cluster-management.io/v1alpha1 kind: ClusterManagementAddOn metadata: name: kubernetes-dashboard - annotations: - addon.open-cluster-management.io/lifecycle: "addon-manager" spec: addOnMeta: description: kubernetes-dashboard diff --git a/test/e2e/helloworld_helm_test.go b/test/e2e/helloworld_helm_test.go index d4f9a91d7..ea3273a80 100644 --- a/test/e2e/helloworld_helm_test.go +++ b/test/e2e/helloworld_helm_test.go @@ -144,15 +144,15 @@ var _ = ginkgo.Describe("install/uninstall helloworld helm addons", func() { }) ginkgo.It("addon should be available", func() { - ginkgo.By("Make sure cma annotation is not added since no install strategy defined") + ginkgo.By("Make sure cma annotation managed by addon-manager is added") gomega.Eventually(func() error { cma, err := hubAddOnClient.AddonV1alpha1().ClusterManagementAddOns().Get(context.Background(), helloWorldHelmAddonName, metav1.GetOptions{}) if err != nil { return err } - if _, exist := cma.Annotations[addonapiv1alpha1.AddonLifecycleAnnotationKey]; exist { - return fmt.Errorf("addon should not have annotation, but get %v", cma.Annotations) + if cma.Annotations[addonapiv1alpha1.AddonLifecycleAnnotationKey] != addonapiv1alpha1.AddonLifecycleAddonManagerAnnotationValue { + return fmt.Errorf("addon should have annotation, but get %v", cma.Annotations) } return nil diff --git a/test/e2e/helloworld_test.go b/test/e2e/helloworld_test.go index 2bc46d3ee..1953bec54 100644 --- a/test/e2e/helloworld_test.go +++ b/test/e2e/helloworld_test.go @@ -101,7 +101,7 @@ var _ = ginkgo.Describe("install/uninstall helloworld addons", func() { }) ginkgo.It("addon should be worked", func() { - ginkgo.By("Make sure cma annotation managed by self is added") + ginkgo.By("Make sure cma annotation managed by addon-manager is added") gomega.Eventually(func() error { cma, err := hubAddOnClient.AddonV1alpha1().ClusterManagementAddOns().Get(context.Background(), addonName, metav1.GetOptions{}) if err != nil { diff --git a/test/integration/assertion_test.go b/test/integration/assertion_test.go index ee7b99b78..15c2be70e 100644 --- a/test/integration/assertion_test.go +++ b/test/integration/assertion_test.go @@ -103,6 +103,8 @@ func updateClusterManagementAddOn(ctx context.Context, new *addonapiv1alpha1.Clu }, eventuallyTimeout, eventuallyInterval).Should(gomega.BeTrue()) } +// The addon owner controller exist in general addon manager. +// This is for integration testing to assume that addon manager has already added the OwnerReferences. func createManagedClusterAddOnwithOwnerRefs(namespace string, addon *addonapiv1alpha1.ManagedClusterAddOn, cma *addonapiv1alpha1.ClusterManagementAddOn) { addon, err := hubAddonClient.AddonV1alpha1().ManagedClusterAddOns(namespace).Create(context.Background(), addon, metav1.CreateOptions{}) gomega.Expect(err).ToNot(gomega.HaveOccurred()) diff --git a/test/integration/suite_test.go b/test/integration/suite_test.go index 806ebfc7a..9c0600c88 100644 --- a/test/integration/suite_test.go +++ b/test/integration/suite_test.go @@ -134,12 +134,12 @@ var _ = ginkgo.AfterSuite(func() { }) type testAddon struct { - name string - manifests map[string][]runtime.Object - registrations map[string][]addonapiv1alpha1.RegistrationConfig - approveCSR bool - cert []byte - prober *agent.HealthProber + name string + manifests map[string][]runtime.Object + registrations map[string][]addonapiv1alpha1.RegistrationConfig + approveCSR bool + cert []byte + prober *agent.HealthProber hostedModeEnabled bool supportedConfigGVRs []schema.GroupVersionResource } @@ -150,8 +150,8 @@ func (t *testAddon) Manifests(cluster *clusterv1.ManagedCluster, addon *addonapi func (t *testAddon) GetAgentAddonOptions() agent.AgentAddonOptions { option := agent.AgentAddonOptions{ - AddonName: t.name, - HealthProber: t.prober, + AddonName: t.name, + HealthProber: t.prober, HostedModeEnabled: t.hostedModeEnabled, SupportedConfigGVRs: t.supportedConfigGVRs, } diff --git a/vendor/modules.txt b/vendor/modules.txt index 7bd2d2247..bbcfcdb03 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1344,8 +1344,6 @@ open-cluster-management.io/api/work/v1 open-cluster-management.io/api/work/v1alpha1 # open-cluster-management.io/sdk-go v0.13.0 ## explicit; go 1.21 -open-cluster-management.io/sdk-go/pkg/apis/cluster/v1alpha1 -open-cluster-management.io/sdk-go/pkg/apis/cluster/v1beta1 open-cluster-management.io/sdk-go/pkg/apis/work/v1/applier open-cluster-management.io/sdk-go/pkg/apis/work/v1/builder open-cluster-management.io/sdk-go/pkg/patcher diff --git a/vendor/open-cluster-management.io/sdk-go/pkg/apis/cluster/v1alpha1/rollout.go b/vendor/open-cluster-management.io/sdk-go/pkg/apis/cluster/v1alpha1/rollout.go deleted file mode 100644 index 23e04c474..000000000 --- a/vendor/open-cluster-management.io/sdk-go/pkg/apis/cluster/v1alpha1/rollout.go +++ /dev/null @@ -1,616 +0,0 @@ -package v1alpha1 - -import ( - "fmt" - "math" - "regexp" - "sort" - "strconv" - "strings" - "time" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/utils/clock" - clusterv1alpha1 "open-cluster-management.io/api/cluster/v1alpha1" - clusterv1beta1sdk "open-cluster-management.io/sdk-go/pkg/apis/cluster/v1beta1" -) - -var RolloutClock = clock.Clock(clock.RealClock{}) -var maxTimeDuration = time.Duration(math.MaxInt64) - -// RolloutStatus represents the status of a rollout operation. -type RolloutStatus int - -const ( - // ToApply indicates that the resource's desired status has not been applied yet. - ToApply RolloutStatus = iota - // Progressing indicates that the resource's desired status is applied and last applied status is not updated. - Progressing - // Succeeded indicates that the resource's desired status is applied and last applied status is successful. - Succeeded - // Failed indicates that the resource's desired status is applied and last applied status has failed. - Failed - // TimeOut indicates that the rollout status is progressing or failed and the status remains - // for longer than the timeout, resulting in a timeout status. - TimeOut - // Skip indicates that the rollout should be skipped on this cluster. - Skip -) - -// ClusterRolloutStatus holds the rollout status information for a cluster. -type ClusterRolloutStatus struct { - // cluster name - ClusterName string - // GroupKey represents the cluster group key (optional field). - GroupKey clusterv1beta1sdk.GroupKey - // Status is the required field indicating the rollout status. - Status RolloutStatus - // LastTransitionTime is the last transition time of the rollout status (optional field). - // Used to calculate timeout for progressing and failed status and minimum success time (i.e. soak - // time) for succeeded status. - LastTransitionTime *metav1.Time - // TimeOutTime is the timeout time when the status is progressing or failed (optional field). - TimeOutTime *metav1.Time -} - -// RolloutResult contains list of clusters that are timeOut, removed and required to rollOut. A -// boolean is also provided signaling that the rollout may be shortened due to the number of failed -// clusters exceeding the MaxFailure threshold. -type RolloutResult struct { - // ClustersToRollout is a slice of ClusterRolloutStatus that will be rolled out. - ClustersToRollout []ClusterRolloutStatus - // ClustersTimeOut is a slice of ClusterRolloutStatus that are timeout. - ClustersTimeOut []ClusterRolloutStatus - // ClustersRemoved is a slice of ClusterRolloutStatus that are removed. - ClustersRemoved []ClusterRolloutStatus - // MaxFailureBreach is a boolean signaling whether the rollout was cut short because of failed clusters. - MaxFailureBreach bool - // RecheckAfter is the time duration to recheck the rollout status. - RecheckAfter *time.Duration -} - -// ClusterRolloutStatusFunc defines a function that return the rollout status for a given workload. -type ClusterRolloutStatusFunc[T any] func(clusterName string, workload T) (ClusterRolloutStatus, error) - -// The RolloutHandler required workload type (interface/struct) to be assigned to the generic type. -// The custom implementation of the ClusterRolloutStatusFunc is required to use the RolloutHandler. -type RolloutHandler[T any] struct { - // placement decision tracker - pdTracker *clusterv1beta1sdk.PlacementDecisionClustersTracker - statusFunc ClusterRolloutStatusFunc[T] -} - -// NewRolloutHandler creates a new RolloutHandler with the given workload type. -func NewRolloutHandler[T any](pdTracker *clusterv1beta1sdk.PlacementDecisionClustersTracker, statusFunc ClusterRolloutStatusFunc[T]) (*RolloutHandler[T], error) { - if pdTracker == nil { - return nil, fmt.Errorf("invalid placement decision tracker %v", pdTracker) - } - - return &RolloutHandler[T]{pdTracker: pdTracker, statusFunc: statusFunc}, nil -} - -// The inputs are a RolloutStrategy and existingClusterRolloutStatus list. -// The existing ClusterRolloutStatus list should be created using the ClusterRolloutStatusFunc to determine the current workload rollout status. -// The existing ClusterRolloutStatus list should contain all the current workloads rollout status such as ToApply, Progressing, Succeeded, -// Failed, TimeOut and Skip in order to determine the added, removed, timeout clusters and next clusters to rollout. -// -// Return the actual RolloutStrategy that take effect and a RolloutResult contain list of ClusterToRollout, ClustersTimeout and ClusterRemoved. -func (r *RolloutHandler[T]) GetRolloutCluster(rolloutStrategy clusterv1alpha1.RolloutStrategy, existingClusterStatus []ClusterRolloutStatus) (*clusterv1alpha1.RolloutStrategy, RolloutResult, error) { - switch rolloutStrategy.Type { - case clusterv1alpha1.All: - return r.getRolloutAllClusters(rolloutStrategy, existingClusterStatus) - case clusterv1alpha1.Progressive: - return r.getProgressiveClusters(rolloutStrategy, existingClusterStatus) - case clusterv1alpha1.ProgressivePerGroup: - return r.getProgressivePerGroupClusters(rolloutStrategy, existingClusterStatus) - default: - return nil, RolloutResult{}, fmt.Errorf("incorrect rollout strategy type %v", rolloutStrategy.Type) - } -} - -func (r *RolloutHandler[T]) getRolloutAllClusters(rolloutStrategy clusterv1alpha1.RolloutStrategy, existingClusterStatus []ClusterRolloutStatus) (*clusterv1alpha1.RolloutStrategy, RolloutResult, error) { - // Prepare the rollout strategy - strategy := clusterv1alpha1.RolloutStrategy{Type: clusterv1alpha1.All} - strategy.All = rolloutStrategy.All.DeepCopy() - if strategy.All == nil { - strategy.All = &clusterv1alpha1.RolloutAll{} - } - - // Parse timeout for the rollout - failureTimeout, err := parseTimeout(strategy.All.ProgressDeadline) - if err != nil { - return &strategy, RolloutResult{}, err - } - - allClusterGroups := r.pdTracker.ExistingClusterGroupsBesides() - allClusters := allClusterGroups.GetClusters().UnsortedList() - - // Check for removed Clusters - currentClusterStatus, removedClusterStatus := r.getRemovedClusters(allClusterGroups, existingClusterStatus) - rolloutResult := progressivePerCluster(allClusterGroups, len(allClusters), len(allClusters), time.Duration(0), failureTimeout, currentClusterStatus) - rolloutResult.ClustersRemoved = removedClusterStatus - - return &strategy, rolloutResult, nil -} - -func (r *RolloutHandler[T]) getProgressiveClusters(rolloutStrategy clusterv1alpha1.RolloutStrategy, existingClusterStatus []ClusterRolloutStatus) (*clusterv1alpha1.RolloutStrategy, RolloutResult, error) { - // Prepare the rollout strategy - strategy := clusterv1alpha1.RolloutStrategy{Type: clusterv1alpha1.Progressive} - strategy.Progressive = rolloutStrategy.Progressive.DeepCopy() - if strategy.Progressive == nil { - strategy.Progressive = &clusterv1alpha1.RolloutProgressive{} - } - minSuccessTime := strategy.Progressive.MinSuccessTime.Duration - - // Parse timeout for non-mandatory decision groups - failureTimeout, err := parseTimeout(strategy.Progressive.ProgressDeadline) - if err != nil { - return &strategy, RolloutResult{}, err - } - - // Check for removed clusters - clusterGroups := r.pdTracker.ExistingClusterGroupsBesides() - currentClusterStatus, removedClusterStatus := r.getRemovedClusters(clusterGroups, existingClusterStatus) - - // Parse maximum failure threshold for continuing the rollout, defaulting to zero - maxFailures, err := calculateRolloutSize(strategy.Progressive.MaxFailures, len(clusterGroups.GetClusters()), 0) - if err != nil { - return &strategy, RolloutResult{}, fmt.Errorf("failed to parse the provided maxFailures: %w", err) - } - - // Upgrade mandatory decision groups first - groupKeys := decisionGroupsToGroupKeys(strategy.Progressive.MandatoryDecisionGroups.MandatoryDecisionGroups) - clusterGroups = r.pdTracker.ExistingClusterGroups(groupKeys...) - - // Perform progressive rollOut for mandatory decision groups first, tolerating no failures - if len(clusterGroups) > 0 { - rolloutResult := progressivePerGroup( - clusterGroups, intstr.FromInt32(0), minSuccessTime, failureTimeout, currentClusterStatus, - ) - if len(rolloutResult.ClustersToRollout) > 0 || len(rolloutResult.ClustersTimeOut) > 0 { - rolloutResult.ClustersRemoved = removedClusterStatus - return &strategy, rolloutResult, nil - } - } - - // Calculate the size of progressive rollOut - // If the MaxConcurrency not defined, total clusters length is considered as maxConcurrency. - clusterGroups = r.pdTracker.ExistingClusterGroupsBesides(groupKeys...) - rolloutSize, err := calculateRolloutSize(strategy.Progressive.MaxConcurrency, len(clusterGroups.GetClusters()), len(clusterGroups.GetClusters())) - if err != nil { - return &strategy, RolloutResult{}, fmt.Errorf("failed to parse the provided maxConcurrency: %w", err) - } - - // Rollout the remaining clusters - rolloutResult := progressivePerCluster(clusterGroups, rolloutSize, maxFailures, minSuccessTime, failureTimeout, currentClusterStatus) - rolloutResult.ClustersRemoved = removedClusterStatus - - return &strategy, rolloutResult, nil -} - -func (r *RolloutHandler[T]) getProgressivePerGroupClusters(rolloutStrategy clusterv1alpha1.RolloutStrategy, existingClusterStatus []ClusterRolloutStatus) (*clusterv1alpha1.RolloutStrategy, RolloutResult, error) { - // Prepare the rollout strategy - strategy := clusterv1alpha1.RolloutStrategy{Type: clusterv1alpha1.ProgressivePerGroup} - strategy.ProgressivePerGroup = rolloutStrategy.ProgressivePerGroup.DeepCopy() - if strategy.ProgressivePerGroup == nil { - strategy.ProgressivePerGroup = &clusterv1alpha1.RolloutProgressivePerGroup{} - } - minSuccessTime := strategy.ProgressivePerGroup.MinSuccessTime.Duration - maxFailures := strategy.ProgressivePerGroup.MaxFailures - - // Parse timeout for non-mandatory decision groups - failureTimeout, err := parseTimeout(strategy.ProgressivePerGroup.ProgressDeadline) - if err != nil { - return &strategy, RolloutResult{}, err - } - - // Check format of MaxFailures--this value will be re-parsed and used in progressivePerGroup() - err = parseRolloutSize(maxFailures) - if err != nil { - return &strategy, RolloutResult{}, fmt.Errorf("failed to parse the provided maxFailures: %w", err) - } - - // Check for removed Clusters - clusterGroups := r.pdTracker.ExistingClusterGroupsBesides() - currentClusterStatus, removedClusterStatus := r.getRemovedClusters(clusterGroups, existingClusterStatus) - - // Upgrade mandatory decision groups first - mandatoryDecisionGroups := strategy.ProgressivePerGroup.MandatoryDecisionGroups.MandatoryDecisionGroups - groupKeys := decisionGroupsToGroupKeys(mandatoryDecisionGroups) - clusterGroups = r.pdTracker.ExistingClusterGroups(groupKeys...) - - // Perform progressive rollout per group for mandatory decision groups first, tolerating no failures - if len(clusterGroups) > 0 { - rolloutResult := progressivePerGroup(clusterGroups, intstr.FromInt32(0), minSuccessTime, failureTimeout, currentClusterStatus) - - if len(rolloutResult.ClustersToRollout) > 0 || len(rolloutResult.ClustersTimeOut) > 0 { - rolloutResult.ClustersRemoved = removedClusterStatus - return &strategy, rolloutResult, nil - } - } - - // RollOut the rest of the decision groups - restClusterGroups := r.pdTracker.ExistingClusterGroupsBesides(groupKeys...) - - // Perform progressive rollout per group for the remaining decision groups - rolloutResult := progressivePerGroup(restClusterGroups, maxFailures, minSuccessTime, failureTimeout, currentClusterStatus) - rolloutResult.ClustersRemoved = removedClusterStatus - - return &strategy, rolloutResult, nil -} - -func (r *RolloutHandler[T]) getRemovedClusters(clusterGroupsMap clusterv1beta1sdk.ClusterGroupsMap, existingClusterStatus []ClusterRolloutStatus) ([]ClusterRolloutStatus, []ClusterRolloutStatus) { - var currentClusterStatus, removedClusterStatus []ClusterRolloutStatus - - clusters := clusterGroupsMap.GetClusters().UnsortedList() - for _, clusterStatus := range existingClusterStatus { - exist := false - for _, cluster := range clusters { - if clusterStatus.ClusterName == cluster { - exist = true - currentClusterStatus = append(currentClusterStatus, clusterStatus) - break - } - } - - if !exist { - removedClusterStatus = append(removedClusterStatus, clusterStatus) - } - } - return currentClusterStatus, removedClusterStatus -} - -// progressivePerCluster parses the rollout status for the given clusters and returns the rollout -// result. It sorts the clusters alphabetically in order to determine the rollout groupings and the -// rollout group size is determined by the MaxConcurrency setting. -func progressivePerCluster( - clusterGroupsMap clusterv1beta1sdk.ClusterGroupsMap, - rolloutSize int, - maxFailures int, - minSuccessTime time.Duration, - timeout time.Duration, - existingClusterStatus []ClusterRolloutStatus, -) RolloutResult { - var rolloutClusters, timeoutClusters []ClusterRolloutStatus - existingClusters := make(map[string]bool) - failureCount := 0 - failureBreach := false - - // Sort existing cluster status for consistency in case ToApply was determined by the workload applier - sort.Slice(existingClusterStatus, func(i, j int) bool { - return existingClusterStatus[i].ClusterName < existingClusterStatus[j].ClusterName - }) - - // Collect existing cluster status and determine any TimeOut statuses - for _, status := range existingClusterStatus { - if status.ClusterName == "" { - continue - } - - existingClusters[status.ClusterName] = true - - // If there was a breach of MaxFailures, only handle clusters that have already had workload applied - if !failureBreach || failureBreach && status.Status != ToApply { - // For progress per cluster, the length of existing `rolloutClusters` will be compared with the - // target rollout size to determine whether to return or not first. - // The timeoutClusters, as well as failed clusters will be counted into failureCount, the next rollout - // will stop if failureCount > maxFailures. - rolloutClusters, timeoutClusters = determineRolloutStatus(&status, minSuccessTime, timeout, rolloutClusters, timeoutClusters) - } - - // Keep track of TimeOut or Failed clusters and check total against MaxFailures - if status.Status == TimeOut || status.Status == Failed { - failureCount++ - - failureBreach = failureCount > maxFailures - } - - // Return if the list of exsiting rollout clusters has reached the target rollout size - if len(rolloutClusters) >= rolloutSize { - return RolloutResult{ - ClustersToRollout: rolloutClusters, - ClustersTimeOut: timeoutClusters, - MaxFailureBreach: failureBreach, - RecheckAfter: minRecheckAfter(rolloutClusters, minSuccessTime), - } - } - } - - // Return if the exsiting rollout clusters maxFailures is breached. - if failureBreach { - return RolloutResult{ - ClustersToRollout: rolloutClusters, - ClustersTimeOut: timeoutClusters, - MaxFailureBreach: failureBreach, - RecheckAfter: minRecheckAfter(rolloutClusters, minSuccessTime), - } - } - - clusters := clusterGroupsMap.GetClusters().UnsortedList() - clusterToGroupKey := clusterGroupsMap.ClusterToGroupKey() - - // Sort the clusters in alphabetical order to ensure consistency. - sort.Strings(clusters) - - // Amend clusters to the rollout up to the rollout size - for _, cluster := range clusters { - if existingClusters[cluster] { - continue - } - - // For clusters without a rollout status, set the status to ToApply - status := ClusterRolloutStatus{ - ClusterName: cluster, - Status: ToApply, - GroupKey: clusterToGroupKey[cluster], - } - rolloutClusters = append(rolloutClusters, status) - - // Return if the list of rollout clusters has reached the target rollout size - if len(rolloutClusters) >= rolloutSize { - return RolloutResult{ - ClustersToRollout: rolloutClusters, - ClustersTimeOut: timeoutClusters, - RecheckAfter: minRecheckAfter(rolloutClusters, minSuccessTime), - } - } - } - - return RolloutResult{ - ClustersToRollout: rolloutClusters, - ClustersTimeOut: timeoutClusters, - RecheckAfter: minRecheckAfter(rolloutClusters, minSuccessTime), - } -} - -func progressivePerGroup( - clusterGroupsMap clusterv1beta1sdk.ClusterGroupsMap, - maxFailures intstr.IntOrString, - minSuccessTime time.Duration, - timeout time.Duration, - existingClusterStatus []ClusterRolloutStatus, -) RolloutResult { - var rolloutClusters, timeoutClusters []ClusterRolloutStatus - existingClusters := make(map[string]RolloutStatus) - - // Collect existing cluster status and determine any TimeOut statuses - for _, status := range existingClusterStatus { - if status.ClusterName == "" { - continue - } - - // ToApply will be reconsidered in the decisionGroups iteration. - if status.Status != ToApply { - // For progress per group, the existing rollout clusters and timeout clusters status will be recored in existingClusters first, - // then go through group by group. - rolloutClusters, timeoutClusters = determineRolloutStatus(&status, minSuccessTime, timeout, rolloutClusters, timeoutClusters) - existingClusters[status.ClusterName] = status.Status - } - } - - totalFailureCount := 0 - failureBreach := false - clusterGroupKeys := clusterGroupsMap.GetOrderedGroupKeys() - for _, key := range clusterGroupKeys { - groupFailureCount := 0 - if subclusters, ok := clusterGroupsMap[key]; ok { - // Calculate the max failure threshold for the group--the returned error was checked - // previously, so it's ignored here - maxGroupFailures, _ := calculateRolloutSize(maxFailures, len(subclusters), 0) - // Iterate through clusters in the group - clusters := subclusters.UnsortedList() - sort.Strings(clusters) - for _, cluster := range clusters { - if status, ok := existingClusters[cluster]; ok { - // Keep track of TimeOut or Failed clusters and check total against MaxFailures - if status == TimeOut || status == Failed { - groupFailureCount++ - - failureBreach = groupFailureCount > maxGroupFailures - } - - continue - } - - status := ClusterRolloutStatus{ - ClusterName: cluster, - Status: ToApply, - GroupKey: key, - } - rolloutClusters = append(rolloutClusters, status) - } - - totalFailureCount += groupFailureCount - - // As it is perGroup, return if there are clusters to rollOut that aren't - // Failed/Timeout, or there was a breach of the MaxFailure configuration - if len(rolloutClusters)-totalFailureCount > 0 || failureBreach { - return RolloutResult{ - ClustersToRollout: rolloutClusters, - ClustersTimeOut: timeoutClusters, - MaxFailureBreach: failureBreach, - RecheckAfter: minRecheckAfter(rolloutClusters, minSuccessTime), - } - } - } - } - - return RolloutResult{ - ClustersToRollout: rolloutClusters, - ClustersTimeOut: timeoutClusters, - MaxFailureBreach: failureBreach, - RecheckAfter: minRecheckAfter(rolloutClusters, minSuccessTime), - } -} - -// determineRolloutStatus checks whether a cluster should continue its rollout based on its current -// status and timeout. The function updates the cluster status and appends it to the expected slice. -// Nothing is done for TimeOut or Skip statuses. -// -// The minSuccessTime parameter is utilized for handling succeeded clusters that are still within -// the configured soak time, in which case the cluster will be returned as a rolloutCluster. -// -// The timeout parameter is utilized for handling progressing and failed statuses and any other -// unknown status: -// 1. If timeout is set to None (maxTimeDuration), the function will append the clusterStatus to -// the rollOut Clusters. -// 2. If timeout is set to 0, the function append the clusterStatus to the timeOut clusters. -func determineRolloutStatus( - status *ClusterRolloutStatus, - minSuccessTime time.Duration, - timeout time.Duration, - rolloutClusters []ClusterRolloutStatus, - timeoutClusters []ClusterRolloutStatus, -) ([]ClusterRolloutStatus, []ClusterRolloutStatus) { - - switch status.Status { - case ToApply: - rolloutClusters = append(rolloutClusters, *status) - case Succeeded: - // If the cluster succeeded but is still within the MinSuccessTime (i.e. "soak" time), - // still add it to the list of rolloutClusters - minSuccessTimeTime := getTimeOutTime(status.LastTransitionTime, minSuccessTime) - if RolloutClock.Now().Before(minSuccessTimeTime.Time) { - rolloutClusters = append(rolloutClusters, *status) - } - - return rolloutClusters, timeoutClusters - case TimeOut, Skip: - return rolloutClusters, timeoutClusters - default: // For progressing, failed, or unknown status. - timeOutTime := getTimeOutTime(status.LastTransitionTime, timeout) - status.TimeOutTime = timeOutTime - // check if current time is before the timeout time - if timeOutTime == nil || RolloutClock.Now().Before(timeOutTime.Time) { - rolloutClusters = append(rolloutClusters, *status) - } else { - status.Status = TimeOut - timeoutClusters = append(timeoutClusters, *status) - } - } - - return rolloutClusters, timeoutClusters -} - -// getTimeOutTime calculates the timeout time given a start time and duration, instantiating the -// RolloutClock if a start time isn't provided. -func getTimeOutTime(startTime *metav1.Time, timeout time.Duration) *metav1.Time { - var timeoutTime time.Time - // if timeout is not set (default to maxTimeDuration), the timeout time should not be set - if timeout == maxTimeDuration { - return nil - } - if startTime == nil { - timeoutTime = RolloutClock.Now().Add(timeout) - } else { - timeoutTime = startTime.Add(timeout) - } - return &metav1.Time{Time: timeoutTime} -} - -// calculateRolloutSize calculates the maximum portion from a total number of clusters by parsing a -// maximum threshold value that can be either a quantity or a percent, returning an error if the -// threshold can't be parsed to either of those. -func calculateRolloutSize(maxThreshold intstr.IntOrString, total int, defaultThreshold int) (int, error) { - length := defaultThreshold - - // Verify the format of the IntOrString value - err := parseRolloutSize(maxThreshold) - if err != nil { - return length, err - } - - // Calculate the rollout size--errors are ignored because - // they were handled in parseRolloutSize() previously - switch maxThreshold.Type { - case intstr.Int: - length = maxThreshold.IntValue() - case intstr.String: - str := maxThreshold.StrVal - f, _ := strconv.ParseFloat(str[:len(str)-1], 64) - length = int(math.Ceil(f / 100 * float64(total))) - } - - if length <= 0 || length > total { - length = defaultThreshold - } - - return length, nil -} - -// parseRolloutSize parses a maximum threshold value that can be either a quantity or a percent, -// returning an error if the threshold can't be parsed to either of those. -func parseRolloutSize(maxThreshold intstr.IntOrString) error { - - switch maxThreshold.Type { - case intstr.Int: - break - case intstr.String: - str := maxThreshold.StrVal - if strings.HasSuffix(str, "%") { - _, err := strconv.ParseFloat(str[:len(str)-1], 64) - if err != nil { - return err - } - } else { - return fmt.Errorf("'%s' is an invalid maximum threshold value: string is not a percentage", str) - } - default: - return fmt.Errorf("invalid maximum threshold type %+v", maxThreshold.Type) - } - - return nil -} - -// ParseTimeout will return the maximum possible duration given "None", an empty string, or an -// invalid duration, otherwise parsing and returning the duration provided. -func parseTimeout(timeoutStr string) (time.Duration, error) { - // Define the regex pattern to match the timeout string - pattern := "^(([0-9])+[h|m|s])|None$" - regex := regexp.MustCompile(pattern) - - if timeoutStr == "None" || timeoutStr == "" { - // If the timeout is "None" or empty, return the maximum duration - return maxTimeDuration, nil - } - - // Check if the timeout string matches the pattern - if !regex.MatchString(timeoutStr) { - return maxTimeDuration, fmt.Errorf("invalid timeout format") - } - - return time.ParseDuration(timeoutStr) -} - -func decisionGroupsToGroupKeys(decisionsGroup []clusterv1alpha1.MandatoryDecisionGroup) []clusterv1beta1sdk.GroupKey { - var result []clusterv1beta1sdk.GroupKey - for _, d := range decisionsGroup { - gk := clusterv1beta1sdk.GroupKey{} - // GroupName is considered first to select the decisionGroups then GroupIndex. - if d.GroupName != "" { - gk.GroupName = d.GroupName - } else { - gk.GroupIndex = d.GroupIndex - } - result = append(result, gk) - } - return result -} - -func minRecheckAfter(rolloutClusters []ClusterRolloutStatus, minSuccessTime time.Duration) *time.Duration { - var minRecheckAfter *time.Duration - for _, r := range rolloutClusters { - if r.TimeOutTime != nil { - timeOut := r.TimeOutTime.Sub(RolloutClock.Now()) - if minRecheckAfter == nil || *minRecheckAfter > timeOut { - minRecheckAfter = &timeOut - } - } - } - if minSuccessTime != 0 && (minRecheckAfter == nil || minSuccessTime < *minRecheckAfter) { - minRecheckAfter = &minSuccessTime - } - - return minRecheckAfter -} diff --git a/vendor/open-cluster-management.io/sdk-go/pkg/apis/cluster/v1beta1/placement.go b/vendor/open-cluster-management.io/sdk-go/pkg/apis/cluster/v1beta1/placement.go deleted file mode 100644 index 4db8d198e..000000000 --- a/vendor/open-cluster-management.io/sdk-go/pkg/apis/cluster/v1beta1/placement.go +++ /dev/null @@ -1,273 +0,0 @@ -package v1beta1 - -import ( - "fmt" - "sort" - "strconv" - "sync" - - "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/util/sets" - clusterv1beta1 "open-cluster-management.io/api/cluster/v1beta1" -) - -type PlacementDecisionGetter interface { - List(selector labels.Selector, namespace string) (ret []*clusterv1beta1.PlacementDecision, err error) -} - -type PlacementDecisionClustersTracker struct { - placement *clusterv1beta1.Placement - placementDecisionGetter PlacementDecisionGetter - existingScheduledClusterGroups ClusterGroupsMap - clusterGroupsIndexToName map[int32]string - clusterGroupsNameToIndex map[string][]int32 - lock sync.RWMutex -} - -type GroupKey struct { - GroupName string `json:"groupName,omitempty"` - GroupIndex int32 `json:"groupIndex,omitempty"` -} - -// NewPlacementDecisionClustersTracker initializes a PlacementDecisionClustersTracker -// using existing clusters. Clusters are added to the default cluster group with index 0. -// Set existingScheduledClusters to nil if there are no existing clusters. -func NewPlacementDecisionClustersTracker(placement *clusterv1beta1.Placement, pdl PlacementDecisionGetter, existingScheduledClusters sets.Set[string]) *PlacementDecisionClustersTracker { - pdct := &PlacementDecisionClustersTracker{ - placement: placement, - placementDecisionGetter: pdl, - existingScheduledClusterGroups: ClusterGroupsMap{{GroupIndex: 0}: existingScheduledClusters}, - } - - // Generate group name indices for the tracker. - pdct.generateGroupsNameIndex() - return pdct -} - -// NewPlacementDecisionClustersTrackerWithGroups initializes a PlacementDecisionClustersTracker -// using existing cluster groups. Set existingScheduledClusterGroups to nil if no groups exist. -func NewPlacementDecisionClustersTrackerWithGroups(placement *clusterv1beta1.Placement, pdl PlacementDecisionGetter, existingScheduledClusterGroups ClusterGroupsMap) *PlacementDecisionClustersTracker { - pdct := &PlacementDecisionClustersTracker{ - placement: placement, - placementDecisionGetter: pdl, - existingScheduledClusterGroups: existingScheduledClusterGroups, - } - - // Generate group name indices for the tracker. - pdct.generateGroupsNameIndex() - return pdct -} - -// Refresh refreshes the tracker's decisionClusters. -func (pdct *PlacementDecisionClustersTracker) Refresh() error { - pdct.lock.Lock() - defer pdct.lock.Unlock() - - if pdct.placement == nil || pdct.placementDecisionGetter == nil { - return nil - } - - // Get the generated PlacementDecisions - decisionSelector := labels.SelectorFromSet(labels.Set{ - clusterv1beta1.PlacementLabel: pdct.placement.Name, - }) - decisions, err := pdct.placementDecisionGetter.List(decisionSelector, pdct.placement.Namespace) - if err != nil { - return fmt.Errorf("failed to list PlacementDecisions: %w", err) - } - - // Get the decision cluster names and groups - newScheduledClusterGroups := map[GroupKey]sets.Set[string]{} - for _, d := range decisions { - groupKey, err := parseGroupKeyFromDecision(d) - if err != nil { - return err - } - - if _, exist := newScheduledClusterGroups[groupKey]; !exist { - newScheduledClusterGroups[groupKey] = sets.New[string]() - } - - for _, sd := range d.Status.Decisions { - newScheduledClusterGroups[groupKey].Insert(sd.ClusterName) - } - } - - // Update the existing decision cluster groups - pdct.existingScheduledClusterGroups = newScheduledClusterGroups - pdct.generateGroupsNameIndex() - - return nil -} - -// GetClusterChanges updates the tracker's decisionClusters and returns added and deleted cluster names. -func (pdct *PlacementDecisionClustersTracker) GetClusterChanges() (sets.Set[string], sets.Set[string], error) { - // Get existing clusters - existingScheduledClusters := pdct.existingScheduledClusterGroups.GetClusters() - - // Refresh clusters - err := pdct.Refresh() - if err != nil { - return nil, nil, err - } - newScheduledClusters := pdct.existingScheduledClusterGroups.GetClusters() - - // Compare the difference - added := newScheduledClusters.Difference(existingScheduledClusters) - deleted := existingScheduledClusters.Difference(newScheduledClusters) - - return added, deleted, nil -} - -func (pdct *PlacementDecisionClustersTracker) generateGroupsNameIndex() { - pdct.clusterGroupsIndexToName = map[int32]string{} - pdct.clusterGroupsNameToIndex = map[string][]int32{} - - for groupkey := range pdct.existingScheduledClusterGroups { - // index to name - pdct.clusterGroupsIndexToName[groupkey.GroupIndex] = groupkey.GroupName - // name to index - if index, exist := pdct.clusterGroupsNameToIndex[groupkey.GroupName]; exist { - pdct.clusterGroupsNameToIndex[groupkey.GroupName] = append(index, groupkey.GroupIndex) - } else { - pdct.clusterGroupsNameToIndex[groupkey.GroupName] = []int32{groupkey.GroupIndex} - } - } - - // sort index order - for _, index := range pdct.clusterGroupsNameToIndex { - sort.Slice(index, func(i, j int) bool { - return index[i] < index[j] - }) - } -} - -// ExistingClusterGroups returns the tracker's existing decision cluster groups for groups listed in groupKeys. -// Return empty set when groupKeys is empty. -func (pdct *PlacementDecisionClustersTracker) ExistingClusterGroups(groupKeys ...GroupKey) ClusterGroupsMap { - pdct.lock.RLock() - defer pdct.lock.RUnlock() - - resultClusterGroups := make(map[GroupKey]sets.Set[string]) - - includeGroupKeys := pdct.fulfillGroupKeys(groupKeys) - for _, groupKey := range includeGroupKeys { - if clusters, found := pdct.existingScheduledClusterGroups[groupKey]; found { - resultClusterGroups[groupKey] = clusters - } - } - - return resultClusterGroups -} - -// ExistingClusterGroupsBesides returns the tracker's existing decision cluster groups except cluster groups listed in groupKeys. -// Return all the clusters when groupKeys is empty. -func (pdct *PlacementDecisionClustersTracker) ExistingClusterGroupsBesides(groupKeys ...GroupKey) ClusterGroupsMap { - pdct.lock.RLock() - defer pdct.lock.RUnlock() - - resultClusterGroups := make(map[GroupKey]sets.Set[string]) - - excludeGroupKeys := pdct.fulfillGroupKeys(groupKeys) - includeGroupKeys := pdct.getGroupKeysBesides(excludeGroupKeys) - for _, groupKey := range includeGroupKeys { - if clusters, found := pdct.existingScheduledClusterGroups[groupKey]; found { - resultClusterGroups[groupKey] = clusters - } - } - - return resultClusterGroups -} - -// Fulfill the expect groupkeys with group name or group index, the returned groupkeys are ordered by input group name then group index. -// For example, the input is []GroupKey{{GroupName: "group1"}, {GroupIndex: 2}}, -// the returned is []GroupKey{{GroupName: "group1", GroupIndex: 0}, {GroupName: "group1", GroupIndex: 1}, {GroupName: "group2", GroupIndex: 2}} -func (pdct *PlacementDecisionClustersTracker) fulfillGroupKeys(groupKeys []GroupKey) []GroupKey { - fulfilledGroupKeys := []GroupKey{} - for _, gk := range groupKeys { - if gk.GroupName != "" { - if indexes, exist := pdct.clusterGroupsNameToIndex[gk.GroupName]; exist { - for _, groupIndex := range indexes { - fulfilledGroupKeys = append(fulfilledGroupKeys, GroupKey{GroupName: gk.GroupName, GroupIndex: groupIndex}) - } - } - } else { - if groupName, exist := pdct.clusterGroupsIndexToName[gk.GroupIndex]; exist { - fulfilledGroupKeys = append(fulfilledGroupKeys, GroupKey{GroupName: groupName, GroupIndex: gk.GroupIndex}) - } - } - } - return fulfilledGroupKeys -} - -func (pdct *PlacementDecisionClustersTracker) getGroupKeysBesides(groupKeyToExclude []GroupKey) []GroupKey { - groupKey := []GroupKey{} - for i := 0; i < len(pdct.clusterGroupsIndexToName); i++ { - gKey := GroupKey{GroupName: pdct.clusterGroupsIndexToName[int32(i)], GroupIndex: int32(i)} - if !containsGroupKey(groupKeyToExclude, gKey) { - groupKey = append(groupKey, gKey) - } - } - - return groupKey -} - -// ClusterGroupsMap is a custom type representing a map of group keys to sets of cluster names. -type ClusterGroupsMap map[GroupKey]sets.Set[string] - -// GetOrderedGroupKeys returns an ordered slice of GroupKeys, sorted by group index. -func (g ClusterGroupsMap) GetOrderedGroupKeys() []GroupKey { - groupKeys := []GroupKey{} - for groupKey := range g { - groupKeys = append(groupKeys, groupKey) - } - - // sort by group index index - sort.Slice(groupKeys, func(i, j int) bool { - return groupKeys[i].GroupIndex < groupKeys[j].GroupIndex - }) - - return groupKeys -} - -// GetClusters returns a set containing all clusters from all group sets. -func (g ClusterGroupsMap) GetClusters() sets.Set[string] { - clusterSet := sets.New[string]() - for _, clusterGroup := range g { - clusterSet = clusterSet.Union(clusterGroup) - } - return clusterSet -} - -// ClusterToGroupKey returns a mapping of cluster names to their respective group keys. -func (g ClusterGroupsMap) ClusterToGroupKey() map[string]GroupKey { - clusterToGroupKey := map[string]GroupKey{} - - for groupKey, clusterGroup := range g { - for c := range clusterGroup { - clusterToGroupKey[c] = groupKey - } - } - - return clusterToGroupKey -} - -// Helper function to check if a groupKey is present in the groupKeys slice. -func containsGroupKey(groupKeys []GroupKey, groupKey GroupKey) bool { - for _, gk := range groupKeys { - if gk == groupKey { - return true - } - } - return false -} - -func parseGroupKeyFromDecision(d *clusterv1beta1.PlacementDecision) (GroupKey, error) { - groupName := d.Labels[clusterv1beta1.DecisionGroupNameLabel] - groupIndex := d.Labels[clusterv1beta1.DecisionGroupIndexLabel] - groupIndexNum, err := strconv.Atoi(groupIndex) - if err != nil { - return GroupKey{}, fmt.Errorf("incorrect group index: %w", err) - } - return GroupKey{GroupName: groupName, GroupIndex: int32(groupIndexNum)}, nil -} From 03254b0cc5e7e273b1945a020867f7502a6366d4 Mon Sep 17 00:00:00 2001 From: haoqing0110 Date: Mon, 25 Mar 2024 07:53:33 +0000 Subject: [PATCH 4/4] remove self from annotation Signed-off-by: haoqing0110 --- .../controller.go | 20 +-- .../controller_test.go | 6 +- .../controllers/cmamanagedby/controller.go | 94 ++++++++++++ .../cmamanagedby/controller_test.go | 134 ++++++++++++++++++ pkg/addonmanager/manager.go | 16 ++- 5 files changed, 255 insertions(+), 15 deletions(-) rename pkg/addonmanager/controllers/{managementaddonconfig => cmaconfig}/controller.go (89%) rename pkg/addonmanager/controllers/{managementaddonconfig => cmaconfig}/controller_test.go (99%) create mode 100644 pkg/addonmanager/controllers/cmamanagedby/controller.go create mode 100644 pkg/addonmanager/controllers/cmamanagedby/controller_test.go diff --git a/pkg/addonmanager/controllers/managementaddonconfig/controller.go b/pkg/addonmanager/controllers/cmaconfig/controller.go similarity index 89% rename from pkg/addonmanager/controllers/managementaddonconfig/controller.go rename to pkg/addonmanager/controllers/cmaconfig/controller.go index 300caff89..55b877688 100644 --- a/pkg/addonmanager/controllers/managementaddonconfig/controller.go +++ b/pkg/addonmanager/controllers/cmaconfig/controller.go @@ -1,4 +1,4 @@ -package managementaddonconfig +package cmaconfig import ( "context" @@ -30,8 +30,8 @@ const ( type enqueueFunc func(obj interface{}) -// clusterManagementAddonConfigController reconciles all interested addon config types (GroupVersionResource) on the hub. -type clusterManagementAddonConfigController struct { +// cmaConfigController reconciles all interested addon config types (GroupVersionResource) on the hub. +type cmaConfigController struct { addonClient addonv1alpha1client.Interface clusterManagementAddonLister addonlisterv1alpha1.ClusterManagementAddOnLister clusterManagementAddonIndexer cache.Indexer @@ -44,7 +44,7 @@ type clusterManagementAddonConfigController struct { addonapiv1alpha1.ClusterManagementAddOnStatus] } -func NewManagementAddonConfigController( +func NewCMAConfigController( addonClient addonv1alpha1client.Interface, clusterManagementAddonInformers addoninformerv1alpha1.ClusterManagementAddOnInformer, configInformerFactory dynamicinformer.DynamicSharedInformerFactory, @@ -53,7 +53,7 @@ func NewManagementAddonConfigController( ) factory.Controller { syncCtx := factory.NewSyncContext(controllerName) - c := &clusterManagementAddonConfigController{ + c := &cmaConfigController{ addonClient: addonClient, clusterManagementAddonLister: clusterManagementAddonInformers.Lister(), clusterManagementAddonIndexer: clusterManagementAddonInformers.Informer().GetIndexer(), @@ -78,7 +78,7 @@ func NewManagementAddonConfigController( WithSync(c.sync).ToController(controllerName) } -func (c *clusterManagementAddonConfigController) buildConfigInformers( +func (c *cmaConfigController) buildConfigInformers( configInformerFactory dynamicinformer.DynamicSharedInformerFactory, configGVRs map[schema.GroupVersionResource]bool, ) []factory.Informer { @@ -104,7 +104,7 @@ func (c *clusterManagementAddonConfigController) buildConfigInformers( return configInformers } -func (c *clusterManagementAddonConfigController) enqueueClusterManagementAddOnsByConfig(gvr schema.GroupVersionResource) enqueueFunc { +func (c *cmaConfigController) enqueueClusterManagementAddOnsByConfig(gvr schema.GroupVersionResource) enqueueFunc { return func(obj interface{}) { namespaceName, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj) if err != nil { @@ -129,7 +129,7 @@ func (c *clusterManagementAddonConfigController) enqueueClusterManagementAddOnsB } } -func (c *clusterManagementAddonConfigController) sync(ctx context.Context, syncCtx factory.SyncContext, key string) error { +func (c *cmaConfigController) sync(ctx context.Context, syncCtx factory.SyncContext, key string) error { _, addonName, err := cache.SplitMetaNamespaceKey(key) if err != nil { // ignore addon whose key is invalid @@ -158,7 +158,7 @@ func (c *clusterManagementAddonConfigController) sync(ctx context.Context, syncC return err } -func (c *clusterManagementAddonConfigController) updateConfigSpecHash(cma *addonapiv1alpha1.ClusterManagementAddOn) error { +func (c *cmaConfigController) updateConfigSpecHash(cma *addonapiv1alpha1.ClusterManagementAddOn) error { for i, defaultConfigReference := range cma.Status.DefaultConfigReferences { if !utils.ContainGR( @@ -203,7 +203,7 @@ func (c *clusterManagementAddonConfigController) updateConfigSpecHash(cma *addon return nil } -func (c *clusterManagementAddonConfigController) getConfigSpecHash(gr addonapiv1alpha1.ConfigGroupResource, +func (c *cmaConfigController) getConfigSpecHash(gr addonapiv1alpha1.ConfigGroupResource, cr addonapiv1alpha1.ConfigReferent) (string, error) { lister, ok := c.configListers[schema.GroupResource{Group: gr.Group, Resource: gr.Resource}] if !ok { diff --git a/pkg/addonmanager/controllers/managementaddonconfig/controller_test.go b/pkg/addonmanager/controllers/cmaconfig/controller_test.go similarity index 99% rename from pkg/addonmanager/controllers/managementaddonconfig/controller_test.go rename to pkg/addonmanager/controllers/cmaconfig/controller_test.go index 42b04070b..f85830624 100644 --- a/pkg/addonmanager/controllers/managementaddonconfig/controller_test.go +++ b/pkg/addonmanager/controllers/cmaconfig/controller_test.go @@ -1,4 +1,4 @@ -package managementaddonconfig +package cmaconfig import ( "context" @@ -255,7 +255,7 @@ func TestSync(t *testing.T) { syncContext := addontesting.NewFakeSyncContext(t) - ctrl := &clusterManagementAddonConfigController{ + ctrl := &cmaConfigController{ addonClient: fakeAddonClient, clusterManagementAddonLister: addonInformers.Addon().V1alpha1().ClusterManagementAddOns().Lister(), configListers: map[schema.GroupResource]dynamiclister.Lister{}, @@ -348,7 +348,7 @@ func TestEnqueue(t *testing.T) { addonInformers := addoninformers.NewSharedInformerFactory(fakeAddonClient, 10*time.Minute) addonInformer := addonInformers.Addon().V1alpha1().ManagedClusterAddOns().Informer() - ctrl := &clusterManagementAddonConfigController{ + ctrl := &cmaConfigController{ clusterManagementAddonIndexer: addonInformer.GetIndexer(), queue: addontesting.NewFakeSyncContext(t).Queue(), } diff --git a/pkg/addonmanager/controllers/cmamanagedby/controller.go b/pkg/addonmanager/controllers/cmamanagedby/controller.go new file mode 100644 index 000000000..a8c679094 --- /dev/null +++ b/pkg/addonmanager/controllers/cmamanagedby/controller.go @@ -0,0 +1,94 @@ +package cmamanagedby + +import ( + "context" + + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/tools/cache" + addonapiv1alpha1 "open-cluster-management.io/api/addon/v1alpha1" + addonv1alpha1client "open-cluster-management.io/api/client/addon/clientset/versioned" + addoninformerv1alpha1 "open-cluster-management.io/api/client/addon/informers/externalversions/addon/v1alpha1" + addonlisterv1alpha1 "open-cluster-management.io/api/client/addon/listers/addon/v1alpha1" + "open-cluster-management.io/sdk-go/pkg/patcher" + + "open-cluster-management.io/addon-framework/pkg/agent" + "open-cluster-management.io/addon-framework/pkg/basecontroller/factory" +) + +const ( + controllerName = "cma-managed-by-controller" +) + +// cmaManagedByController reconciles clustermanagementaddon on the hub +// to update the annotation "addon.open-cluster-management.io/lifecycle" value. +// It removes the value "self" if exist, which indicate the +// the installation and upgrade of addon will no longer be managed by addon itself. +// Once removed, the value will be set to "addon-manager" by the general addon manager. +type cmaManagedByController struct { + addonClient addonv1alpha1client.Interface + clusterManagementAddonLister addonlisterv1alpha1.ClusterManagementAddOnLister + agentAddons map[string]agent.AgentAddon + addonFilterFunc factory.EventFilterFunc + addonPatcher patcher.Patcher[*addonapiv1alpha1.ClusterManagementAddOn, + addonapiv1alpha1.ClusterManagementAddOnSpec, + addonapiv1alpha1.ClusterManagementAddOnStatus] +} + +func NewCMAManagedByController( + addonClient addonv1alpha1client.Interface, + clusterManagementAddonInformers addoninformerv1alpha1.ClusterManagementAddOnInformer, + agentAddons map[string]agent.AgentAddon, + addonFilterFunc factory.EventFilterFunc, +) factory.Controller { + syncCtx := factory.NewSyncContext(controllerName) + + c := &cmaManagedByController{ + addonClient: addonClient, + clusterManagementAddonLister: clusterManagementAddonInformers.Lister(), + agentAddons: agentAddons, + addonFilterFunc: addonFilterFunc, + addonPatcher: patcher.NewPatcher[*addonapiv1alpha1.ClusterManagementAddOn, + addonapiv1alpha1.ClusterManagementAddOnSpec, + addonapiv1alpha1.ClusterManagementAddOnStatus](addonClient.AddonV1alpha1().ClusterManagementAddOns()), + } + + return factory.New(). + WithSyncContext(syncCtx). + WithFilteredEventsInformersQueueKeysFunc( + func(obj runtime.Object) []string { + key, _ := cache.DeletionHandlingMetaNamespaceKeyFunc(obj) + return []string{key} + }, + c.addonFilterFunc, clusterManagementAddonInformers.Informer()). + WithSync(c.sync).ToController(controllerName) +} + +func (c *cmaManagedByController) sync(ctx context.Context, syncCtx factory.SyncContext, key string) error { + _, addonName, err := cache.SplitMetaNamespaceKey(key) + if err != nil { + // ignore addon whose key is invalid + return nil + } + + cma, err := c.clusterManagementAddonLister.Get(addonName) + if errors.IsNotFound(err) { + // addon cloud be deleted, ignore + return nil + } + if err != nil { + return err + } + + // Remove the annotation value "self" since the WithInstallStrategy() is removed in addon-framework. + // The migration plan refer to https://github.com/open-cluster-management-io/ocm/issues/355. + cmaCopy := cma.DeepCopy() + if cmaCopy.Annotations == nil || + cmaCopy.Annotations[addonapiv1alpha1.AddonLifecycleAnnotationKey] != addonapiv1alpha1.AddonLifecycleSelfManageAnnotationValue { + return nil + } + cmaCopy.Annotations[addonapiv1alpha1.AddonLifecycleAnnotationKey] = "" + + _, err = c.addonPatcher.PatchLabelAnnotations(ctx, cmaCopy, cmaCopy.ObjectMeta, cma.ObjectMeta) + return err +} diff --git a/pkg/addonmanager/controllers/cmamanagedby/controller_test.go b/pkg/addonmanager/controllers/cmamanagedby/controller_test.go new file mode 100644 index 000000000..1baf564a0 --- /dev/null +++ b/pkg/addonmanager/controllers/cmamanagedby/controller_test.go @@ -0,0 +1,134 @@ +package cmamanagedby + +import ( + "context" + "encoding/json" + "testing" + "time" + + "k8s.io/apimachinery/pkg/runtime" + clienttesting "k8s.io/client-go/testing" + "open-cluster-management.io/addon-framework/pkg/addonmanager/addontesting" + "open-cluster-management.io/addon-framework/pkg/agent" + "open-cluster-management.io/addon-framework/pkg/utils" + addonapiv1alpha1 "open-cluster-management.io/api/addon/v1alpha1" + fakeaddon "open-cluster-management.io/api/client/addon/clientset/versioned/fake" + addoninformers "open-cluster-management.io/api/client/addon/informers/externalversions" + clusterv1 "open-cluster-management.io/api/cluster/v1" + "open-cluster-management.io/sdk-go/pkg/patcher" +) + +type testAgent struct { + name string +} + +func (t *testAgent) Manifests(cluster *clusterv1.ManagedCluster, addon *addonapiv1alpha1.ManagedClusterAddOn) ([]runtime.Object, error) { + return nil, nil +} + +func (t *testAgent) GetAgentAddonOptions() agent.AgentAddonOptions { + return agent.AgentAddonOptions{ + AddonName: t.name, + } +} + +func newClusterManagementAddonWithAnnotation(name string, annotations map[string]string) *addonapiv1alpha1.ClusterManagementAddOn { + cma := addontesting.NewClusterManagementAddon(name, "", "").Build() + cma.Annotations = annotations + return cma +} + +func TestReconcile(t *testing.T) { + cases := []struct { + name string + cma []runtime.Object + testaddons map[string]agent.AgentAddon + validateAddonActions func(t *testing.T, actions []clienttesting.Action) + }{ + { + name: "no patch annotation if nil", + cma: []runtime.Object{newClusterManagementAddonWithAnnotation("test", nil)}, + validateAddonActions: addontesting.AssertNoActions, + testaddons: map[string]agent.AgentAddon{ + "test": &testAgent{name: "test"}, + }, + }, + { + name: "no patch annotation if managed by not exist", + cma: []runtime.Object{newClusterManagementAddonWithAnnotation("test", map[string]string{ + "test": "test", + })}, + validateAddonActions: addontesting.AssertNoActions, + testaddons: map[string]agent.AgentAddon{ + "test": &testAgent{name: "test"}, + }, + }, + { + name: "no patch annotation if managed by is not self", + cma: []runtime.Object{newClusterManagementAddonWithAnnotation("test", map[string]string{ + "test": "test", + addonapiv1alpha1.AddonLifecycleAnnotationKey: "xxx", + })}, + validateAddonActions: addontesting.AssertNoActions, + testaddons: map[string]agent.AgentAddon{ + "test": &testAgent{name: "test"}, + }, + }, + { + name: "patch annotation if managed by self", + cma: []runtime.Object{newClusterManagementAddonWithAnnotation("test", map[string]string{ + "test": "test", + addonapiv1alpha1.AddonLifecycleAnnotationKey: addonapiv1alpha1.AddonLifecycleSelfManageAnnotationValue, + })}, + validateAddonActions: func(t *testing.T, actions []clienttesting.Action) { + addontesting.AssertActions(t, actions, "patch") + patch := actions[0].(clienttesting.PatchActionImpl).Patch + cma := &addonapiv1alpha1.ClusterManagementAddOn{} + err := json.Unmarshal(patch, cma) + if err != nil { + t.Fatal(err) + } + + if len(cma.Annotations) != 1 || cma.Annotations[addonapiv1alpha1.AddonLifecycleAnnotationKey] != "" { + t.Errorf("cma annotation is not correct, expected self but got %s", cma.Annotations[addonapiv1alpha1.AddonLifecycleAnnotationKey]) + } + }, + testaddons: map[string]agent.AgentAddon{ + "test": &testAgent{name: "test"}, + }, + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + fakeAddonClient := fakeaddon.NewSimpleClientset(c.cma...) + addonInformers := addoninformers.NewSharedInformerFactory(fakeAddonClient, 10*time.Minute) + + for _, obj := range c.cma { + if err := addonInformers.Addon().V1alpha1().ClusterManagementAddOns().Informer().GetStore().Add(obj); err != nil { + t.Fatal(err) + } + } + + controller := cmaManagedByController{ + addonClient: fakeAddonClient, + clusterManagementAddonLister: addonInformers.Addon().V1alpha1().ClusterManagementAddOns().Lister(), + agentAddons: c.testaddons, + addonFilterFunc: utils.FilterByAddonName(c.testaddons), + addonPatcher: patcher.NewPatcher[*addonapiv1alpha1.ClusterManagementAddOn, + addonapiv1alpha1.ClusterManagementAddOnSpec, + addonapiv1alpha1.ClusterManagementAddOnStatus](fakeAddonClient.AddonV1alpha1().ClusterManagementAddOns()), + } + + for _, obj := range c.cma { + cma := obj.(*addonapiv1alpha1.ClusterManagementAddOn) + syncContext := addontesting.NewFakeSyncContext(t) + err := controller.sync(context.TODO(), syncContext, cma.Name) + if err != nil { + t.Errorf("expected no error when sync: %v", err) + } + } + c.validateAddonActions(t, fakeAddonClient.Actions()) + }) + } +} diff --git a/pkg/addonmanager/manager.go b/pkg/addonmanager/manager.go index a6a121d2e..57953b722 100644 --- a/pkg/addonmanager/manager.go +++ b/pkg/addonmanager/manager.go @@ -24,7 +24,8 @@ import ( "open-cluster-management.io/addon-framework/pkg/addonmanager/controllers/addonconfig" "open-cluster-management.io/addon-framework/pkg/addonmanager/controllers/agentdeploy" "open-cluster-management.io/addon-framework/pkg/addonmanager/controllers/certificate" - "open-cluster-management.io/addon-framework/pkg/addonmanager/controllers/managementaddonconfig" + "open-cluster-management.io/addon-framework/pkg/addonmanager/controllers/cmaconfig" + "open-cluster-management.io/addon-framework/pkg/addonmanager/controllers/cmamanagedby" "open-cluster-management.io/addon-framework/pkg/addonmanager/controllers/registration" "open-cluster-management.io/addon-framework/pkg/agent" "open-cluster-management.io/addon-framework/pkg/basecontroller/factory" @@ -237,6 +238,16 @@ func (a *addonManager) StartWithInformers(ctx context.Context, a.addonAgents, ) + // This controller is used during migrating addons to be managed by addon-manager. + // This should be removed when the migration is done. + // The migration plan refer to https://github.com/open-cluster-management-io/ocm/issues/355. + managementAddonController := cmamanagedby.NewCMAManagedByController( + addonClient, + addonInformers.Addon().V1alpha1().ClusterManagementAddOns(), + a.addonAgents, + utils.FilterByAddonName(a.addonAgents), + ) + var addonConfigController, managementAddonConfigController factory.Controller if len(a.addonConfigs) != 0 { addonConfigController = addonconfig.NewAddonConfigController( @@ -247,7 +258,7 @@ func (a *addonManager) StartWithInformers(ctx context.Context, a.addonConfigs, utils.FilterByAddonName(a.addonAgents), ) - managementAddonConfigController = managementaddonconfig.NewManagementAddonConfigController( + managementAddonConfigController = cmaconfig.NewCMAConfigController( addonClient, addonInformers.Addon().V1alpha1().ClusterManagementAddOns(), dynamicInformers, @@ -293,6 +304,7 @@ func (a *addonManager) StartWithInformers(ctx context.Context, go deployController.Run(ctx, 1) go registrationController.Run(ctx, 1) + go managementAddonController.Run(ctx, 1) if addonConfigController != nil { go addonConfigController.Run(ctx, 1)