@@ -34,7 +34,10 @@ import (
34
34
"k8s.io/client-go/tools/record"
35
35
clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha3"
36
36
"sigs.k8s.io/cluster-api/controllers/external"
37
+ "sigs.k8s.io/cluster-api/controllers/noderefutil"
38
+ "sigs.k8s.io/cluster-api/controllers/remote"
37
39
"sigs.k8s.io/cluster-api/util"
40
+ "sigs.k8s.io/cluster-api/util/patch"
38
41
ctrl "sigs.k8s.io/controller-runtime"
39
42
"sigs.k8s.io/controller-runtime/pkg/client"
40
43
"sigs.k8s.io/controller-runtime/pkg/controller"
@@ -85,7 +88,6 @@ func (r *MachineSetReconciler) SetupWithManager(mgr ctrl.Manager, options contro
85
88
}
86
89
87
90
r .recorder = mgr .GetEventRecorderFor ("machineset-controller" )
88
-
89
91
r .scheme = mgr .GetScheme ()
90
92
return nil
91
93
}
@@ -161,14 +163,6 @@ func (r *MachineSetReconciler) reconcile(ctx context.Context, machineSet *cluste
161
163
machineSet .Spec .Selector .MatchLabels [clusterv1 .MachineSetLabelName ] = machineSet .Name
162
164
machineSet .Spec .Template .Labels [clusterv1 .MachineSetLabelName ] = machineSet .Name
163
165
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
-
172
166
selectorMap , err := metav1 .LabelSelectorAsMap (& machineSet .Spec .Selector )
173
167
if err != nil {
174
168
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
213
207
newStatus := r .calculateStatus (ms , filteredMachines )
214
208
215
209
// 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 )
217
211
if err != nil {
218
212
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 )
220
214
}
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 " )
222
216
}
223
217
224
218
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" )
226
220
}
227
221
228
222
var replicas int32
@@ -549,3 +543,111 @@ func (r *MachineSetReconciler) hasMatchingLabels(machineSet *clusterv1.MachineSe
549
543
func (r * MachineSetReconciler ) shouldAdopt (ms * clusterv1.MachineSet ) bool {
550
544
return ! util .HasOwner (ms .OwnerReferences , clusterv1 .GroupVersion .String (), []string {"MachineDeployment" , "Cluster" })
551
545
}
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
+ }
0 commit comments