55 "fmt"
66 "maps"
77 "path"
8- "slices"
98 "strings"
109
1110 "github.com/aws/aws-sdk-go-v2/aws"
@@ -28,6 +27,9 @@ type NodeLabelController struct {
2827 // Labels is a list of label keys to sync from the node to the cloud provider
2928 Labels []string
3029
30+ // Annotations is a list of annotation keys to sync from the node to the cloud provider
31+ Annotations []string
32+
3133 // Cloud is the cloud provider (aws or gcp)
3234 Cloud string
3335}
@@ -54,8 +56,8 @@ func (r *NodeLabelController) SetupCloudProvider(ctx context.Context) error {
5456
5557func (r * NodeLabelController ) SetupWithManager (mgr ctrl.Manager ) error {
5658 // to reduce the number of API calls to AWS and GCP, filter out node events that
57- // do not involve changes to the monitored label set (r.labels) .
58- labelChangePredicate := predicate.Funcs {
59+ // do not involve changes to the monitored label or annotation sets .
60+ changePredicate := predicate.Funcs {
5961 UpdateFunc : func (e event.UpdateEvent ) bool {
6062 oldNode , ok := e .ObjectOld .(* corev1.Node )
6163 if ! ok {
@@ -65,15 +67,15 @@ func (r *NodeLabelController) SetupWithManager(mgr ctrl.Manager) error {
6567 if ! ok {
6668 return false
6769 }
68- return shouldProcessNodeUpdate (oldNode , newNode , r .Labels )
70+ return shouldProcessNodeUpdate (oldNode , newNode , r .Labels , r . Annotations )
6971 },
7072
7173 CreateFunc : func (e event.CreateEvent ) bool {
7274 node , ok := e .Object .(* corev1.Node )
7375 if ! ok {
7476 return false
7577 }
76- return shouldProcessNodeCreate (node , r .Labels )
78+ return shouldProcessNodeCreate (node , r .Labels , r . Annotations )
7779 },
7880
7981 DeleteFunc : func (e event.DeleteEvent ) bool {
@@ -87,21 +89,46 @@ func (r *NodeLabelController) SetupWithManager(mgr ctrl.Manager) error {
8789
8890 return ctrl .NewControllerManagedBy (mgr ).
8991 For (& corev1.Node {}).
90- WithEventFilter (labelChangePredicate ).
92+ WithEventFilter (changePredicate ).
9193 Complete (r )
9294}
9395
9496// shouldProcessNodeUpdate determines if a node update event should trigger reconciliation
95- // based on whether any monitored labels have changed.
96- func shouldProcessNodeUpdate (oldNode , newNode * corev1.Node , monitoredLabels []string ) bool {
97+ // based on whether any monitored labels or annotations have changed.
98+ func shouldProcessNodeUpdate (oldNode , newNode * corev1.Node , monitoredLabels , monitoredAnnotations []string ) bool {
9799 if oldNode == nil || newNode == nil {
98100 return false
99101 }
100102
101103 // Check if any monitored labels changed
102104 for _ , k := range monitoredLabels {
103- newVal , newExists := newNode .Labels [k ]
104- oldVal , oldExists := oldNode .Labels [k ]
105+ newVal , newExists := "" , false
106+ oldVal , oldExists := "" , false
107+
108+ if newNode .Labels != nil {
109+ newVal , newExists = newNode .Labels [k ]
110+ }
111+ if oldNode .Labels != nil {
112+ oldVal , oldExists = oldNode .Labels [k ]
113+ }
114+
115+ if newExists != oldExists || (newExists && newVal != oldVal ) {
116+ return true
117+ }
118+ }
119+
120+ // Check if any monitored annotations changed
121+ for _ , k := range monitoredAnnotations {
122+ newVal , newExists := "" , false
123+ oldVal , oldExists := "" , false
124+
125+ if newNode .Annotations != nil {
126+ newVal , newExists = newNode .Annotations [k ]
127+ }
128+ if oldNode .Annotations != nil {
129+ oldVal , oldExists = oldNode .Annotations [k ]
130+ }
131+
105132 if newExists != oldExists || (newExists && newVal != oldVal ) {
106133 return true
107134 }
@@ -110,15 +137,27 @@ func shouldProcessNodeUpdate(oldNode, newNode *corev1.Node, monitoredLabels []st
110137}
111138
112139// shouldProcessNodeCreate determines if a newly created node should trigger reconciliation
113- // based on whether it has any of the monitored labels.
114- func shouldProcessNodeCreate (node * corev1.Node , monitoredLabels []string ) bool {
140+ // based on whether it has any of the monitored labels or annotations .
141+ func shouldProcessNodeCreate (node * corev1.Node , monitoredLabels , monitoredAnnotations []string ) bool {
115142 if node == nil {
116143 return false
117144 }
118145
119- for _ , k := range monitoredLabels {
120- if _ , ok := node .Labels [k ]; ok {
121- return true
146+ // Check if node has any monitored labels
147+ if node .Labels != nil {
148+ for _ , k := range monitoredLabels {
149+ if _ , ok := node .Labels [k ]; ok {
150+ return true
151+ }
152+ }
153+ }
154+
155+ // Check if node has any monitored annotations
156+ if node .Annotations != nil {
157+ for _ , k := range monitoredAnnotations {
158+ if _ , ok := node .Annotations [k ]; ok {
159+ return true
160+ }
122161 }
123162 }
124163 return false
@@ -139,31 +178,45 @@ func (r *NodeLabelController) Reconcile(ctx context.Context, req ctrl.Request) (
139178 return ctrl.Result {}, nil
140179 }
141180
142- labels := make (map [string ]string )
143- for _ , k := range r .Labels {
144- if value , exists := node .Labels [k ]; exists {
145- labels [k ] = value
181+ // Create a map for tags to sync with the cloud provider
182+ tagsToSync := make (map [string ]string )
183+
184+ // First collect labels (may be overwritten by annotations with same key)
185+ if node .Labels != nil {
186+ for _ , k := range r .Labels {
187+ if value , exists := node .Labels [k ]; exists {
188+ tagsToSync [k ] = value
189+ }
190+ }
191+ }
192+
193+ // Then collect annotations (will overwrite labels with same key)
194+ if node .Annotations != nil {
195+ for _ , k := range r .Annotations {
196+ if value , exists := node .Annotations [k ]; exists {
197+ tagsToSync [k ] = value
198+ }
146199 }
147200 }
148201
149202 var err error
150203 switch r .Cloud {
151204 case "aws" :
152- err = r .syncAWSTags (ctx , providerID , labels )
205+ err = r .syncAWSTags (ctx , providerID , tagsToSync )
153206 case "gcp" :
154- err = r .syncGCPLabels (ctx , providerID , labels )
207+ err = r .syncGCPLabels (ctx , providerID , tagsToSync )
155208 }
156209
157210 if err != nil {
158- logger .Error (err , "failed to sync labels " )
211+ logger .Error (err , "failed to sync tags " )
159212 return ctrl.Result {}, err
160213 }
161214
162- logger .Info ("Successfully synced labels to cloud provider" , "labels " , labels )
215+ logger .Info ("Successfully synced tags to cloud provider" , "tags " , tagsToSync )
163216 return ctrl.Result {}, nil
164217}
165218
166- func (r * NodeLabelController ) syncAWSTags (ctx context.Context , providerID string , desiredLabels map [string ]string ) error {
219+ func (r * NodeLabelController ) syncAWSTags (ctx context.Context , providerID string , desiredTags map [string ]string ) error {
167220 instanceID := path .Base (providerID )
168221 if instanceID == "" {
169222 return fmt .Errorf ("invalid AWS provider ID format: %q" , providerID )
@@ -181,9 +234,19 @@ func (r *NodeLabelController) syncAWSTags(ctx context.Context, providerID string
181234 return fmt .Errorf ("failed to fetch node's current AWS tags: %v" , err )
182235 }
183236
237+ // Create a set of all monitored keys (both labels and annotations)
238+ monitoredKeys := make (map [string ]bool )
239+ for _ , k := range r .Labels {
240+ monitoredKeys [k ] = true
241+ }
242+ for _ , k := range r .Annotations {
243+ monitoredKeys [k ] = true
244+ }
245+
184246 currentTags := make (map [string ]string )
185247 for _ , tag := range result .Tags {
186- if key := aws .ToString (tag .Key ); key != "" && slices .Contains (r .Labels , key ) {
248+ key := aws .ToString (tag .Key )
249+ if key != "" && monitoredKeys [key ] {
187250 currentTags [key ] = aws .ToString (tag .Value )
188251 }
189252 }
@@ -192,7 +255,7 @@ func (r *NodeLabelController) syncAWSTags(ctx context.Context, providerID string
192255 toDelete := make ([]types.Tag , 0 )
193256
194257 // find tags to add or update
195- for k , v := range desiredLabels {
258+ for k , v := range desiredTags {
196259 if curr , exists := currentTags [k ]; ! exists || curr != v {
197260 toAdd = append (toAdd , types.Tag {
198261 Key : aws .String (k ),
@@ -203,8 +266,8 @@ func (r *NodeLabelController) syncAWSTags(ctx context.Context, providerID string
203266
204267 // find monitored tags to remove
205268 for k := range currentTags {
206- if slices . Contains ( r . Labels , k ) {
207- if _ , exists := desiredLabels [k ]; ! exists {
269+ if monitoredKeys [ k ] {
270+ if _ , exists := desiredTags [k ]; ! exists {
208271 toDelete = append (toDelete , types.Tag {
209272 Key : aws .String (k ),
210273 })
@@ -235,7 +298,7 @@ func (r *NodeLabelController) syncAWSTags(ctx context.Context, providerID string
235298 return nil
236299}
237300
238- func (r * NodeLabelController ) syncGCPLabels (ctx context.Context , providerID string , desiredLabels map [string ]string ) error {
301+ func (r * NodeLabelController ) syncGCPLabels (ctx context.Context , providerID string , desiredTags map [string ]string ) error {
239302 project , zone , name , err := parseGCPProviderID (providerID )
240303 if err != nil {
241304 return fmt .Errorf ("failed to parse GCP provider ID: %v" , err )
@@ -251,23 +314,28 @@ func (r *NodeLabelController) syncGCPLabels(ctx context.Context, providerID stri
251314 newLabels = make (map [string ]string )
252315 }
253316
317+ // Create a set of all monitored keys (both labels and annotations)
318+ allMonitoredKeys := make ([]string , 0 , len (r .Labels )+ len (r .Annotations ))
319+ allMonitoredKeys = append (allMonitoredKeys , r .Labels ... )
320+ allMonitoredKeys = append (allMonitoredKeys , r .Annotations ... )
321+
254322 // create a set of sanitized monitored keys for easy lookup
255323 monitoredKeys := make (map [string ]string ) // sanitized -> original
256- for _ , k := range r . Labels {
324+ for _ , k := range allMonitoredKeys {
257325 monitoredKeys [sanitizeKeyForGCP (k )] = k
258326 }
259327
260328 // remove any existing monitored labels that are no longer desired
261329 for k := range newLabels {
262330 if orig , isMonitored := monitoredKeys [k ]; isMonitored {
263- if _ , exists := desiredLabels [orig ]; ! exists {
331+ if _ , exists := desiredTags [orig ]; ! exists {
264332 delete (newLabels , k )
265333 }
266334 }
267335 }
268336
269- // add or update desired labels
270- for k , v := range desiredLabels {
337+ // add or update desired tags
338+ for k , v := range desiredTags {
271339 newLabels [sanitizeKeyForGCP (k )] = sanitizeValueForGCP (v )
272340 }
273341
0 commit comments