diff --git a/pkg/addonmanager/controllers/agentdeploy/default_sync_test.go b/pkg/addonmanager/controllers/agentdeploy/default_sync_test.go index ea5ef892b..844d002aa 100644 --- a/pkg/addonmanager/controllers/agentdeploy/default_sync_test.go +++ b/pkg/addonmanager/controllers/agentdeploy/default_sync_test.go @@ -44,10 +44,12 @@ var mainfestWorkAppliedCondition = metav1.Condition{ } type testAgent struct { - name string - objects []runtime.Object - err error - healthProber *agent.HealthProber + name string + objects []runtime.Object + err error + healthProber *agent.HealthProber + Updaters []agent.Updater + ManifestConfigs []workapiv1.ManifestConfigOption } func (t *testAgent) Manifests(cluster *clusterv1.ManagedCluster, addon *addonapiv1alpha1.ManagedClusterAddOn) ([]runtime.Object, error) { @@ -56,8 +58,10 @@ func (t *testAgent) Manifests(cluster *clusterv1.ManagedCluster, addon *addonapi func (t *testAgent) GetAgentAddonOptions() agent.AgentAddonOptions { return agent.AgentAddonOptions{ - AddonName: t.name, - HealthProber: t.healthProber, + AddonName: t.name, + HealthProber: t.healthProber, + Updaters: t.Updaters, + ManifestConfigs: t.ManifestConfigs, } } diff --git a/pkg/addonmanager/controllers/agentdeploy/util_test.go b/pkg/addonmanager/controllers/agentdeploy/util_test.go index ba4bed33c..fd1b4f120 100644 --- a/pkg/addonmanager/controllers/agentdeploy/util_test.go +++ b/pkg/addonmanager/controllers/agentdeploy/util_test.go @@ -226,6 +226,244 @@ func TestGetManifestConfigOption(t *testing.T) { }, }, }, + { + name: "set updater", + agentAddon: &testAgent{ + name: "test", + objects: []runtime.Object{ + NewFakeDeployment("test-deployment", "default"), + }, + Updaters: []agent.Updater{ + { + ResourceIdentifier: workapiv1.ResourceIdentifier{ + Group: "apps", + Resource: "deployments", + Name: "test-deployment", + Namespace: "default", + }, + UpdateStrategy: workapiv1.UpdateStrategy{ + Type: workapiv1.UpdateStrategyTypeServerSideApply, + ServerSideApply: &workapiv1.ServerSideApplyConfig{ + FieldManager: "work-agent-test", + }, + }, + }, + }, + }, + expectedManifestConfigOption: []workapiv1.ManifestConfigOption{ + { + ResourceIdentifier: workapiv1.ResourceIdentifier{ + Group: "apps", + Resource: "deployments", + Name: "test-deployment", + Namespace: "default", + }, + UpdateStrategy: &workapiv1.UpdateStrategy{ + Type: workapiv1.UpdateStrategyTypeServerSideApply, + ServerSideApply: &workapiv1.ServerSideApplyConfig{ + FieldManager: "work-agent-test", + }, + }, + }, + }, + }, + { + name: "merge feedback rules", + agentAddon: &testAgent{ + name: "test", + objects: []runtime.Object{ + NewFakeDeployment("test-deployment", "default"), + }, + healthProber: &agent.HealthProber{Type: agent.HealthProberTypeDeploymentAvailability}, + ManifestConfigs: []workapiv1.ManifestConfigOption{ + { + ResourceIdentifier: workapiv1.ResourceIdentifier{ + Group: "apps", + Resource: "deployments", + Name: "test-deployment", + Namespace: "default", + }, + FeedbackRules: []workapiv1.FeedbackRule{ + { + Type: workapiv1.JSONPathsType, + JsonPaths: []workapiv1.JsonPath{ + { + Name: "test-name", + Path: ".metadata.name", + }, + }, + }, + }, + }, + { + ResourceIdentifier: workapiv1.ResourceIdentifier{ + Group: "apps", + Resource: "deployments", + Name: "test-deployment-1", + Namespace: "default", + }, + FeedbackRules: []workapiv1.FeedbackRule{ + { + Type: workapiv1.JSONPathsType, + JsonPaths: []workapiv1.JsonPath{ + { + Name: "test-name", + Path: ".metadata.name", + }, + }, + }, + }, + }, + }, + }, + expectedManifestConfigOption: []workapiv1.ManifestConfigOption{ + { + ResourceIdentifier: workapiv1.ResourceIdentifier{ + Group: "apps", + Resource: "deployments", + Name: "test-deployment", + Namespace: "default", + }, + FeedbackRules: []workapiv1.FeedbackRule{ + { + Type: workapiv1.WellKnownStatusType, + }, + { + Type: workapiv1.JSONPathsType, + JsonPaths: []workapiv1.JsonPath{ + { + Name: "test-name", + Path: ".metadata.name", + }, + }, + }, + }, + }, + { + ResourceIdentifier: workapiv1.ResourceIdentifier{ + Group: "apps", + Resource: "deployments", + Name: "test-deployment-1", + Namespace: "default", + }, + FeedbackRules: []workapiv1.FeedbackRule{ + { + Type: workapiv1.JSONPathsType, + JsonPaths: []workapiv1.JsonPath{ + { + Name: "test-name", + Path: ".metadata.name", + }, + }, + }, + }, + }, + }, + }, + { + name: "merge update strategy", + agentAddon: &testAgent{ + name: "test", + objects: []runtime.Object{ + NewFakeDeployment("test-deployment", "default"), + }, + Updaters: []agent.Updater{ + { + ResourceIdentifier: workapiv1.ResourceIdentifier{ + Group: "apps", + Resource: "deployments", + Name: "test-deployment", + Namespace: "default", + }, + UpdateStrategy: workapiv1.UpdateStrategy{ + Type: workapiv1.UpdateStrategyTypeCreateOnly, + }, + }, + { + ResourceIdentifier: workapiv1.ResourceIdentifier{ + Group: "apps", + Resource: "deployments", + Name: "test-deployment-2", + Namespace: "default", + }, + UpdateStrategy: workapiv1.UpdateStrategy{ + Type: workapiv1.UpdateStrategyTypeCreateOnly, + }, + }, + }, + ManifestConfigs: []workapiv1.ManifestConfigOption{ + { + ResourceIdentifier: workapiv1.ResourceIdentifier{ + Group: "apps", + Resource: "deployments", + Name: "test-deployment", + Namespace: "default", + }, + UpdateStrategy: &workapiv1.UpdateStrategy{ + Type: workapiv1.UpdateStrategyTypeServerSideApply, + ServerSideApply: &workapiv1.ServerSideApplyConfig{ + FieldManager: "work-agent-test", + }, + }, + }, + { + ResourceIdentifier: workapiv1.ResourceIdentifier{ + Group: "apps", + Resource: "deployments", + Name: "test-deployment-1", + Namespace: "default", + }, + UpdateStrategy: &workapiv1.UpdateStrategy{ + Type: workapiv1.UpdateStrategyTypeServerSideApply, + ServerSideApply: &workapiv1.ServerSideApplyConfig{ + FieldManager: "work-agent-test", + }, + }, + }, + }, + }, + expectedManifestConfigOption: []workapiv1.ManifestConfigOption{ + { + ResourceIdentifier: workapiv1.ResourceIdentifier{ + Group: "apps", + Resource: "deployments", + Name: "test-deployment", + Namespace: "default", + }, + UpdateStrategy: &workapiv1.UpdateStrategy{ + Type: workapiv1.UpdateStrategyTypeServerSideApply, + ServerSideApply: &workapiv1.ServerSideApplyConfig{ + FieldManager: "work-agent-test", + }, + }, + }, + { + ResourceIdentifier: workapiv1.ResourceIdentifier{ + Group: "apps", + Resource: "deployments", + Name: "test-deployment-2", + Namespace: "default", + }, + UpdateStrategy: &workapiv1.UpdateStrategy{ + Type: workapiv1.UpdateStrategyTypeCreateOnly, + }, + }, + { + ResourceIdentifier: workapiv1.ResourceIdentifier{ + Group: "apps", + Resource: "deployments", + Name: "test-deployment-1", + Namespace: "default", + }, + UpdateStrategy: &workapiv1.UpdateStrategy{ + Type: workapiv1.UpdateStrategyTypeServerSideApply, + ServerSideApply: &workapiv1.ServerSideApplyConfig{ + FieldManager: "work-agent-test", + }, + }, + }, + }, + }, } for _, c := range cases { diff --git a/pkg/addonmanager/controllers/agentdeploy/utils.go b/pkg/addonmanager/controllers/agentdeploy/utils.go index 3bc50dd5d..8b819df33 100644 --- a/pkg/addonmanager/controllers/agentdeploy/utils.go +++ b/pkg/addonmanager/controllers/agentdeploy/utils.go @@ -3,6 +3,7 @@ package agentdeploy import ( "encoding/json" "fmt" + "reflect" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -517,9 +518,45 @@ func getManifestConfigOption(agentAddon agent.AgentAddon, } } + emc := agentAddon.GetAgentAddonOptions().ManifestConfigs + for _, mc := range emc { + index := containsResourceIdentifier(manifestConfigs, mc.ResourceIdentifier) + if index == -1 { + manifestConfigs = append(manifestConfigs, mc) + continue + } + + for _, rule := range mc.FeedbackRules { + if !containsFeedbackRule(manifestConfigs[index].FeedbackRules, rule) { + manifestConfigs[index].FeedbackRules = append(manifestConfigs[index].FeedbackRules, rule) + } + } + + if mc.UpdateStrategy != nil { + manifestConfigs[index].UpdateStrategy = mc.UpdateStrategy + } + } return manifestConfigs } +func containsResourceIdentifier(mcs []workapiv1.ManifestConfigOption, ri workapiv1.ResourceIdentifier) int { + for index, mc := range mcs { + if mc.ResourceIdentifier == ri { + return index + } + } + return -1 +} + +func containsFeedbackRule(rules []workapiv1.FeedbackRule, rule workapiv1.FeedbackRule) bool { + for _, r := range rules { + if reflect.DeepEqual(r, rule) { + return true + } + } + return false +} + func getDeletionOrphaningRule(obj runtime.Object) (*workapiv1.OrphaningRule, error) { accessor, err := meta.Accessor(obj) if err != nil { diff --git a/pkg/agent/inteface.go b/pkg/agent/inteface.go index 32514684f..84576eb06 100644 --- a/pkg/agent/inteface.go +++ b/pkg/agent/inteface.go @@ -88,7 +88,14 @@ type AgentAddonOptions struct { // AgentDeployTriggerClusterFilter: func(old, new *clusterv1.ManagedCluster) bool { // return !equality.Semantic.DeepEqual(old.Annotations, new.Annotations) // } + // +optional AgentDeployTriggerClusterFilter func(old, new *clusterv1.ManagedCluster) bool + + // ManifestConfigs represents the configurations of manifests defined in workload field. + // It will override the update strategy set by the "Updaters" field and merge the feedback rules set by the + // "HealthProber" field if they have the same resource identifier. + // +optional + ManifestConfigs []workapiv1.ManifestConfigOption } type CSRSignerFunc func(csr *certificatesv1.CertificateSigningRequest) []byte