From 38f00fd30cdd53796aa025bd23148bd9ba31c1b7 Mon Sep 17 00:00:00 2001 From: ramekris3163 Date: Tue, 12 Aug 2025 17:17:02 -0400 Subject: [PATCH 1/4] feat: add labels to managedcluster resource if present and have the ability to pass it through helm chart. Signed-off-by: ramekris3163 --- .../klusterlet/chart/klusterlet/values.yaml | 3 + .../klusterlet-agent-deployment.yaml | 3 + .../klusterlet-registration-deployment.yaml | 3 + .../klusterlet_controller.go | 8 + pkg/registration/spoke/options.go | 2 + .../spoke/registration/creating_controller.go | 74 +++- .../registration/creating_controller_test.go | 413 ++++++++++++++++++ pkg/registration/spoke/spokeagent.go | 1 + 8 files changed, 502 insertions(+), 5 deletions(-) diff --git a/deploy/klusterlet/chart/klusterlet/values.yaml b/deploy/klusterlet/chart/klusterlet/values.yaml index e0fd2a2848..35ec46fa7d 100644 --- a/deploy/klusterlet/chart/klusterlet/values.yaml +++ b/deploy/klusterlet/chart/klusterlet/values.yaml @@ -127,6 +127,9 @@ klusterlet: # - feature: "" # mode: "" # clientCertExpirationSeconds: 600 +# clusterLabels: +# environment: "production" +# region: "us-west-2" workConfiguration: {} # featureGates: # - feature: "" diff --git a/manifests/klusterlet/management/klusterlet-agent-deployment.yaml b/manifests/klusterlet/management/klusterlet-agent-deployment.yaml index 0770dec0b6..bc7f82a157 100644 --- a/manifests/klusterlet/management/klusterlet-agent-deployment.yaml +++ b/manifests/klusterlet/management/klusterlet-agent-deployment.yaml @@ -103,6 +103,9 @@ spec: {{if .ClusterAnnotationsString}} - "--cluster-annotations={{ .ClusterAnnotationsString }}" {{end}} + {{if .ClusterLabelsString}} + - "--cluster-labels={{ .ClusterLabelsString }}" + {{end}} {{if eq .InstallMode "SingletonHosted"}} - "--spoke-kubeconfig=/spoke/config/kubeconfig" - "--terminate-on-files=/spoke/config/kubeconfig" diff --git a/manifests/klusterlet/management/klusterlet-registration-deployment.yaml b/manifests/klusterlet/management/klusterlet-registration-deployment.yaml index 0f14fb7a92..b2024e884b 100644 --- a/manifests/klusterlet/management/klusterlet-registration-deployment.yaml +++ b/manifests/klusterlet/management/klusterlet-registration-deployment.yaml @@ -88,6 +88,9 @@ spec: {{if .ClusterAnnotationsString}} - "--cluster-annotations={{ .ClusterAnnotationsString }}" {{end}} + {{if .ClusterLabelsString}} + - "--cluster-labels={{ .ClusterLabelsString }}" + {{end}} {{if gt .RegistrationKubeAPIQPS 0.0}} - "--kube-api-qps={{ .RegistrationKubeAPIQPS }}" {{end}} diff --git a/pkg/operator/operators/klusterlet/controllers/klusterletcontroller/klusterlet_controller.go b/pkg/operator/operators/klusterlet/controllers/klusterletcontroller/klusterlet_controller.go index 2d1399ca89..68c4bbc02d 100644 --- a/pkg/operator/operators/klusterlet/controllers/klusterletcontroller/klusterlet_controller.go +++ b/pkg/operator/operators/klusterlet/controllers/klusterletcontroller/klusterlet_controller.go @@ -167,6 +167,7 @@ type klusterletConfig struct { Replica int32 ClientCertExpirationSeconds int32 ClusterAnnotationsString string + ClusterLabelsString string RegistrationKubeAPIQPS float32 RegistrationKubeAPIBurst int32 WorkKubeAPIQPS float32 @@ -389,6 +390,13 @@ func (n *klusterletController) sync(ctx context.Context, controllerContext facto annotationsArray = append(annotationsArray, fmt.Sprintf("%s=%s", k, v)) } config.ClusterAnnotationsString = strings.Join(annotationsArray, ",") + + // construct cluster labels string, the final format is "key1=value1,key2=value2" + var labelsArray []string + for k, v := range klusterlet.Spec.RegistrationConfiguration.ClusterLabels { + labelsArray = append(labelsArray, fmt.Sprintf("%s=%s", k, v)) + } + config.ClusterLabelsString = strings.Join(labelsArray, ",") } config.AboutAPIEnabled = helpers.FeatureGateEnabled( diff --git a/pkg/registration/spoke/options.go b/pkg/registration/spoke/options.go index c3566abc4f..9a1829c819 100644 --- a/pkg/registration/spoke/options.go +++ b/pkg/registration/spoke/options.go @@ -36,6 +36,7 @@ type SpokeAgentOptions struct { MaxCustomClusterClaims int ReservedClusterClaimSuffixes []string ClusterAnnotations map[string]string + ClusterLabels map[string]string RegisterDriverOption *registerfactory.Options } @@ -76,6 +77,7 @@ func (o *SpokeAgentOptions) AddFlags(fs *pflag.FlagSet) { "A list of suffixes for reserved cluster claims.") fs.StringToStringVar(&o.ClusterAnnotations, "cluster-annotations", o.ClusterAnnotations, `the annotations with the reserve prefix "agent.open-cluster-management.io" set on ManagedCluster when creating only, other actors can update it afterwards.`) + fs.StringToStringVar(&o.ClusterLabels, "cluster-labels", o.ClusterLabels, `the labels set on ManagedCluster when creating only, other actors can update it afterwards.`) o.RegisterDriverOption.AddFlags(fs) } diff --git a/pkg/registration/spoke/registration/creating_controller.go b/pkg/registration/spoke/registration/creating_controller.go index 9a6c6931d9..f27088fea9 100644 --- a/pkg/registration/spoke/registration/creating_controller.go +++ b/pkg/registration/spoke/registration/creating_controller.go @@ -89,11 +89,14 @@ func (c *managedClusterCreatingController) sync(ctx context.Context, syncCtx fac managedCluster = decorator(managedCluster) } - if len(existingCluster.Spec.ManagedClusterClientConfigs) == len(managedCluster.Spec.ManagedClusterClientConfigs) { + // Check if any updates are needed (ClientConfigs, Labels, or Annotations) + if equalClientConfigs(existingCluster.Spec.ManagedClusterClientConfigs, managedCluster.Spec.ManagedClusterClientConfigs) && + equalLabels(existingCluster.Labels, managedCluster.Labels) && + equalAnnotations(existingCluster.Annotations, managedCluster.Annotations) { return nil } - // update ManagedClusterClientConfigs in ManagedCluster + // update ManagedCluster (ClientConfigs, Labels, and Annotations) _, err = c.hubClusterClient.ClusterV1().ManagedClusters().Update(ctx, managedCluster, metav1.UpdateOptions{}) // ManagedClusterClientConfigs in ManagedCluster is only allowed updated during bootstrap. // After bootstrap secret expired, an unauthorized error will be got, skip it @@ -112,15 +115,33 @@ func skipUnauthorizedError(err error) error { return err } +// AnnotationDecorator set annotations from annotation map to ManagedCluster func AnnotationDecorator(annotations map[string]string) ManagedClusterDecorator { return func(cluster *clusterv1.ManagedCluster) *clusterv1.ManagedCluster { - filteredAnnotations := commonhelpers.FilterClusterAnnotations(annotations) if cluster.Annotations == nil { cluster.Annotations = make(map[string]string) } - for key, value := range filteredAnnotations { - cluster.Annotations[key] = value + + filteredAnnotations := commonhelpers.FilterClusterAnnotations(annotations) + for k, v := range filteredAnnotations { + cluster.Annotations[k] = v } + + return cluster + } +} + +// LabelDecorator set labels from label map to ManagedCluster +func LabelDecorator(labels map[string]string) ManagedClusterDecorator { + return func(cluster *clusterv1.ManagedCluster) *clusterv1.ManagedCluster { + if cluster.Labels == nil { + cluster.Labels = make(map[string]string) + } + + for k, v := range labels { + cluster.Labels[k] = v + } + return cluster } } @@ -148,3 +169,46 @@ func ClientConfigDecorator(externalServerURLs []string, caBundle []byte) Managed return cluster } } + +// equalClientConfigs compares two ClientConfig slices for equality +func equalClientConfigs(configs1, configs2 []clusterv1.ClientConfig) bool { + if len(configs1) != len(configs2) { + return false + } + for i, config1 := range configs1 { + config2 := configs2[i] + if config1.URL != config2.URL { + return false + } + if string(config1.CABundle) != string(config2.CABundle) { + return false + } + } + return true +} + +// equalLabels compares two label maps for equality +func equalLabels(labels1, labels2 map[string]string) bool { + if len(labels1) != len(labels2) { + return false + } + for k, v := range labels1 { + if labels2[k] != v { + return false + } + } + return true +} + +// equalAnnotations compares two annotation maps for equality +func equalAnnotations(annotations1, annotations2 map[string]string) bool { + if len(annotations1) != len(annotations2) { + return false + } + for k, v := range annotations1 { + if annotations2[k] != v { + return false + } + } + return true +} diff --git a/pkg/registration/spoke/registration/creating_controller_test.go b/pkg/registration/spoke/registration/creating_controller_test.go index 3ed52abd8d..20cacbb27d 100644 --- a/pkg/registration/spoke/registration/creating_controller_test.go +++ b/pkg/registration/spoke/registration/creating_controller_test.go @@ -78,3 +78,416 @@ func TestCreateSpokeCluster(t *testing.T) { }) } } + +func TestCreateSpokeClusterWithLabels(t *testing.T) { + cases := []struct { + name string + startingObjects []runtime.Object + labels map[string]string + validateActions func(t *testing.T, actions []clienttesting.Action) + }{ + { + name: "create a new cluster with labels", + startingObjects: []runtime.Object{}, + labels: map[string]string{ + "environment": "test", + "agent.open-cluster-management.io/cluster-type": "spoke", + }, + validateActions: func(t *testing.T, actions []clienttesting.Action) { + expectedClientConfigs := []clusterv1.ClientConfig{ + { + URL: testSpokeExternalServerUrl, + CABundle: []byte("testcabundle"), + }, + } + testingcommon.AssertActions(t, actions, "get", "create") + actual := actions[1].(clienttesting.CreateActionImpl).Object + actualClientConfigs := actual.(*clusterv1.ManagedCluster).Spec.ManagedClusterClientConfigs + testinghelpers.AssertManagedClusterClientConfigs(t, actualClientConfigs, expectedClientConfigs) + + // Verify labels + clusterLabels := actual.(*clusterv1.ManagedCluster).Labels + if len(clusterLabels) != 2 { + t.Errorf("expected cluster labels %#v but got: %#v", 2, len(clusterLabels)) + } + if value, ok := clusterLabels["environment"]; !ok || value != "test" { + t.Errorf("expected cluster label environment=test but got: %#v", + clusterLabels["environment"]) + } + if value, ok := clusterLabels["agent.open-cluster-management.io/cluster-type"]; !ok || value != "spoke" { + t.Errorf("expected cluster label agent.open-cluster-management.io/cluster-type=spoke but got: %#v", + clusterLabels["agent.open-cluster-management.io/cluster-type"]) + } + }, + }, + { + name: "create cluster with empty labels", + startingObjects: []runtime.Object{}, + labels: map[string]string{}, + validateActions: func(t *testing.T, actions []clienttesting.Action) { + testingcommon.AssertActions(t, actions, "get", "create") + actual := actions[1].(clienttesting.CreateActionImpl).Object + + // Verify no labels + clusterLabels := actual.(*clusterv1.ManagedCluster).Labels + if len(clusterLabels) != 0 { + t.Errorf("expected no cluster labels but got: %#v", clusterLabels) + } + }, + }, + { + name: "update an existing cluster with labels", + startingObjects: []runtime.Object{testinghelpers.NewManagedCluster()}, + labels: map[string]string{ + "region": "us-west-2", + }, + validateActions: func(t *testing.T, actions []clienttesting.Action) { + testingcommon.AssertActions(t, actions, "get", "update") + actual := actions[1].(clienttesting.UpdateActionImpl).Object + + // Verify labels + clusterLabels := actual.(*clusterv1.ManagedCluster).Labels + if len(clusterLabels) != 1 { + t.Errorf("expected cluster labels %#v but got: %#v", 1, len(clusterLabels)) + } + if value, ok := clusterLabels["region"]; !ok || value != "us-west-2" { + t.Errorf("expected cluster label region=us-west-2 but got: %#v", + clusterLabels["region"]) + } + }, + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + clusterClient := clusterfake.NewSimpleClientset(c.startingObjects...) + ctrl := managedClusterCreatingController{ + clusterName: testinghelpers.TestManagedClusterName, + clusterDecorators: []ManagedClusterDecorator{ + LabelDecorator(c.labels), + ClientConfigDecorator([]string{testSpokeExternalServerUrl}, []byte("testcabundle")), + }, + hubClusterClient: clusterClient, + } + + syncErr := ctrl.sync(context.TODO(), testingcommon.NewFakeSyncContext(t, "")) + if syncErr != nil { + t.Errorf("unexpected err: %v", syncErr) + } + + c.validateActions(t, clusterClient.Actions()) + }) + } +} + +func TestCreateSpokeClusterWithLabelsAndAnnotations(t *testing.T) { + cases := []struct { + name string + startingObjects []runtime.Object + validateActions func(t *testing.T, actions []clienttesting.Action) + }{ + { + name: "create cluster with both labels and annotations", + startingObjects: []runtime.Object{}, + validateActions: func(t *testing.T, actions []clienttesting.Action) { + expectedClientConfigs := []clusterv1.ClientConfig{ + { + URL: testSpokeExternalServerUrl, + CABundle: []byte("testcabundle"), + }, + } + testingcommon.AssertActions(t, actions, "get", "create") + actual := actions[1].(clienttesting.CreateActionImpl).Object + actualClientConfigs := actual.(*clusterv1.ManagedCluster).Spec.ManagedClusterClientConfigs + testinghelpers.AssertManagedClusterClientConfigs(t, actualClientConfigs, expectedClientConfigs) + + // Verify labels + clusterLabels := actual.(*clusterv1.ManagedCluster).Labels + if len(clusterLabels) != 2 { + t.Errorf("expected cluster labels %#v but got: %#v", 2, len(clusterLabels)) + } + if value, ok := clusterLabels["env"]; !ok || value != "production" { + t.Errorf("expected cluster label env=production but got: %#v", + clusterLabels["env"]) + } + if value, ok := clusterLabels["cluster-role"]; !ok || value != "worker" { + t.Errorf("expected cluster label cluster-role=worker but got: %#v", + clusterLabels["cluster-role"]) + } + + // Verify annotations + clusterAnnotations := actual.(*clusterv1.ManagedCluster).Annotations + if len(clusterAnnotations) != 1 { + t.Errorf("expected cluster annotations %#v but got: %#v", 1, len(clusterAnnotations)) + } + if value, ok := clusterAnnotations["agent.open-cluster-management.io/test"]; !ok || value != "true" { + t.Errorf("expected cluster annotation agent.open-cluster-management.io/test=true but got: %#v", + clusterAnnotations["agent.open-cluster-management.io/test"]) + } + }, + }, + { + name: "update existing cluster preserves both labels and annotations", + startingObjects: []runtime.Object{testinghelpers.NewManagedCluster()}, + validateActions: func(t *testing.T, actions []clienttesting.Action) { + testingcommon.AssertActions(t, actions, "get", "update") + actual := actions[1].(clienttesting.UpdateActionImpl).Object + + // Verify labels are updated + clusterLabels := actual.(*clusterv1.ManagedCluster).Labels + if len(clusterLabels) != 2 { + t.Errorf("expected cluster labels %#v but got: %#v", 2, len(clusterLabels)) + } + + // Verify annotations are updated + clusterAnnotations := actual.(*clusterv1.ManagedCluster).Annotations + if len(clusterAnnotations) != 1 { + t.Errorf("expected cluster annotations %#v but got: %#v", 1, len(clusterAnnotations)) + } + }, + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + clusterClient := clusterfake.NewSimpleClientset(c.startingObjects...) + ctrl := managedClusterCreatingController{ + clusterName: testinghelpers.TestManagedClusterName, + clusterDecorators: []ManagedClusterDecorator{ + LabelDecorator(map[string]string{ + "env": "production", + "cluster-role": "worker", + }), + AnnotationDecorator(map[string]string{ + "agent.open-cluster-management.io/test": "true", + }), + ClientConfigDecorator([]string{testSpokeExternalServerUrl}, []byte("testcabundle")), + }, + hubClusterClient: clusterClient, + } + + syncErr := ctrl.sync(context.TODO(), testingcommon.NewFakeSyncContext(t, "")) + if syncErr != nil { + t.Errorf("unexpected err: %v", syncErr) + } + + c.validateActions(t, clusterClient.Actions()) + }) + } +} + +func TestEqualityHelpers(t *testing.T) { + t.Run("equalLabels", func(t *testing.T) { + tests := []struct { + name string + labels1 map[string]string + labels2 map[string]string + expected bool + }{ + { + name: "equal empty maps", + labels1: map[string]string{}, + labels2: map[string]string{}, + expected: true, + }, + { + name: "equal nil and empty map", + labels1: nil, + labels2: map[string]string{}, + expected: true, + }, + { + name: "equal non-empty maps", + labels1: map[string]string{ + "env": "test", + "region": "us-west", + }, + labels2: map[string]string{ + "env": "test", + "region": "us-west", + }, + expected: true, + }, + { + name: "different values", + labels1: map[string]string{ + "env": "test", + }, + labels2: map[string]string{ + "env": "prod", + }, + expected: false, + }, + { + name: "different keys", + labels1: map[string]string{ + "env": "test", + }, + labels2: map[string]string{ + "environment": "test", + }, + expected: false, + }, + { + name: "different lengths", + labels1: map[string]string{ + "env": "test", + }, + labels2: map[string]string{ + "env": "test", + "region": "us-west", + }, + expected: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := equalLabels(tt.labels1, tt.labels2) + if result != tt.expected { + t.Errorf("equalLabels(%v, %v) = %v, expected %v", + tt.labels1, tt.labels2, result, tt.expected) + } + }) + } + }) + + t.Run("equalAnnotations", func(t *testing.T) { + tests := []struct { + name string + annotations1 map[string]string + annotations2 map[string]string + expected bool + }{ + { + name: "equal empty maps", + annotations1: map[string]string{}, + annotations2: map[string]string{}, + expected: true, + }, + { + name: "equal nil and empty map", + annotations1: nil, + annotations2: map[string]string{}, + expected: true, + }, + { + name: "equal non-empty maps", + annotations1: map[string]string{ + "agent.open-cluster-management.io/test": "true", + "custom.annotation": "value", + }, + annotations2: map[string]string{ + "agent.open-cluster-management.io/test": "true", + "custom.annotation": "value", + }, + expected: true, + }, + { + name: "different values", + annotations1: map[string]string{ + "agent.open-cluster-management.io/test": "true", + }, + annotations2: map[string]string{ + "agent.open-cluster-management.io/test": "false", + }, + expected: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := equalAnnotations(tt.annotations1, tt.annotations2) + if result != tt.expected { + t.Errorf("equalAnnotations(%v, %v) = %v, expected %v", + tt.annotations1, tt.annotations2, result, tt.expected) + } + }) + } + }) + + t.Run("equalClientConfigs", func(t *testing.T) { + tests := []struct { + name string + configs1 []clusterv1.ClientConfig + configs2 []clusterv1.ClientConfig + expected bool + }{ + { + name: "equal empty slices", + configs1: []clusterv1.ClientConfig{}, + configs2: []clusterv1.ClientConfig{}, + expected: true, + }, + { + name: "equal nil and empty slice", + configs1: nil, + configs2: []clusterv1.ClientConfig{}, + expected: true, + }, + { + name: "equal non-empty slices", + configs1: []clusterv1.ClientConfig{ + { + URL: "https://test.example.com", + CABundle: []byte("test-ca-bundle"), + }, + }, + configs2: []clusterv1.ClientConfig{ + { + URL: "https://test.example.com", + CABundle: []byte("test-ca-bundle"), + }, + }, + expected: true, + }, + { + name: "different lengths", + configs1: []clusterv1.ClientConfig{ + {URL: "https://test1.example.com"}, + }, + configs2: []clusterv1.ClientConfig{ + {URL: "https://test1.example.com"}, + {URL: "https://test2.example.com"}, + }, + expected: false, + }, + { + name: "different URLs", + configs1: []clusterv1.ClientConfig{ + {URL: "https://test1.example.com"}, + }, + configs2: []clusterv1.ClientConfig{ + {URL: "https://test2.example.com"}, + }, + expected: false, + }, + { + name: "different CA bundles", + configs1: []clusterv1.ClientConfig{ + { + URL: "https://test.example.com", + CABundle: []byte("bundle1"), + }, + }, + configs2: []clusterv1.ClientConfig{ + { + URL: "https://test.example.com", + CABundle: []byte("bundle2"), + }, + }, + expected: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := equalClientConfigs(tt.configs1, tt.configs2) + if result != tt.expected { + t.Errorf("equalClientConfigs(%v, %v) = %v, expected %v", + tt.configs1, tt.configs2, result, tt.expected) + } + }) + } + }) +} diff --git a/pkg/registration/spoke/spokeagent.go b/pkg/registration/spoke/spokeagent.go index 6894bcc08d..6dcbf9f5bd 100644 --- a/pkg/registration/spoke/spokeagent.go +++ b/pkg/registration/spoke/spokeagent.go @@ -283,6 +283,7 @@ func (o *SpokeAgentConfig) RunSpokeAgentWithSpokeInformers(ctx context.Context, o.agentOptions.SpokeClusterName, []registration.ManagedClusterDecorator{ registration.AnnotationDecorator(o.registrationOption.ClusterAnnotations), + registration.LabelDecorator(o.registrationOption.ClusterLabels), registration.ClientConfigDecorator(o.registrationOption.SpokeExternalServerURLs, spokeClusterCABundle), o.driver.ManagedClusterDecorator, }, From e10651e24a9cb4b18e4d63893d6c5e8ff712928d Mon Sep 17 00:00:00 2001 From: ramekris3163 Date: Tue, 19 Aug 2025 14:11:21 -0400 Subject: [PATCH 2/4] update ocm api to latest commit Signed-off-by: ramekris3163 --- go.mod | 2 +- go.sum | 4 ++-- vendor/modules.txt | 2 +- ...perator.open-cluster-management.io_klusterlets.crd.yaml | 6 ++++++ .../api/operator/v1/types_klusterlet.go | 4 ++++ .../api/operator/v1/zz_generated.deepcopy.go | 7 +++++++ 6 files changed, 21 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 2389d500d3..a93add789f 100644 --- a/go.mod +++ b/go.mod @@ -40,7 +40,7 @@ require ( k8s.io/kubectl v0.33.3 k8s.io/utils v0.0.0-20241210054802-24370beab758 open-cluster-management.io/addon-framework v1.0.1-0.20250722093201-ee47752c02f3 - open-cluster-management.io/api v1.0.1-0.20250730122947-5e3423e7794a + open-cluster-management.io/api v1.0.1-0.20250818020747-c0d6364fa4a6 open-cluster-management.io/sdk-go v1.0.1-0.20250805021042-68bb7fc51d4e sigs.k8s.io/about-api v0.0.0-20250131010323-518069c31c03 sigs.k8s.io/cluster-inventory-api v0.0.0-20240730014211-ef0154379848 diff --git a/go.sum b/go.sum index a0a71d2ad7..0f3db12e7a 100644 --- a/go.sum +++ b/go.sum @@ -496,8 +496,8 @@ k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJ k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= open-cluster-management.io/addon-framework v1.0.1-0.20250722093201-ee47752c02f3 h1:r7f57/YPg4caE2N1JQX5sVdBMW5f4VftIUZoW2AWzMs= open-cluster-management.io/addon-framework v1.0.1-0.20250722093201-ee47752c02f3/go.mod h1:U/AQsLpMi4jay9SC3x+uSh2vsB7ZZPLm63MiRnA9mX4= -open-cluster-management.io/api v1.0.1-0.20250730122947-5e3423e7794a h1:RtfQsfeU+uedsi7btdGdYeSVJw6v2Zp1pMB5XkgHRW8= -open-cluster-management.io/api v1.0.1-0.20250730122947-5e3423e7794a/go.mod h1:KEj/4wbUjdbWktrKLL8+mWzAIzE6Ii3bcRr4CvnBNEg= +open-cluster-management.io/api v1.0.1-0.20250818020747-c0d6364fa4a6 h1:h+ROW7XVTrzWvRH3v3HEuq8lg0kklanK8kFwDIwO3DY= +open-cluster-management.io/api v1.0.1-0.20250818020747-c0d6364fa4a6/go.mod h1:KEj/4wbUjdbWktrKLL8+mWzAIzE6Ii3bcRr4CvnBNEg= open-cluster-management.io/sdk-go v1.0.1-0.20250805021042-68bb7fc51d4e h1:jI/RdWsyShHxLxAt5OilP0YOiH2qsSYcDtHPS1bT8Is= open-cluster-management.io/sdk-go v1.0.1-0.20250805021042-68bb7fc51d4e/go.mod h1:sHOVhUgA286ceEq3IjFWqxobt9Lu+VBCAUZByFgN0oM= sigs.k8s.io/about-api v0.0.0-20250131010323-518069c31c03 h1:1ShFiMjGQOR/8jTBkmZrk1gORxnvMwm1nOy2/DbHg4U= diff --git a/vendor/modules.txt b/vendor/modules.txt index 871cbb8712..02adcf7530 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1737,7 +1737,7 @@ open-cluster-management.io/addon-framework/pkg/agent open-cluster-management.io/addon-framework/pkg/assets open-cluster-management.io/addon-framework/pkg/index open-cluster-management.io/addon-framework/pkg/utils -# open-cluster-management.io/api v1.0.1-0.20250730122947-5e3423e7794a +# open-cluster-management.io/api v1.0.1-0.20250818020747-c0d6364fa4a6 ## explicit; go 1.23.6 open-cluster-management.io/api/addon/v1alpha1 open-cluster-management.io/api/client/addon/clientset/versioned diff --git a/vendor/open-cluster-management.io/api/operator/v1/0000_00_operator.open-cluster-management.io_klusterlets.crd.yaml b/vendor/open-cluster-management.io/api/operator/v1/0000_00_operator.open-cluster-management.io_klusterlets.crd.yaml index e95a0481b4..a91388fdb6 100644 --- a/vendor/open-cluster-management.io/api/operator/v1/0000_00_operator.open-cluster-management.io_klusterlets.crd.yaml +++ b/vendor/open-cluster-management.io/api/operator/v1/0000_00_operator.open-cluster-management.io_klusterlets.crd.yaml @@ -272,6 +272,12 @@ spec: required: - maxCustomClusterClaims type: object + clusterLabels: + additionalProperties: + type: string + description: ClusterLabels is labels set on ManagedCluster when + creating only, other actors can update it afterwards. + type: object featureGates: description: "FeatureGates represents the list of feature gates for registration\nIf it is set empty, default feature gates diff --git a/vendor/open-cluster-management.io/api/operator/v1/types_klusterlet.go b/vendor/open-cluster-management.io/api/operator/v1/types_klusterlet.go index 781ae61619..525713a1cb 100644 --- a/vendor/open-cluster-management.io/api/operator/v1/types_klusterlet.go +++ b/vendor/open-cluster-management.io/api/operator/v1/types_klusterlet.go @@ -152,6 +152,10 @@ type RegistrationConfiguration struct { // +optional ClusterAnnotations map[string]string `json:"clusterAnnotations,omitempty"` + // ClusterLabels is labels set on ManagedCluster when creating only, other actors can update it afterwards. + // +optional + ClusterLabels map[string]string `json:"clusterLabels,omitempty"` + // KubeAPIQPS indicates the maximum QPS while talking with apiserver on the spoke cluster. // If it is set empty, use the default value: 50 // +optional diff --git a/vendor/open-cluster-management.io/api/operator/v1/zz_generated.deepcopy.go b/vendor/open-cluster-management.io/api/operator/v1/zz_generated.deepcopy.go index 5d28a9d7a0..957bb21886 100644 --- a/vendor/open-cluster-management.io/api/operator/v1/zz_generated.deepcopy.go +++ b/vendor/open-cluster-management.io/api/operator/v1/zz_generated.deepcopy.go @@ -657,6 +657,13 @@ func (in *RegistrationConfiguration) DeepCopyInto(out *RegistrationConfiguration (*out)[key] = val } } + if in.ClusterLabels != nil { + in, out := &in.ClusterLabels, &out.ClusterLabels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } in.BootstrapKubeConfigs.DeepCopyInto(&out.BootstrapKubeConfigs) in.RegistrationDriver.DeepCopyInto(&out.RegistrationDriver) if in.ClusterClaimConfiguration != nil { From 797c2975e61d1ae057a74e40ecf3c06049bf2c9d Mon Sep 17 00:00:00 2001 From: ramekris3163 Date: Tue, 19 Aug 2025 16:18:41 -0400 Subject: [PATCH 3/4] update crds Signed-off-by: ramekris3163 --- deploy/.DS_Store | Bin 0 -> 6148 bytes .../cluster-manager.clusterserviceversion.yaml | 2 +- ...en-cluster-management.io_klusterlets.crd.yaml | 6 ++++++ ...en-cluster-management.io_klusterlets.crd.yaml | 6 ++++++ .../klusterlet.clusterserviceversion.yaml | 2 +- ...r.open-cluster-management.io_klusterlets.yaml | 6 ++++++ 6 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 deploy/.DS_Store diff --git a/deploy/.DS_Store b/deploy/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..23baacb761c0596347c8356759c95aa045ddcaca GIT binary patch literal 6148 zcmeHKu};H44E2Q!0R}oU<_8!$u!kzlAoT~JEo!AEq=I(8FJM7pLgFJ>nb{Eozr*v{ zltgJ&!~h7{lJ8x7_Qm&3ien;jy~U_Y)FGk-${1~9SRuU5+LA~uyy(InbGo1_I-ruy zTi#~)iwyAH?a`15TF^Ds@9z?KIOh8-#@+s~DrX`6=;35@L|Ntepr{7$Os2OlPltEU zM_-8=eIsf%sbv>?yoPpY+Bi>vQ_P#ExW8HFSI_CzlD@XbczwO$N5)vBUNMczyrh z46-L;f47fP&=RF)rYisLpTx(P48I* Date: Mon, 25 Aug 2025 13:26:54 -0400 Subject: [PATCH 4/4] fix verification errors. Signed-off-by: ramekris3163 --- pkg/registration/spoke/options.go | 3 ++- pkg/registration/spoke/registration/creating_controller.go | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pkg/registration/spoke/options.go b/pkg/registration/spoke/options.go index 9a1829c819..283942e156 100644 --- a/pkg/registration/spoke/options.go +++ b/pkg/registration/spoke/options.go @@ -77,7 +77,8 @@ func (o *SpokeAgentOptions) AddFlags(fs *pflag.FlagSet) { "A list of suffixes for reserved cluster claims.") fs.StringToStringVar(&o.ClusterAnnotations, "cluster-annotations", o.ClusterAnnotations, `the annotations with the reserve prefix "agent.open-cluster-management.io" set on ManagedCluster when creating only, other actors can update it afterwards.`) - fs.StringToStringVar(&o.ClusterLabels, "cluster-labels", o.ClusterLabels, `the labels set on ManagedCluster when creating only, other actors can update it afterwards.`) + fs.StringToStringVar(&o.ClusterLabels, "cluster-labels", o.ClusterLabels, `the labels set on ManagedCluster + when creating only, other actors can update it afterwards.`) o.RegisterDriverOption.AddFlags(fs) } diff --git a/pkg/registration/spoke/registration/creating_controller.go b/pkg/registration/spoke/registration/creating_controller.go index f27088fea9..1620414e8c 100644 --- a/pkg/registration/spoke/registration/creating_controller.go +++ b/pkg/registration/spoke/registration/creating_controller.go @@ -1,6 +1,7 @@ package registration import ( + "bytes" "context" "fmt" "time" @@ -180,7 +181,7 @@ func equalClientConfigs(configs1, configs2 []clusterv1.ClientConfig) bool { if config1.URL != config2.URL { return false } - if string(config1.CABundle) != string(config2.CABundle) { + if !bytes.Equal(config1.CABundle, config2.CABundle) { return false } }