Repository: openshift/assisted-service (language: python)

Issue:
MGMT-17313: Create day 2 BMH and Machine for control plane nodes
https://issues.redhat.com/browse/MGMT-17313
Previously, only day 2 workers had BMH and Machines created for them. We want the same behavior for day 2 control plane nodes (added in https://issues.redhat.com/browse/MGMT-8578) so that their CSRs will be approved automatically.

<!--
Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change.

You can refer to [Kubernetes community documentation] on writing good commit messages, which provides good tips and ideas.

Some PRs address specific issues. Please, refer to the [CONTRIBUTING] documentation for more
information on how to link a PR to an existing issue.

It's recommended to take a few extra minutes to provide more information about
how this code was tested. Here are some questions that may be worth answering:

- Should this PR be tested by the reviewer?
- Is this PR relying on CI for an e2e test run?
- Should this PR be tested in a specific environment?
- Any logs, screenshots, etc that can help with the review process?

-->

## List all the issues related to this PR

- [ ] New Feature <!-- new functionality -->
- [x] Enhancement <!-- refactor, code changes, improvement, that won't add new features -->
- [ ] Bug fix
- [ ] Tests
- [ ] Documentation
- [ ] CI/CD <!-- Notice that changes for Dockerfiles/Jenkinsfiles aren't tested in CI due to a known bug. -->

## What environments does this code impact?

- [ ] Automation (CI, tools, etc)
- [x] Cloud
- [x] Operator Managed Deployments
- [ ] None

## How was this code tested?

<!-- Please, select one or more if needed: -->

- [ ] assisted-test-infra environment
- [ ] dev-scripts environment
- [ ] Reviewer's test appreciated
- [x] Waiting for CI to do a full test run
- [ ] Manual (Elaborate on how it was tested)
- [ ] No tests needed

## Checklist

- [x] Title and description added to both, commit and PR.
- [x] Relevant issues have been associated (see [CONTRIBUTING] guide)
- [x] This change does not require a documentation update (docstring, `docs`, README, etc)
- [ ] Does this change include unit-tests (note that code changes require unit-tests)

## Reviewers Checklist

- Are the title and description (in both PR and commit) meaningful and clear?
- Is there a bug required (and linked) for this change?
- Should this PR be backported?

[Kubernetes community documentation]: https://github.com/kubernetes/community/blob/master/contributors/guide/pull-requests.md#commit-message-guidelines
[CONTRIBUTING]: https://github.com/openshift/assisted-service/blob/master/CONTRIBUTING.md



Discussion / hints:

Fix patch:
diff --git a/internal/controller/controllers/bmh_agent_controller.go b/internal/controller/controllers/bmh_agent_controller.go
index 0c5290a94b4..a76b447e641 100644
--- a/internal/controller/controllers/bmh_agent_controller.go
+++ b/internal/controller/controllers/bmh_agent_controller.go
@@ -926,23 +926,20 @@ func (r *BMACReconciler) reconcileBMH(ctx context.Context, log logrus.FieldLogge
 
 // Reconcile the `BareMetalHost` resource on the spoke cluster
 //
-// Baremetal-operator in the hub cluster creates a host using the live-iso feature. To add this host as a worker node
+// Baremetal-operator in the hub cluster creates a host using the live-iso feature. To add this host as a node
 // to an existing spoke cluster, the following things need to be done:
-// - Check agent to see if day2 flow needs to be triggered.
+// - Check cluster deployment to see if day2 flow needs to be triggered.
 // - Get the kubeconfig client from secret data
 // - Creates a new Machine
 // - Create BMH with externallyProvisioned set to true and set the newly created machine as ConsumerRef
 // BMH_HARDWARE_DETAILS_ANNOTATION is needed for auto approval of the CSR.
 func (r *BMACReconciler) reconcileSpokeBMH(ctx context.Context, log logrus.FieldLogger, bmh *bmh_v1alpha1.BareMetalHost, agent *aiv1beta1.Agent) reconcileResult {
-
-	if !r.validateWorkerForDay2(log, agent) {
-		return reconcileComplete{}
-	}
 	cd, installed, err := r.getClusterDeploymentAndCheckIfInstalled(ctx, log, agent)
 	if err != nil {
 		return reconcileError{err: err}
 	}
 	if !installed {
+		log.Infof("Skipping spoke BareMetalHost reconcile for agent %s/%s since it's not day2.", agent.Name, agent.Namespace)
 		return reconcileComplete{}
 	}
 
@@ -1006,7 +1003,7 @@ func (r *BMACReconciler) reconcileSpokeBMH(ctx context.Context, log logrus.Field
 		return reconcileError{err: err}
 	}
 
-	_, err = r.ensureSpokeMachine(ctx, log, spokeClient, bmh, cd, machineNSName, checksum, url)
+	_, err = r.ensureSpokeMachine(ctx, log, spokeClient, bmh, cd, machineNSName, checksum, url, string(agent.Status.Role))
 	if err != nil {
 		log.WithError(err).Errorf("failed to create or update spoke Machine")
 		return reconcileError{err: err}
@@ -1225,8 +1222,8 @@ func (r *BMACReconciler) getChecksumAndURL(ctx context.Context, spokeClient clie
 	return checksum, url, err, false
 }
 
-func (r *BMACReconciler) ensureSpokeMachine(ctx context.Context, log logrus.FieldLogger, spokeClient client.Client, bmh *bmh_v1alpha1.BareMetalHost, clusterDeployment *hivev1.ClusterDeployment, machineName types.NamespacedName, checksum string, URL string) (*machinev1beta1.Machine, error) {
-	machineSpoke, mutateFn := r.newSpokeMachine(bmh, clusterDeployment, machineName, checksum, URL)
+func (r *BMACReconciler) ensureSpokeMachine(ctx context.Context, log logrus.FieldLogger, spokeClient client.Client, bmh *bmh_v1alpha1.BareMetalHost, clusterDeployment *hivev1.ClusterDeployment, machineName types.NamespacedName, checksum, URL, role string) (*machinev1beta1.Machine, error) {
+	machineSpoke, mutateFn := r.newSpokeMachine(bmh, clusterDeployment, machineName, checksum, URL, role)
 	if result, err := controllerutil.CreateOrUpdate(ctx, spokeClient, machineSpoke, mutateFn); err != nil {
 		return nil, err
 	} else if result != controllerutil.OperationResultNone {
@@ -1236,19 +1233,17 @@ func (r *BMACReconciler) ensureSpokeMachine(ctx context.Context, log logrus.Fiel
 }
 
 // Get MCS cert from the spoke cluster, encode it and add it to BMH
-// To increase the capacity of the spoke cluster on day2, while adding remote worker nodes to the spoke cluster,
+// To increase the capacity of the spoke cluster on day2, while adding day2 nodes to the spoke cluster,
 // we need to pull the MCS certificate from the spoke cluster and inject it as part of the ignition config
 // before the node boots so that there is no failure due to Certificate signed by Unknown Authority
 // Ref: https://access.redhat.com/solutions/4799921
 func (r *BMACReconciler) ensureMCSCert(ctx context.Context, log logrus.FieldLogger, bmh *bmh_v1alpha1.BareMetalHost, agent *aiv1beta1.Agent) reconcileResult {
-	if !r.validateWorkerForDay2(log, agent) {
-		return reconcileComplete{}
-	}
 	cd, installed, err := r.getClusterDeploymentAndCheckIfInstalled(ctx, log, agent)
 	if err != nil {
 		return reconcileError{err: err}
 	}
 	if !installed {
+		log.Infof("Skipping spoke MCS cert for agent %s/%s since it's not day2.", agent.Name, agent.Namespace)
 		return reconcileComplete{}
 	}
 
@@ -1383,7 +1378,7 @@ func (r *BMACReconciler) newSpokeBMH(log logrus.FieldLogger, bmh *bmh_v1alpha1.B
 	return bmhSpoke, mutateFn
 }
 
-func (r *BMACReconciler) newSpokeMachine(bmh *bmh_v1alpha1.BareMetalHost, clusterDeployment *hivev1.ClusterDeployment, machineName types.NamespacedName, checksum string, URL string) (*machinev1beta1.Machine, controllerutil.MutateFn) {
+func (r *BMACReconciler) newSpokeMachine(bmh *bmh_v1alpha1.BareMetalHost, clusterDeployment *hivev1.ClusterDeployment, machineName types.NamespacedName, checksum, URL, role string) (*machinev1beta1.Machine, controllerutil.MutateFn) {
 	machine := &machinev1beta1.Machine{
 		ObjectMeta: metav1.ObjectMeta{
 			Name:      machineName.Name,
@@ -1425,8 +1420,8 @@ func (r *BMACReconciler) newSpokeMachine(bmh *bmh_v1alpha1.BareMetalHost, cluste
 
 		// Setting the same labels as the rest of the machines in the spoke cluster
 		machine.Labels = AddLabel(machine.Labels, machinev1beta1.MachineClusterIDLabel, clusterDeployment.Name)
-		machine.Labels = AddLabel(machine.Labels, MACHINE_ROLE, string(models.HostRoleWorker))
-		machine.Labels = AddLabel(machine.Labels, MACHINE_TYPE, string(models.HostRoleWorker))
+		machine.Labels = AddLabel(machine.Labels, MACHINE_ROLE, role)
+		machine.Labels = AddLabel(machine.Labels, MACHINE_TYPE, role)
 		return nil
 	}
 
@@ -1568,15 +1563,6 @@ func (r *BMACReconciler) formatMCSCertificateIgnition(mcsCert string) (string, e
 	return buf.String(), nil
 }
 
-func (r *BMACReconciler) validateWorkerForDay2(log logrus.FieldLogger, agent *aiv1beta1.Agent) bool {
-	// Only worker role is supported for day2 operation
-	if agent.Status.Role != models.HostRoleWorker || agent.Spec.ClusterDeploymentName == nil {
-		log.Debugf("Skipping spoke BareMetalHost reconcile for agent, role %s and clusterDeployment %s.", agent.Status.Role, agent.Spec.ClusterDeploymentName)
-		return false
-	}
-	return true
-}
-
 func (r *BMACReconciler) getClusterDeploymentAndCheckIfInstalled(ctx context.Context, log logrus.FieldLogger, agent *aiv1beta1.Agent) (*hivev1.ClusterDeployment, bool, error) {
 	if agent.Spec.ClusterDeploymentName == nil {
 		log.Debugf("Agent is not bound yet")


Test patch:
diff --git a/internal/controller/controllers/bmh_agent_controller_test.go b/internal/controller/controllers/bmh_agent_controller_test.go
index 539040b7839..26fa56197f5 100644
--- a/internal/controller/controllers/bmh_agent_controller_test.go
+++ b/internal/controller/controllers/bmh_agent_controller_test.go
@@ -1067,6 +1067,244 @@ var _ = Describe("bmac reconcile", func() {
 			Expect(c.Delete(ctx, infraEnv)).ShouldNot(HaveOccurred())
 		})
 
+		Context("Day 2 host", func() {
+			var host_day2 *bmh_v1alpha1.BareMetalHost
+			var agent_day2 *v1beta1.Agent
+			var infraEnv_day2 *v1beta1.InfraEnv
+			BeforeEach(func() {
+				macStr := "12-34-56-78-9A-AA"
+				isoImageURL := "http://buzz.lightyear.io/discovery-image.iso"
+				image := &bmh_v1alpha1.Image{URL: isoImageURL}
+				infraEnv_day2 = newInfraEnvImage("testInfraEnv-day2", testNamespace, v1beta1.InfraEnvSpec{})
+				infraEnv_day2.Status = v1beta1.InfraEnvStatus{ISODownloadURL: isoImageURL}
+				Expect(c.Create(ctx, infraEnv_day2)).To(BeNil())
+				bmhName = "bmh-reconcile-day2"
+				host_day2 = newBMH(bmhName, &bmh_v1alpha1.BareMetalHostSpec{Image: image, BootMACAddress: macStr, BMC: bmh_v1alpha1.BMCDetails{CredentialsName: fmt.Sprintf(adminKubeConfigStringTemplate, cluster.Name)}})
+				annotations := make(map[string]string)
+				annotations[BMH_AGENT_IGNITION_CONFIG_OVERRIDES] = `{"ignition":{"version":"3.1.0", "security": {"tls":{"certificateAuthorities":[{"source":"data:text/plain;charset=utf-8;base64,c29tZSBjZXJ0aWZpY2F0ZQ=="}]}}}}`
+
+				host_day2.ObjectMeta.SetAnnotations(annotations)
+				labels := make(map[string]string)
+				labels[BMH_INFRA_ENV_LABEL] = infraEnv.Name
+				host_day2.ObjectMeta.Labels = labels
+
+				Expect(c.Create(ctx, host_day2)).To(BeNil())
+
+				agent_day2 = newAgent("bmac-agent-day2", testNamespace, v1beta1.AgentSpec{Approved: true})
+				agent_day2.Status.Inventory = v1beta1.HostInventory{
+					Memory: v1beta1.HostMemory{
+						PhysicalBytes: 2,
+					},
+					Interfaces: []v1beta1.HostInterface{
+						{
+							Name: "eth0",
+							IPV4Addresses: []string{
+								"1.2.3.4",
+							},
+							IPV6Addresses: []string{
+								"1001:db8::10/120",
+							},
+							MacAddress: macStr,
+						},
+					},
+					Disks: []v1beta1.HostDisk{
+						{Path: "/dev/sda", Bootable: true},
+						{Path: "/dev/sdb", Bootable: false},
+					},
+				}
+				agent_day2.Status.Role = models.HostRoleWorker
+				agent_day2.Spec.ClusterDeploymentName = &v1beta1.ClusterReference{Name: cluster.Name, Namespace: testNamespace}
+				Expect(c.Create(ctx, agent_day2)).To(BeNil())
+
+			})
+			It("should create spoke BMH for day 2 host with worker role when it's installing - happy flow", func() {
+				agent_day2.Status.DebugInfo.State = models.HostStatusInstalling
+				Expect(c.Update(ctx, agent_day2)).To(BeNil())
+
+				configMap := &corev1.ConfigMap{
+					ObjectMeta: metav1.ObjectMeta{
+						Name:      "root-ca",
+						Namespace: "kube-system",
+					},
+					Data: map[string]string{
+						"ca.crt": BASIC_CERT,
+					},
+				}
+				Expect(bmhr.spokeClient.Create(ctx, configMap)).ShouldNot(HaveOccurred())
+				Expect(c.Update(context.Background(), agent_day2)).ShouldNot(HaveOccurred())
+				for range [3]int{} {
+					result, err := bmhr.Reconcile(ctx, newBMHRequest(host_day2))
+					Expect(err).To(BeNil())
+					Expect(result).To(Equal(ctrl.Result{}))
+				}
+
+				By("Checking if the BMH has the correct annotations")
+				updatedHost := &bmh_v1alpha1.BareMetalHost{}
+				err := c.Get(ctx, types.NamespacedName{Name: host_day2.Name, Namespace: testNamespace}, updatedHost)
+				Expect(err).To(BeNil())
+				Expect(updatedHost.ObjectMeta.Annotations).To(HaveKey(BMH_HARDWARE_DETAILS_ANNOTATION))
+				Expect(updatedHost.ObjectMeta.Annotations).To(HaveKey(BMH_DETACHED_ANNOTATION))
+				Expect(updatedHost.ObjectMeta.Annotations[BMH_DETACHED_ANNOTATION]).To(Equal("assisted-service-controller"))
+				Expect(updatedHost.ObjectMeta.Annotations).To(HaveKey(BMH_AGENT_IGNITION_CONFIG_OVERRIDES))
+				Expect(updatedHost.ObjectMeta.Annotations[BMH_AGENT_IGNITION_CONFIG_OVERRIDES]).NotTo(Equal(""))
+				Expect(updatedHost.ObjectMeta.Annotations[BMH_AGENT_IGNITION_CONFIG_OVERRIDES]).To(ContainSubstring("dGVzdA=="))
+
+				By("Checking the spoke BMH exists and is correct")
+				machineName := fmt.Sprintf("%s-%s", cluster.Name, host_day2.Name)
+				spokeBMH := &bmh_v1alpha1.BareMetalHost{}
+				spokeClient := bmhr.spokeClient
+				err = spokeClient.Get(ctx, types.NamespacedName{Name: host_day2.Name, Namespace: OPENSHIFT_MACHINE_API_NAMESPACE}, spokeBMH)
+				Expect(err).To(BeNil())
+				Expect(spokeBMH.ObjectMeta.Annotations).To(HaveKey(BMH_HARDWARE_DETAILS_ANNOTATION))
+				Expect(spokeBMH.ObjectMeta.Annotations[BMH_HARDWARE_DETAILS_ANNOTATION]).To(Equal(updatedHost.ObjectMeta.Annotations[BMH_HARDWARE_DETAILS_ANNOTATION]))
+				Expect(spokeBMH.ObjectMeta.Annotations).ToNot(HaveKey(BMH_DETACHED_ANNOTATION))
+				Expect(spokeBMH.ObjectMeta.Annotations[BMH_INSPECT_ANNOTATION]).To(Equal("disabled"))
+				Expect(spokeBMH.Spec.Image).To(Equal(updatedHost.Spec.Image))
+				Expect(spokeBMH.Spec.ConsumerRef.Kind).To(Equal("Machine"))
+				Expect(spokeBMH.Spec.ConsumerRef.Name).To(Equal(machineName))
+				Expect(spokeBMH.Spec.ConsumerRef.Namespace).To(Equal(OPENSHIFT_MACHINE_API_NAMESPACE))
+				Expect(spokeBMH.Spec.ConsumerRef.APIVersion).To(Equal(machinev1beta1.SchemeGroupVersion.String()))
+
+				By("Checking the spoke Machine exists and is correct")
+				spokeMachine := &machinev1beta1.Machine{}
+				err = spokeClient.Get(ctx, types.NamespacedName{Name: machineName, Namespace: OPENSHIFT_MACHINE_API_NAMESPACE}, spokeMachine)
+				Expect(err).To(BeNil())
+				Expect(spokeMachine.ObjectMeta.Labels).To(HaveKey(machinev1beta1.MachineClusterIDLabel))
+				Expect(spokeMachine.ObjectMeta.Annotations).To(HaveKey("metal3.io/BareMetalHost"))
+				Expect(spokeMachine.ObjectMeta.Labels).To(HaveKey(MACHINE_ROLE))
+				Expect(spokeMachine.ObjectMeta.Labels[MACHINE_ROLE]).To(Equal(string(models.HostRoleWorker)))
+				Expect(spokeMachine.ObjectMeta.Labels).To(HaveKey(MACHINE_TYPE))
+				Expect(spokeMachine.ObjectMeta.Labels[MACHINE_TYPE]).To(Equal(string(models.HostRoleWorker)))
+				Expect(string(spokeMachine.Spec.ProviderSpec.Value.Raw)).To(ContainSubstring(imageURL))
+			})
+			It("should create spoke BMH & Machine for day 2 host with master role when it's installing - happy flow", func() {
+				configMap := &corev1.ConfigMap{
+					ObjectMeta: metav1.ObjectMeta{
+						Name:      "root-ca",
+						Namespace: "kube-system",
+					},
+					Data: map[string]string{
+						"ca.crt": BASIC_CERT,
+					},
+				}
+				Expect(bmhr.spokeClient.Create(ctx, configMap)).ShouldNot(HaveOccurred())
+
+				By("Setting the role to master")
+				annotations := make(map[string]string)
+				annotations[BMH_AGENT_IGNITION_CONFIG_OVERRIDES] = `{"ignition":{"version":"3.1.0", "security": {"tls":{"certificateAuthorities":[{"source":"data:text/plain;charset=utf-8;base64,c29tZSBjZXJ0aWZpY2F0ZQ=="}]}}}}`
+				annotations[BMH_AGENT_ROLE] = "master"
+				host_day2.ObjectMeta.SetAnnotations(annotations)
+				Expect(c.Update(ctx, host_day2)).To(BeNil())
+				agent_day2.Status.Role = "master"
+
+				By("Updating the agent to installing")
+				agent_day2.Status.DebugInfo.State = models.HostStatusInstalling
+				Expect(c.Update(context.Background(), agent_day2)).ShouldNot(HaveOccurred())
+				for range [3]int{} {
+					result, err := bmhr.Reconcile(ctx, newBMHRequest(host_day2))
+					Expect(err).To(BeNil())
+					Expect(result).To(Equal(ctrl.Result{}))
+				}
+
+				By("Checking if the BMH has the correct annotations")
+				updatedHost := &bmh_v1alpha1.BareMetalHost{}
+				err := c.Get(ctx, types.NamespacedName{Name: host_day2.Name, Namespace: testNamespace}, updatedHost)
+				Expect(err).To(BeNil())
+				Expect(updatedHost.ObjectMeta.Annotations).To(HaveKey(BMH_HARDWARE_DETAILS_ANNOTATION))
+				Expect(updatedHost.ObjectMeta.Annotations).To(HaveKey(BMH_DETACHED_ANNOTATION))
+				Expect(updatedHost.ObjectMeta.Annotations[BMH_DETACHED_ANNOTATION]).To(Equal("assisted-service-controller"))
+				Expect(updatedHost.ObjectMeta.Annotations).To(HaveKey(BMH_AGENT_IGNITION_CONFIG_OVERRIDES))
+				Expect(updatedHost.ObjectMeta.Annotations[BMH_AGENT_IGNITION_CONFIG_OVERRIDES]).NotTo(Equal(""))
+				Expect(updatedHost.ObjectMeta.Annotations[BMH_AGENT_IGNITION_CONFIG_OVERRIDES]).To(ContainSubstring("dGVzdA=="))
+
+				By("Checking the spoke BMH exists and is correct")
+				machineName := fmt.Sprintf("%s-%s", cluster.Name, host_day2.Name)
+				spokeBMH := &bmh_v1alpha1.BareMetalHost{}
+				spokeClient := bmhr.spokeClient
+				err = spokeClient.Get(ctx, types.NamespacedName{Name: host_day2.Name, Namespace: OPENSHIFT_MACHINE_API_NAMESPACE}, spokeBMH)
+				Expect(err).To(BeNil())
+				Expect(spokeBMH.ObjectMeta.Annotations).To(HaveKey(BMH_HARDWARE_DETAILS_ANNOTATION))
+				Expect(spokeBMH.ObjectMeta.Annotations[BMH_HARDWARE_DETAILS_ANNOTATION]).To(Equal(updatedHost.ObjectMeta.Annotations[BMH_HARDWARE_DETAILS_ANNOTATION]))
+				Expect(spokeBMH.ObjectMeta.Annotations).ToNot(HaveKey(BMH_DETACHED_ANNOTATION))
+				Expect(spokeBMH.ObjectMeta.Annotations[BMH_INSPECT_ANNOTATION]).To(Equal("disabled"))
+				Expect(spokeBMH.Spec.Image).To(Equal(updatedHost.Spec.Image))
+				Expect(spokeBMH.Spec.ConsumerRef.Kind).To(Equal("Machine"))
+				Expect(spokeBMH.Spec.ConsumerRef.Name).To(Equal(machineName))
+				Expect(spokeBMH.Spec.ConsumerRef.Namespace).To(Equal(OPENSHIFT_MACHINE_API_NAMESPACE))
+				Expect(spokeBMH.Spec.ConsumerRef.APIVersion).To(Equal(machinev1beta1.SchemeGroupVersion.String()))
+
+				By("Checking the spoke Machine exists and is correct")
+				spokeMachine := &machinev1beta1.Machine{}
+				err = spokeClient.Get(ctx, types.NamespacedName{Name: machineName, Namespace: OPENSHIFT_MACHINE_API_NAMESPACE}, spokeMachine)
+				Expect(err).To(BeNil())
+				Expect(spokeMachine.ObjectMeta.Labels).To(HaveKey(machinev1beta1.MachineClusterIDLabel))
+				Expect(spokeMachine.ObjectMeta.Annotations).To(HaveKey("metal3.io/BareMetalHost"))
+				Expect(spokeMachine.ObjectMeta.Labels).To(HaveKey(MACHINE_ROLE))
+				Expect(spokeMachine.ObjectMeta.Labels[MACHINE_ROLE]).To(Equal(string(models.HostRoleMaster)))
+				Expect(spokeMachine.ObjectMeta.Labels).To(HaveKey(MACHINE_TYPE))
+				Expect(spokeMachine.ObjectMeta.Labels[MACHINE_TYPE]).To(Equal(string(models.HostRoleMaster)))
+				Expect(string(spokeMachine.Spec.ProviderSpec.Value.Raw)).To(ContainSubstring(imageURL))
+
+			})
+			It("should not create spoke BMH for day 2 host if cluster does not exist", func() {
+				agent_day2.Spec.ClusterDeploymentName = nil
+				Expect(c.Update(ctx, agent_day2)).To(BeNil())
+
+				for range [3]int{} {
+					result, err := bmhr.Reconcile(ctx, newBMHRequest(host_day2))
+					Expect(err).To(BeNil())
+					Expect(result).To(Equal(ctrl.Result{}))
+				}
+				By("Checking if the BMH does not have the detached annotation")
+				updatedHost := &bmh_v1alpha1.BareMetalHost{}
+				err := c.Get(ctx, types.NamespacedName{Name: host_day2.Name, Namespace: testNamespace}, updatedHost)
+				Expect(err).To(BeNil())
+				Expect(updatedHost.ObjectMeta.Annotations).ToNot(HaveKey(BMH_DETACHED_ANNOTATION))
+
+				By("Checking the spoke BMH does not exist")
+				machineName := fmt.Sprintf("%s-%s", cluster.Name, host_day2.Name)
+				spokeBMH := &bmh_v1alpha1.BareMetalHost{}
+				spokeClient := bmhr.spokeClient
+				err = spokeClient.Get(ctx, types.NamespacedName{Name: host_day2.Name, Namespace: OPENSHIFT_MACHINE_API_NAMESPACE}, spokeBMH)
+				Expect(err).To(HaveOccurred())
+
+				By("Checking the spoke Machine does not exist")
+				spokeMachine := &machinev1beta1.Machine{}
+				err = spokeClient.Get(ctx, types.NamespacedName{Name: machineName, Namespace: OPENSHIFT_MACHINE_API_NAMESPACE}, spokeMachine)
+				Expect(err).To(HaveOccurred())
+			})
+			It("should not create spoke BMH for day 2 if cluster is not day 2", func() {
+				pullSecretName := "pull-secret"
+				clusterName := "fake-day2-test-cluster"
+				defaultClusterSpec := getDefaultClusterDeploymentSpec(clusterName, "test-cluster-aci-day2", pullSecretName)
+				fake_day2_cluster := newClusterDeployment(clusterName, testNamespace, defaultClusterSpec)
+				fake_day2_cluster.Spec.Installed = false
+				Expect(c.Create(ctx, fake_day2_cluster)).ShouldNot(HaveOccurred())
+				clusterInstall = newAgentClusterInstall(fake_day2_cluster.Spec.ClusterInstallRef.Name, testNamespace, hiveext.AgentClusterInstallSpec{}, fake_day2_cluster)
+				Expect(c.Create(ctx, clusterInstall)).ShouldNot(HaveOccurred())
+				agent_day2.Spec.ClusterDeploymentName = &v1beta1.ClusterReference{Name: clusterName, Namespace: testNamespace}
+				Expect(c.Update(ctx, agent_day2)).To(BeNil())
+
+				By("Checking if the BMH does not have the detached annotation")
+				updatedHost := &bmh_v1alpha1.BareMetalHost{}
+				err := c.Get(ctx, types.NamespacedName{Name: host_day2.Name, Namespace: testNamespace}, updatedHost)
+				Expect(err).To(BeNil())
+				Expect(updatedHost.ObjectMeta.Annotations).ToNot(HaveKey(BMH_DETACHED_ANNOTATION))
+
+				By("Checking the spoke BMH does not exist")
+				machineName := fmt.Sprintf("%s-%s", cluster.Name, host_day2.Name)
+				spokeBMH := &bmh_v1alpha1.BareMetalHost{}
+				spokeClient := bmhr.spokeClient
+				err = spokeClient.Get(ctx, types.NamespacedName{Name: host_day2.Name, Namespace: OPENSHIFT_MACHINE_API_NAMESPACE}, spokeBMH)
+				Expect(err).To(HaveOccurred())
+
+				By("Checking the spoke Machine does not exist")
+				spokeMachine := &machinev1beta1.Machine{}
+				err = spokeClient.Get(ctx, types.NamespacedName{Name: machineName, Namespace: OPENSHIFT_MACHINE_API_NAMESPACE}, spokeMachine)
+				Expect(err).To(HaveOccurred())
+			})
+		})
+
 		Context("when agent role worker and cluster deployment is set", func() {
 			It("should not create spoke BMH when agent is not installing", func() {
 				configMap := &corev1.ConfigMap{