Skip to content

Commit c297f35

Browse files
committed
Refactors phases to functions
1 parent dc8e95a commit c297f35

File tree

3 files changed

+147
-62
lines changed

3 files changed

+147
-62
lines changed

api/v1alpha1/imageclusterinstall_types.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ import (
2626

2727
const (
2828
ConfigurationPendingReason = "ConfigurationPending"
29-
ConfigurationFailedReason = "ConfigurationFailed"
29+
ConfigurationFailedReason = "ConfigurationFailed"
3030

31-
ImageCreationFailedReason = "ImageCreationFailed"
31+
ImageCreationFailedReason = "ImageCreationFailed"
3232
ImageCreationPendingReason = "ImageCreationPending"
3333

3434
HostConfigurationPendingReason = "HostConfigurationPending"

controllers/imageclusterinstall_controller.go

Lines changed: 136 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
_ "crypto/sha512"
2727
"encoding/base64"
2828
"encoding/json"
29+
"errors"
2930
"fmt"
3031
"net"
3132
"net/url"
@@ -37,7 +38,7 @@ import (
3738

3839
"gopkg.in/yaml.v3"
3940
corev1 "k8s.io/api/core/v1"
40-
"k8s.io/apimachinery/pkg/api/errors"
41+
k8sapierrors "k8s.io/apimachinery/pkg/api/errors"
4142
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
4243
"k8s.io/apimachinery/pkg/runtime"
4344
"k8s.io/apimachinery/pkg/types"
@@ -161,7 +162,6 @@ func (r *ImageClusterInstallReconciler) Reconcile(ctx context.Context, req ctrl.
161162
return ctrl.Result{}, err
162163
}
163164

164-
/////// Requirements not met yet: setting defaults //////
165165
cond := hivev1.ClusterInstallCondition{
166166
Type: hivev1.ClusterInstallRequirementsMet,
167167
Status: corev1.ConditionFalse,
@@ -172,31 +172,101 @@ func (r *ImageClusterInstallReconciler) Reconcile(ctx context.Context, req ctrl.
172172
r.setRequirementsMetCondition(ctx, ici, cond.Status, cond.Reason, cond.Message)
173173
}()
174174

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.
176181
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) {
177247

178248
if ici.Spec.ClusterDeploymentRef == nil || ici.Spec.ClusterDeploymentRef.Name == "" {
179249
cond.Message = "ClusterDeploymentRef is unset"
180-
return ctrl.Result{}, nil
250+
return nil, nil, errors.New(cond.Message)
181251
}
182252

183-
clusterDeployment, err := r.getCD(ctx, ici)
253+
cd, err := r.getCD(ctx, ici)
184254
if err != nil {
185255
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
188258
}
189259

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)
193263
}
194264

195265
bmh, err := r.getBMH(ctx, ici.Spec.BareMetalHostRef)
196266
if err != nil {
197267
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
200270
}
201271

202272
// 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.
208278
cond.Reason = v1alpha1.ConfigurationFailedReason
209279
cond.Message = fmt.Sprintf("failed to disable automated cleaning mode for BareMetalHost %s/%s", bmh.Namespace, bmh.Name)
210280
log.WithError(err).Error(cond.Message)
211-
return ctrl.Result{}, err
281+
return nil, nil, err
212282
}
213283
}
214284

215-
/////// Requirements not met yet: config validated, starting host validation //////
216-
cond.Reason = v1alpha1.HostValidationFailedReason
285+
return cd, bmh, nil
286+
}
217287

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 {
219298
return res, err
220299
}
221300

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+
226309
}
227310

228-
/////// Requirements not met yet: host validated, starting image creation //////
229-
cond.Reason = v1alpha1.ImageCreationFailedReason
311+
return ctrl.Result{}, nil
312+
}
230313

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)
232325
if !res.IsZero() || err != nil {
233326
cond.Reason = v1alpha1.ImageCreationPendingReason
234327
cond.Message = "could not acquire lock for image data"
@@ -237,20 +330,26 @@ func (r *ImageClusterInstallReconciler) Reconcile(ctx context.Context, req ctrl.
237330
cond.Message = "failed to create image"
238331
log.WithError(err).Error(cond.Message)
239332
}
240-
return res, err
333+
return "", res, err
241334
}
242335

243-
r.labelReferencedObjectsForBackup(ctx, log, ici, clusterDeployment)
244-
245336
imageUrl, err := url.JoinPath(r.BaseURL, "images", req.Namespace, fmt.Sprintf("%s.iso", ici.ObjectMeta.UID))
246337
if err != nil {
247338
cond.Message = "failed to create image url"
248339
log.WithError(err).Error(cond.Message)
249-
return ctrl.Result{}, err
340+
return "", ctrl.Result{}, err
250341
}
251342

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) {
254353

255354
dataImage, res, err := r.ensureBMHDataImage(ctx, log, bmh, imageUrl)
256355
if err != nil {
@@ -267,8 +366,8 @@ func (r *ImageClusterInstallReconciler) Reconcile(ctx context.Context, req ctrl.
267366
// in case the dataImage was created less than a second ago requeue to allow BMO some time to get
268367
// notified about the newly created DataImage before adding the reboot annotation in updateBMHProvisioningState
269368
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)
272371
}
273372

274373
if err := r.updateBMHProvisioningState(ctx, log, bmh, dataImage); err != nil {
@@ -279,9 +378,9 @@ func (r *ImageClusterInstallReconciler) Reconcile(ctx context.Context, req ctrl.
279378
if !annotationExists(&bmh.ObjectMeta, ibioManagedBMH) {
280379
// TODO: consider replacing this condition with `dataDisk.Status.AttachedImage`
281380
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)
283382
log.Info(cond.Message)
284-
return ctrl.Result{}, nil
383+
return ctrl.Result{}, errors.New(cond.Message)
285384
}
286385

287386
if ici.Status.BareMetalHostRef == nil {
@@ -298,20 +397,10 @@ func (r *ImageClusterInstallReconciler) Reconcile(ctx context.Context, req ctrl.
298397
}
299398
}
300399

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-
306400
return ctrl.Result{}, nil
307401
}
308402

309-
func GetClusterConfigDir(namespacesDir, namespace, uid string) string {
310-
return filepath.Join(namespacesDir, namespace, uid, FilesDir, ClusterConfigDir)
311-
}
312-
313403
func (r *ImageClusterInstallReconciler) validateBMH(
314-
ctx context.Context,
315404
ici *v1alpha1.ImageClusterInstall,
316405
bmh *bmh_v1alpha1.BareMetalHost,
317406
cond *hivev1.ClusterInstallCondition) (ctrl.Result, error) {
@@ -499,13 +588,7 @@ func (r *ImageClusterInstallReconciler) updateBMHProvisioningState(ctx context.C
499588
if bmh.Status.Provisioning.State != bmh_v1alpha1.StateAvailable && bmh.Status.Provisioning.State != bmh_v1alpha1.StateExternallyProvisioned {
500589
return nil
501590
}
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)
509592
if !bmh.Spec.Online {
510593
bmh.Spec.Online = true
511594
log.Infof("Setting BareMetalHost (%s/%s) spec.Online to true", bmh.Namespace, bmh.Name)
@@ -539,7 +622,7 @@ func (r *ImageClusterInstallReconciler) ensureBMHDataImage(
539622
return dataImage, ctrl.Result{}, nil
540623
}
541624

542-
if err != nil && !errors.IsNotFound(err) {
625+
if err != nil && !k8sapierrors.IsNotFound(err) {
543626
return dataImage, ctrl.Result{}, err
544627
}
545628
log.Infof("creating new dataImage for BareMetalHost (%s/%s)", bmh.Name, bmh.Namespace)
@@ -600,7 +683,7 @@ func (r *ImageClusterInstallReconciler) removeBMHDataImage(ctx context.Context,
600683

601684
bmh := &bmh_v1alpha1.BareMetalHost{}
602685
if err := r.Get(ctx, bmhRef, bmh); err != nil {
603-
if errors.IsNotFound(err) {
686+
if k8sapierrors.IsNotFound(err) {
604687
log.Warnf("Referenced BareMetalHost %s/%s does not exist, not waiting for dataImage deletion", bmhRef.Namespace, bmhRef.Name)
605688
return nil, nil
606689
} else {
@@ -634,7 +717,7 @@ func (r *ImageClusterInstallReconciler) deleteDataImage(ctx context.Context, log
634717
dataImage := &bmh_v1alpha1.DataImage{}
635718

636719
if err := r.Get(ctx, dataImageRef, dataImage); err != nil {
637-
if errors.IsNotFound(err) {
720+
if k8sapierrors.IsNotFound(err) {
638721
log.Infof("Can't find DataImage from BareMetalHost %s/%s, Nothing to remove", dataImageRef.Namespace, dataImageRef.Name)
639722
return nil, nil
640723
}

controllers/imageclusterinstall_controller_test.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -997,7 +997,8 @@ var _ = Describe("Reconcile", func() {
997997
}
998998
installerSuccess()
999999
res, err := r.Reconcile(ctx, req)
1000-
Expect(err).NotTo(HaveOccurred())
1000+
Expect(err).To(HaveOccurred())
1001+
Expect(err.Error()).To(ContainSubstring("current state is: registering"))
10011002
Expect(res).To(Equal(ctrl.Result{}))
10021003

10031004
key := types.NamespacedName{
@@ -1295,15 +1296,15 @@ var _ = Describe("Reconcile", func() {
12951296
Expect(res).To(Equal(ctrl.Result{}))
12961297
})
12971298

1298-
It("doesn't error when ClusterDeploymentRef is unset", func() {
1299+
It("sets the ClusterInstallRequirementsMet condition to false when ClusterDeploymentRef is unset", func() {
12991300
clusterInstall.Spec.ClusterDeploymentRef = nil
13001301
Expect(c.Create(ctx, clusterInstall)).To(Succeed())
13011302
key := types.NamespacedName{
13021303
Namespace: clusterInstallNamespace,
13031304
Name: clusterInstallName,
13041305
}
13051306
res, err := r.Reconcile(ctx, ctrl.Request{NamespacedName: key})
1306-
Expect(err).NotTo(HaveOccurred())
1307+
Expect(err).To(HaveOccurred())
13071308
Expect(res).To(Equal(ctrl.Result{}))
13081309
Expect(c.Get(ctx, key, clusterInstall)).To(Succeed())
13091310
cond := findCondition(clusterInstall.Status.Conditions, hivev1.ClusterInstallRequirementsMet)
@@ -1323,15 +1324,15 @@ var _ = Describe("Reconcile", func() {
13231324
Name: clusterInstallName,
13241325
}
13251326
res, err := r.Reconcile(ctx, ctrl.Request{NamespacedName: key})
1326-
Expect(err).NotTo(HaveOccurred())
1327+
Expect(err).To(HaveOccurred())
13271328
Expect(res).To(Equal(ctrl.Result{}))
13281329

13291330
Expect(c.Get(ctx, key, clusterInstall)).To(Succeed())
13301331
cond := findCondition(clusterInstall.Status.Conditions, hivev1.ClusterInstallRequirementsMet)
13311332
Expect(cond).NotTo(BeNil())
13321333
Expect(cond.Status).To(Equal(corev1.ConditionFalse))
13331334
Expect(cond.Reason).To(Equal(v1alpha1.ConfigurationPendingReason))
1334-
Expect(cond.Message).To(Equal("no BareMetalHostRef set, nothing to do without provided bmh"))
1335+
Expect(cond.Message).To(Equal("BareMetalHostRef is unset"))
13351336
})
13361337

13371338
It("Set ClusterInstallRequirementsMet to false in case there is not actual bmh under the reference", func() {
@@ -1538,7 +1539,7 @@ var _ = Describe("Reconcile", func() {
15381539
Expect(err).To(HaveOccurred())
15391540
})
15401541

1541-
It("reque in case bmh has no hw details but after adding them it succeeds", func() {
1542+
It("requeue in case bmh has no hw details but after adding them it succeeds", func() {
15421543
bmh := bmhInState(bmh_v1alpha1.StateAvailable)
15431544
bmh.Status.HardwareDetails = nil
15441545
Expect(c.Create(ctx, bmh)).To(Succeed())
@@ -1959,7 +1960,8 @@ var _ = Describe("Reconcile with DataImageCoolDownPeriod set to 1 second", func(
19591960
}
19601961
installerSuccess()
19611962
res, err := r.Reconcile(ctx, req)
1962-
Expect(err).NotTo(HaveOccurred())
1963+
Expect(err).To(HaveOccurred())
1964+
Expect(err.Error()).To(ContainSubstring("waiting for DataImage to cool down"))
19631965
Expect(res).To(Equal(ctrl.Result{RequeueAfter: time.Second}))
19641966
time.Sleep(time.Second)
19651967
res, err = r.Reconcile(ctx, req)

0 commit comments

Comments
 (0)