@@ -44,7 +44,8 @@ import (
44
44
"sigs.k8s.io/cluster-api/controlplane/kubeadm/internal"
45
45
expv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1"
46
46
"sigs.k8s.io/cluster-api/feature"
47
- "sigs.k8s.io/cluster-api/internal/labels"
47
+ "sigs.k8s.io/cluster-api/internal/contract"
48
+ "sigs.k8s.io/cluster-api/internal/util/ssa"
48
49
"sigs.k8s.io/cluster-api/util"
49
50
"sigs.k8s.io/cluster-api/util/annotations"
50
51
"sigs.k8s.io/cluster-api/util/collections"
@@ -55,6 +56,8 @@ import (
55
56
"sigs.k8s.io/cluster-api/util/version"
56
57
)
57
58
59
+ const kcpManagerName = "capi-kubeadmcontrolplane"
60
+
58
61
// +kubebuilder:rbac:groups=core,resources=events,verbs=get;list;watch;create;patch
59
62
// +kubebuilder:rbac:groups=core,resources=secrets,verbs=get;list;watch;create;update;patch
60
63
// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io;bootstrap.cluster.x-k8s.io;controlplane.cluster.x-k8s.io,resources=*,verbs=get;list;watch;create;update;patch;delete
@@ -77,6 +80,12 @@ type KubeadmControlPlaneReconciler struct {
77
80
78
81
managementCluster internal.ManagementCluster
79
82
managementClusterUncached internal.ManagementCluster
83
+
84
+ // disableInPlacePropagation should only be used for tests. This is used to skip
85
+ // some parts of the controller that need SSA as the current test setup does not
86
+ // support SSA. This flag should be dropped after all affected tests are migrated
87
+ // to envtest.
88
+ disableInPlacePropagation bool
80
89
}
81
90
82
91
func (r * KubeadmControlPlaneReconciler ) SetupWithManager (ctx context.Context , mgr ctrl.Manager , options controller.Options ) error {
@@ -331,25 +340,16 @@ func (r *KubeadmControlPlaneReconciler) reconcile(ctx context.Context, cluster *
331
340
return ctrl.Result {}, err
332
341
}
333
342
343
+ if ! r .disableInPlacePropagation {
344
+ if err := r .syncMachines (ctx , controlPlane ); err != nil {
345
+ return ctrl.Result {}, errors .Wrap (err , "failed to sync Machines" )
346
+ }
347
+ }
348
+
334
349
// Aggregate the operational state of all the machines; while aggregating we are adding the
335
350
// source ref (reason@machine/name) so the problem can be easily tracked down to its source machine.
336
351
conditions .SetAggregate (controlPlane .KCP , controlplanev1 .MachinesReadyCondition , ownedMachines .ConditionGetters (), conditions .AddSourceRef (), conditions .WithStepCounterIf (false ))
337
352
338
- // Ensure all required labels exist on the controlled Machines.
339
- // This logic is needed to add the `cluster.x-k8s.io/control-plane-name` label to Machines
340
- // which were created before the `cluster.x-k8s.io/control-plane-name` label was introduced
341
- // or if a user manually removed the label.
342
- // NOTE: Changes will be applied to the Machines in reconcileControlPlaneConditions.
343
- // NOTE: cluster.x-k8s.io/control-plane is already set at this stage (it is used when reading controlPlane.Machines).
344
- for i := range controlPlane .Machines {
345
- machine := controlPlane .Machines [i ]
346
- // Note: MustEqualValue and MustFormatValue is used here as the label value can be a hash if the control plane
347
- // name is longer than 63 characters.
348
- if value , ok := machine .Labels [clusterv1 .MachineControlPlaneNameLabel ]; ! ok || ! labels .MustEqualValue (kcp .Name , value ) {
349
- machine .Labels [clusterv1 .MachineControlPlaneNameLabel ] = labels .MustFormatValue (kcp .Name )
350
- }
351
- }
352
-
353
353
// Updates conditions reporting the status of static pods and the status of the etcd cluster.
354
354
// NOTE: Conditions reporting KCP operation progress like e.g. Resized or SpecUpToDate are inlined with the rest of the execution.
355
355
if result , err := r .reconcileControlPlaneConditions (ctx , controlPlane ); err != nil || ! result .IsZero () {
@@ -535,6 +535,86 @@ func (r *KubeadmControlPlaneReconciler) ClusterToKubeadmControlPlane(o client.Ob
535
535
return nil
536
536
}
537
537
538
+ // syncMachines updates Machines, InfrastructureMachines and KubeadmConfigs to propagate in-place mutable fields from KCP.
539
+ // Note: It also cleans up managed fields of all Machines so that Machines that were
540
+ // created/patched before (< v1.4.0) the controller adopted Server-Side-Apply (SSA) can also work with SSA.
541
+ // Note: For InfrastructureMachines and KubeadmConfigs it also drops ownership of "metadata.labels" and
542
+ // "metadata.annotations" from "manager" so that "capi-kubeadmcontrolplane" can own these fields and can work with SSA.
543
+ // Otherwise, fields would be co-owned by our "old" "manager" and "capi-kubeadmcontrolplane" and then we would not be
544
+ // able to e.g. drop labels and annotations.
545
+ func (r * KubeadmControlPlaneReconciler ) syncMachines (ctx context.Context , controlPlane * internal.ControlPlane ) error {
546
+ patchHelpers := map [string ]* patch.Helper {}
547
+ for machineName := range controlPlane .Machines {
548
+ m := controlPlane .Machines [machineName ]
549
+ // If the machine is already being deleted, we don't need to update it.
550
+ if ! m .DeletionTimestamp .IsZero () {
551
+ continue
552
+ }
553
+
554
+ // Cleanup managed fields of all Machines.
555
+ // We do this so that Machines that were created/patched before the controller adopted Server-Side-Apply (SSA)
556
+ // (< v1.4.0) can also work with SSA. Otherwise, fields would be co-owned by our "old" "manager" and
557
+ // "capi-kubeadmcontrolplane" and then we would not be able to e.g. drop labels and annotations.
558
+ if err := ssa .CleanUpManagedFieldsForSSAAdoption (ctx , r .Client , m , kcpManagerName ); err != nil {
559
+ return errors .Wrapf (err , "failed to update Machine: failed to adjust the managedFields of the Machine %s" , klog .KObj (m ))
560
+ }
561
+ // Update Machine to propagate in-place mutable fields from KCP.
562
+ updatedMachine , err := r .updateMachine (ctx , m , controlPlane .KCP , controlPlane .Cluster )
563
+ if err != nil {
564
+ return errors .Wrapf (err , "failed to update Machine: %s" , klog .KObj (m ))
565
+ }
566
+ controlPlane .Machines [machineName ] = updatedMachine
567
+ // Since the machine is updated, re-create the patch helper so that any subsequent
568
+ // Patch calls use the correct base machine object to calculate the diffs.
569
+ // Example: reconcileControlPlaneConditions patches the machine objects in a subsequent call
570
+ // and, it should use the updated machine to calculate the diff.
571
+ // Note: If the patchHelpers are not re-computed based on the new updated machines, subsequent
572
+ // Patch calls will fail because the patch will be calculated based on an outdated machine and will error
573
+ // because of outdated resourceVersion.
574
+ // TODO: This should be cleaned-up to have a more streamline way of constructing and using patchHelpers.
575
+ patchHelper , err := patch .NewHelper (updatedMachine , r .Client )
576
+ if err != nil {
577
+ return errors .Wrapf (err , "failed to create patch helper for Machine %s" , klog .KObj (updatedMachine ))
578
+ }
579
+ patchHelpers [machineName ] = patchHelper
580
+
581
+ labelsAndAnnotationsManagedFieldPaths := []contract.Path {
582
+ {"f:metadata" , "f:annotations" },
583
+ {"f:metadata" , "f:labels" },
584
+ }
585
+ infraMachine := controlPlane .InfraResources [machineName ]
586
+ // Cleanup managed fields of all InfrastructureMachines to drop ownership of labels and annotations
587
+ // from "manager". We do this so that InfrastructureMachines that are created using the Create method
588
+ // can also work with SSA. Otherwise, labels and annotations would be co-owned by our "old" "manager"
589
+ // and "capi-kubeadmcontrolplane" and then we would not be able to e.g. drop labels and annotations.
590
+ if err := ssa .DropManagedFields (ctx , r .Client , infraMachine , kcpManagerName , labelsAndAnnotationsManagedFieldPaths ); err != nil {
591
+ return errors .Wrapf (err , "failed to clean up managedFields of InfrastructureMachine %s" , klog .KObj (infraMachine ))
592
+ }
593
+ // Update in-place mutating fields on InfrastructureMachine.
594
+ if err := r .updateExternalObject (ctx , infraMachine , controlPlane .KCP , controlPlane .Cluster ); err != nil {
595
+ return errors .Wrapf (err , "failed to update InfrastructureMachine %s" , klog .KObj (infraMachine ))
596
+ }
597
+
598
+ kubeadmConfig := controlPlane .KubeadmConfigs [machineName ]
599
+ // Note: Set the GroupVersionKind because updateExternalObject depends on it.
600
+ kubeadmConfig .SetGroupVersionKind (m .Spec .Bootstrap .ConfigRef .GroupVersionKind ())
601
+ // Cleanup managed fields of all KubeadmConfigs to drop ownership of labels and annotations
602
+ // from "manager". We do this so that KubeadmConfigs that are created using the Create method
603
+ // can also work with SSA. Otherwise, labels and annotations would be co-owned by our "old" "manager"
604
+ // and "capi-kubeadmcontrolplane" and then we would not be able to e.g. drop labels and annotations.
605
+ if err := ssa .DropManagedFields (ctx , r .Client , kubeadmConfig , kcpManagerName , labelsAndAnnotationsManagedFieldPaths ); err != nil {
606
+ return errors .Wrapf (err , "failed to clean up managedFields of KubeadmConfig %s" , klog .KObj (kubeadmConfig ))
607
+ }
608
+ // Update in-place mutating fields on BootstrapConfig.
609
+ if err := r .updateExternalObject (ctx , kubeadmConfig , controlPlane .KCP , controlPlane .Cluster ); err != nil {
610
+ return errors .Wrapf (err , "failed to update KubeadmConfig %s" , klog .KObj (kubeadmConfig ))
611
+ }
612
+ }
613
+ // Update the patch helpers.
614
+ controlPlane .SetPatchHelpers (patchHelpers )
615
+ return nil
616
+ }
617
+
538
618
// reconcileControlPlaneConditions is responsible of reconciling conditions reporting the status of static pods and
539
619
// the status of the etcd cluster.
540
620
func (r * KubeadmControlPlaneReconciler ) reconcileControlPlaneConditions (ctx context.Context , controlPlane * internal.ControlPlane ) (ctrl.Result , error ) {
0 commit comments