-
Notifications
You must be signed in to change notification settings - Fork 111
✨ ✨ feature feat: add labels to managedcluster resource if present #1123
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -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: | ||||||||||||||||||||||||||||
Comment on lines
+275
to
281
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix grammar in user-facing description (pluralization and phrasing). Current text is awkward: "ClusterLabels is labels set on ManagedCluster when creating only, other actors can update it afterwards." Tighten it to be grammatically correct and clearer. - description: ClusterLabels is labels set on ManagedCluster when
- creating only, other actors can update it afterwards.
+ description: ClusterLabels are labels set on the ManagedCluster at creation time; other actors can update them afterwards. 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
|
||||||||||||||||||||||||||||
description: "FeatureGates represents the list of feature gates | ||||||||||||||||||||||||||||
for registration\nIf it is set empty, default feature gates | ||||||||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -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: | ||||||||||||||||||||||||||||
Comment on lines
+275
to
281
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix grammar in user-facing description (pluralization and phrasing). Mirror the same correction here to keep copies consistent. - description: ClusterLabels is labels set on ManagedCluster when
- creating only, other actors can update it afterwards.
+ description: ClusterLabels are labels set on the ManagedCluster at creation time; other actors can update them afterwards. 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
|
||||||||||||||||||||||||||||
description: "FeatureGates represents the list of feature gates | ||||||||||||||||||||||||||||
for registration\nIf it is set empty, default feature gates | ||||||||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -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: | ||||||||||||||||||||||||||||
Comment on lines
+275
to
281
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix grammar in user-facing description (pluralization and phrasing). Align wording with the other CRD copies. - description: ClusterLabels is labels set on ManagedCluster when
- creating only, other actors can update it afterwards.
+ description: ClusterLabels are labels set on the ManagedCluster at creation time; other actors can update them afterwards. 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
|
||||||||||||||||||||||||||||
description: "FeatureGates represents the list of feature gates | ||||||||||||||||||||||||||||
for registration\nIf it is set empty, default feature gates | ||||||||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -88,6 +88,9 @@ spec: | |
{{if .ClusterAnnotationsString}} | ||
- "--cluster-annotations={{ .ClusterAnnotationsString }}" | ||
{{end}} | ||
{{if .ClusterLabelsString}} | ||
- "--cluster-labels={{ .ClusterLabelsString }}" | ||
{{end}} | ||
Comment on lines
+91
to
+93
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainConfirm registration agent supports --cluster-labels and values flow from Helm. Validate that:
Run this script: 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "Registration deployment renders the new flag:"
rg -n --no-heading 'registration-agent' -A20 manifests/klusterlet/management/klusterlet-registration-deployment.yaml | sed 's/^/YAML: /'
echo
echo "Search for registration option flag definition and usage:"
rg -n --no-heading 'cluster-labels' -A3 -B3 pkg/registration | sed 's/^/GO: /'
echo
echo "Check Helm values/docs for cluster labels sample:"
fd -a 'values.yaml' deploy | while read -r f; do
echo "== $f =="
rg -n --no-heading 'clusterLabels' -A3 -B3 "$f" || true
done Length of output: 5146 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "1) Search for --cluster-labels in registration deployment manifest:"
rg -n --no-heading -- '--cluster-labels' -A2 -B2 manifests/klusterlet/management/klusterlet-registration-deployment.yaml || echo "Not found"
echo
echo "2) Search for .ClusterLabelsString in registration deployment manifest:"
rg -n --no-heading 'ClusterLabelsString' -A2 -B2 manifests/klusterlet/management/klusterlet-registration-deployment.yaml || echo "Not found"
echo
echo "3) Search for --cluster-labels in agent deployment manifest:"
rg -n --no-heading -- '--cluster-labels' -A2 -B2 manifests/klusterlet/management/klusterlet-agent-deployment.yaml || echo "Not found"
echo
echo "4) Verify Helm chart defines Values.clusterLabels and helper to build ClusterLabelsString:"
echo "4a) Look for clusterLabels in klusterlet chart values:"
rg -n --no-heading 'clusterLabels' -A3 -B3 deploy/klusterlet/chart/klusterlet/values.yaml || echo "No clusterLabels in values.yaml"
echo
echo "4b) Search for clusterLabelsString helper in klusterlet chart templates:"
rg -n --no-heading 'clusterLabelsString' -A3 -B3 deploy/klusterlet/chart/klusterlet/templates || echo "No helper found" Length of output: 2493 Ensure Helm chart exposes and injects We’ve confirmed that:
To complete the feature, please:
This will ensure 🤖 Prompt for AI Agents
|
||
{{if gt .RegistrationKubeAPIQPS 0.0}} | ||
- "--kube-api-qps={{ .RegistrationKubeAPIQPS }}" | ||
{{end}} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
package registration | ||
|
||
import ( | ||
"bytes" | ||
"context" | ||
"fmt" | ||
"time" | ||
|
@@ -89,11 +90,14 @@ func (c *managedClusterCreatingController) sync(ctx context.Context, syncCtx fac | |
managedCluster = decorator(managedCluster) | ||
} | ||
|
||
if len(existingCluster.Spec.ManagedClusterClientConfigs) == len(managedCluster.Spec.ManagedClusterClientConfigs) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the reason we only compare size is because the configs can be set on the hub with different cabundle or endpoint. And the agent should trust setting of hub since hub would have more accurate info on how to access spoke apiserver. @zhujian7 I wonder whether to update the cluster should be returned by the decorator rather than compare in the controller. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, make sense, I think we can let the decorator interface return an additional |
||
// 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{}) | ||
Comment on lines
+93
to
101
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Equality gate may churn on ordering; make client-config comparison order-insensitive The current equalClientConfigs is order-sensitive; different ordering will trigger unnecessary updates. Sort by URL (and CABundle as tiebreaker) before comparing. -// 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
-}
+// equalClientConfigs compares two ClientConfig slices for equality (order-insensitive).
+func equalClientConfigs(configs1, configs2 []clusterv1.ClientConfig) bool {
+ if len(configs1) != len(configs2) {
+ return false
+ }
+ s1 := append([]clusterv1.ClientConfig(nil), configs1...)
+ s2 := append([]clusterv1.ClientConfig(nil), configs2...)
+ sort.Slice(s1, func(i, j int) bool {
+ if s1[i].URL == s1[j].URL {
+ return string(s1[i].CABundle) < string(s1[j].CABundle)
+ }
+ return s1[i].URL < s1[j].URL
+ })
+ sort.Slice(s2, func(i, j int) bool {
+ if s2[i].URL == s2[j].URL {
+ return string(s2[i].CABundle) < string(s2[j].CABundle)
+ }
+ return s2[i].URL < s2[j].URL
+ })
+ for i := range s1 {
+ if s1[i].URL != s2[i].URL || string(s1[i].CABundle) != string(s2[i].CABundle) {
+ return false
+ }
+ }
+ return true
+} Note: add the following import in this file: import "sort" 🤖 Prompt for AI Agents
|
||
// ManagedClusterClientConfigs in ManagedCluster is only allowed updated during bootstrap. | ||
// After bootstrap secret expired, an unauthorized error will be got, skip it | ||
|
@@ -112,15 +116,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 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. note we should not allowing set ANY label from the spoke. For instance, open-cluster-management.io/clusterset label is to define the label on the managedcluster, it should not be set by agent. |
||
cluster.Labels[k] = v | ||
} | ||
|
||
return cluster | ||
} | ||
} | ||
|
@@ -148,3 +170,46 @@ func ClientConfigDecorator(externalServerURLs []string, caBundle []byte) Managed | |
return cluster | ||
} | ||
} | ||
|
||
// equalClientConfigs compares two ClientConfig slices for equality | ||
func equalClientConfigs(configs1, configs2 []clusterv1.ClientConfig) bool { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same here, clientconfigs can also be updated by user, so we cannot update clientConfigs based on equality. |
||
if len(configs1) != len(configs2) { | ||
return false | ||
} | ||
for i, config1 := range configs1 { | ||
config2 := configs2[i] | ||
if config1.URL != config2.URL { | ||
return false | ||
} | ||
if !bytes.Equal(config1.CABundle, config2.CABundle) { | ||
return false | ||
} | ||
} | ||
return true | ||
} | ||
|
||
// equalLabels compares two label maps for equality | ||
func equalLabels(labels1, labels2 map[string]string) bool { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we cannot do such equal since the label of cluster can also be set by user on hub. So such label setting can only happen when creating the cluster. If the label exists already on the cluster, the controller cannot update the label or annotation since it might be updated by user on purpose. |
||
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 | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems not necessary?