@@ -26,6 +26,7 @@ import (
26
26
_ "crypto/sha512"
27
27
"encoding/base64"
28
28
"encoding/json"
29
+ "errors"
29
30
"fmt"
30
31
"net"
31
32
"net/url"
@@ -37,7 +38,7 @@ import (
37
38
38
39
"gopkg.in/yaml.v3"
39
40
corev1 "k8s.io/api/core/v1"
40
- "k8s.io/apimachinery/pkg/api/errors"
41
+ k8sapierrors "k8s.io/apimachinery/pkg/api/errors"
41
42
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
42
43
"k8s.io/apimachinery/pkg/runtime"
43
44
"k8s.io/apimachinery/pkg/types"
@@ -161,7 +162,6 @@ func (r *ImageClusterInstallReconciler) Reconcile(ctx context.Context, req ctrl.
161
162
return ctrl.Result {}, err
162
163
}
163
164
164
- /////// Requirements not met yet: setting defaults //////
165
165
cond := hivev1.ClusterInstallCondition {
166
166
Type : hivev1 .ClusterInstallRequirementsMet ,
167
167
Status : corev1 .ConditionFalse ,
@@ -172,31 +172,101 @@ func (r *ImageClusterInstallReconciler) Reconcile(ctx context.Context, req ctrl.
172
172
r .setRequirementsMetCondition (ctx , ici , cond .Status , cond .Reason , cond .Message )
173
173
}()
174
174
175
- /////// Requirements not met yet: starting config (CD, BMH) validation //////
175
+ // 1. Config validation phase
176
+ // Possible reasons for not meeting requirements and exiting reconcile:
177
+ // - ConfigurationPending (default): it's either the user needs to complete the ImageClusterInstall definition, or some of
178
+ // referenced resources (CD or BMH) are not available yet. In both cases the reconcile ends, and will be triggered again
179
+ // when the problem is resolved.
180
+ // - ConfigurationFailed: sets this reason when AutomatedCleaningMode cannot be modified in BMH.
176
181
cond .Reason = v1alpha1 .ConfigurationPendingReason
182
+ cd , bmh , err := r .validateConfiguration (ctx , ici , & cond , log )
183
+ if err != nil {
184
+ return ctrl.Result {}, err
185
+ }
186
+
187
+ // 2. Host validation phase
188
+ // Possible reasons for not meeting requirements and exiting reconcile:
189
+ // - HostValidationPending: if BMH provisioning or hardware inspection is not ready yet, reconcile is requeued for 30s later.
190
+ // - HostValidationFailed (default): in case of any errors or invalid BMH configuration the reconcile ends here.
191
+ // Default is HostValidationFailedReason but validateBMH() can change this to HostValidationPendingReason
192
+ cond .Reason = v1alpha1 .HostValidationFailedReason
193
+ res , err := r .validateHost (ctx , ici , bmh , cd , & cond , log )
194
+ if ! res .IsZero () || err != nil {
195
+ return res , err
196
+ }
197
+
198
+ if err := r .setClusterInstallMetadata (ctx , log , ici , cd ); err != nil {
199
+ cond .Message = "failed to set ClusterMetaData in ImageClusterInstall"
200
+ log .Error (err )
201
+ return ctrl.Result {}, err
202
+ }
203
+
204
+ // 3. Image creation phase
205
+ // Possible reasons for not meeting requirements and exiting reconcile:
206
+ // - ImageCreationPending: when lock cannot be acquired, reconcile gets requeued for 5s later to try again.
207
+ // - ImageCreationFailed (default): any other unexpected error stops the reconcile loop with this reason.
208
+ cond .Reason = v1alpha1 .ImageCreationFailedReason
209
+ imageUrl , res , err := r .createImage (ctx , ici , req , bmh , cd , & cond , log )
210
+ if ! res .IsZero () || err != nil {
211
+ return res , err
212
+ }
213
+
214
+ r .labelReferencedObjectsForBackup (ctx , log , ici , cd )
215
+
216
+ // 4. Host configuration phase
217
+ // Possible reasons for not meeting requirements and exiting reconcile:
218
+ // - HostConfigurationPending: sets this reason in following scenarios:
219
+ // > earlier DataImage instance is still being deleted for some reason (requeue after 30s)
220
+ // > current DataImage was just created less than a second ago so BMO might not be notified yet (requeue after 1s)
221
+ // > image-based-install-managed annotation is not set yet in BMH (no requeue)
222
+ // - HostConfigurationFailed (default): any unexpected errors during this phase will lead to this reason and finish reconcile.
223
+ cond .Reason = v1alpha1 .HostConfigurationFailedReason
224
+ res , err = r .configureHost (ctx , ici , imageUrl , bmh , & cond , log )
225
+ if ! res .IsZero () || err != nil {
226
+ return res , err
227
+ }
228
+
229
+ // Requirements met, host configured
230
+ cond .Status = corev1 .ConditionTrue
231
+ cond .Reason = v1alpha1 .HostConfigurationSucceededReason
232
+ cond .Message = "configuration image is attached to the referenced host"
233
+
234
+ return ctrl.Result {}, nil
235
+ }
236
+
237
+ func GetClusterConfigDir (namespacesDir , namespace , uid string ) string {
238
+ return filepath .Join (namespacesDir , namespace , uid , FilesDir , ClusterConfigDir )
239
+ }
240
+
241
+ func (r * ImageClusterInstallReconciler ) validateConfiguration (
242
+ ctx context.Context ,
243
+ ici * v1alpha1.ImageClusterInstall ,
244
+ cond * hivev1.ClusterInstallCondition ,
245
+ log logrus.FieldLogger ,
246
+ ) (* hivev1.ClusterDeployment , * bmh_v1alpha1.BareMetalHost , error ) {
177
247
178
248
if ici .Spec .ClusterDeploymentRef == nil || ici .Spec .ClusterDeploymentRef .Name == "" {
179
249
cond .Message = "ClusterDeploymentRef is unset"
180
- return ctrl. Result {} , nil
250
+ return nil , nil , errors . New ( cond . Message )
181
251
}
182
252
183
- clusterDeployment , err := r .getCD (ctx , ici )
253
+ cd , err := r .getCD (ctx , ici )
184
254
if err != nil {
185
255
cond .Message = fmt .Sprintf ("failed to get ClusterDeployment %s/%s" , ici .Namespace , ici .Spec .ClusterDeploymentRef .Name )
186
- log .WithError ( err ). Error (cond . Message )
187
- return ctrl. Result {} , err
256
+ log .Error (err )
257
+ return nil , nil , err
188
258
}
189
259
190
- if ici .Spec .BareMetalHostRef == nil {
191
- cond .Message = "no BareMetalHostRef set, nothing to do without provided bmh "
192
- return ctrl. Result {} , nil
260
+ if ici .Spec .BareMetalHostRef == nil || ici . Spec . BareMetalHostRef . Name == "" {
261
+ cond .Message = "BareMetalHostRef is unset "
262
+ return nil , nil , errors . New ( cond . Message )
193
263
}
194
264
195
265
bmh , err := r .getBMH (ctx , ici .Spec .BareMetalHostRef )
196
266
if err != nil {
197
267
cond .Message = fmt .Sprintf ("failed to get BareMetalHost %s/%s" , ici .Spec .BareMetalHostRef .Namespace , ici .Spec .BareMetalHostRef .Name )
198
- log .WithError ( err ). Error (cond . Message )
199
- return ctrl. Result {} , err
268
+ log .Error (err )
269
+ return nil , nil , err
200
270
}
201
271
202
272
// AutomatedCleaningMode is set at the beginning of this flow because we don't want ironic to format the disk
@@ -208,27 +278,50 @@ func (r *ImageClusterInstallReconciler) Reconcile(ctx context.Context, req ctrl.
208
278
cond .Reason = v1alpha1 .ConfigurationFailedReason
209
279
cond .Message = fmt .Sprintf ("failed to disable automated cleaning mode for BareMetalHost %s/%s" , bmh .Namespace , bmh .Name )
210
280
log .WithError (err ).Error (cond .Message )
211
- return ctrl. Result {} , err
281
+ return nil , nil , err
212
282
}
213
283
}
214
284
215
- /////// Requirements not met yet: config validated, starting host validation //////
216
- cond . Reason = v1alpha1 . HostValidationFailedReason
285
+ return cd , bmh , nil
286
+ }
217
287
218
- if res , err := r .validateBMH (ctx , ici , bmh , & cond ); ! res .IsZero () || err != nil {
288
+ func (r * ImageClusterInstallReconciler ) validateHost (
289
+ ctx context.Context ,
290
+ ici * v1alpha1.ImageClusterInstall ,
291
+ bmh * bmh_v1alpha1.BareMetalHost ,
292
+ cd * hivev1.ClusterDeployment ,
293
+ cond * hivev1.ClusterInstallCondition ,
294
+ log logrus.FieldLogger ,
295
+ ) (ctrl.Result , error ) {
296
+
297
+ if res , err := r .validateBMH (ici , bmh , cond ); ! res .IsZero () || err != nil {
219
298
return res , err
220
299
}
221
300
222
- if err = r .setClusterInstallMetadata (ctx , log , ici , clusterDeployment ); err != nil {
223
- cond .Message = "failed to set ImageClusterInstall data"
224
- log .WithError (err ).Error (cond .Message )
225
- return ctrl.Result {}, err
301
+ if ! bmh .Spec .ExternallyProvisioned {
302
+ log .Infof ("Setting BareMetalHost (%s/%s) ExternallyProvisioned spec" , bmh .Namespace , bmh .Name )
303
+ patch := client .MergeFrom (bmh .DeepCopy ())
304
+ bmh .Spec .ExternallyProvisioned = true
305
+ if err := r .Patch (ctx , bmh , patch ); err != nil {
306
+ return ctrl.Result {}, err
307
+ }
308
+
226
309
}
227
310
228
- /////// Requirements not met yet: host validated, starting image creation //////
229
- cond . Reason = v1alpha1 . ImageCreationFailedReason
311
+ return ctrl. Result {}, nil
312
+ }
230
313
231
- res , err := r .writeInputData (ctx , log , ici , clusterDeployment , bmh )
314
+ func (r * ImageClusterInstallReconciler ) createImage (
315
+ ctx context.Context ,
316
+ ici * v1alpha1.ImageClusterInstall ,
317
+ req ctrl.Request ,
318
+ bmh * bmh_v1alpha1.BareMetalHost ,
319
+ cd * hivev1.ClusterDeployment ,
320
+ cond * hivev1.ClusterInstallCondition ,
321
+ log logrus.FieldLogger ,
322
+ ) (string , ctrl.Result , error ) {
323
+
324
+ res , err := r .writeInputData (ctx , log , ici , cd , bmh )
232
325
if ! res .IsZero () || err != nil {
233
326
cond .Reason = v1alpha1 .ImageCreationPendingReason
234
327
cond .Message = "could not acquire lock for image data"
@@ -237,20 +330,26 @@ func (r *ImageClusterInstallReconciler) Reconcile(ctx context.Context, req ctrl.
237
330
cond .Message = "failed to create image"
238
331
log .WithError (err ).Error (cond .Message )
239
332
}
240
- return res , err
333
+ return "" , res , err
241
334
}
242
335
243
- r .labelReferencedObjectsForBackup (ctx , log , ici , clusterDeployment )
244
-
245
336
imageUrl , err := url .JoinPath (r .BaseURL , "images" , req .Namespace , fmt .Sprintf ("%s.iso" , ici .ObjectMeta .UID ))
246
337
if err != nil {
247
338
cond .Message = "failed to create image url"
248
339
log .WithError (err ).Error (cond .Message )
249
- return ctrl.Result {}, err
340
+ return "" , ctrl.Result {}, err
250
341
}
251
342
252
- /////// Requirements not met yet: image created, starting host configuration //////
253
- cond .Reason = v1alpha1 .HostConfigurationFailedReason
343
+ return imageUrl , ctrl.Result {}, nil
344
+ }
345
+ func (r * ImageClusterInstallReconciler ) configureHost (
346
+ ctx context.Context ,
347
+ ici * v1alpha1.ImageClusterInstall ,
348
+ imageUrl string ,
349
+ bmh * bmh_v1alpha1.BareMetalHost ,
350
+ cond * hivev1.ClusterInstallCondition ,
351
+ log logrus.FieldLogger ,
352
+ ) (ctrl.Result , error ) {
254
353
255
354
dataImage , res , err := r .ensureBMHDataImage (ctx , log , bmh , imageUrl )
256
355
if err != nil {
@@ -267,8 +366,8 @@ func (r *ImageClusterInstallReconciler) Reconcile(ctx context.Context, req ctrl.
267
366
// in case the dataImage was created less than a second ago requeue to allow BMO some time to get
268
367
// notified about the newly created DataImage before adding the reboot annotation in updateBMHProvisioningState
269
368
cond .Reason = v1alpha1 .HostConfigurationPendingReason
270
- cond .Message = "Waiting for BareMetalHost to get DataImage "
271
- return ctrl.Result {RequeueAfter : r .Options .DataImageCoolDownPeriod }, err
369
+ cond .Message = "waiting for DataImage to cool down "
370
+ return ctrl.Result {RequeueAfter : r .Options .DataImageCoolDownPeriod }, errors . New ( cond . Message )
272
371
}
273
372
274
373
if err := r .updateBMHProvisioningState (ctx , log , bmh , dataImage ); err != nil {
@@ -279,9 +378,9 @@ func (r *ImageClusterInstallReconciler) Reconcile(ctx context.Context, req ctrl.
279
378
if ! annotationExists (& bmh .ObjectMeta , ibioManagedBMH ) {
280
379
// TODO: consider replacing this condition with `dataDisk.Status.AttachedImage`
281
380
cond .Reason = v1alpha1 .HostConfigurationPendingReason
282
- cond .Message = fmt .Sprintf ("Waiting for BMH to get %s annotation " , ibioManagedBMH )
381
+ cond .Message = fmt .Sprintf ("waiting for BMH provisioning state to be StateAvailable or StateExternallyProvisioned, current state is: %s " , bmh . Status . Provisioning . State )
283
382
log .Info (cond .Message )
284
- return ctrl.Result {}, nil
383
+ return ctrl.Result {}, errors . New ( cond . Message )
285
384
}
286
385
287
386
if ici .Status .BareMetalHostRef == nil {
@@ -298,20 +397,10 @@ func (r *ImageClusterInstallReconciler) Reconcile(ctx context.Context, req ctrl.
298
397
}
299
398
}
300
399
301
- /////// Requirements met, host configured //////
302
- cond .Status = corev1 .ConditionTrue
303
- cond .Reason = v1alpha1 .HostConfigurationSucceededReason
304
- cond .Message = "Configuration image is attached to the referenced host"
305
-
306
400
return ctrl.Result {}, nil
307
401
}
308
402
309
- func GetClusterConfigDir (namespacesDir , namespace , uid string ) string {
310
- return filepath .Join (namespacesDir , namespace , uid , FilesDir , ClusterConfigDir )
311
- }
312
-
313
403
func (r * ImageClusterInstallReconciler ) validateBMH (
314
- ctx context.Context ,
315
404
ici * v1alpha1.ImageClusterInstall ,
316
405
bmh * bmh_v1alpha1.BareMetalHost ,
317
406
cond * hivev1.ClusterInstallCondition ) (ctrl.Result , error ) {
@@ -499,13 +588,7 @@ func (r *ImageClusterInstallReconciler) updateBMHProvisioningState(ctx context.C
499
588
if bmh .Status .Provisioning .State != bmh_v1alpha1 .StateAvailable && bmh .Status .Provisioning .State != bmh_v1alpha1 .StateExternallyProvisioned {
500
589
return nil
501
590
}
502
- log .Infof ("Updating BareMetalHost %s/%s provisioning state, current PoweredOn status is: %s" , bmh .Namespace , bmh .Name , bmh .Status .PoweredOn )
503
- if bmh .Status .Provisioning .State == bmh_v1alpha1 .StateAvailable {
504
- if ! bmh .Spec .ExternallyProvisioned {
505
- log .Infof ("Setting BareMetalHost (%s/%s) ExternallyProvisioned spec" , bmh .Namespace , bmh .Name )
506
- bmh .Spec .ExternallyProvisioned = true
507
- }
508
- }
591
+ log .Infof ("BareMetalHost %s/%s PoweredOn status is: %s" , bmh .Namespace , bmh .Name , bmh .Status .PoweredOn )
509
592
if ! bmh .Spec .Online {
510
593
bmh .Spec .Online = true
511
594
log .Infof ("Setting BareMetalHost (%s/%s) spec.Online to true" , bmh .Namespace , bmh .Name )
@@ -539,7 +622,7 @@ func (r *ImageClusterInstallReconciler) ensureBMHDataImage(
539
622
return dataImage , ctrl.Result {}, nil
540
623
}
541
624
542
- if err != nil && ! errors .IsNotFound (err ) {
625
+ if err != nil && ! k8sapierrors .IsNotFound (err ) {
543
626
return dataImage , ctrl.Result {}, err
544
627
}
545
628
log .Infof ("creating new dataImage for BareMetalHost (%s/%s)" , bmh .Name , bmh .Namespace )
@@ -600,7 +683,7 @@ func (r *ImageClusterInstallReconciler) removeBMHDataImage(ctx context.Context,
600
683
601
684
bmh := & bmh_v1alpha1.BareMetalHost {}
602
685
if err := r .Get (ctx , bmhRef , bmh ); err != nil {
603
- if errors .IsNotFound (err ) {
686
+ if k8sapierrors .IsNotFound (err ) {
604
687
log .Warnf ("Referenced BareMetalHost %s/%s does not exist, not waiting for dataImage deletion" , bmhRef .Namespace , bmhRef .Name )
605
688
return nil , nil
606
689
} else {
@@ -634,7 +717,7 @@ func (r *ImageClusterInstallReconciler) deleteDataImage(ctx context.Context, log
634
717
dataImage := & bmh_v1alpha1.DataImage {}
635
718
636
719
if err := r .Get (ctx , dataImageRef , dataImage ); err != nil {
637
- if errors .IsNotFound (err ) {
720
+ if k8sapierrors .IsNotFound (err ) {
638
721
log .Infof ("Can't find DataImage from BareMetalHost %s/%s, Nothing to remove" , dataImageRef .Namespace , dataImageRef .Name )
639
722
return nil , nil
640
723
}
0 commit comments