diff --git a/go.mod b/go.mod index 760eeb7a1..c6f1472c0 100644 --- a/go.mod +++ b/go.mod @@ -36,7 +36,7 @@ require ( k8s.io/klog/v2 v2.130.1 k8s.io/kube-aggregator v0.31.4 k8s.io/utils v0.0.0-20240921022957-49e7df575cb6 - open-cluster-management.io/addon-framework v0.11.1-0.20241129080247-57b1d2859f50 + open-cluster-management.io/addon-framework v0.11.1-0.20250218075422-4329ebea390c open-cluster-management.io/api v0.15.1-0.20250116010516-3a595d6a4e40 open-cluster-management.io/sdk-go v0.15.1-0.20241125015855-1536c3970f8f sigs.k8s.io/cluster-inventory-api v0.0.0-20240730014211-ef0154379848 diff --git a/go.sum b/go.sum index 986541b18..072343326 100644 --- a/go.sum +++ b/go.sum @@ -487,8 +487,8 @@ k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7F k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= k8s.io/utils v0.0.0-20240921022957-49e7df575cb6 h1:MDF6h2H/h4tbzmtIKTuctcwZmY0tY9mD9fNT47QO6HI= k8s.io/utils v0.0.0-20240921022957-49e7df575cb6/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -open-cluster-management.io/addon-framework v0.11.1-0.20241129080247-57b1d2859f50 h1:TXRd6OdGjArh6cwlCYOqlIcyx21k81oUIYj4rmHlYx0= -open-cluster-management.io/addon-framework v0.11.1-0.20241129080247-57b1d2859f50/go.mod h1:tsBSNs9mGfVQQjXBnjgpiX6r0UM+G3iNfmzQgKhEfw4= +open-cluster-management.io/addon-framework v0.11.1-0.20250218075422-4329ebea390c h1:Lyv5mhYDMRFj03OqAuBldLppUKSqf+WiemafiG0wzwc= +open-cluster-management.io/addon-framework v0.11.1-0.20250218075422-4329ebea390c/go.mod h1:3+UAkReHIEyqsDuq0Iv5w+ZRgZr254iehYV/JR2j038= open-cluster-management.io/api v0.15.1-0.20250116010516-3a595d6a4e40 h1:LckTHZ68rcy3hDFu6wa7BVOJ9wbWItJLZXmi0bpMyh8= open-cluster-management.io/api v0.15.1-0.20250116010516-3a595d6a4e40/go.mod h1:9erZEWEn4bEqh0nIX2wA7f/s3KCuFycQdBrPrRzi0QM= open-cluster-management.io/sdk-go v0.15.1-0.20241125015855-1536c3970f8f h1:zeC7QrFNarfK2zY6jGtd+mX+yDrQQmnH/J8A7n5Nh38= diff --git a/test/integration/addon/addon_configs_test.go b/test/integration/addon/addon_configs_test.go index bb1874321..dd8bf01a2 100644 --- a/test/integration/addon/addon_configs_test.go +++ b/test/integration/addon/addon_configs_test.go @@ -8,7 +8,6 @@ import ( "github.com/onsi/gomega" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/rand" addonapiv1alpha1 "open-cluster-management.io/api/addon/v1alpha1" @@ -412,20 +411,6 @@ var _ = ginkgo.Describe("AddConfigs", func() { }) ginkgo.It("Should not update unsupported config spec hash", func() { - addOnConfig := &addonapiv1alpha1.AddOnDeploymentConfig{ - ObjectMeta: metav1.ObjectMeta{ - Name: "addon-config", - Namespace: managedClusterName, - }, - Spec: addOnTest1ConfigSpec, - } - _, err = hubAddonClient.AddonV1alpha1().AddOnDeploymentConfigs(managedClusterName).Create(context.Background(), addOnConfig, metav1.CreateOptions{}) - gomega.Expect(err).ToNot(gomega.HaveOccurred()) - - // empty supported config - supportedConfig := testAddOnConfigsImpl.supportedConfigGVRs - testAddOnConfigsImpl.supportedConfigGVRs = []schema.GroupVersionResource{} - // do not update mca status.SupportedConfigs addon := &addonapiv1alpha1.ManagedClusterAddOn{ ObjectMeta: metav1.ObjectMeta{ @@ -437,12 +422,12 @@ var _ = ginkgo.Describe("AddConfigs", func() { Configs: []addonapiv1alpha1.AddOnConfig{ { ConfigGroupResource: addonapiv1alpha1.ConfigGroupResource{ - Group: addOnDeploymentConfigGVR.Group, - Resource: addOnDeploymentConfigGVR.Resource, + Group: addOnDeploymentConfigGVR.Group + "test", + Resource: addOnDeploymentConfigGVR.Resource + "test", }, ConfigReferent: addonapiv1alpha1.ConfigReferent{ - Name: addOnConfig.Name, - Namespace: addOnConfig.Namespace, + Name: "addon-config-test", + Namespace: managedClusterName, }, }, }, @@ -451,12 +436,17 @@ var _ = ginkgo.Describe("AddConfigs", func() { _, err = hubAddonClient.AddonV1alpha1().ManagedClusterAddOns(managedClusterName).Create(context.Background(), addon, metav1.CreateOptions{}) gomega.Expect(err).ToNot(gomega.HaveOccurred()) - // check cma status - assertClusterManagementAddOnDefaultConfigReferences(testAddOnConfigsImpl.name, addonapiv1alpha1.DefaultConfigReference{ + // check mca status + assertManagedClusterAddOnConfigReferences(testAddOnConfigsImpl.name, managedClusterName, addonapiv1alpha1.ConfigReference{ ConfigGroupResource: addonapiv1alpha1.ConfigGroupResource{ Group: addOnDeploymentConfigGVR.Group, Resource: addOnDeploymentConfigGVR.Resource, }, + ConfigReferent: addonapiv1alpha1.ConfigReferent{ + Namespace: configDefaultNamespace, + Name: configDefaultName, + }, + LastObservedGeneration: 1, DesiredConfig: &addonapiv1alpha1.ConfigSpecHash{ ConfigReferent: addonapiv1alpha1.ConfigReferent{ Namespace: configDefaultNamespace, @@ -464,29 +454,23 @@ var _ = ginkgo.Describe("AddConfigs", func() { }, SpecHash: addOnDefaultConfigSpecHash, }, - }) - assertClusterManagementAddOnInstallProgression(testAddOnConfigsImpl.name) - - // check mca status - assertManagedClusterAddOnConfigReferences(testAddOnConfigsImpl.name, managedClusterName, addonapiv1alpha1.ConfigReference{ + }, addonapiv1alpha1.ConfigReference{ ConfigGroupResource: addonapiv1alpha1.ConfigGroupResource{ - Group: addOnDeploymentConfigGVR.Group, - Resource: addOnDeploymentConfigGVR.Resource, + Group: addOnDeploymentConfigGVR.Group + "test", + Resource: addOnDeploymentConfigGVR.Resource + "test", }, ConfigReferent: addonapiv1alpha1.ConfigReferent{ - Namespace: addOnConfig.Namespace, - Name: addOnConfig.Name, + Namespace: managedClusterName, + Name: "addon-config-test", }, - LastObservedGeneration: 1, + LastObservedGeneration: 0, DesiredConfig: &addonapiv1alpha1.ConfigSpecHash{ ConfigReferent: addonapiv1alpha1.ConfigReferent{ - Namespace: addOnConfig.Namespace, - Name: addOnConfig.Name, + Namespace: managedClusterName, + Name: "addon-config-test", }, SpecHash: "", }, }) - - testAddOnConfigsImpl.supportedConfigGVRs = supportedConfig }) }) diff --git a/vendor/modules.txt b/vendor/modules.txt index 9b52fda21..50fcb4298 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1691,7 +1691,7 @@ k8s.io/utils/pointer k8s.io/utils/ptr k8s.io/utils/strings/slices k8s.io/utils/trace -# open-cluster-management.io/addon-framework v0.11.1-0.20241129080247-57b1d2859f50 +# open-cluster-management.io/addon-framework v0.11.1-0.20250218075422-4329ebea390c ## explicit; go 1.22.0 open-cluster-management.io/addon-framework/pkg/addonfactory open-cluster-management.io/addon-framework/pkg/addonmanager diff --git a/vendor/open-cluster-management.io/addon-framework/pkg/addonfactory/addondeploymentconfig.go b/vendor/open-cluster-management.io/addon-framework/pkg/addonfactory/addondeploymentconfig.go index f297c4870..96bf2721a 100644 --- a/vendor/open-cluster-management.io/addon-framework/pkg/addonfactory/addondeploymentconfig.go +++ b/vendor/open-cluster-management.io/addon-framework/pkg/addonfactory/addondeploymentconfig.go @@ -22,6 +22,20 @@ var AddOnDeploymentConfigGVR = schema.GroupVersionResource{ Resource: "addondeploymentconfigs", } +type resourceRequirements struct { + Limits map[string]string `json:"limits,omitempty"` + Requests map[string]string `json:"requests,omitempty"` +} + +// regexResourceRequirements defines a resource requirement rule for containers. A container is eligible for the +// specified resource requirements if its container ID matches the regular expression. +type regexResourceRequirements struct { + // ContainerIDRegex is the regular expression used to match container IDs. + ContainerIDRegex string `json:"containerIDRegex"` + // Resources defines the resource requirements for matched containers + Resources resourceRequirements `json:"resources"` +} + // ToAddOnNodePlacementValues only transform the AddOnDeploymentConfig NodePlacement part into Values object that has // a specific for helm chart values // for example: the spec of one AddOnDeploymentConfig is: @@ -109,6 +123,47 @@ func ToAddOnProxyConfigValues(config addonapiv1alpha1.AddOnDeploymentConfig) (Va return values, nil } +// ToAddOnResourceRequirementsValues only transform the AddOnDeploymentConfig ResourceRequirements part into Values object that has +// a specific for helm chart values +// for example: the spec of one AddOnDeploymentConfig is: +// +// { +// resourceRequirements:[{containerID: "*:*:*", resources: { limits: { memory: "4Gi"}, requests: { memory: "512Mi"}}}], +// } +// +// after transformed, the Values will be: +// map[global:map[resourceRequirements:[map[containerIDRegExp:"^.+:.+:.+$" resources:map[limits:map[memory:4Gi] +// requests:map[memory:512Mi]]]]]] +func ToAddOnResourceRequirementsValues(config addonapiv1alpha1.AddOnDeploymentConfig) (Values, error) { + if config.Spec.ResourceRequirements == nil { + return nil, nil + } + + resourceRequirements, err := getRegexResourceRequirements(config.Spec.ResourceRequirements) + if err != nil { + return nil, err + } + + type global struct { + ResourceRequirements []regexResourceRequirements `json:"resourceRequirements"` + } + + jsonStruct := struct { + Global global `json:"global"` + }{ + Global: global{ + ResourceRequirements: resourceRequirements, + }, + } + + values, err := JsonStructToValues(jsonStruct) + if err != nil { + return nil, err + } + + return values, nil +} + // ToAddOnCustomizedVariableValues only transform the CustomizedVariables in the spec of AddOnDeploymentConfig into Values object. // for example: the spec of one AddOnDeploymentConfig is: // @@ -166,15 +221,53 @@ func GetAddOnDeploymentConfigValues( } } +func getRegexResourceRequirements(requirements []addonapiv1alpha1.ContainerResourceRequirements) ([]regexResourceRequirements, error) { + newRequirements := []regexResourceRequirements{} + for _, item := range requirements { + // convert container ID to regex + parts := strings.Split(item.ContainerID, ":") + if len(parts) != 3 { + return nil, fmt.Errorf("invalid ContainerID: %s", item.ContainerID) + } + for index, part := range parts { + if part == "*" { + parts[index] = ".+" + } + } + newRequirements = append(newRequirements, regexResourceRequirements{ + ContainerIDRegex: fmt.Sprintf("^%s:%s:%s$", parts[0], parts[1], parts[2]), + Resources: resourceRequirements{ + Requests: toStringResourceList(item.Resources.Requests), + Limits: toStringResourceList(item.Resources.Limits), + }, + }) + } + + return newRequirements, nil +} + +func toStringResourceList(resourceList corev1.ResourceList) map[string]string { + if len(resourceList) == 0 { + return nil + } + newResourceList := map[string]string{} + for key, value := range resourceList { + newResourceList[string(key)] = value.String() + } + return newResourceList +} + // ToAddOnDeploymentConfigValues transform the AddOnDeploymentConfig object into Values object that is a plain value map // for example: the spec of one AddOnDeploymentConfig is: // // { // customizedVariables: [{name: "Image", value: "img"}, {name: "ImagePullPolicy", value: "Always"}], // nodePlacement: {nodeSelector: {"host": "ssd"}, tolerations: {"key": "test"}}, +// resourceRequirements:[{containerID: "*:*:*", resources: { limits: { memory: "4Gi"}, requests: { memory: "512Mi"}}}], // } // -// after transformed, the key set of Values object will be: {"Image", "ImagePullPolicy", "NodeSelector", "Tolerations"} +// after transformed, the key set of Values object will be: {"Image", "ImagePullPolicy", "NodeSelector", "Tolerations", +// "ResourceRequirements"} func ToAddOnDeploymentConfigValues(config addonapiv1alpha1.AddOnDeploymentConfig) (Values, error) { values, err := ToAddOnCustomizedVariableValues(config) if err != nil { @@ -200,6 +293,15 @@ func ToAddOnDeploymentConfigValues(config addonapiv1alpha1.AddOnDeploymentConfig values["ProxyCABundle"] = string(config.Spec.ProxyConfig.CABundle) } + // load ResourceRequirements settings + resourceRequirements, err := getRegexResourceRequirements(config.Spec.ResourceRequirements) + if err != nil { + return nil, err + } + if len(resourceRequirements) > 0 { + values["ResourceRequirements"] = resourceRequirements + } + return values, nil } diff --git a/vendor/open-cluster-management.io/addon-framework/pkg/addonmanager/controllers/addonconfig/controller.go b/vendor/open-cluster-management.io/addon-framework/pkg/addonmanager/controllers/addonconfig/controller.go index 2a39dff42..70c405602 100644 --- a/vendor/open-cluster-management.io/addon-framework/pkg/addonmanager/controllers/addonconfig/controller.go +++ b/vendor/open-cluster-management.io/addon-framework/pkg/addonmanager/controllers/addonconfig/controller.go @@ -172,12 +172,9 @@ func (c *addonConfigController) sync(ctx context.Context, syncCtx factory.SyncCo } func (c *addonConfigController) updateConfigSpecHashAndGenerations(addon *addonapiv1alpha1.ManagedClusterAddOn) error { - supportedConfigSet := map[addonapiv1alpha1.ConfigGroupResource]bool{} - for _, config := range addon.Status.SupportedConfigs { - supportedConfigSet[config] = true - } for index, configReference := range addon.Status.ConfigReferences { + // do not update for unsupported configs if !utils.ContainGR( c.configGVRs, configReference.ConfigGroupResource.Group, @@ -211,10 +208,6 @@ func (c *addonConfigController) updateConfigSpecHashAndGenerations(addon *addona // update desired spec hash only for the configs in spec for _, addonconfig := range addon.Spec.Configs { - // do not update spec hash for unsupported configs - if _, ok := supportedConfigSet[addonconfig.ConfigGroupResource]; !ok { - continue - } if configReference.DesiredConfig == nil { continue } diff --git a/vendor/open-cluster-management.io/addon-framework/pkg/addonmanager/controllers/agentdeploy/healthcheck_sync.go b/vendor/open-cluster-management.io/addon-framework/pkg/addonmanager/controllers/agentdeploy/healthcheck_sync.go index e81c02258..049622ca1 100644 --- a/vendor/open-cluster-management.io/addon-framework/pkg/addonmanager/controllers/agentdeploy/healthcheck_sync.go +++ b/vendor/open-cluster-management.io/addon-framework/pkg/addonmanager/controllers/agentdeploy/healthcheck_sync.go @@ -227,14 +227,16 @@ func (s *healthCheckSyncer) probeAddonStatusByWorks( } - if healthChecker != nil && healthChecker(FieldResults, cluster, addon) != nil { - meta.SetStatusCondition(&addon.Status.Conditions, metav1.Condition{ - Type: addonapiv1alpha1.ManagedClusterAddOnConditionAvailable, - Status: metav1.ConditionFalse, - Reason: addonapiv1alpha1.AddonAvailableReasonProbeUnavailable, - Message: fmt.Sprintf("Probe addon unavailable with err %v", err), - }) - return nil + if healthChecker != nil { + if err := healthChecker(FieldResults, cluster, addon); err != nil { + meta.SetStatusCondition(&addon.Status.Conditions, metav1.Condition{ + Type: addonapiv1alpha1.ManagedClusterAddOnConditionAvailable, + Status: metav1.ConditionFalse, + Reason: addonapiv1alpha1.AddonAvailableReasonProbeUnavailable, + Message: fmt.Sprintf("Probe addon unavailable with err %v", err), + }) + return nil + } } meta.SetStatusCondition(&addon.Status.Conditions, metav1.Condition{ diff --git a/vendor/open-cluster-management.io/addon-framework/pkg/addonmanager/controllers/registration/controller.go b/vendor/open-cluster-management.io/addon-framework/pkg/addonmanager/controllers/registration/controller.go index 1f98be332..b90fd2132 100644 --- a/vendor/open-cluster-management.io/addon-framework/pkg/addonmanager/controllers/registration/controller.go +++ b/vendor/open-cluster-management.io/addon-framework/pkg/addonmanager/controllers/registration/controller.go @@ -102,6 +102,12 @@ func (c *addonRegistrationController) sync(ctx context.Context, syncCtx factory. return nil } + addonPatcher := patcher.NewPatcher[ + *addonapiv1alpha1.ManagedClusterAddOn, + addonapiv1alpha1.ManagedClusterAddOnSpec, + addonapiv1alpha1.ManagedClusterAddOnStatus](c.addonClient.AddonV1alpha1().ManagedClusterAddOns(clusterName)) + + // patch supported configs var supportedConfigs []addonapiv1alpha1.ConfigGroupResource for _, config := range agentAddon.GetAgentAddonOptions().SupportedConfigGVRs { supportedConfigs = append(supportedConfigs, addonapiv1alpha1.ConfigGroupResource{ @@ -111,11 +117,12 @@ func (c *addonRegistrationController) sync(ctx context.Context, syncCtx factory. } managedClusterAddonCopy.Status.SupportedConfigs = supportedConfigs - addonPatcher := patcher.NewPatcher[ - *addonapiv1alpha1.ManagedClusterAddOn, - addonapiv1alpha1.ManagedClusterAddOnSpec, - addonapiv1alpha1.ManagedClusterAddOnStatus](c.addonClient.AddonV1alpha1().ManagedClusterAddOns(clusterName)) + statusChanged, err := addonPatcher.PatchStatus(ctx, managedClusterAddonCopy, managedClusterAddonCopy.Status, managedClusterAddon.Status) + if statusChanged { + return err + } + // if supported configs not change, continue to patch condition RegistrationApplied, status.Registrations and status.Namespace registrationOption := agentAddon.GetAgentAddonOptions().Registration if registrationOption == nil { meta.SetStatusCondition(&managedClusterAddonCopy.Status.Conditions, metav1.Condition{ @@ -155,23 +162,25 @@ func (c *addonRegistrationController) sync(ctx context.Context, syncCtx factory. _, err = addonPatcher.PatchStatus(ctx, managedClusterAddonCopy, managedClusterAddonCopy.Status, managedClusterAddon.Status) return err } - configs := registrationOption.CSRConfigurations(managedCluster) + configs := registrationOption.CSRConfigurations(managedCluster) managedClusterAddonCopy.Status.Registrations = configs - managedClusterAddonCopy.Status.Namespace = registrationOption.Namespace - if len(managedClusterAddonCopy.Spec.InstallNamespace) > 0 { - managedClusterAddonCopy.Status.Namespace = managedClusterAddonCopy.Spec.InstallNamespace - } - + var agentInstallNamespace string if registrationOption.AgentInstallNamespace != nil { - ns, err := registrationOption.AgentInstallNamespace(managedClusterAddonCopy) + agentInstallNamespace, err = registrationOption.AgentInstallNamespace(managedClusterAddonCopy) if err != nil { return err } - if len(ns) > 0 { - managedClusterAddonCopy.Status.Namespace = ns - } + } + + // Set the default namespace to registrationOption.Namespace + managedClusterAddonCopy.Status.Namespace = registrationOption.Namespace + // Override if agentInstallNamespace or InstallNamespace is specified + if len(agentInstallNamespace) > 0 { + managedClusterAddonCopy.Status.Namespace = agentInstallNamespace + } else if len(managedClusterAddonCopy.Spec.InstallNamespace) > 0 { + managedClusterAddonCopy.Status.Namespace = managedClusterAddonCopy.Spec.InstallNamespace } meta.SetStatusCondition(&managedClusterAddonCopy.Status.Conditions, metav1.Condition{ diff --git a/vendor/open-cluster-management.io/addon-framework/pkg/assets/template.go b/vendor/open-cluster-management.io/addon-framework/pkg/assets/template.go index 785439220..50d7c76b2 100644 --- a/vendor/open-cluster-management.io/addon-framework/pkg/assets/template.go +++ b/vendor/open-cluster-management.io/addon-framework/pkg/assets/template.go @@ -3,6 +3,7 @@ package assets import ( "bytes" "encoding/base64" + "regexp" "strings" "text/template" "time" @@ -11,12 +12,13 @@ import ( ) var templateFuncs = map[string]interface{}{ - "notAfter": notAfter, - "notBefore": notBefore, - "issuer": issuer, - "base64": base64encode, - "indent": indent, - "load": load, + "notAfter": notAfter, + "notBefore": notBefore, + "issuer": issuer, + "base64": base64encode, + "indent": indent, + "load": load, + "regexMatch": regexMatch, } func indent(indention int, v []byte) string { @@ -65,6 +67,11 @@ func load(n string, assets map[string][]byte) []byte { return assets[n] } +func regexMatch(pattern, input string) bool { + match, _ := regexp.MatchString(pattern, input) + return match +} + func renderFile(name string, tb []byte, data interface{}) ([]byte, error) { tmpl, err := template.New(name).Funcs(templateFuncs).Parse(string(tb)) if err != nil {