Repository: kubernetes-sigs/cluster-api
Pull request #7304 (2022-09-28 10:02:46)

🌱 Bump kpromo to v3.4.5
<!-- please add a icon to the title of this PR (see https://sigs.k8s.io/cluster-api/CONTRIBUTING.md#contributing-a-patch), and delete this line and similar ones -->
<!-- the icon will be either ⚠️ (:warning:, major or breaking changes), ✨ (:sparkles:, feature additions), 🐛 (:bug:, patch and bugfixes), 📖 (:book:, documentation or proposals), or 🌱 (:seedling:, minor or other) -->

**What this PR does / why we need it**:
Just to keep kpromo up-to-date. There should be nothing critical in the release: https://github.com/kubernetes-sigs/promo-tools/releases/tag/v3.4.5

**Which issue(s) this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close the issue(s) when PR gets merged)*:
Fixes #



Patch:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index f94ece0e8..cf8eaa136 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -78,14 +78,7 @@ The test module, clusterctl, and experiments do not provide any backward compati
 #### Backporting a patch
 
 We only accept backports of critical bugs, security issues, or bugs without easy workarounds, any
-backport MUST not be breaking for either API or behavioral changes. In order to improve user/developer experience
-maintainers can choose to backport:
-- Doc improvements
-- Improvements to CI signal
-- Improvements to the test framework (enabling improvements to provider's test signal)
-- Cert-manager bump (to avoid having branch using cert-manager versions out of support, when possible)
-- Changes required to support new Kubernetes versions, when possible.
-
+backport MUST not be breaking for either API or behavioral changes.
 We generally do not accept PRs against older release branches.
 
 ### APIs
diff --git a/Makefile b/Makefile
index 0ef60b0b0..57bc48049 100644
--- a/Makefile
+++ b/Makefile
@@ -116,7 +116,7 @@ GO_APIDIFF_PKG := github.com/joelanford/go-apidiff
 HADOLINT_VER := v2.10.0
 HADOLINT_FAILURE_THRESHOLD = warning
 
-KPROMO_VER := v3.4.4
+KPROMO_VER := v3.4.5
 KPROMO_BIN := kpromo
 KPROMO :=  $(abspath $(TOOLS_BIN_DIR)/$(KPROMO_BIN)-$(KPROMO_VER))
 KPROMO_PKG := sigs.k8s.io/promo-tools/v3/cmd/kpromo
diff --git a/OWNERS_ALIASES b/OWNERS_ALIASES
index a1239cc69..4c3df3cb9 100644
--- a/OWNERS_ALIASES
+++ b/OWNERS_ALIASES
@@ -100,5 +100,4 @@ aliases:
 
   cluster-api-docs-reviewers:
     - killianmuldoon
-    - oscr
   cluster-api-docs-maintainers:
diff --git a/api/v1alpha4/conversion.go b/api/v1alpha4/conversion.go
index feea4a6f9..5b129239e 100644
--- a/api/v1alpha4/conversion.go
+++ b/api/v1alpha4/conversion.go
@@ -43,10 +43,6 @@ func (src *Cluster) ConvertTo(dstRaw conversion.Hub) error {
 		}
 		dst.Spec.Topology.Variables = restored.Spec.Topology.Variables
 
-		if restored.Spec.Topology.ControlPlane.MachineHealthCheck != nil {
-			dst.Spec.Topology.ControlPlane.MachineHealthCheck = restored.Spec.Topology.ControlPlane.MachineHealthCheck
-		}
-
 		if restored.Spec.Topology.ControlPlane.NodeDrainTimeout != nil {
 			dst.Spec.Topology.ControlPlane.NodeDrainTimeout = restored.Spec.Topology.ControlPlane.NodeDrainTimeout
 		}
@@ -69,7 +65,6 @@ func (src *Cluster) ConvertTo(dstRaw conversion.Hub) error {
 				dst.Spec.Topology.Workers.MachineDeployments[i].NodeDrainTimeout = restored.Spec.Topology.Workers.MachineDeployments[i].NodeDrainTimeout
 				dst.Spec.Topology.Workers.MachineDeployments[i].NodeVolumeDetachTimeout = restored.Spec.Topology.Workers.MachineDeployments[i].NodeVolumeDetachTimeout
 				dst.Spec.Topology.Workers.MachineDeployments[i].NodeDeletionTimeout = restored.Spec.Topology.Workers.MachineDeployments[i].NodeDeletionTimeout
-				dst.Spec.Topology.Workers.MachineDeployments[i].MachineHealthCheck = restored.Spec.Topology.Workers.MachineDeployments[i].MachineHealthCheck
 			}
 		}
 	}
diff --git a/api/v1alpha4/zz_generated.conversion.go b/api/v1alpha4/zz_generated.conversion.go
index d412b2bc1..2c5db3464 100644
--- a/api/v1alpha4/zz_generated.conversion.go
+++ b/api/v1alpha4/zz_generated.conversion.go
@@ -888,7 +888,6 @@ func autoConvert_v1beta1_ControlPlaneTopology_To_v1alpha4_ControlPlaneTopology(i
 		return err
 	}
 	out.Replicas = (*int32)(unsafe.Pointer(in.Replicas))
-	// WARNING: in.MachineHealthCheck requires manual conversion: does not exist in peer-type
 	// WARNING: in.NodeDrainTimeout requires manual conversion: does not exist in peer-type
 	// WARNING: in.NodeVolumeDetachTimeout requires manual conversion: does not exist in peer-type
 	// WARNING: in.NodeDeletionTimeout requires manual conversion: does not exist in peer-type
@@ -1244,7 +1243,6 @@ func autoConvert_v1beta1_MachineDeploymentTopology_To_v1alpha4_MachineDeployment
 	out.Name = in.Name
 	// WARNING: in.FailureDomain requires manual conversion: does not exist in peer-type
 	out.Replicas = (*int32)(unsafe.Pointer(in.Replicas))
-	// WARNING: in.MachineHealthCheck requires manual conversion: does not exist in peer-type
 	// WARNING: in.NodeDrainTimeout requires manual conversion: does not exist in peer-type
 	// WARNING: in.NodeVolumeDetachTimeout requires manual conversion: does not exist in peer-type
 	// WARNING: in.NodeDeletionTimeout requires manual conversion: does not exist in peer-type
diff --git a/api/v1beta1/cluster_types.go b/api/v1beta1/cluster_types.go
index 1d6cd1553..4ff44b763 100644
--- a/api/v1beta1/cluster_types.go
+++ b/api/v1beta1/cluster_types.go
@@ -116,11 +116,6 @@ type ControlPlaneTopology struct {
 	// +optional
 	Replicas *int32 `json:"replicas,omitempty"`
 
-	// MachineHealthCheck allows to enable, disable and override
-	// the MachineHealthCheck configuration in the ClusterClass for this control plane.
-	// +optional
-	MachineHealthCheck *MachineHealthCheckTopology `json:"machineHealthCheck,omitempty"`
-
 	// NodeDrainTimeout is the total amount of time that the controller will spend on draining a node.
 	// The default value is 0, meaning that the node can be drained without any time limitations.
 	// NOTE: NodeDrainTimeout is different from `kubectl drain --timeout`
@@ -177,11 +172,6 @@ type MachineDeploymentTopology struct {
 	// +optional
 	Replicas *int32 `json:"replicas,omitempty"`
 
-	// MachineHealthCheck allows to enable, disable and override
-	// the MachineHealthCheck configuration in the ClusterClass for this MachineDeployment.
-	// +optional
-	MachineHealthCheck *MachineHealthCheckTopology `json:"machineHealthCheck,omitempty"`
-
 	// NodeDrainTimeout is the total amount of time that the controller will spend on draining a node.
 	// The default value is 0, meaning that the node can be drained without any time limitations.
 	// NOTE: NodeDrainTimeout is different from `kubectl drain --timeout`
@@ -204,25 +194,6 @@ type MachineDeploymentTopology struct {
 	Variables *MachineDeploymentVariables `json:"variables,omitempty"`
 }
 
-// MachineHealthCheckTopology defines a MachineHealthCheck for a group of machines.
-type MachineHealthCheckTopology struct {
-	// Enable controls if a MachineHealthCheck should be created for the target machines.
-	//
-	// If false: No MachineHealthCheck will be created.
-	//
-	// If not set(default): A MachineHealthCheck will be created if it is defined here or
-	//  in the associated ClusterClass. If no MachineHealthCheck is defined then none will be created.
-	//
-	// If true: A MachineHealthCheck is guaranteed to be created. Cluster validation will
-	// block if `enable` is true and no MachineHealthCheck definition is available.
-	// +optional
-	Enable *bool `json:"enable,omitempty"`
-
-	// MachineHealthCheckClass defines a MachineHealthCheck for a group of machines.
-	// If specified (any field is set), it entirely overrides the MachineHealthCheckClass defined in ClusterClass.
-	MachineHealthCheckClass `json:",inline"`
-}
-
 // ClusterVariable can be used to customize the Cluster through
 // patches. It must comply to the corresponding
 // ClusterClassVariable defined in the ClusterClass.
diff --git a/api/v1beta1/clusterclass_types.go b/api/v1beta1/clusterclass_types.go
index 6d41bd40e..d3386811a 100644
--- a/api/v1beta1/clusterclass_types.go
+++ b/api/v1beta1/clusterclass_types.go
@@ -17,8 +17,6 @@ limitations under the License.
 package v1beta1
 
 import (
-	"reflect"
-
 	corev1 "k8s.io/api/core/v1"
 	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -179,11 +177,6 @@ type MachineHealthCheckClass struct {
 	RemediationTemplate *corev1.ObjectReference `json:"remediationTemplate,omitempty"`
 }
 
-// IsZero returns true if none of the values of MachineHealthCheckClass are defined.
-func (m MachineHealthCheckClass) IsZero() bool {
-	return reflect.ValueOf(m).IsZero()
-}
-
 // ClusterClassVariable defines a variable which can
 // be configured in the Cluster topology and used in patches.
 type ClusterClassVariable struct {
diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go
index ac42871e7..1edb3e478 100644
--- a/api/v1beta1/zz_generated.deepcopy.go
+++ b/api/v1beta1/zz_generated.deepcopy.go
@@ -461,11 +461,6 @@ func (in *ControlPlaneTopology) DeepCopyInto(out *ControlPlaneTopology) {
 		*out = new(int32)
 		**out = **in
 	}
-	if in.MachineHealthCheck != nil {
-		in, out := &in.MachineHealthCheck, &out.MachineHealthCheck
-		*out = new(MachineHealthCheckTopology)
-		(*in).DeepCopyInto(*out)
-	}
 	if in.NodeDrainTimeout != nil {
 		in, out := &in.NodeDrainTimeout, &out.NodeDrainTimeout
 		*out = new(metav1.Duration)
@@ -972,11 +967,6 @@ func (in *MachineDeploymentTopology) DeepCopyInto(out *MachineDeploymentTopology
 		*out = new(int32)
 		**out = **in
 	}
-	if in.MachineHealthCheck != nil {
-		in, out := &in.MachineHealthCheck, &out.MachineHealthCheck
-		*out = new(MachineHealthCheckTopology)
-		(*in).DeepCopyInto(*out)
-	}
 	if in.NodeDrainTimeout != nil {
 		in, out := &in.NodeDrainTimeout, &out.NodeDrainTimeout
 		*out = new(metav1.Duration)
@@ -1198,27 +1188,6 @@ func (in *MachineHealthCheckStatus) DeepCopy() *MachineHealthCheckStatus {
 	return out
 }
 
-// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *MachineHealthCheckTopology) DeepCopyInto(out *MachineHealthCheckTopology) {
-	*out = *in
-	if in.Enable != nil {
-		in, out := &in.Enable, &out.Enable
-		*out = new(bool)
-		**out = **in
-	}
-	in.MachineHealthCheckClass.DeepCopyInto(&out.MachineHealthCheckClass)
-}
-
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MachineHealthCheckTopology.
-func (in *MachineHealthCheckTopology) DeepCopy() *MachineHealthCheckTopology {
-	if in == nil {
-		return nil
-	}
-	out := new(MachineHealthCheckTopology)
-	in.DeepCopyInto(out)
-	return out
-}
-
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *MachineList) DeepCopyInto(out *MachineList) {
 	*out = *in
diff --git a/api/v1beta1/zz_generated.openapi.go b/api/v1beta1/zz_generated.openapi.go
index 66578cbdb..5310c2ec0 100644
--- a/api/v1beta1/zz_generated.openapi.go
+++ b/api/v1beta1/zz_generated.openapi.go
@@ -68,7 +68,6 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
 		"sigs.k8s.io/cluster-api/api/v1beta1.MachineHealthCheckList":                   schema_sigsk8sio_cluster_api_api_v1beta1_MachineHealthCheckList(ref),
 		"sigs.k8s.io/cluster-api/api/v1beta1.MachineHealthCheckSpec":                   schema_sigsk8sio_cluster_api_api_v1beta1_MachineHealthCheckSpec(ref),
 		"sigs.k8s.io/cluster-api/api/v1beta1.MachineHealthCheckStatus":                 schema_sigsk8sio_cluster_api_api_v1beta1_MachineHealthCheckStatus(ref),
-		"sigs.k8s.io/cluster-api/api/v1beta1.MachineHealthCheckTopology":               schema_sigsk8sio_cluster_api_api_v1beta1_MachineHealthCheckTopology(ref),
 		"sigs.k8s.io/cluster-api/api/v1beta1.MachineList":                              schema_sigsk8sio_cluster_api_api_v1beta1_MachineList(ref),
 		"sigs.k8s.io/cluster-api/api/v1beta1.MachineRollingUpdateDeployment":           schema_sigsk8sio_cluster_api_api_v1beta1_MachineRollingUpdateDeployment(ref),
 		"sigs.k8s.io/cluster-api/api/v1beta1.MachineSet":                               schema_sigsk8sio_cluster_api_api_v1beta1_MachineSet(ref),
@@ -832,12 +831,6 @@ func schema_sigsk8sio_cluster_api_api_v1beta1_ControlPlaneTopology(ref common.Re
 							Format:      "int32",
 						},
 					},
-					"machineHealthCheck": {
-						SchemaProps: spec.SchemaProps{
-							Description: "MachineHealthCheck allows to enable, disable and override the MachineHealthCheck configuration in the ClusterClass for this control plane.",
-							Ref:         ref("sigs.k8s.io/cluster-api/api/v1beta1.MachineHealthCheckTopology"),
-						},
-					},
 					"nodeDrainTimeout": {
 						SchemaProps: spec.SchemaProps{
 							Description: "NodeDrainTimeout is the total amount of time that the controller will spend on draining a node. The default value is 0, meaning that the node can be drained without any time limitations. NOTE: NodeDrainTimeout is different from `kubectl drain --timeout`",
@@ -860,7 +853,7 @@ func schema_sigsk8sio_cluster_api_api_v1beta1_ControlPlaneTopology(ref common.Re
 			},
 		},
 		Dependencies: []string{
-			"k8s.io/apimachinery/pkg/apis/meta/v1.Duration", "sigs.k8s.io/cluster-api/api/v1beta1.MachineHealthCheckTopology", "sigs.k8s.io/cluster-api/api/v1beta1.ObjectMeta"},
+			"k8s.io/apimachinery/pkg/apis/meta/v1.Duration", "sigs.k8s.io/cluster-api/api/v1beta1.ObjectMeta"},
 	}
 }
 
@@ -1684,12 +1677,6 @@ func schema_sigsk8sio_cluster_api_api_v1beta1_MachineDeploymentTopology(ref comm
 							Format:      "int32",
 						},
 					},
-					"machineHealthCheck": {
-						SchemaProps: spec.SchemaProps{
-							Description: "MachineHealthCheck allows to enable, disable and override the MachineHealthCheck configuration in the ClusterClass for this MachineDeployment.",
-							Ref:         ref("sigs.k8s.io/cluster-api/api/v1beta1.MachineHealthCheckTopology"),
-						},
-					},
 					"nodeDrainTimeout": {
 						SchemaProps: spec.SchemaProps{
 							Description: "NodeDrainTimeout is the total amount of time that the controller will spend on draining a node. The default value is 0, meaning that the node can be drained without any time limitations. NOTE: NodeDrainTimeout is different from `kubectl drain --timeout`",
@@ -1719,7 +1706,7 @@ func schema_sigsk8sio_cluster_api_api_v1beta1_MachineDeploymentTopology(ref comm
 			},
 		},
 		Dependencies: []string{
-			"k8s.io/apimachinery/pkg/apis/meta/v1.Duration", "sigs.k8s.io/cluster-api/api/v1beta1.MachineDeploymentVariables", "sigs.k8s.io/cluster-api/api/v1beta1.MachineHealthCheckTopology", "sigs.k8s.io/cluster-api/api/v1beta1.ObjectMeta"},
+			"k8s.io/apimachinery/pkg/apis/meta/v1.Duration", "sigs.k8s.io/cluster-api/api/v1beta1.MachineDeploymentVariables", "sigs.k8s.io/cluster-api/api/v1beta1.ObjectMeta"},
 	}
 }
 
@@ -2049,67 +2036,6 @@ func schema_sigsk8sio_cluster_api_api_v1beta1_MachineHealthCheckStatus(ref commo
 	}
 }
 
-func schema_sigsk8sio_cluster_api_api_v1beta1_MachineHealthCheckTopology(ref common.ReferenceCallback) common.OpenAPIDefinition {
-	return common.OpenAPIDefinition{
-		Schema: spec.Schema{
-			SchemaProps: spec.SchemaProps{
-				Description: "MachineHealthCheckTopology defines a MachineHealthCheck for a group of machines.",
-				Type:        []string{"object"},
-				Properties: map[string]spec.Schema{
-					"enable": {
-						SchemaProps: spec.SchemaProps{
-							Description: "Enable controls if a MachineHealthCheck should be created for the target machines.\n\nIf false: No MachineHealthCheck will be created.\n\nIf not set(default): A MachineHealthCheck will be created if it is defined here or\n in the associated ClusterClass. If no MachineHealthCheck is defined then none will be created.\n\nIf true: A MachineHealthCheck is guaranteed to be created. Cluster validation will block if `enable` is true and no MachineHealthCheck definition is available.",
-							Type:        []string{"boolean"},
-							Format:      "",
-						},
-					},
-					"unhealthyConditions": {
-						SchemaProps: spec.SchemaProps{
-							Description: "UnhealthyConditions contains a list of the conditions that determine whether a node is considered unhealthy. The conditions are combined in a logical OR, i.e. if any of the conditions is met, the node is unhealthy.",
-							Type:        []string{"array"},
-							Items: &spec.SchemaOrArray{
-								Schema: &spec.Schema{
-									SchemaProps: spec.SchemaProps{
-										Default: map[string]interface{}{},
-										Ref:     ref("sigs.k8s.io/cluster-api/api/v1beta1.UnhealthyCondition"),
-									},
-								},
-							},
-						},
-					},
-					"maxUnhealthy": {
-						SchemaProps: spec.SchemaProps{
-							Description: "Any further remediation is only allowed if at most \"MaxUnhealthy\" machines selected by \"selector\" are not healthy.",
-							Ref:         ref("k8s.io/apimachinery/pkg/util/intstr.IntOrString"),
-						},
-					},
-					"unhealthyRange": {
-						SchemaProps: spec.SchemaProps{
-							Description: "Any further remediation is only allowed if the number of machines selected by \"selector\" as not healthy is within the range of \"UnhealthyRange\". Takes precedence over MaxUnhealthy. Eg. \"[3-5]\" - This means that remediation will be allowed only when: (a) there are at least 3 unhealthy machines (and) (b) there are at most 5 unhealthy machines",
-							Type:        []string{"string"},
-							Format:      "",
-						},
-					},
-					"nodeStartupTimeout": {
-						SchemaProps: spec.SchemaProps{
-							Description: "Machines older than this duration without a node will be considered to have failed and will be remediated. If you wish to disable this feature, set the value explicitly to 0.",
-							Ref:         ref("k8s.io/apimachinery/pkg/apis/meta/v1.Duration"),
-						},
-					},
-					"remediationTemplate": {
-						SchemaProps: spec.SchemaProps{
-							Description: "RemediationTemplate is a reference to a remediation template provided by an infrastructure provider.\n\nThis field is completely optional, when filled, the MachineHealthCheck controller creates a new object from the template referenced and hands off remediation of the machine to a controller that lives outside of Cluster API.",
-							Ref:         ref("k8s.io/api/core/v1.ObjectReference"),
-						},
-					},
-				},
-			},
-		},
-		Dependencies: []string{
-			"k8s.io/api/core/v1.ObjectReference", "k8s.io/apimachinery/pkg/apis/meta/v1.Duration", "k8s.io/apimachinery/pkg/util/intstr.IntOrString", "sigs.k8s.io/cluster-api/api/v1beta1.UnhealthyCondition"},
-	}
-}
-
 func schema_sigsk8sio_cluster_api_api_v1beta1_MachineList(ref common.ReferenceCallback) common.OpenAPIDefinition {
 	return common.OpenAPIDefinition{
 		Schema: spec.Schema{
diff --git a/config/crd/bases/cluster.x-k8s.io_clusters.yaml b/config/crd/bases/cluster.x-k8s.io_clusters.yaml
index 2ea4025ae..d72819f8e 100644
--- a/config/crd/bases/cluster.x-k8s.io_clusters.yaml
+++ b/config/crd/bases/cluster.x-k8s.io_clusters.yaml
@@ -837,119 +837,6 @@ spec:
                   controlPlane:
                     description: ControlPlane describes the cluster control plane.
                     properties:
-                      machineHealthCheck:
-                        description: MachineHealthCheck allows to enable, disable
-                          and override the MachineHealthCheck configuration in the
-                          ClusterClass for this control plane.
-                        properties:
-                          enable:
-                            description: "Enable controls if a MachineHealthCheck
-                              should be created for the target machines. \n If false:
-                              No MachineHealthCheck will be created. \n If not set(default):
-                              A MachineHealthCheck will be created if it is defined
-                              here or in the associated ClusterClass. If no MachineHealthCheck
-                              is defined then none will be created. \n If true: A
-                              MachineHealthCheck is guaranteed to be created. Cluster
-                              validation will block if `enable` is true and no MachineHealthCheck
-                              definition is available."
-                            type: boolean
-                          maxUnhealthy:
-                            anyOf:
-                            - type: integer
-                            - type: string
-                            description: Any further remediation is only allowed if
-                              at most "MaxUnhealthy" machines selected by "selector"
-                              are not healthy.
-                            x-kubernetes-int-or-string: true
-                          nodeStartupTimeout:
-                            description: Machines older than this duration without
-                              a node will be considered to have failed and will be
-                              remediated. If you wish to disable this feature, set
-                              the value explicitly to 0.
-                            type: string
-                          remediationTemplate:
-                            description: "RemediationTemplate is a reference to a
-                              remediation template provided by an infrastructure provider.
-                              \n This field is completely optional, when filled, the
-                              MachineHealthCheck controller creates a new object from
-                              the template referenced and hands off remediation of
-                              the machine to a controller that lives outside of Cluster
-                              API."
-                            properties:
-                              apiVersion:
-                                description: API version of the referent.
-                                type: string
-                              fieldPath:
-                                description: 'If referring to a piece of an object
-                                  instead of an entire object, this string should
-                                  contain a valid JSON/Go field access statement,
-                                  such as desiredState.manifest.containers[2]. For
-                                  example, if the object reference is to a container
-                                  within a pod, this would take on a value like: "spec.containers{name}"
-                                  (where "name" refers to the name of the container
-                                  that triggered the event) or if no container name
-                                  is specified "spec.containers[2]" (container with
-                                  index 2 in this pod). This syntax is chosen only
-                                  to have some well-defined way of referencing a part
-                                  of an object. TODO: this design is not final and
-                                  this field is subject to change in the future.'
-                                type: string
-                              kind:
-                                description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
-                                type: string
-                              name:
-                                description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
-                                type: string
-                              namespace:
-                                description: 'Namespace of the referent. More info:
-                                  https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/'
-                                type: string
-                              resourceVersion:
-                                description: 'Specific resourceVersion to which this
-                                  reference is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency'
-                                type: string
-                              uid:
-                                description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids'
-                                type: string
-                            type: object
-                            x-kubernetes-map-type: atomic
-                          unhealthyConditions:
-                            description: UnhealthyConditions contains a list of the
-                              conditions that determine whether a node is considered
-                              unhealthy. The conditions are combined in a logical
-                              OR, i.e. if any of the conditions is met, the node is
-                              unhealthy.
-                            items:
-                              description: UnhealthyCondition represents a Node condition
-                                type and value with a timeout specified as a duration.  When
-                                the named condition has been in the given status for
-                                at least the timeout value, a node is considered unhealthy.
-                              properties:
-                                status:
-                                  minLength: 1
-                                  type: string
-                                timeout:
-                                  type: string
-                                type:
-                                  minLength: 1
-                                  type: string
-                              required:
-                              - status
-                              - timeout
-                              - type
-                              type: object
-                            type: array
-                          unhealthyRange:
-                            description: 'Any further remediation is only allowed
-                              if the number of machines selected by "selector" as
-                              not healthy is within the range of "UnhealthyRange".
-                              Takes precedence over MaxUnhealthy. Eg. "[3-5]" - This
-                              means that remediation will be allowed only when: (a)
-                              there are at least 3 unhealthy machines (and) (b) there
-                              are at most 5 unhealthy machines'
-                            pattern: ^\[[0-9]+-[0-9]+\]$
-                            type: string
-                        type: object
                       metadata:
                         description: "Metadata is the metadata applied to the machines
                           of the ControlPlane. At runtime this metadata is merged
@@ -1065,128 +952,6 @@ spec:
                                 machines will be created in. Must match a key in the
                                 FailureDomains map stored on the cluster object.
                               type: string
-                            machineHealthCheck:
-                              description: MachineHealthCheck allows to enable, disable
-                                and override the MachineHealthCheck configuration
-                                in the ClusterClass for this MachineDeployment.
-                              properties:
-                                enable:
-                                  description: "Enable controls if a MachineHealthCheck
-                                    should be created for the target machines. \n
-                                    If false: No MachineHealthCheck will be created.
-                                    \n If not set(default): A MachineHealthCheck will
-                                    be created if it is defined here or in the associated
-                                    ClusterClass. If no MachineHealthCheck is defined
-                                    then none will be created. \n If true: A MachineHealthCheck
-                                    is guaranteed to be created. Cluster validation
-                                    will block if `enable` is true and no MachineHealthCheck
-                                    definition is available."
-                                  type: boolean
-                                maxUnhealthy:
-                                  anyOf:
-                                  - type: integer
-                                  - type: string
-                                  description: Any further remediation is only allowed
-                                    if at most "MaxUnhealthy" machines selected by
-                                    "selector" are not healthy.
-                                  x-kubernetes-int-or-string: true
-                                nodeStartupTimeout:
-                                  description: Machines older than this duration without
-                                    a node will be considered to have failed and will
-                                    be remediated. If you wish to disable this feature,
-                                    set the value explicitly to 0.
-                                  type: string
-                                remediationTemplate:
-                                  description: "RemediationTemplate is a reference
-                                    to a remediation template provided by an infrastructure
-                                    provider. \n This field is completely optional,
-                                    when filled, the MachineHealthCheck controller
-                                    creates a new object from the template referenced
-                                    and hands off remediation of the machine to a
-                                    controller that lives outside of Cluster API."
-                                  properties:
-                                    apiVersion:
-                                      description: API version of the referent.
-                                      type: string
-                                    fieldPath:
-                                      description: 'If referring to a piece of an
-                                        object instead of an entire object, this string
-                                        should contain a valid JSON/Go field access
-                                        statement, such as desiredState.manifest.containers[2].
-                                        For example, if the object reference is to
-                                        a container within a pod, this would take
-                                        on a value like: "spec.containers{name}" (where
-                                        "name" refers to the name of the container
-                                        that triggered the event) or if no container
-                                        name is specified "spec.containers[2]" (container
-                                        with index 2 in this pod). This syntax is
-                                        chosen only to have some well-defined way
-                                        of referencing a part of an object. TODO:
-                                        this design is not final and this field is
-                                        subject to change in the future.'
-                                      type: string
-                                    kind:
-                                      description: 'Kind of the referent. More info:
-                                        https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
-                                      type: string
-                                    name:
-                                      description: 'Name of the referent. More info:
-                                        https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
-                                      type: string
-                                    namespace:
-                                      description: 'Namespace of the referent. More
-                                        info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/'
-                                      type: string
-                                    resourceVersion:
-                                      description: 'Specific resourceVersion to which
-                                        this reference is made, if any. More info:
-                                        https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency'
-                                      type: string
-                                    uid:
-                                      description: 'UID of the referent. More info:
-                                        https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids'
-                                      type: string
-                                  type: object
-                                  x-kubernetes-map-type: atomic
-                                unhealthyConditions:
-                                  description: UnhealthyConditions contains a list
-                                    of the conditions that determine whether a node
-                                    is considered unhealthy. The conditions are combined
-                                    in a logical OR, i.e. if any of the conditions
-                                    is met, the node is unhealthy.
-                                  items:
-                                    description: UnhealthyCondition represents a Node
-                                      condition type and value with a timeout specified
-                                      as a duration.  When the named condition has
-                                      been in the given status for at least the timeout
-                                      value, a node is considered unhealthy.
-                                    properties:
-                                      status:
-                                        minLength: 1
-                                        type: string
-                                      timeout:
-                                        type: string
-                                      type:
-                                        minLength: 1
-                                        type: string
-                                    required:
-                                    - status
-                                    - timeout
-                                    - type
-                                    type: object
-                                  type: array
-                                unhealthyRange:
-                                  description: 'Any further remediation is only allowed
-                                    if the number of machines selected by "selector"
-                                    as not healthy is within the range of "UnhealthyRange".
-                                    Takes precedence over MaxUnhealthy. Eg. "[3-5]"
-                                    - This means that remediation will be allowed
-                                    only when: (a) there are at least 3 unhealthy
-                                    machines (and) (b) there are at most 5 unhealthy
-                                    machines'
-                                  pattern: ^\[[0-9]+-[0-9]+\]$
-                                  type: string
-                              type: object
                             metadata:
                               description: Metadata is the metadata applied to the
                                 machines of the MachineDeployment. At runtime this
diff --git a/docs/book/src/reference/jobs.md b/docs/book/src/reference/jobs.md
index b4c47d082..b2d748c17 100644
--- a/docs/book/src/reference/jobs.md
+++ b/docs/book/src/reference/jobs.md
@@ -1,7 +1,6 @@
 # Jobs
 
 This document intents to provide an overview over our jobs running via Prow, GitHub actions and Google Cloud Build.
-It also documents the cluster-api specific configuration in test-infra.
 
 ## Builds and Tests running on the main branch
 
@@ -16,7 +15,7 @@ It also documents the cluster-api specific configuration in test-infra.
 Prow Presubmits:
 * [pull-cluster-api-build-main] `./scripts/ci-build.sh`
 * ✳️️ [pull-cluster-api-apidiff-main] `./scripts/ci-apidiff.sh`
-* [pull-cluster-api-verify-main] `./scripts/ci-verify.sh`
+* [pull-cluster-api-verify] `./scripts/ci-verify.sh`
 * [pull-cluster-api-test-main] `./scripts/ci-test.sh`
 * [pull-cluster-api-test-mink8s-main] `./scripts/ci-test.sh`
 * [pull-cluster-api-e2e-main] `./scripts/ci-e2e.sh`
@@ -31,17 +30,8 @@ Prow Presubmits:
   * GINKGO_FOCUS: `[K8s-Upgrade]`
 
 GitHub Presubmit Workflows:
-* golangci-lint: golangci/golangci-lint-action
-  * Runs golangci-lint. Can be run locally via `make lint`.
-* verify: kubernetes-sigs/kubebuilder-release-tools verifier
-  * Verifies the PR titles have a valid format, i.e. contains one of the valid icons.
-  * Verifies the PR description is valid, i.e. is long enough.
-* Check PR Markdown links (run when markdown files changed)
-  * Checks markdown for broken links. 
-* dependabot (run on dependabot PRs)
-  * Regenerates Go modules and code.
-* release (run on tags)
-  * Creates a GitHub release with release notes for the tag.
+* golangci-lint: golangci/golangci-lint-action@v2 (locally via `make lint`)
+* verify: kubernetes-sigs/kubebuilder-release-tools@v0.1 verifier
 
 ### Postsubmits
 
@@ -52,13 +42,14 @@ Prow Postsubmits:
 
 Prow Periodics:
 * [periodic-cluster-api-test-main] `./scripts/ci-test.sh`
-* [periodic-cluster-api-test-mink8s-main] `./scripts/ci-test.sh`
-  * KUBEBUILDER_ENVTEST_KUBERNETES_VERSION: `1.20.2` 
 * [periodic-cluster-api-e2e-main] `./scripts/ci-e2e.sh`
   * GINKGO_SKIP: `[Conformance] [K8s-Upgrade]|[IPv6]`
+* [periodic-cluster-api-e2e-upgrade-v0-3-to-main] `./scripts/ci-e2e.sh`
+  * GINKGO_FOCUS: `[clusterctl-Upgrade]`
+* [periodic-cluster-api-e2e-upgrade-v1-0-to-main] `./scripts/ci-e2e.sh`
+  * GINKGO_FOCUS: `[clusterctl-Upgrade]`
 * [periodic-cluster-api-e2e-mink8s-main] `./scripts/ci-e2e.sh`
   * GINKGO_SKIP: `[Conformance] [K8s-Upgrade]|[IPv6]|[ClusterClass]`
-  * KUBERNETES_VERSION_MANAGEMENT: `stable-1.20`, CLUSTER_TOPOLOGY: `false`
 * [periodic-cluster-api-e2e-workload-upgrade-1-18-1-19-main] `./scripts/ci-e2e.sh` FROM: `stable-1.18` TO: `stable-1.19`
   * GINKGO_FOCUS: `[K8s-Upgrade]`
 * [periodic-cluster-api-e2e-workload-upgrade-1-19-1-20-main] `./scripts/ci-e2e.sh` FROM: `stable-1.19` TO: `stable-1.20`
@@ -70,39 +61,20 @@ Prow Periodics:
 * [periodic-cluster-api-e2e-workload-upgrade-1-22-1-23-main] `./scripts/ci-e2e.sh` FROM: `stable-1.22` TO: `stable-1.23`
   * GINKGO_FOCUS: `[K8s-Upgrade]`
 * [periodic-cluster-api-e2e-workload-upgrade-1-23-1-24-main] `./scripts/ci-e2e.sh` FROM: `stable-1.23` TO: `stable-1.24`
-  * GINKGO_FOCUS: `[K8s-Upgrade]`
 * [periodic-cluster-api-e2e-workload-upgrade-1-24-1-25-main] `./scripts/ci-e2e.sh` FROM: `stable-1.24` TO: `stable-1.25`
-  * GINKGO_FOCUS: `[K8s-Upgrade]`
 * [periodic-cluster-api-e2e-workload-upgrade-1-25-latest-main] `./scripts/ci-e2e.sh` FROM: `stable-1.25` TO: `ci/latest-1.26`
   * GINKGO_FOCUS: `[K8s-Upgrade]`
 * [cluster-api-push-images-nightly] Google Cloud Build: `make release-staging-nightly`, `make release-capd-staging-nightly`
 
-## Test-infra configuration
-
-* config/jobs/image-pushing/k8s-staging-cluster-api.yaml
-  * Configures nightly and postsubmit jobs to push images and manifests. 
-* config/jobs/kubernetes-sigs/cluster-api/
-  * Configures Cluster API  presubmit and periodic jobs.
-* config/testgrids/kubernetes/sig-cluster-lifecycle/config.yaml
-  * Configures Cluster API testgrid dashboards.  
-* config/prow/config.yaml
-  * `branch-protection` and `tide` are configured to make the golangci-lint GitHub action mandatory for merge
-* config/prow/plugins.yaml
-  * `triggers`: configures `/ok-to-test`
-  * `approve`: disable auto-approval of PR authors, ignore GitHub reviews (/approve is explicitly required)
-  * `milestone_applier`: configures that merged PRs are automatically added to the correct milestone after merge
-  * `repo_milestone`: configures `cluster-api-maintainers` as maintainers
-  * `require_matching_label`: configures `needs-triage`
-  * `plugins`: enables `milestone`, `override` and `require-matching-label` plugins
-  * `external_plugins`: enables `cherrypicker`
-* label_sync/labels.yaml
-  * Configures labels for the `cluster-api` repository. 
+## Builds and Tests running on releases
 
+GitHub (On Release) Workflows:
+* Update Homebrew Formula On Release: dawidd6/action-homebrew-bump-formula@v3 clusterctl
 
 <!-- links -->
 [pull-cluster-api-build-main]: https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api#capi-pr-build-main
 [pull-cluster-api-apidiff-main]: https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api#capi-pr-apidiff-main
-[pull-cluster-api-verify-main]: https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api#capi-pr-verify-main
+[pull-cluster-api-verify]: https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api#capi-pr-verify-main
 [pull-cluster-api-test-main]: https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api#capi-pr-test-main
 [pull-cluster-api-test-mink8s-main]: https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api#capi-pr-test-mink8s-main
 [pull-cluster-api-e2e-main]: https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api#capi-pr-e2e-main
@@ -111,7 +83,6 @@ Prow Periodics:
 [pull-cluster-api-e2e-full-main]: https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api#capi-pr-e2e-full-main
 [pull-cluster-api-e2e-workload-upgrade-1-25-latest-main]: https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api#capi-pr-e2e-main-1-25-latest
 [periodic-cluster-api-test-main]: https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api#capi-test-main
-[periodic-cluster-api-test-mink8s-main]: https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api#capi-test-mink8s-main
 [periodic-cluster-api-e2e-main]: https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api#capi-e2e-main
 [periodic-cluster-api-e2e-upgrade-v0-3-to-main]: https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api#capi-e2e-upgrade-v0-3-to-main
 [periodic-cluster-api-e2e-upgrade-v1-0-to-main]: https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api#capi-e2e-upgrade-v1-0-to-main
diff --git a/go.mod b/go.mod
index 7f1ee2fc8..a60b971b0 100644
--- a/go.mod
+++ b/go.mod
@@ -15,7 +15,7 @@ require (
 	github.com/flatcar/container-linux-config-transpiler v0.9.4
 	github.com/flatcar/ignition v0.36.2
 	github.com/go-logr/logr v1.2.3
-	github.com/gobuffalo/flect v0.3.0
+	github.com/gobuffalo/flect v0.2.5
 	github.com/google/go-cmp v0.5.9
 	github.com/google/go-github/v45 v45.2.0
 	github.com/google/gofuzz v1.2.0
diff --git a/go.sum b/go.sum
index 1b0206f6d..7facb1192 100644
--- a/go.sum
+++ b/go.sum
@@ -236,8 +236,8 @@ github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh
 github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
 github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
-github.com/gobuffalo/flect v0.3.0 h1:erfPWM+K1rFNIQeRPdeEXxo8yFr/PO17lhRnS8FUrtk=
-github.com/gobuffalo/flect v0.3.0/go.mod h1:5pf3aGnsvqvCj50AVni7mJJF8ICxGZ8HomberC3pXLE=
+github.com/gobuffalo/flect v0.2.5 h1:H6vvsv2an0lalEaCDRThvtBfmg44W/QHXBCYUXf/6S4=
+github.com/gobuffalo/flect v0.2.5/go.mod h1:1ZyCLIbg0YD7sDkzvFdPoOydPtD8y9JQnrOROolUcM8=
 github.com/godbus/dbus v0.0.0-20181025153459-66d97aec3384/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
 github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
 github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
@@ -589,8 +589,7 @@ github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ai
 github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4=
-github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
@@ -599,7 +598,6 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
-github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
 github.com/subosito/gotenv v1.3.0 h1:mjC+YW8QpAdXibNi+vNWgzmgBH4+5l5dCXv8cNysBLI=
 github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t6PwAXzs=
diff --git a/hack/tools/go.mod b/hack/tools/go.mod
index 000ddd4a6..750cce98a 100644
--- a/hack/tools/go.mod
+++ b/hack/tools/go.mod
@@ -67,7 +67,7 @@ require (
 	github.com/go-openapi/jsonpointer v0.19.5 // indirect
 	github.com/go-openapi/jsonreference v0.20.0 // indirect
 	github.com/go-openapi/swag v0.22.3 // indirect
-	github.com/gobuffalo/flect v0.3.0 // indirect
+	github.com/gobuffalo/flect v0.2.5 // indirect
 	github.com/gogo/protobuf v1.3.2 // indirect
 	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
 	github.com/golang/protobuf v1.5.2 // indirect
diff --git a/hack/tools/go.sum b/hack/tools/go.sum
index 8266976f3..d3e03bee0 100644
--- a/hack/tools/go.sum
+++ b/hack/tools/go.sum
@@ -201,8 +201,8 @@ github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh
 github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
 github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
-github.com/gobuffalo/flect v0.3.0 h1:erfPWM+K1rFNIQeRPdeEXxo8yFr/PO17lhRnS8FUrtk=
-github.com/gobuffalo/flect v0.3.0/go.mod h1:5pf3aGnsvqvCj50AVni7mJJF8ICxGZ8HomberC3pXLE=
+github.com/gobuffalo/flect v0.2.5 h1:H6vvsv2an0lalEaCDRThvtBfmg44W/QHXBCYUXf/6S4=
+github.com/gobuffalo/flect v0.2.5/go.mod h1:1ZyCLIbg0YD7sDkzvFdPoOydPtD8y9JQnrOROolUcM8=
 github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
 github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
@@ -489,8 +489,7 @@ github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ai
 github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4=
-github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
@@ -499,7 +498,6 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
-github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 github.com/subosito/gotenv v1.3.0 h1:mjC+YW8QpAdXibNi+vNWgzmgBH4+5l5dCXv8cNysBLI=
 github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t6PwAXzs=
 github.com/valyala/fastjson v1.6.3 h1:tAKFnnwmeMGPbwJ7IwxcTPCNr3uIzoIj3/Fh90ra4xc=
diff --git a/hack/tools/runtime-openapi-gen/vendored_openapi.go b/hack/tools/runtime-openapi-gen/vendored_openapi.go
index dbc38bb11..d8a2afbaf 100644
--- a/hack/tools/runtime-openapi-gen/vendored_openapi.go
+++ b/hack/tools/runtime-openapi-gen/vendored_openapi.go
@@ -3,7 +3,6 @@ package main
 import (
 	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/util/intstr"
 	"k8s.io/kube-openapi/pkg/common"
 	"k8s.io/kube-openapi/pkg/validation/spec"
 )
@@ -14,7 +13,6 @@ import (
 // Prior art: https://github.com/kubernetes-sigs/controller-tools/blob/master/pkg/crd/known_types.go.
 func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition {
 	return map[string]common.OpenAPIDefinition{
-		"k8s.io/api/core/v1.ObjectReference":                            schema_k8sio_api_core_v1_ObjectReference(ref),
 		"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1.JSON": schema_pkg_apis_apiextensions_v1_JSON(ref),
 		"k8s.io/apimachinery/pkg/apis/meta/v1.Duration":                 schema_pkg_apis_meta_v1_Duration(ref),
 		"k8s.io/apimachinery/pkg/apis/meta/v1.FieldsV1":                 schema_pkg_apis_meta_v1_FieldsV1(ref),
@@ -23,7 +21,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
 		"k8s.io/apimachinery/pkg/apis/meta/v1.OwnerReference":           schema_pkg_apis_meta_v1_OwnerReference(ref),
 		"k8s.io/apimachinery/pkg/apis/meta/v1.Time":                     schema_pkg_apis_meta_v1_Time(ref),
 		"k8s.io/apimachinery/pkg/runtime.RawExtension":                  schema_k8sio_apimachinery_pkg_runtime_RawExtension(ref),
-		"k8s.io/apimachinery/pkg/util/intstr.IntOrString":               schema_apimachinery_pkg_util_intstr_IntOrString(ref),
+		"k8s.io/api/core/v1.ObjectReference":                            schema_k8sio_api_core_v1_ObjectReference(ref),
 	}
 }
 
@@ -455,23 +453,3 @@ func schema_k8sio_api_core_v1_ObjectReference(ref common.ReferenceCallback) comm
 		},
 	}
 }
-
-func schema_apimachinery_pkg_util_intstr_IntOrString(ref common.ReferenceCallback) common.OpenAPIDefinition {
-	return common.EmbedOpenAPIDefinitionIntoV2Extension(common.OpenAPIDefinition{
-		Schema: spec.Schema{
-			SchemaProps: spec.SchemaProps{
-				Description: "IntOrString is a type that can hold an int32 or a string.  When used in JSON or YAML marshalling and unmarshalling, it produces or consumes the inner type.  This allows you to have, for example, a JSON field that can accept a name or number.",
-				OneOf:       common.GenerateOpenAPIV3OneOfSchema(intstr.IntOrString{}.OpenAPIV3OneOfTypes()),
-				Format:      intstr.IntOrString{}.OpenAPISchemaFormat(),
-			},
-		},
-	}, common.OpenAPIDefinition{
-		Schema: spec.Schema{
-			SchemaProps: spec.SchemaProps{
-				Description: "IntOrString is a type that can hold an int32 or a string.  When used in JSON or YAML marshalling and unmarshalling, it produces or consumes the inner type.  This allows you to have, for example, a JSON field that can accept a name or number.",
-				Type:        intstr.IntOrString{}.OpenAPISchemaType(),
-				Format:      intstr.IntOrString{}.OpenAPISchemaFormat(),
-			},
-		},
-	})
-}
diff --git a/internal/controllers/topology/cluster/desired_state.go b/internal/controllers/topology/cluster/desired_state.go
index 72c029fc0..33ee2251a 100644
--- a/internal/controllers/topology/cluster/desired_state.go
+++ b/internal/controllers/topology/cluster/desired_state.go
@@ -70,12 +70,12 @@ func (r *Reconciler) computeDesiredState(ctx context.Context, s *scope.Scope) (*
 
 	// Compute the desired state of the ControlPlane MachineHealthCheck if defined.
 	// The MachineHealthCheck will have the same name as the ControlPlane Object and a selector for the ControlPlane InfrastructureMachines.
-	if s.Blueprint.IsControlPlaneMachineHealthCheckEnabled() {
+	if s.Blueprint.HasControlPlaneMachineHealthCheck() {
 		desiredState.ControlPlane.MachineHealthCheck = computeMachineHealthCheck(
 			desiredState.ControlPlane.Object,
 			selectorForControlPlaneMHC(),
 			s.Current.Cluster.Name,
-			s.Blueprint.ControlPlaneMachineHealthCheckClass())
+			s.Blueprint.ControlPlane.MachineHealthCheck)
 	}
 
 	// Compute the desired state for the Cluster object adding a reference to the
@@ -604,13 +604,13 @@ func computeMachineDeployment(_ context.Context, s *scope.Scope, desiredControlP
 	desiredMachineDeployment.Object = desiredMachineDeploymentObj
 
 	// If the ClusterClass defines a MachineHealthCheck for the MachineDeployment add it to the desired state.
-	if s.Blueprint.IsMachineDeploymentMachineHealthCheckEnabled(&machineDeploymentTopology) {
+	if machineDeploymentBlueprint.MachineHealthCheck != nil {
 		// Note: The MHC is going to use a selector that provides a minimal set of labels which are common to all MachineSets belonging to the MachineDeployment.
 		desiredMachineDeployment.MachineHealthCheck = computeMachineHealthCheck(
 			desiredMachineDeploymentObj,
 			selectorForMachineDeploymentMHC(desiredMachineDeploymentObj),
 			s.Current.Cluster.Name,
-			s.Blueprint.MachineDeploymentMachineHealthCheckClass(&machineDeploymentTopology))
+			machineDeploymentBlueprint.MachineHealthCheck)
 	}
 	return desiredMachineDeployment, nil
 }
diff --git a/internal/controllers/topology/cluster/scope/blueprint.go b/internal/controllers/topology/cluster/scope/blueprint.go
index 54a9f7175..fdf2b6afb 100644
--- a/internal/controllers/topology/cluster/scope/blueprint.go
+++ b/internal/controllers/topology/cluster/scope/blueprint.go
@@ -78,61 +78,11 @@ func (b *ClusterBlueprint) HasControlPlaneInfrastructureMachine() bool {
 	return b.ClusterClass.Spec.ControlPlane.MachineInfrastructure != nil && b.ClusterClass.Spec.ControlPlane.MachineInfrastructure.Ref != nil
 }
 
-// IsControlPlaneMachineHealthCheckEnabled returns true if a MachineHealthCheck should be created for the control plane.
-// Returns false otherwise.
-func (b *ClusterBlueprint) IsControlPlaneMachineHealthCheckEnabled() bool {
-	if !b.HasControlPlaneInfrastructureMachine() {
-		return false
-	}
-	// If no MachineHealthCheck is defined in the ClusterClass or in the Cluster Topology then return false.
-	if b.ClusterClass.Spec.ControlPlane.MachineHealthCheck == nil &&
-		(b.Topology.ControlPlane.MachineHealthCheck == nil || b.Topology.ControlPlane.MachineHealthCheck.MachineHealthCheckClass.IsZero()) {
-		return false
-	}
-	// If `enable` is not set then consider it as true. A MachineHealthCheck will be created from either ClusterClass or Cluster Topology.
-	if b.Topology.ControlPlane.MachineHealthCheck == nil || b.Topology.ControlPlane.MachineHealthCheck.Enable == nil {
-		return true
-	}
-	// If `enable` is explicitly set, use the value.
-	return *b.Topology.ControlPlane.MachineHealthCheck.Enable
-}
-
-// ControlPlaneMachineHealthCheckClass returns the MachineHealthCheckClass that should be used to create the MachineHealthCheck object.
-func (b *ClusterBlueprint) ControlPlaneMachineHealthCheckClass() *clusterv1.MachineHealthCheckClass {
-	if b.Topology.ControlPlane.MachineHealthCheck != nil && !b.Topology.ControlPlane.MachineHealthCheck.MachineHealthCheckClass.IsZero() {
-		return &b.Topology.ControlPlane.MachineHealthCheck.MachineHealthCheckClass
-	}
-	return b.ControlPlane.MachineHealthCheck
-}
-
 // HasControlPlaneMachineHealthCheck returns true if the ControlPlaneClass has both MachineInfrastructure and a MachineHealthCheck defined.
 func (b *ClusterBlueprint) HasControlPlaneMachineHealthCheck() bool {
 	return b.HasControlPlaneInfrastructureMachine() && b.ClusterClass.Spec.ControlPlane.MachineHealthCheck != nil
 }
 
-// IsMachineDeploymentMachineHealthCheckEnabled returns true if a MachineHealthCheck should be created for the MachineDeployment.
-// Returns false otherwise.
-func (b *ClusterBlueprint) IsMachineDeploymentMachineHealthCheckEnabled(md *clusterv1.MachineDeploymentTopology) bool {
-	// If no MachineHealthCheck is defined in the ClusterClass or in the Cluster Topology then return false.
-	if b.MachineDeployments[md.Class].MachineHealthCheck == nil && (md.MachineHealthCheck == nil || md.MachineHealthCheck.MachineHealthCheckClass.IsZero()) {
-		return false
-	}
-	// If `enable` is not set then consider it as true. A MachineHealthCheck will be created from either ClusterClass or Cluster Topology.
-	if md.MachineHealthCheck == nil || md.MachineHealthCheck.Enable == nil {
-		return true
-	}
-	// If `enable` is explicitly set, use the value.
-	return *md.MachineHealthCheck.Enable
-}
-
-// MachineDeploymentMachineHealthCheckClass return the MachineHealthCheckClass that should be used to create the MachineHealthCheck object.
-func (b *ClusterBlueprint) MachineDeploymentMachineHealthCheckClass(md *clusterv1.MachineDeploymentTopology) *clusterv1.MachineHealthCheckClass {
-	if md.MachineHealthCheck != nil && !md.MachineHealthCheck.MachineHealthCheckClass.IsZero() {
-		return &md.MachineHealthCheck.MachineHealthCheckClass
-	}
-	return b.MachineDeployments[md.Class].MachineHealthCheck
-}
-
 // HasMachineDeployments checks whether the topology has MachineDeployments.
 func (b *ClusterBlueprint) HasMachineDeployments() bool {
 	return b.Topology.Workers != nil && len(b.Topology.Workers.MachineDeployments) > 0
diff --git a/internal/topology/variables/cluster_variable_validation.go b/internal/topology/variables/cluster_variable_validation.go
index c7e3adbef..b1b1f4384 100644
--- a/internal/topology/variables/cluster_variable_validation.go
+++ b/internal/topology/variables/cluster_variable_validation.go
@@ -102,6 +102,23 @@ func validateClusterVariablesDefined(clusterVariables []clusterv1.ClusterVariabl
 	return allErrs
 }
 
+// ValidateTopLevelClusterVariablesExist validates that all overrides have a corresponding top-level variable.
+func ValidateTopLevelClusterVariablesExist(clusterVariablesOverrides []clusterv1.ClusterVariable, clusterVariables []clusterv1.ClusterVariable, fldPath *field.Path) field.ErrorList {
+	var allErrs field.ErrorList
+
+	// Build map for easier and faster access.
+	clusterVariablesMap := getClusterVariablesMap(clusterVariables)
+
+	for i, clusterVariableOverride := range clusterVariablesOverrides {
+		if _, ok := clusterVariablesMap[clusterVariableOverride.Name]; !ok {
+			return field.ErrorList{field.Invalid(fldPath.Index(i).Child("name"), clusterVariableOverride.Name,
+				fmt.Sprintf("variable override with name %q is missing a corresponding top-level variable", clusterVariableOverride.Name))}
+		}
+	}
+
+	return allErrs
+}
+
 // ValidateClusterVariable validates a clusterVariable.
 func ValidateClusterVariable(clusterVariable *clusterv1.ClusterVariable, clusterClassVariable *clusterv1.ClusterClassVariable, fldPath *field.Path) field.ErrorList {
 	// Parse JSON value.
diff --git a/internal/webhooks/cluster.go b/internal/webhooks/cluster.go
index b50f99beb..cb1d37bdc 100644
--- a/internal/webhooks/cluster.go
+++ b/internal/webhooks/cluster.go
@@ -249,9 +249,6 @@ func (webhook *Cluster) validateTopology(ctx context.Context, oldCluster, newClu
 	allErrs = append(allErrs, variables.ValidateClusterVariables(newCluster.Spec.Topology.Variables, clusterClass.Spec.Variables,
 		fldPath.Child("variables"))...)
 
-	// validate the MachineHealthChecks defined in the cluster topology
-	allErrs = append(allErrs, validateMachineHealthChecks(newCluster, clusterClass)...)
-
 	if newCluster.Spec.Topology.Workers != nil {
 		for i, md := range newCluster.Spec.Topology.Workers.MachineDeployments {
 			// Continue if there are no variable overrides.
@@ -259,6 +256,9 @@ func (webhook *Cluster) validateTopology(ctx context.Context, oldCluster, newClu
 				continue
 			}
 
+			allErrs = append(allErrs, variables.ValidateTopLevelClusterVariablesExist(md.Variables.Overrides, newCluster.Spec.Topology.Variables,
+				fldPath.Child("workers", "machineDeployments").Index(i).Child("variables", "overrides"))...)
+
 			allErrs = append(allErrs, variables.ValidateMachineDeploymentVariables(md.Variables.Overrides, clusterClass.Spec.Variables,
 				fldPath.Child("workers", "machineDeployments").Index(i).Child("variables", "overrides"))...)
 		}
@@ -362,84 +362,3 @@ func (webhook *Cluster) getClusterClassForCluster(ctx context.Context, cluster *
 	}
 	return clusterClass, nil
 }
-
-func validateMachineHealthChecks(cluster *clusterv1.Cluster, clusterClass *clusterv1.ClusterClass) field.ErrorList {
-	var allErrs field.ErrorList
-
-	// Validate ControlPlane MachineHealthCheck if defined.
-	if cluster.Spec.Topology.ControlPlane.MachineHealthCheck != nil && !cluster.Spec.Topology.ControlPlane.MachineHealthCheck.MachineHealthCheckClass.IsZero() {
-		// Ensure ControlPlane does not define a MachineHealthCheck if the ClusterClass does not define MachineInfrastructure.
-		if clusterClass.Spec.ControlPlane.MachineInfrastructure == nil {
-			allErrs = append(allErrs, field.Forbidden(
-				field.NewPath("spec", "topology", "controlPlane", "machineHealthCheck"),
-				"can be set only if spec.controlPlane.machineInfrastructure is set in ClusterClass",
-			))
-		}
-		// Ensure ControlPlane MachineHealthCheck defines UnhealthyConditions.
-		if len(cluster.Spec.Topology.ControlPlane.MachineHealthCheck.MachineHealthCheckClass.UnhealthyConditions) == 0 {
-			allErrs = append(allErrs, field.Forbidden(
-				field.NewPath("spec", "topology", "controlPlane", "machineHealthCheck", "unhealthyConditions"),
-				"must have at least one value",
-			))
-		}
-	}
-
-	// If MachineHealthCheck is explicitly enabled then make sure that a MachineHealthCheck definition is
-	// available either in the Cluster topology or in the ClusterClass.
-	// (One of these definitions will be used in the controller to create the MachineHealthCheck)
-	if cluster.Spec.Topology.ControlPlane.MachineHealthCheck != nil &&
-		cluster.Spec.Topology.ControlPlane.MachineHealthCheck.Enable != nil &&
-		*cluster.Spec.Topology.ControlPlane.MachineHealthCheck.Enable &&
-		cluster.Spec.Topology.ControlPlane.MachineHealthCheck.MachineHealthCheckClass.IsZero() &&
-		clusterClass.Spec.ControlPlane.MachineHealthCheck == nil {
-		allErrs = append(allErrs, field.Forbidden(
-			field.NewPath("spec", "topology", "controlPlane", "machineHealthCheck", "enable"),
-			fmt.Sprintf("cannot be set to %t as MachineHealthCheck definition is not available in the Cluster topology or the ClusterClass", *cluster.Spec.Topology.ControlPlane.MachineHealthCheck.Enable),
-		))
-	}
-
-	if cluster.Spec.Topology.Workers != nil {
-		for i, md := range cluster.Spec.Topology.Workers.MachineDeployments {
-			// If MachineHealthCheck is defined ensure it defines UnhealthyConditions.
-			if md.MachineHealthCheck != nil && !md.MachineHealthCheck.MachineHealthCheckClass.IsZero() {
-				if len(md.MachineHealthCheck.MachineHealthCheckClass.UnhealthyConditions) == 0 {
-					allErrs = append(allErrs, field.Forbidden(
-						field.NewPath("spec", "topology", "workers", "machineDeployments", "machineHealthCheck").Index(i).Child("unhealthyConditions"),
-						"must have at least one value",
-					))
-				}
-			}
-
-			// If MachineHealthCheck is explicitly enabled then make sure that a MachineHealthCheck definition is
-			// available either in the Cluster topology or in the ClusterClass.
-			// (One of these definitions will be used in the controller to create the MachineHealthCheck)
-			mdClass := machineDeploymentClassOfName(clusterClass, md.Class)
-			if mdClass != nil { // Note: we skip handling the nil case here as it is already handled in previous validations.
-				if md.MachineHealthCheck != nil &&
-					md.MachineHealthCheck.Enable != nil &&
-					*md.MachineHealthCheck.Enable &&
-					md.MachineHealthCheck.MachineHealthCheckClass.IsZero() &&
-					mdClass.MachineHealthCheck == nil {
-					allErrs = append(allErrs, field.Forbidden(
-						field.NewPath("spec", "topology", "workers", "machineDeployments", "machineHealthCheck").Index(i).Child("enable"),
-						fmt.Sprintf("cannot be set to %t as MachineHealthCheck definition is not available in the Cluster topology or the ClusterClass", *md.MachineHealthCheck.Enable),
-					))
-				}
-			}
-		}
-	}
-
-	return allErrs
-}
-
-// machineDeploymentClassOfName find a MachineDeploymentClass of the given name in the provided ClusterClass.
-// Returns nill if can not find one.
-// TODO: Check if there is already a helper function that can do this.
-func machineDeploymentClassOfName(clusterClass *clusterv1.ClusterClass, name string) *clusterv1.MachineDeploymentClass {
-	for _, mdClass := range clusterClass.Spec.Workers.MachineDeployments {
-		if mdClass.Class == name {
-			return &mdClass
-		}
-	}
-	return nil
-}
diff --git a/internal/webhooks/clusterclass.go b/internal/webhooks/clusterclass.go
index 470a3b423..de87055ea 100644
--- a/internal/webhooks/clusterclass.go
+++ b/internal/webhooks/clusterclass.go
@@ -20,7 +20,6 @@ import (
 	"context"
 	"fmt"
 	"reflect"
-	"strings"
 
 	"github.com/pkg/errors"
 	corev1 "k8s.io/api/core/v1"
@@ -172,10 +171,6 @@ func (webhook *ClusterClass) validate(ctx context.Context, oldClusterClass, newC
 		allErrs = append(allErrs,
 			webhook.validateRemovedMachineDeploymentClassesAreNotUsed(clusters, oldClusterClass, newClusterClass)...)
 
-		// Ensure no MachineHealthCheck currently in use has been removed from the ClusterClass.
-		allErrs = append(allErrs,
-			validateUpdatesToMachineHealthCheckClasses(clusters, oldClusterClass, newClusterClass)...)
-
 		// Ensure no Variable would be invalidated by the update in spec
 		allErrs = append(allErrs,
 			validateVariableUpdates(clusters, oldClusterClass, newClusterClass, field.NewPath("spec", "variables"))...)
@@ -299,72 +294,6 @@ func validateVariableUpdates(clusters []clusterv1.Cluster, oldClusterClass, newC
 	return aggregateErrors(errorInfo, clusterClassVariablesIndex, path)
 }
 
-// validateUpdatesToMachineHealthCheckClasses checks if the updates made to MachineHealthChecks are valid.
-// It makes sure that if a MachineHealthCheck definition is dropped from the ClusterClass then none of the
-// clusters using the ClusterClass rely on it to create a MachineHealthCheck.
-// A cluster relies on an MachineHealthCheck in the ClusterClass if in cluster topology MachineHealthCheck
-// is explicitly enabled and it does not provide a MachineHealthCheckOverride.
-func validateUpdatesToMachineHealthCheckClasses(clusters []clusterv1.Cluster, oldClusterClass, newClusterClass *clusterv1.ClusterClass) field.ErrorList {
-	var allErrs field.ErrorList
-
-	// Check if the MachineHealthCheck for the control plane is dropped.
-	if oldClusterClass.Spec.ControlPlane.MachineHealthCheck != nil && newClusterClass.Spec.ControlPlane.MachineHealthCheck == nil {
-		// Make sure that none of the clusters are using this MachineHealthCheck.
-		clustersUsingMHC := []string{}
-		for _, cluster := range clusters {
-			if cluster.Spec.Topology.ControlPlane.MachineHealthCheck != nil &&
-				cluster.Spec.Topology.ControlPlane.MachineHealthCheck.Enable != nil &&
-				*cluster.Spec.Topology.ControlPlane.MachineHealthCheck.Enable &&
-				cluster.Spec.Topology.ControlPlane.MachineHealthCheck.MachineHealthCheckClass.IsZero() {
-				clustersUsingMHC = append(clustersUsingMHC, cluster.Name)
-			}
-		}
-		if len(clustersUsingMHC) != 0 {
-			allErrs = append(allErrs, field.Forbidden(
-				field.NewPath("spec", "controlPlane", "machineHealthCheck"),
-				fmt.Sprintf("MachineHealthCheck cannot be deleted because it is used by Cluster(s) %q", strings.Join(clustersUsingMHC, ",")),
-			))
-		}
-	}
-
-	// For each MachineDeploymentClass check if the MachineHealthCheck definition is dropped.
-	for i, newMdClass := range newClusterClass.Spec.Workers.MachineDeployments {
-		oldMdClass := machineDeploymentClassOfName(oldClusterClass, newMdClass.Class)
-		if oldMdClass == nil {
-			// This is a new MachineDeploymentClass. Nothing to do here.
-			continue
-		}
-		// If the MachineHealthCheck is dropped then check that no cluster is using it.
-		if oldMdClass.MachineHealthCheck != nil && newMdClass.MachineHealthCheck == nil {
-			clustersUsingMHC := []string{}
-			for _, cluster := range clusters {
-				if cluster.Spec.Topology.Workers == nil {
-					continue
-				}
-				for _, mdTopology := range cluster.Spec.Topology.Workers.MachineDeployments {
-					if mdTopology.Class == newMdClass.Class {
-						if mdTopology.MachineHealthCheck != nil &&
-							mdTopology.MachineHealthCheck.Enable != nil &&
-							*mdTopology.MachineHealthCheck.Enable &&
-							mdTopology.MachineHealthCheck.MachineHealthCheckClass.IsZero() {
-							clustersUsingMHC = append(clustersUsingMHC, cluster.Name)
-							break
-						}
-					}
-				}
-			}
-			if len(clustersUsingMHC) != 0 {
-				allErrs = append(allErrs, field.Forbidden(
-					field.NewPath("spec", "workers", "machineDeployments").Index(i).Child("machineHealthCheck"),
-					fmt.Sprintf("MachineHealthCheck cannot be deleted because it is used by Cluster(s) %q", strings.Join(clustersUsingMHC, ",")),
-				))
-			}
-		}
-	}
-
-	return allErrs
-}
-
 type errorAggregator map[variableValidationKey][]string
 
 func (e errorAggregator) add(name string, action variableValidationType, references ...string) {


Test patch:
diff --git a/internal/controllers/topology/cluster/scope/blueprint_test.go b/internal/controllers/topology/cluster/scope/blueprint_test.go
deleted file mode 100644
index aee89cf40..000000000
--- a/internal/controllers/topology/cluster/scope/blueprint_test.go
+++ /dev/null
@@ -1,467 +0,0 @@
-/*
-Copyright 2022 The Kubernetes Authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package scope
-
-import (
-	"testing"
-	"time"
-
-	. "github.com/onsi/gomega"
-	corev1 "k8s.io/api/core/v1"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
-	"k8s.io/apimachinery/pkg/util/intstr"
-	"k8s.io/utils/pointer"
-
-	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
-	"sigs.k8s.io/cluster-api/internal/test/builder"
-)
-
-func TestIsControlPlaneMachineHealthCheckEnabled(t *testing.T) {
-	tests := []struct {
-		name      string
-		blueprint *ClusterBlueprint
-		want      bool
-	}{
-		{
-			name: "should return false if the control plane does not have infrastructure machine",
-			blueprint: &ClusterBlueprint{
-				ClusterClass: builder.ClusterClass(metav1.NamespaceDefault, "cluster-class").
-					Build(),
-			},
-			want: false,
-		},
-		{
-			name: "should return false if no MachineHealthCheck is defined in ClusterClass or ClusterTopology",
-			blueprint: &ClusterBlueprint{
-				ClusterClass: builder.ClusterClass(metav1.NamespaceDefault, "cluster-class").
-					WithControlPlaneInfrastructureMachineTemplate(&unstructured.Unstructured{}).
-					Build(),
-				Topology: builder.ClusterTopology().
-					WithClass("cluster-class").
-					Build(),
-			},
-			want: false,
-		},
-		{
-			name: "should return true if MachineHealthCheck if defined in ClusterClass, not defined in cluster topology and enable is not set",
-			blueprint: &ClusterBlueprint{
-				ClusterClass: builder.ClusterClass(metav1.NamespaceDefault, "cluster-class").
-					WithControlPlaneInfrastructureMachineTemplate(&unstructured.Unstructured{}).
-					WithControlPlaneMachineHealthCheck(&clusterv1.MachineHealthCheckClass{}).
-					Build(),
-				Topology: builder.ClusterTopology().
-					WithClass("cluster-class").
-					Build(),
-			},
-			want: true,
-		},
-		{
-			name: "should return false if MachineHealthCheck if defined in ClusterClass, not defined in cluster topology and enable is false",
-			blueprint: &ClusterBlueprint{
-				ClusterClass: builder.ClusterClass(metav1.NamespaceDefault, "cluster-class").
-					WithControlPlaneInfrastructureMachineTemplate(&unstructured.Unstructured{}).
-					WithControlPlaneMachineHealthCheck(&clusterv1.MachineHealthCheckClass{}).
-					Build(),
-				Topology: builder.ClusterTopology().
-					WithClass("cluster-class").
-					WithControlPlaneMachineHealthCheck(&clusterv1.MachineHealthCheckTopology{
-						Enable: pointer.Bool(false),
-					}).
-					Build(),
-			},
-			want: false,
-		},
-		{
-			name: "should return true if MachineHealthCheck if defined in ClusterClass, not defined in cluster topology and enable is true",
-			blueprint: &ClusterBlueprint{
-				ClusterClass: builder.ClusterClass(metav1.NamespaceDefault, "cluster-class").
-					WithControlPlaneInfrastructureMachineTemplate(&unstructured.Unstructured{}).
-					WithControlPlaneMachineHealthCheck(&clusterv1.MachineHealthCheckClass{}).
-					Build(),
-				Topology: builder.ClusterTopology().
-					WithClass("cluster-class").
-					WithControlPlaneMachineHealthCheck(&clusterv1.MachineHealthCheckTopology{
-						Enable: pointer.Bool(true),
-					}).
-					Build(),
-			},
-			want: true,
-		},
-		{
-			name: "should return true if MachineHealthCheck if defined in cluster topology, not defined in ClusterClass and enable is not set",
-			blueprint: &ClusterBlueprint{
-				ClusterClass: builder.ClusterClass(metav1.NamespaceDefault, "cluster-class").
-					WithControlPlaneInfrastructureMachineTemplate(&unstructured.Unstructured{}).
-					Build(),
-				Topology: builder.ClusterTopology().
-					WithClass("cluster-class").
-					WithControlPlaneMachineHealthCheck(&clusterv1.MachineHealthCheckTopology{
-						MachineHealthCheckClass: clusterv1.MachineHealthCheckClass{
-							UnhealthyConditions: []clusterv1.UnhealthyCondition{
-								{
-									Type:    corev1.NodeReady,
-									Status:  corev1.ConditionUnknown,
-									Timeout: metav1.Duration{Duration: 5 * time.Minute},
-								},
-							},
-						},
-					}).
-					Build(),
-			},
-			want: true,
-		},
-		{
-			name: "should return false if MachineHealthCheck if defined in cluster topology, not defined in ClusterClass and enable is false",
-			blueprint: &ClusterBlueprint{
-				ClusterClass: builder.ClusterClass(metav1.NamespaceDefault, "cluster-class").
-					WithControlPlaneInfrastructureMachineTemplate(&unstructured.Unstructured{}).
-					Build(),
-				Topology: builder.ClusterTopology().
-					WithClass("cluster-class").
-					WithControlPlaneMachineHealthCheck(&clusterv1.MachineHealthCheckTopology{
-						Enable: pointer.Bool(false),
-						MachineHealthCheckClass: clusterv1.MachineHealthCheckClass{
-							UnhealthyConditions: []clusterv1.UnhealthyCondition{
-								{
-									Type:    corev1.NodeReady,
-									Status:  corev1.ConditionUnknown,
-									Timeout: metav1.Duration{Duration: 5 * time.Minute},
-								},
-							},
-						},
-					}).
-					Build(),
-			},
-			want: false,
-		},
-		{
-			name: "should return true if MachineHealthCheck if defined in cluster topology, not defined in ClusterClass and enable is true",
-			blueprint: &ClusterBlueprint{
-				ClusterClass: builder.ClusterClass(metav1.NamespaceDefault, "cluster-class").
-					WithControlPlaneInfrastructureMachineTemplate(&unstructured.Unstructured{}).
-					Build(),
-				Topology: builder.ClusterTopology().
-					WithClass("cluster-class").
-					WithControlPlaneMachineHealthCheck(&clusterv1.MachineHealthCheckTopology{
-						Enable: pointer.Bool(true),
-						MachineHealthCheckClass: clusterv1.MachineHealthCheckClass{
-							UnhealthyConditions: []clusterv1.UnhealthyCondition{
-								{
-									Type:    corev1.NodeReady,
-									Status:  corev1.ConditionUnknown,
-									Timeout: metav1.Duration{Duration: 5 * time.Minute},
-								},
-							},
-						},
-					}).
-					Build(),
-			},
-			want: true,
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			g := NewWithT(t)
-			g.Expect(tt.blueprint.IsControlPlaneMachineHealthCheckEnabled()).To(Equal(tt.want))
-		})
-	}
-}
-
-func TestControlPlaneMachineHealthCheckClass(t *testing.T) {
-	mhcInClusterClass := &clusterv1.MachineHealthCheckClass{
-		UnhealthyConditions: []clusterv1.UnhealthyCondition{
-			{
-				Type:    corev1.NodeReady,
-				Status:  corev1.ConditionFalse,
-				Timeout: metav1.Duration{Duration: 10 * time.Minute},
-			},
-		},
-	}
-
-	percent50 := intstr.FromString("50%")
-	mhcInClusterTopology := &clusterv1.MachineHealthCheckClass{
-		UnhealthyConditions: []clusterv1.UnhealthyCondition{
-			{
-				Type:    corev1.NodeReady,
-				Status:  corev1.ConditionFalse,
-				Timeout: metav1.Duration{Duration: 20 * time.Minute},
-			},
-		},
-		MaxUnhealthy: &percent50,
-	}
-
-	tests := []struct {
-		name      string
-		blueprint *ClusterBlueprint
-		want      *clusterv1.MachineHealthCheckClass
-	}{
-		{
-			name: "should return the MachineHealthCheck from cluster topology if defined - should take precedence over MachineHealthCheck in ClusterClass",
-			blueprint: &ClusterBlueprint{
-				Topology: builder.ClusterTopology().
-					WithControlPlaneMachineHealthCheck(&clusterv1.MachineHealthCheckTopology{
-						MachineHealthCheckClass: *mhcInClusterTopology,
-					}).
-					Build(),
-				ControlPlane: &ControlPlaneBlueprint{
-					MachineHealthCheck: mhcInClusterClass,
-				},
-			},
-			want: mhcInClusterTopology,
-		},
-		{
-			name: "should return the MachineHealthCheck from ClusterClass if no MachineHealthCheck is defined in cluster topology",
-			blueprint: &ClusterBlueprint{
-				Topology: builder.ClusterTopology().
-					WithControlPlaneMachineHealthCheck(&clusterv1.MachineHealthCheckTopology{}).
-					Build(),
-				ControlPlane: &ControlPlaneBlueprint{
-					MachineHealthCheck: mhcInClusterClass,
-				},
-			},
-			want: mhcInClusterClass,
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			g := NewWithT(t)
-			g.Expect(tt.blueprint.ControlPlaneMachineHealthCheckClass()).To(Equal(tt.want))
-		})
-	}
-}
-
-func TestIsMachineDeploymentMachineHealthCheckEnabled(t *testing.T) {
-	tests := []struct {
-		name       string
-		blueprint  *ClusterBlueprint
-		mdTopology *clusterv1.MachineDeploymentTopology
-		want       bool
-	}{
-		{
-			name: "should return false if MachineHealthCheck is not defined in ClusterClass and cluster topology",
-			blueprint: &ClusterBlueprint{
-				MachineDeployments: map[string]*MachineDeploymentBlueprint{
-					"worker-class": {},
-				},
-			},
-			mdTopology: &clusterv1.MachineDeploymentTopology{
-				Class: "worker-class",
-			},
-			want: false,
-		},
-		{
-			name: "should return true if MachineHealthCheck is defined in ClusterClass and enable is not set",
-			blueprint: &ClusterBlueprint{
-				MachineDeployments: map[string]*MachineDeploymentBlueprint{
-					"worker-class": {
-						MachineHealthCheck: &clusterv1.MachineHealthCheckClass{},
-					},
-				},
-			},
-			mdTopology: &clusterv1.MachineDeploymentTopology{
-				Class: "worker-class",
-			},
-			want: true,
-		},
-		{
-			name: "should return false if MachineHealthCheck is defined in ClusterClass and enable is false",
-			blueprint: &ClusterBlueprint{
-				MachineDeployments: map[string]*MachineDeploymentBlueprint{
-					"worker-class": {
-						MachineHealthCheck: &clusterv1.MachineHealthCheckClass{},
-					},
-				},
-			},
-			mdTopology: &clusterv1.MachineDeploymentTopology{
-				Class: "worker-class",
-				MachineHealthCheck: &clusterv1.MachineHealthCheckTopology{
-					Enable: pointer.Bool(false),
-				},
-			},
-			want: false,
-		},
-		{
-			name: "should return true if MachineHealthCheck is defined in ClusterClass and enable is true",
-			blueprint: &ClusterBlueprint{
-				MachineDeployments: map[string]*MachineDeploymentBlueprint{
-					"worker-class": {
-						MachineHealthCheck: &clusterv1.MachineHealthCheckClass{},
-					},
-				},
-			},
-			mdTopology: &clusterv1.MachineDeploymentTopology{
-				Class: "worker-class",
-				MachineHealthCheck: &clusterv1.MachineHealthCheckTopology{
-					Enable: pointer.Bool(true),
-				},
-			},
-			want: true,
-		},
-		{
-			name: "should return true if MachineHealthCheck is defined in cluster topology and enable is not set",
-			blueprint: &ClusterBlueprint{
-				MachineDeployments: map[string]*MachineDeploymentBlueprint{
-					"worker-class": {},
-				},
-			},
-			mdTopology: &clusterv1.MachineDeploymentTopology{
-				Class: "worker-class",
-				MachineHealthCheck: &clusterv1.MachineHealthCheckTopology{
-					MachineHealthCheckClass: clusterv1.MachineHealthCheckClass{
-						UnhealthyConditions: []clusterv1.UnhealthyCondition{
-							{
-								Type:    corev1.NodeReady,
-								Status:  corev1.ConditionUnknown,
-								Timeout: metav1.Duration{Duration: 5 * time.Minute},
-							},
-						},
-					},
-				},
-			},
-			want: true,
-		},
-		{
-			name: "should return false if MachineHealthCheck is defined in cluster topology and enable is false",
-			blueprint: &ClusterBlueprint{
-				MachineDeployments: map[string]*MachineDeploymentBlueprint{
-					"worker-class": {},
-				},
-			},
-			mdTopology: &clusterv1.MachineDeploymentTopology{
-				Class: "worker-class",
-				MachineHealthCheck: &clusterv1.MachineHealthCheckTopology{
-					Enable: pointer.Bool(false),
-					MachineHealthCheckClass: clusterv1.MachineHealthCheckClass{
-						UnhealthyConditions: []clusterv1.UnhealthyCondition{
-							{
-								Type:    corev1.NodeReady,
-								Status:  corev1.ConditionUnknown,
-								Timeout: metav1.Duration{Duration: 5 * time.Minute},
-							},
-						},
-					},
-				},
-			},
-			want: false,
-		},
-		{
-			name: "should return true if MachineHealthCheck is defined in cluster topology and enable is true",
-			blueprint: &ClusterBlueprint{
-				MachineDeployments: map[string]*MachineDeploymentBlueprint{
-					"worker-class": {},
-				},
-			},
-			mdTopology: &clusterv1.MachineDeploymentTopology{
-				Class: "worker-class",
-				MachineHealthCheck: &clusterv1.MachineHealthCheckTopology{
-					Enable: pointer.Bool(true),
-					MachineHealthCheckClass: clusterv1.MachineHealthCheckClass{
-						UnhealthyConditions: []clusterv1.UnhealthyCondition{
-							{
-								Type:    corev1.NodeReady,
-								Status:  corev1.ConditionUnknown,
-								Timeout: metav1.Duration{Duration: 5 * time.Minute},
-							},
-						},
-					},
-				},
-			},
-			want: true,
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			g := NewWithT(t)
-			g.Expect(tt.blueprint.IsMachineDeploymentMachineHealthCheckEnabled(tt.mdTopology)).To(Equal(tt.want))
-		})
-	}
-}
-
-func TestMachineDeploymentMachineHealthCheckClass(t *testing.T) {
-	mhcInClusterClass := &clusterv1.MachineHealthCheckClass{
-		UnhealthyConditions: []clusterv1.UnhealthyCondition{
-			{
-				Type:    corev1.NodeReady,
-				Status:  corev1.ConditionFalse,
-				Timeout: metav1.Duration{Duration: 10 * time.Minute},
-			},
-		},
-	}
-
-	percent50 := intstr.FromString("50%")
-	mhcInClusterTopology := &clusterv1.MachineHealthCheckClass{
-		UnhealthyConditions: []clusterv1.UnhealthyCondition{
-			{
-				Type:    corev1.NodeReady,
-				Status:  corev1.ConditionFalse,
-				Timeout: metav1.Duration{Duration: 20 * time.Minute},
-			},
-		},
-		MaxUnhealthy: &percent50,
-	}
-
-	tests := []struct {
-		name       string
-		blueprint  *ClusterBlueprint
-		mdTopology *clusterv1.MachineDeploymentTopology
-		want       *clusterv1.MachineHealthCheckClass
-	}{
-		{
-			name: "should return the MachineHealthCheck from cluster topology if defined - should take precedence over MachineHealthCheck in ClusterClass",
-			blueprint: &ClusterBlueprint{
-				MachineDeployments: map[string]*MachineDeploymentBlueprint{
-					"worker-class": {
-						MachineHealthCheck: mhcInClusterClass,
-					},
-				},
-			},
-			mdTopology: &clusterv1.MachineDeploymentTopology{
-				Class: "worker-class",
-				MachineHealthCheck: &clusterv1.MachineHealthCheckTopology{
-					MachineHealthCheckClass: *mhcInClusterTopology,
-				},
-			},
-			want: mhcInClusterTopology,
-		},
-		{
-			name: "should return the MachineHealthCheck from ClusterClass if no MachineHealthCheck is defined in cluster topology",
-			blueprint: &ClusterBlueprint{
-				MachineDeployments: map[string]*MachineDeploymentBlueprint{
-					"worker-class": {
-						MachineHealthCheck: mhcInClusterClass,
-					},
-				},
-			},
-			mdTopology: &clusterv1.MachineDeploymentTopology{
-				Class:              "worker-class",
-				MachineHealthCheck: &clusterv1.MachineHealthCheckTopology{},
-			},
-			want: mhcInClusterClass,
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			g := NewWithT(t)
-			g.Expect(tt.blueprint.MachineDeploymentMachineHealthCheckClass(tt.mdTopology)).To(Equal(tt.want))
-		})
-	}
-}
diff --git a/internal/test/builder/builders.go b/internal/test/builder/builders.go
index 705cc0928..1d1b79325 100644
--- a/internal/test/builder/builders.go
+++ b/internal/test/builder/builders.go
@@ -111,7 +111,6 @@ type ClusterTopologyBuilder struct {
 	workers              *clusterv1.WorkersTopology
 	version              string
 	controlPlaneReplicas int32
-	controlPlaneMHC      *clusterv1.MachineHealthCheckTopology
 	variables            []clusterv1.ClusterVariable
 }
 
@@ -140,12 +139,6 @@ func (c *ClusterTopologyBuilder) WithControlPlaneReplicas(replicas int32) *Clust
 	return c
 }
 
-// WithControlPlaneMachineHealthCheck adds MachineHealthCheckTopology used as the MachineHealthCheck value.
-func (c *ClusterTopologyBuilder) WithControlPlaneMachineHealthCheck(mhc *clusterv1.MachineHealthCheckTopology) *ClusterTopologyBuilder {
-	c.controlPlaneMHC = mhc
-	return c
-}
-
 // WithMachineDeployment passes the full MachineDeploymentTopology and adds it to an existing list in the ClusterTopologyBuilder.
 func (c *ClusterTopologyBuilder) WithMachineDeployment(mdc clusterv1.MachineDeploymentTopology) *ClusterTopologyBuilder {
 	c.workers.MachineDeployments = append(c.workers.MachineDeployments, mdc)
@@ -165,8 +158,7 @@ func (c *ClusterTopologyBuilder) Build() *clusterv1.Topology {
 		Workers: c.workers,
 		Version: c.version,
 		ControlPlane: clusterv1.ControlPlaneTopology{
-			Replicas:           &c.controlPlaneReplicas,
-			MachineHealthCheck: c.controlPlaneMHC,
+			Replicas: &c.controlPlaneReplicas,
 		},
 		Variables: c.variables,
 	}
@@ -177,7 +169,6 @@ type MachineDeploymentTopologyBuilder struct {
 	class     string
 	name      string
 	replicas  *int32
-	mhc       *clusterv1.MachineHealthCheckTopology
 	variables []clusterv1.ClusterVariable
 }
 
@@ -206,19 +197,12 @@ func (m *MachineDeploymentTopologyBuilder) WithVariables(variables ...clusterv1.
 	return m
 }
 
-// WithMachineHealthCheck adds MachineHealthCheckTopology used as the MachineHealthCheck value.
-func (m *MachineDeploymentTopologyBuilder) WithMachineHealthCheck(mhc *clusterv1.MachineHealthCheckTopology) *MachineDeploymentTopologyBuilder {
-	m.mhc = mhc
-	return m
-}
-
 // Build returns a testable MachineDeploymentTopology with any values passed to the builder.
 func (m *MachineDeploymentTopologyBuilder) Build() clusterv1.MachineDeploymentTopology {
 	md := clusterv1.MachineDeploymentTopology{
-		Class:              m.class,
-		Name:               m.name,
-		Replicas:           m.replicas,
-		MachineHealthCheck: m.mhc,
+		Class:    m.class,
+		Name:     m.name,
+		Replicas: m.replicas,
 	}
 
 	if len(m.variables) > 0 {
diff --git a/internal/test/builder/zz_generated.deepcopy.go b/internal/test/builder/zz_generated.deepcopy.go
index a616faa9d..4926f72b3 100644
--- a/internal/test/builder/zz_generated.deepcopy.go
+++ b/internal/test/builder/zz_generated.deepcopy.go
@@ -155,11 +155,6 @@ func (in *ClusterTopologyBuilder) DeepCopyInto(out *ClusterTopologyBuilder) {
 		*out = new(v1beta1.WorkersTopology)
 		(*in).DeepCopyInto(*out)
 	}
-	if in.controlPlaneMHC != nil {
-		in, out := &in.controlPlaneMHC, &out.controlPlaneMHC
-		*out = new(v1beta1.MachineHealthCheckTopology)
-		(*in).DeepCopyInto(*out)
-	}
 	if in.variables != nil {
 		in, out := &in.variables, &out.variables
 		*out = make([]v1beta1.ClusterVariable, len(*in))
@@ -405,11 +400,6 @@ func (in *MachineDeploymentTopologyBuilder) DeepCopyInto(out *MachineDeploymentT
 		*out = new(int32)
 		**out = **in
 	}
-	if in.mhc != nil {
-		in, out := &in.mhc, &out.mhc
-		*out = new(v1beta1.MachineHealthCheckTopology)
-		(*in).DeepCopyInto(*out)
-	}
 	if in.variables != nil {
 		in, out := &in.variables, &out.variables
 		*out = make([]v1beta1.ClusterVariable, len(*in))
diff --git a/internal/topology/variables/cluster_variable_validation_test.go b/internal/topology/variables/cluster_variable_validation_test.go
index 306646c27..fe35bbdb6 100644
--- a/internal/topology/variables/cluster_variable_validation_test.go
+++ b/internal/topology/variables/cluster_variable_validation_test.go
@@ -236,6 +236,135 @@ func Test_ValidateClusterVariables(t *testing.T) {
 	}
 }
 
+func Test_ValidateTopLevelClusterVariablesExist(t *testing.T) {
+	tests := []struct {
+		name                      string
+		clusterVariablesOverrides []clusterv1.ClusterVariable
+		clusterVariables          []clusterv1.ClusterVariable
+		wantErr                   bool
+	}{
+		{
+			name: "Pass if all variable overrides have corresponding top-level variables.",
+			clusterVariablesOverrides: []clusterv1.ClusterVariable{
+				{
+					Name: "cpu",
+					Value: apiextensionsv1.JSON{
+						Raw: []byte(`1`),
+					},
+				},
+				{
+					Name: "zone",
+					Value: apiextensionsv1.JSON{
+						Raw: []byte(`"longerThanOneCharacter"`),
+					},
+				},
+			},
+			clusterVariables: []clusterv1.ClusterVariable{
+				{
+					Name: "cpu",
+					Value: apiextensionsv1.JSON{
+						Raw: []byte(`1`),
+					},
+				},
+				{
+					Name: "zone",
+					Value: apiextensionsv1.JSON{
+						Raw: []byte(`"longerThanOneCharacter"`),
+					},
+				},
+			},
+		},
+		{
+			name:                      "Pass if there are no variable overrides and no top-level variables.",
+			clusterVariablesOverrides: []clusterv1.ClusterVariable{},
+			clusterVariables:          []clusterv1.ClusterVariable{},
+		},
+		{
+			name:                      "Pass if there are no variable overrides.",
+			clusterVariablesOverrides: []clusterv1.ClusterVariable{},
+			clusterVariables: []clusterv1.ClusterVariable{
+				{
+					Name: "cpu",
+					Value: apiextensionsv1.JSON{
+						Raw: []byte(`1`),
+					},
+				},
+				{
+					Name: "zone",
+					Value: apiextensionsv1.JSON{
+						Raw: []byte(`"longerThanOneCharacter"`),
+					},
+				},
+			},
+		},
+		{
+			name: "Error if a variable override is missing the corresponding top-level variables.",
+			clusterVariablesOverrides: []clusterv1.ClusterVariable{
+				{
+					Name: "cpu",
+					Value: apiextensionsv1.JSON{
+						Raw: []byte(`1`),
+					},
+				},
+				{
+					Name: "zone",
+					Value: apiextensionsv1.JSON{
+						Raw: []byte(`"longerThanOneCharacter"`),
+					},
+				},
+			},
+			clusterVariables: []clusterv1.ClusterVariable{
+				{
+					Name: "zone",
+					Value: apiextensionsv1.JSON{
+						Raw: []byte(`"longerThanOneCharacter"`),
+					},
+				},
+			},
+			wantErr: true,
+		},
+		{
+			name: "Pass if not every top-level variable has an override.",
+			clusterVariablesOverrides: []clusterv1.ClusterVariable{
+				{
+					Name: "zone",
+					Value: apiextensionsv1.JSON{
+						Raw: []byte(`"longerThanOneCharacter"`),
+					},
+				},
+			},
+			clusterVariables: []clusterv1.ClusterVariable{
+				{
+					Name: "cpu",
+					Value: apiextensionsv1.JSON{
+						Raw: []byte(`1`),
+					},
+				},
+				{
+					Name: "zone",
+					Value: apiextensionsv1.JSON{
+						Raw: []byte(`"longerThanOneCharacter"`),
+					},
+				},
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			g := NewWithT(t)
+
+			errList := ValidateTopLevelClusterVariablesExist(tt.clusterVariablesOverrides, tt.clusterVariables,
+				field.NewPath("spec", "topology", "workers", "machineDeployments").Index(0).Child("variables", "overrides"))
+
+			if tt.wantErr {
+				g.Expect(errList).NotTo(BeEmpty())
+				return
+			}
+			g.Expect(errList).To(BeEmpty())
+		})
+	}
+}
+
 func Test_ValidateClusterVariable(t *testing.T) {
 	tests := []struct {
 		name                 string
diff --git a/internal/webhooks/cluster_test.go b/internal/webhooks/cluster_test.go
index de9b443ba..4178171ed 100644
--- a/internal/webhooks/cluster_test.go
+++ b/internal/webhooks/cluster_test.go
@@ -25,7 +25,6 @@ import (
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 	utilfeature "k8s.io/component-base/featuregate/testing"
-	"k8s.io/utils/pointer"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	"sigs.k8s.io/controller-runtime/pkg/client/fake"
 
@@ -757,7 +756,7 @@ func TestClusterTopologyValidation(t *testing.T) {
 			expectErr: true,
 		},
 		{
-			name: "should pass even when variable override is missing the corresponding top-level variable",
+			name: "should fail when variable override is missing the corresponding top-level variable",
 			clusterClassVariables: []clusterv1.ClusterClassVariable{
 				{
 					Name:     "cpu",
@@ -782,7 +781,7 @@ func TestClusterTopologyValidation(t *testing.T) {
 						Build()).
 					Build()).
 				Build(),
-			expectErr: false,
+			expectErr: true,
 		},
 	}
 
@@ -855,222 +854,6 @@ func TestClusterTopologyValidationWithClient(t *testing.T) {
 				Build(),
 			wantErr: true,
 		},
-		{
-			name: "Reject a cluster that has MHC enabled for control plane but is missing MHC definition in cluster topology and clusterclass",
-			cluster: builder.Cluster(metav1.NamespaceDefault, "cluster1").
-				WithTopology(
-					builder.ClusterTopology().
-						WithClass("clusterclass").
-						WithVersion("v1.22.2").
-						WithControlPlaneReplicas(3).
-						WithControlPlaneMachineHealthCheck(&clusterv1.MachineHealthCheckTopology{
-							Enable: pointer.Bool(true),
-						}).
-						Build()).
-				Build(),
-			class: builder.ClusterClass(metav1.NamespaceDefault, "clusterclass").
-				Build(),
-			wantErr: true,
-		},
-		{
-			name: "Reject a cluster that MHC override defined for control plane but is missing unhealthy conditions",
-			cluster: builder.Cluster(metav1.NamespaceDefault, "cluster1").
-				WithTopology(
-					builder.ClusterTopology().
-						WithClass("clusterclass").
-						WithVersion("v1.22.2").
-						WithControlPlaneReplicas(3).
-						WithControlPlaneMachineHealthCheck(&clusterv1.MachineHealthCheckTopology{
-							MachineHealthCheckClass: clusterv1.MachineHealthCheckClass{
-								UnhealthyConditions: []clusterv1.UnhealthyCondition{},
-							},
-						}).
-						Build()).
-				Build(),
-			class: builder.ClusterClass(metav1.NamespaceDefault, "clusterclass").
-				Build(),
-			wantErr: true,
-		},
-		{
-			name: "Reject a cluster that MHC override defined for control plane but is set when control plane is missing machineInfrastructure",
-			cluster: builder.Cluster(metav1.NamespaceDefault, "cluster1").
-				WithTopology(
-					builder.ClusterTopology().
-						WithClass("clusterclass").
-						WithVersion("v1.22.2").
-						WithControlPlaneReplicas(3).
-						WithControlPlaneMachineHealthCheck(&clusterv1.MachineHealthCheckTopology{
-							MachineHealthCheckClass: clusterv1.MachineHealthCheckClass{
-								UnhealthyConditions: []clusterv1.UnhealthyCondition{
-									{
-										Type:   corev1.NodeReady,
-										Status: corev1.ConditionFalse,
-									},
-								},
-							},
-						}).
-						Build()).
-				Build(),
-			class: builder.ClusterClass(metav1.NamespaceDefault, "clusterclass").
-				Build(),
-			wantErr: true,
-		},
-		{
-			name: "Accept a cluster that has MHC enabled for control plane with control plane MHC defined in ClusterClass",
-			cluster: builder.Cluster(metav1.NamespaceDefault, "cluster1").
-				WithTopology(
-					builder.ClusterTopology().
-						WithClass("clusterclass").
-						WithVersion("v1.22.2").
-						WithControlPlaneReplicas(3).
-						WithControlPlaneMachineHealthCheck(&clusterv1.MachineHealthCheckTopology{
-							Enable: pointer.Bool(true),
-						}).
-						Build()).
-				Build(),
-			class: builder.ClusterClass(metav1.NamespaceDefault, "clusterclass").
-				WithControlPlaneMachineHealthCheck(&clusterv1.MachineHealthCheckClass{}).
-				Build(),
-			wantErr: false,
-		},
-		{
-			name: "Accept a cluster that has MHC enabled for control plane with control plane MHC defined in cluster topology",
-			cluster: builder.Cluster(metav1.NamespaceDefault, "cluster1").
-				WithTopology(
-					builder.ClusterTopology().
-						WithClass("clusterclass").
-						WithVersion("v1.22.2").
-						WithControlPlaneReplicas(3).
-						WithControlPlaneMachineHealthCheck(&clusterv1.MachineHealthCheckTopology{
-							Enable: pointer.Bool(true),
-							MachineHealthCheckClass: clusterv1.MachineHealthCheckClass{
-								UnhealthyConditions: []clusterv1.UnhealthyCondition{
-									{
-										Type:   corev1.NodeReady,
-										Status: corev1.ConditionFalse,
-									},
-								},
-							},
-						}).
-						Build()).
-				Build(),
-			class: builder.ClusterClass(metav1.NamespaceDefault, "clusterclass").
-				WithControlPlaneInfrastructureMachineTemplate(&unstructured.Unstructured{}).
-				Build(),
-			wantErr: false,
-		},
-		{
-			name: "Reject a cluster that has MHC enabled for machine deployment but is missing MHC definition in cluster topology and ClusterClass",
-			cluster: builder.Cluster(metav1.NamespaceDefault, "cluster1").
-				WithTopology(
-					builder.ClusterTopology().
-						WithClass("clusterclass").
-						WithVersion("v1.22.2").
-						WithControlPlaneReplicas(3).
-						WithMachineDeployment(
-							builder.MachineDeploymentTopology("md1").
-								WithClass("worker-class").
-								WithMachineHealthCheck(&clusterv1.MachineHealthCheckTopology{
-									Enable: pointer.Bool(true),
-								}).
-								Build(),
-						).
-						Build()).
-				Build(),
-			class: builder.ClusterClass(metav1.NamespaceDefault, "clusterclass").
-				WithWorkerMachineDeploymentClasses(
-					*builder.MachineDeploymentClass("worker-class").Build(),
-				).
-				Build(),
-			wantErr: true,
-		},
-		{
-			name: "Reject a cluster that has MHC override defined for machine deployment but is missing unhealthy conditions",
-			cluster: builder.Cluster(metav1.NamespaceDefault, "cluster1").
-				WithTopology(
-					builder.ClusterTopology().
-						WithClass("clusterclass").
-						WithVersion("v1.22.2").
-						WithControlPlaneReplicas(3).
-						WithMachineDeployment(
-							builder.MachineDeploymentTopology("md1").
-								WithClass("worker-class").
-								WithMachineHealthCheck(&clusterv1.MachineHealthCheckTopology{
-									MachineHealthCheckClass: clusterv1.MachineHealthCheckClass{
-										UnhealthyConditions: []clusterv1.UnhealthyCondition{},
-									},
-								}).
-								Build(),
-						).
-						Build()).
-				Build(),
-			class: builder.ClusterClass(metav1.NamespaceDefault, "clusterclass").
-				WithWorkerMachineDeploymentClasses(
-					*builder.MachineDeploymentClass("worker-class").Build(),
-				).
-				Build(),
-			wantErr: true,
-		},
-		{
-			name: "Accept a cluster that has MHC enabled for machine deployment with machine deployment MHC defined in ClusterClass",
-			cluster: builder.Cluster(metav1.NamespaceDefault, "cluster1").
-				WithTopology(
-					builder.ClusterTopology().
-						WithClass("clusterclass").
-						WithVersion("v1.22.2").
-						WithControlPlaneReplicas(3).
-						WithMachineDeployment(
-							builder.MachineDeploymentTopology("md1").
-								WithClass("worker-class").
-								WithMachineHealthCheck(&clusterv1.MachineHealthCheckTopology{
-									Enable: pointer.Bool(true),
-								}).
-								Build(),
-						).
-						Build()).
-				Build(),
-			class: builder.ClusterClass(metav1.NamespaceDefault, "clusterclass").
-				WithWorkerMachineDeploymentClasses(
-					*builder.MachineDeploymentClass("worker-class").
-						WithMachineHealthCheckClass(&clusterv1.MachineHealthCheckClass{}).
-						Build(),
-				).
-				Build(),
-			wantErr: false,
-		},
-		{
-			name: "Accept a cluster that has MHC enabled for machine deployment with machine deployment MHC defined in cluster topology",
-			cluster: builder.Cluster(metav1.NamespaceDefault, "cluster1").
-				WithTopology(
-					builder.ClusterTopology().
-						WithClass("clusterclass").
-						WithVersion("v1.22.2").
-						WithControlPlaneReplicas(3).
-						WithMachineDeployment(
-							builder.MachineDeploymentTopology("md1").
-								WithClass("worker-class").
-								WithMachineHealthCheck(&clusterv1.MachineHealthCheckTopology{
-									Enable: pointer.Bool(true),
-									MachineHealthCheckClass: clusterv1.MachineHealthCheckClass{
-										UnhealthyConditions: []clusterv1.UnhealthyCondition{
-											{
-												Type:   corev1.NodeReady,
-												Status: corev1.ConditionFalse,
-											},
-										},
-									},
-								}).
-								Build(),
-						).
-						Build()).
-				Build(),
-			class: builder.ClusterClass(metav1.NamespaceDefault, "clusterclass").
-				WithWorkerMachineDeploymentClasses(
-					*builder.MachineDeploymentClass("worker-class").Build(),
-				).
-				Build(),
-			wantErr: false,
-		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
diff --git a/internal/webhooks/clusterclass_test.go b/internal/webhooks/clusterclass_test.go
index 555e3fe90..278448870 100644
--- a/internal/webhooks/clusterclass_test.go
+++ b/internal/webhooks/clusterclass_test.go
@@ -1335,257 +1335,6 @@ func TestClusterClassValidationWithClusterAwareChecks(t *testing.T) {
 				Build(),
 			expectErr: true,
 		},
-		{
-			name: "error if a control plane MachineHealthCheck that is in use by a cluster is removed",
-			clusters: []client.Object{
-				builder.Cluster(metav1.NamespaceDefault, "cluster1").
-					WithTopology(builder.ClusterTopology().
-						WithClass("clusterclass1").
-						WithControlPlaneMachineHealthCheck(&clusterv1.MachineHealthCheckTopology{
-							Enable: pointer.Bool(true),
-						}).
-						Build()).
-					Build(),
-			},
-			oldClusterClass: builder.ClusterClass(metav1.NamespaceDefault, "clusterclass1").
-				WithInfrastructureClusterTemplate(
-					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "inf").Build()).
-				WithControlPlaneTemplate(
-					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
-						Build()).
-				WithControlPlaneMachineHealthCheck(&clusterv1.MachineHealthCheckClass{
-					UnhealthyConditions: []clusterv1.UnhealthyCondition{
-						{
-							Type:    corev1.NodeReady,
-							Status:  corev1.ConditionUnknown,
-							Timeout: metav1.Duration{Duration: 5 * time.Minute},
-						},
-					},
-				}).
-				Build(),
-			newClusterClass: builder.ClusterClass(metav1.NamespaceDefault, "clusterclass1").
-				WithInfrastructureClusterTemplate(
-					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "inf").Build()).
-				WithControlPlaneTemplate(
-					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
-						Build()).
-				Build(),
-			expectErr: true,
-		},
-		{
-			name: "pass if a control plane MachineHealthCheck is removed but no cluster enforces it",
-			clusters: []client.Object{
-				builder.Cluster(metav1.NamespaceDefault, "cluster1").
-					WithTopology(builder.ClusterTopology().
-						WithClass("clusterclass1").
-						Build()).
-					Build(),
-			},
-			oldClusterClass: builder.ClusterClass(metav1.NamespaceDefault, "clusterclass1").
-				WithInfrastructureClusterTemplate(
-					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "inf").Build()).
-				WithControlPlaneTemplate(
-					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
-						Build()).
-				WithControlPlaneMachineHealthCheck(&clusterv1.MachineHealthCheckClass{}).
-				Build(),
-			newClusterClass: builder.ClusterClass(metav1.NamespaceDefault, "clusterclass1").
-				WithInfrastructureClusterTemplate(
-					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "inf").Build()).
-				WithControlPlaneTemplate(
-					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
-						Build()).
-				Build(),
-			expectErr: false,
-		},
-		{
-			name: "pass if a control plane MachineHealthCheck is removed when clusters are not using it (clusters have overrides in topology)",
-			clusters: []client.Object{
-				builder.Cluster(metav1.NamespaceDefault, "cluster1").
-					WithTopology(builder.ClusterTopology().
-						WithClass("clusterclass1").
-						WithControlPlaneMachineHealthCheck(&clusterv1.MachineHealthCheckTopology{
-							Enable: pointer.Bool(true),
-							MachineHealthCheckClass: clusterv1.MachineHealthCheckClass{
-								UnhealthyConditions: []clusterv1.UnhealthyCondition{
-									{
-										Type:    corev1.NodeReady,
-										Status:  corev1.ConditionUnknown,
-										Timeout: metav1.Duration{Duration: 5 * time.Minute},
-									},
-								},
-							},
-						}).
-						Build()).
-					Build(),
-			},
-			oldClusterClass: builder.ClusterClass(metav1.NamespaceDefault, "clusterclass1").
-				WithInfrastructureClusterTemplate(
-					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "inf").Build()).
-				WithControlPlaneTemplate(
-					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
-						Build()).
-				WithControlPlaneMachineHealthCheck(&clusterv1.MachineHealthCheckClass{}).
-				Build(),
-			newClusterClass: builder.ClusterClass(metav1.NamespaceDefault, "clusterclass1").
-				WithInfrastructureClusterTemplate(
-					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "inf").Build()).
-				WithControlPlaneTemplate(
-					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
-						Build()).
-				Build(),
-			expectErr: false,
-		},
-		{
-			name: "error if a MachineDeployment MachineHealthCheck that is in use by a cluster is removed",
-			clusters: []client.Object{
-				builder.Cluster(metav1.NamespaceDefault, "cluster1").
-					WithTopology(builder.ClusterTopology().
-						WithClass("clusterclass1").
-						WithMachineDeployment(builder.MachineDeploymentTopology("md1").
-							WithClass("mdclass1").
-							WithMachineHealthCheck(&clusterv1.MachineHealthCheckTopology{
-								Enable: pointer.Bool(true),
-							}).
-							Build()).
-						Build()).
-					Build(),
-			},
-			oldClusterClass: builder.ClusterClass(metav1.NamespaceDefault, "clusterclass1").
-				WithInfrastructureClusterTemplate(
-					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "inf").Build()).
-				WithControlPlaneTemplate(
-					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
-						Build()).
-				WithWorkerMachineDeploymentClasses(
-					*builder.MachineDeploymentClass("mdclass1").
-						WithInfrastructureTemplate(
-							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
-						WithBootstrapTemplate(
-							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
-						WithMachineHealthCheckClass(&clusterv1.MachineHealthCheckClass{}).
-						Build(),
-				).
-				Build(),
-			newClusterClass: builder.ClusterClass(metav1.NamespaceDefault, "clusterclass1").
-				WithInfrastructureClusterTemplate(
-					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "inf").Build()).
-				WithControlPlaneTemplate(
-					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
-						Build()).
-				WithWorkerMachineDeploymentClasses(
-					*builder.MachineDeploymentClass("mdclass1").
-						WithInfrastructureTemplate(
-							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
-						WithBootstrapTemplate(
-							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
-						Build(),
-				).
-				Build(),
-			expectErr: true,
-		},
-		{
-			name: "pass if a MachineDeployment MachineHealthCheck is removed but no cluster enforces it",
-			clusters: []client.Object{
-				builder.Cluster(metav1.NamespaceDefault, "cluster1").
-					WithTopology(builder.ClusterTopology().
-						WithClass("clusterclass1").
-						WithMachineDeployment(builder.MachineDeploymentTopology("md1").
-							WithClass("mdclass1").
-							Build()).
-						Build()).
-					Build(),
-			},
-			oldClusterClass: builder.ClusterClass(metav1.NamespaceDefault, "clusterclass1").
-				WithInfrastructureClusterTemplate(
-					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "inf").Build()).
-				WithControlPlaneTemplate(
-					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
-						Build()).
-				WithWorkerMachineDeploymentClasses(
-					*builder.MachineDeploymentClass("mdclass1").
-						WithInfrastructureTemplate(
-							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
-						WithBootstrapTemplate(
-							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
-						WithMachineHealthCheckClass(&clusterv1.MachineHealthCheckClass{}).
-						Build(),
-				).
-				Build(),
-			newClusterClass: builder.ClusterClass(metav1.NamespaceDefault, "clusterclass1").
-				WithInfrastructureClusterTemplate(
-					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "inf").Build()).
-				WithControlPlaneTemplate(
-					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
-						Build()).
-				WithWorkerMachineDeploymentClasses(
-					*builder.MachineDeploymentClass("mdclass1").
-						WithInfrastructureTemplate(
-							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
-						WithBootstrapTemplate(
-							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
-						Build(),
-				).
-				Build(),
-			expectErr: false,
-		},
-		{
-			name: "pass if a MachineDeployment MachineHealthCheck is removed when clusters are not using it (clusters have overrides in topology)",
-			clusters: []client.Object{
-				builder.Cluster(metav1.NamespaceDefault, "cluster1").
-					WithTopology(builder.ClusterTopology().
-						WithClass("clusterclass1").
-						WithMachineDeployment(builder.MachineDeploymentTopology("md1").
-							WithClass("mdclass1").
-							WithMachineHealthCheck(&clusterv1.MachineHealthCheckTopology{
-								Enable: pointer.Bool(true),
-								MachineHealthCheckClass: clusterv1.MachineHealthCheckClass{
-									UnhealthyConditions: []clusterv1.UnhealthyCondition{
-										{
-											Type:    corev1.NodeReady,
-											Status:  corev1.ConditionUnknown,
-											Timeout: metav1.Duration{Duration: 5 * time.Minute},
-										},
-									},
-								},
-							}).
-							Build()).
-						Build()).
-					Build(),
-			},
-			oldClusterClass: builder.ClusterClass(metav1.NamespaceDefault, "clusterclass1").
-				WithInfrastructureClusterTemplate(
-					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "inf").Build()).
-				WithControlPlaneTemplate(
-					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
-						Build()).
-				WithWorkerMachineDeploymentClasses(
-					*builder.MachineDeploymentClass("mdclass1").
-						WithInfrastructureTemplate(
-							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
-						WithBootstrapTemplate(
-							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
-						WithMachineHealthCheckClass(&clusterv1.MachineHealthCheckClass{}).
-						Build(),
-				).
-				Build(),
-			newClusterClass: builder.ClusterClass(metav1.NamespaceDefault, "clusterclass1").
-				WithInfrastructureClusterTemplate(
-					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "inf").Build()).
-				WithControlPlaneTemplate(
-					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
-						Build()).
-				WithWorkerMachineDeploymentClasses(
-					*builder.MachineDeploymentClass("mdclass1").
-						WithInfrastructureTemplate(
-							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
-						WithBootstrapTemplate(
-							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
-						Build(),
-				).
-				Build(),
-			expectErr: false,
-		},
 	}
 
 	for _, tt := range tests {
diff --git a/test/e2e/data/cni/kindnet/kindnet.yaml b/test/e2e/data/cni/kindnet/kindnet.yaml
index fbe6d0383..809915c0b 100644
--- a/test/e2e/data/cni/kindnet/kindnet.yaml
+++ b/test/e2e/data/cni/kindnet/kindnet.yaml
@@ -66,7 +66,7 @@ spec:
       serviceAccountName: kindnet
       containers:
         - name: kindnet-cni
-          image: kindest/kindnetd:v20220927-ce36d7c0
+          image: kindest/kindnetd:v20220726-ed811e41
           env:
             - name: HOST_IP
               valueFrom:
diff --git a/test/go.mod b/test/go.mod
index 166fc59dc..cbfc23b1d 100644
--- a/test/go.mod
+++ b/test/go.mod
@@ -59,7 +59,7 @@ require (
 	github.com/go-openapi/jsonpointer v0.19.5 // indirect
 	github.com/go-openapi/jsonreference v0.20.0 // indirect
 	github.com/go-openapi/swag v0.22.3 // indirect
-	github.com/gobuffalo/flect v0.3.0 // indirect
+	github.com/gobuffalo/flect v0.2.5 // indirect
 	github.com/gogo/protobuf v1.3.2 // indirect
 	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
 	github.com/golang/protobuf v1.5.2 // indirect
diff --git a/test/go.sum b/test/go.sum
index 146034e7c..12f5baf31 100644
--- a/test/go.sum
+++ b/test/go.sum
@@ -211,8 +211,8 @@ github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh
 github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
 github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
-github.com/gobuffalo/flect v0.3.0 h1:erfPWM+K1rFNIQeRPdeEXxo8yFr/PO17lhRnS8FUrtk=
-github.com/gobuffalo/flect v0.3.0/go.mod h1:5pf3aGnsvqvCj50AVni7mJJF8ICxGZ8HomberC3pXLE=
+github.com/gobuffalo/flect v0.2.5 h1:H6vvsv2an0lalEaCDRThvtBfmg44W/QHXBCYUXf/6S4=
+github.com/gobuffalo/flect v0.2.5/go.mod h1:1ZyCLIbg0YD7sDkzvFdPoOydPtD8y9JQnrOROolUcM8=
 github.com/godbus/dbus v0.0.0-20181025153459-66d97aec3384/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
 github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
@@ -536,7 +536,6 @@ github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ai
 github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
@@ -545,7 +544,6 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
-github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
 github.com/subosito/gotenv v1.3.0 h1:mjC+YW8QpAdXibNi+vNWgzmgBH4+5l5dCXv8cNysBLI=
 github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t6PwAXzs=