Skip to content

Commit 7bf5a92

Browse files
committed
feat: set new replica fields for machine pools
This updates the MachinePool reconciliation to set the values for the replica fields in the status section to be consistent with things like the MachineSets. If a infra machine pool doesn't have "machine pool machines" then we can't realistically set the values for all the fields, so in this case we set the values the same as the replicas. This change also introduces setting the "UptoDate" condition on the Machines for the first time. It currently only takes the deletion timestamp into account when determining if a machine is outdated. We can iterate on this going forward. Signed-off-by: Richard Case <[email protected]>
1 parent d43e0ac commit 7bf5a92

File tree

5 files changed

+322
-82
lines changed

5 files changed

+322
-82
lines changed

exp/internal/controllers/machinepool_controller.go

Lines changed: 152 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ import (
4949
"sigs.k8s.io/cluster-api/util/conditions"
5050
v1beta1conditions "sigs.k8s.io/cluster-api/util/conditions/deprecated/v1beta1"
5151
"sigs.k8s.io/cluster-api/util/finalizers"
52+
"sigs.k8s.io/cluster-api/util/labels/format"
5253
"sigs.k8s.io/cluster-api/util/patch"
5354
"sigs.k8s.io/cluster-api/util/paused"
5455
"sigs.k8s.io/cluster-api/util/predicates"
@@ -62,10 +63,8 @@ import (
6263
// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io;bootstrap.cluster.x-k8s.io,resources=*,verbs=get;list;watch;create;update;patch;delete
6364
// +kubebuilder:rbac:groups=cluster.x-k8s.io,resources=machinepools;machinepools/status;machinepools/finalizers,verbs=get;list;watch;create;update;patch;delete
6465

65-
var (
66-
// machinePoolKind contains the schema.GroupVersionKind for the MachinePool type.
67-
machinePoolKind = clusterv1.GroupVersion.WithKind("MachinePool")
68-
)
66+
// machinePoolKind contains the schema.GroupVersionKind for the MachinePool type.
67+
var machinePoolKind = clusterv1.GroupVersion.WithKind("MachinePool")
6968

7069
const (
7170
// MachinePoolControllerName defines the controller used when creating clients.
@@ -89,21 +88,6 @@ type MachinePoolReconciler struct {
8988
predicateLog *logr.Logger
9089
}
9190

92-
// scope holds the different objects that are read and used during the reconcile.
93-
type scope struct {
94-
// cluster is the Cluster object the Machine belongs to.
95-
// It is set at the beginning of the reconcile function.
96-
cluster *clusterv1.Cluster
97-
98-
// machinePool is the MachinePool object. It is set at the beginning
99-
// of the reconcile function.
100-
machinePool *clusterv1.MachinePool
101-
102-
// nodeRefMapResult is a map of providerIDs to Nodes that are associated with the Cluster.
103-
// It is set after reconcileInfrastructure is called.
104-
nodeRefMap map[string]*corev1.Node
105-
}
106-
10791
func (r *MachinePoolReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error {
10892
if r.Client == nil || r.APIReader == nil || r.ClusterCache == nil {
10993
return errors.New("Client, APIReader and ClusterCache must not be nil")
@@ -189,7 +173,17 @@ func (r *MachinePoolReconciler) Reconcile(ctx context.Context, req ctrl.Request)
189173
return ctrl.Result{}, err
190174
}
191175

176+
// Handle normal reconciliation loop.
177+
scope := &scope{
178+
cluster: cluster,
179+
machinePool: mp,
180+
}
181+
192182
defer func() {
183+
if err := r.updateStatus(ctx, scope); err != nil {
184+
reterr = kerrors.NewAggregate([]error{reterr, err})
185+
}
186+
193187
r.reconcilePhase(mp)
194188
// TODO(jpang): add support for metrics.
195189

@@ -223,79 +217,69 @@ func (r *MachinePoolReconciler) Reconcile(ctx context.Context, req ctrl.Request)
223217
}
224218
}()
225219

226-
// Reconcile labels.
227-
if mp.Labels == nil {
228-
mp.Labels = make(map[string]string)
220+
alwaysReconcile := []machinePoolReconcileFunc{
221+
wrapErrMachinePoolReconcileFunc(r.reconcileSetOwnerAndLabels, "failed to set MachinePool owner and labels"),
229222
}
230-
mp.Labels[clusterv1.ClusterNameLabel] = mp.Spec.ClusterName
231223

232224
// Handle deletion reconciliation loop.
233225
if !mp.DeletionTimestamp.IsZero() {
234-
return ctrl.Result{}, r.reconcileDelete(ctx, cluster, mp)
226+
reconcileDelete := append(
227+
alwaysReconcile,
228+
wrapErrMachinePoolReconcileFunc(r.reconcileDelete, "failed to reconcile delete"),
229+
)
230+
return doReconcile(ctx, scope, reconcileDelete)
235231
}
236232

237-
// Handle normal reconciliation loop.
238-
scope := &scope{
239-
cluster: cluster,
240-
machinePool: mp,
241-
}
242-
return r.reconcile(ctx, scope)
233+
reconcileNormal := append(alwaysReconcile,
234+
wrapErrMachinePoolReconcileFunc(r.reconcileBootstrap, "failed to reconcile bootstrap config"),
235+
wrapErrMachinePoolReconcileFunc(r.reconcileInfrastructure, "failed to reconcile infrastructure"),
236+
wrapErrMachinePoolReconcileFunc(r.getMachinesForMachinePool, "failed to get Machines for MachinePool"),
237+
wrapErrMachinePoolReconcileFunc(r.reconcileNodeRefs, "failed to reconcile nodeRefs"),
238+
wrapErrMachinePoolReconcileFunc(r.setMachinesUptoDate, "failed to set machines up to date"),
239+
)
240+
241+
return doReconcile(ctx, scope, reconcileNormal)
243242
}
244243

245-
func (r *MachinePoolReconciler) reconcile(ctx context.Context, s *scope) (ctrl.Result, error) {
246-
// Ensure the MachinePool is owned by the Cluster it belongs to.
244+
func (r *MachinePoolReconciler) reconcileSetOwnerAndLabels(_ context.Context, s *scope) (ctrl.Result, error) {
247245
cluster := s.cluster
248246
mp := s.machinePool
247+
248+
if mp.Labels == nil {
249+
mp.Labels = make(map[string]string)
250+
}
251+
mp.Labels[clusterv1.ClusterNameLabel] = mp.Spec.ClusterName
252+
249253
mp.SetOwnerReferences(util.EnsureOwnerRef(mp.GetOwnerReferences(), metav1.OwnerReference{
250254
APIVersion: clusterv1.GroupVersion.String(),
251255
Kind: "Cluster",
252256
Name: cluster.Name,
253257
UID: cluster.UID,
254258
}))
255259

256-
phases := []func(context.Context, *scope) (ctrl.Result, error){
257-
r.reconcileBootstrap,
258-
r.reconcileInfrastructure,
259-
r.reconcileNodeRefs,
260-
}
261-
262-
res := ctrl.Result{}
263-
errs := []error{}
264-
for _, phase := range phases {
265-
// Call the inner reconciliation methods.
266-
phaseResult, err := phase(ctx, s)
267-
if err != nil {
268-
errs = append(errs, err)
269-
}
270-
if len(errs) > 0 {
271-
continue
272-
}
273-
274-
res = util.LowestNonZeroResult(res, phaseResult)
275-
}
276-
return res, kerrors.NewAggregate(errs)
260+
return ctrl.Result{}, nil
277261
}
278262

279263
// reconcileDelete delete machinePool related resources.
280-
func (r *MachinePoolReconciler) reconcileDelete(ctx context.Context, cluster *clusterv1.Cluster, machinePool *clusterv1.MachinePool) error {
281-
if ok, err := r.reconcileDeleteExternal(ctx, machinePool); !ok || err != nil {
264+
func (r *MachinePoolReconciler) reconcileDelete(ctx context.Context, s *scope) (ctrl.Result, error) {
265+
if ok, err := r.reconcileDeleteExternal(ctx, s.machinePool); !ok || err != nil {
282266
// Return early and don't remove the finalizer if we got an error or
283267
// the external reconciliation deletion isn't ready.
284-
return err
268+
return ctrl.Result{}, fmt.Errorf("failed deleting external references: %s", err)
285269
}
286270

287271
// check nodes delete timeout passed.
288-
if !r.isMachinePoolNodeDeleteTimeoutPassed(machinePool) {
289-
if err := r.reconcileDeleteNodes(ctx, cluster, machinePool); err != nil {
272+
if !r.isMachinePoolNodeDeleteTimeoutPassed(s.machinePool) {
273+
if err := r.reconcileDeleteNodes(ctx, s.cluster, s.machinePool); err != nil {
290274
// Return early and don't remove the finalizer if we got an error.
291-
return err
275+
return ctrl.Result{}, fmt.Errorf("failed deleting nodes: %w", err)
292276
}
293277
} else {
294278
ctrl.LoggerFrom(ctx).Info("NodeDeleteTimeout passed, skipping Nodes deletion")
295279
}
296280

297-
controllerutil.RemoveFinalizer(machinePool, clusterv1.MachinePoolFinalizer)
298-
return nil
281+
controllerutil.RemoveFinalizer(s.machinePool, clusterv1.MachinePoolFinalizer)
282+
return ctrl.Result{}, nil
299283
}
300284

301285
// reconcileDeleteNodes delete the cluster nodes.
@@ -430,3 +414,110 @@ func (r *MachinePoolReconciler) nodeToMachinePool(ctx context.Context, o client.
430414

431415
return nil
432416
}
417+
418+
func (r *MachinePoolReconciler) getMachinesForMachinePool(ctx context.Context, s *scope) (ctrl.Result, error) {
419+
infraMachineSelector := metav1.LabelSelector{
420+
MatchLabels: map[string]string{
421+
clusterv1.MachinePoolNameLabel: format.MustFormatValue(s.machinePool.Name),
422+
clusterv1.ClusterNameLabel: s.machinePool.Spec.ClusterName,
423+
},
424+
}
425+
426+
allMachines := &clusterv1.MachineList{}
427+
if err := r.Client.List(ctx,
428+
allMachines,
429+
client.InNamespace(s.machinePool.Namespace),
430+
client.MatchingLabels(infraMachineSelector.MatchLabels)); err != nil {
431+
return ctrl.Result{}, err
432+
}
433+
434+
filteredMachines := make([]*clusterv1.Machine, 0, len(allMachines.Items))
435+
for idx := range allMachines.Items {
436+
machine := &allMachines.Items[idx]
437+
if shouldExcludeMachine(s.machinePool, machine) {
438+
continue
439+
}
440+
filteredMachines = append(filteredMachines, machine)
441+
}
442+
443+
s.machines = filteredMachines
444+
445+
return ctrl.Result{}, nil
446+
}
447+
448+
func (r *MachinePoolReconciler) setMachinesUptoDate(ctx context.Context, s *scope) (ctrl.Result, error) {
449+
var errs []error
450+
for _, machine := range s.machines {
451+
patchHelper, err := patch.NewHelper(machine, r.Client)
452+
if err != nil {
453+
errs = append(errs, err)
454+
continue
455+
}
456+
457+
upToDateCondition := &metav1.Condition{
458+
Type: clusterv1.MachineUpToDateCondition,
459+
}
460+
461+
if machine.DeletionTimestamp.IsZero() {
462+
upToDateCondition.Status = metav1.ConditionTrue
463+
upToDateCondition.Reason = clusterv1.MachineUpToDateReason
464+
} else {
465+
upToDateCondition.Status = metav1.ConditionFalse
466+
upToDateCondition.Reason = clusterv1.MachineNotUpToDateReason
467+
upToDateCondition.Message = "Machine is being deleted"
468+
}
469+
conditions.Set(machine, *upToDateCondition)
470+
471+
if err := patchHelper.Patch(ctx, machine, patch.WithOwnedConditions{Conditions: []string{
472+
clusterv1.MachineUpToDateCondition,
473+
}}); err != nil {
474+
errs = append(errs, err)
475+
continue
476+
}
477+
}
478+
479+
if len(errs) > 0 {
480+
return ctrl.Result{}, kerrors.NewAggregate(errs)
481+
}
482+
483+
return ctrl.Result{}, nil
484+
}
485+
486+
type machinePoolReconcileFunc func(ctx context.Context, s *scope) (ctrl.Result, error)
487+
488+
func wrapErrMachinePoolReconcileFunc(f machinePoolReconcileFunc, msg string) machinePoolReconcileFunc {
489+
return func(ctx context.Context, s *scope) (ctrl.Result, error) {
490+
res, err := f(ctx, s)
491+
return res, errors.Wrap(err, msg)
492+
}
493+
}
494+
495+
func doReconcile(ctx context.Context, s *scope, phases []machinePoolReconcileFunc) (ctrl.Result, kerrors.Aggregate) {
496+
res := ctrl.Result{}
497+
errs := []error{}
498+
for _, phase := range phases {
499+
// Call the inner reconciliation methods.
500+
phaseResult, err := phase(ctx, s)
501+
if err != nil {
502+
errs = append(errs, err)
503+
}
504+
if len(errs) > 0 {
505+
continue
506+
}
507+
res = util.LowestNonZeroResult(res, phaseResult)
508+
}
509+
510+
if len(errs) > 0 {
511+
return ctrl.Result{}, kerrors.NewAggregate(errs)
512+
}
513+
514+
return res, nil
515+
}
516+
517+
func shouldExcludeMachine(machinePool *clusterv1.MachinePool, machine *clusterv1.Machine) bool {
518+
if metav1.GetControllerOf(machine) != nil && !metav1.IsControlledBy(machine, machinePool) {
519+
return true
520+
}
521+
522+
return false
523+
}

exp/internal/controllers/machinepool_controller_phases.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,7 @@ func (r *MachinePoolReconciler) reconcileInfrastructure(ctx context.Context, s *
276276
return ctrl.Result{}, err
277277
}
278278
infraConfig := infraReconcileResult.Result
279+
s.infraMachinePool = infraConfig
279280

280281
if !infraConfig.GetDeletionTimestamp().IsZero() {
281282
return ctrl.Result{}, nil

0 commit comments

Comments
 (0)