Skip to content

Commit 12c2572

Browse files
authored
Merge pull request kubernetes-sigs#9619 from Jont828/mpm-owner-fix
🐛 Fix bug where MachinePool Machine ownerRefs weren't updating
2 parents 34a990c + 43a5091 commit 12c2572

File tree

6 files changed

+57
-75
lines changed

6 files changed

+57
-75
lines changed

exp/internal/controllers/machinepool_controller.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import (
4141
"sigs.k8s.io/cluster-api/controllers/external"
4242
"sigs.k8s.io/cluster-api/controllers/remote"
4343
expv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1"
44+
"sigs.k8s.io/cluster-api/internal/util/ssa"
4445
"sigs.k8s.io/cluster-api/util"
4546
"sigs.k8s.io/cluster-api/util/annotations"
4647
"sigs.k8s.io/cluster-api/util/conditions"
@@ -69,6 +70,7 @@ type MachinePoolReconciler struct {
6970
WatchFilterValue string
7071

7172
controller controller.Controller
73+
ssaCache ssa.Cache
7274
recorder record.EventRecorder
7375
externalTracker external.ObjectTracker
7476
}
@@ -105,6 +107,8 @@ func (r *MachinePoolReconciler) SetupWithManager(ctx context.Context, mgr ctrl.M
105107
Controller: c,
106108
Cache: mgr.GetCache(),
107109
}
110+
r.ssaCache = ssa.NewCache()
111+
108112
return nil
109113
}
110114

exp/internal/controllers/machinepool_controller_phases.go

Lines changed: 35 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ import (
2828
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2929
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
3030
kerrors "k8s.io/apimachinery/pkg/util/errors"
31-
"k8s.io/apimachinery/pkg/util/sets"
3231
"k8s.io/apimachinery/pkg/util/wait"
3332
"k8s.io/apiserver/pkg/storage/names"
3433
"k8s.io/klog/v2"
@@ -43,6 +42,7 @@ import (
4342
capierrors "sigs.k8s.io/cluster-api/errors"
4443
expv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1"
4544
utilexp "sigs.k8s.io/cluster-api/exp/util"
45+
"sigs.k8s.io/cluster-api/internal/util/ssa"
4646
"sigs.k8s.io/cluster-api/util"
4747
"sigs.k8s.io/cluster-api/util/annotations"
4848
"sigs.k8s.io/cluster-api/util/conditions"
@@ -376,100 +376,63 @@ func (r *MachinePoolReconciler) reconcileMachines(ctx context.Context, mp *expv1
376376
return err
377377
}
378378

379-
updatedMachines, err := r.createMachinesIfNotExists(ctx, mp, machineList.Items, infraMachineList.Items)
380-
if err != nil {
381-
return errors.Wrapf(err, "failed to create machines for MachinePool %q in namespace %q", mp.Name, mp.Namespace)
382-
}
383-
384-
if err := r.ensureInfraMachineOnwerRefs(ctx, updatedMachines, infraMachineList.Items); err != nil {
379+
if err := r.createOrUpdateMachines(ctx, mp, machineList.Items, infraMachineList.Items); err != nil {
385380
return errors.Wrapf(err, "failed to create machines for MachinePool %q in namespace %q", mp.Name, mp.Namespace)
386381
}
387382

388383
return nil
389384
}
390385

391-
// createMachinesIfNotExists creates a MachinePool Machine for each infraMachine if it doesn't already exist and sets the owner reference and infraRef.
392-
func (r *MachinePoolReconciler) createMachinesIfNotExists(ctx context.Context, mp *expv1.MachinePool, machines []clusterv1.Machine, infraMachines []unstructured.Unstructured) ([]clusterv1.Machine, error) {
386+
// createOrUpdateMachines creates a MachinePool Machine for each infraMachine if it doesn't already exist and sets the owner reference and infraRef.
387+
func (r *MachinePoolReconciler) createOrUpdateMachines(ctx context.Context, mp *expv1.MachinePool, machines []clusterv1.Machine, infraMachines []unstructured.Unstructured) error {
393388
log := ctrl.LoggerFrom(ctx)
394389

395390
// Construct a set of names of infraMachines that already have a Machine.
396-
infraRefNames := sets.Set[string]{}
391+
infraMachineToMachine := map[string]clusterv1.Machine{}
397392
for _, machine := range machines {
398393
infraRef := machine.Spec.InfrastructureRef
399-
infraRefNames.Insert(infraRef.Name)
394+
infraMachineToMachine[infraRef.Name] = machine
400395
}
401396

402397
createdMachines := []clusterv1.Machine{}
403398
var errs []error
404399
for i := range infraMachines {
405400
infraMachine := &infraMachines[i]
406-
// If infraMachine already has a Machine, skip it.
407-
if infraRefNames.Has(infraMachine.GetName()) {
408-
log.V(4).Info("Machine already exists for infraMachine", "infraMachine", klog.KObj(infraMachine))
409-
continue
410-
}
411-
// Otherwise create a new Machine for the infraMachine.
412-
log.Info("Creating new Machine for infraMachine", infraMachine.GroupVersionKind().Kind, klog.KObj(infraMachine))
413-
machine := getNewMachine(mp, infraMachine)
414-
if err := r.Client.Create(ctx, machine); err != nil {
415-
errs = append(errs, errors.Wrapf(err, "failed to create new Machine for infraMachine %q in namespace %q", infraMachine.GetName(), infraMachine.GetNamespace()))
416-
continue
401+
// If infraMachine already has a Machine, update it if needed.
402+
if existingMachine, ok := infraMachineToMachine[infraMachine.GetName()]; ok {
403+
log.V(2).Info("Patching existing Machine for infraMachine", "infraMachine", klog.KObj(infraMachine), "machine", klog.KObj(&existingMachine))
404+
405+
desiredMachine := computeDesiredMachine(mp, infraMachine, &existingMachine)
406+
if err := ssa.Patch(ctx, r.Client, MachinePoolControllerName, desiredMachine, ssa.WithCachingProxy{Cache: r.ssaCache, Original: &existingMachine}); err != nil {
407+
log.Error(err, "failed to update Machine", "Machine", klog.KObj(desiredMachine))
408+
errs = append(errs, errors.Wrapf(err, "failed to update Machine %q", klog.KObj(desiredMachine)))
409+
}
410+
} else {
411+
// Otherwise create a new Machine for the infraMachine.
412+
log.Info("Creating new Machine for infraMachine", "infraMachine", klog.KObj(infraMachine))
413+
machine := computeDesiredMachine(mp, infraMachine, nil)
414+
415+
if err := r.Client.Create(ctx, machine); err != nil {
416+
errs = append(errs, errors.Wrapf(err, "failed to create new Machine for infraMachine %q in namespace %q", infraMachine.GetName(), infraMachine.GetNamespace()))
417+
continue
418+
}
419+
420+
createdMachines = append(createdMachines, *machine)
417421
}
418-
createdMachines = append(createdMachines, *machine)
419422
}
420-
machines = append(machines, createdMachines...)
421423
if err := r.waitForMachineCreation(ctx, createdMachines); err != nil {
422424
errs = append(errs, errors.Wrapf(err, "failed to wait for machines to be created"))
423425
}
424426
if len(errs) > 0 {
425-
return nil, kerrors.NewAggregate(errs)
426-
}
427-
428-
return machines, nil
429-
}
430-
431-
// ensureInfraMachineOnwerRefs sets the ownerReferences on the each infraMachine to its associated MachinePool Machine if it hasn't already been done.
432-
func (r *MachinePoolReconciler) ensureInfraMachineOnwerRefs(ctx context.Context, updatedMachines []clusterv1.Machine, infraMachines []unstructured.Unstructured) error {
433-
log := ctrl.LoggerFrom(ctx)
434-
infraMachineNameToMachine := make(map[string]clusterv1.Machine)
435-
for _, machine := range updatedMachines {
436-
infraRef := machine.Spec.InfrastructureRef
437-
infraMachineNameToMachine[infraRef.Name] = machine
438-
}
439-
440-
for i := range infraMachines {
441-
infraMachine := &infraMachines[i]
442-
ownerRefs := infraMachine.GetOwnerReferences()
443-
444-
machine, ok := infraMachineNameToMachine[infraMachine.GetName()]
445-
if !ok {
446-
return errors.Errorf("failed to patch ownerRef for infraMachine %q because no Machine has an infraRef pointing to it", infraMachine.GetName())
447-
}
448-
machineRef := metav1.NewControllerRef(&machine, machine.GroupVersionKind())
449-
if !util.HasOwnerRef(ownerRefs, *machineRef) {
450-
log.V(2).Info("Setting ownerRef on infraMachine", "infraMachine", infraMachine.GetName(), "namespace", infraMachine.GetNamespace(), "machine", machine.GetName())
451-
452-
patchHelper, err := patch.NewHelper(infraMachine, r.Client)
453-
if err != nil {
454-
return errors.Wrapf(err, "failed to create patch helper for %s", klog.KObj(infraMachine))
455-
}
456-
457-
ownerRefs = util.EnsureOwnerRef(ownerRefs, *machineRef)
458-
infraMachine.SetOwnerReferences(ownerRefs)
459-
460-
if err := patchHelper.Patch(ctx, infraMachine); err != nil {
461-
return errors.Wrapf(err, "failed to patch %s", klog.KObj(infraMachine))
462-
}
463-
464-
log.V(4).Info("Successfully set ownerRef on infraMachine", "infraMachine", infraMachine.GetName(), "namespace", infraMachine.GetNamespace(), "machine", machine.GetName())
465-
}
427+
return kerrors.NewAggregate(errs)
466428
}
467429

468430
return nil
469431
}
470432

471-
// getNewMachine creates a new Machine object.
472-
func getNewMachine(mp *expv1.MachinePool, infraMachine *unstructured.Unstructured) *clusterv1.Machine {
433+
// computeDesiredMachine constructs the desired Machine for an infraMachine.
434+
// If the Machine exists, it ensures the Machine always owned by the MachinePool.
435+
func computeDesiredMachine(mp *expv1.MachinePool, infraMachine *unstructured.Unstructured, existingMachine *clusterv1.Machine) *clusterv1.Machine {
473436
infraRef := corev1.ObjectReference{
474437
APIVersion: infraMachine.GetAPIVersion(),
475438
Kind: infraMachine.GetKind(),
@@ -492,6 +455,11 @@ func getNewMachine(mp *expv1.MachinePool, infraMachine *unstructured.Unstructure
492455
},
493456
}
494457

458+
if existingMachine != nil {
459+
machine.SetName(existingMachine.Name)
460+
machine.SetUID(existingMachine.UID)
461+
}
462+
495463
for k, v := range mp.Spec.Template.Annotations {
496464
machine.Annotations[k] = v
497465
}

exp/internal/controllers/machinepool_controller_phases_test.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ import (
3939
"sigs.k8s.io/cluster-api/controllers/remote"
4040
expv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1"
4141
"sigs.k8s.io/cluster-api/internal/test/builder"
42-
"sigs.k8s.io/cluster-api/util"
42+
"sigs.k8s.io/cluster-api/internal/util/ssa"
4343
"sigs.k8s.io/cluster-api/util/kubeconfig"
4444
"sigs.k8s.io/cluster-api/util/labels/format"
4545
)
@@ -1312,7 +1312,8 @@ func TestReconcileMachinePoolMachines(t *testing.T) {
13121312
}
13131313

13141314
r := &MachinePoolReconciler{
1315-
Client: fake.NewClientBuilder().WithObjects(objs...).Build(),
1315+
Client: fake.NewClientBuilder().WithObjects(objs...).Build(),
1316+
ssaCache: ssa.NewCache(),
13161317
}
13171318

13181319
err := r.reconcileMachines(ctx, tc.machinepool, infraConfig)
@@ -1335,10 +1336,8 @@ func TestReconcileMachinePoolMachines(t *testing.T) {
13351336
g.Expect(machineList.Items).To(HaveLen(len(tc.infraMachines)))
13361337
for i := range machineList.Items {
13371338
machine := &machineList.Items[i]
1338-
infraMachine, err := external.Get(ctx, r.Client, &machine.Spec.InfrastructureRef, machine.Namespace)
1339+
_, err := external.Get(ctx, r.Client, &machine.Spec.InfrastructureRef, machine.Namespace)
13391340
g.Expect(err).ToNot(HaveOccurred())
1340-
1341-
g.Expect(util.IsControlledBy(infraMachine, machine)).To(BeTrue())
13421341
}
13431342
} else {
13441343
g.Expect(machineList.Items).To(BeEmpty())

internal/controllers/machine/machine_controller.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -797,13 +797,18 @@ func (r *Reconciler) shouldAdopt(m *clusterv1.Machine) bool {
797797
}
798798

799799
// Note: following checks are required because after restore from a backup both the Machine controller and the
800-
// MachineSet/ControlPlane controller are racing to adopt Machines, see https://github.com/kubernetes-sigs/cluster-api/issues/7529
800+
// MachineSet, MachinePool, or ControlPlane controller are racing to adopt Machines, see https://github.com/kubernetes-sigs/cluster-api/issues/7529
801801

802802
// If the Machine is originated by a MachineSet, it should not be adopted directly by the Cluster as a stand-alone Machine.
803803
if _, ok := m.Labels[clusterv1.MachineSetNameLabel]; ok {
804804
return false
805805
}
806806

807+
// If the Machine is originated by a MachinePool object, it should not be adopted directly by the Cluster as a stand-alone Machine.
808+
if _, ok := m.Labels[clusterv1.MachinePoolNameLabel]; ok {
809+
return false
810+
}
811+
807812
// If the Machine is originated by a ControlPlane object, it should not be adopted directly by the Cluster as a stand-alone Machine.
808813
if _, ok := m.Labels[clusterv1.MachineControlPlaneNameLabel]; ok {
809814
return false

internal/webhooks/machine.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,12 @@ func (webhook *Machine) validate(oldM, newM *clusterv1.Machine) error {
173173
}
174174

175175
func isMachinePoolMachine(m *clusterv1.Machine) bool {
176+
if m.Labels != nil {
177+
if _, ok := m.Labels[clusterv1.MachinePoolNameLabel]; ok {
178+
return true
179+
}
180+
}
181+
176182
for _, owner := range m.OwnerReferences {
177183
if owner.Kind == "MachinePool" {
178184
return true

test/framework/ownerreference_helpers.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,8 +171,8 @@ var CoreOwnerReferenceAssertion = map[string]func([]metav1.OwnerReference) error
171171
return HasExactOwners(owners, machineDeploymentController)
172172
},
173173
machineKind: func(owners []metav1.OwnerReference) error {
174-
// Machines must be owned and controlled by a MachineSet or a KubeadmControlPlane, depending on if this Machine is part of a ControlPlane or not.
175-
return HasOneOfExactOwners(owners, []metav1.OwnerReference{machineSetController}, []metav1.OwnerReference{kubeadmControlPlaneController})
174+
// Machines must be owned and controlled by a MachineSet, MachinePool, or a KubeadmControlPlane, depending on if this Machine is part of a Machine Deployment, MachinePool, or ControlPlane.
175+
return HasOneOfExactOwners(owners, []metav1.OwnerReference{machineSetController}, []metav1.OwnerReference{machinePoolController}, []metav1.OwnerReference{kubeadmControlPlaneController})
176176
},
177177
machineHealthCheckKind: func(owners []metav1.OwnerReference) error {
178178
// MachineHealthChecks must be owned by the Cluster.

0 commit comments

Comments
 (0)