Repository: kubernetes-sigs/cluster-api
Pull request #5591 (2021-11-05 09:08:04)

🌱 enable godot on top-level comments
<!-- please add a icon to the title of this PR (see https://sigs.k8s.io/cluster-api/VERSIONING.md), and delete this line and similar ones -->
<!-- the icon will be either ⚠️ (:warning:, major or breaking changes), ✨ (:sparkles:, feature additions), 🐛 (:bug:, patch and bugfixes), 📖 (:book:, documentation or proposals), or 🌱 (:seedling:, minor or other) -->

**What this PR does / why we need it**:

Goal of the PR in its current state is to discuss if we want to enable godot also on toplevel comments (default is declarations).



**Which issue(s) this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close the issue(s) when PR gets merged)*:
Fixes #


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

This pull-request has been approved by:
To complete the [pull request process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process), please assign **vincepri** after the PR has been reviewed.
You can assign the PR to them by writing `/assign @vincepri` in a comment when ready.

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":["vincepri"]} -->
fabriziopandini: This goes in the direction of more consistency across the code base, so I'm +1

Patch:
diff --git a/.golangci.yml b/.golangci.yml
index f78b81069..a66f60373 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -39,6 +39,14 @@ linters:
   - whitespace
 
 linters-settings:
+  godot:
+    #   declarations - for top level declaration comments (default);
+    #   toplevel     - for top level comments;
+    #   all          - for all comments.
+    scope: toplevel
+    exclude:
+    - '^ \+.*'
+    - '^ ANCHOR.*'
   ifshort:
     # Maximum length of variable declaration measured in number of characters, after which linter won't suggest using short syntax.
     max-decl-chars: 50
diff --git a/Tiltfile b/Tiltfile
index 98f3b89ca..944ff607d 100644
--- a/Tiltfile
+++ b/Tiltfile
@@ -4,6 +4,12 @@ load("ext://local_output", "local_output")
 
 version_settings(True, ">=0.22.2")
 
+envsubst_cmd = "./hack/tools/bin/envsubst"
+kustomize_cmd = "./hack/tools/bin/kustomize"
+yq_cmd = "./hack/tools/bin/yq"
+os_name = local_output("go env GOOS")
+os_arch = local_output("go env GOARCH")
+
 settings = {
     "deploy_cert_manager": True,
     "preload_images_for_kind": True,
@@ -18,17 +24,11 @@ settings.update(read_json(
     default = {},
 ))
 
-allow_k8s_contexts(settings.get("allowed_contexts"))
-
-envsubst_cmd = "./hack/tools/bin/envsubst"
-kustomize_cmd = "./hack/tools/bin/kustomize"
-yq_cmd = "./hack/tools/bin/yq"
-os_name = local_output("go env GOOS")
-os_arch = local_output("go env GOARCH")
-
 if settings.get("trigger_mode") == "manual":
     trigger_mode(TRIGGER_MODE_MANUAL)
 
+allow_k8s_contexts(settings.get("allowed_contexts"))
+
 default_registry(settings.get("default_registry"))
 
 always_enable_providers = ["core"]
diff --git a/api/v1alpha3/condition_consts.go b/api/v1alpha3/condition_consts.go
index da19868e3..d925550aa 100644
--- a/api/v1alpha3/condition_consts.go
+++ b/api/v1alpha3/condition_consts.go
@@ -52,7 +52,7 @@ const (
 
 // ANCHOR_END: CommonConditions
 
-// Conditions and condition Reasons for the Cluster object
+// Conditions and condition Reasons for the Cluster object.
 
 const (
 	// ControlPlaneReadyCondition reports the ready condition from the control plane object defined for this cluster.
@@ -74,7 +74,7 @@ const (
 	WaitingForControlPlaneAvailableReason = "WaitingForControlPlaneAvailable"
 )
 
-// Conditions and condition Reasons for the Machine object
+// Conditions and condition Reasons for the Machine object.
 
 const (
 	// BootstrapReadyCondition reports a summary of current status of the bootstrap object defined for this machine.
@@ -173,7 +173,7 @@ const (
 	NodeConditionsFailedReason = "NodeConditionsFailed"
 )
 
-// Conditions and condition Reasons for the MachineHealthCheck object
+// Conditions and condition Reasons for the MachineHealthCheck object.
 
 const (
 	// RemediationAllowedCondition is set on MachineHealthChecks to show the status of whether the MachineHealthCheck is
diff --git a/api/v1alpha4/condition_consts.go b/api/v1alpha4/condition_consts.go
index f858910e0..38450f9dc 100644
--- a/api/v1alpha4/condition_consts.go
+++ b/api/v1alpha4/condition_consts.go
@@ -55,7 +55,7 @@ const (
 
 // ANCHOR_END: CommonConditions
 
-// Conditions and condition Reasons for the Cluster object
+// Conditions and condition Reasons for the Cluster object.
 
 const (
 	// ControlPlaneInitializedCondition reports if the cluster's control plane has been initialized such that the
@@ -91,7 +91,7 @@ const (
 	WaitingForControlPlaneAvailableReason = "WaitingForControlPlaneAvailable"
 )
 
-// Conditions and condition Reasons for the Machine object
+// Conditions and condition Reasons for the Machine object.
 
 const (
 	// BootstrapReadyCondition reports a summary of current status of the bootstrap object defined for this machine.
@@ -196,7 +196,7 @@ const (
 	NodeConditionsFailedReason = "NodeConditionsFailed"
 )
 
-// Conditions and condition Reasons for the MachineHealthCheck object
+// Conditions and condition Reasons for the MachineHealthCheck object.
 
 const (
 	// RemediationAllowedCondition is set on MachineHealthChecks to show the status of whether the MachineHealthCheck is
@@ -208,7 +208,7 @@ const (
 	TooManyUnhealthyReason = "TooManyUnhealthy"
 )
 
-// Conditions and condition Reasons for  MachineDeployments
+// Conditions and condition Reasons for  MachineDeployments.
 
 const (
 	// MachineDeploymentAvailableCondition means the MachineDeployment is available, that is, at least the minimum available
@@ -219,7 +219,7 @@ const (
 	WaitingForAvailableMachinesReason = "WaitingForAvailableMachines"
 )
 
-// Conditions and condition Reasons for  MachineSets
+// Conditions and condition Reasons for  MachineSets.
 
 const (
 	// MachinesCreatedCondition documents that the machines controlled by the MachineSet are created.
diff --git a/api/v1beta1/condition_consts.go b/api/v1beta1/condition_consts.go
index c056e844b..7c81b0e4c 100644
--- a/api/v1beta1/condition_consts.go
+++ b/api/v1beta1/condition_consts.go
@@ -55,7 +55,7 @@ const (
 
 // ANCHOR_END: CommonConditions
 
-// Conditions and condition Reasons for the Cluster object
+// Conditions and condition Reasons for the Cluster object.
 
 const (
 	// ControlPlaneInitializedCondition reports if the cluster's control plane has been initialized such that the
@@ -91,7 +91,7 @@ const (
 	WaitingForControlPlaneAvailableReason = "WaitingForControlPlaneAvailable"
 )
 
-// Conditions and condition Reasons for the Machine object
+// Conditions and condition Reasons for the Machine object.
 
 const (
 	// BootstrapReadyCondition reports a summary of current status of the bootstrap object defined for this machine.
@@ -196,7 +196,7 @@ const (
 	NodeConditionsFailedReason = "NodeConditionsFailed"
 )
 
-// Conditions and condition Reasons for the MachineHealthCheck object
+// Conditions and condition Reasons for the MachineHealthCheck object.
 
 const (
 	// RemediationAllowedCondition is set on MachineHealthChecks to show the status of whether the MachineHealthCheck is
@@ -208,7 +208,7 @@ const (
 	TooManyUnhealthyReason = "TooManyUnhealthy"
 )
 
-// Conditions and condition Reasons for  MachineDeployments
+// Conditions and condition Reasons for  MachineDeployments.
 
 const (
 	// MachineDeploymentAvailableCondition means the MachineDeployment is available, that is, at least the minimum available
@@ -219,7 +219,7 @@ const (
 	WaitingForAvailableMachinesReason = "WaitingForAvailableMachines"
 )
 
-// Conditions and condition Reasons for  MachineSets
+// Conditions and condition Reasons for  MachineSets.
 
 const (
 	// MachinesCreatedCondition documents that the machines controlled by the MachineSet are created.
diff --git a/api/v1beta1/index/index.go b/api/v1beta1/index/index.go
index fdbed5663..4bbbdd698 100644
--- a/api/v1beta1/index/index.go
+++ b/api/v1beta1/index/index.go
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-// Package index provides indexes for the api
+// Package index provides indexes for the api.
 package index
 
 import (
diff --git a/bootstrap/kubeadm/api/v1alpha3/condition_consts.go b/bootstrap/kubeadm/api/v1alpha3/condition_consts.go
index 345ab71e7..7da7d983a 100644
--- a/bootstrap/kubeadm/api/v1alpha3/condition_consts.go
+++ b/bootstrap/kubeadm/api/v1alpha3/condition_consts.go
@@ -18,7 +18,7 @@ package v1alpha3
 
 import clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha3"
 
-// Conditions and condition Reasons for the KubeadmConfig object
+// Conditions and condition Reasons for the KubeadmConfig object.
 
 const (
 	// DataSecretAvailableCondition documents the status of the bootstrap secret generation process.
diff --git a/bootstrap/kubeadm/api/v1alpha4/condition_consts.go b/bootstrap/kubeadm/api/v1alpha4/condition_consts.go
index 6ee694b4c..4a997b4b5 100644
--- a/bootstrap/kubeadm/api/v1alpha4/condition_consts.go
+++ b/bootstrap/kubeadm/api/v1alpha4/condition_consts.go
@@ -18,7 +18,7 @@ package v1alpha4
 
 import clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha4"
 
-// Conditions and condition Reasons for the KubeadmConfig object
+// Conditions and condition Reasons for the KubeadmConfig object.
 
 const (
 	// DataSecretAvailableCondition documents the status of the bootstrap secret generation process.
diff --git a/bootstrap/kubeadm/api/v1beta1/condition_consts.go b/bootstrap/kubeadm/api/v1beta1/condition_consts.go
index d5c4bf095..3691ed1ca 100644
--- a/bootstrap/kubeadm/api/v1beta1/condition_consts.go
+++ b/bootstrap/kubeadm/api/v1beta1/condition_consts.go
@@ -18,7 +18,7 @@ package v1beta1
 
 import clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
 
-// Conditions and condition Reasons for the KubeadmConfig object
+// Conditions and condition Reasons for the KubeadmConfig object.
 
 const (
 	// DataSecretAvailableCondition documents the status of the bootstrap secret generation process.
diff --git a/cmd/clusterctl/client/cluster/mover.go b/cmd/clusterctl/client/cluster/mover.go
index 4966c120e..6352788e2 100644
--- a/cmd/clusterctl/client/cluster/mover.go
+++ b/cmd/clusterctl/client/cluster/mover.go
@@ -128,9 +128,6 @@ func (o *objectMover) Restore(toCluster Client, directory string) error {
 	// by a naming convention (without any explicit OwnerReference).
 	objectGraph.setSoftOwnership()
 
-	// Completes the graph by setting for each node the list of tenants the node belongs to.
-	objectGraph.setTenants()
-
 	// Check whether nodes are not included in GVK considered for restore.
 	objectGraph.checkVirtualNode()
 
diff --git a/cmd/clusterctl/client/config/providers_client.go b/cmd/clusterctl/client/config/providers_client.go
index b2ab25413..35f241548 100644
--- a/cmd/clusterctl/client/config/providers_client.go
+++ b/cmd/clusterctl/client/config/providers_client.go
@@ -35,7 +35,6 @@ const (
 const (
 	AWSProviderName       = "aws"
 	AzureProviderName     = "azure"
-	BYOHProviderName      = "byoh"
 	DockerProviderName    = "docker"
 	DOProviderName        = "digitalocean"
 	GCPProviderName       = "gcp"
@@ -170,11 +169,6 @@ func (p *providersClient) defaults() []Provider {
 			url:          "https://github.com/spectrocloud/cluster-api-provider-maas/releases/latest/infrastructure-components.yaml",
 			providerType: clusterctlv1.InfrastructureProviderType,
 		},
-		&provider{
-			name:         BYOHProviderName,
-			url:          "https://github.com/vmware-tanzu/cluster-api-provider-bringyourownhost/releases/latest/infrastructure-components.yaml",
-			providerType: clusterctlv1.InfrastructureProviderType,
-		},
 
 		// Bootstrap providers
 		&provider{
diff --git a/controllers/topology/internal/mergepatch/mergepatch.go b/controllers/topology/internal/mergepatch/mergepatch.go
index 75fe758d1..2f1723813 100644
--- a/controllers/topology/internal/mergepatch/mergepatch.go
+++ b/controllers/topology/internal/mergepatch/mergepatch.go
@@ -21,7 +21,7 @@ import (
 	"context"
 	"encoding/json"
 
-	jsonpatch "github.com/evanphx/json-patch/v5"
+	jsonpatch "github.com/evanphx/json-patch"
 	"github.com/pkg/errors"
 	"k8s.io/apimachinery/pkg/types"
 	"sigs.k8s.io/cluster-api/controllers/topology/internal/contract"
diff --git a/controlplane/kubeadm/api/v1alpha3/condition_consts.go b/controlplane/kubeadm/api/v1alpha3/condition_consts.go
index 5178960bc..da72a6472 100644
--- a/controlplane/kubeadm/api/v1alpha3/condition_consts.go
+++ b/controlplane/kubeadm/api/v1alpha3/condition_consts.go
@@ -18,7 +18,7 @@ package v1alpha3
 
 import clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha3"
 
-// Conditions and condition Reasons for the KubeadmControlPlane object
+// Conditions and condition Reasons for the KubeadmControlPlane object.
 
 const (
 	// MachinesReadyCondition reports an aggregate of current status of the machines controlled by the KubeadmControlPlane.
diff --git a/controlplane/kubeadm/api/v1alpha4/condition_consts.go b/controlplane/kubeadm/api/v1alpha4/condition_consts.go
index 5bfe23541..25329e9ed 100644
--- a/controlplane/kubeadm/api/v1alpha4/condition_consts.go
+++ b/controlplane/kubeadm/api/v1alpha4/condition_consts.go
@@ -18,7 +18,7 @@ package v1alpha4
 
 import clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha4"
 
-// Conditions and condition Reasons for the KubeadmControlPlane object
+// Conditions and condition Reasons for the KubeadmControlPlane object.
 
 const (
 	// MachinesReadyCondition reports an aggregate of current status of the machines controlled by the KubeadmControlPlane.
diff --git a/controlplane/kubeadm/api/v1beta1/condition_consts.go b/controlplane/kubeadm/api/v1beta1/condition_consts.go
index be78c7670..18dc47da2 100644
--- a/controlplane/kubeadm/api/v1beta1/condition_consts.go
+++ b/controlplane/kubeadm/api/v1beta1/condition_consts.go
@@ -18,7 +18,7 @@ package v1beta1
 
 import clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
 
-// Conditions and condition Reasons for the KubeadmControlPlane object
+// Conditions and condition Reasons for the KubeadmControlPlane object.
 
 const (
 	// MachinesReadyCondition reports an aggregate of current status of the machines controlled by the KubeadmControlPlane.
diff --git a/controlplane/kubeadm/api/v1beta1/kubeadm_control_plane_webhook.go b/controlplane/kubeadm/api/v1beta1/kubeadm_control_plane_webhook.go
index 0791f0070..63338bd41 100644
--- a/controlplane/kubeadm/api/v1beta1/kubeadm_control_plane_webhook.go
+++ b/controlplane/kubeadm/api/v1beta1/kubeadm_control_plane_webhook.go
@@ -23,7 +23,7 @@ import (
 
 	"github.com/blang/semver"
 	"github.com/coredns/corefile-migration/migration"
-	jsonpatch "github.com/evanphx/json-patch/v5"
+	jsonpatch "github.com/evanphx/json-patch"
 	"github.com/pkg/errors"
 	apierrors "k8s.io/apimachinery/pkg/api/errors"
 	"k8s.io/apimachinery/pkg/runtime"
diff --git a/controlplane/kubeadm/internal/etcd/etcd.go b/controlplane/kubeadm/internal/etcd/etcd.go
index 9bdfc4acd..00b1a7fae 100644
--- a/controlplane/kubeadm/internal/etcd/etcd.go
+++ b/controlplane/kubeadm/internal/etcd/etcd.go
@@ -86,7 +86,7 @@ var AlarmTypeName = map[AlarmType]string{
 	AlarmCorrupt: "CORRUPT",
 }
 
-// Adapted from kubeadm
+// Adapted from kubeadm.
 
 // Member struct defines an etcd member; it is used to avoid spreading
 // github.com/coreos/etcd dependencies.
diff --git a/docs/book/src/clusterctl/provider-contract.md b/docs/book/src/clusterctl/provider-contract.md
index ba006dbe0..a74b82847 100644
--- a/docs/book/src/clusterctl/provider-contract.md
+++ b/docs/book/src/clusterctl/provider-contract.md
@@ -185,7 +185,6 @@ providers.
 |CACPK         | cluster.x-k8s.io/provider=control-plane-kubeadm        |
 |CACPN         | cluster.x-k8s.io/provider=control-plane-nested        |
 |CAPA          | cluster.x-k8s.io/provider=infrastructure-aws           |
-|CAPB          | cluster.x-k8s.io/provider=infrastructure-byoh          |
 |CAPV          | cluster.x-k8s.io/provider=infrastructure-vsphere       |
 |CAPD          | cluster.x-k8s.io/provider=infrastructure-docker        |
 |CAPM3         | cluster.x-k8s.io/provider=infrastructure-metal3        |
diff --git a/docs/book/src/reference/providers.md b/docs/book/src/reference/providers.md
index 364df7db0..06866f015 100644
--- a/docs/book/src/reference/providers.md
+++ b/docs/book/src/reference/providers.md
@@ -16,7 +16,6 @@ updated info about which API version they are supporting.
 - [Azure](https://github.com/kubernetes-sigs/cluster-api-provider-azure)
 - [Azure Stack HCI](https://github.com/microsoft/cluster-api-provider-azurestackhci)
 - [Baidu Cloud](https://github.com/baidu/cluster-api-provider-baiducloud)
-- [BYOH](https://github.com/vmware-tanzu/cluster-api-provider-bringyourownhost)
 - [Metal3](https://github.com/metal3-io/cluster-api-provider-metal3)
 - [DigitalOcean](https://github.com/kubernetes-sigs/cluster-api-provider-digitalocean)
 - [Exoscale](https://github.com/exoscale/cluster-api-provider-exoscale)
diff --git a/exp/addons/api/v1alpha3/condition_consts.go b/exp/addons/api/v1alpha3/condition_consts.go
index e61fba5ed..fe9115d61 100644
--- a/exp/addons/api/v1alpha3/condition_consts.go
+++ b/exp/addons/api/v1alpha3/condition_consts.go
@@ -18,7 +18,7 @@ package v1alpha3
 
 import clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha3"
 
-// Conditions and condition Reasons for the ClusterResourceSet object
+// Conditions and condition Reasons for the ClusterResourceSet object.
 
 const (
 	// ResourcesAppliedCondition documents that all resources in the ClusterResourceSet object are applied to
diff --git a/exp/addons/api/v1alpha4/condition_consts.go b/exp/addons/api/v1alpha4/condition_consts.go
index f4961f2bb..97dfd10b3 100644
--- a/exp/addons/api/v1alpha4/condition_consts.go
+++ b/exp/addons/api/v1alpha4/condition_consts.go
@@ -18,7 +18,7 @@ package v1alpha4
 
 import clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
 
-// Conditions and condition Reasons for the ClusterResourceSet object
+// Conditions and condition Reasons for the ClusterResourceSet object.
 
 const (
 	// ResourcesAppliedCondition documents that all resources in the ClusterResourceSet object are applied to
diff --git a/exp/addons/api/v1beta1/condition_consts.go b/exp/addons/api/v1beta1/condition_consts.go
index c354fe702..1672c9d5f 100644
--- a/exp/addons/api/v1beta1/condition_consts.go
+++ b/exp/addons/api/v1beta1/condition_consts.go
@@ -18,7 +18,7 @@ package v1beta1
 
 import clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
 
-// Conditions and condition Reasons for the ClusterResourceSet object
+// Conditions and condition Reasons for the ClusterResourceSet object.
 
 const (
 	// ResourcesAppliedCondition documents that all resources in the ClusterResourceSet object are applied to
diff --git a/exp/api/v1alpha3/condition_consts.go b/exp/api/v1alpha3/condition_consts.go
index 8a9d2d25d..95e7df26a 100644
--- a/exp/api/v1alpha3/condition_consts.go
+++ b/exp/api/v1alpha3/condition_consts.go
@@ -18,7 +18,7 @@ package v1alpha3
 
 import clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha3"
 
-// Conditions and condition Reasons for the MachinePool object
+// Conditions and condition Reasons for the MachinePool object.
 
 const (
 	// ReplicasReadyCondition reports an aggregate of current status of the replicas controlled by the MachinePool.
diff --git a/exp/api/v1alpha4/condition_consts.go b/exp/api/v1alpha4/condition_consts.go
index 7da5038f9..e8b32a5a7 100644
--- a/exp/api/v1alpha4/condition_consts.go
+++ b/exp/api/v1alpha4/condition_consts.go
@@ -18,7 +18,7 @@ package v1alpha4
 
 import clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha4"
 
-// Conditions and condition Reasons for the MachinePool object
+// Conditions and condition Reasons for the MachinePool object.
 
 const (
 	// ReplicasReadyCondition reports an aggregate of current status of the replicas controlled by the MachinePool.
diff --git a/exp/api/v1beta1/condition_consts.go b/exp/api/v1beta1/condition_consts.go
index ca6f090f5..18e29600a 100644
--- a/exp/api/v1beta1/condition_consts.go
+++ b/exp/api/v1beta1/condition_consts.go
@@ -18,7 +18,7 @@ package v1beta1
 
 import clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
 
-// Conditions and condition Reasons for the MachinePool object
+// Conditions and condition Reasons for the MachinePool object.
 
 const (
 	// ReplicasReadyCondition reports an aggregate of current status of the replicas controlled by the MachinePool.
diff --git a/go.mod b/go.mod
index 0ff9fee9b..8709e0227 100644
--- a/go.mod
+++ b/go.mod
@@ -9,7 +9,7 @@ require (
 	github.com/davecgh/go-spew v1.1.1
 	github.com/docker/distribution v2.7.1+incompatible
 	github.com/drone/envsubst/v2 v2.0.0-20210615175204-7bf45dbf5372
-	github.com/evanphx/json-patch/v5 v5.6.0
+	github.com/evanphx/json-patch v4.11.0+incompatible
 	github.com/fatih/color v1.13.0
 	github.com/go-logr/logr v0.4.0
 	github.com/gobuffalo/flect v0.2.3
diff --git a/go.sum b/go.sum
index dd0cc2a92..f795c572b 100644
--- a/go.sum
+++ b/go.sum
@@ -163,8 +163,6 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
 github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ=
 github.com/evanphx/json-patch v4.11.0+incompatible h1:glyUF9yIYtMHzn8xaKw5rMhdWcwsYV8dZHIq5567/xs=
 github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
-github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww=
-github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4=
 github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM=
 github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4=
 github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
diff --git a/internal/matchers/doc.go b/internal/matchers/doc.go
index 43be560c1..577b8e109 100644
--- a/internal/matchers/doc.go
+++ b/internal/matchers/doc.go
@@ -14,5 +14,5 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-// Package matchers contains the functionality to match two Kubernetes runtime.Objects
+// Package matchers contains the functionality to match two Kubernetes runtime.Objects.
 package matchers
diff --git a/internal/matchers/matchers.go b/internal/matchers/matchers.go
index 6d17630c1..9f0580c0a 100644
--- a/internal/matchers/matchers.go
+++ b/internal/matchers/matchers.go
@@ -22,7 +22,7 @@ import (
 	"fmt"
 	"reflect"
 
-	jsonpatch "github.com/evanphx/json-patch/v5"
+	jsonpatch "github.com/evanphx/json-patch"
 	"github.com/onsi/gomega/format"
 	"github.com/pkg/errors"
 	"k8s.io/apimachinery/pkg/runtime"
diff --git a/webhooks/cluster.go b/webhooks/cluster.go
index 673b97f03..fdfa64a05 100644
--- a/webhooks/cluster.go
+++ b/webhooks/cluster.go
@@ -19,26 +19,20 @@ package webhooks
 import (
 	"context"
 	"fmt"
-	"strings"
 
-	"github.com/blang/semver"
 	apierrors "k8s.io/apimachinery/pkg/api/errors"
 	"k8s.io/apimachinery/pkg/runtime"
-	"k8s.io/apimachinery/pkg/util/sets"
-	"k8s.io/apimachinery/pkg/util/validation/field"
 	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
-	"sigs.k8s.io/cluster-api/feature"
-	"sigs.k8s.io/cluster-api/util/version"
 	ctrl "sigs.k8s.io/controller-runtime"
 	"sigs.k8s.io/controller-runtime/pkg/webhook"
 )
 
 // SetupWebhookWithManager sets up Cluster webhooks.
-func (webhook *Cluster) SetupWebhookWithManager(mgr ctrl.Manager) error {
+func (c *Cluster) SetupWebhookWithManager(mgr ctrl.Manager) error {
 	return ctrl.NewWebhookManagedBy(mgr).
 		For(&clusterv1.Cluster{}).
-		WithDefaulter(webhook).
-		WithValidator(webhook).
+		WithDefaulter(c).
+		WithValidator(c).
 		Complete()
 }
 
@@ -52,42 +46,39 @@ var _ webhook.CustomDefaulter = &Cluster{}
 var _ webhook.CustomValidator = &Cluster{}
 
 // Default satisfies the defaulting webhook interface.
-func (webhook *Cluster) Default(_ context.Context, obj runtime.Object) error {
+func (c *Cluster) Default(_ context.Context, obj runtime.Object) error {
 	cluster, ok := obj.(*clusterv1.Cluster)
 	if !ok {
 		return apierrors.NewBadRequest(fmt.Sprintf("expected a Cluster but got a %T", obj))
 	}
 
-	if cluster.Spec.InfrastructureRef != nil && len(cluster.Spec.InfrastructureRef.Namespace) == 0 {
-		cluster.Spec.InfrastructureRef.Namespace = cluster.Namespace
-	}
-
-	if cluster.Spec.ControlPlaneRef != nil && len(cluster.Spec.ControlPlaneRef.Namespace) == 0 {
-		cluster.Spec.ControlPlaneRef.Namespace = cluster.Namespace
-	}
+	// Note: The code in Default is intentionally not duplicated to avoid that we accidentally
+	// implement new checks in the API package and forget to duplicate them to the webhook package.
+	// The idea is to add new defaulting which requires a reader in the webhook package.
+	// When we drop the method in the API package we must inline it here.
+	cluster.Default()
 
-	// If the Cluster uses a managed topology
-	if cluster.Spec.Topology != nil {
-		// tolerate version strings without a "v" prefix: prepend it if it's not there
-		if !strings.HasPrefix(cluster.Spec.Topology.Version, "v") {
-			cluster.Spec.Topology.Version = "v" + cluster.Spec.Topology.Version
-		}
-	}
 	return nil
 }
 
-// ValidateCreate implements webhook.CustomValidator so a webhook will be registered for the type.
-func (webhook *Cluster) ValidateCreate(_ context.Context, obj runtime.Object) error {
+// ValidateCreate implements webhook.Validator so a webhook will be registered for the type.
+func (c *Cluster) ValidateCreate(_ context.Context, obj runtime.Object) error {
 	cluster, ok := obj.(*clusterv1.Cluster)
 	if !ok {
 		return apierrors.NewBadRequest(fmt.Sprintf("expected a Cluster but got a %T", obj))
 	}
-	return webhook.validate(nil, cluster)
+
+	// Note: The code in ValidateCreate is intentionally not duplicated to avoid that we accidentally
+	// implement new checks in the API package and forget to duplicate them to the webhook package.
+	// The idea is to add new validation which requires a reader and Cluster variable/patch validation
+	// in the webhook package.
+	// When we drop the method in the API package we must inline it here.
+	return cluster.ValidateCreate()
 }
 
-// ValidateUpdate implements webhook.CustomValidator so a webhook will be registered for the type.
-func (webhook *Cluster) ValidateUpdate(_ context.Context, oldObj, newObj runtime.Object) error {
-	newCluster, ok := newObj.(*clusterv1.Cluster)
+// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type.
+func (c *Cluster) ValidateUpdate(_ context.Context, oldObj, newObj runtime.Object) error {
+	cluster, ok := newObj.(*clusterv1.Cluster)
 	if !ok {
 		return apierrors.NewBadRequest(fmt.Sprintf("expected a Cluster but got a %T", newObj))
 	}
@@ -95,154 +86,20 @@ func (webhook *Cluster) ValidateUpdate(_ context.Context, oldObj, newObj runtime
 	if !ok {
 		return apierrors.NewBadRequest(fmt.Sprintf("expected a Cluster but got a %T", oldObj))
 	}
-	return webhook.validate(oldCluster, newCluster)
-}
-
-// ValidateDelete implements webhook.CustomValidator so a webhook will be registered for the type.
-func (webhook *Cluster) ValidateDelete(_ context.Context, obj runtime.Object) error {
-	return nil
-}
-
-func (webhook *Cluster) validate(old, new *clusterv1.Cluster) error {
-	var allErrs field.ErrorList
-	if new.Spec.InfrastructureRef != nil && new.Spec.InfrastructureRef.Namespace != new.Namespace {
-		allErrs = append(
-			allErrs,
-			field.Invalid(
-				field.NewPath("spec", "infrastructureRef", "namespace"),
-				new.Spec.InfrastructureRef.Namespace,
-				"must match metadata.namespace",
-			),
-		)
-	}
-
-	if new.Spec.ControlPlaneRef != nil && new.Spec.ControlPlaneRef.Namespace != new.Namespace {
-		allErrs = append(
-			allErrs,
-			field.Invalid(
-				field.NewPath("spec", "controlPlaneRef", "namespace"),
-				new.Spec.ControlPlaneRef.Namespace,
-				"must match metadata.namespace",
-			),
-		)
-	}
-
-	// Validate the managed topology, if defined.
-	if new.Spec.Topology != nil {
-		if topologyErrs := webhook.validateTopology(old, new); len(topologyErrs) > 0 {
-			allErrs = append(allErrs, topologyErrs...)
-		}
-	}
 
-	if len(allErrs) == 0 {
-		return nil
-	}
-	return apierrors.NewInvalid(clusterv1.GroupVersion.WithKind("Cluster").GroupKind(), new.Name, allErrs)
+	// Note: The code in ValidateUpdate is intentionally not duplicated to avoid that we accidentally
+	// implement new checks in the API package and forget to duplicate them to the webhook package.
+	// The idea is to add new validation which requires a reader and Cluster variable/patch validation
+	// in the webhook package.
+	// When we drop the method in the API package we must inline it here.
+	return cluster.ValidateUpdate(oldCluster)
 }
 
-func (webhook *Cluster) validateTopology(old, new *clusterv1.Cluster) field.ErrorList {
-	// NOTE: ClusterClass and managed topologies are behind ClusterTopology feature gate flag; the web hook
-	// must prevent the usage of Cluster.Topology in case the feature flag is disabled.
-	if !feature.Gates.Enabled(feature.ClusterTopology) {
-		return field.ErrorList{
-			field.Forbidden(
-				field.NewPath("spec", "topology"),
-				"can be set only if the ClusterTopology feature flag is enabled",
-			),
-		}
-	}
-
-	var allErrs field.ErrorList
-
-	// class should be defined.
-	if len(new.Spec.Topology.Class) == 0 {
-		allErrs = append(
-			allErrs,
-			field.Invalid(
-				field.NewPath("spec", "topology", "class"),
-				new.Spec.Topology.Class,
-				"cannot be empty",
-			),
-		)
-	}
-
-	// version should be valid.
-	if !version.KubeSemver.MatchString(new.Spec.Topology.Version) {
-		allErrs = append(
-			allErrs,
-			field.Invalid(
-				field.NewPath("spec", "topology", "version"),
-				new.Spec.Topology.Version,
-				"must be a valid semantic version",
-			),
-		)
-	}
-
-	// MachineDeployment names must be unique.
-	if new.Spec.Topology.Workers != nil {
-		names := sets.String{}
-		for _, md := range new.Spec.Topology.Workers.MachineDeployments {
-			if names.Has(md.Name) {
-				allErrs = append(allErrs,
-					field.Invalid(
-						field.NewPath("spec", "topology", "workers", "machineDeployments"),
-						md,
-						fmt.Sprintf("MachineDeployment names should be unique. MachineDeployment with name %q is defined more than once.", md.Name),
-					),
-				)
-			}
-			names.Insert(md.Name)
-		}
-	}
-
-	if old != nil { // On update
-		// Class could not be mutated.
-		if new.Spec.Topology.Class != old.Spec.Topology.Class {
-			allErrs = append(
-				allErrs,
-				field.Invalid(
-					field.NewPath("spec", "topology", "class"),
-					new.Spec.Topology.Class,
-					"class cannot be changed",
-				),
-			)
-		}
-
-		// Version could only be increased.
-		inVersion, err := semver.ParseTolerant(new.Spec.Topology.Version)
-		if err != nil {
-			allErrs = append(
-				allErrs,
-				field.Invalid(
-					field.NewPath("spec", "topology", "version"),
-					new.Spec.Topology.Version,
-					"is not a valid version",
-				),
-			)
-		}
-		oldVersion, err := semver.ParseTolerant(old.Spec.Topology.Version)
-		if err != nil {
-			// NOTE: this should never happen. Nevertheless, handling this for extra caution.
-			allErrs = append(
-				allErrs,
-				field.Invalid(
-					field.NewPath("spec", "topology", "version"),
-					new.Spec.Topology.Class,
-					"cannot be compared with the old version",
-				),
-			)
-		}
-		if inVersion.NE(semver.Version{}) && oldVersion.NE(semver.Version{}) && version.Compare(inVersion, oldVersion, version.WithBuildTags()) == -1 {
-			allErrs = append(
-				allErrs,
-				field.Invalid(
-					field.NewPath("spec", "topology", "version"),
-					new.Spec.Topology.Version,
-					"cannot be decreased",
-				),
-			)
-		}
+// ValidateDelete implements webhook.Validator so a webhook will be registered for the type.
+func (c *Cluster) ValidateDelete(ctx context.Context, obj runtime.Object) error {
+	cluster, ok := obj.(*clusterv1.Cluster)
+	if !ok {
+		return apierrors.NewBadRequest(fmt.Sprintf("expected a Cluster but got a %T", obj))
 	}
-
-	return allErrs
+	return cluster.ValidateDelete()
 }
diff --git a/webhooks/clusterclass.go b/webhooks/clusterclass.go
index 046663b9c..f10dd5508 100644
--- a/webhooks/clusterclass.go
+++ b/webhooks/clusterclass.go
@@ -19,25 +19,19 @@ package webhooks
 import (
 	"context"
 	"fmt"
-	"strings"
 
-	corev1 "k8s.io/api/core/v1"
 	apierrors "k8s.io/apimachinery/pkg/api/errors"
 	"k8s.io/apimachinery/pkg/runtime"
-	"k8s.io/apimachinery/pkg/runtime/schema"
-	"k8s.io/apimachinery/pkg/util/sets"
-	"k8s.io/apimachinery/pkg/util/validation/field"
 	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
-	"sigs.k8s.io/cluster-api/feature"
 	ctrl "sigs.k8s.io/controller-runtime"
 	"sigs.k8s.io/controller-runtime/pkg/webhook"
 )
 
-func (webhook *ClusterClass) SetupWebhookWithManager(mgr ctrl.Manager) error {
+func (c *ClusterClass) SetupWebhookWithManager(mgr ctrl.Manager) error {
 	return ctrl.NewWebhookManagedBy(mgr).
 		For(&clusterv1.ClusterClass{}).
-		WithDefaulter(webhook).
-		WithValidator(webhook).
+		WithDefaulter(c).
+		WithValidator(c).
 		Complete()
 }
 
@@ -51,44 +45,39 @@ var _ webhook.CustomDefaulter = &ClusterClass{}
 var _ webhook.CustomValidator = &ClusterClass{}
 
 // Default implements defaulting for ClusterClass create and update.
-func (webhook *ClusterClass) Default(_ context.Context, obj runtime.Object) error {
-	in, ok := obj.(*clusterv1.ClusterClass)
+func (c *ClusterClass) Default(_ context.Context, obj runtime.Object) error {
+	clusterClass, ok := obj.(*clusterv1.ClusterClass)
 	if !ok {
 		return apierrors.NewBadRequest(fmt.Sprintf("expected a ClusterClass but got a %T", obj))
 	}
-	// Default all namespaces in the references to the object namespace.
-	defaultNamespace(in.Spec.Infrastructure.Ref, in.Namespace)
-	defaultNamespace(in.Spec.ControlPlane.Ref, in.Namespace)
 
-	if in.Spec.ControlPlane.MachineInfrastructure != nil {
-		defaultNamespace(in.Spec.ControlPlane.MachineInfrastructure.Ref, in.Namespace)
-	}
+	// Note: The code in Default is intentionally not duplicated to avoid that we accidentally
+	// implement new checks in the API package and forget to duplicate them to the webhook package.
+	// The idea is to add new defaulting which requires a reader in the webhook package.
+	// When we drop the method in the API package we must inline it here.
+	clusterClass.Default()
 
-	for i := range in.Spec.Workers.MachineDeployments {
-		defaultNamespace(in.Spec.Workers.MachineDeployments[i].Template.Bootstrap.Ref, in.Namespace)
-		defaultNamespace(in.Spec.Workers.MachineDeployments[i].Template.Infrastructure.Ref, in.Namespace)
-	}
 	return nil
 }
 
-func defaultNamespace(ref *corev1.ObjectReference, namespace string) {
-	if ref != nil && len(ref.Namespace) == 0 {
-		ref.Namespace = namespace
-	}
-}
-
 // ValidateCreate implements validation for ClusterClass create.
-func (webhook *ClusterClass) ValidateCreate(_ context.Context, obj runtime.Object) error {
-	in, ok := obj.(*clusterv1.ClusterClass)
+func (c *ClusterClass) ValidateCreate(_ context.Context, obj runtime.Object) error {
+	clusterClass, ok := obj.(*clusterv1.ClusterClass)
 	if !ok {
 		return apierrors.NewBadRequest(fmt.Sprintf("expected a ClusterClass but got a %T", obj))
 	}
-	return webhook.validate(nil, in)
+
+	// Note: The code in ValidateCreate is intentionally not duplicated to avoid that we accidentally
+	// implement new checks in the API package and forget to duplicate them to the webhook package.
+	// The idea is to add new validation which requires a reader and ClusterClass variable/patch validation
+	// in the webhook package.
+	// When we drop the method in the API package we must inline it here.
+	return clusterClass.ValidateCreate()
 }
 
 // ValidateUpdate implements validation for ClusterClass update.
-func (webhook *ClusterClass) ValidateUpdate(_ context.Context, oldObj, newObj runtime.Object) error {
-	newClusterClass, ok := newObj.(*clusterv1.ClusterClass)
+func (c *ClusterClass) ValidateUpdate(_ context.Context, oldObj, newObj runtime.Object) error {
+	clusterClass, ok := newObj.(*clusterv1.ClusterClass)
 	if !ok {
 		return apierrors.NewBadRequest(fmt.Sprintf("expected a ClusterClass but got a %T", newObj))
 	}
@@ -96,286 +85,21 @@ func (webhook *ClusterClass) ValidateUpdate(_ context.Context, oldObj, newObj ru
 	if !ok {
 		return apierrors.NewBadRequest(fmt.Sprintf("expected a ClusterClass but got a %T", oldObj))
 	}
-	return webhook.validate(oldClusterClass, newClusterClass)
-}
-
-// ValidateDelete implements validation for ClusterClass delete.
-func (webhook *ClusterClass) ValidateDelete(ctx context.Context, obj runtime.Object) error {
-	return nil
-}
-
-func (webhook *ClusterClass) validate(old, in *clusterv1.ClusterClass) error {
-	// NOTE: ClusterClass and managed topologies are behind ClusterTopology feature gate flag; the web hook
-	// must prevent creating in objects in case the feature flag is disabled.
-	if !feature.Gates.Enabled(feature.ClusterTopology) {
-		return field.Forbidden(
-			field.NewPath("spec"),
-			"can be set only if the ClusterTopology feature flag is enabled",
-		)
-	}
-
-	var allErrs field.ErrorList
-
-	// Ensure all references are valid.
-	allErrs = append(allErrs, webhook.validateAllRefs(in)...)
-
-	// Ensure all MachineDeployment classes are unique.
-	allErrs = append(allErrs, webhook.validateUniqueClasses(in.Spec.Workers, field.NewPath("spec", "workers"))...)
-
-	// Ensure spec changes are compatible.
-	allErrs = append(allErrs, webhook.validateCompatibleSpecChanges(old, in)...)
-
-	if len(allErrs) > 0 {
-		return apierrors.NewInvalid(clusterv1.GroupVersion.WithKind("ClusterClass").GroupKind(), in.Name, allErrs)
-	}
-	return nil
-}
-
-func (webhook *ClusterClass) validateAllRefs(in *clusterv1.ClusterClass) field.ErrorList {
-	var allErrs field.ErrorList
-
-	allErrs = append(allErrs, webhook.validateTemplate(&in.Spec.Infrastructure, in.Namespace, field.NewPath("spec", "infrastructure"))...)
-	allErrs = append(allErrs, webhook.validateTemplate(&in.Spec.ControlPlane.LocalObjectTemplate, in.Namespace, field.NewPath("spec", "controlPlane"))...)
-	if in.Spec.ControlPlane.MachineInfrastructure != nil {
-		allErrs = append(allErrs, webhook.validateTemplate(in.Spec.ControlPlane.MachineInfrastructure, in.Namespace, field.NewPath("spec", "controlPlane", "machineInfrastructure"))...)
-	}
-
-	for i, class := range in.Spec.Workers.MachineDeployments {
-		allErrs = append(allErrs, webhook.validateTemplate(&class.Template.Bootstrap, in.Namespace, field.NewPath("spec", "workers", "machineDeployments").Index(i).Child("template", "bootstrap"))...)
-		allErrs = append(allErrs, webhook.validateTemplate(&class.Template.Infrastructure, in.Namespace, field.NewPath("spec", "workers", "machineDeployments").Index(i).Child("template", "infrastructure"))...)
-	}
-
-	return allErrs
-}
-
-func (webhook *ClusterClass) validateCompatibleSpecChanges(old, in *clusterv1.ClusterClass) field.ErrorList {
-	var allErrs field.ErrorList
-
-	// in case of create, no changes to verify
-	// return early.
-	if old == nil {
-		return nil
-	}
-
-	// Validate changes to MachineDeployments.
-	allErrs = append(allErrs, webhook.validateMachineDeploymentsCompatibleChanges(old, in)...)
-
-	// Validate InfrastructureClusterTemplate changes in a compatible way.
-	allErrs = append(allErrs, webhook.validateTemplatesAreCompatible(in.Spec.Infrastructure,
-		old.Spec.Infrastructure,
-		field.NewPath("spec", "infrastructure"),
-	)...)
-
-	// Validate control plane changes in a compatible way.
-	allErrs = append(allErrs, webhook.validateTemplatesAreCompatible(in.Spec.ControlPlane.LocalObjectTemplate,
-		old.Spec.ControlPlane.LocalObjectTemplate,
-		field.NewPath("spec", "controlPlane"),
-	)...)
-
-	if in.Spec.ControlPlane.MachineInfrastructure != nil && old.Spec.ControlPlane.MachineInfrastructure != nil {
-		allErrs = append(allErrs, webhook.validateTemplatesAreCompatible(
-			*old.Spec.ControlPlane.MachineInfrastructure,
-			*in.Spec.ControlPlane.MachineInfrastructure,
-			field.NewPath("spec", "controlPlane", "machineInfrastructure"),
-		)...)
-	}
 
-	return allErrs
+	// Note: The code in ValidateUpdate is intentionally not duplicated to avoid that we accidentally
+	// implement new checks in the API package and forget to duplicate them to the webhook package.
+	// The idea is to add new validation which requires a reader and ClusterClass variable/patch validation
+	// in the webhook package.
+	// When we drop the method in the API package we must inline it here.
+	return clusterClass.ValidateUpdate(oldClusterClass)
 }
 
-func (webhook *ClusterClass) validateMachineDeploymentsCompatibleChanges(old, in *clusterv1.ClusterClass) field.ErrorList {
-	var allErrs field.ErrorList
-
-	// Ensure no MachineDeployment class was removed.
-	classes := webhook.classNamesFromWorkerClass(in.Spec.Workers)
-	for _, oldClass := range old.Spec.Workers.MachineDeployments {
-		if !classes.Has(oldClass.Class) {
-			allErrs = append(allErrs,
-				field.Invalid(
-					field.NewPath("spec", "workers", "machineDeployments"),
-					in.Spec.Workers.MachineDeployments,
-					fmt.Sprintf("The %q MachineDeployment class can't be removed.", oldClass.Class),
-				),
-			)
-		}
-	}
-
-	// Ensure previous MachineDeployment class was modified in a compatible way.
-	for i, class := range in.Spec.Workers.MachineDeployments {
-		for _, oldClass := range old.Spec.Workers.MachineDeployments {
-			if class.Class == oldClass.Class {
-				// NOTE: class.Template.Metadata and class.Template.Bootstrap are allowed to change;
-				// class.Template.Bootstrap are ensured syntactically correct by validateAllRefs.
-
-				// Validates class.Template.Infrastructure template changes in a compatible way
-				allErrs = append(allErrs, webhook.validateTemplatesAreCompatible(
-					class.Template.Infrastructure,
-					oldClass.Template.Infrastructure,
-					field.NewPath("spec", "workers", "machineDeployments").Index(i).Child("template", "infrastructure"),
-				)...)
-			}
-		}
-	}
-
-	return allErrs
-}
-
-func (webhook *ClusterClass) validateTemplate(in *clusterv1.LocalObjectTemplate, namespace string, pathPrefix *field.Path) field.ErrorList {
-	var allErrs field.ErrorList
-
-	// check if ref is not nil.
-	if in.Ref == nil {
-		return field.ErrorList{field.Invalid(
-			pathPrefix.Child("ref"),
-			in.Ref.Name,
-			"cannot be nil",
-		)}
-	}
-
-	// check if a name is provided
-	if in.Ref.Name == "" {
-		allErrs = append(allErrs,
-			field.Invalid(
-				pathPrefix.Child("ref", "name"),
-				in.Ref.Name,
-				"cannot be empty",
-			),
-		)
-	}
-
-	// validate if namespace matches the provided namespace
-	if namespace != "" && in.Ref.Namespace != namespace {
-		allErrs = append(
-			allErrs,
-			field.Invalid(
-				pathPrefix.Child("ref", "namespace"),
-				in.Ref.Namespace,
-				fmt.Sprintf("must be '%s'", namespace),
-			),
-		)
-	}
-
-	// check if kind is a template
-	if len(in.Ref.Kind) <= len(clusterv1.TemplateSuffix) || !strings.HasSuffix(in.Ref.Kind, clusterv1.TemplateSuffix) {
-		allErrs = append(allErrs,
-			field.Invalid(
-				pathPrefix.Child("ref", "kind"),
-				in.Ref.Kind,
-				fmt.Sprintf("kind must be of form '<name>%s'", clusterv1.TemplateSuffix),
-			),
-		)
-	}
-
-	// check if apiVersion is valid
-	gv, err := schema.ParseGroupVersion(in.Ref.APIVersion)
-	if err != nil {
-		allErrs = append(allErrs,
-			field.Invalid(
-				pathPrefix.Child("ref", "apiVersion"),
-				in.Ref.APIVersion,
-				fmt.Sprintf("must be a valid apiVersion: %v", err),
-			),
-		)
-	}
-	if err == nil && gv.Empty() {
-		allErrs = append(allErrs,
-			field.Invalid(
-				pathPrefix.Child("ref", "apiVersion"),
-				in.Ref.APIVersion,
-				"value cannot be empty",
-			),
-		)
-	}
-
-	return allErrs
-}
-
-// validateTemplatesAreCompatible checks if a reference is compatible with the old one.
-// NOTE: this func assumes that validateTemplate() is called before, thus both ref are defined and syntactically valid;
-// also namespace are enforced to be the same of the ClusterClass.
-func (webhook *ClusterClass) validateTemplatesAreCompatible(old, in clusterv1.LocalObjectTemplate, pathPrefix *field.Path) field.ErrorList {
-	var allErrs field.ErrorList
-
-	// Check for nil Ref here to avoid panic.
-	if in.Ref == nil || old.Ref == nil {
-		return allErrs
-	}
-
-	// Ensure that the API Group and Kind does not change, while instead we allow version to change.
-	// TODO: In the future we might want to relax this requirement with some sort of opt-in behavior (e.g. annotation).
-
-	gv, err := schema.ParseGroupVersion(in.Ref.APIVersion)
-	if err != nil {
-		// NOTE this should never happen.
-		allErrs = append(allErrs,
-			field.Invalid(
-				pathPrefix.Child("ref", "apiVersion"),
-				in.Ref.APIVersion,
-				fmt.Sprintf("must be a valid apiVersion: %v", err),
-			),
-		)
-	}
-
-	oldGV, err := schema.ParseGroupVersion(old.Ref.APIVersion)
-	if err != nil {
-		// NOTE this should never happen.
-		allErrs = append(allErrs,
-			field.Invalid(
-				pathPrefix.Child("ref", "apiVersion"),
-				old.Ref.APIVersion,
-				fmt.Sprintf("must be a valid apiVersion: %v", err),
-			),
-		)
-	}
-
-	if gv.Group != oldGV.Group {
-		allErrs = append(allErrs,
-			field.Invalid(
-				pathPrefix.Child("ref", "apiVersion"),
-				in.Ref.APIVersion,
-				"apiGroup cannot be changed",
-			),
-		)
-	}
-
-	if in.Ref.Kind != old.Ref.Kind {
-		allErrs = append(allErrs,
-			field.Invalid(
-				pathPrefix.Child("ref", "kind"),
-				in.Ref.Kind,
-				"value cannot be changed",
-			),
-		)
-	}
-
-	return allErrs
-}
-
-// classNames returns the set of MachineDeployment class names.
-func (webhook *ClusterClass) classNamesFromWorkerClass(w clusterv1.WorkersClass) sets.String {
-	classes := sets.NewString()
-	for _, class := range w.MachineDeployments {
-		classes.Insert(class.Class)
-	}
-	return classes
-}
-
-func (webhook *ClusterClass) validateUniqueClasses(w clusterv1.WorkersClass, pathPrefix *field.Path) field.ErrorList {
-	var allErrs field.ErrorList
-
-	classes := sets.NewString()
-	for i, class := range w.MachineDeployments {
-		if classes.Has(class.Class) {
-			allErrs = append(allErrs,
-				field.Invalid(
-					pathPrefix.Child("machineDeployments").Index(i).Child("class"),
-					class.Class,
-					fmt.Sprintf("MachineDeployment class should be unique. MachineDeployment with class %q is defined more than once.", class.Class),
-				),
-			)
-		}
-		classes.Insert(class.Class)
+// ValidateDelete implements validation for ClusterClass delete.
+func (c *ClusterClass) ValidateDelete(ctx context.Context, obj runtime.Object) error {
+	clusterClass, ok := obj.(*clusterv1.ClusterClass)
+	if !ok {
+		return apierrors.NewBadRequest(fmt.Sprintf("expected a ClusterClass but got a %T", obj))
 	}
 
-	return allErrs
+	return clusterClass.ValidateDelete()
 }


Test patch:
diff --git a/bootstrap/kubeadm/internal/controllers/kubeadmconfig_controller_test.go b/bootstrap/kubeadm/internal/controllers/kubeadmconfig_controller_test.go
index 2f4f82f6e..308eeb16f 100644
--- a/bootstrap/kubeadm/internal/controllers/kubeadmconfig_controller_test.go
+++ b/bootstrap/kubeadm/internal/controllers/kubeadmconfig_controller_test.go
@@ -1682,7 +1682,7 @@ func TestKubeadmConfigReconciler_ResolveFiles(t *testing.T) {
 	}
 }
 
-// test utils
+// test utils.
 
 // newCluster return a CAPI cluster object.
 func newCluster(name, namespace string) *clusterv1.Cluster {
diff --git a/cmd/clusterctl/client/cluster/mover_test.go b/cmd/clusterctl/client/cluster/mover_test.go
index 7c7f74959..e0aaec72f 100644
--- a/cmd/clusterctl/client/cluster/mover_test.go
+++ b/cmd/clusterctl/client/cluster/mover_test.go
@@ -901,12 +901,8 @@ func Test_objectMover_restore(t *testing.T) {
 				g.Expect(graph.addRestoredObj(&objs[i])).NotTo(HaveOccurred())
 			}
 
-			// restore works on the target cluster which does not yet have objs to discover
-			// instead set the owners and tenants correctly on object graph like how ObjectMover.Restore does
-			// https://github.com/kubernetes-sigs/cluster-api/blob/main/cmd/clusterctl/client/cluster/mover.go#L129-L132
-			graph.setSoftOwnership()
-			graph.setTenants()
-			graph.checkVirtualNode()
+			// trigger discovery the content of the source cluster
+			g.Expect(graph.Discovery("")).To(Succeed())
 
 			err = mover.restore(graph, toProxy)
 			if tt.wantErr {
diff --git a/cmd/clusterctl/client/config_test.go b/cmd/clusterctl/client/config_test.go
index c43fda940..bb836d58d 100644
--- a/cmd/clusterctl/client/config_test.go
+++ b/cmd/clusterctl/client/config_test.go
@@ -65,7 +65,6 @@ func Test_clusterctlClient_GetProvidersConfig(t *testing.T) {
 				config.TalosControlPlaneProviderName,
 				config.AWSProviderName,
 				config.AzureProviderName,
-				config.BYOHProviderName,
 				config.DOProviderName,
 				config.DockerProviderName,
 				config.GCPProviderName,
@@ -97,7 +96,6 @@ func Test_clusterctlClient_GetProvidersConfig(t *testing.T) {
 				config.TalosControlPlaneProviderName,
 				config.AWSProviderName,
 				config.AzureProviderName,
-				config.BYOHProviderName,
 				config.DOProviderName,
 				config.DockerProviderName,
 				config.GCPProviderName,
diff --git a/cmd/clusterctl/cmd/config_repositories_test.go b/cmd/clusterctl/cmd/config_repositories_test.go
index 669300f58..ff980c5e8 100644
--- a/cmd/clusterctl/cmd/config_repositories_test.go
+++ b/cmd/clusterctl/cmd/config_repositories_test.go
@@ -111,7 +111,6 @@ nested              ControlPlaneProvider     https://github.com/kubernetes-sigs/
 talos               ControlPlaneProvider     https://github.com/talos-systems/cluster-api-control-plane-provider-talos/releases/latest/   control-plane-components.yaml
 aws                 InfrastructureProvider                                                                                                my-aws-infrastructure-components.yaml
 azure               InfrastructureProvider   https://github.com/kubernetes-sigs/cluster-api-provider-azure/releases/latest/               infrastructure-components.yaml
-byoh                InfrastructureProvider   https://github.com/vmware-tanzu/cluster-api-provider-bringyourownhost/releases/latest/       infrastructure-components.yaml
 digitalocean        InfrastructureProvider   https://github.com/kubernetes-sigs/cluster-api-provider-digitalocean/releases/latest/        infrastructure-components.yaml
 docker              InfrastructureProvider   https://github.com/kubernetes-sigs/cluster-api/releases/latest/                              infrastructure-components-development.yaml
 gcp                 InfrastructureProvider   https://github.com/kubernetes-sigs/cluster-api-provider-gcp/releases/latest/                 infrastructure-components.yaml
@@ -169,10 +168,6 @@ var expectedOutputYaml = `- File: core_components.yaml
   Name: azure
   ProviderType: InfrastructureProvider
   URL: https://github.com/kubernetes-sigs/cluster-api-provider-azure/releases/latest/
-- File: infrastructure-components.yaml
-  Name: byoh
-  ProviderType: InfrastructureProvider
-  URL: https://github.com/vmware-tanzu/cluster-api-provider-bringyourownhost/releases/latest/
 - File: infrastructure-components.yaml
   Name: digitalocean
   ProviderType: InfrastructureProvider
diff --git a/controlplane/kubeadm/controllers/controller_test.go b/controlplane/kubeadm/controllers/controller_test.go
index 705fb1fd7..e59699137 100644
--- a/controlplane/kubeadm/controllers/controller_test.go
+++ b/controlplane/kubeadm/controllers/controller_test.go
@@ -1361,7 +1361,7 @@ func TestKubeadmControlPlaneReconciler_reconcileDelete(t *testing.T) {
 	})
 }
 
-// test utils
+// test utils.
 
 func newFakeClient(initObjs ...client.Object) client.Client {
 	return &fakeClient{
diff --git a/test/framework/interfaces.go b/test/framework/interfaces.go
index 95e2dd33b..28d516f1a 100644
--- a/test/framework/interfaces.go
+++ b/test/framework/interfaces.go
@@ -22,7 +22,7 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/client"
 )
 
-// Interfaces to scope down client.Client
+// Interfaces to scope down client.Client.
 
 // Getter can get resources.
 type Getter interface {
diff --git a/test/go.sum b/test/go.sum
index f0301276a..67f7a7222 100644
--- a/test/go.sum
+++ b/test/go.sum
@@ -312,9 +312,8 @@ github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMi
 github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
 github.com/evanphx/json-patch v4.11.0+incompatible h1:glyUF9yIYtMHzn8xaKw5rMhdWcwsYV8dZHIq5567/xs=
 github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
+github.com/evanphx/json-patch/v5 v5.2.0 h1:8ozOH5xxoMYDt5/u+yMTsVXydVCbTORFnOOoq2lumco=
 github.com/evanphx/json-patch/v5 v5.2.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4=
-github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww=
-github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4=
 github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4=
 github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
 github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
diff --git a/test/infrastructure/docker/api/v1alpha3/condition_consts.go b/test/infrastructure/docker/api/v1alpha3/condition_consts.go
index 9f9dfebe4..3da821ab3 100644
--- a/test/infrastructure/docker/api/v1alpha3/condition_consts.go
+++ b/test/infrastructure/docker/api/v1alpha3/condition_consts.go
@@ -18,7 +18,7 @@ package v1alpha3
 
 import clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha3"
 
-// Conditions and condition Reasons for the DockerMachine object
+// Conditions and condition Reasons for the DockerMachine object.
 
 const (
 	// ContainerProvisionedCondition documents the status of the provisioning of the container
@@ -62,7 +62,7 @@ const (
 	BootstrapFailedReason = "BootstrapFailed"
 )
 
-// Conditions and condition Reasons for the DockerCluster object
+// Conditions and condition Reasons for the DockerCluster object.
 
 const (
 	// LoadBalancerAvailableCondition documents the availability of the container that implements the cluster load balancer.
diff --git a/test/infrastructure/docker/api/v1alpha4/condition_consts.go b/test/infrastructure/docker/api/v1alpha4/condition_consts.go
index 45235d5d6..033491246 100644
--- a/test/infrastructure/docker/api/v1alpha4/condition_consts.go
+++ b/test/infrastructure/docker/api/v1alpha4/condition_consts.go
@@ -18,7 +18,7 @@ package v1alpha4
 
 import clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
 
-// Conditions and condition Reasons for the DockerMachine object
+// Conditions and condition Reasons for the DockerMachine object.
 
 const (
 	// ContainerProvisionedCondition documents the status of the provisioning of the container
@@ -64,7 +64,7 @@ const (
 	BootstrapFailedReason = "BootstrapFailed"
 )
 
-// Conditions and condition Reasons for the DockerCluster object
+// Conditions and condition Reasons for the DockerCluster object.
 
 const (
 	// LoadBalancerAvailableCondition documents the availability of the container that implements the cluster load balancer.
diff --git a/test/infrastructure/docker/api/v1beta1/condition_consts.go b/test/infrastructure/docker/api/v1beta1/condition_consts.go
index 39fd09efc..92db126e9 100644
--- a/test/infrastructure/docker/api/v1beta1/condition_consts.go
+++ b/test/infrastructure/docker/api/v1beta1/condition_consts.go
@@ -18,7 +18,7 @@ package v1beta1
 
 import clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
 
-// Conditions and condition Reasons for the DockerMachine object
+// Conditions and condition Reasons for the DockerMachine object.
 
 const (
 	// ContainerProvisionedCondition documents the status of the provisioning of the container
@@ -68,7 +68,7 @@ const (
 	BootstrapFailedReason = "BootstrapFailed"
 )
 
-// Conditions and condition Reasons for the DockerCluster object
+// Conditions and condition Reasons for the DockerCluster object.
 
 const (
 	// LoadBalancerAvailableCondition documents the availability of the container that implements the cluster load balancer.
diff --git a/util/collections/machine_collection_test.go b/util/collections/machine_collection_test.go
index 4fae8bbe6..517c0dc50 100644
--- a/util/collections/machine_collection_test.go
+++ b/util/collections/machine_collection_test.go
@@ -141,7 +141,7 @@ func TestMachinesLowestVersion(t *testing.T) {
 	}
 }
 
-/* Helper functions to build machine objects for tests */
+/* Helper functions to build machine objects for tests. */
 
 type machineOpt func(*clusterv1.Machine)
 
diff --git a/webhooks/cluster_test.go b/webhooks/cluster_test.go
index 187ba0ae0..3fe238784 100644
--- a/webhooks/cluster_test.go
+++ b/webhooks/cluster_test.go
@@ -20,6 +20,7 @@ import (
 	"testing"
 
 	. "github.com/onsi/gomega"
+
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	utilfeature "k8s.io/component-base/featuregate/testing"
@@ -30,7 +31,7 @@ import (
 func TestClusterDefaultNamespaces(t *testing.T) {
 	g := NewWithT(t)
 
-	c := &clusterv1.Cluster{
+	in := &clusterv1.Cluster{
 		ObjectMeta: metav1.ObjectMeta{
 			Namespace: "fooboo",
 		},
@@ -39,12 +40,14 @@ func TestClusterDefaultNamespaces(t *testing.T) {
 			ControlPlaneRef:   &corev1.ObjectReference{},
 		},
 	}
+
 	webhook := &Cluster{}
-	t.Run("for Cluster", customDefaultValidateTest(ctx, c, webhook))
-	g.Expect(webhook.Default(ctx, c)).To(Succeed())
+	t.Run("for Cluster", customDefaultValidateTest(ctx, in, webhook))
 
-	g.Expect(c.Spec.InfrastructureRef.Namespace).To(Equal(c.Namespace))
-	g.Expect(c.Spec.ControlPlaneRef.Namespace).To(Equal(c.Namespace))
+	g.Expect(webhook.Default(ctx, in)).To(Succeed())
+
+	g.Expect(in.Spec.InfrastructureRef.Namespace).To(Equal(in.Namespace))
+	g.Expect(in.Spec.ControlPlaneRef.Namespace).To(Equal(in.Namespace))
 }
 
 func TestClusterDefaultTopologyVersion(t *testing.T) {
@@ -54,7 +57,7 @@ func TestClusterDefaultTopologyVersion(t *testing.T) {
 
 	g := NewWithT(t)
 
-	c := &clusterv1.Cluster{
+	in := &clusterv1.Cluster{
 		ObjectMeta: metav1.ObjectMeta{
 			Namespace: "fooboo",
 		},
@@ -66,380 +69,8 @@ func TestClusterDefaultTopologyVersion(t *testing.T) {
 		},
 	}
 	webhook := &Cluster{}
-	t.Run("for Cluster", customDefaultValidateTest(ctx, c, webhook))
-	g.Expect(webhook.Default(ctx, c)).To(Succeed())
-
-	g.Expect(c.Spec.Topology.Version).To(HavePrefix("v"))
-}
-
-func TestClusterValidation(t *testing.T) {
-	// NOTE: ClusterTopology feature flag is disabled by default, thus preventing to set Cluster.Topologies.
-
-	tests := []struct {
-		name      string
-		in        *clusterv1.Cluster
-		old       *clusterv1.Cluster
-		expectErr bool
-	}{
-		{
-			name:      "should return error when cluster namespace and infrastructure ref namespace mismatch",
-			expectErr: true,
-			in: &clusterv1.Cluster{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "foo",
-				},
-				Spec: clusterv1.ClusterSpec{
-					InfrastructureRef: &corev1.ObjectReference{
-						Namespace: "bar",
-					},
-					ControlPlaneRef: &corev1.ObjectReference{
-						Namespace: "foo",
-					},
-				},
-			},
-		},
-		{
-			name:      "should return error when cluster namespace and controlplane ref namespace mismatch",
-			expectErr: true,
-			in: &clusterv1.Cluster{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "foo",
-				},
-				Spec: clusterv1.ClusterSpec{
-					InfrastructureRef: &corev1.ObjectReference{
-						Namespace: "foo",
-					},
-					ControlPlaneRef: &corev1.ObjectReference{
-						Namespace: "bar",
-					},
-				},
-			},
-		},
-		{
-			name:      "should succeed when namespaces match",
-			expectErr: false,
-			in: &clusterv1.Cluster{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "foo",
-				},
-				Spec: clusterv1.ClusterSpec{
-					ControlPlaneRef: &corev1.ObjectReference{
-						Namespace: "foo",
-					},
-					InfrastructureRef: &corev1.ObjectReference{
-						Namespace: "foo",
-					},
-				},
-			},
-		},
-		{
-			name:      "fails if topology is set but feature flag is disabled",
-			expectErr: true,
-			in: &clusterv1.Cluster{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "foo",
-				},
-				Spec: clusterv1.ClusterSpec{
-					ControlPlaneRef: &corev1.ObjectReference{
-						Namespace: "foo",
-					},
-					InfrastructureRef: &corev1.ObjectReference{
-						Namespace: "foo",
-					},
-					Topology: &clusterv1.Topology{},
-				},
-			},
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			g := NewWithT(t)
-			webhook := Cluster{}
-			err := webhook.validate(tt.old, tt.in)
-			if tt.expectErr {
-				g.Expect(err).To(HaveOccurred())
-			} else {
-				g.Expect(err).ToNot(HaveOccurred())
-			}
-		})
-	}
-}
+	t.Run("for Cluster", customDefaultValidateTest(ctx, in, webhook))
+	g.Expect(webhook.Default(ctx, in)).To(Succeed())
 
-func TestClusterTopologyValidation(t *testing.T) {
-	// NOTE: ClusterTopology feature flag is disabled by default, thus preventing to set Cluster.Topologies.
-	// Enabling the feature flag temporarily for this test.
-	defer utilfeature.SetFeatureGateDuringTest(t, feature.Gates, feature.ClusterTopology, true)()
-
-	tests := []struct {
-		name      string
-		in        *clusterv1.Cluster
-		old       *clusterv1.Cluster
-		expectErr bool
-	}{
-		{
-			name:      "should return error when topology does not have class",
-			expectErr: true,
-			in: &clusterv1.Cluster{
-				Spec: clusterv1.ClusterSpec{
-					Topology: &clusterv1.Topology{},
-				},
-			},
-		},
-		{
-			name:      "should return error when topology does not have valid version",
-			expectErr: true,
-			in: &clusterv1.Cluster{
-				Spec: clusterv1.ClusterSpec{
-					Topology: &clusterv1.Topology{
-						Class:   "foo",
-						Version: "invalid",
-					},
-				},
-			},
-		},
-		{
-			name:      "should return error when downgrading topology version - major",
-			expectErr: true,
-			old: &clusterv1.Cluster{
-				Spec: clusterv1.ClusterSpec{
-					Topology: &clusterv1.Topology{
-						Class:   "foo",
-						Version: "v2.2.3",
-					},
-				},
-			},
-			in: &clusterv1.Cluster{
-				Spec: clusterv1.ClusterSpec{
-					Topology: &clusterv1.Topology{
-						Class:   "foo",
-						Version: "v1.2.3",
-					},
-				},
-			},
-		},
-		{
-			name:      "should return error when downgrading topology version - minor",
-			expectErr: true,
-			old: &clusterv1.Cluster{
-				Spec: clusterv1.ClusterSpec{
-					Topology: &clusterv1.Topology{
-						Class:   "foo",
-						Version: "v1.2.3",
-					},
-				},
-			},
-			in: &clusterv1.Cluster{
-				Spec: clusterv1.ClusterSpec{
-					Topology: &clusterv1.Topology{
-						Class:   "foo",
-						Version: "v1.1.3",
-					},
-				},
-			},
-		},
-		{
-			name:      "should return error when downgrading topology version - patch",
-			expectErr: true,
-			old: &clusterv1.Cluster{
-				Spec: clusterv1.ClusterSpec{
-					Topology: &clusterv1.Topology{
-						Class:   "foo",
-						Version: "v1.2.3",
-					},
-				},
-			},
-			in: &clusterv1.Cluster{
-				Spec: clusterv1.ClusterSpec{
-					Topology: &clusterv1.Topology{
-						Class:   "foo",
-						Version: "v1.2.2",
-					},
-				},
-			},
-		},
-		{
-			name:      "should return error when downgrading topology version - pre-release",
-			expectErr: true,
-			old: &clusterv1.Cluster{
-				Spec: clusterv1.ClusterSpec{
-					Topology: &clusterv1.Topology{
-						Class:   "foo",
-						Version: "v1.2.3-xyz.2",
-					},
-				},
-			},
-			in: &clusterv1.Cluster{
-				Spec: clusterv1.ClusterSpec{
-					Topology: &clusterv1.Topology{
-						Class:   "foo",
-						Version: "v1.2.3-xyz.1",
-					},
-				},
-			},
-		},
-		{
-			name:      "should return error when downgrading topology version - build tag",
-			expectErr: true,
-			old: &clusterv1.Cluster{
-				Spec: clusterv1.ClusterSpec{
-					Topology: &clusterv1.Topology{
-						Class:   "foo",
-						Version: "v1.2.3+xyz.2",
-					},
-				},
-			},
-			in: &clusterv1.Cluster{
-				Spec: clusterv1.ClusterSpec{
-					Topology: &clusterv1.Topology{
-						Class:   "foo",
-						Version: "v1.2.3+xyz.1",
-					},
-				},
-			},
-		},
-		{
-			name:      "should return error when duplicated MachineDeployments names exists in a Topology",
-			expectErr: true,
-			in: &clusterv1.Cluster{
-				Spec: clusterv1.ClusterSpec{
-					Topology: &clusterv1.Topology{
-						Class:   "foo",
-						Version: "v1.19.1",
-						Workers: &clusterv1.WorkersTopology{
-							MachineDeployments: []clusterv1.MachineDeploymentTopology{
-								{
-									Name: "aa",
-								},
-								{
-									Name: "aa",
-								},
-							},
-						},
-					},
-				},
-			},
-		},
-		{
-			name:      "should pass when MachineDeployments names in a Topology are unique",
-			expectErr: false,
-			in: &clusterv1.Cluster{
-				Spec: clusterv1.ClusterSpec{
-					Topology: &clusterv1.Topology{
-						Class:   "foo",
-						Version: "v1.19.1",
-						Workers: &clusterv1.WorkersTopology{
-							MachineDeployments: []clusterv1.MachineDeploymentTopology{
-								{
-									Name: "aa",
-								},
-								{
-									Name: "bb",
-								},
-							},
-						},
-					},
-				},
-			},
-		},
-		{
-			name:      "should return error on update when Topology class is changed",
-			expectErr: true,
-			old: &clusterv1.Cluster{
-				Spec: clusterv1.ClusterSpec{
-					InfrastructureRef: &corev1.ObjectReference{},
-					Topology: &clusterv1.Topology{
-						Class:   "foo",
-						Version: "v1.19.1",
-					},
-				},
-			},
-			in: &clusterv1.Cluster{
-				Spec: clusterv1.ClusterSpec{
-					InfrastructureRef: &corev1.ObjectReference{},
-					Topology: &clusterv1.Topology{
-						Class:   "bar",
-						Version: "v1.19.1",
-					},
-				},
-			},
-		},
-		{
-			name:      "should return error on update when Topology version is downgraded",
-			expectErr: true,
-			old: &clusterv1.Cluster{
-				Spec: clusterv1.ClusterSpec{
-					InfrastructureRef: &corev1.ObjectReference{},
-					Topology: &clusterv1.Topology{
-						Class:   "foo",
-						Version: "v1.19.1",
-					},
-				},
-			},
-			in: &clusterv1.Cluster{
-				Spec: clusterv1.ClusterSpec{
-					InfrastructureRef: &corev1.ObjectReference{},
-					Topology: &clusterv1.Topology{
-						Class:   "foo",
-						Version: "v1.19.0",
-					},
-				},
-			},
-		},
-		{
-			name:      "should update",
-			expectErr: false,
-			old: &clusterv1.Cluster{
-				Spec: clusterv1.ClusterSpec{
-					InfrastructureRef: &corev1.ObjectReference{},
-					Topology: &clusterv1.Topology{
-						Class:   "foo",
-						Version: "v1.19.1",
-						Workers: &clusterv1.WorkersTopology{
-							MachineDeployments: []clusterv1.MachineDeploymentTopology{
-								{
-									Name: "aa",
-								},
-								{
-									Name: "bb",
-								},
-							},
-						},
-					},
-				},
-			},
-			in: &clusterv1.Cluster{
-				Spec: clusterv1.ClusterSpec{
-					InfrastructureRef: &corev1.ObjectReference{},
-					Topology: &clusterv1.Topology{
-						Class:   "foo",
-						Version: "v1.19.2",
-						Workers: &clusterv1.WorkersTopology{
-							MachineDeployments: []clusterv1.MachineDeploymentTopology{
-								{
-									Name: "aa",
-								},
-								{
-									Name: "bb",
-								},
-							},
-						},
-					},
-				},
-			},
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			g := NewWithT(t)
-			webhook := Cluster{}
-			err := webhook.validate(tt.old, tt.in)
-			if tt.expectErr {
-				g.Expect(err).To(HaveOccurred())
-			} else {
-				g.Expect(err).ToNot(HaveOccurred())
-			}
-		})
-	}
+	g.Expect(in.Spec.Topology.Version).To(HavePrefix("v"))
 }
diff --git a/webhooks/clusterclass_test.go b/webhooks/clusterclass_test.go
index 772026cca..eceb1ca86 100644
--- a/webhooks/clusterclass_test.go
+++ b/webhooks/clusterclass_test.go
@@ -74,6 +74,7 @@ func TestClusterClassDefaultNamespaces(t *testing.T) {
 	}
 
 	webhook := &ClusterClass{}
+
 	t.Run("for ClusterClass", customDefaultValidateTest(ctx, in, webhook))
 
 	g := NewWithT(t)
@@ -88,1371 +89,3 @@ func TestClusterClassDefaultNamespaces(t *testing.T) {
 		g.Expect(in.Spec.Workers.MachineDeployments[i].Template.Infrastructure.Ref.Namespace).To(Equal(namespace))
 	}
 }
-
-func TestClusterClassValidationFeatureGated(t *testing.T) {
-	// NOTE: ClusterTopology feature flag is disabled by default, thus preventing to create or update ClusterClasses.
-
-	ref := &corev1.ObjectReference{
-		APIVersion: "foo",
-		Kind:       "barTemplate",
-		Name:       "baz",
-		Namespace:  "default",
-	}
-	tests := []struct {
-		name      string
-		in        *clusterv1.ClusterClass
-		old       *clusterv1.ClusterClass
-		expectErr bool
-	}{
-		{
-			name: "creation should fail if feature flag is disabled, no matter the ClusterClass is valid(or not)",
-			in: &clusterv1.ClusterClass{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "default",
-				},
-				Spec: clusterv1.ClusterClassSpec{
-					Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-					ControlPlane: clusterv1.ControlPlaneClass{
-						LocalObjectTemplate: clusterv1.LocalObjectTemplate{Ref: ref},
-					},
-					Workers: clusterv1.WorkersClass{
-						MachineDeployments: []clusterv1.MachineDeploymentClass{
-							{
-								Class: "aa",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: ref},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-								},
-							},
-						},
-					},
-				},
-			},
-			expectErr: true,
-		},
-		{
-			name: "update should fail if feature flag is disabled, no matter the ClusterClass is valid(or not)",
-			old: &clusterv1.ClusterClass{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "default",
-				},
-				Spec: clusterv1.ClusterClassSpec{
-					Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-					ControlPlane: clusterv1.ControlPlaneClass{
-						LocalObjectTemplate: clusterv1.LocalObjectTemplate{Ref: ref},
-					},
-					Workers: clusterv1.WorkersClass{
-						MachineDeployments: []clusterv1.MachineDeploymentClass{
-							{
-								Class: "aa",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: ref},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-								},
-							},
-						},
-					},
-				},
-			},
-			in: &clusterv1.ClusterClass{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "default",
-				},
-				Spec: clusterv1.ClusterClassSpec{
-					Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-					ControlPlane: clusterv1.ControlPlaneClass{
-						LocalObjectTemplate: clusterv1.LocalObjectTemplate{Ref: ref},
-					},
-					Workers: clusterv1.WorkersClass{
-						MachineDeployments: []clusterv1.MachineDeploymentClass{
-							{
-								Class: "aa",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Metadata:       clusterv1.ObjectMeta{},
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: ref},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-								},
-							},
-						},
-					},
-				},
-			},
-			expectErr: true,
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			g := NewWithT(t)
-			webhook := &ClusterClass{}
-			if tt.expectErr {
-				g.Expect(webhook.validate(tt.old, tt.in)).NotTo(Succeed())
-			} else {
-				g.Expect(webhook.validate(tt.old, tt.in)).To(Succeed())
-			}
-		})
-	}
-}
-
-func TestClusterClassValidation(t *testing.T) {
-	// NOTE: ClusterTopology feature flag is disabled by default, thus preventing to create or update ClusterClasses.
-	// Enabling the feature flag temporarily for this test.
-	defer utilfeature.SetFeatureGateDuringTest(t, feature.Gates, feature.ClusterTopology, true)()
-
-	ref := &corev1.ObjectReference{
-		APIVersion: "group.test.io/foo",
-		Kind:       "barTemplate",
-		Name:       "baz",
-		Namespace:  "default",
-	}
-	refInAnotherNamespace := &corev1.ObjectReference{
-		APIVersion: "group.test.io/foo",
-		Kind:       "barTemplate",
-		Name:       "baz",
-		Namespace:  "another-namespace",
-	}
-	refBadTemplate := &corev1.ObjectReference{
-		APIVersion: "group.test.io/foo",
-		Kind:       "bar",
-		Name:       "baz",
-		Namespace:  "default",
-	}
-	refBadAPIVersion := &corev1.ObjectReference{
-		APIVersion: "group/test.io/v1/foo",
-		Kind:       "barTemplate",
-		Name:       "baz",
-		Namespace:  "default",
-	}
-	refEmptyName := &corev1.ObjectReference{
-		APIVersion: "group.test.io/foo",
-		Namespace:  "default",
-		Kind:       "barTemplate",
-	}
-	incompatibleRef := &corev1.ObjectReference{
-		APIVersion: "group.test.io/foo",
-		Kind:       "another-barTemplate",
-		Name:       "baz",
-		Namespace:  "default",
-	}
-	compatibleRef := &corev1.ObjectReference{
-		APIVersion: "group.test.io/another-foo",
-		Kind:       "barTemplate",
-		Name:       "another-baz",
-		Namespace:  "default",
-	}
-
-	tests := []struct {
-		name      string
-		in        *clusterv1.ClusterClass
-		old       *clusterv1.ClusterClass
-		expectErr bool
-	}{
-
-		/*
-			CREATE Tests
-		*/
-
-		{
-			name: "create pass",
-			in: &clusterv1.ClusterClass{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "default",
-				},
-				Spec: clusterv1.ClusterClassSpec{
-					Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-					ControlPlane: clusterv1.ControlPlaneClass{
-						LocalObjectTemplate: clusterv1.LocalObjectTemplate{Ref: ref},
-					},
-					Workers: clusterv1.WorkersClass{
-						MachineDeployments: []clusterv1.MachineDeploymentClass{
-							{
-								Class: "aa",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: ref},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-								},
-							},
-							{
-								Class: "bb",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: ref},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-								},
-							},
-						},
-					},
-				},
-			},
-			expectErr: false,
-		},
-
-		// empty name in ref tests
-		{
-			name: "create fail infrastructure has empty name",
-			in: &clusterv1.ClusterClass{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "default",
-				},
-				Spec: clusterv1.ClusterClassSpec{
-					Infrastructure: clusterv1.LocalObjectTemplate{Ref: refEmptyName},
-					ControlPlane: clusterv1.ControlPlaneClass{
-						LocalObjectTemplate: clusterv1.LocalObjectTemplate{Ref: ref},
-					},
-					Workers: clusterv1.WorkersClass{
-						MachineDeployments: []clusterv1.MachineDeploymentClass{
-							{
-								Class: "aa",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: ref},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-								},
-							},
-						},
-					},
-				},
-			},
-			expectErr: true,
-		},
-		{
-			name: "create fail control plane class has empty name",
-			in: &clusterv1.ClusterClass{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "default",
-				},
-				Spec: clusterv1.ClusterClassSpec{
-					Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-					ControlPlane: clusterv1.ControlPlaneClass{
-						LocalObjectTemplate: clusterv1.LocalObjectTemplate{Ref: refEmptyName},
-					},
-					Workers: clusterv1.WorkersClass{
-						MachineDeployments: []clusterv1.MachineDeploymentClass{
-							{
-								Class: "aa",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: ref},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-								},
-							},
-						},
-					},
-				},
-			},
-			expectErr: true,
-		},
-		{
-			name: "create fail control plane class machineinfrastructure has empty name",
-			in: &clusterv1.ClusterClass{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "default",
-				},
-				Spec: clusterv1.ClusterClassSpec{
-					Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-					ControlPlane: clusterv1.ControlPlaneClass{
-						LocalObjectTemplate:   clusterv1.LocalObjectTemplate{Ref: ref},
-						MachineInfrastructure: &clusterv1.LocalObjectTemplate{Ref: refEmptyName},
-					},
-					Workers: clusterv1.WorkersClass{
-						MachineDeployments: []clusterv1.MachineDeploymentClass{
-							{
-								Class: "aa",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: ref},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-								},
-							},
-						},
-					},
-				},
-			},
-			expectErr: true,
-		},
-		{
-			name: "create fail machine deployment bootstrap has empty name",
-			in: &clusterv1.ClusterClass{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "default",
-				},
-				Spec: clusterv1.ClusterClassSpec{
-					Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-					ControlPlane: clusterv1.ControlPlaneClass{
-						LocalObjectTemplate: clusterv1.LocalObjectTemplate{Ref: ref},
-					},
-					Workers: clusterv1.WorkersClass{
-						MachineDeployments: []clusterv1.MachineDeploymentClass{
-							{
-								Class: "aa",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: refEmptyName},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-								},
-							},
-						},
-					},
-				},
-			},
-			expectErr: true,
-		},
-		{
-			name: "create fail machine deployment infrastructure has empty name",
-			in: &clusterv1.ClusterClass{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "default",
-				},
-				Spec: clusterv1.ClusterClassSpec{
-					Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-					ControlPlane: clusterv1.ControlPlaneClass{
-						LocalObjectTemplate: clusterv1.LocalObjectTemplate{Ref: ref},
-					},
-					Workers: clusterv1.WorkersClass{
-						MachineDeployments: []clusterv1.MachineDeploymentClass{
-							{
-								Class: "aa",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: ref},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: refEmptyName},
-								},
-							},
-						},
-					},
-				},
-			},
-			expectErr: true,
-		},
-
-		// inconsistent namespace in ref tests
-		{
-			name: "create fail if infrastructure has inconsistent namespace",
-			in: &clusterv1.ClusterClass{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "default",
-				},
-				Spec: clusterv1.ClusterClassSpec{
-					Infrastructure: clusterv1.LocalObjectTemplate{Ref: refInAnotherNamespace},
-					ControlPlane: clusterv1.ControlPlaneClass{
-						LocalObjectTemplate: clusterv1.LocalObjectTemplate{Ref: ref},
-					},
-					Workers: clusterv1.WorkersClass{
-						MachineDeployments: []clusterv1.MachineDeploymentClass{
-							{
-								Class: "aa",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: ref},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-								},
-							},
-						},
-					},
-				},
-			},
-			expectErr: true,
-		},
-		{
-			name: "create fail if control plane has inconsistent namespace",
-			in: &clusterv1.ClusterClass{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "default",
-				},
-				Spec: clusterv1.ClusterClassSpec{
-					Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-					ControlPlane: clusterv1.ControlPlaneClass{
-						LocalObjectTemplate: clusterv1.LocalObjectTemplate{Ref: refInAnotherNamespace},
-					},
-					Workers: clusterv1.WorkersClass{
-						MachineDeployments: []clusterv1.MachineDeploymentClass{
-							{
-								Class: "aa",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: ref},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-								},
-							},
-						},
-					},
-				},
-			},
-			expectErr: true,
-		},
-		{
-			name: "create fail if control plane class machineinfrastructure has inconsistent namespace",
-			in: &clusterv1.ClusterClass{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "default",
-				},
-				Spec: clusterv1.ClusterClassSpec{
-					Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-					ControlPlane: clusterv1.ControlPlaneClass{
-						LocalObjectTemplate:   clusterv1.LocalObjectTemplate{Ref: ref},
-						MachineInfrastructure: &clusterv1.LocalObjectTemplate{Ref: refInAnotherNamespace},
-					},
-					Workers: clusterv1.WorkersClass{
-						MachineDeployments: []clusterv1.MachineDeploymentClass{
-							{
-								Class: "aa",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: ref},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-								},
-							},
-						},
-					},
-				},
-			},
-			expectErr: true,
-		},
-		{
-			name: "create fail if machine deployment / bootstrap has inconsistent namespace",
-			in: &clusterv1.ClusterClass{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "default",
-				},
-				Spec: clusterv1.ClusterClassSpec{
-					Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-					ControlPlane: clusterv1.ControlPlaneClass{
-						LocalObjectTemplate: clusterv1.LocalObjectTemplate{Ref: ref},
-					},
-					Workers: clusterv1.WorkersClass{
-						MachineDeployments: []clusterv1.MachineDeploymentClass{
-							{
-								Class: "aa",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: refInAnotherNamespace},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-								},
-							},
-						},
-					},
-				},
-			},
-			expectErr: true,
-		},
-		{
-			name: "create fail if machine deployment / infrastructure has inconsistent namespace",
-			in: &clusterv1.ClusterClass{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "default",
-				},
-				Spec: clusterv1.ClusterClassSpec{
-					Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-					ControlPlane: clusterv1.ControlPlaneClass{
-						LocalObjectTemplate: clusterv1.LocalObjectTemplate{Ref: ref},
-					},
-					Workers: clusterv1.WorkersClass{
-						MachineDeployments: []clusterv1.MachineDeploymentClass{
-							{
-								Class: "aa",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: ref},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: refInAnotherNamespace},
-								},
-							},
-						},
-					},
-				},
-			},
-			expectErr: true,
-		},
-
-		// bad template in ref tests
-		{
-			name: "create fail if bad template in control plane",
-			in: &clusterv1.ClusterClass{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "default",
-				},
-				Spec: clusterv1.ClusterClassSpec{
-					Infrastructure: clusterv1.LocalObjectTemplate{Ref: refBadTemplate},
-					ControlPlane: clusterv1.ControlPlaneClass{
-						LocalObjectTemplate: clusterv1.LocalObjectTemplate{Ref: ref},
-					},
-					Workers: clusterv1.WorkersClass{
-						MachineDeployments: []clusterv1.MachineDeploymentClass{
-							{
-								Class: "aa",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: ref},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-								},
-							},
-						},
-					},
-				},
-			},
-			old:       nil,
-			expectErr: true,
-		},
-		{
-			name: "create fail if bad template in control plane class machineinfrastructure",
-			in: &clusterv1.ClusterClass{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "default",
-				},
-				Spec: clusterv1.ClusterClassSpec{
-					Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-					ControlPlane: clusterv1.ControlPlaneClass{
-						LocalObjectTemplate:   clusterv1.LocalObjectTemplate{Ref: ref},
-						MachineInfrastructure: &clusterv1.LocalObjectTemplate{Ref: refBadTemplate},
-					},
-					Workers: clusterv1.WorkersClass{
-						MachineDeployments: []clusterv1.MachineDeploymentClass{
-							{
-								Class: "aa",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: ref},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-								},
-							},
-						},
-					},
-				},
-			},
-			old:       nil,
-			expectErr: true,
-		},
-		{
-			name: "create fail if bad template in infrastructure",
-			in: &clusterv1.ClusterClass{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "default",
-				},
-				Spec: clusterv1.ClusterClassSpec{
-					Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-					ControlPlane: clusterv1.ControlPlaneClass{
-						LocalObjectTemplate: clusterv1.LocalObjectTemplate{Ref: refBadTemplate},
-					},
-					Workers: clusterv1.WorkersClass{
-						MachineDeployments: []clusterv1.MachineDeploymentClass{
-							{
-								Class: "aa",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: ref},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-								},
-							},
-						},
-					},
-				},
-			},
-			old:       nil,
-			expectErr: true,
-		},
-		{
-			name: "create fail if bad template in machine deployment bootstrap",
-			in: &clusterv1.ClusterClass{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "default",
-				},
-				Spec: clusterv1.ClusterClassSpec{
-					Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-					ControlPlane: clusterv1.ControlPlaneClass{
-						LocalObjectTemplate: clusterv1.LocalObjectTemplate{Ref: ref},
-					},
-					Workers: clusterv1.WorkersClass{
-						MachineDeployments: []clusterv1.MachineDeploymentClass{
-							{
-								Class: "aa",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: refBadTemplate},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-								},
-							},
-						},
-					},
-				},
-			},
-			old:       nil,
-			expectErr: true,
-		},
-		{
-			name: "create fail if bad template in machine deployment infrastructure",
-			in: &clusterv1.ClusterClass{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "default",
-				},
-				Spec: clusterv1.ClusterClassSpec{
-					Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-					ControlPlane: clusterv1.ControlPlaneClass{
-						LocalObjectTemplate: clusterv1.LocalObjectTemplate{Ref: ref},
-					},
-					Workers: clusterv1.WorkersClass{
-						MachineDeployments: []clusterv1.MachineDeploymentClass{
-							{
-								Class: "aa",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: ref},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: refBadTemplate},
-								},
-							},
-						},
-					},
-				},
-			},
-			old:       nil,
-			expectErr: true,
-		},
-
-		// bad apiVersion in ref tests
-		{
-			name: "create fail if bad apiVersion in control plane",
-			in: &clusterv1.ClusterClass{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "default",
-				},
-				Spec: clusterv1.ClusterClassSpec{
-					Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-					ControlPlane: clusterv1.ControlPlaneClass{
-						LocalObjectTemplate: clusterv1.LocalObjectTemplate{Ref: refBadAPIVersion},
-					},
-					Workers: clusterv1.WorkersClass{
-						MachineDeployments: []clusterv1.MachineDeploymentClass{
-							{
-								Class: "aa",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: ref},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-								},
-							},
-						},
-					},
-				},
-			},
-			old:       nil,
-			expectErr: true,
-		},
-		{
-			name: "create fail if bad apiVersion in control plane class machineinfrastructure",
-			in: &clusterv1.ClusterClass{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "default",
-				},
-				Spec: clusterv1.ClusterClassSpec{
-					Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-					ControlPlane: clusterv1.ControlPlaneClass{
-						LocalObjectTemplate:   clusterv1.LocalObjectTemplate{Ref: ref},
-						MachineInfrastructure: &clusterv1.LocalObjectTemplate{Ref: refBadAPIVersion},
-					},
-					Workers: clusterv1.WorkersClass{
-						MachineDeployments: []clusterv1.MachineDeploymentClass{
-							{
-								Class: "aa",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: ref},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-								},
-							},
-						},
-					},
-				},
-			},
-			old:       nil,
-			expectErr: true,
-		},
-		{
-			name: "create fail if bad apiVersion in infrastructure",
-			in: &clusterv1.ClusterClass{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "default",
-				},
-				Spec: clusterv1.ClusterClassSpec{
-					Infrastructure: clusterv1.LocalObjectTemplate{Ref: refBadAPIVersion},
-					ControlPlane: clusterv1.ControlPlaneClass{
-						LocalObjectTemplate: clusterv1.LocalObjectTemplate{Ref: ref},
-					},
-					Workers: clusterv1.WorkersClass{
-						MachineDeployments: []clusterv1.MachineDeploymentClass{
-							{
-								Class: "aa",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: ref},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-								},
-							},
-						},
-					},
-				},
-			},
-			old:       nil,
-			expectErr: true,
-		},
-		{
-			name: "create fail if bad apiVersion in machine deployment bootstrap",
-			in: &clusterv1.ClusterClass{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "default",
-				},
-				Spec: clusterv1.ClusterClassSpec{
-					Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-					ControlPlane: clusterv1.ControlPlaneClass{
-						LocalObjectTemplate: clusterv1.LocalObjectTemplate{Ref: ref},
-					},
-					Workers: clusterv1.WorkersClass{
-						MachineDeployments: []clusterv1.MachineDeploymentClass{
-							{
-								Class: "aa",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: refBadAPIVersion},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-								},
-							},
-						},
-					},
-				},
-			},
-			old:       nil,
-			expectErr: true,
-		},
-		{
-			name: "create fail if bad apiVersion in machine deployment infrastructure",
-			in: &clusterv1.ClusterClass{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "default",
-				},
-				Spec: clusterv1.ClusterClassSpec{
-					Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-					ControlPlane: clusterv1.ControlPlaneClass{
-						LocalObjectTemplate: clusterv1.LocalObjectTemplate{Ref: ref},
-					},
-					Workers: clusterv1.WorkersClass{
-						MachineDeployments: []clusterv1.MachineDeploymentClass{
-							{
-								Class: "aa",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: ref},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: refBadAPIVersion},
-								},
-							},
-						},
-					},
-				},
-			},
-			old:       nil,
-			expectErr: true,
-		},
-
-		// create test
-		{
-			name: "create fail if duplicated DeploymentClasses",
-			in: &clusterv1.ClusterClass{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "default",
-				},
-				Spec: clusterv1.ClusterClassSpec{
-					Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-					ControlPlane: clusterv1.ControlPlaneClass{
-						LocalObjectTemplate: clusterv1.LocalObjectTemplate{Ref: ref},
-					},
-					Workers: clusterv1.WorkersClass{
-						MachineDeployments: []clusterv1.MachineDeploymentClass{
-							{
-								Class: "aa",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: ref},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-								},
-							},
-							{
-								Class: "aa",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: ref},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-								},
-							},
-						},
-					},
-				},
-			},
-			expectErr: true,
-		},
-
-		/*
-			UPDATE Tests
-		*/
-
-		{
-			name: "update pass in case of no changes",
-			old: &clusterv1.ClusterClass{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "default",
-				},
-				Spec: clusterv1.ClusterClassSpec{
-					Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-					ControlPlane: clusterv1.ControlPlaneClass{
-						LocalObjectTemplate: clusterv1.LocalObjectTemplate{Ref: ref},
-					},
-					Workers: clusterv1.WorkersClass{
-						MachineDeployments: []clusterv1.MachineDeploymentClass{
-							{
-								Class: "aa",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: ref},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-								},
-							},
-						},
-					},
-				},
-			},
-			in: &clusterv1.ClusterClass{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "default",
-				},
-				Spec: clusterv1.ClusterClassSpec{
-					Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-					ControlPlane: clusterv1.ControlPlaneClass{
-						LocalObjectTemplate: clusterv1.LocalObjectTemplate{Ref: ref},
-					},
-					Workers: clusterv1.WorkersClass{
-						MachineDeployments: []clusterv1.MachineDeploymentClass{
-							{
-								Class: "aa",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Metadata:       clusterv1.ObjectMeta{},
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: ref},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-								},
-							},
-						},
-					},
-				},
-			},
-			expectErr: false,
-		},
-		{
-			name: "update pass if infrastructure changes in a compatible way",
-			old: &clusterv1.ClusterClass{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "default",
-				},
-				Spec: clusterv1.ClusterClassSpec{
-					Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-					ControlPlane: clusterv1.ControlPlaneClass{
-						LocalObjectTemplate: clusterv1.LocalObjectTemplate{Ref: ref},
-					},
-					Workers: clusterv1.WorkersClass{
-						MachineDeployments: []clusterv1.MachineDeploymentClass{
-							{
-								Class: "aa",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Metadata:       clusterv1.ObjectMeta{},
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: ref},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-								},
-							},
-						},
-					},
-				},
-			},
-			in: &clusterv1.ClusterClass{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "default",
-				},
-				Spec: clusterv1.ClusterClassSpec{
-					Infrastructure: clusterv1.LocalObjectTemplate{Ref: compatibleRef},
-					ControlPlane: clusterv1.ControlPlaneClass{
-						LocalObjectTemplate: clusterv1.LocalObjectTemplate{Ref: ref},
-					},
-					Workers: clusterv1.WorkersClass{
-						MachineDeployments: []clusterv1.MachineDeploymentClass{
-							{
-								Class: "aa",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Metadata:       clusterv1.ObjectMeta{},
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: ref},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-								},
-							},
-						},
-					},
-				},
-			},
-			expectErr: false,
-		},
-		{
-			name: "update fails if infrastructure changes in an incompatible way",
-			old: &clusterv1.ClusterClass{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "default",
-				},
-				Spec: clusterv1.ClusterClassSpec{
-					Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-					ControlPlane: clusterv1.ControlPlaneClass{
-						LocalObjectTemplate: clusterv1.LocalObjectTemplate{Ref: ref},
-					},
-					Workers: clusterv1.WorkersClass{
-						MachineDeployments: []clusterv1.MachineDeploymentClass{
-							{
-								Class: "aa",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Metadata:       clusterv1.ObjectMeta{},
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: ref},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-								},
-							},
-						},
-					},
-				},
-			},
-			in: &clusterv1.ClusterClass{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "default",
-				},
-				Spec: clusterv1.ClusterClassSpec{
-					Infrastructure: clusterv1.LocalObjectTemplate{Ref: incompatibleRef},
-					ControlPlane: clusterv1.ControlPlaneClass{
-						LocalObjectTemplate: clusterv1.LocalObjectTemplate{Ref: ref},
-					},
-					Workers: clusterv1.WorkersClass{
-						MachineDeployments: []clusterv1.MachineDeploymentClass{
-							{
-								Class: "aa",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Metadata:       clusterv1.ObjectMeta{},
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: ref},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-								},
-							},
-						},
-					},
-				},
-			},
-			expectErr: true,
-		},
-		{
-			name: "update pass if controlPlane changes in a compatible way",
-			old: &clusterv1.ClusterClass{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "default",
-				},
-				Spec: clusterv1.ClusterClassSpec{
-					Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-					ControlPlane: clusterv1.ControlPlaneClass{
-						LocalObjectTemplate:   clusterv1.LocalObjectTemplate{Ref: ref},
-						MachineInfrastructure: &clusterv1.LocalObjectTemplate{Ref: ref},
-					},
-					Workers: clusterv1.WorkersClass{
-						MachineDeployments: []clusterv1.MachineDeploymentClass{
-							{
-								Class: "aa",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Metadata:       clusterv1.ObjectMeta{},
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: ref},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-								},
-							},
-						},
-					},
-				},
-			},
-			in: &clusterv1.ClusterClass{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "default",
-				},
-				Spec: clusterv1.ClusterClassSpec{
-					Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-					ControlPlane: clusterv1.ControlPlaneClass{
-						Metadata: clusterv1.ObjectMeta{
-							Labels:      map[string]string{"foo": "bar"},
-							Annotations: map[string]string{"foo": "bar"},
-						},
-						LocalObjectTemplate:   clusterv1.LocalObjectTemplate{Ref: compatibleRef},
-						MachineInfrastructure: &clusterv1.LocalObjectTemplate{Ref: compatibleRef},
-					},
-					Workers: clusterv1.WorkersClass{
-						MachineDeployments: []clusterv1.MachineDeploymentClass{
-							{
-								Class: "aa",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Metadata:       clusterv1.ObjectMeta{},
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: ref},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-								},
-							},
-						},
-					},
-				},
-			},
-			expectErr: false,
-		},
-		{
-			name: "update fails if controlPlane changes in an incompatible way (control plane template)",
-			old: &clusterv1.ClusterClass{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "default",
-				},
-				Spec: clusterv1.ClusterClassSpec{
-					Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-					ControlPlane: clusterv1.ControlPlaneClass{
-						LocalObjectTemplate: clusterv1.LocalObjectTemplate{Ref: ref},
-					},
-					Workers: clusterv1.WorkersClass{
-						MachineDeployments: []clusterv1.MachineDeploymentClass{
-							{
-								Class: "aa",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Metadata:       clusterv1.ObjectMeta{},
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: ref},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-								},
-							},
-						},
-					},
-				},
-			},
-			in: &clusterv1.ClusterClass{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "default",
-				},
-				Spec: clusterv1.ClusterClassSpec{
-					Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-					ControlPlane: clusterv1.ControlPlaneClass{
-						LocalObjectTemplate: clusterv1.LocalObjectTemplate{Ref: incompatibleRef},
-					},
-					Workers: clusterv1.WorkersClass{
-						MachineDeployments: []clusterv1.MachineDeploymentClass{
-							{
-								Class: "aa",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Metadata:       clusterv1.ObjectMeta{},
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: ref},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-								},
-							},
-						},
-					},
-				},
-			},
-			expectErr: true,
-		},
-		{
-			name: "update fails if controlPlane changes in an incompatible way (control plane infrastructure machine template)",
-			old: &clusterv1.ClusterClass{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "default",
-				},
-				Spec: clusterv1.ClusterClassSpec{
-					Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-					ControlPlane: clusterv1.ControlPlaneClass{
-						LocalObjectTemplate:   clusterv1.LocalObjectTemplate{Ref: ref},
-						MachineInfrastructure: &clusterv1.LocalObjectTemplate{Ref: ref},
-					},
-					Workers: clusterv1.WorkersClass{
-						MachineDeployments: []clusterv1.MachineDeploymentClass{
-							{
-								Class: "aa",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Metadata:       clusterv1.ObjectMeta{},
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: ref},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-								},
-							},
-						},
-					},
-				},
-			},
-			in: &clusterv1.ClusterClass{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "default",
-				},
-				Spec: clusterv1.ClusterClassSpec{
-					Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-					ControlPlane: clusterv1.ControlPlaneClass{
-						LocalObjectTemplate:   clusterv1.LocalObjectTemplate{Ref: ref},
-						MachineInfrastructure: &clusterv1.LocalObjectTemplate{Ref: incompatibleRef},
-					},
-					Workers: clusterv1.WorkersClass{
-						MachineDeployments: []clusterv1.MachineDeploymentClass{
-							{
-								Class: "aa",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Metadata:       clusterv1.ObjectMeta{},
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: ref},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-								},
-							},
-						},
-					},
-				},
-			},
-			expectErr: true,
-		},
-		{
-			name: "update pass if a machine deployment changes in a compatible way",
-			old: &clusterv1.ClusterClass{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "default",
-				},
-				Spec: clusterv1.ClusterClassSpec{
-					Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-					ControlPlane: clusterv1.ControlPlaneClass{
-						LocalObjectTemplate: clusterv1.LocalObjectTemplate{Ref: ref},
-					},
-					Workers: clusterv1.WorkersClass{
-						MachineDeployments: []clusterv1.MachineDeploymentClass{
-							{
-								Class: "aa",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Metadata:       clusterv1.ObjectMeta{},
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: ref},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-								},
-							},
-						},
-					},
-				},
-			},
-			in: &clusterv1.ClusterClass{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "default",
-				},
-				Spec: clusterv1.ClusterClassSpec{
-					Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-					ControlPlane: clusterv1.ControlPlaneClass{
-						LocalObjectTemplate: clusterv1.LocalObjectTemplate{Ref: ref},
-					},
-					Workers: clusterv1.WorkersClass{
-						MachineDeployments: []clusterv1.MachineDeploymentClass{
-							{
-								Class: "aa",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Metadata: clusterv1.ObjectMeta{
-										Labels:      map[string]string{"foo": "bar"},
-										Annotations: map[string]string{"foo": "bar"},
-									},
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: incompatibleRef}, // NOTE: this should be tolerated
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: compatibleRef},
-								},
-							},
-						},
-					},
-				},
-			},
-			expectErr: false,
-		},
-		{
-			name: "update fails a machine deployment changes in an incompatible way",
-			old: &clusterv1.ClusterClass{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "default",
-				},
-				Spec: clusterv1.ClusterClassSpec{
-					Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-					ControlPlane: clusterv1.ControlPlaneClass{
-						LocalObjectTemplate: clusterv1.LocalObjectTemplate{Ref: ref},
-					},
-					Workers: clusterv1.WorkersClass{
-						MachineDeployments: []clusterv1.MachineDeploymentClass{
-							{
-								Class: "aa",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Metadata:       clusterv1.ObjectMeta{},
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: ref},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-								},
-							},
-						},
-					},
-				},
-			},
-			in: &clusterv1.ClusterClass{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "default",
-				},
-				Spec: clusterv1.ClusterClassSpec{
-					Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-					ControlPlane: clusterv1.ControlPlaneClass{
-						LocalObjectTemplate: clusterv1.LocalObjectTemplate{Ref: ref},
-					},
-					Workers: clusterv1.WorkersClass{
-						MachineDeployments: []clusterv1.MachineDeploymentClass{
-							{
-								Class: "aa",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Metadata:       clusterv1.ObjectMeta{},
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: ref},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: incompatibleRef},
-								},
-							},
-						},
-					},
-				},
-			},
-			expectErr: true,
-		},
-		{
-			name: "update pass if a machine deployment class gets added",
-			old: &clusterv1.ClusterClass{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "default",
-				},
-				Spec: clusterv1.ClusterClassSpec{
-					Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-					ControlPlane: clusterv1.ControlPlaneClass{
-						LocalObjectTemplate: clusterv1.LocalObjectTemplate{Ref: ref},
-					},
-					Workers: clusterv1.WorkersClass{
-						MachineDeployments: []clusterv1.MachineDeploymentClass{
-							{
-								Class: "aa",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: ref},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-								},
-							},
-						},
-					},
-				},
-			},
-			in: &clusterv1.ClusterClass{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "default",
-				},
-				Spec: clusterv1.ClusterClassSpec{
-					Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-					ControlPlane: clusterv1.ControlPlaneClass{
-						LocalObjectTemplate: clusterv1.LocalObjectTemplate{Ref: ref},
-					},
-					Workers: clusterv1.WorkersClass{
-						MachineDeployments: []clusterv1.MachineDeploymentClass{
-							{
-								Class: "aa",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: ref},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-								},
-							},
-							{
-								Class: "bb",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: ref},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-								},
-							},
-						},
-					},
-				},
-			},
-			expectErr: false,
-		},
-		{
-			name: "update fails if a duplicated deployment class gets added",
-			old: &clusterv1.ClusterClass{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "default",
-				},
-				Spec: clusterv1.ClusterClassSpec{
-					Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-					ControlPlane: clusterv1.ControlPlaneClass{
-						LocalObjectTemplate: clusterv1.LocalObjectTemplate{Ref: ref},
-					},
-					Workers: clusterv1.WorkersClass{
-						MachineDeployments: []clusterv1.MachineDeploymentClass{
-							{
-								Class: "aa",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: ref},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-								},
-							},
-						},
-					},
-				},
-			},
-			in: &clusterv1.ClusterClass{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "default",
-				},
-				Spec: clusterv1.ClusterClassSpec{
-					Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-					ControlPlane: clusterv1.ControlPlaneClass{
-						LocalObjectTemplate: clusterv1.LocalObjectTemplate{Ref: ref},
-					},
-					Workers: clusterv1.WorkersClass{
-						MachineDeployments: []clusterv1.MachineDeploymentClass{
-							{
-								Class: "aa",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: ref},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-								},
-							},
-							{
-								Class: "aa",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: ref},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-								},
-							},
-						},
-					},
-				},
-			},
-			expectErr: true,
-		},
-		{
-			name: "update fails if a machine deployment class gets removed",
-			old: &clusterv1.ClusterClass{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "default",
-				},
-				Spec: clusterv1.ClusterClassSpec{
-					Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-					ControlPlane: clusterv1.ControlPlaneClass{
-						LocalObjectTemplate: clusterv1.LocalObjectTemplate{Ref: ref},
-					},
-					Workers: clusterv1.WorkersClass{
-						MachineDeployments: []clusterv1.MachineDeploymentClass{
-							{
-								Class: "aa",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: ref},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-								},
-							},
-							{
-								Class: "bb",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: ref},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-								},
-							},
-						},
-					},
-				},
-			},
-			in: &clusterv1.ClusterClass{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "default",
-				},
-				Spec: clusterv1.ClusterClassSpec{
-					Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-					ControlPlane: clusterv1.ControlPlaneClass{
-						LocalObjectTemplate: clusterv1.LocalObjectTemplate{Ref: ref},
-					},
-					Workers: clusterv1.WorkersClass{
-						MachineDeployments: []clusterv1.MachineDeploymentClass{
-							{
-								Class: "aa",
-								Template: clusterv1.MachineDeploymentClassTemplate{
-									Bootstrap:      clusterv1.LocalObjectTemplate{Ref: ref},
-									Infrastructure: clusterv1.LocalObjectTemplate{Ref: ref},
-								},
-							},
-						},
-					},
-				},
-			},
-			expectErr: true,
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			g := NewWithT(t)
-			webhook := &ClusterClass{}
-			if tt.expectErr {
-				g.Expect(webhook.validate(tt.old, tt.in)).NotTo(Succeed())
-			} else {
-				g.Expect(webhook.validate(tt.old, tt.in)).To(Succeed())
-			}
-		})
-	}
-}