diff --git a/controllers/common.go b/controllers/common.go new file mode 100644 index 000000000..eabbbafc5 --- /dev/null +++ b/controllers/common.go @@ -0,0 +1,126 @@ +/* +Copyright 2023. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controllers + +import ( + "context" + "fmt" + + k8sapierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + + bmh_v1alpha1 "github.com/metal3-io/baremetal-operator/apis/metal3.io/v1alpha1" + "github.com/sirupsen/logrus" + + "github.com/openshift/image-based-install-operator/api/v1alpha1" +) + +func getDataImage(ctx context.Context, c client.Client, namespace, name string) (*bmh_v1alpha1.DataImage, error) { + dataImage := &bmh_v1alpha1.DataImage{} + key := types.NamespacedName{Name: name, Namespace: namespace} + if err := c.Get(ctx, key, dataImage); err != nil { + return nil, err + } + return dataImage, nil +} + +func deleteDataImage(ctx context.Context, c client.Client, log logrus.FieldLogger, dataImageRef types.NamespacedName) (*bmh_v1alpha1.DataImage, error) { + dataImage := &bmh_v1alpha1.DataImage{} + if err := c.Get(ctx, dataImageRef, dataImage); err != nil { + if k8sapierrors.IsNotFound(err) { + log.Infof("Can't find DataImage from BareMetalHost %s/%s, Nothing to remove", dataImageRef.Namespace, dataImageRef.Name) + return nil, nil + } + return nil, fmt.Errorf("failed to get DataImage %s/%s: %w", dataImageRef.Namespace, dataImageRef.Name, err) + } + + log.Infof("Deleting DataImage %s/%s, deletion may take some time since a BMH restart is required", dataImageRef.Namespace, dataImageRef.Name) + if err := c.Delete(ctx, dataImage); err != nil { + return dataImage, err + } + return dataImage, nil +} + +func removeBMHDataImage(ctx context.Context, c client.Client, log logrus.FieldLogger, bmhRef types.NamespacedName) (*bmh_v1alpha1.DataImage, error) { + dataImage, err := deleteDataImage(ctx, c, log, bmhRef) + if err != nil || dataImage == nil { + return dataImage, err + } + + bmh := &bmh_v1alpha1.BareMetalHost{} + if err := c.Get(ctx, bmhRef, bmh); err != nil { + if k8sapierrors.IsNotFound(err) { + log.Warnf("Referenced BareMetalHost %s/%s does not exist, not waiting for dataImage deletion", bmhRef.Namespace, bmhRef.Name) + return nil, nil + } + return dataImage, fmt.Errorf("failed to get BareMetalHost %s/%s: %w", bmhRef.Namespace, bmhRef.Name, err) + } + return dataImage, attachAndRebootBMH(ctx, c, log, bmh) +} + +func attachAndRebootBMH(ctx context.Context, c client.Client, log logrus.FieldLogger, bmh *bmh_v1alpha1.BareMetalHost) error { + patch := client.MergeFrom(bmh.DeepCopy()) + dirty := false + if annotationExists(&bmh.ObjectMeta, detachedAnnotation) { + log.Infof("Removing Detached annotation if exists on BareMetalHost %s/%s", bmh.Namespace, bmh.Name) + delete(bmh.ObjectMeta.Annotations, detachedAnnotation) + dirty = true + } + + if setAnnotationIfNotExists(&bmh.ObjectMeta, rebootAnnotation, rebootAnnotationValue) { + log.Infof("Adding reboot annotation to BareMetalHost %s/%s", bmh.Namespace, bmh.Name) + dirty = true + } + if dirty { + return c.Patch(ctx, bmh, patch) + } + return nil +} + +func getBMH(ctx context.Context, c client.Client, bmhRef *v1alpha1.BareMetalHostReference) (*bmh_v1alpha1.BareMetalHost, error) { + bmh := &bmh_v1alpha1.BareMetalHost{} + key := types.NamespacedName{ + Name: bmhRef.Name, + Namespace: bmhRef.Namespace, + } + if err := c.Get(ctx, key, bmh); err != nil { + return nil, err + } + + return bmh, nil +} + +func setAnnotationIfNotExists(meta *metav1.ObjectMeta, key string, value string) bool { + if meta.Annotations == nil { + meta.Annotations = make(map[string]string) + } + if _, ok := meta.Annotations[key]; !ok { + meta.Annotations[key] = value + return true + } + return false +} + +func annotationExists(meta *metav1.ObjectMeta, key string) bool { + if meta.Annotations == nil { + return false + } + _, ok := meta.Annotations[key] + return ok +} diff --git a/controllers/imageclusterinstall_controller.go b/controllers/imageclusterinstall_controller.go index c16358480..f84a9e279 100644 --- a/controllers/imageclusterinstall_controller.go +++ b/controllers/imageclusterinstall_controller.go @@ -292,7 +292,7 @@ func (r *ImageClusterInstallReconciler) validateConfiguration( return nil, nil } - bmh, err := r.getBMH(ctx, ici.Spec.BareMetalHostRef) + bmh, err := getBMH(ctx, r.Client, ici.Spec.BareMetalHostRef) if err != nil { cond.Message = fmt.Sprintf("failed to get BareMetalHost %s/%s", ici.Spec.BareMetalHostRef.Namespace, ici.Spec.BareMetalHostRef.Name) log.Error(err) @@ -702,16 +702,6 @@ func (r *ImageClusterInstallReconciler) addIndexforBaremetalHostRef(mgr ctrl.Man return nil } -func (r *ImageClusterInstallReconciler) getDataImage(ctx context.Context, namespace, name string) (*bmh_v1alpha1.DataImage, error) { - dataImage := bmh_v1alpha1.DataImage{} - key := client.ObjectKey{ - Name: name, - Namespace: namespace, - } - err := r.Get(ctx, key, &dataImage) - return &dataImage, err -} - func isInspectionEnabled(bmh *bmh_v1alpha1.BareMetalHost) bool { if bmh.ObjectMeta.Annotations != nil && bmh.ObjectMeta.Annotations[inspectAnnotation] != "disabled" { return true @@ -755,7 +745,7 @@ func (r *ImageClusterInstallReconciler) ensureBMHDataImage( log logrus.FieldLogger, bmh *bmh_v1alpha1.BareMetalHost, url string) (*bmh_v1alpha1.DataImage, ctrl.Result, error) { - dataImage, err := r.getDataImage(ctx, bmh.Namespace, bmh.Name) + dataImage, err := getDataImage(ctx, r.Client, bmh.Namespace, bmh.Name) if err == nil { if !dataImage.ObjectMeta.DeletionTimestamp.IsZero() { log.Errorf("dataImage %s/%s already exists but is being deleted, probably leftover from previous installation", bmh.Namespace, bmh.Name) @@ -789,7 +779,7 @@ func (r *ImageClusterInstallReconciler) ensureBMHDataImage( return dataImage, ctrl.Result{}, fmt.Errorf("failed to create dataImage due to %w", err) } - dataImage, err = r.getDataImage(ctx, bmh.Namespace, bmh.Name) + dataImage, err = getDataImage(ctx, r.Client, bmh.Namespace, bmh.Name) return dataImage, ctrl.Result{}, err } @@ -805,75 +795,6 @@ func (r *ImageClusterInstallReconciler) getCD(ctx context.Context, ici *v1alpha1 return clusterDeployment, nil } -func (r *ImageClusterInstallReconciler) getBMH(ctx context.Context, bmhRef *v1alpha1.BareMetalHostReference) (*bmh_v1alpha1.BareMetalHost, error) { - bmh := &bmh_v1alpha1.BareMetalHost{} - key := types.NamespacedName{ - Name: bmhRef.Name, - Namespace: bmhRef.Namespace, - } - if err := r.Get(ctx, key, bmh); err != nil { - return nil, err - } - - return bmh, nil -} - -func (r *ImageClusterInstallReconciler) removeBMHDataImage(ctx context.Context, log logrus.FieldLogger, bmhRef types.NamespacedName) (*bmh_v1alpha1.DataImage, error) { - dataImage, err := r.deleteDataImage(ctx, log, bmhRef) - if err != nil || dataImage == nil { - return dataImage, err - } - - bmh := &bmh_v1alpha1.BareMetalHost{} - if err := r.Get(ctx, bmhRef, bmh); err != nil { - if k8sapierrors.IsNotFound(err) { - log.Warnf("Referenced BareMetalHost %s/%s does not exist, not waiting for dataImage deletion", bmhRef.Namespace, bmhRef.Name) - return nil, nil - } else { - return dataImage, fmt.Errorf("failed to get BareMetalHost %s/%s: %w", bmhRef.Namespace, bmhRef.Name, err) - } - } - return dataImage, r.attachAndRebootBMH(ctx, log, bmh) -} - -func (r *ImageClusterInstallReconciler) attachAndRebootBMH(ctx context.Context, log logrus.FieldLogger, bmh *bmh_v1alpha1.BareMetalHost) error { - patch := client.MergeFrom(bmh.DeepCopy()) - dirty := false - if annotationExists(&bmh.ObjectMeta, detachedAnnotation) { - log.Infof("Removing Detached annotation if exists on BareMetalHost %s/%s", bmh.Namespace, bmh.Name) - delete(bmh.ObjectMeta.Annotations, detachedAnnotation) - dirty = true - } - - if setAnnotationIfNotExists(&bmh.ObjectMeta, rebootAnnotation, rebootAnnotationValue) { - log.Infof("Adding reboot annotation to BareMetalHost %s/%s", bmh.Namespace, bmh.Name) - dirty = true - } - if dirty { - return r.Patch(ctx, bmh, patch) - - } - return nil -} - -func (r *ImageClusterInstallReconciler) deleteDataImage(ctx context.Context, log logrus.FieldLogger, dataImageRef types.NamespacedName) (*bmh_v1alpha1.DataImage, error) { - dataImage := &bmh_v1alpha1.DataImage{} - - if err := r.Get(ctx, dataImageRef, dataImage); err != nil { - if k8sapierrors.IsNotFound(err) { - log.Infof("Can't find DataImage from BareMetalHost %s/%s, Nothing to remove", dataImageRef.Namespace, dataImageRef.Name) - return nil, nil - } - return nil, fmt.Errorf("failed to get DataImage %s/%s: %w", dataImageRef.Namespace, dataImageRef.Name, err) - } - - log.Infof("Deleting DataImage %s/%s, deletion may take some time since a BMH restart is required", dataImageRef.Namespace, dataImageRef.Name) - if err := r.Client.Delete(ctx, dataImage); err != nil { - return dataImage, err - } - return dataImage, nil -} - func setBackupLabel(obj client.Object) bool { labels := obj.GetLabels() if labels == nil { @@ -915,7 +836,7 @@ func (r *ImageClusterInstallReconciler) labelSecretForBackup(ctx context.Context } func (r *ImageClusterInstallReconciler) labelBMHForBackup(ctx context.Context, bmhRef *v1alpha1.BareMetalHostReference) error { - bmh, err := r.getBMH(ctx, bmhRef) + bmh, err := getBMH(ctx, r.Client, bmhRef) if err != nil { return err } @@ -928,7 +849,7 @@ func (r *ImageClusterInstallReconciler) labelBMHForBackup(ctx context.Context, b } func (r *ImageClusterInstallReconciler) labelDataImageForBackup(ctx context.Context, bmhRef *v1alpha1.BareMetalHostReference) error { - dataImage, err := r.getDataImage(ctx, bmhRef.Namespace, bmhRef.Name) + dataImage, err := getDataImage(ctx, r.Client, bmhRef.Namespace, bmhRef.Name) if err != nil { return err } @@ -1418,7 +1339,7 @@ func (r *ImageClusterInstallReconciler) handleFinalizer(ctx context.Context, log Namespace: ici.Spec.BareMetalHostRef.Namespace, } - dataImage, err := r.removeBMHDataImage(ctx, log, key) + dataImage, err := removeBMHDataImage(ctx, r.Client, log, key) if err != nil { return ctrl.Result{}, true, fmt.Errorf("failed to delete DataImage %s/%s: %w", key.Namespace, key.Name, err) } @@ -1476,25 +1397,6 @@ func (r *ImageClusterInstallReconciler) writeIBIOStartTimeCM(filePath string) er return nil } -func setAnnotationIfNotExists(meta *metav1.ObjectMeta, key string, value string) bool { - if meta.Annotations == nil { - meta.Annotations = make(map[string]string) - } - if _, ok := meta.Annotations[key]; !ok { - meta.Annotations[key] = value - return true - } - return false -} - -func annotationExists(meta *metav1.ObjectMeta, key string) bool { - if meta.Annotations == nil { - return false - } - _, ok := meta.Annotations[key] - return ok -} - func ipInCidr(ipAddr, cidr string) (bool, error) { _, ipNet, err := net.ParseCIDR(cidr) if err != nil { diff --git a/controllers/imageclusterinstall_monitor.go b/controllers/imageclusterinstall_monitor.go index 9e80a449e..79614d4a1 100644 --- a/controllers/imageclusterinstall_monitor.go +++ b/controllers/imageclusterinstall_monitor.go @@ -26,6 +26,7 @@ import ( "time" corev1 "k8s.io/api/core/v1" + k8sapierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" utilruntime "k8s.io/apimachinery/pkg/util/runtime" @@ -38,9 +39,10 @@ import ( bmh_v1alpha1 "github.com/metal3-io/baremetal-operator/apis/metal3.io/v1alpha1" apicfgv1 "github.com/openshift/api/config/v1" + "github.com/sirupsen/logrus" + "github.com/openshift/image-based-install-operator/api/v1alpha1" "github.com/openshift/image-based-install-operator/internal/monitor" - "github.com/sirupsen/logrus" ) // ImageClusterInstallMonitor reconciles a ImageClusterInstall object @@ -57,6 +59,11 @@ type ImageClusterInstallMonitor struct { //+kubebuilder:rbac:groups=extensions.hive.openshift.io,resources=imageclusterinstalls,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups=extensions.hive.openshift.io,resources=imageclusterinstalls/status,verbs=get;update;patch //+kubebuilder:rbac:groups=metal3.io,resources=baremetalhosts,verbs=get;list;watch;update;patch +//+kubebuilder:rbac:groups=metal3.io,resources=dataimages,verbs=get;list;watch;delete + +const ( + dataImageRemovalPendingMessage = "Waiting for DataImage to be deleted" +) func (r *ImageClusterInstallMonitor) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { log := r.Log.WithFields(logrus.Fields{"name": req.Name, "namespace": req.Namespace}) @@ -85,7 +92,7 @@ func (r *ImageClusterInstallMonitor) monitorInstallationProgress( log logrus.FieldLogger, ici *v1alpha1.ImageClusterInstall) (ctrl.Result, error) { - bmh, err := r.getBMH(ctx, ici.Status.BareMetalHostRef) + bmh, err := getBMH(ctx, r.Client, ici.Status.BareMetalHostRef) if err != nil { log.WithError(err).Error("failed to get BareMetalHost") return ctrl.Result{}, err @@ -143,6 +150,13 @@ func (r *ImageClusterInstallMonitor) checkClusterStatus(ctx context.Context, log logrus.FieldLogger, ici *v1alpha1.ImageClusterInstall, bmh *bmh_v1alpha1.BareMetalHost) (ctrl.Result, error) { + bmhRef := types.NamespacedName{Name: bmh.Name, Namespace: bmh.Namespace} + + res, stop, err := r.handleDataImageDeletion(ctx, log, ici, bmhRef) + if stop || err != nil { + return res, err + } + spokeClient, err := r.spokeClient(ctx, ici) if err != nil { log.WithError(err).Error("failed to create spoke client") @@ -167,14 +181,15 @@ func (r *ImageClusterInstallMonitor) checkClusterStatus(ctx context.Context, } log.Info("cluster is installed") - // After installation ended we don't want that ironic will do any changes in the node - patch := client.MergeFrom(bmh.DeepCopy()) - if setAnnotationIfNotExists(&bmh.ObjectMeta, detachedAnnotation, detachedAnnotationValue) { - log.Infof("Adding detached annotations to BareMetalHost (%s/%s)", bmh.Name, bmh.Namespace) - if err := r.Patch(ctx, bmh, patch); err != nil { - return ctrl.Result{}, err - } + if _, err = removeBMHDataImage(ctx, r.Client, log, bmhRef); err != nil { + log.WithError(err).Error("failed to delete DataImage") + return ctrl.Result{}, err + } + res, stop, err = r.handleDataImageDeletion(ctx, log, ici, bmhRef) + if stop || err != nil { + return res, err } + if err := r.setClusterInstalledConditions(ctx, ici); err != nil { log.WithError(err).Error("failed to set installed conditions") return ctrl.Result{}, err @@ -182,6 +197,22 @@ func (r *ImageClusterInstallMonitor) checkClusterStatus(ctx context.Context, return ctrl.Result{}, nil } +func (r *ImageClusterInstallMonitor) handleDataImageDeletion(ctx context.Context, log logrus.FieldLogger, ici *v1alpha1.ImageClusterInstall, bmhRef types.NamespacedName) (ctrl.Result, bool, error) { + dataImage, err := getDataImage(ctx, r.Client, bmhRef.Namespace, bmhRef.Name) + if err != nil && !k8sapierrors.IsNotFound(err) { + log.WithError(err).Error("failed to get DataImage") + return ctrl.Result{}, true, err + } + if dataImage != nil && !dataImage.DeletionTimestamp.IsZero() { + log.Infof("Waiting for DataImage %s/%s to be deleted", bmhRef.Namespace, bmhRef.Name) + if err := r.setClusterInstallingConditions(ctx, ici, dataImageRemovalPendingMessage); err != nil { + log.WithError(err).Error("failed to set installing conditions") + } + return ctrl.Result{RequeueAfter: time.Minute}, true, nil + } + return ctrl.Result{}, false, nil +} + func (r *ImageClusterInstallMonitor) spokeClient(ctx context.Context, ici *v1alpha1.ImageClusterInstall) (client.Client, error) { if ici.Spec.ClusterMetadata == nil || ici.Spec.ClusterMetadata.AdminKubeconfigSecretRef.Name == "" { return nil, fmt.Errorf("kubeconfig secret must be set to get spoke client") @@ -254,16 +285,3 @@ func (r *ImageClusterInstallMonitor) SetupWithManager(mgr ctrl.Manager) error { WithEventFilter(bootTimeInitialized). Complete(r) } - -func (r *ImageClusterInstallMonitor) getBMH(ctx context.Context, bmhRef *v1alpha1.BareMetalHostReference) (*bmh_v1alpha1.BareMetalHost, error) { - bmh := &bmh_v1alpha1.BareMetalHost{} - key := types.NamespacedName{ - Name: bmhRef.Name, - Namespace: bmhRef.Namespace, - } - if err := r.Get(ctx, key, bmh); err != nil { - return nil, err - } - - return bmh, nil -} diff --git a/controllers/imageclusterinstall_monitor_test.go b/controllers/imageclusterinstall_monitor_test.go index c06bcba85..f0a5015ad 100644 --- a/controllers/imageclusterinstall_monitor_test.go +++ b/controllers/imageclusterinstall_monitor_test.go @@ -18,10 +18,11 @@ import ( bmh_v1alpha1 "github.com/metal3-io/baremetal-operator/apis/metal3.io/v1alpha1" hivev1 "github.com/openshift/hive/apis/hive/v1" + "github.com/sirupsen/logrus" + "github.com/openshift/image-based-install-operator/api/v1alpha1" "github.com/openshift/image-based-install-operator/internal/credentials" "github.com/openshift/image-based-install-operator/internal/monitor" - "github.com/sirupsen/logrus" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -163,6 +164,16 @@ var _ = Describe("Monitor", func() { It("sets conditions to cluster installed when the BMH is managed and cluster is ready", func() { r.GetSpokeClusterInstallStatus = monitor.SuccessMonitor + dataImage := &bmh_v1alpha1.DataImage{ + ObjectMeta: metav1.ObjectMeta{ + Name: bmh.Name, + Namespace: bmh.Namespace, + }, + Spec: bmh_v1alpha1.DataImageSpec{ + URL: "https://example.com/config.iso", + }, + } + Expect(c.Create(ctx, dataImage)).To(Succeed()) Expect(c.Create(ctx, clusterInstall)).To(Succeed()) Expect(c.Create(ctx, clusterDeployment)).To(Succeed()) @@ -186,9 +197,12 @@ var _ = Describe("Monitor", func() { Expect(cond).NotTo(BeNil()) Expect(cond.Status).To(Equal(corev1.ConditionTrue)) - By("Verify BMH was detached") + By("Verify DataImage was removed") + Expect(c.Get(ctx, types.NamespacedName{Namespace: bmh.Namespace, Name: bmh.Name}, &bmh_v1alpha1.DataImage{})).NotTo(Succeed()) + + By("Verify BMH was rebooted to complete DataImage removal") Expect(c.Get(ctx, types.NamespacedName{Namespace: bmh.Namespace, Name: bmh.Name}, bmh)).To(Succeed()) - Expect(bmh.Annotations[detachedAnnotation]).To(Equal(detachedAnnotationValue)) + Expect(bmh.Annotations).To(HaveKey(rebootAnnotation)) By("Verify that clusterInstall was not updated on second run") resourceVersion := clusterInstall.ResourceVersion @@ -208,6 +222,82 @@ var _ = Describe("Monitor", func() { Expect(bmh.ObjectMeta.ResourceVersion).To(Equal(resourceVersion)) }) + It("waits for DataImage deletion before reporting cluster installed", func() { + r.GetSpokeClusterInstallStatus = monitor.SuccessMonitor + dataImage := &bmh_v1alpha1.DataImage{ + ObjectMeta: metav1.ObjectMeta{ + Name: bmh.Name, + Namespace: bmh.Namespace, + Finalizers: []string{bmh_v1alpha1.DataImageFinalizer}, + }, + Spec: bmh_v1alpha1.DataImageSpec{ + URL: "https://example.com/config.iso", + }, + } + Expect(c.Create(ctx, dataImage)).To(Succeed()) + Expect(c.Delete(ctx, dataImage)).To(Succeed()) + Expect(c.Create(ctx, clusterInstall)).To(Succeed()) + Expect(c.Create(ctx, clusterDeployment)).To(Succeed()) + + key := types.NamespacedName{ + Namespace: clusterInstallNamespace, + Name: clusterInstallName, + } + res, err := r.Reconcile(ctx, ctrl.Request{NamespacedName: key}) + Expect(err).NotTo(HaveOccurred()) + Expect(res).To(Equal(ctrl.Result{RequeueAfter: time.Minute})) + + Expect(c.Get(ctx, key, clusterInstall)).To(Succeed()) + cond := findCondition(clusterInstall.Status.Conditions, hivev1.ClusterInstallStopped) + Expect(cond).NotTo(BeNil()) + Expect(cond.Status).To(Equal(corev1.ConditionFalse)) + Expect(cond.Message).To(Equal(dataImageRemovalPendingMessage)) + cond = findCondition(clusterInstall.Status.Conditions, hivev1.ClusterInstallCompleted) + Expect(cond).NotTo(BeNil()) + Expect(cond.Status).To(Equal(corev1.ConditionFalse)) + + By("Verify DataImage still exists while deletion is in progress") + Expect(c.Get(ctx, types.NamespacedName{Namespace: bmh.Namespace, Name: bmh.Name}, dataImage)).To(Succeed()) + }) + + It("reports DataImage removal while waiting for deletion after cluster install", func() { + r.GetSpokeClusterInstallStatus = monitor.SuccessMonitor + dataImage := &bmh_v1alpha1.DataImage{ + ObjectMeta: metav1.ObjectMeta{ + Name: bmh.Name, + Namespace: bmh.Namespace, + Finalizers: []string{bmh_v1alpha1.DataImageFinalizer}, + }, + Spec: bmh_v1alpha1.DataImageSpec{ + URL: "https://example.com/config.iso", + }, + } + Expect(c.Create(ctx, dataImage)).To(Succeed()) + Expect(c.Create(ctx, clusterInstall)).To(Succeed()) + Expect(c.Create(ctx, clusterDeployment)).To(Succeed()) + + key := types.NamespacedName{ + Namespace: clusterInstallNamespace, + Name: clusterInstallName, + } + res, err := r.Reconcile(ctx, ctrl.Request{NamespacedName: key}) + Expect(err).NotTo(HaveOccurred()) + Expect(res).To(Equal(ctrl.Result{RequeueAfter: time.Minute})) + + Expect(c.Get(ctx, key, clusterInstall)).To(Succeed()) + cond := findCondition(clusterInstall.Status.Conditions, hivev1.ClusterInstallStopped) + Expect(cond).NotTo(BeNil()) + Expect(cond.Status).To(Equal(corev1.ConditionFalse)) + Expect(cond.Message).To(Equal(dataImageRemovalPendingMessage)) + cond = findCondition(clusterInstall.Status.Conditions, hivev1.ClusterInstallCompleted) + Expect(cond).NotTo(BeNil()) + Expect(cond.Status).To(Equal(corev1.ConditionFalse)) + + By("Verify DataImage deletion was initiated") + Expect(c.Get(ctx, types.NamespacedName{Namespace: bmh.Namespace, Name: bmh.Name}, dataImage)).To(Succeed()) + Expect(dataImage.DeletionTimestamp).NotTo(BeNil()) + }) + It("requeues and sets conditions when spoke cluster is not ready yet", func() { r.GetSpokeClusterInstallStatus = monitor.FailureMonitor