Repository: kubernetes-sigs/cluster-api
Pull request #7535 (2022-11-11 14:46:30)

✨ Add missing fields in ControlPlaneClass and MachineDeploymentClass
**What this PR does / why we need it**:
This PRs add missing fields in ControlPlaneClass and MachineDeploymentClass

**Which issue(s) this PR fixes**:
Fixes https://github.com/kubernetes-sigs/cluster-api/issues/7519
Fixes https://github.com/kubernetes-sigs/cluster-api/issues/7520


k8s-ci-robot: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by:
**Once this PR has been reviewed and has the lgtm label**, please ask for approval from fabriziopandini by writing `/assign @fabriziopandini` in a comment. For more information see [the Kubernetes Code Review Process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process).

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=kubernetes-sigs%2Fcluster-api).

<details open>
Needs approval from an approver in each of these files:

- **[OWNERS](https://github.com/kubernetes-sigs/cluster-api/blob/main/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["fabriziopandini"]} -->
k8s-ci-robot: @fabriziopandini: The following test **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-cluster-api-verify-main | 5f03ab11848ed64348b65d52cdcc3e7041220ca3 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_cluster-api/7535/pull-cluster-api-verify-main/1591079936657985536) | true | `/test pull-cluster-api-verify-main`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=cluster-api&pr=7535). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Afabriziopandini). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes-sigs/cluster-api/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
k8s-ci-robot: @fabriziopandini: The following tests **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-cluster-api-verify-main | 5f03ab11848ed64348b65d52cdcc3e7041220ca3 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_cluster-api/7535/pull-cluster-api-verify-main/1591079936657985536) | true | `/test pull-cluster-api-verify-main`
pull-cluster-api-e2e-informing-main | 5f03ab11848ed64348b65d52cdcc3e7041220ca3 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_cluster-api/7535/pull-cluster-api-e2e-informing-main/1591079936813174784) | false | `/test pull-cluster-api-e2e-informing-main`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=cluster-api&pr=7535). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Afabriziopandini). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes-sigs/cluster-api/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->

Patch:
diff --git a/api/v1alpha4/conversion.go b/api/v1alpha4/conversion.go
index df357d49f..b40311936 100644
--- a/api/v1alpha4/conversion.go
+++ b/api/v1alpha4/conversion.go
@@ -122,9 +122,18 @@ func (src *ClusterClass) ConvertTo(dstRaw conversion.Hub) error {
 	dst.Spec.Patches = restored.Spec.Patches
 	dst.Spec.Variables = restored.Spec.Variables
 	dst.Spec.ControlPlane.MachineHealthCheck = restored.Spec.ControlPlane.MachineHealthCheck
+	dst.Spec.ControlPlane.NodeDrainTimeout = restored.Spec.ControlPlane.NodeDrainTimeout
+	dst.Spec.ControlPlane.NodeVolumeDetachTimeout = restored.Spec.ControlPlane.NodeVolumeDetachTimeout
+	dst.Spec.ControlPlane.NodeDeletionTimeout = restored.Spec.ControlPlane.NodeDeletionTimeout
 
 	for i := range restored.Spec.Workers.MachineDeployments {
 		dst.Spec.Workers.MachineDeployments[i].MachineHealthCheck = restored.Spec.Workers.MachineDeployments[i].MachineHealthCheck
+		dst.Spec.Workers.MachineDeployments[i].FailureDomain = restored.Spec.Workers.MachineDeployments[i].FailureDomain
+		dst.Spec.Workers.MachineDeployments[i].NodeDrainTimeout = restored.Spec.Workers.MachineDeployments[i].NodeDrainTimeout
+		dst.Spec.Workers.MachineDeployments[i].NodeVolumeDetachTimeout = restored.Spec.Workers.MachineDeployments[i].NodeVolumeDetachTimeout
+		dst.Spec.Workers.MachineDeployments[i].NodeDeletionTimeout = restored.Spec.Workers.MachineDeployments[i].NodeDeletionTimeout
+		dst.Spec.Workers.MachineDeployments[i].MinReadySeconds = restored.Spec.Workers.MachineDeployments[i].MinReadySeconds
+		dst.Spec.Workers.MachineDeployments[i].Strategy = restored.Spec.Workers.MachineDeployments[i].Strategy
 	}
 
 	dst.Status = restored.Status
diff --git a/api/v1alpha4/zz_generated.conversion.go b/api/v1alpha4/zz_generated.conversion.go
index 4f7e6f5a3..3598cba1c 100644
--- a/api/v1alpha4/zz_generated.conversion.go
+++ b/api/v1alpha4/zz_generated.conversion.go
@@ -863,6 +863,9 @@ func autoConvert_v1beta1_ControlPlaneClass_To_v1alpha4_ControlPlaneClass(in *v1b
 	}
 	out.MachineInfrastructure = (*LocalObjectTemplate)(unsafe.Pointer(in.MachineInfrastructure))
 	// 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
 	return nil
 }
 
@@ -1038,6 +1041,12 @@ func autoConvert_v1beta1_MachineDeploymentClass_To_v1alpha4_MachineDeploymentCla
 		return err
 	}
 	// WARNING: in.MachineHealthCheck requires manual conversion: does not exist in peer-type
+	// WARNING: in.FailureDomain 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
+	// WARNING: in.MinReadySeconds requires manual conversion: does not exist in peer-type
+	// WARNING: in.Strategy requires manual conversion: does not exist in peer-type
 	return nil
 }
 
diff --git a/api/v1beta1/cluster_types.go b/api/v1beta1/cluster_types.go
index 50898cfe0..4647c1af9 100644
--- a/api/v1beta1/cluster_types.go
+++ b/api/v1beta1/cluster_types.go
@@ -422,8 +422,6 @@ func (c *Cluster) SetConditions(conditions Conditions) {
 }
 
 // GetIPFamily returns a ClusterIPFamily from the configuration provided.
-// Note: IPFamily is not a concept in Kubernetes. It was originally introduced in CAPI for CAPD.
-// IPFamily may be dropped in a future release. More details at https://github.com/kubernetes-sigs/cluster-api/issues/7521
 func (c *Cluster) GetIPFamily() (ClusterIPFamily, error) {
 	var podCIDRs, serviceCIDRs []string
 	if c.Spec.ClusterNetwork != nil {
diff --git a/api/v1beta1/clusterclass_types.go b/api/v1beta1/clusterclass_types.go
index 4a23c99ca..1912b83f5 100644
--- a/api/v1beta1/clusterclass_types.go
+++ b/api/v1beta1/clusterclass_types.go
@@ -100,6 +100,26 @@ type ControlPlaneClass struct {
 	// referenced above is Machine based and supports setting replicas.
 	// +optional
 	MachineHealthCheck *MachineHealthCheckClass `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`
+	// NOTE: This value can be overridden while defining a Cluster.Topology.
+	// +optional
+	NodeDrainTimeout *metav1.Duration `json:"nodeDrainTimeout,omitempty"`
+
+	// NodeVolumeDetachTimeout is the total amount of time that the controller will spend on waiting for all volumes
+	// to be detached. The default value is 0, meaning that the volumes can be detached without any time limitations.
+	// NOTE: This value can be overridden while defining a Cluster.Topology.
+	// +optional
+	NodeVolumeDetachTimeout *metav1.Duration `json:"nodeVolumeDetachTimeout,omitempty"`
+
+	// NodeDeletionTimeout defines how long the controller will attempt to delete the Node that the Machine
+	// hosts after the Machine is marked for deletion. A duration of 0 will retry deletion indefinitely.
+	// Defaults to 10 seconds.
+	// NOTE: This value can be overridden while defining a Cluster.Topology.
+	// +optional
+	NodeDeletionTimeout *metav1.Duration `json:"nodeDeletionTimeout,omitempty"`
 }
 
 // WorkersClass is a collection of deployment classes.
@@ -125,6 +145,44 @@ type MachineDeploymentClass struct {
 	// MachineHealthCheck defines a MachineHealthCheck for this MachineDeploymentClass.
 	// +optional
 	MachineHealthCheck *MachineHealthCheckClass `json:"machineHealthCheck,omitempty"`
+
+	// FailureDomain is the failure domain the machines will be created in.
+	// Must match a key in the FailureDomains map stored on the cluster object.
+	// NOTE: This value can be overridden while defining a Cluster.Topology using this MachineDeploymentClass.
+	// +optional
+	FailureDomain *string `json:"failureDomain,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`
+	// NOTE: This value can be overridden while defining a Cluster.Topology using this MachineDeploymentClass.
+	// +optional
+	NodeDrainTimeout *metav1.Duration `json:"nodeDrainTimeout,omitempty"`
+
+	// NodeVolumeDetachTimeout is the total amount of time that the controller will spend on waiting for all volumes
+	// to be detached. The default value is 0, meaning that the volumes can be detached without any time limitations.
+	// NOTE: This value can be overridden while defining a Cluster.Topology using this MachineDeploymentClass.
+	// +optional
+	NodeVolumeDetachTimeout *metav1.Duration `json:"nodeVolumeDetachTimeout,omitempty"`
+
+	// NodeDeletionTimeout defines how long the controller will attempt to delete the Node that the Machine
+	// hosts after the Machine is marked for deletion. A duration of 0 will retry deletion indefinitely.
+	// Defaults to 10 seconds.
+	// NOTE: This value can be overridden while defining a Cluster.Topology using this MachineDeploymentClass.
+	// +optional
+	NodeDeletionTimeout *metav1.Duration `json:"nodeDeletionTimeout,omitempty"`
+
+	// Minimum number of seconds for which a newly created machine should
+	// be ready.
+	// Defaults to 0 (machine will be considered available as soon as it
+	// is ready)
+	// NOTE: This value can be overridden while defining a Cluster.Topology using this MachineDeploymentClass.
+	MinReadySeconds *int32 `json:"minReadySeconds,omitempty"`
+
+	// The deployment strategy to use to replace existing machines with
+	// new ones.
+	// NOTE: This value can be overridden while defining a Cluster.Topology using this MachineDeploymentClass.
+	Strategy *MachineDeploymentStrategy `json:"strategy,omitempty"`
 }
 
 // MachineDeploymentClassTemplate defines how a MachineDeployment generated from a MachineDeploymentClass
diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go
index f3f9b905a..d583401f6 100644
--- a/api/v1beta1/zz_generated.deepcopy.go
+++ b/api/v1beta1/zz_generated.deepcopy.go
@@ -463,6 +463,21 @@ func (in *ControlPlaneClass) DeepCopyInto(out *ControlPlaneClass) {
 		*out = new(MachineHealthCheckClass)
 		(*in).DeepCopyInto(*out)
 	}
+	if in.NodeDrainTimeout != nil {
+		in, out := &in.NodeDrainTimeout, &out.NodeDrainTimeout
+		*out = new(metav1.Duration)
+		**out = **in
+	}
+	if in.NodeVolumeDetachTimeout != nil {
+		in, out := &in.NodeVolumeDetachTimeout, &out.NodeVolumeDetachTimeout
+		*out = new(metav1.Duration)
+		**out = **in
+	}
+	if in.NodeDeletionTimeout != nil {
+		in, out := &in.NodeDeletionTimeout, &out.NodeDeletionTimeout
+		*out = new(metav1.Duration)
+		**out = **in
+	}
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ControlPlaneClass.
@@ -835,6 +850,36 @@ func (in *MachineDeploymentClass) DeepCopyInto(out *MachineDeploymentClass) {
 		*out = new(MachineHealthCheckClass)
 		(*in).DeepCopyInto(*out)
 	}
+	if in.FailureDomain != nil {
+		in, out := &in.FailureDomain, &out.FailureDomain
+		*out = new(string)
+		**out = **in
+	}
+	if in.NodeDrainTimeout != nil {
+		in, out := &in.NodeDrainTimeout, &out.NodeDrainTimeout
+		*out = new(metav1.Duration)
+		**out = **in
+	}
+	if in.NodeVolumeDetachTimeout != nil {
+		in, out := &in.NodeVolumeDetachTimeout, &out.NodeVolumeDetachTimeout
+		*out = new(metav1.Duration)
+		**out = **in
+	}
+	if in.NodeDeletionTimeout != nil {
+		in, out := &in.NodeDeletionTimeout, &out.NodeDeletionTimeout
+		*out = new(metav1.Duration)
+		**out = **in
+	}
+	if in.MinReadySeconds != nil {
+		in, out := &in.MinReadySeconds, &out.MinReadySeconds
+		*out = new(int32)
+		**out = **in
+	}
+	if in.Strategy != nil {
+		in, out := &in.Strategy, &out.Strategy
+		*out = new(MachineDeploymentStrategy)
+		(*in).DeepCopyInto(*out)
+	}
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MachineDeploymentClass.
diff --git a/api/v1beta1/zz_generated.openapi.go b/api/v1beta1/zz_generated.openapi.go
index dc06f001a..29ac88b41 100644
--- a/api/v1beta1/zz_generated.openapi.go
+++ b/api/v1beta1/zz_generated.openapi.go
@@ -838,12 +838,30 @@ func schema_sigsk8sio_cluster_api_api_v1beta1_ControlPlaneClass(ref common.Refer
 							Ref:         ref("sigs.k8s.io/cluster-api/api/v1beta1.MachineHealthCheckClass"),
 						},
 					},
+					"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` NOTE: This value can be overridden while defining a Cluster.Topology.",
+							Ref:         ref("k8s.io/apimachinery/pkg/apis/meta/v1.Duration"),
+						},
+					},
+					"nodeVolumeDetachTimeout": {
+						SchemaProps: spec.SchemaProps{
+							Description: "NodeVolumeDetachTimeout is the total amount of time that the controller will spend on waiting for all volumes to be detached. The default value is 0, meaning that the volumes can be detached without any time limitations. NOTE: This value can be overridden while defining a Cluster.Topology.",
+							Ref:         ref("k8s.io/apimachinery/pkg/apis/meta/v1.Duration"),
+						},
+					},
+					"nodeDeletionTimeout": {
+						SchemaProps: spec.SchemaProps{
+							Description: "NodeDeletionTimeout defines how long the controller will attempt to delete the Node that the Machine hosts after the Machine is marked for deletion. A duration of 0 will retry deletion indefinitely. Defaults to 10 seconds. NOTE: This value can be overridden while defining a Cluster.Topology.",
+							Ref:         ref("k8s.io/apimachinery/pkg/apis/meta/v1.Duration"),
+						},
+					},
 				},
 				Required: []string{"ref"},
 			},
 		},
 		Dependencies: []string{
-			"k8s.io/api/core/v1.ObjectReference", "sigs.k8s.io/cluster-api/api/v1beta1.LocalObjectTemplate", "sigs.k8s.io/cluster-api/api/v1beta1.MachineHealthCheckClass", "sigs.k8s.io/cluster-api/api/v1beta1.ObjectMeta"},
+			"k8s.io/api/core/v1.ObjectReference", "k8s.io/apimachinery/pkg/apis/meta/v1.Duration", "sigs.k8s.io/cluster-api/api/v1beta1.LocalObjectTemplate", "sigs.k8s.io/cluster-api/api/v1beta1.MachineHealthCheckClass", "sigs.k8s.io/cluster-api/api/v1beta1.ObjectMeta"},
 	}
 }
 
@@ -1391,12 +1409,50 @@ func schema_sigsk8sio_cluster_api_api_v1beta1_MachineDeploymentClass(ref common.
 							Ref:         ref("sigs.k8s.io/cluster-api/api/v1beta1.MachineHealthCheckClass"),
 						},
 					},
+					"failureDomain": {
+						SchemaProps: spec.SchemaProps{
+							Description: "FailureDomain is the failure domain the machines will be created in. Must match a key in the FailureDomains map stored on the cluster object. NOTE: This value can be overridden while defining a Cluster.Topology using this MachineDeploymentClass.",
+							Type:        []string{"string"},
+							Format:      "",
+						},
+					},
+					"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` NOTE: This value can be overridden while defining a Cluster.Topology using this MachineDeploymentClass.",
+							Ref:         ref("k8s.io/apimachinery/pkg/apis/meta/v1.Duration"),
+						},
+					},
+					"nodeVolumeDetachTimeout": {
+						SchemaProps: spec.SchemaProps{
+							Description: "NodeVolumeDetachTimeout is the total amount of time that the controller will spend on waiting for all volumes to be detached. The default value is 0, meaning that the volumes can be detached without any time limitations. NOTE: This value can be overridden while defining a Cluster.Topology using this MachineDeploymentClass.",
+							Ref:         ref("k8s.io/apimachinery/pkg/apis/meta/v1.Duration"),
+						},
+					},
+					"nodeDeletionTimeout": {
+						SchemaProps: spec.SchemaProps{
+							Description: "NodeDeletionTimeout defines how long the controller will attempt to delete the Node that the Machine hosts after the Machine is marked for deletion. A duration of 0 will retry deletion indefinitely. Defaults to 10 seconds. NOTE: This value can be overridden while defining a Cluster.Topology using this MachineDeploymentClass.",
+							Ref:         ref("k8s.io/apimachinery/pkg/apis/meta/v1.Duration"),
+						},
+					},
+					"minReadySeconds": {
+						SchemaProps: spec.SchemaProps{
+							Description: "Minimum number of seconds for which a newly created machine should be ready. Defaults to 0 (machine will be considered available as soon as it is ready) NOTE: This value can be overridden while defining a Cluster.Topology using this MachineDeploymentClass.",
+							Type:        []string{"integer"},
+							Format:      "int32",
+						},
+					},
+					"strategy": {
+						SchemaProps: spec.SchemaProps{
+							Description: "The deployment strategy to use to replace existing machines with new ones. NOTE: This value can be overridden while defining a Cluster.Topology using this MachineDeploymentClass.",
+							Ref:         ref("sigs.k8s.io/cluster-api/api/v1beta1.MachineDeploymentStrategy"),
+						},
+					},
 				},
 				Required: []string{"class", "template"},
 			},
 		},
 		Dependencies: []string{
-			"sigs.k8s.io/cluster-api/api/v1beta1.MachineDeploymentClassTemplate", "sigs.k8s.io/cluster-api/api/v1beta1.MachineHealthCheckClass"},
+			"k8s.io/apimachinery/pkg/apis/meta/v1.Duration", "sigs.k8s.io/cluster-api/api/v1beta1.MachineDeploymentClassTemplate", "sigs.k8s.io/cluster-api/api/v1beta1.MachineDeploymentStrategy", "sigs.k8s.io/cluster-api/api/v1beta1.MachineHealthCheckClass"},
 	}
 }
 
diff --git a/cmd/clusterctl/api/v1alpha3/annotations.go b/cmd/clusterctl/api/v1alpha3/annotations.go
index 26db20fc6..107b1b57a 100644
--- a/cmd/clusterctl/api/v1alpha3/annotations.go
+++ b/cmd/clusterctl/api/v1alpha3/annotations.go
@@ -19,10 +19,4 @@ package v1alpha3
 const (
 	// CertManagerVersionAnnotation reports the cert manager version installed by clusterctl.
 	CertManagerVersionAnnotation = "cert-manager.clusterctl.cluster.x-k8s.io/version"
-
-	// SkipCRDNamePreflightCheckAnnotation can be placed on provider CRDs, so that clusterctl doesn't emit a
-	// warning if the CRD doesn't comply with Cluster APIs naming scheme.
-	// Note: Only CRDs that are referenced by core Cluster API CRDs have to comply with the naming scheme.
-	// See the following issue for more information: https://github.com/kubernetes-sigs/cluster-api/issues/5686#issuecomment-1260897278
-	SkipCRDNamePreflightCheckAnnotation = "clusterctl.cluster.x-k8s.io/skip-crd-name-preflight-check"
 )
diff --git a/cmd/clusterctl/client/cluster/installer.go b/cmd/clusterctl/client/cluster/installer.go
index f167abb82..bedf18908 100644
--- a/cmd/clusterctl/client/cluster/installer.go
+++ b/cmd/clusterctl/client/cluster/installer.go
@@ -25,10 +25,7 @@ import (
 	"github.com/pkg/errors"
 	appsv1 "k8s.io/api/apps/v1"
 	corev1 "k8s.io/api/core/v1"
-	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
-	apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
-	"k8s.io/apimachinery/pkg/runtime/schema"
 	"k8s.io/apimachinery/pkg/util/sets"
 	"k8s.io/apimachinery/pkg/util/version"
 	"k8s.io/apimachinery/pkg/util/wait"
@@ -38,10 +35,8 @@ import (
 	clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3"
 	"sigs.k8s.io/cluster-api/cmd/clusterctl/client/config"
 	"sigs.k8s.io/cluster-api/cmd/clusterctl/client/repository"
-	"sigs.k8s.io/cluster-api/cmd/clusterctl/internal/scheme"
 	"sigs.k8s.io/cluster-api/cmd/clusterctl/internal/util"
 	logf "sigs.k8s.io/cluster-api/cmd/clusterctl/log"
-	"sigs.k8s.io/cluster-api/util/contract"
 )
 
 // ProviderInstaller defines methods for enforcing consistency rules for provider installation.
@@ -58,8 +53,6 @@ type ProviderInstaller interface {
 	// The following checks are performed in order to ensure a fully operational cluster:
 	// - There must be only one instance of the same provider
 	// - All the providers in must support the same API Version of Cluster API (contract)
-	// - All provider CRDs that are referenced in core Cluster API CRDs must comply with the CRD naming scheme,
-	//   otherwise a warning is logged.
 	Validate() error
 
 	// Images returns the list of images required for installing the providers ready in the install queue.
@@ -216,75 +209,9 @@ func (i *providerInstaller) Validate() error {
 			return errors.Errorf("installing provider %q can lead to a non functioning management cluster: the target version for the provider supports the %s API Version of Cluster API (contract), while the management cluster is using %s", components.ManifestLabel(), providerContract, managementClusterContract)
 		}
 	}
-
-	// Validate if provider CRDs comply with the naming scheme.
-	for _, components := range i.installQueue {
-		componentObjects := components.Objs()
-
-		for _, obj := range componentObjects {
-			// Continue if object is not a CRD.
-			if obj.GetKind() != customResourceDefinitionKind {
-				continue
-			}
-
-			gk, err := getCRDGroupKind(obj)
-			if err != nil {
-				return errors.Wrap(err, "Failed to read group and kind from CustomResourceDefinition")
-			}
-
-			if err := validateCRDName(obj, gk); err != nil {
-				logf.Log.Info(err.Error())
-			}
-		}
-	}
-
 	return nil
 }
 
-func getCRDGroupKind(obj unstructured.Unstructured) (*schema.GroupKind, error) {
-	var group string
-	var kind string
-	version := obj.GroupVersionKind().Version
-	switch version {
-	case apiextensionsv1beta1.SchemeGroupVersion.Version:
-		crd := &apiextensionsv1beta1.CustomResourceDefinition{}
-		if err := scheme.Scheme.Convert(&obj, crd, nil); err != nil {
-			return nil, errors.Wrapf(err, "failed to convert %s CustomResourceDefinition %q", version, obj.GetName())
-		}
-		group = crd.Spec.Group
-		kind = crd.Spec.Names.Kind
-	case apiextensionsv1.SchemeGroupVersion.Version:
-		crd := &apiextensionsv1.CustomResourceDefinition{}
-		if err := scheme.Scheme.Convert(&obj, crd, nil); err != nil {
-			return nil, errors.Wrapf(err, "failed to convert %s CustomResourceDefinition %q", version, obj.GetName())
-		}
-		group = crd.Spec.Group
-		kind = crd.Spec.Names.Kind
-	default:
-		return nil, errors.Errorf("failed to read %s CustomResourceDefinition %q", version, obj.GetName())
-	}
-	return &schema.GroupKind{Group: group, Kind: kind}, nil
-}
-
-func validateCRDName(obj unstructured.Unstructured, gk *schema.GroupKind) error {
-	// Return if CRD has skip CRD name preflight check annotation.
-	if _, ok := obj.GetAnnotations()[clusterctlv1.SkipCRDNamePreflightCheckAnnotation]; ok {
-		return nil
-	}
-
-	correctCRDName := contract.CalculateCRDName(gk.Group, gk.Kind)
-
-	// Return if name is correct.
-	if obj.GetName() == correctCRDName {
-		return nil
-	}
-
-	return errors.Errorf("WARNING: CRD name %q is invalid for a CRD referenced in a core Cluster API CRD,"+
-		"it should be %q. Support for CRDs that are referenced in core Cluster API resources with invalid names will be "+
-		"dropped in a future Cluster API release. Note: Please check if this CRD is actually referenced in core Cluster API "+
-		"CRDs. If not, this warning can be hidden by setting the %q' annotation.", obj.GetName(), correctCRDName, clusterctlv1.SkipCRDNamePreflightCheckAnnotation)
-}
-
 // getProviderContract returns the API Version of Cluster API (contract) for a provider instance.
 func (i *providerInstaller) getProviderContract(providerInstanceContracts map[string]string, provider clusterctlv1.Provider) (string, error) {
 	// If the contract for the provider instance is already known, return it.
diff --git a/cmd/clusterctl/client/init.go b/cmd/clusterctl/client/init.go
index 0f9e50cec..4e5f7742f 100644
--- a/cmd/clusterctl/client/init.go
+++ b/cmd/clusterctl/client/init.go
@@ -117,8 +117,6 @@ func (c *clusterctlClient) Init(options InitOptions) ([]Components, error) {
 	// Before installing the providers, validates the management cluster resulting by the planned installation. The following checks are performed:
 	// - There should be only one instance of the same provider.
 	// - All the providers must support the same API Version of Cluster API (contract)
-	// - All provider CRDs that are referenced in core Cluster API CRDs must comply with the CRD naming scheme,
-	//   otherwise a warning is logged.
 	if err := installer.Validate(); err != nil {
 		if !options.IgnoreValidationErrors {
 			return nil, err
diff --git a/config/crd/bases/cluster.x-k8s.io_clusterclasses.yaml b/config/crd/bases/cluster.x-k8s.io_clusterclasses.yaml
index 3902950d6..269a376ad 100644
--- a/config/crd/bases/cluster.x-k8s.io_clusterclasses.yaml
+++ b/config/crd/bases/cluster.x-k8s.io_clusterclasses.yaml
@@ -584,6 +584,28 @@ spec:
                           More info: http://kubernetes.io/docs/user-guide/labels'
                         type: object
                     type: object
+                  nodeDeletionTimeout:
+                    description: 'NodeDeletionTimeout defines how long the controller
+                      will attempt to delete the Node that the Machine hosts after
+                      the Machine is marked for deletion. A duration of 0 will retry
+                      deletion indefinitely. Defaults to 10 seconds. NOTE: This value
+                      can be overridden while defining a Cluster.Topology.'
+                    type: string
+                  nodeDrainTimeout:
+                    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` NOTE: This value can be overridden while defining
+                      a Cluster.Topology.'
+                    type: string
+                  nodeVolumeDetachTimeout:
+                    description: 'NodeVolumeDetachTimeout is the total amount of time
+                      that the controller will spend on waiting for all volumes to
+                      be detached. The default value is 0, meaning that the volumes
+                      can be detached without any time limitations. NOTE: This value
+                      can be overridden while defining a Cluster.Topology.'
+                    type: string
                   ref:
                     description: Ref is a required reference to a custom resource
                       offered by a provider.
@@ -1002,6 +1024,13 @@ spec:
                             and can be referenced in the Cluster to create a managed
                             MachineDeployment.
                           type: string
+                        failureDomain:
+                          description: 'FailureDomain is the failure domain the machines
+                            will be created in. Must match a key in the FailureDomains
+                            map stored on the cluster object. NOTE: This value can
+                            be overridden while defining a Cluster.Topology using
+                            this MachineDeploymentClass.'
+                          type: string
                         machineHealthCheck:
                           description: MachineHealthCheck defines a MachineHealthCheck
                             for this MachineDeploymentClass.
@@ -1105,6 +1134,106 @@ spec:
                               pattern: ^\[[0-9]+-[0-9]+\]$
                               type: string
                           type: object
+                        minReadySeconds:
+                          description: 'Minimum number of seconds for which a newly
+                            created machine should be ready. Defaults to 0 (machine
+                            will be considered available as soon as it is ready) NOTE:
+                            This value can be overridden while defining a Cluster.Topology
+                            using this MachineDeploymentClass.'
+                          format: int32
+                          type: integer
+                        nodeDeletionTimeout:
+                          description: 'NodeDeletionTimeout defines how long the controller
+                            will attempt to delete the Node that the Machine hosts
+                            after the Machine is marked for deletion. A duration of
+                            0 will retry deletion indefinitely. Defaults to 10 seconds.
+                            NOTE: This value can be overridden while defining a Cluster.Topology
+                            using this MachineDeploymentClass.'
+                          type: string
+                        nodeDrainTimeout:
+                          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` NOTE: This value
+                            can be overridden while defining a Cluster.Topology using
+                            this MachineDeploymentClass.'
+                          type: string
+                        nodeVolumeDetachTimeout:
+                          description: 'NodeVolumeDetachTimeout is the total amount
+                            of time that the controller will spend on waiting for
+                            all volumes to be detached. The default value is 0, meaning
+                            that the volumes can be detached without any time limitations.
+                            NOTE: This value can be overridden while defining a Cluster.Topology
+                            using this MachineDeploymentClass.'
+                          type: string
+                        strategy:
+                          description: 'The deployment strategy to use to replace
+                            existing machines with new ones. NOTE: This value can
+                            be overridden while defining a Cluster.Topology using
+                            this MachineDeploymentClass.'
+                          properties:
+                            rollingUpdate:
+                              description: Rolling update config params. Present only
+                                if MachineDeploymentStrategyType = RollingUpdate.
+                              properties:
+                                deletePolicy:
+                                  description: DeletePolicy defines the policy used
+                                    by the MachineDeployment to identify nodes to
+                                    delete when downscaling. Valid values are "Random,
+                                    "Newest", "Oldest" When no value is supplied,
+                                    the default DeletePolicy of MachineSet is used
+                                  enum:
+                                  - Random
+                                  - Newest
+                                  - Oldest
+                                  type: string
+                                maxSurge:
+                                  anyOf:
+                                  - type: integer
+                                  - type: string
+                                  description: 'The maximum number of machines that
+                                    can be scheduled above the desired number of machines.
+                                    Value can be an absolute number (ex: 5) or a percentage
+                                    of desired machines (ex: 10%). This can not be
+                                    0 if MaxUnavailable is 0. Absolute number is calculated
+                                    from percentage by rounding up. Defaults to 1.
+                                    Example: when this is set to 30%, the new MachineSet
+                                    can be scaled up immediately when the rolling
+                                    update starts, such that the total number of old
+                                    and new machines do not exceed 130% of desired
+                                    machines. Once old machines have been killed,
+                                    new MachineSet can be scaled up further, ensuring
+                                    that total number of machines running at any time
+                                    during the update is at most 130% of desired machines.'
+                                  x-kubernetes-int-or-string: true
+                                maxUnavailable:
+                                  anyOf:
+                                  - type: integer
+                                  - type: string
+                                  description: 'The maximum number of machines that
+                                    can be unavailable during the update. Value can
+                                    be an absolute number (ex: 5) or a percentage
+                                    of desired machines (ex: 10%). Absolute number
+                                    is calculated from percentage by rounding down.
+                                    This can not be 0 if MaxSurge is 0. Defaults to
+                                    0. Example: when this is set to 30%, the old MachineSet
+                                    can be scaled down to 70% of desired machines
+                                    immediately when the rolling update starts. Once
+                                    new machines are ready, old MachineSet can be
+                                    scaled down further, followed by scaling up the
+                                    new MachineSet, ensuring that the total number
+                                    of machines available at all times during the
+                                    update is at least 70% of desired machines.'
+                                  x-kubernetes-int-or-string: true
+                              type: object
+                            type:
+                              description: Type of deployment. Default is RollingUpdate.
+                              enum:
+                              - RollingUpdate
+                              - OnDelete
+                              type: string
+                          type: object
                         template:
                           description: Template is a local struct containing a collection
                             of templates for creation of MachineDeployment objects
diff --git a/controlplane/kubeadm/internal/controllers/controller.go b/controlplane/kubeadm/internal/controllers/controller.go
index a2cb30e99..1d9d1df14 100644
--- a/controlplane/kubeadm/internal/controllers/controller.go
+++ b/controlplane/kubeadm/internal/controllers/controller.go
@@ -600,7 +600,7 @@ func (r *KubeadmControlPlaneReconciler) reconcileCertificateExpiries(ctx context
 	}
 
 	for _, m := range machines {
-		log := log.WithValues("Machine", klog.KObj(m))
+		log = log.WithValues("Machine", klog.KObj(m))
 
 		kubeadmConfig, ok := controlPlane.GetKubeadmConfig(m.Name)
 		if !ok {
diff --git a/docs/book/src/developer/providers/v1.2-to-v1.3.md b/docs/book/src/developer/providers/v1.2-to-v1.3.md
index 8c5386145..784e3a3c7 100644
--- a/docs/book/src/developer/providers/v1.2-to-v1.3.md
+++ b/docs/book/src/developer/providers/v1.2-to-v1.3.md
@@ -45,9 +45,6 @@ The default value is 0, meaning that the volume can be detached without any time
 
 ### Other
 
-- clusterctl now emits a warning for provider CRDs which don't comply with the CRD naming conventions. This warning can be skipped for resources not referenced by Cluster API 
-  core resources via the `clusterctl.cluster.x-k8s.io/skip-crd-name-preflight-check` annotation. The contracts specify:
-  > The CRD name must have the format produced by sigs.k8s.io/cluster-api/util/contract.CalculateCRDName(Group, Kind)
 - The Kubernetes default registry has been changed from `k8s.gcr.io` to `registry.k8s.io`. Kubernetes image promotion currently publishes to both registries. Please
   consider publishing manifests which reference the controller images from the new registry (for reference [Cluster API PR](https://github.com/kubernetes-sigs/cluster-api/pull/7478)).
 - e2e tests are upgraded to use Ginkgo v2 (v2.4.0) and Gomega v1.22.1. Providers who use the test framework from this release will also need to upgrade, because Ginkgo v2 can't be imported alongside v1. Please see the [Ginkgo upgrade guide](https://onsi.github.io/ginkgo/MIGRATING_TO_V2), and note:
@@ -69,7 +66,3 @@ The default value is 0, meaning that the volume can be detached without any time
   The variable `SkipUpgrade` could be set to revert to the old behaviour by making use of the `KUBERNETES_VERSION` variable and skipping the kubernetes upgrade.
 - cert-manager upgraded from v1.9.1 to v1.10.0.
 - Machine `providerID` is now being strictly checked for equality when compared against Kubernetes node `providerID` data. This is the expected criteria for correlating a Cluster API machine to its corresponding Kubernetes node, but historically this comparison was not strict, and instead compared only against the `ID` substring part of the full `providerID` string. Because different providers construct `providerID` strings differently, the `ID` substring is not uniformly defined and implemented across providers, and thus the existing `providerID` equality can not guarantee the correct Machine-Node correlation. It is very unlikely that this new behavior will break existing providers, but FYI: if strict `providerID` equality will degrade expected behaviors, you may need to update your provider implementation prior to adopting Cluster API v1.3.
-- The default minimum TLS version in use by the webhook servers is 1.2.
-
-### Suggested changes for providers
-- Provider can expose the configuration of the TLS Options for the webhook server; it is recommended to use utility functions under the `util/flags` package to ensure consistency across CAPI and other providers.
\ No newline at end of file
diff --git a/docs/book/src/reference/labels_and_annotations.md b/docs/book/src/reference/labels_and_annotations.md
index e9cdcad5d..e15eb24b0 100644
--- a/docs/book/src/reference/labels_and_annotations.md
+++ b/docs/book/src/reference/labels_and_annotations.md
@@ -20,7 +20,6 @@
 
 | Annotation     | Note     |
 |:--------|:--------|
-| clusterctl.cluster.x-k8s.io/skip-crd-name-preflight-check   | Can be placed on provider CRDs, so that clusterctl doesn't emit a warning if the CRD doesn't comply with Cluster APIs naming scheme. Only CRDs that are referenced by core Cluster API CRDs have to comply with the naming scheme.   |
 | unsafe.topology.cluster.x-k8s.io/disable-update-class-name-check   | It can be used to disable the webhook check on update that disallows a pre-existing Cluster to be populated with Topology information and Class.  |
 | cluster.x-k8s.io/cluster-name   | It is set on nodes identifying the name of the cluster the node belongs to.  |
 |cluster.x-k8s.io/cluster-namespace    | It is set on nodes identifying the namespace of the cluster the node belongs to.   |
@@ -46,4 +45,4 @@
 |  machinedeployment.clusters.x-k8s.io/max-replicas  | It is the maximum replicas a deployment can have at a given point, which is machinedeployment.spec.replicas + maxSurge. Used by the underlying machine sets to estimate their proportions in case the deployment has surge replicas. |
 | controlplane.cluster.x-k8s.io/skip-coredns | It explicitly skips reconciling CoreDNS if set. |
 |controlplane.cluster.x-k8s.io/skip-kube-proxy | It explicitly skips reconciling kube-proxy if set.|
-| controlplane.cluster.x-k8s.io/kubeadm-cluster-configuration| It is a machine annotation that stores the json-marshalled string of KCP ClusterConfiguration. This annotation is used to detect any changes in ClusterConfiguration and trigger machine rollout in KCP.|
+| controlplane.cluster.x-k8s.io/kubeadm-cluster-configuration| It is a machine annotation that stores the json-marshalled string of KCP ClusterConfiguration. This annotation is used to detect any changes in ClusterConfiguration and trigger machine rollout in KCP.|
\ No newline at end of file
diff --git a/go.mod b/go.mod
index b1c748ceb..f7c96d84a 100644
--- a/go.mod
+++ b/go.mod
@@ -20,8 +20,8 @@ require (
 	github.com/google/go-github/v45 v45.2.0
 	github.com/google/gofuzz v1.2.0
 	github.com/mattn/go-runewidth v0.0.14 // indirect
-	github.com/onsi/ginkgo/v2 v2.5.0
-	github.com/onsi/gomega v1.24.1
+	github.com/onsi/ginkgo/v2 v2.4.0
+	github.com/onsi/gomega v1.22.1
 	github.com/pkg/errors v0.9.1
 	github.com/spf13/cobra v1.6.1
 	github.com/spf13/pflag v1.0.5
@@ -29,7 +29,7 @@ require (
 	github.com/valyala/fastjson v1.6.3
 	go.etcd.io/etcd/api/v3 v3.5.4
 	go.etcd.io/etcd/client/v3 v3.5.4
-	golang.org/x/net v0.2.0 // indirect
+	golang.org/x/net v0.1.0 // indirect
 	golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1
 	google.golang.org/grpc v1.47.0
 	k8s.io/api v0.25.0
@@ -130,8 +130,8 @@ require (
 	go.uber.org/zap v1.21.0 // indirect
 	go4.org v0.0.0-20201209231011-d4a079459e60 // indirect
 	golang.org/x/crypto v0.1.0 // indirect
-	golang.org/x/sys v0.2.0 // indirect
-	golang.org/x/term v0.2.0 // indirect
+	golang.org/x/sys v0.1.0 // indirect
+	golang.org/x/term v0.1.0 // indirect
 	golang.org/x/text v0.4.0
 	golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 // indirect
 	gomodules.xyz/jsonpatch/v2 v2.2.0
diff --git a/go.sum b/go.sum
index 370899b4d..6b48a04bb 100644
--- a/go.sum
+++ b/go.sum
@@ -484,10 +484,10 @@ github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn
 github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
 github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
 github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
-github.com/onsi/ginkgo/v2 v2.5.0 h1:TRtrvv2vdQqzkwrQ1ke6vtXf7IK34RBUJafIy1wMwls=
-github.com/onsi/ginkgo/v2 v2.5.0/go.mod h1:Luc4sArBICYCS8THh8v3i3i5CuSZO+RaQRaJoeNwomw=
-github.com/onsi/gomega v1.24.1 h1:KORJXNNTzJXzu4ScJWssJfJMnJ+2QJqhoQSRwNlze9E=
-github.com/onsi/gomega v1.24.1/go.mod h1:3AOiACssS3/MajrniINInwbfOOtfZvplPzuRSmvt1jM=
+github.com/onsi/ginkgo/v2 v2.4.0 h1:+Ig9nvqgS5OBSACXNk15PLdp0U9XPYROt9CFzVdFGIs=
+github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo=
+github.com/onsi/gomega v1.22.1 h1:pY8O4lBfsHKZHM/6nrxkhVPUznOlIu3quZcKP/M20KI=
+github.com/onsi/gomega v1.22.1/go.mod h1:x6n7VNe4hw0vkyYUM4mjIXx3JbLiPaBPNgB7PRQ1tuM=
 github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
 github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
@@ -759,8 +759,8 @@ golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su
 golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
-golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
+golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0=
+golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -872,12 +872,12 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
-golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
+golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM=
-golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
+golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw=
+golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
diff --git a/hack/tools/go.mod b/hack/tools/go.mod
index fe33476cc..68255b9a2 100644
--- a/hack/tools/go.mod
+++ b/hack/tools/go.mod
@@ -106,7 +106,7 @@ require (
 	github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
 	github.com/morikuni/aec v1.0.0 // indirect
 	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
-	github.com/onsi/gomega v1.24.1 // indirect
+	github.com/onsi/gomega v1.22.1 // indirect
 	github.com/opencontainers/go-digest v1.0.0 // indirect
 	github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect
 	github.com/pelletier/go-toml v1.9.5 // indirect
@@ -130,11 +130,11 @@ require (
 	go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect
 	golang.org/x/crypto v0.1.0 // indirect
 	golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
-	golang.org/x/net v0.2.0 // indirect
+	golang.org/x/net v0.1.0 // indirect
 	golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1 // indirect
 	golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
-	golang.org/x/sys v0.2.0 // indirect
-	golang.org/x/term v0.2.0 // indirect
+	golang.org/x/sys v0.1.0 // indirect
+	golang.org/x/term v0.1.0 // indirect
 	golang.org/x/text v0.4.0 // indirect
 	golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 // indirect
 	golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect
diff --git a/hack/tools/go.sum b/hack/tools/go.sum
index 262b6319d..ff725522a 100644
--- a/hack/tools/go.sum
+++ b/hack/tools/go.sum
@@ -407,9 +407,9 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW
 github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
 github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
-github.com/onsi/ginkgo/v2 v2.5.0 h1:TRtrvv2vdQqzkwrQ1ke6vtXf7IK34RBUJafIy1wMwls=
-github.com/onsi/gomega v1.24.1 h1:KORJXNNTzJXzu4ScJWssJfJMnJ+2QJqhoQSRwNlze9E=
-github.com/onsi/gomega v1.24.1/go.mod h1:3AOiACssS3/MajrniINInwbfOOtfZvplPzuRSmvt1jM=
+github.com/onsi/ginkgo/v2 v2.4.0 h1:+Ig9nvqgS5OBSACXNk15PLdp0U9XPYROt9CFzVdFGIs=
+github.com/onsi/gomega v1.22.1 h1:pY8O4lBfsHKZHM/6nrxkhVPUznOlIu3quZcKP/M20KI=
+github.com/onsi/gomega v1.22.1/go.mod h1:x6n7VNe4hw0vkyYUM4mjIXx3JbLiPaBPNgB7PRQ1tuM=
 github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
 github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 h1:rc3tiVYb5z54aKaDfakKn0dDjIyPpTtszkjuMzyt7ec=
@@ -627,8 +627,8 @@ golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su
 golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
-golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
+golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0=
+golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -738,12 +738,12 @@ golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
-golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
+golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM=
-golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
+golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw=
+golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
diff --git a/internal/controllers/machine/machine_controller.go b/internal/controllers/machine/machine_controller.go
index 4919e8935..52cdbd763 100644
--- a/internal/controllers/machine/machine_controller.go
+++ b/internal/controllers/machine/machine_controller.go
@@ -253,12 +253,14 @@ func patchMachine(ctx context.Context, patchHelper *patch.Helper, machine *clust
 }
 
 func (r *Reconciler) reconcile(ctx context.Context, cluster *clusterv1.Cluster, m *clusterv1.Machine) (ctrl.Result, error) {
+	log := ctrl.LoggerFrom(ctx)
+
 	if err := r.watchClusterNodes(ctx, cluster); err != nil {
-		return ctrl.Result{}, errors.Wrapf(err, "error watching nodes on target cluster")
+		log.Error(err, "error watching nodes on target cluster")
+		return ctrl.Result{}, err
 	}
 
-	// If the machine is a stand-alone one, meaning not originated from a MachineDeployment, then set it as directly
-	// owned by the Cluster (if not already present).
+	// If the Machine belongs to a cluster, add an owner reference.
 	if r.shouldAdopt(m) {
 		m.OwnerReferences = util.EnsureOwnerRef(m.OwnerReferences, metav1.OwnerReference{
 			APIVersion: clusterv1.GroupVersion.String(),
@@ -739,20 +741,8 @@ func (r *Reconciler) reconcileDeleteExternal(ctx context.Context, m *clusterv1.M
 	return obj, nil
 }
 
-// shouldAdopt returns true if the Machine should be adopted as a stand-alone Machine directly owned by the Cluster.
 func (r *Reconciler) shouldAdopt(m *clusterv1.Machine) bool {
-	// if the machine is controlled by something (MS or KCP), or if it is a stand-alone machine directly owned by the Cluster, then no-op.
-	if metav1.GetControllerOf(m) != nil || util.HasOwner(m.OwnerReferences, clusterv1.GroupVersion.String(), []string{"Cluster"}) {
-		return false
-	}
-
-	// If the Machine is originated by a MachineDeployment, this prevents it from being adopted as a stand-alone Machine.
-	// Note: this is required because after restore from a backup both the Machine controller and the
-	// MachineSet controller are racing to adopt Machines, see https://github.com/kubernetes-sigs/cluster-api/issues/7529
-	if _, ok := m.Labels[clusterv1.MachineDeploymentUniqueLabel]; ok {
-		return false
-	}
-	return true
+	return metav1.GetControllerOf(m) == nil && !util.HasOwner(m.OwnerReferences, clusterv1.GroupVersion.String(), []string{"Cluster"})
 }
 
 func (r *Reconciler) watchClusterNodes(ctx context.Context, cluster *clusterv1.Cluster) error {
diff --git a/internal/controllers/machinedeployment/machinedeployment_rollout_ondelete.go b/internal/controllers/machinedeployment/machinedeployment_rollout_ondelete.go
index 55be548ad..7f3db8775 100644
--- a/internal/controllers/machinedeployment/machinedeployment_rollout_ondelete.go
+++ b/internal/controllers/machinedeployment/machinedeployment_rollout_ondelete.go
@@ -85,7 +85,7 @@ func (r *Reconciler) reconcileOldMachineSetsOnDelete(ctx context.Context, oldMSs
 	totalReplicas := mdutil.GetReplicaCountForMachineSets(allMSs)
 	scaleDownAmount := totalReplicas - *deployment.Spec.Replicas
 	for _, oldMS := range oldMSs {
-		log := log.WithValues("MachineSet", klog.KObj(oldMS))
+		log = log.WithValues("MachineSet", klog.KObj(oldMS))
 		if oldMS.Spec.Replicas == nil || *oldMS.Spec.Replicas <= 0 {
 			log.V(4).Info("fully scaled down")
 			continue
@@ -138,7 +138,7 @@ func (r *Reconciler) reconcileOldMachineSetsOnDelete(ctx context.Context, oldMSs
 	}
 	log.V(4).Info("Finished reconcile of Old MachineSets to account for deleted machines. Now analyzing if there's more potential to scale down")
 	for _, oldMS := range oldMSs {
-		log := log.WithValues("MachineSet", klog.KObj(oldMS))
+		log = log.WithValues("MachineSet", klog.KObj(oldMS))
 		if scaleDownAmount <= 0 {
 			break
 		}
diff --git a/internal/controllers/machineset/machineset_controller.go b/internal/controllers/machineset/machineset_controller.go
index c67338e59..88dc22958 100644
--- a/internal/controllers/machineset/machineset_controller.go
+++ b/internal/controllers/machineset/machineset_controller.go
@@ -209,8 +209,6 @@ func (r *Reconciler) reconcile(ctx context.Context, cluster *clusterv1.Cluster,
 	}
 	machineSet.Labels[clusterv1.ClusterLabelName] = machineSet.Spec.ClusterName
 
-	// If the machine set is a stand alone one, meaning not originated from a MachineDeployment, then set it as directly
-	// owned by the Cluster (if not already present).
 	if r.shouldAdopt(machineSet) {
 		machineSet.OwnerReferences = util.EnsureOwnerRef(machineSet.OwnerReferences, metav1.OwnerReference{
 			APIVersion: clusterv1.GroupVersion.String(),
@@ -264,7 +262,7 @@ func (r *Reconciler) reconcile(ctx context.Context, cluster *clusterv1.Cluster,
 	filteredMachines := make([]*clusterv1.Machine, 0, len(allMachines.Items))
 	for idx := range allMachines.Items {
 		machine := &allMachines.Items[idx]
-		log := log.WithValues("Machine", klog.KObj(machine))
+		log = log.WithValues("Machine", klog.KObj(machine))
 		if shouldExcludeMachine(machineSet, machine) {
 			continue
 		}
@@ -285,7 +283,7 @@ func (r *Reconciler) reconcile(ctx context.Context, cluster *clusterv1.Cluster,
 
 	var errs []error
 	for _, machine := range filteredMachines {
-		log := log.WithValues("Machine", klog.KObj(machine))
+		log = log.WithValues("Machine", klog.KObj(machine))
 		// filteredMachines contains machines in deleting status to calculate correct status.
 		// skip remediation for those in deleting status.
 		if !machine.DeletionTimestamp.IsZero() {
@@ -371,8 +369,6 @@ func (r *Reconciler) syncReplicas(ctx context.Context, ms *clusterv1.MachineSet,
 		)
 
 		for i := 0; i < diff; i++ {
-			// Create a new logger so the global logger is not modified.
-			log := log
 			machine := r.getNewMachine(ms)
 
 			// Clone and set the infrastructure and bootstrap references.
@@ -639,20 +635,8 @@ func (r *Reconciler) getMachineSetsForMachine(ctx context.Context, m *clusterv1.
 	return mss, nil
 }
 
-// shouldAdopt returns true if the MachineSet should be adopted as a stand-alone MachineSet directly owned by the Cluster.
 func (r *Reconciler) shouldAdopt(ms *clusterv1.MachineSet) bool {
-	// if the MachineSet is controlled by a MachineDeployment, or if it is a stand-alone MachinesSet directly owned by the Cluster, then no-op.
-	if util.HasOwner(ms.OwnerReferences, clusterv1.GroupVersion.String(), []string{"MachineDeployment", "Cluster"}) {
-		return false
-	}
-
-	// If the MachineSet is originated by a MachineDeployment, this prevents it from being adopted as a stand-alone MachineSet.
-	// Note: this is required because after restore from a backup both the MachineSet controller and the
-	// MachineDeployment controller are racing to adopt MachineSets, see https://github.com/kubernetes-sigs/cluster-api/issues/7529
-	if _, ok := ms.Labels[clusterv1.MachineDeploymentUniqueLabel]; ok {
-		return false
-	}
-	return true
+	return !util.HasOwner(ms.OwnerReferences, clusterv1.GroupVersion.String(), []string{"MachineDeployment", "Cluster"})
 }
 
 // updateStatus updates the Status field for the MachineSet
@@ -681,7 +665,7 @@ func (r *Reconciler) updateStatus(ctx context.Context, cluster *clusterv1.Cluste
 	templateLabel := labels.Set(ms.Spec.Template.Labels).AsSelectorPreValidated()
 
 	for _, machine := range filteredMachines {
-		log := log.WithValues("Machine", klog.KObj(machine))
+		log = log.WithValues("Machine", klog.KObj(machine))
 
 		if templateLabel.Matches(labels.Set(machine.Labels)) {
 			fullyLabeledReplicasCount++
diff --git a/internal/controllers/topology/cluster/desired_state.go b/internal/controllers/topology/cluster/desired_state.go
index 8e2a89cec..08f670bd5 100644
--- a/internal/controllers/topology/cluster/desired_state.go
+++ b/internal/controllers/topology/cluster/desired_state.go
@@ -256,22 +256,34 @@ func (r *Reconciler) computeControlPlane(ctx context.Context, s *scope.Scope, in
 	}
 
 	// If it is required to manage the NodeDrainTimeout for the control plane, set the corresponding field.
+	nodeDrainTimeout := s.Blueprint.ClusterClass.Spec.ControlPlane.NodeDrainTimeout
 	if s.Blueprint.Topology.ControlPlane.NodeDrainTimeout != nil {
-		if err := contract.ControlPlane().MachineTemplate().NodeDrainTimeout().Set(controlPlane, *s.Blueprint.Topology.ControlPlane.NodeDrainTimeout); err != nil {
+		nodeDrainTimeout = s.Blueprint.Topology.ControlPlane.NodeDrainTimeout
+	}
+	if nodeDrainTimeout != nil {
+		if err := contract.ControlPlane().MachineTemplate().NodeDrainTimeout().Set(controlPlane, *nodeDrainTimeout); err != nil {
 			return nil, errors.Wrap(err, "failed to set spec.machineTemplate.nodeDrainTimeout in the ControlPlane object")
 		}
 	}
 
 	// If it is required to manage the NodeVolumeDetachTimeout for the control plane, set the corresponding field.
+	nodeVolumeDetachTimeout := s.Blueprint.ClusterClass.Spec.ControlPlane.NodeVolumeDetachTimeout
 	if s.Blueprint.Topology.ControlPlane.NodeVolumeDetachTimeout != nil {
-		if err := contract.ControlPlane().MachineTemplate().NodeVolumeDetachTimeout().Set(controlPlane, *s.Blueprint.Topology.ControlPlane.NodeVolumeDetachTimeout); err != nil {
+		nodeVolumeDetachTimeout = s.Blueprint.Topology.ControlPlane.NodeVolumeDetachTimeout
+	}
+	if nodeVolumeDetachTimeout != nil {
+		if err := contract.ControlPlane().MachineTemplate().NodeVolumeDetachTimeout().Set(controlPlane, *nodeVolumeDetachTimeout); err != nil {
 			return nil, errors.Wrap(err, "failed to set spec.machineTemplate.nodeVolumeDetachTimeout in the ControlPlane object")
 		}
 	}
 
 	// If it is required to manage the NodeDeletionTimeout for the control plane, set the corresponding field.
+	nodeDeletionTimeout := s.Blueprint.ClusterClass.Spec.ControlPlane.NodeDeletionTimeout
 	if s.Blueprint.Topology.ControlPlane.NodeDeletionTimeout != nil {
-		if err := contract.ControlPlane().MachineTemplate().NodeDeletionTimeout().Set(controlPlane, *s.Blueprint.Topology.ControlPlane.NodeDeletionTimeout); err != nil {
+		nodeDeletionTimeout = s.Blueprint.Topology.ControlPlane.NodeDeletionTimeout
+	}
+	if nodeDeletionTimeout != nil {
+		if err := contract.ControlPlane().MachineTemplate().NodeDeletionTimeout().Set(controlPlane, *nodeDeletionTimeout); err != nil {
 			return nil, errors.Wrap(err, "failed to set spec.machineTemplate.nodeDeletionTimeout in the ControlPlane object")
 		}
 	}
@@ -526,6 +538,18 @@ func computeMachineDeployment(_ context.Context, s *scope.Scope, desiredControlP
 		return nil, errors.Errorf("MachineDeployment class %s not found in %s", className, tlog.KObj{Obj: s.Blueprint.ClusterClass})
 	}
 
+	var machineDeploymentClass *clusterv1.MachineDeploymentClass
+	for _, mdClass := range s.Blueprint.ClusterClass.Spec.Workers.MachineDeployments {
+		mdClass := mdClass
+		if mdClass.Class == className {
+			machineDeploymentClass = &mdClass
+			break
+		}
+	}
+	if machineDeploymentClass == nil {
+		return nil, errors.Errorf("MachineDeployment class %s not found in %s", className, tlog.KObj{Obj: s.Blueprint.ClusterClass})
+	}
+
 	// Compute the bootstrap template.
 	currentMachineDeployment := s.Current.MachineDeployments[machineDeploymentTopology.Name]
 	var currentBootstrapTemplateRef *corev1.ObjectReference
@@ -581,6 +605,37 @@ func computeMachineDeployment(_ context.Context, s *scope.Scope, desiredControlP
 		return nil, errors.Wrapf(err, "failed to compute version for %s", machineDeploymentTopology.Name)
 	}
 
+	// Compute values that can be set both in the MachineDeploymentClass and in the MachineDeploymentTopology
+	minReadySeconds := machineDeploymentClass.MinReadySeconds
+	if machineDeploymentTopology.MinReadySeconds != nil {
+		minReadySeconds = machineDeploymentTopology.MinReadySeconds
+	}
+
+	strategy := machineDeploymentClass.Strategy
+	if machineDeploymentTopology.Strategy != nil {
+		strategy = machineDeploymentTopology.Strategy
+	}
+
+	failureDomain := machineDeploymentClass.FailureDomain
+	if machineDeploymentTopology.FailureDomain != nil {
+		failureDomain = machineDeploymentTopology.FailureDomain
+	}
+
+	nodeDrainTimeout := machineDeploymentClass.NodeDrainTimeout
+	if machineDeploymentTopology.NodeDrainTimeout != nil {
+		nodeDrainTimeout = machineDeploymentTopology.NodeDrainTimeout
+	}
+
+	nodeVolumeDetachTimeout := machineDeploymentClass.NodeVolumeDetachTimeout
+	if machineDeploymentTopology.NodeVolumeDetachTimeout != nil {
+		nodeVolumeDetachTimeout = machineDeploymentTopology.NodeVolumeDetachTimeout
+	}
+
+	nodeDeletionTimeout := machineDeploymentClass.NodeDeletionTimeout
+	if machineDeploymentTopology.NodeDeletionTimeout != nil {
+		nodeDeletionTimeout = machineDeploymentTopology.NodeDeletionTimeout
+	}
+
 	// Compute the MachineDeployment object.
 	desiredBootstrapTemplateRef, err := calculateRefDesiredAPIVersion(currentBootstrapTemplateRef, desiredMachineDeployment.BootstrapTemplate)
 	if err != nil {
@@ -602,8 +657,8 @@ func computeMachineDeployment(_ context.Context, s *scope.Scope, desiredControlP
 		},
 		Spec: clusterv1.MachineDeploymentSpec{
 			ClusterName:     s.Current.Cluster.Name,
-			MinReadySeconds: machineDeploymentTopology.MinReadySeconds,
-			Strategy:        machineDeploymentTopology.Strategy,
+			MinReadySeconds: minReadySeconds,
+			Strategy:        strategy,
 			Template: clusterv1.MachineTemplateSpec{
 				ObjectMeta: clusterv1.ObjectMeta{
 					Labels:      mergeMap(machineDeploymentTopology.Metadata.Labels, machineDeploymentBlueprint.Metadata.Labels),
@@ -614,10 +669,10 @@ func computeMachineDeployment(_ context.Context, s *scope.Scope, desiredControlP
 					Version:                 pointer.String(version),
 					Bootstrap:               clusterv1.Bootstrap{ConfigRef: desiredBootstrapTemplateRef},
 					InfrastructureRef:       *desiredInfraMachineTemplateRef,
-					FailureDomain:           machineDeploymentTopology.FailureDomain,
-					NodeDrainTimeout:        machineDeploymentTopology.NodeDrainTimeout,
-					NodeVolumeDetachTimeout: machineDeploymentTopology.NodeVolumeDetachTimeout,
-					NodeDeletionTimeout:     machineDeploymentTopology.NodeDeletionTimeout,
+					FailureDomain:           failureDomain,
+					NodeDrainTimeout:        nodeDrainTimeout,
+					NodeVolumeDetachTimeout: nodeVolumeDetachTimeout,
+					NodeDeletionTimeout:     nodeDeletionTimeout,
 				},
 			},
 		},
diff --git a/internal/controllers/topology/cluster/patches/engine.go b/internal/controllers/topology/cluster/patches/engine.go
index 586f37dad..0b29481bf 100644
--- a/internal/controllers/topology/cluster/patches/engine.go
+++ b/internal/controllers/topology/cluster/patches/engine.go
@@ -78,7 +78,7 @@ func (e *engine) Apply(ctx context.Context, blueprint *scope.ClusterBlueprint, d
 	// respecting the order in which they are defined.
 	for i := range blueprint.ClusterClass.Spec.Patches {
 		clusterClassPatch := blueprint.ClusterClass.Spec.Patches[i]
-		ctx, log := log.WithValues("patch", clusterClassPatch.Name).Into(ctx)
+		ctx, log = log.WithValues("patch", clusterClassPatch.Name).Into(ctx)
 
 		log.V(5).Infof("Applying patch to templates")
 
@@ -115,7 +115,7 @@ func (e *engine) Apply(ctx context.Context, blueprint *scope.ClusterBlueprint, d
 			continue
 		}
 
-		ctx, log := log.WithValues("patch", clusterClassPatch.Name).Into(ctx)
+		ctx, log = log.WithValues("patch", clusterClassPatch.Name).Into(ctx)
 
 		log.V(5).Infof("Validating topology")
 
@@ -283,7 +283,7 @@ func applyPatchesToRequest(ctx context.Context, req *runtimehooksv1.GeneratePatc
 	log := tlog.LoggerFrom(ctx)
 
 	for _, patch := range resp.Items {
-		log := log.WithValues("uid", patch.UID)
+		log = log.WithValues("uid", patch.UID)
 
 		// Get the request item the patch belongs to.
 		requestItem := getRequestItemByUID(req, patch.UID)
diff --git a/internal/controllers/topology/cluster/patches/variables/variables.go b/internal/controllers/topology/cluster/patches/variables/variables.go
index aafe95c33..fe618e147 100644
--- a/internal/controllers/topology/cluster/patches/variables/variables.go
+++ b/internal/controllers/topology/cluster/patches/variables/variables.go
@@ -77,8 +77,6 @@ type ClusterNetworkBuiltins struct {
 	// Pods is the network ranges from which Pod networks are allocated.
 	Pods []string `json:"pods,omitempty"`
 	// IPFamily is the IPFamily the Cluster is operating in. One of Invalid, IPv4, IPv6, DualStack.
-	// Note: IPFamily is not a concept in Kubernetes. It was originally introduced in CAPI for CAPD.
-	// IPFamily may be dropped in a future release. More details at https://github.com/kubernetes-sigs/cluster-api/issues/7521
 	IPFamily string `json:"ipFamily,omitempty"`
 }
 
@@ -199,7 +197,10 @@ func Global(clusterTopology *clusterv1.Topology, cluster *clusterv1.Cluster) ([]
 		},
 	}
 	if cluster.Spec.ClusterNetwork != nil {
-		clusterNetworkIPFamily, _ := cluster.GetIPFamily()
+		clusterNetworkIPFamily, err := cluster.GetIPFamily()
+		if err != nil {
+			return nil, err
+		}
 		builtin.Cluster.Network = &ClusterNetworkBuiltins{
 			IPFamily: ipFamilyToString(clusterNetworkIPFamily),
 		}
diff --git a/internal/webhooks/cluster.go b/internal/webhooks/cluster.go
index ef6a6d870..9dc499b2a 100644
--- a/internal/webhooks/cluster.go
+++ b/internal/webhooks/cluster.go
@@ -19,7 +19,6 @@ package webhooks
 import (
 	"context"
 	"fmt"
-	"net"
 	"strings"
 
 	"github.com/blang/semver"
@@ -171,19 +170,6 @@ func (webhook *Cluster) validate(ctx context.Context, oldCluster, newCluster *cl
 			),
 		)
 	}
-	if newCluster.Spec.ClusterNetwork != nil {
-		// Ensure that the CIDR blocks defined under ClusterNetwork are valid.
-		if newCluster.Spec.ClusterNetwork.Pods != nil {
-			allErrs = append(allErrs, validateCIDRBlocks(specPath.Child("clusterNetwork", "pods", "cidrBlocks"),
-				newCluster.Spec.ClusterNetwork.Pods.CIDRBlocks)...)
-		}
-
-		if newCluster.Spec.ClusterNetwork.Services != nil {
-			allErrs = append(allErrs, validateCIDRBlocks(specPath.Child("clusterNetwork", "services", "cidrBlocks"),
-				newCluster.Spec.ClusterNetwork.Services.CIDRBlocks)...)
-		}
-	}
-
 	topologyPath := specPath.Child("topology")
 
 	// Validate the managed topology, if defined.
@@ -457,17 +443,3 @@ func machineDeploymentClassOfName(clusterClass *clusterv1.ClusterClass, name str
 	}
 	return nil
 }
-
-// validateCIDRBlocks ensures the passed CIDR is valid.
-func validateCIDRBlocks(fldPath *field.Path, cidrs []string) field.ErrorList {
-	var allErrs field.ErrorList
-	for i, cidr := range cidrs {
-		if _, _, err := net.ParseCIDR(cidr); err != nil {
-			allErrs = append(allErrs, field.Invalid(
-				fldPath.Index(i),
-				cidr,
-				err.Error()))
-		}
-	}
-	return allErrs
-}


Test patch:
diff --git a/cmd/clusterctl/client/cluster/installer_test.go b/cmd/clusterctl/client/cluster/installer_test.go
index 6b34cd2a0..1c71a5680 100644
--- a/cmd/clusterctl/client/cluster/installer_test.go
+++ b/cmd/clusterctl/client/cluster/installer_test.go
@@ -21,7 +21,6 @@ import (
 
 	. "github.com/onsi/gomega"
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
-	"k8s.io/apimachinery/pkg/runtime/schema"
 
 	clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3"
 	"sigs.k8s.io/cluster-api/cmd/clusterctl/client/config"
@@ -258,49 +257,6 @@ func Test_providerInstaller_Validate(t *testing.T) {
 	}
 }
 
-func Test_providerInstaller_ValidateCRDName(t *testing.T) {
-	tests := []struct {
-		name    string
-		crd     unstructured.Unstructured
-		gk      *schema.GroupKind
-		wantErr bool
-	}{
-		{
-			name:    "CRD with valid name",
-			crd:     newFakeCRD("clusterclasses.cluster.x-k8s.io", nil),
-			gk:      &schema.GroupKind{Group: "cluster.x-k8s.io", Kind: "ClusterClass"},
-			wantErr: false,
-		},
-		{
-			name:    "CRD with invalid name",
-			crd:     newFakeCRD("clusterclass.cluster.x-k8s.io", nil),
-			gk:      &schema.GroupKind{Group: "cluster.x-k8s.io", Kind: "ClusterClass"},
-			wantErr: true,
-		},
-		{
-			name: "CRD with invalid name but has skip annotation",
-			crd: newFakeCRD("clusterclass.cluster.x-k8s.io", map[string]string{
-				clusterctlv1.SkipCRDNamePreflightCheckAnnotation: "",
-			}),
-			gk:      &schema.GroupKind{Group: "cluster.x-k8s.io", Kind: "ClusterClass"},
-			wantErr: false,
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			g := NewWithT(t)
-
-			err := validateCRDName(tt.crd, tt.gk)
-			if tt.wantErr {
-				g.Expect(err).To(HaveOccurred())
-			} else {
-				g.Expect(err).NotTo(HaveOccurred())
-			}
-		})
-	}
-}
-
 type fakeComponents struct {
 	config.Provider
 	inventoryObject clusterctlv1.Provider
@@ -327,7 +283,7 @@ func (c *fakeComponents) InventoryObject() clusterctlv1.Provider {
 }
 
 func (c *fakeComponents) Objs() []unstructured.Unstructured {
-	return []unstructured.Unstructured{}
+	panic("not implemented")
 }
 
 func (c *fakeComponents) Yaml() ([]byte, error) {
@@ -341,10 +297,3 @@ func newFakeComponents(name string, providerType clusterctlv1.ProviderType, vers
 		inventoryObject: inventoryObject,
 	}
 }
-
-func newFakeCRD(name string, annotations map[string]string) unstructured.Unstructured {
-	u := unstructured.Unstructured{}
-	u.SetName(name)
-	u.SetAnnotations(annotations)
-	return u
-}
diff --git a/internal/controllers/topology/cluster/desired_state_test.go b/internal/controllers/topology/cluster/desired_state_test.go
index 562128504..7ae9b3a04 100644
--- a/internal/controllers/topology/cluster/desired_state_test.go
+++ b/internal/controllers/topology/cluster/desired_state_test.go
@@ -253,18 +253,22 @@ func TestComputeControlPlane(t *testing.T) {
 
 	controlPlaneTemplate := builder.ControlPlaneTemplate(metav1.NamespaceDefault, "template1").
 		Build()
+	clusterClassDuration := 20 * time.Second
 	clusterClass := builder.ClusterClass(metav1.NamespaceDefault, "class1").
 		WithControlPlaneMetadata(labels, annotations).
 		WithControlPlaneTemplate(controlPlaneTemplate).
+		WithControlPlaneNodeDrainTimeout(&metav1.Duration{Duration: clusterClassDuration}).
+		WithControlPlaneNodeVolumeDetachTimeout(&metav1.Duration{Duration: clusterClassDuration}).
+		WithControlPlaneNodeDeletionTimeout(&metav1.Duration{Duration: clusterClassDuration}).
 		Build()
 	// TODO: Replace with object builder.
 	// current cluster objects
 	version := "v1.21.2"
 	replicas := int32(3)
-	duration := 10 * time.Second
-	nodeDrainTimeout := metav1.Duration{Duration: duration}
-	nodeVolumeDetachTimeout := metav1.Duration{Duration: duration}
-	nodeDeletionTimeout := metav1.Duration{Duration: duration}
+	topologyDuration := 10 * time.Second
+	nodeDrainTimeout := metav1.Duration{Duration: topologyDuration}
+	nodeVolumeDetachTimeout := metav1.Duration{Duration: topologyDuration}
+	nodeDeletionTimeout := metav1.Duration{Duration: topologyDuration}
 	cluster := &clusterv1.Cluster{
 		ObjectMeta: metav1.ObjectMeta{
 			Name:      "cluster1",
@@ -318,14 +322,60 @@ func TestComputeControlPlane(t *testing.T) {
 
 		assertNestedField(g, obj, version, contract.ControlPlane().Version().Path()...)
 		assertNestedField(g, obj, int64(replicas), contract.ControlPlane().Replicas().Path()...)
-		assertNestedField(g, obj, duration.String(), contract.ControlPlane().MachineTemplate().NodeDrainTimeout().Path()...)
-		assertNestedField(g, obj, duration.String(), contract.ControlPlane().MachineTemplate().NodeVolumeDetachTimeout().Path()...)
-		assertNestedField(g, obj, duration.String(), contract.ControlPlane().MachineTemplate().NodeDeletionTimeout().Path()...)
+		assertNestedField(g, obj, topologyDuration.String(), contract.ControlPlane().MachineTemplate().NodeDrainTimeout().Path()...)
+		assertNestedField(g, obj, topologyDuration.String(), contract.ControlPlane().MachineTemplate().NodeVolumeDetachTimeout().Path()...)
+		assertNestedField(g, obj, topologyDuration.String(), contract.ControlPlane().MachineTemplate().NodeDeletionTimeout().Path()...)
 		assertNestedFieldUnset(g, obj, contract.ControlPlane().MachineTemplate().InfrastructureRef().Path()...)
 
 		// Ensure no ownership is added to generated ControlPlane.
 		g.Expect(obj.GetOwnerReferences()).To(HaveLen(0))
 	})
+	t.Run("Generates the ControlPlane from the template using ClusterClass defaults", func(t *testing.T) {
+		g := NewWithT(t)
+
+		cluster := &clusterv1.Cluster{
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      "cluster1",
+				Namespace: metav1.NamespaceDefault,
+			},
+			Spec: clusterv1.ClusterSpec{
+				Topology: &clusterv1.Topology{
+					Version: version,
+					ControlPlane: clusterv1.ControlPlaneTopology{
+						Metadata: clusterv1.ObjectMeta{
+							Labels:      map[string]string{"l2": ""},
+							Annotations: map[string]string{"a2": ""},
+						},
+						Replicas: &replicas,
+						// no values for NodeDrainTimeout, NodeVolumeDetachTimeout, NodeDeletionTimeout
+					},
+				},
+			},
+		}
+
+		blueprint := &scope.ClusterBlueprint{
+			Topology:     cluster.Spec.Topology,
+			ClusterClass: clusterClass,
+			ControlPlane: &scope.ControlPlaneBlueprint{
+				Template: controlPlaneTemplate,
+			},
+		}
+
+		// aggregating current cluster objects into ClusterState (simulating getCurrentState)
+		scope := scope.New(cluster)
+		scope.Blueprint = blueprint
+
+		r := &Reconciler{}
+
+		obj, err := r.computeControlPlane(ctx, scope, nil)
+		g.Expect(err).ToNot(HaveOccurred())
+		g.Expect(obj).ToNot(BeNil())
+
+		// checking only values from CC defaults
+		assertNestedField(g, obj, clusterClassDuration.String(), contract.ControlPlane().MachineTemplate().NodeDrainTimeout().Path()...)
+		assertNestedField(g, obj, clusterClassDuration.String(), contract.ControlPlane().MachineTemplate().NodeVolumeDetachTimeout().Path()...)
+		assertNestedField(g, obj, clusterClassDuration.String(), contract.ControlPlane().MachineTemplate().NodeDeletionTimeout().Path()...)
+	})
 	t.Run("Skips setting replicas if required", func(t *testing.T) {
 		g := NewWithT(t)
 
@@ -1275,6 +1325,12 @@ func TestComputeMachineDeployment(t *testing.T) {
 	}
 	nodeTimeoutDuration := &metav1.Duration{Duration: time.Duration(1)}
 
+	clusterClassFailureDomain := "A"
+	clusterClassDuration := metav1.Duration{Duration: 20 * time.Second}
+	var clusterClassMinReadySeconds int32 = 20
+	clusterClassStrategy := clusterv1.MachineDeploymentStrategy{
+		Type: clusterv1.OnDeleteMachineDeploymentStrategyType,
+	}
 	md1 := builder.MachineDeploymentClass("linux-worker").
 		WithLabels(labels).
 		WithAnnotations(annotations).
@@ -1284,6 +1340,12 @@ func TestComputeMachineDeployment(t *testing.T) {
 			UnhealthyConditions: unhealthyConditions,
 			NodeStartupTimeout:  nodeTimeoutDuration,
 		}).
+		WithFailureDomain(&clusterClassFailureDomain).
+		WithNodeDrainTimeout(&clusterClassDuration).
+		WithNodeVolumeDetachTimeout(&clusterClassDuration).
+		WithNodeDeletionTimeout(&clusterClassDuration).
+		WithMinReadySeconds(&clusterClassMinReadySeconds).
+		WithStrategy(&clusterClassStrategy).
 		Build()
 	mcds := []clusterv1.MachineDeploymentClass{*md1}
 	fakeClass := builder.ClusterClass(metav1.NamespaceDefault, "class1").
@@ -1324,13 +1386,11 @@ func TestComputeMachineDeployment(t *testing.T) {
 	}
 
 	replicas := int32(5)
-	failureDomain := "always-up-region"
-	nodeDrainTimeout := metav1.Duration{Duration: 10 * time.Second}
-	nodeVolumeDetachTimeout := metav1.Duration{Duration: 10 * time.Second}
-	nodeDeletionTimeout := metav1.Duration{Duration: 10 * time.Second}
-	minReadySeconds := int32(5)
-	strategy := clusterv1.MachineDeploymentStrategy{
-		Type: clusterv1.OnDeleteMachineDeploymentStrategyType,
+	topologyFailureDomain := "B"
+	topologyDuration := metav1.Duration{Duration: 10 * time.Second}
+	var topologyMinReadySeconds int32 = 10
+	topologyStrategy := clusterv1.MachineDeploymentStrategy{
+		Type: clusterv1.RollingUpdateMachineDeploymentStrategyType,
 	}
 	mdTopology := clusterv1.MachineDeploymentTopology{
 		Metadata: clusterv1.ObjectMeta{
@@ -1339,12 +1399,12 @@ func TestComputeMachineDeployment(t *testing.T) {
 		Class:                   "linux-worker",
 		Name:                    "big-pool-of-machines",
 		Replicas:                &replicas,
-		FailureDomain:           &failureDomain,
-		NodeDrainTimeout:        &nodeDrainTimeout,
-		NodeVolumeDetachTimeout: &nodeVolumeDetachTimeout,
-		NodeDeletionTimeout:     &nodeDeletionTimeout,
-		MinReadySeconds:         &minReadySeconds,
-		Strategy:                &strategy,
+		FailureDomain:           &topologyFailureDomain,
+		NodeDrainTimeout:        &topologyDuration,
+		NodeVolumeDetachTimeout: &topologyDuration,
+		NodeDeletionTimeout:     &topologyDuration,
+		MinReadySeconds:         &topologyMinReadySeconds,
+		Strategy:                &topologyStrategy,
 	}
 
 	t.Run("Generates the machine deployment and the referenced templates", func(t *testing.T) {
@@ -1371,11 +1431,12 @@ func TestComputeMachineDeployment(t *testing.T) {
 
 		actualMd := actual.Object
 		g.Expect(*actualMd.Spec.Replicas).To(Equal(replicas))
-		g.Expect(*actualMd.Spec.MinReadySeconds).To(Equal(minReadySeconds))
-		g.Expect(*actualMd.Spec.Strategy).To(Equal(strategy))
-		g.Expect(*actualMd.Spec.Template.Spec.FailureDomain).To(Equal(failureDomain))
-		g.Expect(*actualMd.Spec.Template.Spec.NodeDrainTimeout).To(Equal(nodeDrainTimeout))
-		g.Expect(*actualMd.Spec.Template.Spec.NodeDeletionTimeout).To(Equal(nodeDeletionTimeout))
+		g.Expect(*actualMd.Spec.MinReadySeconds).To(Equal(topologyMinReadySeconds))
+		g.Expect(*actualMd.Spec.Strategy).To(Equal(topologyStrategy))
+		g.Expect(*actualMd.Spec.Template.Spec.FailureDomain).To(Equal(topologyFailureDomain))
+		g.Expect(*actualMd.Spec.Template.Spec.NodeDrainTimeout).To(Equal(topologyDuration))
+		g.Expect(*actualMd.Spec.Template.Spec.NodeVolumeDetachTimeout).To(Equal(topologyDuration))
+		g.Expect(*actualMd.Spec.Template.Spec.NodeDeletionTimeout).To(Equal(topologyDuration))
 		g.Expect(actualMd.Spec.ClusterName).To(Equal("cluster1"))
 		g.Expect(actualMd.Name).To(ContainSubstring("cluster1"))
 		g.Expect(actualMd.Name).To(ContainSubstring("big-pool-of-machines"))
@@ -1393,6 +1454,33 @@ func TestComputeMachineDeployment(t *testing.T) {
 		g.Expect(actualMd.Spec.Template.Spec.InfrastructureRef.Name).ToNot(Equal("linux-worker-inframachinetemplate"))
 		g.Expect(actualMd.Spec.Template.Spec.Bootstrap.ConfigRef.Name).ToNot(Equal("linux-worker-bootstraptemplate"))
 	})
+	t.Run("Generates the machine deployment and the referenced templates using ClusterClass defaults", func(t *testing.T) {
+		g := NewWithT(t)
+		scope := scope.New(cluster)
+		scope.Blueprint = blueprint
+
+		mdTopology := clusterv1.MachineDeploymentTopology{
+			Metadata: clusterv1.ObjectMeta{
+				Labels: map[string]string{"foo": "baz"},
+			},
+			Class:    "linux-worker",
+			Name:     "big-pool-of-machines",
+			Replicas: &replicas,
+			// missing FailureDomain, NodeDrainTimeout, NodeVolumeDetachTimeout, NodeDeletionTimeout, MinReadySeconds, Strategy
+		}
+
+		actual, err := computeMachineDeployment(ctx, scope, nil, mdTopology)
+		g.Expect(err).ToNot(HaveOccurred())
+
+		// checking only values from CC defaults
+		actualMd := actual.Object
+		g.Expect(*actualMd.Spec.MinReadySeconds).To(Equal(clusterClassMinReadySeconds))
+		g.Expect(*actualMd.Spec.Strategy).To(Equal(clusterClassStrategy))
+		g.Expect(*actualMd.Spec.Template.Spec.FailureDomain).To(Equal(clusterClassFailureDomain))
+		g.Expect(*actualMd.Spec.Template.Spec.NodeDrainTimeout).To(Equal(clusterClassDuration))
+		g.Expect(*actualMd.Spec.Template.Spec.NodeVolumeDetachTimeout).To(Equal(clusterClassDuration))
+		g.Expect(*actualMd.Spec.Template.Spec.NodeDeletionTimeout).To(Equal(clusterClassDuration))
+	})
 
 	t.Run("If there is already a machine deployment, it preserves the object name and the reference names", func(t *testing.T) {
 		g := NewWithT(t)
@@ -1431,7 +1519,7 @@ func TestComputeMachineDeployment(t *testing.T) {
 		actualMd := actual.Object
 
 		g.Expect(*actualMd.Spec.Replicas).NotTo(Equal(currentReplicas))
-		g.Expect(*actualMd.Spec.Template.Spec.FailureDomain).To(Equal(failureDomain))
+		g.Expect(*actualMd.Spec.Template.Spec.FailureDomain).To(Equal(topologyFailureDomain))
 		g.Expect(actualMd.Name).To(Equal("existing-deployment-1"))
 
 		g.Expect(actualMd.Labels).To(HaveKeyWithValue(clusterv1.ClusterTopologyMachineDeploymentLabelName, "big-pool-of-machines"))
diff --git a/internal/test/builder/builders.go b/internal/test/builder/builders.go
index acc75341d..62c4c248b 100644
--- a/internal/test/builder/builders.go
+++ b/internal/test/builder/builders.go
@@ -39,7 +39,6 @@ type ClusterBuilder struct {
 	topology              *clusterv1.Topology
 	infrastructureCluster *unstructured.Unstructured
 	controlPlane          *unstructured.Unstructured
-	network               *clusterv1.ClusterNetwork
 }
 
 // Cluster returns a ClusterBuilder with the given name and namespace.
@@ -50,12 +49,6 @@ func Cluster(namespace, name string) *ClusterBuilder {
 	}
 }
 
-// WithClusterNetwork sets the ClusterNetwork for the ClusterBuilder.
-func (c *ClusterBuilder) WithClusterNetwork(clusterNetwork *clusterv1.ClusterNetwork) *ClusterBuilder {
-	c.network = clusterNetwork
-	return c
-}
-
 // WithLabels sets the labels for the ClusterBuilder.
 func (c *ClusterBuilder) WithLabels(labels map[string]string) *ClusterBuilder {
 	c.labels = labels
@@ -100,8 +93,7 @@ func (c *ClusterBuilder) Build() *clusterv1.Cluster {
 			Annotations: c.annotations,
 		},
 		Spec: clusterv1.ClusterSpec{
-			Topology:       c.topology,
-			ClusterNetwork: c.network,
+			Topology: c.topology,
 		},
 	}
 	if c.infrastructureCluster != nil {
@@ -247,6 +239,9 @@ type ClusterClassBuilder struct {
 	controlPlaneTemplate                      *unstructured.Unstructured
 	controlPlaneInfrastructureMachineTemplate *unstructured.Unstructured
 	controlPlaneMHC                           *clusterv1.MachineHealthCheckClass
+	controlPlaneNodeDrainTimeout              *metav1.Duration
+	controlPlaneNodeVolumeDetachTimeout       *metav1.Duration
+	controlPlaneNodeDeletionTimeout           *metav1.Duration
 	machineDeploymentClasses                  []clusterv1.MachineDeploymentClass
 	variables                                 []clusterv1.ClusterClassVariable
 	patches                                   []clusterv1.ClusterClassPatch
@@ -293,6 +288,24 @@ func (c *ClusterClassBuilder) WithControlPlaneMachineHealthCheck(mhc *clusterv1.
 	return c
 }
 
+// WithControlPlaneNodeDrainTimeout adds a NodeDrainTimeout for the ControlPlane to the ClusterClassBuilder.
+func (c *ClusterClassBuilder) WithControlPlaneNodeDrainTimeout(t *metav1.Duration) *ClusterClassBuilder {
+	c.controlPlaneNodeDrainTimeout = t
+	return c
+}
+
+// WithControlPlaneNodeVolumeDetachTimeout adds a NodeVolumeDetachTimeout for the ControlPlane to the ClusterClassBuilder.
+func (c *ClusterClassBuilder) WithControlPlaneNodeVolumeDetachTimeout(t *metav1.Duration) *ClusterClassBuilder {
+	c.controlPlaneNodeVolumeDetachTimeout = t
+	return c
+}
+
+// WithControlPlaneNodeDeletionTimeout adds a NodeDeletionTimeout for the ControlPlane to the ClusterClassBuilder.
+func (c *ClusterClassBuilder) WithControlPlaneNodeDeletionTimeout(t *metav1.Duration) *ClusterClassBuilder {
+	c.controlPlaneNodeDeletionTimeout = t
+	return c
+}
+
 // WithVariables adds the Variables the ClusterClassBuilder.
 func (c *ClusterClassBuilder) WithVariables(vars ...clusterv1.ClusterClassVariable) *ClusterClassBuilder {
 	c.variables = vars
@@ -346,6 +359,15 @@ func (c *ClusterClassBuilder) Build() *clusterv1.ClusterClass {
 	if c.controlPlaneMHC != nil {
 		obj.Spec.ControlPlane.MachineHealthCheck = c.controlPlaneMHC
 	}
+	if c.controlPlaneNodeDrainTimeout != nil {
+		obj.Spec.ControlPlane.NodeDrainTimeout = c.controlPlaneNodeDrainTimeout
+	}
+	if c.controlPlaneNodeVolumeDetachTimeout != nil {
+		obj.Spec.ControlPlane.NodeVolumeDetachTimeout = c.controlPlaneNodeVolumeDetachTimeout
+	}
+	if c.controlPlaneNodeDeletionTimeout != nil {
+		obj.Spec.ControlPlane.NodeDeletionTimeout = c.controlPlaneNodeDeletionTimeout
+	}
 	if c.controlPlaneInfrastructureMachineTemplate != nil {
 		obj.Spec.ControlPlane.MachineInfrastructure = &clusterv1.LocalObjectTemplate{
 			Ref: objToRef(c.controlPlaneInfrastructureMachineTemplate),
@@ -364,6 +386,12 @@ type MachineDeploymentClassBuilder struct {
 	labels                        map[string]string
 	annotations                   map[string]string
 	machineHealthCheckClass       *clusterv1.MachineHealthCheckClass
+	failureDomain                 *string
+	nodeDrainTimeout              *metav1.Duration
+	nodeVolumeDetachTimeout       *metav1.Duration
+	nodeDeletionTimeout           *metav1.Duration
+	minReadySeconds               *int32
+	strategy                      *clusterv1.MachineDeploymentStrategy
 }
 
 // MachineDeploymentClass returns a MachineDeploymentClassBuilder with the given name and namespace.
@@ -403,6 +431,42 @@ func (m *MachineDeploymentClassBuilder) WithMachineHealthCheckClass(mhc *cluster
 	return m
 }
 
+// WithFailureDomain sets the FailureDomain for the MachineDeploymentClassBuilder.
+func (m *MachineDeploymentClassBuilder) WithFailureDomain(f *string) *MachineDeploymentClassBuilder {
+	m.failureDomain = f
+	return m
+}
+
+// WithNodeDrainTimeout sets the NodeDrainTimeout for the MachineDeploymentClassBuilder.
+func (m *MachineDeploymentClassBuilder) WithNodeDrainTimeout(t *metav1.Duration) *MachineDeploymentClassBuilder {
+	m.nodeDrainTimeout = t
+	return m
+}
+
+// WithNodeVolumeDetachTimeout sets the NodeVolumeDetachTimeout for the MachineDeploymentClassBuilder.
+func (m *MachineDeploymentClassBuilder) WithNodeVolumeDetachTimeout(t *metav1.Duration) *MachineDeploymentClassBuilder {
+	m.nodeVolumeDetachTimeout = t
+	return m
+}
+
+// WithNodeDeletionTimeout sets the NodeDeletionTimeout for the MachineDeploymentClassBuilder.
+func (m *MachineDeploymentClassBuilder) WithNodeDeletionTimeout(t *metav1.Duration) *MachineDeploymentClassBuilder {
+	m.nodeDeletionTimeout = t
+	return m
+}
+
+// WithMinReadySeconds sets the MinReadySeconds for the MachineDeploymentClassBuilder.
+func (m *MachineDeploymentClassBuilder) WithMinReadySeconds(t *int32) *MachineDeploymentClassBuilder {
+	m.minReadySeconds = t
+	return m
+}
+
+// WithStrategy sets the Strategy for the MachineDeploymentClassBuilder.
+func (m *MachineDeploymentClassBuilder) WithStrategy(s *clusterv1.MachineDeploymentStrategy) *MachineDeploymentClassBuilder {
+	m.strategy = s
+	return m
+}
+
 // Build creates a full MachineDeploymentClass object with the variables passed to the MachineDeploymentClassBuilder.
 func (m *MachineDeploymentClassBuilder) Build() *clusterv1.MachineDeploymentClass {
 	obj := &clusterv1.MachineDeploymentClass{
@@ -423,6 +487,24 @@ func (m *MachineDeploymentClassBuilder) Build() *clusterv1.MachineDeploymentClas
 	if m.machineHealthCheckClass != nil {
 		obj.MachineHealthCheck = m.machineHealthCheckClass
 	}
+	if m.failureDomain != nil {
+		obj.FailureDomain = m.failureDomain
+	}
+	if m.nodeDrainTimeout != nil {
+		obj.NodeDrainTimeout = m.nodeDrainTimeout
+	}
+	if m.nodeVolumeDetachTimeout != nil {
+		obj.NodeVolumeDetachTimeout = m.nodeVolumeDetachTimeout
+	}
+	if m.nodeDeletionTimeout != nil {
+		obj.NodeDeletionTimeout = m.nodeDeletionTimeout
+	}
+	if m.minReadySeconds != nil {
+		obj.MinReadySeconds = m.minReadySeconds
+	}
+	if m.strategy != nil {
+		obj.Strategy = m.strategy
+	}
 	return obj
 }
 
diff --git a/internal/test/builder/zz_generated.deepcopy.go b/internal/test/builder/zz_generated.deepcopy.go
index f71f8a9cb..1c6d8cb0c 100644
--- a/internal/test/builder/zz_generated.deepcopy.go
+++ b/internal/test/builder/zz_generated.deepcopy.go
@@ -77,11 +77,6 @@ func (in *ClusterBuilder) DeepCopyInto(out *ClusterBuilder) {
 		in, out := &in.controlPlane, &out.controlPlane
 		*out = (*in).DeepCopy()
 	}
-	if in.network != nil {
-		in, out := &in.network, &out.network
-		*out = new(v1beta1.ClusterNetwork)
-		(*in).DeepCopyInto(*out)
-	}
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterBuilder.
@@ -119,6 +114,21 @@ func (in *ClusterClassBuilder) DeepCopyInto(out *ClusterClassBuilder) {
 		*out = new(v1beta1.MachineHealthCheckClass)
 		(*in).DeepCopyInto(*out)
 	}
+	if in.controlPlaneNodeDrainTimeout != nil {
+		in, out := &in.controlPlaneNodeDrainTimeout, &out.controlPlaneNodeDrainTimeout
+		*out = new(v1.Duration)
+		**out = **in
+	}
+	if in.controlPlaneNodeVolumeDetachTimeout != nil {
+		in, out := &in.controlPlaneNodeVolumeDetachTimeout, &out.controlPlaneNodeVolumeDetachTimeout
+		*out = new(v1.Duration)
+		**out = **in
+	}
+	if in.controlPlaneNodeDeletionTimeout != nil {
+		in, out := &in.controlPlaneNodeDeletionTimeout, &out.controlPlaneNodeDeletionTimeout
+		*out = new(v1.Duration)
+		**out = **in
+	}
 	if in.machineDeploymentClasses != nil {
 		in, out := &in.machineDeploymentClasses, &out.machineDeploymentClasses
 		*out = make([]v1beta1.MachineDeploymentClass, len(*in))
@@ -390,6 +400,36 @@ func (in *MachineDeploymentClassBuilder) DeepCopyInto(out *MachineDeploymentClas
 		*out = new(v1beta1.MachineHealthCheckClass)
 		(*in).DeepCopyInto(*out)
 	}
+	if in.failureDomain != nil {
+		in, out := &in.failureDomain, &out.failureDomain
+		*out = new(string)
+		**out = **in
+	}
+	if in.nodeDrainTimeout != nil {
+		in, out := &in.nodeDrainTimeout, &out.nodeDrainTimeout
+		*out = new(v1.Duration)
+		**out = **in
+	}
+	if in.nodeVolumeDetachTimeout != nil {
+		in, out := &in.nodeVolumeDetachTimeout, &out.nodeVolumeDetachTimeout
+		*out = new(v1.Duration)
+		**out = **in
+	}
+	if in.nodeDeletionTimeout != nil {
+		in, out := &in.nodeDeletionTimeout, &out.nodeDeletionTimeout
+		*out = new(v1.Duration)
+		**out = **in
+	}
+	if in.minReadySeconds != nil {
+		in, out := &in.minReadySeconds, &out.minReadySeconds
+		*out = new(int32)
+		**out = **in
+	}
+	if in.strategy != nil {
+		in, out := &in.strategy, &out.strategy
+		*out = new(v1beta1.MachineDeploymentStrategy)
+		(*in).DeepCopyInto(*out)
+	}
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MachineDeploymentClassBuilder.
diff --git a/internal/webhooks/cluster_test.go b/internal/webhooks/cluster_test.go
index d403f8dc0..de9b443ba 100644
--- a/internal/webhooks/cluster_test.go
+++ b/internal/webhooks/cluster_test.go
@@ -409,115 +409,6 @@ func TestClusterValidation(t *testing.T) {
 					WithTopology(&clusterv1.Topology{}).
 					Build(),
 			},
-			{
-				name:      "pass with undefined CIDR ranges",
-				expectErr: false,
-				in: builder.Cluster("fooNamespace", "cluster1").
-					WithClusterNetwork(&clusterv1.ClusterNetwork{
-						Services: &clusterv1.NetworkRanges{
-							CIDRBlocks: []string{}},
-						Pods: &clusterv1.NetworkRanges{
-							CIDRBlocks: []string{}},
-					}).
-					Build(),
-			},
-			{
-				name:      "pass with nil CIDR ranges",
-				expectErr: false,
-				in: builder.Cluster("fooNamespace", "cluster1").
-					WithClusterNetwork(&clusterv1.ClusterNetwork{
-						Services: &clusterv1.NetworkRanges{
-							CIDRBlocks: nil},
-						Pods: &clusterv1.NetworkRanges{
-							CIDRBlocks: nil},
-					}).
-					Build(),
-			},
-			{
-				name:      "pass with valid IPv4 CIDR ranges",
-				expectErr: false,
-				in: builder.Cluster("fooNamespace", "cluster1").
-					WithClusterNetwork(&clusterv1.ClusterNetwork{
-						Services: &clusterv1.NetworkRanges{
-							CIDRBlocks: []string{"10.10.10.10/24"}},
-						Pods: &clusterv1.NetworkRanges{
-							CIDRBlocks: []string{"10.10.10.10/24"}},
-					}).
-					Build(),
-			},
-			{
-				name:      "pass with valid IPv6 CIDR ranges",
-				expectErr: false,
-				in: builder.Cluster("fooNamespace", "cluster1").
-					WithClusterNetwork(&clusterv1.ClusterNetwork{
-						Services: &clusterv1.NetworkRanges{
-							CIDRBlocks: []string{"2004::1234:abcd:ffff:c0a8:101/64"}},
-						Pods: &clusterv1.NetworkRanges{
-							CIDRBlocks: []string{"2004::1234:abcd:ffff:c0a8:101/64"}},
-					}).
-					Build(),
-			},
-			{
-				name:      "pass with valid dualstack CIDR ranges",
-				expectErr: false,
-				in: builder.Cluster("fooNamespace", "cluster1").
-					WithClusterNetwork(&clusterv1.ClusterNetwork{
-						Services: &clusterv1.NetworkRanges{
-							CIDRBlocks: []string{"2004::1234:abcd:ffff:c0a8:101/64", "10.10.10.10/24"}},
-						Pods: &clusterv1.NetworkRanges{
-							CIDRBlocks: []string{"2004::1234:abcd:ffff:c0a8:101/64", "10.10.10.10/24"}},
-					}).
-					Build(),
-			},
-			{
-				name:      "pass if multiple CIDR ranges of IPv4 are passed",
-				expectErr: false,
-				in: builder.Cluster("fooNamespace", "cluster1").
-					WithClusterNetwork(&clusterv1.ClusterNetwork{
-						Services: &clusterv1.NetworkRanges{
-							CIDRBlocks: []string{"10.10.10.10/24", "11.11.11.11/24"}},
-					}).
-					Build(),
-			},
-			{
-				name:      "pass if multiple CIDR ranges of IPv6 are passed",
-				expectErr: false,
-				in: builder.Cluster("fooNamespace", "cluster1").
-					WithClusterNetwork(&clusterv1.ClusterNetwork{
-						Services: &clusterv1.NetworkRanges{
-							CIDRBlocks: []string{"2002::1234:abcd:ffff:c0a8:101/64", "2004::1234:abcd:ffff:c0a8:101/64"}},
-					}).
-					Build(),
-			},
-			{
-				name:      "pass if too many cidr ranges are specified in the clusterNetwork pods field",
-				expectErr: false,
-				in: builder.Cluster("fooNamespace", "cluster1").
-					WithClusterNetwork(&clusterv1.ClusterNetwork{
-						Pods: &clusterv1.NetworkRanges{
-							CIDRBlocks: []string{"10.10.10.10/24", "11.11.11.11/24", "12.12.12.12/24"}}}).
-					Build(),
-			},
-			{
-				name:      "fails if service cidr ranges are not valid",
-				expectErr: true,
-				in: builder.Cluster("fooNamespace", "cluster1").
-					WithClusterNetwork(&clusterv1.ClusterNetwork{
-						Services: &clusterv1.NetworkRanges{
-							// Invalid ranges: missing network suffix
-							CIDRBlocks: []string{"10.10.10.10", "11.11.11.11"}}}).
-					Build(),
-			},
-			{
-				name:      "fails if pod cidr ranges are not valid",
-				expectErr: true,
-				in: builder.Cluster("fooNamespace", "cluster1").
-					WithClusterNetwork(&clusterv1.ClusterNetwork{
-						Pods: &clusterv1.NetworkRanges{
-							// Invalid ranges: missing network suffix
-							CIDRBlocks: []string{"10.10.10.10", "11.11.11.11"}}}).
-					Build(),
-			},
 		}
 	)
 	for _, tt := range tests {
diff --git a/test/extension/handlers/topologymutation/handler.go b/test/extension/handlers/topologymutation/handler.go
index e2bd7e060..842b6b5f9 100644
--- a/test/extension/handlers/topologymutation/handler.go
+++ b/test/extension/handlers/topologymutation/handler.go
@@ -81,8 +81,6 @@ func (h *ExtensionHandlers) GeneratePatches(ctx context.Context, req *runtimehoo
 	// easier to read and less error prone than using unstructured or working with raw json/yaml.
 	// IMPORTANT: by unit testing this func/nested func properly, it is possible to prevent unexpected rollouts when patches are modified.
 	topologymutation.WalkTemplates(ctx, h.decoder, req, resp, func(ctx context.Context, obj runtime.Object, variables map[string]apiextensionsv1.JSON, holderRef runtimehooksv1.HolderReference) error {
-		log := ctrl.LoggerFrom(ctx)
-
 		switch obj := obj.(type) {
 		case *infrav1.DockerClusterTemplate:
 			if err := patchDockerClusterTemplate(ctx, obj, variables); err != nil {
diff --git a/test/go.mod b/test/go.mod
index 93b8a6eed..7b7461970 100644
--- a/test/go.mod
+++ b/test/go.mod
@@ -10,8 +10,8 @@ require (
 	github.com/docker/go-connections v0.4.0
 	github.com/flatcar/ignition v0.36.2
 	github.com/go-logr/logr v1.2.3
-	github.com/onsi/ginkgo/v2 v2.5.0
-	github.com/onsi/gomega v1.24.1
+	github.com/onsi/ginkgo/v2 v2.4.0
+	github.com/onsi/gomega v1.22.1
 	github.com/pkg/errors v0.9.1
 	github.com/spf13/pflag v1.0.5
 	github.com/vincent-petithory/dataurl v1.0.0
@@ -109,10 +109,10 @@ require (
 	go.uber.org/multierr v1.6.0 // indirect
 	go.uber.org/zap v1.21.0 // indirect
 	golang.org/x/crypto v0.1.0 // indirect
-	golang.org/x/net v0.2.0 // indirect
+	golang.org/x/net v0.1.0 // indirect
 	golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1 // indirect
-	golang.org/x/sys v0.2.0 // indirect
-	golang.org/x/term v0.2.0 // indirect
+	golang.org/x/sys v0.1.0 // indirect
+	golang.org/x/term v0.1.0 // indirect
 	golang.org/x/text v0.4.0 // indirect
 	golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 // indirect
 	gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect
diff --git a/test/go.sum b/test/go.sum
index 1a0c06cce..60f3bcbe6 100644
--- a/test/go.sum
+++ b/test/go.sum
@@ -436,10 +436,10 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA
 github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
 github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
 github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
-github.com/onsi/ginkgo/v2 v2.5.0 h1:TRtrvv2vdQqzkwrQ1ke6vtXf7IK34RBUJafIy1wMwls=
-github.com/onsi/ginkgo/v2 v2.5.0/go.mod h1:Luc4sArBICYCS8THh8v3i3i5CuSZO+RaQRaJoeNwomw=
-github.com/onsi/gomega v1.24.1 h1:KORJXNNTzJXzu4ScJWssJfJMnJ+2QJqhoQSRwNlze9E=
-github.com/onsi/gomega v1.24.1/go.mod h1:3AOiACssS3/MajrniINInwbfOOtfZvplPzuRSmvt1jM=
+github.com/onsi/ginkgo/v2 v2.4.0 h1:+Ig9nvqgS5OBSACXNk15PLdp0U9XPYROt9CFzVdFGIs=
+github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo=
+github.com/onsi/gomega v1.22.1 h1:pY8O4lBfsHKZHM/6nrxkhVPUznOlIu3quZcKP/M20KI=
+github.com/onsi/gomega v1.22.1/go.mod h1:x6n7VNe4hw0vkyYUM4mjIXx3JbLiPaBPNgB7PRQ1tuM=
 github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
 github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
@@ -691,8 +691,8 @@ golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su
 golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
-golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
+golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0=
+golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -802,12 +802,12 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
-golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
+golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM=
-golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
+golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw=
+golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=