Skip to content

Commit 2aa9c9d

Browse files
authored
Merge pull request #8060 from ykakarap/node-label_md-ms-in-place_infra-bootstrap
⚠️ in-place propagation from MS to InfraMachine and BootstrapConfig
2 parents 83e7476 + f405b5a commit 2aa9c9d

File tree

12 files changed

+759
-244
lines changed

12 files changed

+759
-244
lines changed

internal/controllers/machineset/machineset_controller.go

Lines changed: 100 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
corev1 "k8s.io/api/core/v1"
2727
apierrors "k8s.io/apimachinery/pkg/api/errors"
2828
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2930
"k8s.io/apimachinery/pkg/labels"
3031
kerrors "k8s.io/apimachinery/pkg/util/errors"
3132
"k8s.io/apiserver/pkg/storage/names"
@@ -41,6 +42,7 @@ import (
4142
"sigs.k8s.io/cluster-api/controllers/external"
4243
"sigs.k8s.io/cluster-api/controllers/noderefutil"
4344
"sigs.k8s.io/cluster-api/controllers/remote"
45+
"sigs.k8s.io/cluster-api/internal/contract"
4446
"sigs.k8s.io/cluster-api/internal/controllers/machine"
4547
capilabels "sigs.k8s.io/cluster-api/internal/labels"
4648
"sigs.k8s.io/cluster-api/internal/util/ssa"
@@ -364,11 +366,14 @@ func (r *Reconciler) reconcile(ctx context.Context, cluster *clusterv1.Cluster,
364366
return ctrl.Result{}, nil
365367
}
366368

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.
372377
func (r *Reconciler) syncMachines(ctx context.Context, machineSet *clusterv1.MachineSet, machines []*clusterv1.Machine) error {
373378
log := ctrl.LoggerFrom(ctx)
374379
for i := range machines {
@@ -397,6 +402,46 @@ func (r *Reconciler) syncMachines(ctx context.Context, machineSet *clusterv1.Mac
397402
return errors.Wrapf(err, "failed to update Machine %q", klog.KObj(updatedMachine))
398403
}
399404
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+
}
400445
}
401446
return nil
402447
}
@@ -604,34 +649,72 @@ func (r *Reconciler) computeDesiredMachine(machineSet *clusterv1.MachineSet, exi
604649
// When we update an existing Machine will we update the fields on the existing Machine (in-place mutate).
605650

606651
// 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{}
607692
// Note: We can't just set `machineSet.Spec.Template.Labels` directly and thus "share" the labels
608693
// map between Machine and machineSet.Spec.Template.Labels. This would mean that adding the
609694
// MachineSetNameLabel and MachineDeploymentNameLabel later on the Machine would also add the labels
610695
// to machineSet.Spec.Template.Labels and thus modify the labels of the MachineSet.
611696
for k, v := range machineSet.Spec.Template.Labels {
612-
desiredMachine.Labels[k] = v
697+
machineLabels[k] = v
613698
}
614699
// Always set the MachineSetNameLabel.
615700
// Note: If a client tries to create a MachineSet without a selector, the MachineSet webhook
616701
// will add this label automatically. But we want this label to always be present even if the MachineSet
617702
// 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)
619704
// Propagate the MachineDeploymentNameLabel from MachineSet to Machine if it exists.
620705
if mdName, ok := machineSet.Labels[clusterv1.MachineDeploymentNameLabel]; ok {
621-
desiredMachine.Labels[clusterv1.MachineDeploymentNameLabel] = mdName
706+
machineLabels[clusterv1.MachineDeploymentNameLabel] = mdName
622707
}
708+
return machineLabels
709+
}
623710

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{}
625714
for k, v := range machineSet.Spec.Template.Annotations {
626-
desiredMachine.Annotations[k] = v
715+
annotations[k] = v
627716
}
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
635718
}
636719

637720
// shouldExcludeMachine returns true if the machine should be filtered out, false otherwise.

0 commit comments

Comments
 (0)