@@ -27,6 +27,7 @@ import (
27
27
apierrors "k8s.io/apimachinery/pkg/api/errors"
28
28
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29
29
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
30
+ kerrors "k8s.io/apimachinery/pkg/util/errors"
30
31
"k8s.io/apimachinery/pkg/util/sets"
31
32
"k8s.io/apimachinery/pkg/util/validation/field"
32
33
"k8s.io/apimachinery/pkg/util/wait"
@@ -75,17 +76,30 @@ func (r *Reconciler) reconcileState(ctx context.Context, s *scope.Scope) error {
75
76
}
76
77
77
78
// Reconcile desired state of the InfrastructureCluster object.
78
- if err := r .reconcileInfrastructureCluster (ctx , s ); err != nil {
79
- return err
79
+ createdInfraCluster , errInfraCluster := r .reconcileInfrastructureCluster (ctx , s )
80
+ if errInfraCluster != nil {
81
+ return errInfraCluster
80
82
}
81
83
82
84
// Reconcile desired state of the ControlPlane object.
83
- if err := r .reconcileControlPlane (ctx , s ); err != nil {
84
- return err
85
+ createdControlPlane , errControlPlane := r .reconcileControlPlane (ctx , s )
86
+ if errControlPlane != nil {
87
+ // NOTE: report control plane error immediately only if we did not just create the infrastructure cluster; otherwise attempt reconcile cluster before returning.
88
+ if ! createdInfraCluster {
89
+ return errControlPlane
90
+ }
91
+
92
+ // In this case (reconcileInfrastructureCluster passed reporting creation of the infrastructure cluster object, reconcileControlPlane - which is expected to create the control plane object - failed),
93
+ // if the creation of the control plane actually did not happen, blank out ControlPlaneRef from desired cluster.
94
+ if s .Current .Cluster .Spec .ControlPlaneRef == nil && ! createdControlPlane {
95
+ s .Desired .Cluster .Spec .ControlPlaneRef = nil
96
+ }
85
97
}
86
98
87
99
// Reconcile desired state of the Cluster object.
88
- if err := r .reconcileCluster (ctx , s ); err != nil {
100
+ errCluster := r .reconcileCluster (ctx , s )
101
+ err := kerrors .NewAggregate ([]error {errControlPlane , errCluster })
102
+ if err != nil {
89
103
return err
90
104
}
91
105
@@ -283,12 +297,12 @@ func (r *Reconciler) callAfterClusterUpgrade(ctx context.Context, s *scope.Scope
283
297
}
284
298
285
299
// reconcileInfrastructureCluster reconciles the desired state of the InfrastructureCluster object.
286
- func (r * Reconciler ) reconcileInfrastructureCluster (ctx context.Context , s * scope.Scope ) error {
300
+ func (r * Reconciler ) reconcileInfrastructureCluster (ctx context.Context , s * scope.Scope ) ( bool , error ) {
287
301
ctx , _ = tlog .LoggerFrom (ctx ).WithObject (s .Desired .InfrastructureCluster ).Into (ctx )
288
302
289
303
ignorePaths , err := contract .InfrastructureCluster ().IgnorePaths (s .Desired .InfrastructureCluster )
290
304
if err != nil {
291
- return errors .Wrap (err , "failed to calculate ignore paths" )
305
+ return false , errors .Wrap (err , "failed to calculate ignore paths" )
292
306
}
293
307
294
308
return r .reconcileReferencedObject (ctx , reconcileReferencedObjectInput {
@@ -301,30 +315,30 @@ func (r *Reconciler) reconcileInfrastructureCluster(ctx context.Context, s *scop
301
315
302
316
// reconcileControlPlane works to bring the current state of a managed topology in line with the desired state. This involves
303
317
// updating the cluster where needed.
304
- func (r * Reconciler ) reconcileControlPlane (ctx context.Context , s * scope.Scope ) error {
318
+ func (r * Reconciler ) reconcileControlPlane (ctx context.Context , s * scope.Scope ) ( bool , error ) {
305
319
// If the ControlPlane has defined a current or desired MachineHealthCheck attempt to reconcile it.
306
320
// MHC changes are not Kubernetes version dependent, therefore proceed with MHC reconciliation
307
321
// even if the Control Plane is pending an upgrade.
308
322
if s .Desired .ControlPlane .MachineHealthCheck != nil || s .Current .ControlPlane .MachineHealthCheck != nil {
309
323
// Reconcile the current and desired state of the MachineHealthCheck.
310
324
if err := r .reconcileMachineHealthCheck (ctx , s .Current .ControlPlane .MachineHealthCheck , s .Desired .ControlPlane .MachineHealthCheck ); err != nil {
311
- return err
325
+ return false , err
312
326
}
313
327
}
314
328
315
329
// Return early if the control plane is pending an upgrade.
316
330
// Do not reconcile the control plane yet to avoid updating the control plane while it is still pending a
317
331
// version upgrade. This will prevent the control plane from performing a double rollout.
318
332
if s .UpgradeTracker .ControlPlane .IsPendingUpgrade {
319
- return nil
333
+ return false , nil
320
334
}
321
335
// If the clusterClass mandates the controlPlane has infrastructureMachines, reconcile it.
322
336
if s .Blueprint .HasControlPlaneInfrastructureMachine () {
323
337
ctx , _ := tlog .LoggerFrom (ctx ).WithObject (s .Desired .ControlPlane .InfrastructureMachineTemplate ).Into (ctx )
324
338
325
339
cpInfraRef , err := contract .ControlPlane ().MachineTemplate ().InfrastructureRef ().Get (s .Desired .ControlPlane .Object )
326
340
if err != nil {
327
- return errors .Wrapf (err , "failed to reconcile %s" , tlog.KObj {Obj : s .Desired .ControlPlane .InfrastructureMachineTemplate })
341
+ return false , errors .Wrapf (err , "failed to reconcile %s" , tlog.KObj {Obj : s .Desired .ControlPlane .InfrastructureMachineTemplate })
328
342
}
329
343
330
344
// Create or update the MachineInfrastructureTemplate of the control plane.
@@ -337,25 +351,26 @@ func (r *Reconciler) reconcileControlPlane(ctx context.Context, s *scope.Scope)
337
351
templateNamePrefix : controlPlaneInfrastructureMachineTemplateNamePrefix (s .Current .Cluster .Name ),
338
352
},
339
353
); err != nil {
340
- return err
354
+ return false , err
341
355
}
342
356
343
357
// The controlPlaneObject.Spec.machineTemplate.infrastructureRef has to be updated in the desired object
344
358
err = contract .ControlPlane ().MachineTemplate ().InfrastructureRef ().Set (s .Desired .ControlPlane .Object , refToUnstructured (cpInfraRef ))
345
359
if err != nil {
346
- return errors .Wrapf (err , "failed to reconcile %s" , tlog.KObj {Obj : s .Desired .ControlPlane .Object })
360
+ return false , errors .Wrapf (err , "failed to reconcile %s" , tlog.KObj {Obj : s .Desired .ControlPlane .Object })
347
361
}
348
362
}
349
363
350
364
// Create or update the ControlPlaneObject for the ControlPlaneState.
351
365
ctx , _ = tlog .LoggerFrom (ctx ).WithObject (s .Desired .ControlPlane .Object ).Into (ctx )
352
- if err := r .reconcileReferencedObject (ctx , reconcileReferencedObjectInput {
366
+ created , err := r .reconcileReferencedObject (ctx , reconcileReferencedObjectInput {
353
367
cluster : s .Current .Cluster ,
354
368
current : s .Current .ControlPlane .Object ,
355
369
desired : s .Desired .ControlPlane .Object ,
356
370
versionGetter : contract .ControlPlane ().Version ().Get ,
357
- }); err != nil {
358
- return err
371
+ })
372
+ if err != nil {
373
+ return created , err
359
374
}
360
375
361
376
// If the controlPlane has infrastructureMachines and the InfrastructureMachineTemplate has changed on this reconcile
@@ -364,15 +379,15 @@ func (r *Reconciler) reconcileControlPlane(ctx context.Context, s *scope.Scope)
364
379
if s .Blueprint .HasControlPlaneInfrastructureMachine () && s .Current .ControlPlane .InfrastructureMachineTemplate != nil {
365
380
if s .Current .ControlPlane .InfrastructureMachineTemplate .GetName () != s .Desired .ControlPlane .InfrastructureMachineTemplate .GetName () {
366
381
if err := r .Client .Delete (ctx , s .Current .ControlPlane .InfrastructureMachineTemplate ); err != nil {
367
- return errors .Wrapf (err , "failed to delete oldinfrastructure machine template %s of control plane %s" ,
382
+ return created , errors .Wrapf (err , "failed to delete oldinfrastructure machine template %s of control plane %s" ,
368
383
tlog.KObj {Obj : s .Current .ControlPlane .InfrastructureMachineTemplate },
369
384
tlog.KObj {Obj : s .Current .ControlPlane .Object },
370
385
)
371
386
}
372
387
}
373
388
}
374
389
375
- return nil
390
+ return created , nil
376
391
}
377
392
378
393
// reconcileMachineHealthCheck creates, updates, deletes or leaves untouched a MachineHealthCheck depending on the difference between the
@@ -824,15 +839,15 @@ func (r *Reconciler) createMachinePool(ctx context.Context, s *scope.Scope, mp *
824
839
log := tlog .LoggerFrom (ctx ).WithMachinePool (mp .Object )
825
840
cluster := s .Current .Cluster
826
841
infraCtx , _ := log .WithObject (mp .InfrastructureMachinePoolObject ).Into (ctx )
827
- if err := r .reconcileReferencedObject (infraCtx , reconcileReferencedObjectInput {
842
+ if _ , err := r .reconcileReferencedObject (infraCtx , reconcileReferencedObjectInput {
828
843
cluster : cluster ,
829
844
desired : mp .InfrastructureMachinePoolObject ,
830
845
}); err != nil {
831
846
return errors .Wrapf (err , "failed to create %s" , mp .Object .Kind )
832
847
}
833
848
834
849
bootstrapCtx , _ := log .WithObject (mp .BootstrapObject ).Into (ctx )
835
- if err := r .reconcileReferencedObject (bootstrapCtx , reconcileReferencedObjectInput {
850
+ if _ , err := r .reconcileReferencedObject (bootstrapCtx , reconcileReferencedObjectInput {
836
851
cluster : cluster ,
837
852
desired : mp .BootstrapObject ,
838
853
}); err != nil {
@@ -883,7 +898,7 @@ func (r *Reconciler) updateMachinePool(ctx context.Context, s *scope.Scope, curr
883
898
884
899
cluster := s .Current .Cluster
885
900
infraCtx , _ := log .WithObject (desiredMP .InfrastructureMachinePoolObject ).Into (ctx )
886
- if err := r .reconcileReferencedObject (infraCtx , reconcileReferencedObjectInput {
901
+ if _ , err := r .reconcileReferencedObject (infraCtx , reconcileReferencedObjectInput {
887
902
cluster : cluster ,
888
903
current : currentMP .InfrastructureMachinePoolObject ,
889
904
desired : desiredMP .InfrastructureMachinePoolObject ,
@@ -892,7 +907,7 @@ func (r *Reconciler) updateMachinePool(ctx context.Context, s *scope.Scope, curr
892
907
}
893
908
894
909
bootstrapCtx , _ := log .WithObject (desiredMP .BootstrapObject ).Into (ctx )
895
- if err := r .reconcileReferencedObject (bootstrapCtx , reconcileReferencedObjectInput {
910
+ if _ , err := r .reconcileReferencedObject (bootstrapCtx , reconcileReferencedObjectInput {
896
911
cluster : cluster ,
897
912
current : currentMP .BootstrapObject ,
898
913
desired : desiredMP .BootstrapObject ,
@@ -1022,44 +1037,44 @@ type reconcileReferencedObjectInput struct {
1022
1037
// reconcileReferencedObject reconciles the desired state of the referenced object.
1023
1038
// NOTE: After a referenced object is created it is assumed that the reference should
1024
1039
// never change (only the content of the object can eventually change). Thus, we are checking for strict compatibility.
1025
- func (r * Reconciler ) reconcileReferencedObject (ctx context.Context , in reconcileReferencedObjectInput ) error {
1040
+ func (r * Reconciler ) reconcileReferencedObject (ctx context.Context , in reconcileReferencedObjectInput ) ( bool , error ) {
1026
1041
log := tlog .LoggerFrom (ctx )
1027
1042
1028
1043
// If there is no current object, create it.
1029
1044
if in .current == nil {
1030
1045
log .Infof ("Creating %s" , tlog.KObj {Obj : in .desired })
1031
1046
helper , err := r .patchHelperFactory (ctx , nil , in .desired , structuredmerge .IgnorePaths (in .ignorePaths ))
1032
1047
if err != nil {
1033
- return errors .Wrap (createErrorWithoutObjectName (ctx , err , in .desired ), "failed to create patch helper" )
1048
+ return false , errors .Wrap (createErrorWithoutObjectName (ctx , err , in .desired ), "failed to create patch helper" )
1034
1049
}
1035
1050
if err := helper .Patch (ctx ); err != nil {
1036
- return createErrorWithoutObjectName (ctx , err , in .desired )
1051
+ return false , createErrorWithoutObjectName (ctx , err , in .desired )
1037
1052
}
1038
1053
r .recorder .Eventf (in .cluster , corev1 .EventTypeNormal , createEventReason , "Created %q" , tlog.KObj {Obj : in .desired })
1039
- return nil
1054
+ return true , nil
1040
1055
}
1041
1056
1042
1057
// Check if the current and desired referenced object are compatible.
1043
1058
if allErrs := check .ObjectsAreStrictlyCompatible (in .current , in .desired ); len (allErrs ) > 0 {
1044
- return allErrs .ToAggregate ()
1059
+ return false , allErrs .ToAggregate ()
1045
1060
}
1046
1061
1047
1062
// Check differences between current and desired state, and eventually patch the current object.
1048
1063
patchHelper , err := r .patchHelperFactory (ctx , in .current , in .desired , structuredmerge .IgnorePaths (in .ignorePaths ))
1049
1064
if err != nil {
1050
- return errors .Wrapf (err , "failed to create patch helper for %s" , tlog.KObj {Obj : in .current })
1065
+ return false , errors .Wrapf (err , "failed to create patch helper for %s" , tlog.KObj {Obj : in .current })
1051
1066
}
1052
1067
if ! patchHelper .HasChanges () {
1053
1068
log .V (3 ).Infof ("No changes for %s" , tlog.KObj {Obj : in .desired })
1054
- return nil
1069
+ return false , nil
1055
1070
}
1056
1071
1057
1072
log .Infof ("Patching %s" , tlog.KObj {Obj : in .desired })
1058
1073
if err := patchHelper .Patch (ctx ); err != nil {
1059
- return errors .Wrapf (err , "failed to patch %s" , tlog.KObj {Obj : in .current })
1074
+ return false , errors .Wrapf (err , "failed to patch %s" , tlog.KObj {Obj : in .current })
1060
1075
}
1061
1076
r .recorder .Eventf (in .cluster , corev1 .EventTypeNormal , updateEventReason , "Updated %q%s" , tlog.KObj {Obj : in .desired }, logUnstructuredVersionChange (in .current , in .desired , in .versionGetter ))
1062
- return nil
1077
+ return false , nil
1063
1078
}
1064
1079
1065
1080
func logUnstructuredVersionChange (current , desired * unstructured.Unstructured , versionGetter unstructuredVersionGetter ) string {
0 commit comments