@@ -25,12 +25,14 @@ import (
25
25
corev1 "k8s.io/api/core/v1"
26
26
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27
27
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
28
+ "k8s.io/apimachinery/pkg/runtime/schema"
28
29
"k8s.io/klog/v2"
29
30
ctrl "sigs.k8s.io/controller-runtime"
30
31
"sigs.k8s.io/controller-runtime/pkg/client"
31
32
32
33
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
33
34
"sigs.k8s.io/cluster-api/api/v1beta1/index"
35
+ "sigs.k8s.io/cluster-api/internal/controllers/machinedeployment/mdutil"
34
36
"sigs.k8s.io/cluster-api/internal/util/taints"
35
37
"sigs.k8s.io/cluster-api/util"
36
38
"sigs.k8s.io/cluster-api/util/annotations"
@@ -133,7 +135,7 @@ func (r *Reconciler) reconcileNode(ctx context.Context, s *scope) (ctrl.Result,
133
135
_ , nodeHadInterruptibleLabel := node .Labels [clusterv1 .InterruptibleLabel ]
134
136
135
137
// Reconcile node taints
136
- if err := r .patchNode (ctx , remoteClient , node , nodeLabels , nodeAnnotations ); err != nil {
138
+ if err := r .patchNode (ctx , remoteClient , node , nodeLabels , nodeAnnotations , machine ); err != nil {
137
139
return ctrl.Result {}, errors .Wrapf (err , "failed to reconcile Node %s" , klog .KObj (node ))
138
140
}
139
141
if ! nodeHadInterruptibleLabel && interruptible {
@@ -255,7 +257,7 @@ func (r *Reconciler) getNode(ctx context.Context, c client.Reader, providerID st
255
257
256
258
// PatchNode is required to workaround an issue on Node.Status.Address which is incorrectly annotated as patchStrategy=merge
257
259
// and this causes SSA patch to fail in case there are two addresses with the same key https://github.com/kubernetes-sigs/cluster-api/issues/8417
258
- func (r * Reconciler ) patchNode (ctx context.Context , remoteClient client.Client , node * corev1.Node , newLabels , newAnnotations map [string ]string ) error {
260
+ func (r * Reconciler ) patchNode (ctx context.Context , remoteClient client.Client , node * corev1.Node , newLabels , newAnnotations map [string ]string , m * clusterv1. Machine ) error {
259
261
newNode := node .DeepCopy ()
260
262
261
263
// Adds the annotations CAPI sets on the node.
@@ -292,9 +294,70 @@ func (r *Reconciler) patchNode(ctx context.Context, remoteClient client.Client,
292
294
// Drop the NodeUninitializedTaint taint on the node given that we are reconciling labels.
293
295
hasTaintChanges := taints .RemoveNodeTaint (newNode , clusterv1 .NodeUninitializedTaint )
294
296
297
+ // Set Taint to a node in an old MachineSet and unset Taint from a node in a new MachineSet
298
+ isOutdated , err := shouldNodeHaveOutdatedTaint (ctx , r .Client , m )
299
+ if err != nil {
300
+ return errors .Wrapf (err , "failed to check if Node %s is outdated" , klog .KRef ("" , node .Name ))
301
+ }
302
+ if isOutdated {
303
+ hasTaintChanges = taints .EnsureNodeTaint (newNode , clusterv1 .NodeOutdatedRevisionTaint ) || hasTaintChanges
304
+ } else {
305
+ hasTaintChanges = taints .RemoveNodeTaint (newNode , clusterv1 .NodeOutdatedRevisionTaint ) || hasTaintChanges
306
+ }
307
+
295
308
if ! hasAnnotationChanges && ! hasLabelChanges && ! hasTaintChanges {
296
309
return nil
297
310
}
298
311
299
312
return remoteClient .Patch (ctx , newNode , client .StrategicMergeFrom (node ))
300
313
}
314
+
315
+ func shouldNodeHaveOutdatedTaint (ctx context.Context , c client.Client , m * clusterv1.Machine ) (bool , error ) {
316
+ if _ , hasLabel := m .Labels [clusterv1 .MachineDeploymentNameLabel ]; ! hasLabel {
317
+ return false , nil
318
+ }
319
+
320
+ // Resolve the MachineSet name via owner references because the label value
321
+ // could also be a hash.
322
+ objKey , err := getOwnerMachineSetObjectKey (m .ObjectMeta )
323
+ if err != nil {
324
+ return false , err
325
+ }
326
+ ms := & clusterv1.MachineSet {}
327
+ if err := c .Get (ctx , * objKey , ms ); err != nil {
328
+ return false , err
329
+ }
330
+ md := & clusterv1.MachineDeployment {}
331
+ objKey = & client.ObjectKey {
332
+ Namespace : m .ObjectMeta .Namespace ,
333
+ Name : m .Labels [clusterv1 .MachineDeploymentNameLabel ],
334
+ }
335
+ if err := c .Get (ctx , * objKey , md ); err != nil {
336
+ return false , err
337
+ }
338
+ msRev , err := mdutil .Revision (ms )
339
+ if err != nil {
340
+ return false , err
341
+ }
342
+ mdRev , err := mdutil .Revision (md )
343
+ if err != nil {
344
+ return false , err
345
+ }
346
+ if msRev < mdRev {
347
+ return true , nil
348
+ }
349
+ return false , nil
350
+ }
351
+
352
+ func getOwnerMachineSetObjectKey (obj metav1.ObjectMeta ) (* client.ObjectKey , error ) {
353
+ for _ , ref := range obj .GetOwnerReferences () {
354
+ gv , err := schema .ParseGroupVersion (ref .APIVersion )
355
+ if err != nil {
356
+ return nil , err
357
+ }
358
+ if ref .Kind == "MachineSet" && gv .Group == clusterv1 .GroupVersion .Group {
359
+ return & client.ObjectKey {Namespace : obj .Namespace , Name : ref .Name }, nil
360
+ }
361
+ }
362
+ return nil , errors .Errorf ("failed to find MachineSet owner reference for Machine %s" , klog .KRef (obj .GetNamespace (), obj .GetName ()))
363
+ }
0 commit comments