Skip to content

Commit 2fd1671

Browse files
committed
✨ Refactor MS controller status patching
Signed-off-by: Vince Prignano <[email protected]>
1 parent c531036 commit 2fd1671

File tree

2 files changed

+115
-165
lines changed

2 files changed

+115
-165
lines changed

controllers/machineset_controller.go

Lines changed: 115 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,10 @@ import (
3434
"k8s.io/client-go/tools/record"
3535
clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha3"
3636
"sigs.k8s.io/cluster-api/controllers/external"
37+
"sigs.k8s.io/cluster-api/controllers/noderefutil"
38+
"sigs.k8s.io/cluster-api/controllers/remote"
3739
"sigs.k8s.io/cluster-api/util"
40+
"sigs.k8s.io/cluster-api/util/patch"
3841
ctrl "sigs.k8s.io/controller-runtime"
3942
"sigs.k8s.io/controller-runtime/pkg/client"
4043
"sigs.k8s.io/controller-runtime/pkg/controller"
@@ -85,7 +88,6 @@ func (r *MachineSetReconciler) SetupWithManager(mgr ctrl.Manager, options contro
8588
}
8689

8790
r.recorder = mgr.GetEventRecorderFor("machineset-controller")
88-
8991
r.scheme = mgr.GetScheme()
9092
return nil
9193
}
@@ -161,14 +163,6 @@ func (r *MachineSetReconciler) reconcile(ctx context.Context, machineSet *cluste
161163
machineSet.Spec.Selector.MatchLabels[clusterv1.MachineSetLabelName] = machineSet.Name
162164
machineSet.Spec.Template.Labels[clusterv1.MachineSetLabelName] = machineSet.Name
163165

164-
selector, err := metav1.LabelSelectorAsSelector(&machineSet.Spec.Selector)
165-
if err != nil {
166-
return ctrl.Result{}, errors.Wrapf(err, "failed to parse MachineSet %q label selector", machineSet.Name)
167-
}
168-
// Copy label selector to its status counterpart in string format.
169-
// This is necessary for CRDs including scale subresources.
170-
machineSet.Status.Selector = selector.String()
171-
172166
selectorMap, err := metav1.LabelSelectorAsMap(&machineSet.Spec.Selector)
173167
if err != nil {
174168
return ctrl.Result{}, errors.Wrapf(err, "failed to convert MachineSet %q label selector to a map", machineSet.Name)
@@ -213,16 +207,16 @@ func (r *MachineSetReconciler) reconcile(ctx context.Context, machineSet *cluste
213207
newStatus := r.calculateStatus(ms, filteredMachines)
214208

215209
// Always updates status as machines come up or die.
216-
updatedMS, err := updateMachineSetStatus(r.Client, machineSet, newStatus, logger)
210+
updatedMS, err := r.patchMachineSetStatus(ctx, machineSet, newStatus)
217211
if err != nil {
218212
if syncErr != nil {
219-
return ctrl.Result{}, errors.Wrapf(err, "failed to sync machines: %v. failed to update machine set status", syncErr)
213+
return ctrl.Result{}, errors.Wrapf(err, "failed to sync machines: %v. failed to patch MachineSet's Status", syncErr)
220214
}
221-
return ctrl.Result{}, errors.Wrap(err, "failed to update machine set status")
215+
return ctrl.Result{}, errors.Wrap(err, "failed to patch MachineSet's Status")
222216
}
223217

224218
if syncErr != nil {
225-
return ctrl.Result{}, errors.Wrapf(syncErr, "failed to sync Machineset replicas")
219+
return ctrl.Result{}, errors.Wrapf(syncErr, "failed to sync MachineSet replicas")
226220
}
227221

228222
var replicas int32
@@ -549,3 +543,111 @@ func (r *MachineSetReconciler) hasMatchingLabels(machineSet *clusterv1.MachineSe
549543
func (r *MachineSetReconciler) shouldAdopt(ms *clusterv1.MachineSet) bool {
550544
return !util.HasOwner(ms.OwnerReferences, clusterv1.GroupVersion.String(), []string{"MachineDeployment", "Cluster"})
551545
}
546+
547+
func (r *MachineSetReconciler) calculateStatus(ms *clusterv1.MachineSet, filteredMachines []*clusterv1.Machine) clusterv1.MachineSetStatus {
548+
logger := r.Log.WithValues("machineset", ms.Name, "namespace", ms.Namespace)
549+
newStatus := ms.Status
550+
551+
// Copy label selector to its status counterpart in string format.
552+
// This is necessary for CRDs including scale subresources.
553+
selector, _ := metav1.LabelSelectorAsSelector(&ms.Spec.Selector)
554+
newStatus.Selector = selector.String()
555+
556+
// Count the number of machines that have labels matching the labels of the machine
557+
// template of the replica set, the matching machines may have more
558+
// labels than are in the template. Because the label of machineTemplateSpec is
559+
// a superset of the selector of the replica set, so the possible
560+
// matching machines must be part of the filteredMachines.
561+
fullyLabeledReplicasCount := 0
562+
readyReplicasCount := 0
563+
availableReplicasCount := 0
564+
templateLabel := labels.Set(ms.Spec.Template.Labels).AsSelectorPreValidated()
565+
566+
// Retrieve Cluster, if any.
567+
cluster, _ := util.GetClusterByName(context.Background(), r.Client, ms.ObjectMeta.Namespace, ms.Spec.ClusterName)
568+
569+
for _, machine := range filteredMachines {
570+
if templateLabel.Matches(labels.Set(machine.Labels)) {
571+
fullyLabeledReplicasCount++
572+
}
573+
574+
if machine.Status.NodeRef == nil {
575+
logger.V(2).Info("Unable to retrieve Node status, missing NodeRef", "machine", machine.Name)
576+
continue
577+
}
578+
579+
node, err := r.getMachineNode(cluster, machine)
580+
if err != nil {
581+
logger.Error(err, "Unable to retrieve Node status")
582+
continue
583+
}
584+
585+
if noderefutil.IsNodeReady(node) {
586+
readyReplicasCount++
587+
if noderefutil.IsNodeAvailable(node, ms.Spec.MinReadySeconds, metav1.Now()) {
588+
availableReplicasCount++
589+
}
590+
}
591+
}
592+
593+
newStatus.Replicas = int32(len(filteredMachines))
594+
newStatus.FullyLabeledReplicas = int32(fullyLabeledReplicasCount)
595+
newStatus.ReadyReplicas = int32(readyReplicasCount)
596+
newStatus.AvailableReplicas = int32(availableReplicasCount)
597+
return newStatus
598+
}
599+
600+
// patchMachineSetStatus attempts to update the Status.Replicas of the given MachineSet.
601+
func (r *MachineSetReconciler) patchMachineSetStatus(ctx context.Context, ms *clusterv1.MachineSet, newStatus clusterv1.MachineSetStatus) (*clusterv1.MachineSet, error) {
602+
logger := r.Log.WithValues("machineset", ms.Name, "namespace", ms.Namespace)
603+
604+
// This is the steady state. It happens when the MachineSet doesn't have any expectations, since
605+
// we do a periodic relist every 10 minutes. If the generations differ but the replicas are
606+
// the same, a caller might've resized to the same replica count.
607+
if ms.Status.Replicas == newStatus.Replicas &&
608+
ms.Status.FullyLabeledReplicas == newStatus.FullyLabeledReplicas &&
609+
ms.Status.ReadyReplicas == newStatus.ReadyReplicas &&
610+
ms.Status.AvailableReplicas == newStatus.AvailableReplicas &&
611+
ms.Generation == ms.Status.ObservedGeneration {
612+
return ms, nil
613+
}
614+
615+
patchHelper, err := patch.NewHelper(ms, r.Client)
616+
if err != nil {
617+
return nil, err
618+
}
619+
620+
// Save the generation number we acted on, otherwise we might wrongfully indicate
621+
// that we've seen a spec update when we retry.
622+
newStatus.ObservedGeneration = ms.Generation
623+
624+
// Calculate the replicas for logging.
625+
var replicas int32
626+
if ms.Spec.Replicas != nil {
627+
replicas = *ms.Spec.Replicas
628+
}
629+
logger.V(4).Info(fmt.Sprintf("Updating status for %v: %s/%s, ", ms.Kind, ms.Namespace, ms.Name) +
630+
fmt.Sprintf("replicas %d->%d (need %d), ", ms.Status.Replicas, newStatus.Replicas, replicas) +
631+
fmt.Sprintf("fullyLabeledReplicas %d->%d, ", ms.Status.FullyLabeledReplicas, newStatus.FullyLabeledReplicas) +
632+
fmt.Sprintf("readyReplicas %d->%d, ", ms.Status.ReadyReplicas, newStatus.ReadyReplicas) +
633+
fmt.Sprintf("availableReplicas %d->%d, ", ms.Status.AvailableReplicas, newStatus.AvailableReplicas) +
634+
fmt.Sprintf("sequence No: %v->%v", ms.Status.ObservedGeneration, newStatus.ObservedGeneration))
635+
636+
ms.Status = newStatus
637+
if err := patchHelper.Patch(ctx, ms); err != nil {
638+
return nil, err
639+
}
640+
return ms, nil
641+
}
642+
643+
func (r *MachineSetReconciler) getMachineNode(cluster *clusterv1.Cluster, machine *clusterv1.Machine) (*corev1.Node, error) {
644+
c, err := remote.NewClusterClient(r.Client, cluster, r.scheme)
645+
if err != nil {
646+
return nil, err
647+
}
648+
node := &corev1.Node{}
649+
if err := c.Get(context.TODO(), client.ObjectKey{Name: machine.Status.NodeRef.Name}, node); err != nil {
650+
return nil, errors.Wrapf(err, "error retrieving node %s for machine %s/%s", machine.Status.NodeRef.Name, machine.Namespace, machine.Name)
651+
}
652+
return node, nil
653+
}

controllers/machineset_status.go

Lines changed: 0 additions & 152 deletions
This file was deleted.

0 commit comments

Comments
 (0)