Repository: kubernetes-sigs/cluster-api
Pull request #4298 (2021-03-11 19:55:42)

🌱Use distroless for CAPD
**What this PR does / why we need it**:
Use distroless for CAPD, so the image size shrink down from >1GB to 158MB

**Which issue(s) this PR fixes**:
Fixes #



Patch:
diff --git a/api/v1alpha3/conversion.go b/api/v1alpha3/conversion.go
index c693abd72..87b9251b6 100644
--- a/api/v1alpha3/conversion.go
+++ b/api/v1alpha3/conversion.go
@@ -19,7 +19,6 @@ package v1alpha3
 import (
 	apiconversion "k8s.io/apimachinery/pkg/conversion"
 	"sigs.k8s.io/cluster-api/api/v1alpha4"
-	"sigs.k8s.io/cluster-api/util/conditions"
 	utilconversion "sigs.k8s.io/cluster-api/util/conversion"
 	"sigs.k8s.io/controller-runtime/pkg/conversion"
 )
@@ -27,33 +26,13 @@ import (
 func (src *Cluster) ConvertTo(dstRaw conversion.Hub) error {
 	dst := dstRaw.(*v1alpha4.Cluster)
 
-	if err := Convert_v1alpha3_Cluster_To_v1alpha4_Cluster(src, dst, nil); err != nil {
-		return err
-	}
-
-	// Given this is a bool and there is no timestamp associated with it, when this condition is set, its timestamp
-	// will be "now". See https://github.com/kubernetes-sigs/cluster-api/issues/3798#issuecomment-708619826 for more
-	// discussion.
-	if src.Status.ControlPlaneInitialized {
-		conditions.MarkTrue(dst, v1alpha4.ControlPlaneInitializedCondition)
-	}
-
-	return nil
+	return Convert_v1alpha3_Cluster_To_v1alpha4_Cluster(src, dst, nil)
 }
 
 func (dst *Cluster) ConvertFrom(srcRaw conversion.Hub) error {
 	src := srcRaw.(*v1alpha4.Cluster)
 
-	if err := Convert_v1alpha4_Cluster_To_v1alpha3_Cluster(src, dst, nil); err != nil {
-		return err
-	}
-
-	// Set the v1alpha3 boolean status field if the v1alpha4 condition was true
-	if conditions.IsTrue(src, v1alpha4.ControlPlaneInitializedCondition) {
-		dst.Status.ControlPlaneInitialized = true
-	}
-
-	return nil
+	return Convert_v1alpha4_Cluster_To_v1alpha3_Cluster(src, dst, nil)
 }
 
 func (src *ClusterList) ConvertTo(dstRaw conversion.Hub) error {
@@ -229,7 +208,3 @@ func Convert_v1alpha4_MachineRollingUpdateDeployment_To_v1alpha3_MachineRollingU
 func Convert_v1alpha4_MachineHealthCheckSpec_To_v1alpha3_MachineHealthCheckSpec(in *v1alpha4.MachineHealthCheckSpec, out *MachineHealthCheckSpec, s apiconversion.Scope) error {
 	return autoConvert_v1alpha4_MachineHealthCheckSpec_To_v1alpha3_MachineHealthCheckSpec(in, out, s)
 }
-
-func Convert_v1alpha3_ClusterStatus_To_v1alpha4_ClusterStatus(in *ClusterStatus, out *v1alpha4.ClusterStatus, s apiconversion.Scope) error {
-	return autoConvert_v1alpha3_ClusterStatus_To_v1alpha4_ClusterStatus(in, out, s)
-}
diff --git a/api/v1alpha3/zz_generated.conversion.go b/api/v1alpha3/zz_generated.conversion.go
index 8122dd24a..3000514fc 100644
--- a/api/v1alpha3/zz_generated.conversion.go
+++ b/api/v1alpha3/zz_generated.conversion.go
@@ -94,6 +94,11 @@ func RegisterConversions(s *runtime.Scheme) error {
 	}); err != nil {
 		return err
 	}
+	if err := s.AddGeneratedConversionFunc((*ClusterStatus)(nil), (*v1alpha4.ClusterStatus)(nil), func(a, b interface{}, scope conversion.Scope) error {
+		return Convert_v1alpha3_ClusterStatus_To_v1alpha4_ClusterStatus(a.(*ClusterStatus), b.(*v1alpha4.ClusterStatus), scope)
+	}); err != nil {
+		return err
+	}
 	if err := s.AddGeneratedConversionFunc((*v1alpha4.ClusterStatus)(nil), (*ClusterStatus)(nil), func(a, b interface{}, scope conversion.Scope) error {
 		return Convert_v1alpha4_ClusterStatus_To_v1alpha3_ClusterStatus(a.(*v1alpha4.ClusterStatus), b.(*ClusterStatus), scope)
 	}); err != nil {
@@ -344,11 +349,6 @@ func RegisterConversions(s *runtime.Scheme) error {
 	}); err != nil {
 		return err
 	}
-	if err := s.AddConversionFunc((*ClusterStatus)(nil), (*v1alpha4.ClusterStatus)(nil), func(a, b interface{}, scope conversion.Scope) error {
-		return Convert_v1alpha3_ClusterStatus_To_v1alpha4_ClusterStatus(a.(*ClusterStatus), b.(*v1alpha4.ClusterStatus), scope)
-	}); err != nil {
-		return err
-	}
 	if err := s.AddConversionFunc((*v1alpha4.MachineHealthCheckSpec)(nil), (*MachineHealthCheckSpec)(nil), func(a, b interface{}, scope conversion.Scope) error {
 		return Convert_v1alpha4_MachineHealthCheckSpec_To_v1alpha3_MachineHealthCheckSpec(a.(*v1alpha4.MachineHealthCheckSpec), b.(*MachineHealthCheckSpec), scope)
 	}); err != nil {
@@ -436,17 +436,7 @@ func Convert_v1alpha4_Cluster_To_v1alpha3_Cluster(in *v1alpha4.Cluster, out *Clu
 
 func autoConvert_v1alpha3_ClusterList_To_v1alpha4_ClusterList(in *ClusterList, out *v1alpha4.ClusterList, s conversion.Scope) error {
 	out.ListMeta = in.ListMeta
-	if in.Items != nil {
-		in, out := &in.Items, &out.Items
-		*out = make([]v1alpha4.Cluster, len(*in))
-		for i := range *in {
-			if err := Convert_v1alpha3_Cluster_To_v1alpha4_Cluster(&(*in)[i], &(*out)[i], s); err != nil {
-				return err
-			}
-		}
-	} else {
-		out.Items = nil
-	}
+	out.Items = *(*[]v1alpha4.Cluster)(unsafe.Pointer(&in.Items))
 	return nil
 }
 
@@ -457,17 +447,7 @@ func Convert_v1alpha3_ClusterList_To_v1alpha4_ClusterList(in *ClusterList, out *
 
 func autoConvert_v1alpha4_ClusterList_To_v1alpha3_ClusterList(in *v1alpha4.ClusterList, out *ClusterList, s conversion.Scope) error {
 	out.ListMeta = in.ListMeta
-	if in.Items != nil {
-		in, out := &in.Items, &out.Items
-		*out = make([]Cluster, len(*in))
-		for i := range *in {
-			if err := Convert_v1alpha4_Cluster_To_v1alpha3_Cluster(&(*in)[i], &(*out)[i], s); err != nil {
-				return err
-			}
-		}
-	} else {
-		out.Items = nil
-	}
+	out.Items = *(*[]Cluster)(unsafe.Pointer(&in.Items))
 	return nil
 }
 
@@ -540,19 +520,25 @@ func autoConvert_v1alpha3_ClusterStatus_To_v1alpha4_ClusterStatus(in *ClusterSta
 	out.FailureMessage = (*string)(unsafe.Pointer(in.FailureMessage))
 	out.Phase = in.Phase
 	out.InfrastructureReady = in.InfrastructureReady
-	// WARNING: in.ControlPlaneInitialized requires manual conversion: does not exist in peer-type
+	out.ControlPlaneInitialized = in.ControlPlaneInitialized
 	out.ControlPlaneReady = in.ControlPlaneReady
 	out.Conditions = *(*v1alpha4.Conditions)(unsafe.Pointer(&in.Conditions))
 	out.ObservedGeneration = in.ObservedGeneration
 	return nil
 }
 
+// Convert_v1alpha3_ClusterStatus_To_v1alpha4_ClusterStatus is an autogenerated conversion function.
+func Convert_v1alpha3_ClusterStatus_To_v1alpha4_ClusterStatus(in *ClusterStatus, out *v1alpha4.ClusterStatus, s conversion.Scope) error {
+	return autoConvert_v1alpha3_ClusterStatus_To_v1alpha4_ClusterStatus(in, out, s)
+}
+
 func autoConvert_v1alpha4_ClusterStatus_To_v1alpha3_ClusterStatus(in *v1alpha4.ClusterStatus, out *ClusterStatus, s conversion.Scope) error {
 	out.FailureDomains = *(*FailureDomains)(unsafe.Pointer(&in.FailureDomains))
 	out.FailureReason = (*errors.ClusterStatusError)(unsafe.Pointer(in.FailureReason))
 	out.FailureMessage = (*string)(unsafe.Pointer(in.FailureMessage))
 	out.Phase = in.Phase
 	out.InfrastructureReady = in.InfrastructureReady
+	out.ControlPlaneInitialized = in.ControlPlaneInitialized
 	out.ControlPlaneReady = in.ControlPlaneReady
 	out.Conditions = *(*Conditions)(unsafe.Pointer(&in.Conditions))
 	out.ObservedGeneration = in.ObservedGeneration
diff --git a/api/v1alpha4/cluster_types.go b/api/v1alpha4/cluster_types.go
index 51d1893a2..b41a77c1e 100644
--- a/api/v1alpha4/cluster_types.go
+++ b/api/v1alpha4/cluster_types.go
@@ -128,6 +128,10 @@ type ClusterStatus struct {
 	// +optional
 	InfrastructureReady bool `json:"infrastructureReady"`
 
+	// ControlPlaneInitialized defines if the control plane has been initialized.
+	// +optional
+	ControlPlaneInitialized bool `json:"controlPlaneInitialized"`
+
 	// ControlPlaneReady defines if the control plane is ready.
 	// +optional
 	ControlPlaneReady bool `json:"controlPlaneReady,omitempty"`
diff --git a/api/v1alpha4/condition_consts.go b/api/v1alpha4/condition_consts.go
index cd2f38988..7aca8adc6 100644
--- a/api/v1alpha4/condition_consts.go
+++ b/api/v1alpha4/condition_consts.go
@@ -55,20 +55,6 @@ const (
 // Conditions and condition Reasons for the Cluster object
 
 const (
-	// ControlPlaneInitializedCondition reports if the cluster's control plane has been initialized such that the
-	// cluster's apiserver is reachable and at least one control plane Machine has a node reference. Once this
-	// condition is marked true, its value is never changed. See the ControlPlaneReady condition for an indication of
-	// the current readiness of the cluster's control plane.
-	ControlPlaneInitializedCondition ConditionType = "ControlPlaneInitialized"
-
-	// MissingNodeRefReason (Severity=Info) documents a cluster waiting for at least one control plane Machine to have
-	// its node reference populated.
-	MissingNodeRefReason = "MissingNodeRef"
-
-	// WaitingForControlPlaneProviderInitializedReason (Severity=Info) documents a cluster waiting for the control plane
-	// provider to report successful control plane initialization.
-	WaitingForControlPlaneProviderInitializedReason = "WaitingForControlPlaneProviderInitialized"
-
 	// ControlPlaneReady reports the ready condition from the control plane object defined for this cluster.
 	// This condition is mirrored from the Ready condition in the control plane ref object, and
 	// the absence of this condition might signal problems in the reconcile external loops or the fact that
diff --git a/bootstrap/kubeadm/controllers/kubeadmconfig_controller.go b/bootstrap/kubeadm/controllers/kubeadmconfig_controller.go
index 01987cc37..f3355a268 100644
--- a/bootstrap/kubeadm/controllers/kubeadmconfig_controller.go
+++ b/bootstrap/kubeadm/controllers/kubeadmconfig_controller.go
@@ -242,8 +242,7 @@ func (r *KubeadmConfigReconciler) Reconcile(ctx context.Context, req ctrl.Reques
 		return ctrl.Result{}, nil
 	}
 
-	// Note: can't use IsFalse here because we need to handle the absence of the condition as well as false.
-	if !conditions.IsTrue(cluster, clusterv1.ControlPlaneInitializedCondition) {
+	if !cluster.Status.ControlPlaneInitialized {
 		return r.handleClusterNotInitialized(ctx, scope)
 	}
 
diff --git a/cmd/clusterctl/client/cluster/mover.go b/cmd/clusterctl/client/cluster/mover.go
index a44f7b48c..a4d40be34 100644
--- a/cmd/clusterctl/client/cluster/mover.go
+++ b/cmd/clusterctl/client/cluster/mover.go
@@ -30,7 +30,6 @@ import (
 	"k8s.io/apimachinery/pkg/util/version"
 	clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha4"
 	logf "sigs.k8s.io/cluster-api/cmd/clusterctl/log"
-	"sigs.k8s.io/cluster-api/util/conditions"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 )
 
@@ -138,8 +137,7 @@ func (o *objectMover) checkProvisioningCompleted(graph *objectGraph) error {
 			continue
 		}
 
-		// Note: can't use IsFalse here because we need to handle the absence of the condition as well as false.
-		if !conditions.IsTrue(clusterObj, clusterv1.ControlPlaneInitializedCondition) {
+		if !clusterObj.Status.ControlPlaneInitialized {
 			errList = append(errList, errors.Errorf("cannot start the move operation while the control plane for %q %s/%s is not yet initialized", clusterObj.GroupVersionKind(), clusterObj.GetNamespace(), clusterObj.GetName()))
 			continue
 		}
diff --git a/config/crd/bases/cluster.x-k8s.io_clusters.yaml b/config/crd/bases/cluster.x-k8s.io_clusters.yaml
index c800cdcb4..8d4b3d517 100644
--- a/config/crd/bases/cluster.x-k8s.io_clusters.yaml
+++ b/config/crd/bases/cluster.x-k8s.io_clusters.yaml
@@ -367,6 +367,9 @@ spec:
                   - type
                   type: object
                 type: array
+              controlPlaneInitialized:
+                description: ControlPlaneInitialized defines if the control plane has been initialized.
+                type: boolean
               controlPlaneReady:
                 description: ControlPlaneReady defines if the control plane is ready.
                 type: boolean
diff --git a/controllers/cluster_controller.go b/controllers/cluster_controller.go
index 12ea00e5b..385dbe2df 100644
--- a/controllers/cluster_controller.go
+++ b/controllers/cluster_controller.go
@@ -465,35 +465,28 @@ func splitMachineList(list *clusterv1.MachineList) (*clusterv1.MachineList, *clu
 func (r *ClusterReconciler) reconcileControlPlaneInitialized(ctx context.Context, cluster *clusterv1.Cluster) (ctrl.Result, error) {
 	log := ctrl.LoggerFrom(ctx)
 
-	// Skip checking if the control plane is initialized when using a Control Plane Provider (this is reconciled in
-	// reconcileControlPlane instead).
+	// Skip checking if the control plane is initialized when using a Control Plane Provider
 	if cluster.Spec.ControlPlaneRef != nil {
-		log.V(4).Info("Skipping reconcileControlPlaneInitialized because cluster has a controlPlaneRef")
 		return ctrl.Result{}, nil
 	}
 
-	if conditions.IsTrue(cluster, clusterv1.ControlPlaneInitializedCondition) {
-		log.V(4).Info("Skipping reconcileControlPlaneInitialized because control plane already initialized")
+	if cluster.Status.ControlPlaneInitialized {
 		return ctrl.Result{}, nil
 	}
 
-	log.V(4).Info("Checking for control plane initialization")
-
 	machines, err := getActiveMachinesInCluster(ctx, r.Client, cluster.Namespace, cluster.Name)
 	if err != nil {
-		log.Error(err, "unable to determine ControlPlaneInitialized")
+		log.Error(err, "Error getting machines in cluster")
 		return ctrl.Result{}, err
 	}
 
 	for _, m := range machines {
 		if util.IsControlPlaneMachine(m) && m.Status.NodeRef != nil {
-			conditions.MarkTrue(cluster, clusterv1.ControlPlaneInitializedCondition)
+			cluster.Status.ControlPlaneInitialized = true
 			return ctrl.Result{}, nil
 		}
 	}
 
-	conditions.MarkFalse(cluster, clusterv1.ControlPlaneInitializedCondition, clusterv1.MissingNodeRefReason, clusterv1.ConditionSeverityInfo, "Waiting for the first control plane machine to have its status.nodeRef set")
-
 	return ctrl.Result{}, nil
 }
 
@@ -516,7 +509,7 @@ func (r *ClusterReconciler) controlPlaneMachineToCluster(o client.Object) []ctrl
 		return nil
 	}
 
-	if conditions.IsTrue(cluster, clusterv1.ControlPlaneInitializedCondition) {
+	if cluster.Status.ControlPlaneInitialized {
 		return nil
 	}
 
diff --git a/controllers/cluster_controller_phases.go b/controllers/cluster_controller_phases.go
index 234bc854f..0167b5425 100644
--- a/controllers/cluster_controller_phases.go
+++ b/controllers/cluster_controller_phases.go
@@ -237,16 +237,12 @@ func (r *ClusterReconciler) reconcileControlPlane(ctx context.Context, cluster *
 
 	// Update cluster.Status.ControlPlaneInitialized if it hasn't already been set
 	// Determine if the control plane provider is initialized.
-	if !conditions.IsTrue(cluster, clusterv1.ControlPlaneInitializedCondition) {
+	if !cluster.Status.ControlPlaneInitialized {
 		initialized, err := external.IsInitialized(controlPlaneConfig)
 		if err != nil {
 			return ctrl.Result{}, err
 		}
-		if initialized {
-			conditions.MarkTrue(cluster, clusterv1.ControlPlaneInitializedCondition)
-		} else {
-			conditions.MarkFalse(cluster, clusterv1.ControlPlaneInitializedCondition, clusterv1.WaitingForControlPlaneProviderInitializedReason, clusterv1.ConditionSeverityInfo, "Waiting for control plane provider to indicate the control plane has been initialized")
-		}
+		cluster.Status.ControlPlaneInitialized = initialized
 	}
 
 	return ctrl.Result{}, nil
diff --git a/controllers/machine_controller.go b/controllers/machine_controller.go
index 370c018b4..86dabb571 100644
--- a/controllers/machine_controller.go
+++ b/controllers/machine_controller.go
@@ -251,7 +251,7 @@ func patchMachine(ctx context.Context, patchHelper *patch.Helper, machine *clust
 func (r *MachineReconciler) reconcile(ctx context.Context, cluster *clusterv1.Cluster, m *clusterv1.Machine) (ctrl.Result, error) {
 	log := ctrl.LoggerFrom(ctx)
 
-	if conditions.IsTrue(cluster, clusterv1.ControlPlaneInitializedCondition) {
+	if cluster.Status.ControlPlaneInitialized {
 		if err := r.watchClusterNodes(ctx, cluster); err != nil {
 			log.Error(err, "error watching nodes on target cluster")
 			return ctrl.Result{}, err
diff --git a/controllers/machinehealthcheck_targets.go b/controllers/machinehealthcheck_targets.go
index 95cc1bf46..5493b54ef 100644
--- a/controllers/machinehealthcheck_targets.go
+++ b/controllers/machinehealthcheck_targets.go
@@ -59,7 +59,6 @@ const (
 // healthCheckTarget contains the information required to perform a health check
 // on the node to determine if any remediation is required.
 type healthCheckTarget struct {
-	Cluster     *clusterv1.Cluster
 	Machine     *clusterv1.Machine
 	Node        *corev1.Node
 	MHC         *clusterv1.MachineHealthCheck
@@ -116,46 +115,19 @@ func (t *healthCheckTarget) needsRemediation(logger logr.Logger, timeoutForMachi
 		return true, time.Duration(0)
 	}
 
-	// Don't penalize any Machine/Node if the control plane has not been initialized.
-	if !conditions.IsTrue(t.Cluster, clusterv1.ControlPlaneInitializedCondition) {
-		logger.V(3).Info("Not evaluating target health because the control plane has not yet been initialized")
-		// Return a nextCheck time of 0 because we'll get requeued when the Cluster is updated.
-		return false, 0
-	}
-
-	// Don't penalize any Machine/Node if the cluster infrastructure is not ready.
-	if !conditions.IsTrue(t.Cluster, clusterv1.InfrastructureReadyCondition) {
-		logger.V(3).Info("Not evaluating target health because the cluster infrastructure is not ready")
-		// Return a nextCheck time of 0 because we'll get requeued when the Cluster is updated.
-		return false, 0
-	}
-
 	// the node has not been set yet
 	if t.Node == nil {
-		controlPlaneInitializedTime := conditions.GetLastTransitionTime(t.Cluster, clusterv1.ControlPlaneInitializedCondition).Time
-		clusterInfraReadyTime := conditions.GetLastTransitionTime(t.Cluster, clusterv1.InfrastructureReadyCondition).Time
-		machineCreationTime := t.Machine.CreationTimestamp.Time
-
-		// Use the latest of the 3 times
-		comparisonTime := machineCreationTime
-		logger.V(3).Info("Determining comparison time", "machineCreationTime", machineCreationTime, "clusterInfraReadyTime", clusterInfraReadyTime, "controlPlaneInitializedTime", controlPlaneInitializedTime)
-		if controlPlaneInitializedTime.After(comparisonTime) {
-			comparisonTime = controlPlaneInitializedTime
-		}
-		if clusterInfraReadyTime.After(comparisonTime) {
-			comparisonTime = clusterInfraReadyTime
+		// status not updated yet
+		if t.Machine.Status.LastUpdated == nil {
+			return false, timeoutForMachineToHaveNode
 		}
-		logger.V(3).Info("Using comparison time", "time", comparisonTime)
-
-		if comparisonTime.Add(timeoutForMachineToHaveNode).Before(now) {
+		if t.Machine.Status.LastUpdated.Add(timeoutForMachineToHaveNode).Before(now) {
 			conditions.MarkFalse(t.Machine, clusterv1.MachineHealthCheckSuccededCondition, clusterv1.NodeStartupTimeoutReason, clusterv1.ConditionSeverityWarning, "Node failed to report startup in %s", timeoutForMachineToHaveNode.String())
 			logger.V(3).Info("Target is unhealthy: machine has no node", "duration", timeoutForMachineToHaveNode.String())
 			return true, time.Duration(0)
 		}
-
-		durationUnhealthy := now.Sub(comparisonTime)
+		durationUnhealthy := now.Sub(t.Machine.Status.LastUpdated.Time)
 		nextCheck := timeoutForMachineToHaveNode - durationUnhealthy + time.Second
-
 		return false, nextCheck
 	}
 
@@ -197,11 +169,6 @@ func (r *MachineHealthCheckReconciler) getTargetsFromMHC(ctx context.Context, lo
 		return nil, nil
 	}
 
-	var cluster clusterv1.Cluster
-	if err := clusterClient.Get(ctx, client.ObjectKey{Namespace: mhc.Namespace, Name: mhc.Spec.ClusterName}, &cluster); err != nil {
-		return nil, errors.Wrapf(err, "error getting Cluster %s/%s for MachineHealthCheck %s", mhc.Namespace, mhc.Spec.ClusterName, mhc.Name)
-	}
-
 	targets := []healthCheckTarget{}
 	for k := range machines {
 		skip, reason := shouldSkipRemediation(&machines[k])
@@ -215,7 +182,6 @@ func (r *MachineHealthCheckReconciler) getTargetsFromMHC(ctx context.Context, lo
 			return nil, errors.Wrap(err, "unable to initialize patch helper")
 		}
 		target := healthCheckTarget{
-			Cluster:     &cluster,
 			MHC:         mhc,
 			Machine:     &machines[k],
 			patchHelper: patchHelper,
@@ -235,7 +201,7 @@ func (r *MachineHealthCheckReconciler) getTargetsFromMHC(ctx context.Context, lo
 	return targets, nil
 }
 
-// getMachinesFromMHC fetches Machines matched by the MachineHealthCheck's
+//getMachinesFromMHC fetches Machines matched by the MachineHealthCheck's
 // label selector
 func (r *MachineHealthCheckReconciler) getMachinesFromMHC(ctx context.Context, mhc *clusterv1.MachineHealthCheck) ([]clusterv1.Machine, error) {
 	selector, err := metav1.LabelSelectorAsSelector(metav1.CloneSelectorAndAddLabel(
diff --git a/controllers/remote/cluster_cache.go b/controllers/remote/cluster_cache.go
index 9c45ae680..b6c402a9a 100644
--- a/controllers/remote/cluster_cache.go
+++ b/controllers/remote/cluster_cache.go
@@ -32,7 +32,6 @@ import (
 	"k8s.io/client-go/kubernetes/scheme"
 	"k8s.io/client-go/rest"
 	clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha4"
-	"sigs.k8s.io/cluster-api/util/conditions"
 	ctrl "sigs.k8s.io/controller-runtime"
 	"sigs.k8s.io/controller-runtime/pkg/cache"
 	"sigs.k8s.io/controller-runtime/pkg/client"
@@ -314,7 +313,7 @@ func (t *ClusterCacheTracker) healthCheckCluster(ctx context.Context, in *health
 			return false, nil
 		}
 
-		if !cluster.Status.InfrastructureReady || !conditions.IsTrue(cluster, clusterv1.ControlPlaneInitializedCondition) {
+		if !cluster.Status.InfrastructureReady || !cluster.Status.ControlPlaneInitialized {
 			// If the infrastructure or control plane aren't marked as ready, we should requeue and wait.
 			return false, nil
 		}
diff --git a/util/conversion/conversion.go b/util/conversion/conversion.go
index 152eb9624..278a1861a 100644
--- a/util/conversion/conversion.go
+++ b/util/conversion/conversion.go
@@ -136,71 +136,51 @@ func GetFuzzer(scheme *runtime.Scheme, funcs ...fuzzer.FuzzerFuncs) *fuzz.Fuzzer
 	)
 }
 
-type FuzzTestFuncInput struct {
-	Scheme *runtime.Scheme
-
-	Hub conversion.Hub
-	HubAfterMutation func(conversion.Hub)
-
-	Spoke conversion.Convertible
-	SpokeAfterMutation func(convertible conversion.Convertible)
-
-	FuzzerFuncs []fuzzer.FuzzerFuncs
-}
-
 // FuzzTestFunc returns a new testing function to be used in tests to make sure conversions between
 // the Hub version of an object and an older version aren't lossy.
-func FuzzTestFunc(input FuzzTestFuncInput) func(*testing.T) {
+func FuzzTestFunc(scheme *runtime.Scheme, hub conversion.Hub, dst conversion.Convertible, funcs ...fuzzer.FuzzerFuncs) func(*testing.T) {
 	return func(t *testing.T) {
 		t.Run("spoke-hub-spoke", func(t *testing.T) {
 			g := gomega.NewWithT(t)
-			fuzzer := GetFuzzer(input.Scheme, input.FuzzerFuncs...)
+			fuzzer := GetFuzzer(scheme, funcs...)
 
 			for i := 0; i < 10000; i++ {
 				// Create the spoke and fuzz it
-				spokeBefore := input.Spoke.DeepCopyObject().(conversion.Convertible)
+				spokeBefore := dst.DeepCopyObject().(conversion.Convertible)
 				fuzzer.Fuzz(spokeBefore)
 
 				// First convert spoke to hub
-				hubCopy := input.Hub.DeepCopyObject().(conversion.Hub)
+				hubCopy := hub.DeepCopyObject().(conversion.Hub)
 				g.Expect(spokeBefore.ConvertTo(hubCopy)).To(gomega.Succeed())
 
 				// Convert hub back to spoke and check if the resulting spoke is equal to the spoke before the round trip
-				spokeAfter := input.Spoke.DeepCopyObject().(conversion.Convertible)
+				spokeAfter := dst.DeepCopyObject().(conversion.Convertible)
 				g.Expect(spokeAfter.ConvertFrom(hubCopy)).To(gomega.Succeed())
 
 				// Remove data annotation eventually added by ConvertFrom for avoiding data loss in hub-spoke-hub round trips
 				metaAfter := spokeAfter.(metav1.Object)
 				delete(metaAfter.GetAnnotations(), DataAnnotation)
 
-				if input.SpokeAfterMutation != nil {
-					input.SpokeAfterMutation(spokeAfter)
-				}
-
 				g.Expect(apiequality.Semantic.DeepEqual(spokeBefore, spokeAfter)).To(gomega.BeTrue(), cmp.Diff(spokeBefore, spokeAfter))
 			}
 		})
 		t.Run("hub-spoke-hub", func(t *testing.T) {
 			g := gomega.NewWithT(t)
-			fuzzer := GetFuzzer(input.Scheme, input.FuzzerFuncs...)
+			fuzzer := GetFuzzer(scheme, funcs...)
 
 			for i := 0; i < 10000; i++ {
 				// Create the hub and fuzz it
-				hubBefore := input.Hub.DeepCopyObject().(conversion.Hub)
+				hubBefore := hub.DeepCopyObject().(conversion.Hub)
 				fuzzer.Fuzz(hubBefore)
 
 				// First convert hub to spoke
-				dstCopy := input.Spoke.DeepCopyObject().(conversion.Convertible)
+				dstCopy := dst.DeepCopyObject().(conversion.Convertible)
 				g.Expect(dstCopy.ConvertFrom(hubBefore)).To(gomega.Succeed())
 
 				// Convert spoke back to hub and check if the resulting hub is equal to the hub before the round trip
-				hubAfter := input.Hub.DeepCopyObject().(conversion.Hub)
+				hubAfter := hub.DeepCopyObject().(conversion.Hub)
 				g.Expect(dstCopy.ConvertTo(hubAfter)).To(gomega.Succeed())
 
-				if input.HubAfterMutation != nil {
-					input.HubAfterMutation(hubAfter)
-				}
-
 				g.Expect(apiequality.Semantic.DeepEqual(hubBefore, hubAfter)).To(gomega.BeTrue(), cmp.Diff(hubBefore, hubAfter))
 			}
 		})


Test patch:
diff --git a/api/v1alpha3/conversion_test.go b/api/v1alpha3/conversion_test.go
index c55680620..6db751918 100644
--- a/api/v1alpha3/conversion_test.go
+++ b/api/v1alpha3/conversion_test.go
@@ -21,8 +21,6 @@ import (
 
 	fuzz "github.com/google/gofuzz"
 	. "github.com/onsi/gomega"
-	"k8s.io/apimachinery/pkg/api/apitesting/fuzzer"
-	"sigs.k8s.io/controller-runtime/pkg/conversion"
 
 	"k8s.io/apimachinery/pkg/runtime"
 	runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer"
@@ -36,39 +34,11 @@ func TestFuzzyConversion(t *testing.T) {
 	g.Expect(AddToScheme(scheme)).To(Succeed())
 	g.Expect(v1alpha4.AddToScheme(scheme)).To(Succeed())
 
-	t.Run("for Cluster", utilconversion.FuzzTestFunc(utilconversion.FuzzTestFuncInput{
-		Scheme:             scheme,
-		Hub:                &v1alpha4.Cluster{},
-		Spoke:              &Cluster{},
-		SpokeAfterMutation: clusterSpokeAfterMutation,
-	}))
-
-	t.Run("for Machine", utilconversion.FuzzTestFunc(utilconversion.FuzzTestFuncInput{
-		Scheme:      scheme,
-		Hub:         &v1alpha4.Machine{},
-		Spoke:       &Machine{},
-		FuzzerFuncs: []fuzzer.FuzzerFuncs{BootstrapFuzzFuncs},
-	}))
-
-	t.Run("for MachineSet", utilconversion.FuzzTestFunc(utilconversion.FuzzTestFuncInput{
-		Scheme:      scheme,
-		Hub:         &v1alpha4.MachineSet{},
-		Spoke:       &MachineSet{},
-		FuzzerFuncs: []fuzzer.FuzzerFuncs{BootstrapFuzzFuncs},
-	}))
-
-	t.Run("for MachineDeployment", utilconversion.FuzzTestFunc(utilconversion.FuzzTestFuncInput{
-		Scheme:      scheme,
-		Hub:         &v1alpha4.MachineDeployment{},
-		Spoke:       &MachineDeployment{},
-		FuzzerFuncs: []fuzzer.FuzzerFuncs{BootstrapFuzzFuncs},
-	}))
-
-	t.Run("for MachineHealthCheckSpec", utilconversion.FuzzTestFunc(utilconversion.FuzzTestFuncInput{
-		Scheme: scheme,
-		Hub:    &v1alpha4.MachineHealthCheck{},
-		Spoke:  &MachineHealthCheck{},
-	}))
+	t.Run("for Cluster", utilconversion.FuzzTestFunc(scheme, &v1alpha4.Cluster{}, &Cluster{}))
+	t.Run("for Machine", utilconversion.FuzzTestFunc(scheme, &v1alpha4.Machine{}, &Machine{}, BootstrapFuzzFuncs))
+	t.Run("for MachineSet", utilconversion.FuzzTestFunc(scheme, &v1alpha4.MachineSet{}, &MachineSet{}, BootstrapFuzzFuncs))
+	t.Run("for MachineDeployment", utilconversion.FuzzTestFunc(scheme, &v1alpha4.MachineDeployment{}, &MachineDeployment{}, BootstrapFuzzFuncs))
+	t.Run("for MachineHealthCheckSpec", utilconversion.FuzzTestFunc(scheme, &v1alpha4.MachineHealthCheck{}, &MachineHealthCheck{}))
 }
 
 func BootstrapFuzzFuncs(_ runtimeserializer.CodecFactory) []interface{} {
@@ -83,25 +53,3 @@ func BootstrapFuzzer(obj *Bootstrap, c fuzz.Continue) {
 	// Bootstrap.Data has been removed in v1alpha4, so setting it to nil in order to avoid v1alpha3 --> v1alpha4 --> v1alpha3 round trip errors.
 	obj.Data = nil
 }
-
-// clusterSpokeAfterMutation modifies the spoke version of the Cluster such that it can pass an equality test in the
-// spoke-hub-spoke conversion scenario.
-func clusterSpokeAfterMutation(c conversion.Convertible) {
-	cluster := c.(*Cluster)
-
-	// Create a temporary 0-length slice using the same underlying array as cluster.Status.Conditions to avoid
-	// allocations.
-	tmp := cluster.Status.Conditions[:0]
-
-	for i := range cluster.Status.Conditions {
-		condition := cluster.Status.Conditions[i]
-
-		// Keep everything that is not ControlPlaneInitializedCondition
-		if condition.Type != ConditionType(v1alpha4.ControlPlaneInitializedCondition) {
-			tmp = append(tmp, condition)
-		}
-	}
-
-	// Point cluster.Status.Conditions and our slice that does not have ControlPlaneInitializedCondition
-	cluster.Status.Conditions = tmp
-}
diff --git a/bootstrap/kubeadm/api/v1alpha3/conversion_test.go b/bootstrap/kubeadm/api/v1alpha3/conversion_test.go
index 88a622831..300b9fd50 100644
--- a/bootstrap/kubeadm/api/v1alpha3/conversion_test.go
+++ b/bootstrap/kubeadm/api/v1alpha3/conversion_test.go
@@ -21,7 +21,6 @@ import (
 
 	fuzz "github.com/google/gofuzz"
 	. "github.com/onsi/gomega"
-	"k8s.io/apimachinery/pkg/api/apitesting/fuzzer"
 	runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer"
 
 	"k8s.io/apimachinery/pkg/runtime"
@@ -35,17 +34,8 @@ func TestFuzzyConversion(t *testing.T) {
 	g.Expect(AddToScheme(scheme)).To(Succeed())
 	g.Expect(v1alpha4.AddToScheme(scheme)).To(Succeed())
 
-	t.Run("for KubeadmConfig", utilconversion.FuzzTestFunc(utilconversion.FuzzTestFuncInput{
-		Scheme:      scheme,
-		Hub:         &v1alpha4.KubeadmConfig{},
-		Spoke:       &KubeadmConfig{},
-		FuzzerFuncs: []fuzzer.FuzzerFuncs{KubeadmConfigStatusFuzzFuncs},
-	}))
-	t.Run("for KubeadmConfigTemplate", utilconversion.FuzzTestFunc(utilconversion.FuzzTestFuncInput{
-		Scheme: scheme,
-		Hub:    &v1alpha4.KubeadmConfigTemplate{},
-		Spoke:  &KubeadmConfigTemplate{},
-	}))
+	t.Run("for KubeadmConfig", utilconversion.FuzzTestFunc(scheme, &v1alpha4.KubeadmConfig{}, &KubeadmConfig{}, KubeadmConfigStatusFuzzFuncs))
+	t.Run("for KubeadmConfigTemplate", utilconversion.FuzzTestFunc(scheme, &v1alpha4.KubeadmConfigTemplate{}, &KubeadmConfigTemplate{}))
 }
 
 func KubeadmConfigStatusFuzzFuncs(_ runtimeserializer.CodecFactory) []interface{} {
diff --git a/bootstrap/kubeadm/controllers/kubeadmconfig_controller_test.go b/bootstrap/kubeadm/controllers/kubeadmconfig_controller_test.go
index 17261fbf4..bfdf03972 100644
--- a/bootstrap/kubeadm/controllers/kubeadmconfig_controller_test.go
+++ b/bootstrap/kubeadm/controllers/kubeadmconfig_controller_test.go
@@ -21,6 +21,7 @@ import (
 	"context"
 	"fmt"
 	"reflect"
+	"sigs.k8s.io/cluster-api/util/patch"
 	"testing"
 	"time"
 
@@ -40,7 +41,6 @@ import (
 	"sigs.k8s.io/cluster-api/test/helpers"
 	"sigs.k8s.io/cluster-api/util"
 	"sigs.k8s.io/cluster-api/util/conditions"
-	"sigs.k8s.io/cluster-api/util/patch"
 	"sigs.k8s.io/cluster-api/util/secret"
 	ctrl "sigs.k8s.io/controller-runtime"
 	"sigs.k8s.io/controller-runtime/pkg/client"
@@ -423,7 +423,7 @@ func TestKubeadmConfigReconciler_Reconcile_ErrorIfJoiningControlPlaneHasInvalidC
 	// TODO: extract this kind of code into a setup function that puts the state of objects into an initialized controlplane (implies secrets exist)
 	cluster := newCluster("cluster")
 	cluster.Status.InfrastructureReady = true
-	conditions.MarkTrue(cluster, clusterv1.ControlPlaneInitializedCondition)
+	cluster.Status.ControlPlaneInitialized = true
 	cluster.Spec.ControlPlaneEndpoint = clusterv1.APIEndpoint{Host: "100.105.150.1", Port: 6443}
 	controlPlaneInitMachine := newControlPlaneMachine(cluster, "control-plane-init-machine")
 	controlPlaneInitConfig := newControlPlaneInitKubeadmConfig(controlPlaneInitMachine, "control-plane-init-cfg")
@@ -462,7 +462,7 @@ func TestKubeadmConfigReconciler_Reconcile_RequeueIfControlPlaneIsMissingAPIEndp
 
 	cluster := newCluster("cluster")
 	cluster.Status.InfrastructureReady = true
-	conditions.MarkTrue(cluster, clusterv1.ControlPlaneInitializedCondition)
+	cluster.Status.ControlPlaneInitialized = true
 	controlPlaneInitMachine := newControlPlaneMachine(cluster, "control-plane-init-machine")
 	controlPlaneInitConfig := newControlPlaneInitKubeadmConfig(controlPlaneInitMachine, "control-plane-init-cfg")
 
@@ -498,7 +498,7 @@ func TestKubeadmConfigReconciler_Reconcile_RequeueIfControlPlaneIsMissingAPIEndp
 func TestReconcileIfJoinNodesAndControlPlaneIsReady(t *testing.T) {
 	cluster := newCluster("cluster")
 	cluster.Status.InfrastructureReady = true
-	conditions.MarkTrue(cluster, clusterv1.ControlPlaneInitializedCondition)
+	cluster.Status.ControlPlaneInitialized = true
 	cluster.Spec.ControlPlaneEndpoint = clusterv1.APIEndpoint{Host: "100.105.150.1", Port: 6443}
 
 	var useCases = []struct {
@@ -587,7 +587,7 @@ func TestReconcileIfJoinNodePoolsAndControlPlaneIsReady(t *testing.T) {
 
 	cluster := newCluster("cluster")
 	cluster.Status.InfrastructureReady = true
-	conditions.MarkTrue(cluster, clusterv1.ControlPlaneInitializedCondition)
+	cluster.Status.ControlPlaneInitialized = true
 	cluster.Spec.ControlPlaneEndpoint = clusterv1.APIEndpoint{Host: "100.105.150.1", Port: 6443}
 
 	var useCases = []struct {
@@ -666,7 +666,7 @@ func TestKubeadmConfigSecretCreatedStatusNotPatched(t *testing.T) {
 
 	cluster := newCluster("cluster")
 	cluster.Status.InfrastructureReady = true
-	conditions.MarkTrue(cluster, clusterv1.ControlPlaneInitializedCondition)
+	cluster.Status.ControlPlaneInitialized = true
 	cluster.Spec.ControlPlaneEndpoint = clusterv1.APIEndpoint{Host: "100.105.150.1", Port: 6443}
 
 	controlPlaneInitMachine := newControlPlaneMachine(cluster, "control-plane-init-machine")
@@ -735,7 +735,7 @@ func TestBootstrapTokenTTLExtension(t *testing.T) {
 
 	cluster := newCluster("cluster")
 	cluster.Status.InfrastructureReady = true
-	conditions.MarkTrue(cluster, clusterv1.ControlPlaneInitializedCondition)
+	cluster.Status.ControlPlaneInitialized = true
 	cluster.Spec.ControlPlaneEndpoint = clusterv1.APIEndpoint{Host: "100.105.150.1", Port: 6443}
 
 	controlPlaneInitMachine := newControlPlaneMachine(cluster, "control-plane-init-machine")
@@ -887,7 +887,7 @@ func TestBootstrapTokenRotationMachinePool(t *testing.T) {
 
 	cluster := newCluster("cluster")
 	cluster.Status.InfrastructureReady = true
-	conditions.MarkTrue(cluster, clusterv1.ControlPlaneInitializedCondition)
+	cluster.Status.ControlPlaneInitialized = true
 	cluster.Spec.ControlPlaneEndpoint = clusterv1.APIEndpoint{Host: "100.105.150.1", Port: 6443}
 
 	controlPlaneInitMachine := newControlPlaneMachine(cluster, "control-plane-init-machine")
@@ -1305,7 +1305,7 @@ func TestKubeadmConfigReconciler_Reconcile_AlwaysCheckCAVerificationUnlessReques
 	// Setup work for an initialized cluster
 	clusterName := "my-cluster"
 	cluster := newCluster(clusterName)
-	conditions.MarkTrue(cluster, clusterv1.ControlPlaneInitializedCondition)
+	cluster.Status.ControlPlaneInitialized = true
 	cluster.Status.InfrastructureReady = true
 	cluster.Spec.ControlPlaneEndpoint = clusterv1.APIEndpoint{
 		Host: "example.com",
@@ -1433,6 +1433,7 @@ func TestKubeadmConfigReconciler_Reconcile_DoesNotFailIfCASecretsAlreadyExist(t
 
 	cluster := newCluster("my-cluster")
 	cluster.Status.InfrastructureReady = true
+	cluster.Status.ControlPlaneInitialized = false
 	m := newControlPlaneMachine(cluster, "control-plane-machine")
 	configName := "my-config"
 	c := newControlPlaneInitKubeadmConfig(m, configName)
diff --git a/cmd/clusterctl/client/cluster/mover_test.go b/cmd/clusterctl/client/cluster/mover_test.go
index 07fc152ce..5718d4fa0 100644
--- a/cmd/clusterctl/client/cluster/mover_test.go
+++ b/cmd/clusterctl/client/cluster/mover_test.go
@@ -27,7 +27,6 @@ import (
 	clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha4"
 	clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3"
 	"sigs.k8s.io/cluster-api/cmd/clusterctl/internal/test"
-	"sigs.k8s.io/cluster-api/util/conditions"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 )
 
@@ -644,10 +643,8 @@ func Test_objectMover_checkProvisioningCompleted(t *testing.T) {
 							Name:      "cluster1",
 						},
 						Status: clusterv1.ClusterStatus{
-							InfrastructureReady: false,
-							Conditions: clusterv1.Conditions{
-								*conditions.TrueCondition(clusterv1.ControlPlaneInitializedCondition),
-							},
+							InfrastructureReady:     false,
+							ControlPlaneInitialized: true,
 						},
 					},
 				},
@@ -668,31 +665,8 @@ func Test_objectMover_checkProvisioningCompleted(t *testing.T) {
 							Name:      "cluster1",
 						},
 						Status: clusterv1.ClusterStatus{
-							InfrastructureReady: true,
-						},
-					},
-				},
-			},
-			wantErr: true,
-		},
-		{
-			name: "Blocks with a cluster with ControlPlaneInitialized=False",
-			fields: fields{
-				objs: []client.Object{
-					&clusterv1.Cluster{
-						TypeMeta: metav1.TypeMeta{
-							Kind:       "Cluster",
-							APIVersion: clusterv1.GroupVersion.String(),
-						},
-						ObjectMeta: metav1.ObjectMeta{
-							Namespace: "ns1",
-							Name:      "cluster1",
-						},
-						Status: clusterv1.ClusterStatus{
-							InfrastructureReady: true,
-							Conditions: clusterv1.Conditions{
-								*conditions.FalseCondition(clusterv1.ControlPlaneInitializedCondition, "", clusterv1.ConditionSeverityInfo, ""),
-							},
+							InfrastructureReady:     true,
+							ControlPlaneInitialized: false,
 						},
 					},
 				},
@@ -716,11 +690,9 @@ func Test_objectMover_checkProvisioningCompleted(t *testing.T) {
 							ControlPlaneRef: &corev1.ObjectReference{},
 						},
 						Status: clusterv1.ClusterStatus{
-							InfrastructureReady: true,
-							Conditions: clusterv1.Conditions{
-								*conditions.TrueCondition(clusterv1.ControlPlaneInitializedCondition),
-							},
-							ControlPlaneReady: false,
+							InfrastructureReady:     true,
+							ControlPlaneInitialized: true,
+							ControlPlaneReady:       false,
 						},
 					},
 				},
@@ -742,10 +714,8 @@ func Test_objectMover_checkProvisioningCompleted(t *testing.T) {
 							UID:       "cluster1",
 						},
 						Status: clusterv1.ClusterStatus{
-							InfrastructureReady: true,
-							Conditions: clusterv1.Conditions{
-								*conditions.TrueCondition(clusterv1.ControlPlaneInitializedCondition),
-							},
+							InfrastructureReady:     true,
+							ControlPlaneInitialized: true,
 						},
 					},
 					&clusterv1.Machine{
@@ -788,10 +758,8 @@ func Test_objectMover_checkProvisioningCompleted(t *testing.T) {
 							UID:       "cluster1",
 						},
 						Status: clusterv1.ClusterStatus{
-							InfrastructureReady: true,
-							Conditions: clusterv1.Conditions{
-								*conditions.TrueCondition(clusterv1.ControlPlaneInitializedCondition),
-							},
+							InfrastructureReady:     true,
+							ControlPlaneInitialized: true,
 						},
 					},
 					&clusterv1.Machine{
diff --git a/controllers/cluster_controller_test.go b/controllers/cluster_controller_test.go
index bb30a4f0a..4636d03e5 100644
--- a/controllers/cluster_controller_test.go
+++ b/controllers/cluster_controller_test.go
@@ -21,7 +21,6 @@ import (
 
 	. "github.com/onsi/ginkgo"
 	. "github.com/onsi/gomega"
-	"sigs.k8s.io/cluster-api/util/conditions"
 
 	corev1 "k8s.io/api/core/v1"
 	v1 "k8s.io/api/core/v1"
@@ -241,7 +240,7 @@ var _ = Describe("Cluster Reconciler", func() {
 		}, timeout).ShouldNot(BeEmpty())
 	})
 
-	It("Should successfully set ControlPlaneInitialized on the cluster object if controlplane is ready", func() {
+	It("Should successfully set Status.ControlPlaneInitialized on the cluster object if controlplane is ready", func() {
 		cluster := &clusterv1.Cluster{
 			ObjectMeta: metav1.ObjectMeta{
 				GenerateName: "test6-",
@@ -322,7 +321,7 @@ var _ = Describe("Cluster Reconciler", func() {
 			if err := testEnv.Get(ctx, key, cluster); err != nil {
 				return false
 			}
-			return conditions.IsTrue(cluster, clusterv1.ControlPlaneInitializedCondition)
+			return cluster.Status.ControlPlaneInitialized
 		}, timeout).Should(BeTrue())
 	})
 })
@@ -688,5 +687,5 @@ func TestReconcileControlPlaneInitializedControlPlaneRef(t *testing.T) {
 	res, err := r.reconcileControlPlaneInitialized(ctx, c)
 	g.Expect(res.IsZero()).To(BeTrue())
 	g.Expect(err).ToNot(HaveOccurred())
-	g.Expect(conditions.Has(c, clusterv1.ControlPlaneInitializedCondition)).To(BeFalse())
+	g.Expect(c.Status.ControlPlaneInitialized).To(BeFalse())
 }
diff --git a/controllers/machine_controller_test.go b/controllers/machine_controller_test.go
index 4c060aa3a..d0ca798f5 100644
--- a/controllers/machine_controller_test.go
+++ b/controllers/machine_controller_test.go
@@ -109,8 +109,14 @@ func TestWatches(t *testing.T) {
 		g.Expect(testEnv.Cleanup(ctx, do...)).To(Succeed())
 	}(ns, testCluster, defaultBootstrap)
 
+	// Patch cluster control plane initialized (this is required to start node watch)
+	patchHelper, err := patch.NewHelper(testCluster, testEnv)
+	g.Expect(err).ShouldNot(HaveOccurred())
+	testCluster.Status.ControlPlaneInitialized = true
+	g.Expect(patchHelper.Patch(ctx, testCluster, patch.WithStatusObservedGeneration{})).To(Succeed())
+
 	// Patch infra machine ready
-	patchHelper, err := patch.NewHelper(infraMachine, testEnv)
+	patchHelper, err = patch.NewHelper(infraMachine, testEnv)
 	g.Expect(err).ShouldNot(HaveOccurred())
 	g.Expect(unstructured.SetNestedField(infraMachine.Object, true, "status", "ready")).To(Succeed())
 	g.Expect(patchHelper.Patch(ctx, infraMachine, patch.WithStatusObservedGeneration{})).To(Succeed())
@@ -126,9 +132,6 @@ func TestWatches(t *testing.T) {
 		ObjectMeta: metav1.ObjectMeta{
 			GenerateName: "machine-created-",
 			Namespace:    ns.Name,
-			Labels: map[string]string{
-				clusterv1.MachineControlPlaneLabelName: "",
-			},
 		},
 		Spec: clusterv1.MachineSpec{
 			ClusterName: testCluster.Name,
diff --git a/controllers/machinehealthcheck_controller_test.go b/controllers/machinehealthcheck_controller_test.go
index 42b74b737..a353438ac 100644
--- a/controllers/machinehealthcheck_controller_test.go
+++ b/controllers/machinehealthcheck_controller_test.go
@@ -19,7 +19,7 @@ import (
 	"context"
 	"errors"
 	"fmt"
-
+	"sigs.k8s.io/controller-runtime/pkg/log"
 	"sort"
 	"testing"
 	"time"
@@ -38,13 +38,11 @@ import (
 	"k8s.io/utils/pointer"
 	clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha4"
 	"sigs.k8s.io/cluster-api/controllers/remote"
-	capierrors "sigs.k8s.io/cluster-api/errors"
 	"sigs.k8s.io/cluster-api/util"
 	"sigs.k8s.io/cluster-api/util/conditions"
 	"sigs.k8s.io/cluster-api/util/patch"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	"sigs.k8s.io/controller-runtime/pkg/client/fake"
-	"sigs.k8s.io/controller-runtime/pkg/log"
 	"sigs.k8s.io/controller-runtime/pkg/reconcile"
 )
 
@@ -175,115 +173,6 @@ func TestMachineHealthCheck_Reconcile(t *testing.T) {
 		))
 	})
 
-	t.Run("it ignores Machines not matching the label selector", func(t *testing.T) {
-		g := NewWithT(t)
-		cluster := createNamespaceAndCluster(g)
-
-		mhc := newMachineHealthCheck(cluster.Namespace, cluster.Name)
-
-		g.Expect(testEnv.Create(ctx, mhc)).To(Succeed())
-		defer func(do ...client.Object) {
-			g.Expect(testEnv.Cleanup(ctx, do...)).To(Succeed())
-		}(cluster, mhc)
-
-		// Healthy nodes and machines matching the MHC's label selector.
-		_, machines, cleanup := createMachinesWithNodes(g, cluster,
-			count(2),
-			firstMachineAsControlPlane(),
-			createNodeRefForMachine(true),
-			nodeStatus(corev1.ConditionTrue),
-			machineLabels(mhc.Spec.Selector.MatchLabels),
-		)
-		defer cleanup()
-		targetMachines := make([]string, len(machines))
-		for i, m := range machines {
-			targetMachines[i] = m.Name
-		}
-		sort.Strings(targetMachines)
-
-		// Healthy nodes and machines NOT matching the MHC's label selector.
-		_, _, cleanup2 := createMachinesWithNodes(g, cluster,
-			count(2),
-			createNodeRefForMachine(true),
-			nodeStatus(corev1.ConditionTrue),
-		)
-		defer cleanup2()
-
-		// Make sure the status matches.
-		g.Eventually(func() *clusterv1.MachineHealthCheckStatus {
-			err := testEnv.Get(ctx, util.ObjectKey(mhc), mhc)
-			if err != nil {
-				return nil
-			}
-			return &mhc.Status
-		}, 5*time.Second, 100*time.Millisecond).Should(MatchMachineHealthCheckStatus(&clusterv1.MachineHealthCheckStatus{
-			ExpectedMachines:    2,
-			CurrentHealthy:      2,
-			RemediationsAllowed: 2,
-			ObservedGeneration:  1,
-			Targets:             targetMachines,
-			Conditions: clusterv1.Conditions{
-				{
-					Type:   clusterv1.RemediationAllowedCondition,
-					Status: corev1.ConditionTrue,
-				},
-			},
-		}))
-	})
-
-	t.Run("it doesn't mark anything unhealthy when cluster infrastructure is not ready", func(t *testing.T) {
-		g := NewWithT(t)
-		cluster := createNamespaceAndCluster(g)
-
-		patchHelper, err := patch.NewHelper(cluster, testEnv.Client)
-		g.Expect(err).To(BeNil())
-
-		conditions.MarkFalse(cluster, clusterv1.InfrastructureReadyCondition, "SomeReason", clusterv1.ConditionSeverityError, "")
-		g.Expect(patchHelper.Patch(ctx, cluster)).To(Succeed())
-
-		mhc := newMachineHealthCheck(cluster.Namespace, cluster.Name)
-
-		g.Expect(testEnv.Create(ctx, mhc)).To(Succeed())
-		defer func(do ...client.Object) {
-			g.Expect(testEnv.Cleanup(ctx, do...)).To(Succeed())
-		}(cluster, mhc)
-
-		// Healthy nodes and machines.
-		_, machines, cleanup := createMachinesWithNodes(g, cluster,
-			count(2),
-			firstMachineAsControlPlane(),
-			createNodeRefForMachine(true),
-			machineLabels(mhc.Spec.Selector.MatchLabels),
-		)
-		defer cleanup()
-		targetMachines := make([]string, len(machines))
-		for i, m := range machines {
-			targetMachines[i] = m.Name
-		}
-		sort.Strings(targetMachines)
-
-		// Make sure the status matches.
-		g.Eventually(func() *clusterv1.MachineHealthCheckStatus {
-			err := testEnv.Get(ctx, util.ObjectKey(mhc), mhc)
-			if err != nil {
-				return nil
-			}
-			return &mhc.Status
-		}).Should(MatchMachineHealthCheckStatus(&clusterv1.MachineHealthCheckStatus{
-			ExpectedMachines:    2,
-			CurrentHealthy:      2,
-			RemediationsAllowed: 2,
-			ObservedGeneration:  1,
-			Targets:             targetMachines,
-			Conditions: clusterv1.Conditions{
-				{
-					Type:   clusterv1.RemediationAllowedCondition,
-					Status: corev1.ConditionTrue,
-				},
-			},
-		}))
-	})
-
 	t.Run("it doesn't mark anything unhealthy when all Machines are healthy", func(t *testing.T) {
 		g := NewWithT(t)
 		cluster := createNamespaceAndCluster(g)
@@ -298,9 +187,8 @@ func TestMachineHealthCheck_Reconcile(t *testing.T) {
 		// Healthy nodes and machines.
 		_, machines, cleanup := createMachinesWithNodes(g, cluster,
 			count(2),
-			firstMachineAsControlPlane(),
 			createNodeRefForMachine(true),
-			nodeStatus(corev1.ConditionTrue),
+			markNodeAsHealthy(true),
 			machineLabels(mhc.Spec.Selector.MatchLabels),
 		)
 		defer cleanup()
@@ -346,9 +234,8 @@ func TestMachineHealthCheck_Reconcile(t *testing.T) {
 		// Healthy nodes and machines.
 		_, machines, cleanup1 := createMachinesWithNodes(g, cluster,
 			count(2),
-			firstMachineAsControlPlane(),
 			createNodeRefForMachine(true),
-			nodeStatus(corev1.ConditionTrue),
+			markNodeAsHealthy(true),
 			machineLabels(mhc.Spec.Selector.MatchLabels),
 		)
 		defer cleanup1()
@@ -356,7 +243,7 @@ func TestMachineHealthCheck_Reconcile(t *testing.T) {
 		_, unhealthyMachines, cleanup2 := createMachinesWithNodes(g, cluster,
 			count(1),
 			createNodeRefForMachine(true),
-			nodeStatus(corev1.ConditionUnknown),
+			markNodeAsHealthy(false),
 			machineLabels(mhc.Spec.Selector.MatchLabels),
 		)
 		defer cleanup2()
@@ -389,122 +276,6 @@ func TestMachineHealthCheck_Reconcile(t *testing.T) {
 		}))
 	})
 
-	t.Run("it marks unhealthy machines for remediation when there a Machine has a failure reason", func(t *testing.T) {
-		g := NewWithT(t)
-		cluster := createNamespaceAndCluster(g)
-
-		mhc := newMachineHealthCheck(cluster.Namespace, cluster.Name)
-
-		g.Expect(testEnv.Create(ctx, mhc)).To(Succeed())
-		defer func(do ...client.Object) {
-			g.Expect(testEnv.Cleanup(ctx, do...)).To(Succeed())
-		}(cluster, mhc)
-
-		// Healthy nodes and machines.
-		_, machines, cleanup1 := createMachinesWithNodes(g, cluster,
-			count(2),
-			firstMachineAsControlPlane(),
-			createNodeRefForMachine(true),
-			nodeStatus(corev1.ConditionTrue),
-			machineLabels(mhc.Spec.Selector.MatchLabels),
-		)
-		defer cleanup1()
-		// Machine with failure reason.
-		_, unhealthyMachines, cleanup2 := createMachinesWithNodes(g, cluster,
-			count(1),
-			createNodeRefForMachine(true),
-			nodeStatus(corev1.ConditionTrue),
-			machineLabels(mhc.Spec.Selector.MatchLabels),
-			machineFailureReason("some failure"),
-		)
-		defer cleanup2()
-		machines = append(machines, unhealthyMachines...)
-		targetMachines := make([]string, len(machines))
-		for i, m := range machines {
-			targetMachines[i] = m.Name
-		}
-		sort.Strings(targetMachines)
-
-		// Make sure the status matches.
-		g.Eventually(func() *clusterv1.MachineHealthCheckStatus {
-			err := testEnv.Get(ctx, util.ObjectKey(mhc), mhc)
-			if err != nil {
-				return nil
-			}
-			return &mhc.Status
-		}).Should(MatchMachineHealthCheckStatus(&clusterv1.MachineHealthCheckStatus{
-			ExpectedMachines:    3,
-			CurrentHealthy:      2,
-			RemediationsAllowed: 2,
-			ObservedGeneration:  1,
-			Targets:             targetMachines,
-			Conditions: clusterv1.Conditions{
-				{
-					Type:   clusterv1.RemediationAllowedCondition,
-					Status: corev1.ConditionTrue,
-				},
-			},
-		}))
-	})
-
-	t.Run("it marks unhealthy machines for remediation when there a Machine has a failure message", func(t *testing.T) {
-		g := NewWithT(t)
-		cluster := createNamespaceAndCluster(g)
-
-		mhc := newMachineHealthCheck(cluster.Namespace, cluster.Name)
-
-		g.Expect(testEnv.Create(ctx, mhc)).To(Succeed())
-		defer func(do ...client.Object) {
-			g.Expect(testEnv.Cleanup(ctx, do...)).To(Succeed())
-		}(cluster, mhc)
-
-		// Healthy nodes and machines.
-		_, machines, cleanup1 := createMachinesWithNodes(g, cluster,
-			count(2),
-			firstMachineAsControlPlane(),
-			createNodeRefForMachine(true),
-			nodeStatus(corev1.ConditionTrue),
-			machineLabels(mhc.Spec.Selector.MatchLabels),
-		)
-		defer cleanup1()
-		// Machine with failure message.
-		_, unhealthyMachines, cleanup2 := createMachinesWithNodes(g, cluster,
-			count(1),
-			createNodeRefForMachine(true),
-			nodeStatus(corev1.ConditionTrue),
-			machineLabels(mhc.Spec.Selector.MatchLabels),
-			machineFailureMessage("some failure"),
-		)
-		defer cleanup2()
-		machines = append(machines, unhealthyMachines...)
-		targetMachines := make([]string, len(machines))
-		for i, m := range machines {
-			targetMachines[i] = m.Name
-		}
-		sort.Strings(targetMachines)
-
-		// Make sure the status matches.
-		g.Eventually(func() *clusterv1.MachineHealthCheckStatus {
-			err := testEnv.Get(ctx, util.ObjectKey(mhc), mhc)
-			if err != nil {
-				return nil
-			}
-			return &mhc.Status
-		}).Should(MatchMachineHealthCheckStatus(&clusterv1.MachineHealthCheckStatus{
-			ExpectedMachines:    3,
-			CurrentHealthy:      2,
-			RemediationsAllowed: 2,
-			ObservedGeneration:  1,
-			Targets:             targetMachines,
-			Conditions: clusterv1.Conditions{
-				{
-					Type:   clusterv1.RemediationAllowedCondition,
-					Status: corev1.ConditionTrue,
-				},
-			},
-		}))
-	})
-
 	t.Run("it marks unhealthy machines for remediation when the unhealthy Machines exceed MaxUnhealthy", func(t *testing.T) {
 		g := NewWithT(t)
 		cluster := createNamespaceAndCluster(g)
@@ -521,9 +292,8 @@ func TestMachineHealthCheck_Reconcile(t *testing.T) {
 		// Healthy nodes and machines.
 		_, machines, cleanup1 := createMachinesWithNodes(g, cluster,
 			count(1),
-			firstMachineAsControlPlane(),
 			createNodeRefForMachine(true),
-			nodeStatus(corev1.ConditionTrue),
+			markNodeAsHealthy(true),
 			machineLabels(mhc.Spec.Selector.MatchLabels),
 		)
 		defer cleanup1()
@@ -531,7 +301,7 @@ func TestMachineHealthCheck_Reconcile(t *testing.T) {
 		_, unhealthyMachines, cleanup2 := createMachinesWithNodes(g, cluster,
 			count(2),
 			createNodeRefForMachine(true),
-			nodeStatus(corev1.ConditionUnknown),
+			markNodeAsHealthy(false),
 			machineLabels(mhc.Spec.Selector.MatchLabels),
 		)
 		defer cleanup2()
@@ -595,7 +365,7 @@ func TestMachineHealthCheck_Reconcile(t *testing.T) {
 			}
 
 			for i := range machines.Items {
-				if conditions.IsTrue(&machines.Items[i], clusterv1.MachineOwnerRemediatedCondition) {
+				if conditions.Get(&machines.Items[i], clusterv1.MachineOwnerRemediatedCondition) != nil {
 					remediated++
 				}
 			}
@@ -619,9 +389,8 @@ func TestMachineHealthCheck_Reconcile(t *testing.T) {
 		// Healthy nodes and machines.
 		_, machines, cleanup1 := createMachinesWithNodes(g, cluster,
 			count(2),
-			firstMachineAsControlPlane(),
 			createNodeRefForMachine(true),
-			nodeStatus(corev1.ConditionTrue),
+			markNodeAsHealthy(true),
 			machineLabels(mhc.Spec.Selector.MatchLabels),
 		)
 		defer cleanup1()
@@ -629,7 +398,7 @@ func TestMachineHealthCheck_Reconcile(t *testing.T) {
 		_, unhealthyMachines, cleanup2 := createMachinesWithNodes(g, cluster,
 			count(1),
 			createNodeRefForMachine(true),
-			nodeStatus(corev1.ConditionUnknown),
+			markNodeAsHealthy(false),
 			machineLabels(mhc.Spec.Selector.MatchLabels),
 		)
 		defer cleanup2()
@@ -678,9 +447,8 @@ func TestMachineHealthCheck_Reconcile(t *testing.T) {
 		// Healthy nodes and machines.
 		_, machines, cleanup1 := createMachinesWithNodes(g, cluster,
 			count(1),
-			firstMachineAsControlPlane(),
 			createNodeRefForMachine(true),
-			nodeStatus(corev1.ConditionTrue),
+			markNodeAsHealthy(true),
 			machineLabels(mhc.Spec.Selector.MatchLabels),
 		)
 		defer cleanup1()
@@ -688,7 +456,7 @@ func TestMachineHealthCheck_Reconcile(t *testing.T) {
 		_, unhealthyMachines, cleanup2 := createMachinesWithNodes(g, cluster,
 			count(2),
 			createNodeRefForMachine(true),
-			nodeStatus(corev1.ConditionUnknown),
+			markNodeAsHealthy(false),
 			machineLabels(mhc.Spec.Selector.MatchLabels),
 		)
 		defer cleanup2()
@@ -764,14 +532,6 @@ func TestMachineHealthCheck_Reconcile(t *testing.T) {
 		g := NewWithT(t)
 		cluster := createNamespaceAndCluster(g)
 
-		// After the cluster exists, we have to set the infrastructure ready condition; otherwise, MachineHealthChecks
-		// will never fail when nodeStartupTimeout is exceeded.
-		patchHelper, err := patch.NewHelper(cluster, testEnv.GetClient())
-		g.Expect(err).ToNot(HaveOccurred())
-
-		conditions.MarkTrue(cluster, clusterv1.InfrastructureReadyCondition)
-		g.Expect(patchHelper.Patch(ctx, cluster)).To(Succeed())
-
 		mhc := newMachineHealthCheck(cluster.Namespace, cluster.Name)
 		mhc.Spec.NodeStartupTimeout = &metav1.Duration{Duration: 5 * time.Hour}
 
@@ -783,9 +543,8 @@ func TestMachineHealthCheck_Reconcile(t *testing.T) {
 		// Healthy nodes and machines.
 		_, machines, cleanup1 := createMachinesWithNodes(g, cluster,
 			count(2),
-			firstMachineAsControlPlane(),
 			createNodeRefForMachine(true),
-			nodeStatus(corev1.ConditionTrue),
+			markNodeAsHealthy(true),
 			machineLabels(mhc.Spec.Selector.MatchLabels),
 		)
 		defer cleanup1()
@@ -793,7 +552,7 @@ func TestMachineHealthCheck_Reconcile(t *testing.T) {
 		_, unhealthyMachines, cleanup2 := createMachinesWithNodes(g, cluster,
 			count(1),
 			createNodeRefForMachine(false),
-			nodeStatus(corev1.ConditionUnknown),
+			markNodeAsHealthy(false),
 			machineLabels(mhc.Spec.Selector.MatchLabels),
 		)
 		defer cleanup2()
@@ -854,7 +613,7 @@ func TestMachineHealthCheck_Reconcile(t *testing.T) {
 			}
 
 			for i := range machines.Items {
-				if conditions.IsTrue(&machines.Items[i], clusterv1.MachineOwnerRemediatedCondition) {
+				if conditions.Get(&machines.Items[i], clusterv1.MachineOwnerRemediatedCondition) != nil {
 					remediated++
 				}
 			}
@@ -879,9 +638,8 @@ func TestMachineHealthCheck_Reconcile(t *testing.T) {
 		// Healthy nodes and machines.
 		_, machines, cleanup1 := createMachinesWithNodes(g, cluster,
 			count(2),
-			firstMachineAsControlPlane(),
 			createNodeRefForMachine(true),
-			nodeStatus(corev1.ConditionTrue),
+			markNodeAsHealthy(true),
 			machineLabels(mhc.Spec.Selector.MatchLabels),
 		)
 		defer cleanup1()
@@ -889,7 +647,7 @@ func TestMachineHealthCheck_Reconcile(t *testing.T) {
 		_, unhealthyMachines, cleanup2 := createMachinesWithNodes(g, cluster,
 			count(1),
 			createNodeRefForMachine(false),
-			nodeStatus(corev1.ConditionUnknown),
+			markNodeAsHealthy(false),
 			machineLabels(mhc.Spec.Selector.MatchLabels),
 		)
 		defer cleanup2()
@@ -978,9 +736,8 @@ func TestMachineHealthCheck_Reconcile(t *testing.T) {
 		// Healthy nodes and machines.
 		nodes, machines, cleanup := createMachinesWithNodes(g, cluster,
 			count(3),
-			firstMachineAsControlPlane(),
 			createNodeRefForMachine(true),
-			nodeStatus(corev1.ConditionTrue),
+			markNodeAsHealthy(true),
 			machineLabels(mhc.Spec.Selector.MatchLabels),
 		)
 		defer cleanup()
@@ -1071,9 +828,8 @@ func TestMachineHealthCheck_Reconcile(t *testing.T) {
 		// Healthy nodes and machines.
 		nodes, machines, cleanup := createMachinesWithNodes(g, cluster,
 			count(1),
-			firstMachineAsControlPlane(),
 			createNodeRefForMachine(true),
-			nodeStatus(corev1.ConditionTrue),
+			markNodeAsHealthy(true),
 			machineLabels(mhc.Spec.Selector.MatchLabels),
 		)
 		defer cleanup()
@@ -1154,7 +910,7 @@ func TestMachineHealthCheck_Reconcile(t *testing.T) {
 			return
 		}).Should(Equal(1))
 
-		// Calculate how many Machines have been marked for remediation
+		// Calculate how many Machines have been remediated.
 		g.Eventually(func() (remediated int) {
 			machines := &clusterv1.MachineList{}
 			err := testEnv.List(ctx, machines, client.MatchingLabels{
@@ -1165,7 +921,7 @@ func TestMachineHealthCheck_Reconcile(t *testing.T) {
 			}
 
 			for i := range machines.Items {
-				if conditions.IsFalse(&machines.Items[i], clusterv1.MachineOwnerRemediatedCondition) {
+				if conditions.Get(&machines.Items[i], clusterv1.MachineOwnerRemediatedCondition) != nil {
 					remediated++
 				}
 			}
@@ -1177,15 +933,6 @@ func TestMachineHealthCheck_Reconcile(t *testing.T) {
 		g := NewWithT(t)
 		cluster := createNamespaceAndCluster(g)
 
-		// Create 1 control plane machine so MHC can proceed
-		_, _, cleanup := createMachinesWithNodes(g, cluster,
-			count(1),
-			firstMachineAsControlPlane(),
-			createNodeRefForMachine(true),
-			nodeStatus(corev1.ConditionTrue),
-		)
-		defer cleanup()
-
 		mhc := newMachineHealthCheck(cluster.Namespace, cluster.Name)
 		// Create infrastructure template resource.
 		infraResource := map[string]interface{}{
@@ -1337,9 +1084,8 @@ func TestMachineHealthCheck_Reconcile(t *testing.T) {
 		// Healthy nodes and machines.
 		nodes, machines, cleanup := createMachinesWithNodes(g, cluster,
 			count(1),
-			firstMachineAsControlPlane(),
 			createNodeRefForMachine(true),
-			nodeStatus(corev1.ConditionTrue),
+			markNodeAsHealthy(true),
 			machineLabels(mhc.Spec.Selector.MatchLabels),
 		)
 		defer cleanup()
@@ -1488,9 +1234,8 @@ func TestMachineHealthCheck_Reconcile(t *testing.T) {
 		// Healthy nodes and machines.
 		nodes, machines, cleanup := createMachinesWithNodes(g, cluster,
 			count(1),
-			firstMachineAsControlPlane(),
 			createNodeRefForMachine(true),
-			nodeStatus(corev1.ConditionTrue),
+			markNodeAsHealthy(true),
 			machineLabels(mhc.Spec.Selector.MatchLabels),
 		)
 		defer cleanup()
@@ -1636,9 +1381,8 @@ func TestMachineHealthCheck_Reconcile(t *testing.T) {
 		// Healthy nodes and machines.
 		nodes, machines, cleanup := createMachinesWithNodes(g, cluster,
 			count(1),
-			firstMachineAsControlPlane(),
 			createNodeRefForMachine(true),
-			nodeStatus(corev1.ConditionTrue),
+			markNodeAsHealthy(true),
 			machineLabels(mhc.Spec.Selector.MatchLabels),
 		)
 		defer cleanup()
@@ -2251,38 +1995,18 @@ func ownerReferenceForCluster(ctx context.Context, g *WithT, c *clusterv1.Cluste
 func createNamespaceAndCluster(g *WithT) *clusterv1.Cluster {
 	ns, err := testEnv.CreateNamespace(ctx, "test-mhc")
 	g.Expect(err).ToNot(HaveOccurred())
-
 	cluster := &clusterv1.Cluster{
 		ObjectMeta: metav1.ObjectMeta{
 			GenerateName: "test-cluster-",
 			Namespace:    ns.Name,
 		},
 	}
-
 	g.Expect(testEnv.Create(ctx, cluster)).To(Succeed())
-
-	// Make sure the cluster is in the cache before proceeding
 	g.Eventually(func() error {
 		var cl clusterv1.Cluster
 		return testEnv.Get(ctx, util.ObjectKey(cluster), &cl)
 	}, timeout, 100*time.Millisecond).Should(Succeed())
 
-	// This is required for MHC to perform checks
-	patchHelper, err := patch.NewHelper(cluster, testEnv.Client)
-	g.Expect(err).To(BeNil())
-	conditions.MarkTrue(cluster, clusterv1.InfrastructureReadyCondition)
-	g.Expect(patchHelper.Patch(ctx, cluster)).To(Succeed())
-
-	// Wait for cluster in cache to be updated post-patch
-	g.Eventually(func() bool {
-		err := testEnv.Get(ctx, util.ObjectKey(cluster), cluster)
-		if err != nil {
-			return false
-		}
-
-		return conditions.IsTrue(cluster, clusterv1.InfrastructureReadyCondition)
-	}, timeout, 100*time.Millisecond).Should(BeTrue())
-
 	g.Expect(testEnv.CreateKubeconfigSecret(ctx, cluster)).To(Succeed())
 
 	return cluster
@@ -2315,6 +2039,28 @@ func newRunningMachine(c *clusterv1.Cluster, labels map[string]string) *clusterv
 	}
 }
 
+// newNode creaetes a Node object with node condition Ready == True
+func newNode() *corev1.Node {
+	return &corev1.Node{
+		ObjectMeta: metav1.ObjectMeta{
+			GenerateName: "test-mhc-node-",
+		},
+		Status: corev1.NodeStatus{
+			Conditions: []corev1.NodeCondition{
+				{Type: corev1.NodeReady, Status: corev1.ConditionTrue},
+			},
+		},
+	}
+}
+
+func setNodeUnhealthy(node *corev1.Node) {
+	node.Status.Conditions[0] = corev1.NodeCondition{
+		Type:               corev1.NodeReady,
+		Status:             corev1.ConditionUnknown,
+		LastTransitionTime: metav1.NewTime(time.Now().Add(-10 * time.Minute)),
+	}
+}
+
 func newInfraMachine(machine *clusterv1.Machine) (*unstructured.Unstructured, string) {
 	providerID := fmt.Sprintf("test:////%v", uuid.NewUUID())
 	return &unstructured.Unstructured{
@@ -2333,13 +2079,10 @@ func newInfraMachine(machine *clusterv1.Machine) (*unstructured.Unstructured, st
 }
 
 type machinesWithNodes struct {
-	count                      int
-	nodeStatus                 corev1.ConditionStatus
-	createNodeRefForMachine    bool
-	firstMachineAsControlPlane bool
-	labels                     map[string]string
-	failureReason              string
-	failureMessage             string
+	count                   int
+	markNodeAsHealthy       bool
+	createNodeRefForMachine bool
+	labels                  map[string]string
 }
 
 type machineWithNodesOption func(m *machinesWithNodes)
@@ -2350,15 +2093,9 @@ func count(n int) machineWithNodesOption {
 	}
 }
 
-func firstMachineAsControlPlane() machineWithNodesOption {
-	return func(m *machinesWithNodes) {
-		m.firstMachineAsControlPlane = true
-	}
-}
-
-func nodeStatus(s corev1.ConditionStatus) machineWithNodesOption {
+func markNodeAsHealthy(b bool) machineWithNodesOption {
 	return func(m *machinesWithNodes) {
-		m.nodeStatus = s
+		m.markNodeAsHealthy = b
 	}
 }
 
@@ -2374,18 +2111,6 @@ func machineLabels(l map[string]string) machineWithNodesOption {
 	}
 }
 
-func machineFailureReason(s string) machineWithNodesOption {
-	return func(m *machinesWithNodes) {
-		m.failureReason = s
-	}
-}
-
-func machineFailureMessage(s string) machineWithNodesOption {
-	return func(m *machinesWithNodes) {
-		m.failureMessage = s
-	}
-}
-
 func createMachinesWithNodes(
 	g *WithT,
 	c *clusterv1.Cluster,
@@ -2405,12 +2130,6 @@ func createMachinesWithNodes(
 
 	for i := 0; i < o.count; i++ {
 		machine := newRunningMachine(c, o.labels)
-		if i == 0 && o.firstMachineAsControlPlane {
-			if machine.Labels == nil {
-				machine.Labels = make(map[string]string)
-			}
-			machine.Labels[clusterv1.MachineControlPlaneLabelName] = ""
-		}
 		infraMachine, providerID := newInfraMachine(machine)
 		g.Expect(testEnv.Create(ctx, infraMachine)).To(Succeed())
 		infraMachines = append(infraMachines, infraMachine)
@@ -2444,61 +2163,36 @@ func createMachinesWithNodes(
 			return machine.Status.LastUpdated
 		}, timeout, 100*time.Millisecond).ShouldNot(BeNil())
 
-		machinePatchHelper, err := patch.NewHelper(machine, testEnv.Client)
-		g.Expect(err).To(BeNil())
-
 		if o.createNodeRefForMachine {
-			// Create node
-			node := &corev1.Node{
-				ObjectMeta: metav1.ObjectMeta{
-					GenerateName: "test-mhc-node-",
-				},
-				Spec: corev1.NodeSpec{
-					ProviderID: providerID,
-				},
+			node := newNode()
+			if !o.markNodeAsHealthy {
+				setNodeUnhealthy(node)
 			}
-
+			machineStatus := machine.Status
+			node.Spec.ProviderID = providerID
+			nodeStatus := node.Status
 			g.Expect(testEnv.Create(ctx, node)).To(Succeed())
 			fmt.Printf("node created: %s\n", node.GetName())
 
-			// Patch node status
-			nodePatchHelper, err := patch.NewHelper(node, testEnv.Client)
-			g.Expect(err).To(BeNil())
-
-			node.Status.Conditions = []corev1.NodeCondition{
-				{
-					Type:               corev1.NodeReady,
-					Status:             o.nodeStatus,
-					LastTransitionTime: metav1.NewTime(time.Now().Add(-10 * time.Minute)),
-				},
-			}
-
-			g.Expect(nodePatchHelper.Patch(ctx, node)).To(Succeed())
-
+			nodePatch := client.MergeFrom(node.DeepCopy())
+			node.Status = nodeStatus
+			g.Expect(testEnv.Status().Patch(ctx, node, nodePatch)).To(Succeed())
 			nodes = append(nodes, node)
 
+			machinePatch := client.MergeFrom(machine.DeepCopy())
+			machine.Status = machineStatus
 			machine.Status.NodeRef = &corev1.ObjectReference{
 				Name: node.Name,
 			}
-		}
 
-		if o.failureReason != "" {
-			failureReason := capierrors.MachineStatusError(o.failureReason)
-			machine.Status.FailureReason = &failureReason
-		}
-		if o.failureMessage != "" {
-			machine.Status.FailureMessage = pointer.StringPtr(o.failureMessage)
+			// Adding one second to ensure there is a difference from the
+			// original time so that the patch works. That is, ensure the
+			// precision isn't lost during conversions.
+			lastUp := metav1.NewTime(machine.Status.LastUpdated.Add(time.Second))
+			machine.Status.LastUpdated = &lastUp
+			g.Expect(testEnv.Status().Patch(ctx, machine, machinePatch)).To(Succeed())
 		}
 
-		// Adding one second to ensure there is a difference from the
-		// original time so that the patch works. That is, ensure the
-		// precision isn't lost during conversions.
-		lastUp := metav1.NewTime(machine.Status.LastUpdated.Add(time.Second))
-		machine.Status.LastUpdated = &lastUp
-
-		// Patch the machine to record the status changes
-		g.Expect(machinePatchHelper.Patch(ctx, machine)).To(Succeed())
-
 		machines = append(machines, machine)
 	}
 
diff --git a/controllers/machinehealthcheck_targets_test.go b/controllers/machinehealthcheck_targets_test.go
index caab2448f..a0a8cb9d0 100644
--- a/controllers/machinehealthcheck_targets_test.go
+++ b/controllers/machinehealthcheck_targets_test.go
@@ -21,12 +21,12 @@ import (
 	"time"
 
 	. "github.com/onsi/gomega"
+
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/client-go/kubernetes/scheme"
 	"k8s.io/client-go/tools/record"
 	clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha4"
-	"sigs.k8s.io/cluster-api/util/conditions"
 	"sigs.k8s.io/cluster-api/util/patch"
 	ctrl "sigs.k8s.io/controller-runtime"
 	"sigs.k8s.io/controller-runtime/pkg/client"
@@ -36,14 +36,6 @@ import (
 func TestGetTargetsFromMHC(t *testing.T) {
 	namespace := "test-mhc"
 	clusterName := "test-cluster"
-
-	cluster := &clusterv1.Cluster{
-		ObjectMeta: metav1.ObjectMeta{
-			Namespace: namespace,
-			Name:      clusterName,
-		},
-	}
-
 	mhcSelector := map[string]string{"cluster": clusterName, "machine-group": "foo"}
 
 	// Create a namespace for the tests
@@ -70,7 +62,7 @@ func TestGetTargetsFromMHC(t *testing.T) {
 		},
 	}
 
-	baseObjects := []client.Object{testNS, cluster, testMHC}
+	baseObjects := []client.Object{testNS, testMHC}
 
 	// Initialise some test machines and nodes for use in the test cases
 
@@ -187,20 +179,8 @@ func TestGetTargetsFromMHC(t *testing.T) {
 func TestHealthCheckTargets(t *testing.T) {
 	namespace := "test-mhc"
 	clusterName := "test-cluster"
-
-	cluster := &clusterv1.Cluster{
-		ObjectMeta: metav1.ObjectMeta{
-			Namespace: namespace,
-			Name:      clusterName,
-		},
-	}
-	conditions.MarkTrue(cluster, clusterv1.InfrastructureReadyCondition)
-	conditions.MarkTrue(cluster, clusterv1.ControlPlaneInitializedCondition)
-
 	mhcSelector := map[string]string{"cluster": clusterName, "machine-group": "foo"}
 
-	timeoutForMachineToHaveNode := 10 * time.Minute
-
 	// Create a test MHC
 	testMHC := &clusterv1.MachineHealthCheck{
 		ObjectMeta: metav1.ObjectMeta{
@@ -235,7 +215,6 @@ func TestHealthCheckTargets(t *testing.T) {
 	testMachineLastUpdated400s.Status.LastUpdated = &nowMinus400s
 
 	nodeNotYetStartedTarget := healthCheckTarget{
-		Cluster: cluster,
 		MHC:     testMHC,
 		Machine: testMachineLastUpdated400s,
 		Node:    nil,
@@ -243,7 +222,6 @@ func TestHealthCheckTargets(t *testing.T) {
 
 	// Target for when the Node has been seen, but has now gone
 	nodeGoneAway := healthCheckTarget{
-		Cluster:     cluster,
 		MHC:         testMHC,
 		Machine:     testMachine,
 		Node:        &corev1.Node{},
@@ -253,7 +231,6 @@ func TestHealthCheckTargets(t *testing.T) {
 	// Target for when the node has been in an unknown state for shorter than the timeout
 	testNodeUnknown200 := newTestUnhealthyNode("node1", corev1.NodeReady, corev1.ConditionUnknown, 200*time.Second)
 	nodeUnknown200 := healthCheckTarget{
-		Cluster:     cluster,
 		MHC:         testMHC,
 		Machine:     testMachine,
 		Node:        testNodeUnknown200,
@@ -263,7 +240,6 @@ func TestHealthCheckTargets(t *testing.T) {
 	// Second Target for when the node has been in an unknown state for shorter than the timeout
 	testNodeUnknown100 := newTestUnhealthyNode("node1", corev1.NodeReady, corev1.ConditionUnknown, 100*time.Second)
 	nodeUnknown100 := healthCheckTarget{
-		Cluster:     cluster,
 		MHC:         testMHC,
 		Machine:     testMachine,
 		Node:        testNodeUnknown100,
@@ -273,7 +249,6 @@ func TestHealthCheckTargets(t *testing.T) {
 	// Target for when the node has been in an unknown state for longer than the timeout
 	testNodeUnknown400 := newTestUnhealthyNode("node1", corev1.NodeReady, corev1.ConditionUnknown, 400*time.Second)
 	nodeUnknown400 := healthCheckTarget{
-		Cluster:     cluster,
 		MHC:         testMHC,
 		Machine:     testMachine,
 		Node:        testNodeUnknown400,
@@ -284,7 +259,6 @@ func TestHealthCheckTargets(t *testing.T) {
 	testNodeHealthy := newTestNode("node1")
 	testNodeHealthy.UID = "12345"
 	nodeHealthy := healthCheckTarget{
-		Cluster:     cluster,
 		MHC:         testMHC,
 		Machine:     testMachine,
 		Node:        testNodeHealthy,
@@ -303,7 +277,7 @@ func TestHealthCheckTargets(t *testing.T) {
 			targets:                  []healthCheckTarget{nodeNotYetStartedTarget},
 			expectedHealthy:          []healthCheckTarget{},
 			expectedNeedsRemediation: []healthCheckTarget{},
-			expectedNextCheckTimes:   []time.Duration{timeoutForMachineToHaveNode},
+			expectedNextCheckTimes:   []time.Duration{200 * time.Second},
 		},
 		{
 			desc:                     "when the node has gone away",
@@ -344,13 +318,18 @@ func TestHealthCheckTargets(t *testing.T) {
 
 	for _, tc := range testCases {
 		t.Run(tc.desc, func(t *testing.T) {
-			gs := NewWithT(t)
+			gs := NewGomegaWithT(t)
+
+			gs.Expect(clusterv1.AddToScheme(scheme.Scheme)).To(Succeed())
+			k8sClient := fake.NewClientBuilder().WithScheme(scheme.Scheme).Build()
 
-			// Create a test reconciler.
+			// Create a test reconciler
 			reconciler := &MachineHealthCheckReconciler{
+				Client:   k8sClient,
 				recorder: record.NewFakeRecorder(5),
 			}
 
+			timeoutForMachineToHaveNode := 10 * time.Minute
 			healthy, unhealthy, nextCheckTimes := reconciler.healthCheckTargets(tc.targets, ctrl.LoggerFrom(ctx), timeoutForMachineToHaveNode)
 
 			// Round durations down to nearest second account for minute differences
diff --git a/controllers/remote/cluster_cache_healthcheck_test.go b/controllers/remote/cluster_cache_healthcheck_test.go
index 37212e2b5..77fb2a541 100644
--- a/controllers/remote/cluster_cache_healthcheck_test.go
+++ b/controllers/remote/cluster_cache_healthcheck_test.go
@@ -31,7 +31,6 @@ import (
 	"k8s.io/klog/klogr"
 	clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha4"
 	"sigs.k8s.io/cluster-api/util"
-	"sigs.k8s.io/cluster-api/util/conditions"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	"sigs.k8s.io/controller-runtime/pkg/manager"
 )
@@ -86,7 +85,7 @@ var _ = Describe("ClusterCache HealthCheck suite", func() {
 				},
 			}
 			Expect(k8sClient.Create(ctx, testCluster)).To(Succeed())
-			conditions.MarkTrue(testCluster, clusterv1.ControlPlaneInitializedCondition)
+			testCluster.Status.ControlPlaneInitialized = true
 			testCluster.Status.InfrastructureReady = true
 			Expect(k8sClient.Status().Update(ctx, testCluster)).To(Succeed())
 
diff --git a/controllers/remote/cluster_cache_tracker_test.go b/controllers/remote/cluster_cache_tracker_test.go
index 3daf58205..5b67cc09a 100644
--- a/controllers/remote/cluster_cache_tracker_test.go
+++ b/controllers/remote/cluster_cache_tracker_test.go
@@ -27,7 +27,6 @@ import (
 	"k8s.io/client-go/kubernetes/scheme"
 	clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha4"
 	"sigs.k8s.io/cluster-api/util"
-	"sigs.k8s.io/cluster-api/util/conditions"
 	ctrl "sigs.k8s.io/controller-runtime"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	"sigs.k8s.io/controller-runtime/pkg/handler"
@@ -102,7 +101,7 @@ var _ = Describe("ClusterCache Tracker suite", func() {
 				},
 			}
 			Expect(k8sClient.Create(ctx, clusterA)).To(Succeed())
-			conditions.MarkTrue(clusterA, clusterv1.ControlPlaneInitializedCondition)
+			clusterA.Status.ControlPlaneInitialized = true
 			clusterA.Status.InfrastructureReady = true
 			Expect(k8sClient.Status().Update(ctx, clusterA)).To(Succeed())
 
diff --git a/controlplane/kubeadm/api/v1alpha3/conversion_test.go b/controlplane/kubeadm/api/v1alpha3/conversion_test.go
index 62d6e0f8f..8de6c65b0 100644
--- a/controlplane/kubeadm/api/v1alpha3/conversion_test.go
+++ b/controlplane/kubeadm/api/v1alpha3/conversion_test.go
@@ -21,7 +21,6 @@ import (
 
 	fuzz "github.com/google/gofuzz"
 	. "github.com/onsi/gomega"
-	"k8s.io/apimachinery/pkg/api/apitesting/fuzzer"
 
 	"k8s.io/apimachinery/pkg/runtime"
 	runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer"
@@ -36,29 +35,25 @@ func TestFuzzyConversion(t *testing.T) {
 	g.Expect(AddToScheme(scheme)).To(Succeed())
 	g.Expect(v1alpha4.AddToScheme(scheme)).To(Succeed())
 
-	t.Run("for KubeadmControlPLane", utilconversion.FuzzTestFunc(utilconversion.FuzzTestFuncInput{
-		Scheme: scheme,
-		Hub:    &v1alpha4.KubeadmControlPlane{},
-		Spoke:  &KubeadmControlPlane{},
-		FuzzerFuncs: []fuzzer.FuzzerFuncs{
-			func(codecs runtimeserializer.CodecFactory) []interface{} {
-				return []interface{}{
-					// This custom function is needed when ConvertTo/ConvertFrom functions
-					// uses the json package to unmarshal the bootstrap token string.
-					//
-					// The Kubeadm v1beta1.BootstrapTokenString type ships with a custom
-					// json string representation, in particular it supplies a customized
-					// UnmarshalJSON function that can return an error if the string
-					// isn't in the correct form.
-					//
-					// This function effectively disables any fuzzing for the token by setting
-					// the values for ID and Secret to working alphanumeric values.
-					func(in *kubeadmv1.BootstrapTokenString, c fuzz.Continue) {
-						in.ID = "abcdef"
-						in.Secret = "abcdef0123456789"
-					},
-				}
-			},
+	t.Run("for KubeadmControlPLane", utilconversion.FuzzTestFunc(
+		scheme, &v1alpha4.KubeadmControlPlane{}, &KubeadmControlPlane{},
+		func(codecs runtimeserializer.CodecFactory) []interface{} {
+			return []interface{}{
+				// This custom function is needed when ConvertTo/ConvertFrom functions
+				// uses the json package to unmarshal the bootstrap token string.
+				//
+				// The Kubeadm v1beta1.BootstrapTokenString type ships with a custom
+				// json string representation, in particular it supplies a customized
+				// UnmarshalJSON function that can return an error if the string
+				// isn't in the correct form.
+				//
+				// This function effectively disables any fuzzing for the token by setting
+				// the values for ID and Secret to working alphanumeric values.
+				func(in *kubeadmv1.BootstrapTokenString, c fuzz.Continue) {
+					in.ID = "abcdef"
+					in.Secret = "abcdef0123456789"
+				},
+			}
 		},
-	}))
+	))
 }
diff --git a/test/infrastructure/docker/Dockerfile b/test/infrastructure/docker/Dockerfile
index ee673ac30..d3fbf114c 100644
--- a/test/infrastructure/docker/Dockerfile
+++ b/test/infrastructure/docker/Dockerfile
@@ -48,20 +48,23 @@ RUN --mount=type=cache,target=/root/.cache/go-build \
     --mount=type=cache,target=/go/pkg/mod \
     CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o /workspace/manager main.go
 
-FROM golang:1.16.0
-
-# install a couple of dependencies
+# Gets additional CAPD dependencies
 WORKDIR /tmp
 
 RUN curl -LO https://storage.googleapis.com/kubernetes-release/release/v1.19.2/bin/linux/amd64/kubectl && \
     chmod +x ./kubectl && \
-    mv ./kubectl /usr/local/bin/kubectl
+    mv ./kubectl /usr/bin/kubectl
 
 RUN curl -LO https://download.docker.com/linux/static/stable/x86_64/docker-19.03.1.tgz && \
     tar zxvf docker-19.03.1.tgz --strip 1 -C /usr/bin docker/docker && \
     rm docker-19.03.1.tgz
 
+# NOTE: CAPD can't use non-root because docker requires access to the docker socket
+FROM gcr.io/distroless/static:latest
+
 WORKDIR /
 COPY --from=builder /workspace/manager .
+COPY --from=builder /usr/bin/kubectl /usr/bin/kubectl
+COPY --from=builder /usr/bin/docker /usr/bin/docker
 
 ENTRYPOINT ["/manager"]
diff --git a/test/infrastructure/docker/controllers/dockermachine_controller.go b/test/infrastructure/docker/controllers/dockermachine_controller.go
index 980007ed2..a96334e87 100644
--- a/test/infrastructure/docker/controllers/dockermachine_controller.go
+++ b/test/infrastructure/docker/controllers/dockermachine_controller.go
@@ -191,7 +191,7 @@ func (r *DockerMachineReconciler) reconcileNormal(ctx context.Context, cluster *
 
 	// Make sure bootstrap data is available and populated.
 	if machine.Spec.Bootstrap.DataSecretName == nil {
-		if !util.IsControlPlaneMachine(machine) && !conditions.IsTrue(cluster, clusterv1.ControlPlaneInitializedCondition) {
+		if !util.IsControlPlaneMachine(machine) && !cluster.Status.ControlPlaneInitialized {
 			log.Info("Waiting for the control plane to be initialized")
 			conditions.MarkFalse(dockerMachine, infrav1.ContainerProvisionedCondition, clusterv1.WaitingForControlPlaneAvailableReason, clusterv1.ConditionSeverityInfo, "")
 			return ctrl.Result{}, nil