@@ -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
7069const (
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-
10791func (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+ }
0 commit comments