@@ -26,6 +26,7 @@ import (
26
26
corev1 "k8s.io/api/core/v1"
27
27
apierrors "k8s.io/apimachinery/pkg/api/errors"
28
28
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
29
30
"k8s.io/apimachinery/pkg/labels"
30
31
kerrors "k8s.io/apimachinery/pkg/util/errors"
31
32
"k8s.io/apiserver/pkg/storage/names"
@@ -41,6 +42,7 @@ import (
41
42
"sigs.k8s.io/cluster-api/controllers/external"
42
43
"sigs.k8s.io/cluster-api/controllers/noderefutil"
43
44
"sigs.k8s.io/cluster-api/controllers/remote"
45
+ "sigs.k8s.io/cluster-api/internal/contract"
44
46
"sigs.k8s.io/cluster-api/internal/controllers/machine"
45
47
capilabels "sigs.k8s.io/cluster-api/internal/labels"
46
48
"sigs.k8s.io/cluster-api/internal/util/ssa"
@@ -364,11 +366,14 @@ func (r *Reconciler) reconcile(ctx context.Context, cluster *clusterv1.Cluster,
364
366
return ctrl.Result {}, nil
365
367
}
366
368
367
- // syncMachines updates Machines to propagate in-place mutable fields from the MachineSet.
368
- // Note: It also cleans up managed fields of all Machines so that Machines that were created/patched before (< v1.4.0)
369
- // the controller adopted Server-Side-Apply (SSA) can also work with SSA. Otherwise fields would be co-owned by
370
- // our "old" "manager" and "capi-machineset" and then we would not be able to e.g. drop labels and annotations.
371
- // TODO: update the labels and annotations to the corresponding infra machines and the boostrap configs of the filtered machines.
369
+ // syncMachines updates Machines, InfrastructureMachine and BootstrapConfig to propagate in-place mutable fields
370
+ // from the MachineSet.
371
+ // Note: It also cleans up managed fields of all Machines so that Machines that were
372
+ // created/patched before (< v1.4.0) the controller adopted Server-Side-Apply (SSA) can also work with SSA.
373
+ // Note: For InfrastructureMachines and BootstrapConfigs it also drops ownership of "metadata.labels" and
374
+ // "metadata.annotations" from "manager" so that "capi-machineset" can own these fields and can work with SSA.
375
+ // Otherwise fields would be co-owned by our "old" "manager" and "capi-machineset" and then we would not be
376
+ // able to e.g. drop labels and annotations.
372
377
func (r * Reconciler ) syncMachines (ctx context.Context , machineSet * clusterv1.MachineSet , machines []* clusterv1.Machine ) error {
373
378
log := ctrl .LoggerFrom (ctx )
374
379
for i := range machines {
@@ -397,6 +402,46 @@ func (r *Reconciler) syncMachines(ctx context.Context, machineSet *clusterv1.Mac
397
402
return errors .Wrapf (err , "failed to update Machine %q" , klog .KObj (updatedMachine ))
398
403
}
399
404
machines [i ] = updatedMachine
405
+
406
+ infraMachine , err := external .Get (ctx , r .Client , & updatedMachine .Spec .InfrastructureRef , updatedMachine .Namespace )
407
+ if err != nil {
408
+ return errors .Wrapf (err , "failed to get InfrastructureMachine %s" ,
409
+ klog .KRef (updatedMachine .Spec .InfrastructureRef .Namespace , updatedMachine .Spec .InfrastructureRef .Name ))
410
+ }
411
+ // Cleanup managed fields of all InfrastructureMachines to drop ownership of labels and annotations
412
+ // from "manager". We do this so that InfrastructureMachines that are created using the Create method
413
+ // can also work with SSA. Otherwise, labels and annotations would be co-owned by our "old" "manager"
414
+ // and "capi-machineset" and then we would not be able to e.g. drop labels and annotations.
415
+ labelsAndAnnotationsManagedFieldPaths := []contract.Path {
416
+ {"f:metadata" , "f:annotations" },
417
+ {"f:metadata" , "f:labels" },
418
+ }
419
+ if err := ssa .DropManagedFields (ctx , r .Client , infraMachine , machineSetManagerName , labelsAndAnnotationsManagedFieldPaths ); err != nil {
420
+ return errors .Wrapf (err , "failed to update machine: failed to adjust the managedFields of the InfrastructureMachine %s" , klog .KObj (infraMachine ))
421
+ }
422
+ // Update in-place mutating fields on InfrastructureMachine.
423
+ if err := r .updateExternalObject (ctx , infraMachine , machineSet ); err != nil {
424
+ return errors .Wrapf (err , "failed to update InfrastructureMachine %s" , klog .KObj (infraMachine ))
425
+ }
426
+
427
+ if updatedMachine .Spec .Bootstrap .ConfigRef != nil {
428
+ bootstrapConfig , err := external .Get (ctx , r .Client , updatedMachine .Spec .Bootstrap .ConfigRef , updatedMachine .Namespace )
429
+ if err != nil {
430
+ return errors .Wrapf (err , "failed to get BootstrapConfig %s" ,
431
+ klog .KRef (updatedMachine .Spec .Bootstrap .ConfigRef .Namespace , updatedMachine .Spec .Bootstrap .ConfigRef .Name ))
432
+ }
433
+ // Cleanup managed fields of all BootstrapConfigs to drop ownership of labels and annotations
434
+ // from "manager". We do this so that BootstrapConfigs that are created using the Create method
435
+ // can also work with SSA. Otherwise, labels and annotations would be co-owned by our "old" "manager"
436
+ // and "capi-machineset" and then we would not be able to e.g. drop labels and annotations.
437
+ if err := ssa .DropManagedFields (ctx , r .Client , bootstrapConfig , machineSetManagerName , labelsAndAnnotationsManagedFieldPaths ); err != nil {
438
+ return errors .Wrapf (err , "failed to update machine: failed to adjust the managedFields of the BootstrapConfig %s" , klog .KObj (bootstrapConfig ))
439
+ }
440
+ // Update in-place mutating fields on BootstrapConfig.
441
+ if err := r .updateExternalObject (ctx , bootstrapConfig , machineSet ); err != nil {
442
+ return errors .Wrapf (err , "failed to update BootstrapConfig %s" , klog .KObj (bootstrapConfig ))
443
+ }
444
+ }
400
445
}
401
446
return nil
402
447
}
@@ -604,34 +649,72 @@ func (r *Reconciler) computeDesiredMachine(machineSet *clusterv1.MachineSet, exi
604
649
// When we update an existing Machine will we update the fields on the existing Machine (in-place mutate).
605
650
606
651
// Set Labels
652
+ desiredMachine .Labels = machineLabelsFromMachineSet (machineSet )
653
+
654
+ // Set Annotations
655
+ desiredMachine .Annotations = machineAnnotationsFromMachineSet (machineSet )
656
+
657
+ // Set all other in-place mutable fields.
658
+ desiredMachine .Spec .NodeDrainTimeout = machineSet .Spec .Template .Spec .NodeDrainTimeout
659
+ desiredMachine .Spec .NodeDeletionTimeout = machineSet .Spec .Template .Spec .NodeDeletionTimeout
660
+ desiredMachine .Spec .NodeVolumeDetachTimeout = machineSet .Spec .Template .Spec .NodeVolumeDetachTimeout
661
+
662
+ return desiredMachine
663
+ }
664
+
665
+ // updateExternalObject updates the external object passed in with the
666
+ // updated labels and annotations from the MachineSet.
667
+ func (r * Reconciler ) updateExternalObject (ctx context.Context , obj client.Object , machineSet * clusterv1.MachineSet ) error {
668
+ updatedObject := & unstructured.Unstructured {}
669
+ updatedObject .SetGroupVersionKind (obj .GetObjectKind ().GroupVersionKind ())
670
+ updatedObject .SetNamespace (obj .GetNamespace ())
671
+ updatedObject .SetName (obj .GetName ())
672
+ // Set the UID to ensure that Server-Side-Apply only performs an update
673
+ // and does not perform an accidental create.
674
+ updatedObject .SetUID (obj .GetUID ())
675
+
676
+ updatedObject .SetLabels (machineLabelsFromMachineSet (machineSet ))
677
+ updatedObject .SetAnnotations (machineAnnotationsFromMachineSet (machineSet ))
678
+
679
+ patchOptions := []client.PatchOption {
680
+ client .ForceOwnership ,
681
+ client .FieldOwner (machineSetManagerName ),
682
+ }
683
+ if err := r .Client .Patch (ctx , updatedObject , client .Apply , patchOptions ... ); err != nil {
684
+ return errors .Wrapf (err , "failed to update %s" , klog .KObj (obj ))
685
+ }
686
+ return nil
687
+ }
688
+
689
+ // machineLabelsFromMachineSet computes the labels the Machine created from this MachineSet should have.
690
+ func machineLabelsFromMachineSet (machineSet * clusterv1.MachineSet ) map [string ]string {
691
+ machineLabels := map [string ]string {}
607
692
// Note: We can't just set `machineSet.Spec.Template.Labels` directly and thus "share" the labels
608
693
// map between Machine and machineSet.Spec.Template.Labels. This would mean that adding the
609
694
// MachineSetNameLabel and MachineDeploymentNameLabel later on the Machine would also add the labels
610
695
// to machineSet.Spec.Template.Labels and thus modify the labels of the MachineSet.
611
696
for k , v := range machineSet .Spec .Template .Labels {
612
- desiredMachine . Labels [k ] = v
697
+ machineLabels [k ] = v
613
698
}
614
699
// Always set the MachineSetNameLabel.
615
700
// Note: If a client tries to create a MachineSet without a selector, the MachineSet webhook
616
701
// will add this label automatically. But we want this label to always be present even if the MachineSet
617
702
// has a selector which doesn't include it. Therefore, we have to set it here explicitly.
618
- desiredMachine . Labels [clusterv1 .MachineSetNameLabel ] = capilabels .MustFormatValue (machineSet .Name )
703
+ machineLabels [clusterv1 .MachineSetNameLabel ] = capilabels .MustFormatValue (machineSet .Name )
619
704
// Propagate the MachineDeploymentNameLabel from MachineSet to Machine if it exists.
620
705
if mdName , ok := machineSet .Labels [clusterv1 .MachineDeploymentNameLabel ]; ok {
621
- desiredMachine . Labels [clusterv1 .MachineDeploymentNameLabel ] = mdName
706
+ machineLabels [clusterv1 .MachineDeploymentNameLabel ] = mdName
622
707
}
708
+ return machineLabels
709
+ }
623
710
624
- // Set Annotations
711
+ // machineAnnotationsFromMachineSet computes the annotations the Machine created from this MachineSet should have.
712
+ func machineAnnotationsFromMachineSet (machineSet * clusterv1.MachineSet ) map [string ]string {
713
+ annotations := map [string ]string {}
625
714
for k , v := range machineSet .Spec .Template .Annotations {
626
- desiredMachine . Annotations [k ] = v
715
+ annotations [k ] = v
627
716
}
628
-
629
- // Set all other in-place mutable fields.
630
- desiredMachine .Spec .NodeDrainTimeout = machineSet .Spec .Template .Spec .NodeDrainTimeout
631
- desiredMachine .Spec .NodeDeletionTimeout = machineSet .Spec .Template .Spec .NodeDeletionTimeout
632
- desiredMachine .Spec .NodeVolumeDetachTimeout = machineSet .Spec .Template .Spec .NodeVolumeDetachTimeout
633
-
634
- return desiredMachine
717
+ return annotations
635
718
}
636
719
637
720
// shouldExcludeMachine returns true if the machine should be filtered out, false otherwise.
0 commit comments