Repository: kubernetes-sigs/cluster-api
Pull request #7991 (2023-01-24 18:18:48)

:seedling: Add ClusterClass variables to status on reconcile
Add variables from the ClusterClass `.spec` to its `.status` field on each reconcile. This change is the foundation for introducing external variables which can be kept in sync in the ClusterClass status.

Part of #7985 


killianmuldoon: /remove-hold
k8s-ci-robot: PR needs rebase.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>
sbueringer: @killianmuldoon Please rebase, when you have some time
killianmuldoon: > >This PR contains a couple of necessary commits from other PRs, so it should not be merged until they're also merged.

> I assume this is not the case anymore?

Yeah - those PRs are now merged.
killianmuldoon: /remove-hold

Underlying PRs are now merged.
k8s-ci-robot: @killianmuldoon: The following test **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-cluster-api-e2e-informing-ipv6-main | f8ac252b9d6cd6bd46fff09bace87be9e1bd05d6 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_cluster-api/7991/pull-cluster-api-e2e-informing-ipv6-main/1620121642313191424) | false | `/test pull-cluster-api-e2e-informing-ipv6-main`

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

<details>

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

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

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

<details>

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

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-cluster-api-e2e-main | 15c89dd24fc5e8c24ab9e6cb026032f14083b76b | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_cluster-api/7991/pull-cluster-api-e2e-main/1620370002643259392) | true | `/test pull-cluster-api-e2e-main`

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

<details>

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

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

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

<details>

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

Patch:
diff --git a/.github/workflows/dependabot.yml b/.github/workflows/dependabot.yml
index 5a28cecd6..574547bac 100644
--- a/.github/workflows/dependabot.yml
+++ b/.github/workflows/dependabot.yml
@@ -26,7 +26,7 @@ jobs:
       uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # tag=v3.5.0
       with:
         go-version: ${{ steps.vars.outputs.go_version }}
-    - uses: actions/cache@627f0f41f6904a5b1efbaed9f96d9eb58e92e920 # tag=v3.2.4
+    - uses: actions/cache@58c146cc91c5b9e778e71775dfe9bf1442ad9a12 # tag=v3.2.3
       name: Restore go cache
       with:
         path: |
diff --git a/Makefile b/Makefile
index dc475c56c..2c94b6bda 100644
--- a/Makefile
+++ b/Makefile
@@ -111,7 +111,7 @@ SETUP_ENVTEST_BIN := setup-envtest
 SETUP_ENVTEST := $(abspath $(TOOLS_BIN_DIR)/$(SETUP_ENVTEST_BIN)-$(SETUP_ENVTEST_VER))
 SETUP_ENVTEST_PKG := sigs.k8s.io/controller-runtime/tools/setup-envtest
 
-CONTROLLER_GEN_VER := v0.11.2
+CONTROLLER_GEN_VER := v0.11.1
 CONTROLLER_GEN_BIN := controller-gen
 CONTROLLER_GEN := $(abspath $(TOOLS_BIN_DIR)/$(CONTROLLER_GEN_BIN)-$(CONTROLLER_GEN_VER))
 CONTROLLER_GEN_PKG := sigs.k8s.io/controller-tools/cmd/controller-gen
diff --git a/api/v1beta1/clusterclass_types.go b/api/v1beta1/clusterclass_types.go
index d9ecb15ae..dccf1110b 100644
--- a/api/v1beta1/clusterclass_types.go
+++ b/api/v1beta1/clusterclass_types.go
@@ -553,6 +553,10 @@ type LocalObjectTemplate struct {
 
 // ClusterClassStatus defines the observed state of the ClusterClass.
 type ClusterClassStatus struct {
+	// Variables is a list of ClusterClassStatusVariable that are defined for the ClusterClass.
+	// +optional
+	Variables []ClusterClassStatusVariable `json:"variables,omitempty"`
+
 	// Conditions defines current observed state of the ClusterClass.
 	// +optional
 	Conditions Conditions `json:"conditions,omitempty"`
@@ -562,6 +566,36 @@ type ClusterClassStatus struct {
 	ObservedGeneration int64 `json:"observedGeneration,omitempty"`
 }
 
+// ClusterClassStatusVariable defines a variable which appears in the status of a ClusterClass.
+type ClusterClassStatusVariable struct {
+	// Name is the name of the variable.
+	Name string `json:"name"`
+
+	// DefintionsConflict specifies whether or not there are conflicting definitions for a single variable name.
+	// +optional
+	DefintionsConflict bool `json:"defintionsConflict,omitempty"`
+
+	// Definitions is a list of definitions for a variable.
+	Definitions []ClusterClassStatusVariableDefinition `json:"definitions"`
+}
+
+// ClusterClassStatusVariableDefinition defines a variable which appears in the status of a ClusterClass.
+type ClusterClassStatusVariableDefinition struct {
+	// From specifies the origin of the variable definition.
+	// This will be `inline` for variables defined in the ClusterClass or the name of a patch defined in the ClusterClass
+	// for variables discovered from a DiscoverVariables runtime extensions.
+	From string `json:"from"`
+
+	// Required specifies if the variable is required.
+	// Note: this applies to the variable as a whole and thus the
+	// top-level object defined in the schema. If nested fields are
+	// required, this will be specified inside the schema.
+	Required bool `json:"required"`
+
+	// Schema defines the schema of the variable.
+	Schema VariableSchema `json:"schema"`
+}
+
 // GetConditions returns the set of conditions for this object.
 func (c *ClusterClass) GetConditions() Conditions {
 	return c.Status.Conditions
diff --git a/api/v1beta1/common_types.go b/api/v1beta1/common_types.go
index d505d1420..3db08c79a 100644
--- a/api/v1beta1/common_types.go
+++ b/api/v1beta1/common_types.go
@@ -120,6 +120,10 @@ const (
 	// instead of being a source of truth for eventual consistency.
 	// This annotation can be used to inform MachinePool status during in-progress scaling scenarios.
 	ReplicasManagedByAnnotation = "cluster.x-k8s.io/replicas-managed-by"
+
+	// VariableDefinitionFromInline indicates a patch or variable was defined in the `.spec` of a ClusterClass
+	// rather than from an external patch extension.
+	VariableDefinitionFromInline = "inline"
 )
 
 const (
diff --git a/api/v1beta1/condition_consts.go b/api/v1beta1/condition_consts.go
index 5c100e779..559f4df34 100644
--- a/api/v1beta1/condition_consts.go
+++ b/api/v1beta1/condition_consts.go
@@ -59,9 +59,9 @@ const (
 
 const (
 	// ControlPlaneInitializedCondition reports if the cluster's control plane has been initialized such that the
-	// cluster's apiserver is reachable. If no Control Plane provider is in use this condition reports that at least one
-	// control plane Machine has a node reference. Once this Condition is marked true, its value is never changed. See
-	// the ControlPlaneReady condition for an indication of the current readiness of the cluster's control plane.
+	// cluster's apiserver is reachable and at least one control plane Machine has a node reference. Once this
+	// condition is marked true, its value is never changed. See the ControlPlaneReady condition for an indication of
+	// the current readiness of the cluster's control plane.
 	ControlPlaneInitializedCondition ConditionType = "ControlPlaneInitialized"
 
 	// MissingNodeRefReason (Severity=Info) documents a cluster waiting for at least one control plane Machine to have
diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go
index 7f8125f44..bbc33e05c 100644
--- a/api/v1beta1/zz_generated.deepcopy.go
+++ b/api/v1beta1/zz_generated.deepcopy.go
@@ -223,6 +223,13 @@ func (in *ClusterClassSpec) DeepCopy() *ClusterClassSpec {
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *ClusterClassStatus) DeepCopyInto(out *ClusterClassStatus) {
 	*out = *in
+	if in.Variables != nil {
+		in, out := &in.Variables, &out.Variables
+		*out = make([]ClusterClassStatusVariable, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
 	if in.Conditions != nil {
 		in, out := &in.Conditions, &out.Conditions
 		*out = make(Conditions, len(*in))
@@ -242,6 +249,44 @@ func (in *ClusterClassStatus) DeepCopy() *ClusterClassStatus {
 	return out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ClusterClassStatusVariable) DeepCopyInto(out *ClusterClassStatusVariable) {
+	*out = *in
+	if in.Definitions != nil {
+		in, out := &in.Definitions, &out.Definitions
+		*out = make([]ClusterClassStatusVariableDefinition, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterClassStatusVariable.
+func (in *ClusterClassStatusVariable) DeepCopy() *ClusterClassStatusVariable {
+	if in == nil {
+		return nil
+	}
+	out := new(ClusterClassStatusVariable)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ClusterClassStatusVariableDefinition) DeepCopyInto(out *ClusterClassStatusVariableDefinition) {
+	*out = *in
+	in.Schema.DeepCopyInto(&out.Schema)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterClassStatusVariableDefinition.
+func (in *ClusterClassStatusVariableDefinition) DeepCopy() *ClusterClassStatusVariableDefinition {
+	if in == nil {
+		return nil
+	}
+	out := new(ClusterClassStatusVariableDefinition)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *ClusterClassVariable) DeepCopyInto(out *ClusterClassVariable) {
 	*out = *in
diff --git a/api/v1beta1/zz_generated.openapi.go b/api/v1beta1/zz_generated.openapi.go
index a496c284f..0201f375e 100644
--- a/api/v1beta1/zz_generated.openapi.go
+++ b/api/v1beta1/zz_generated.openapi.go
@@ -38,6 +38,8 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
 		"sigs.k8s.io/cluster-api/api/v1beta1.ClusterClassPatch":                        schema_sigsk8sio_cluster_api_api_v1beta1_ClusterClassPatch(ref),
 		"sigs.k8s.io/cluster-api/api/v1beta1.ClusterClassSpec":                         schema_sigsk8sio_cluster_api_api_v1beta1_ClusterClassSpec(ref),
 		"sigs.k8s.io/cluster-api/api/v1beta1.ClusterClassStatus":                       schema_sigsk8sio_cluster_api_api_v1beta1_ClusterClassStatus(ref),
+		"sigs.k8s.io/cluster-api/api/v1beta1.ClusterClassStatusVariable":               schema_sigsk8sio_cluster_api_api_v1beta1_ClusterClassStatusVariable(ref),
+		"sigs.k8s.io/cluster-api/api/v1beta1.ClusterClassStatusVariableDefinition":     schema_sigsk8sio_cluster_api_api_v1beta1_ClusterClassStatusVariableDefinition(ref),
 		"sigs.k8s.io/cluster-api/api/v1beta1.ClusterClassVariable":                     schema_sigsk8sio_cluster_api_api_v1beta1_ClusterClassVariable(ref),
 		"sigs.k8s.io/cluster-api/api/v1beta1.ClusterList":                              schema_sigsk8sio_cluster_api_api_v1beta1_ClusterList(ref),
 		"sigs.k8s.io/cluster-api/api/v1beta1.ClusterNetwork":                           schema_sigsk8sio_cluster_api_api_v1beta1_ClusterNetwork(ref),
@@ -423,6 +425,20 @@ func schema_sigsk8sio_cluster_api_api_v1beta1_ClusterClassStatus(ref common.Refe
 				Description: "ClusterClassStatus defines the observed state of the ClusterClass.",
 				Type:        []string{"object"},
 				Properties: map[string]spec.Schema{
+					"variables": {
+						SchemaProps: spec.SchemaProps{
+							Description: "Variables is a list of ClusterClassStatusVariable that are defined for the ClusterClass.",
+							Type:        []string{"array"},
+							Items: &spec.SchemaOrArray{
+								Schema: &spec.Schema{
+									SchemaProps: spec.SchemaProps{
+										Default: map[string]interface{}{},
+										Ref:     ref("sigs.k8s.io/cluster-api/api/v1beta1.ClusterClassStatusVariable"),
+									},
+								},
+							},
+						},
+					},
 					"conditions": {
 						SchemaProps: spec.SchemaProps{
 							Description: "Conditions defines current observed state of the ClusterClass.",
@@ -448,7 +464,91 @@ func schema_sigsk8sio_cluster_api_api_v1beta1_ClusterClassStatus(ref common.Refe
 			},
 		},
 		Dependencies: []string{
-			"sigs.k8s.io/cluster-api/api/v1beta1.Condition"},
+			"sigs.k8s.io/cluster-api/api/v1beta1.ClusterClassStatusVariable", "sigs.k8s.io/cluster-api/api/v1beta1.Condition"},
+	}
+}
+
+func schema_sigsk8sio_cluster_api_api_v1beta1_ClusterClassStatusVariable(ref common.ReferenceCallback) common.OpenAPIDefinition {
+	return common.OpenAPIDefinition{
+		Schema: spec.Schema{
+			SchemaProps: spec.SchemaProps{
+				Description: "ClusterClassStatusVariable defines a variable which appears in the status of a ClusterClass.",
+				Type:        []string{"object"},
+				Properties: map[string]spec.Schema{
+					"name": {
+						SchemaProps: spec.SchemaProps{
+							Description: "Name is the name of the variable.",
+							Default:     "",
+							Type:        []string{"string"},
+							Format:      "",
+						},
+					},
+					"defintionsConflict": {
+						SchemaProps: spec.SchemaProps{
+							Description: "DefintionsConflict specifies whether or not there are conflicting definitions for a single variable name.",
+							Type:        []string{"boolean"},
+							Format:      "",
+						},
+					},
+					"definitions": {
+						SchemaProps: spec.SchemaProps{
+							Description: "Definitions is a list of definitions for a variable.",
+							Type:        []string{"array"},
+							Items: &spec.SchemaOrArray{
+								Schema: &spec.Schema{
+									SchemaProps: spec.SchemaProps{
+										Default: map[string]interface{}{},
+										Ref:     ref("sigs.k8s.io/cluster-api/api/v1beta1.ClusterClassStatusVariableDefinition"),
+									},
+								},
+							},
+						},
+					},
+				},
+				Required: []string{"name", "definitions"},
+			},
+		},
+		Dependencies: []string{
+			"sigs.k8s.io/cluster-api/api/v1beta1.ClusterClassStatusVariableDefinition"},
+	}
+}
+
+func schema_sigsk8sio_cluster_api_api_v1beta1_ClusterClassStatusVariableDefinition(ref common.ReferenceCallback) common.OpenAPIDefinition {
+	return common.OpenAPIDefinition{
+		Schema: spec.Schema{
+			SchemaProps: spec.SchemaProps{
+				Description: "ClusterClassStatusVariableDefinition defines a variable which appears in the status of a ClusterClass.",
+				Type:        []string{"object"},
+				Properties: map[string]spec.Schema{
+					"from": {
+						SchemaProps: spec.SchemaProps{
+							Description: "From specifies the origin of the variable definition. This will be `inline` for variables defined in the ClusterClass or the name of a patch defined in the ClusterClass for variables discovered from a DiscoverVariables runtime extensions.",
+							Default:     "",
+							Type:        []string{"string"},
+							Format:      "",
+						},
+					},
+					"required": {
+						SchemaProps: spec.SchemaProps{
+							Description: "Required specifies if the variable is required. Note: this applies to the variable as a whole and thus the top-level object defined in the schema. If nested fields are required, this will be specified inside the schema.",
+							Default:     false,
+							Type:        []string{"boolean"},
+							Format:      "",
+						},
+					},
+					"schema": {
+						SchemaProps: spec.SchemaProps{
+							Description: "Schema defines the schema of the variable.",
+							Default:     map[string]interface{}{},
+							Ref:         ref("sigs.k8s.io/cluster-api/api/v1beta1.VariableSchema"),
+						},
+					},
+				},
+				Required: []string{"from", "required", "schema"},
+			},
+		},
+		Dependencies: []string{
+			"sigs.k8s.io/cluster-api/api/v1beta1.VariableSchema"},
 	}
 }
 
diff --git a/bootstrap/kubeadm/api/v1alpha3/conversion.go b/bootstrap/kubeadm/api/v1alpha3/conversion.go
index 5a73bb040..ed148fa86 100644
--- a/bootstrap/kubeadm/api/v1alpha3/conversion.go
+++ b/bootstrap/kubeadm/api/v1alpha3/conversion.go
@@ -79,20 +79,6 @@ func (src *KubeadmConfig) ConvertTo(dstRaw conversion.Hub) error {
 		dst.Spec.JoinConfiguration.SkipPhases = restored.Spec.JoinConfiguration.SkipPhases
 	}
 
-	if restored.Spec.JoinConfiguration != nil && restored.Spec.JoinConfiguration.NodeRegistration.ImagePullPolicy != "" {
-		if dst.Spec.JoinConfiguration == nil {
-			dst.Spec.JoinConfiguration = &bootstrapv1.JoinConfiguration{}
-		}
-		dst.Spec.JoinConfiguration.NodeRegistration.ImagePullPolicy = restored.Spec.JoinConfiguration.NodeRegistration.ImagePullPolicy
-	}
-
-	if restored.Spec.InitConfiguration != nil && restored.Spec.InitConfiguration.NodeRegistration.ImagePullPolicy != "" {
-		if dst.Spec.InitConfiguration == nil {
-			dst.Spec.InitConfiguration = &bootstrapv1.InitConfiguration{}
-		}
-		dst.Spec.InitConfiguration.NodeRegistration.ImagePullPolicy = restored.Spec.InitConfiguration.NodeRegistration.ImagePullPolicy
-	}
-
 	return nil
 }
 
@@ -177,20 +163,6 @@ func (src *KubeadmConfigTemplate) ConvertTo(dstRaw conversion.Hub) error {
 		dst.Spec.Template.Spec.JoinConfiguration.SkipPhases = restored.Spec.Template.Spec.JoinConfiguration.SkipPhases
 	}
 
-	if restored.Spec.Template.Spec.JoinConfiguration != nil && restored.Spec.Template.Spec.JoinConfiguration.NodeRegistration.ImagePullPolicy != "" {
-		if dst.Spec.Template.Spec.JoinConfiguration == nil {
-			dst.Spec.Template.Spec.JoinConfiguration = &bootstrapv1.JoinConfiguration{}
-		}
-		dst.Spec.Template.Spec.JoinConfiguration.NodeRegistration.ImagePullPolicy = restored.Spec.Template.Spec.JoinConfiguration.NodeRegistration.ImagePullPolicy
-	}
-
-	if restored.Spec.Template.Spec.InitConfiguration != nil && restored.Spec.Template.Spec.InitConfiguration.NodeRegistration.ImagePullPolicy != "" {
-		if dst.Spec.Template.Spec.InitConfiguration == nil {
-			dst.Spec.Template.Spec.InitConfiguration = &bootstrapv1.InitConfiguration{}
-		}
-		dst.Spec.Template.Spec.InitConfiguration.NodeRegistration.ImagePullPolicy = restored.Spec.Template.Spec.InitConfiguration.NodeRegistration.ImagePullPolicy
-	}
-
 	return nil
 }
 
diff --git a/bootstrap/kubeadm/api/v1alpha4/conversion.go b/bootstrap/kubeadm/api/v1alpha4/conversion.go
index bd3074d27..58ec2acb9 100644
--- a/bootstrap/kubeadm/api/v1alpha4/conversion.go
+++ b/bootstrap/kubeadm/api/v1alpha4/conversion.go
@@ -64,20 +64,6 @@ func (src *KubeadmConfig) ConvertTo(dstRaw conversion.Hub) error {
 		dst.Spec.JoinConfiguration.SkipPhases = restored.Spec.JoinConfiguration.SkipPhases
 	}
 
-	if restored.Spec.JoinConfiguration != nil && restored.Spec.JoinConfiguration.NodeRegistration.ImagePullPolicy != "" {
-		if dst.Spec.JoinConfiguration == nil {
-			dst.Spec.JoinConfiguration = &bootstrapv1.JoinConfiguration{}
-		}
-		dst.Spec.JoinConfiguration.NodeRegistration.ImagePullPolicy = restored.Spec.JoinConfiguration.NodeRegistration.ImagePullPolicy
-	}
-
-	if restored.Spec.InitConfiguration != nil && restored.Spec.InitConfiguration.NodeRegistration.ImagePullPolicy != "" {
-		if dst.Spec.InitConfiguration == nil {
-			dst.Spec.InitConfiguration = &bootstrapv1.InitConfiguration{}
-		}
-		dst.Spec.InitConfiguration.NodeRegistration.ImagePullPolicy = restored.Spec.InitConfiguration.NodeRegistration.ImagePullPolicy
-	}
-
 	return nil
 }
 
@@ -143,20 +129,6 @@ func (src *KubeadmConfigTemplate) ConvertTo(dstRaw conversion.Hub) error {
 		dst.Spec.Template.Spec.JoinConfiguration.SkipPhases = restored.Spec.Template.Spec.JoinConfiguration.SkipPhases
 	}
 
-	if restored.Spec.Template.Spec.JoinConfiguration != nil && restored.Spec.Template.Spec.JoinConfiguration.NodeRegistration.ImagePullPolicy != "" {
-		if dst.Spec.Template.Spec.JoinConfiguration == nil {
-			dst.Spec.Template.Spec.JoinConfiguration = &bootstrapv1.JoinConfiguration{}
-		}
-		dst.Spec.Template.Spec.JoinConfiguration.NodeRegistration.ImagePullPolicy = restored.Spec.Template.Spec.JoinConfiguration.NodeRegistration.ImagePullPolicy
-	}
-
-	if restored.Spec.Template.Spec.InitConfiguration != nil && restored.Spec.Template.Spec.InitConfiguration.NodeRegistration.ImagePullPolicy != "" {
-		if dst.Spec.Template.Spec.InitConfiguration == nil {
-			dst.Spec.Template.Spec.InitConfiguration = &bootstrapv1.InitConfiguration{}
-		}
-		dst.Spec.Template.Spec.InitConfiguration.NodeRegistration.ImagePullPolicy = restored.Spec.Template.Spec.InitConfiguration.NodeRegistration.ImagePullPolicy
-	}
-
 	return nil
 }
 
@@ -207,9 +179,3 @@ func Convert_v1beta1_User_To_v1alpha4_User(in *bootstrapv1.User, out *User, s ap
 	// User.PasswdFrom does not exist in kubeadm v1alpha4 API.
 	return autoConvert_v1beta1_User_To_v1alpha4_User(in, out, s)
 }
-
-func Convert_v1beta1_NodeRegistrationOptions_To_v1alpha4_NodeRegistrationOptions(in *bootstrapv1.NodeRegistrationOptions, out *NodeRegistrationOptions, s apiconversion.Scope) error {
-	// NodeRegistrationOptions.ImagePullPolicy does not exit in
-	// kubeadm v1alpha4 API.
-	return autoConvert_v1beta1_NodeRegistrationOptions_To_v1alpha4_NodeRegistrationOptions(in, out, s)
-}
diff --git a/bootstrap/kubeadm/api/v1alpha4/zz_generated.conversion.go b/bootstrap/kubeadm/api/v1alpha4/zz_generated.conversion.go
index 1ff8a4faf..8b75fbf18 100644
--- a/bootstrap/kubeadm/api/v1alpha4/zz_generated.conversion.go
+++ b/bootstrap/kubeadm/api/v1alpha4/zz_generated.conversion.go
@@ -355,6 +355,11 @@ func RegisterConversions(s *runtime.Scheme) error {
 	}); err != nil {
 		return err
 	}
+	if err := s.AddGeneratedConversionFunc((*v1beta1.NodeRegistrationOptions)(nil), (*NodeRegistrationOptions)(nil), func(a, b interface{}, scope conversion.Scope) error {
+		return Convert_v1beta1_NodeRegistrationOptions_To_v1alpha4_NodeRegistrationOptions(a.(*v1beta1.NodeRegistrationOptions), b.(*NodeRegistrationOptions), scope)
+	}); err != nil {
+		return err
+	}
 	if err := s.AddGeneratedConversionFunc((*Partition)(nil), (*v1beta1.Partition)(nil), func(a, b interface{}, scope conversion.Scope) error {
 		return Convert_v1alpha4_Partition_To_v1beta1_Partition(a.(*Partition), b.(*v1beta1.Partition), scope)
 	}); err != nil {
@@ -400,11 +405,6 @@ func RegisterConversions(s *runtime.Scheme) error {
 	}); err != nil {
 		return err
 	}
-	if err := s.AddConversionFunc((*v1beta1.NodeRegistrationOptions)(nil), (*NodeRegistrationOptions)(nil), func(a, b interface{}, scope conversion.Scope) error {
-		return Convert_v1beta1_NodeRegistrationOptions_To_v1alpha4_NodeRegistrationOptions(a.(*v1beta1.NodeRegistrationOptions), b.(*NodeRegistrationOptions), scope)
-	}); err != nil {
-		return err
-	}
 	if err := s.AddConversionFunc((*v1beta1.User)(nil), (*User)(nil), func(a, b interface{}, scope conversion.Scope) error {
 		return Convert_v1beta1_User_To_v1alpha4_User(a.(*v1beta1.User), b.(*User), scope)
 	}); err != nil {
@@ -1455,10 +1455,14 @@ func autoConvert_v1beta1_NodeRegistrationOptions_To_v1alpha4_NodeRegistrationOpt
 	out.Taints = *(*[]corev1.Taint)(unsafe.Pointer(&in.Taints))
 	out.KubeletExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.KubeletExtraArgs))
 	out.IgnorePreflightErrors = *(*[]string)(unsafe.Pointer(&in.IgnorePreflightErrors))
-	// WARNING: in.ImagePullPolicy requires manual conversion: does not exist in peer-type
 	return nil
 }
 
+// Convert_v1beta1_NodeRegistrationOptions_To_v1alpha4_NodeRegistrationOptions is an autogenerated conversion function.
+func Convert_v1beta1_NodeRegistrationOptions_To_v1alpha4_NodeRegistrationOptions(in *v1beta1.NodeRegistrationOptions, out *NodeRegistrationOptions, s conversion.Scope) error {
+	return autoConvert_v1beta1_NodeRegistrationOptions_To_v1alpha4_NodeRegistrationOptions(in, out, s)
+}
+
 func autoConvert_v1alpha4_Partition_To_v1beta1_Partition(in *Partition, out *v1beta1.Partition, s conversion.Scope) error {
 	out.Device = in.Device
 	out.Layout = in.Layout
diff --git a/bootstrap/kubeadm/api/v1beta1/kubeadm_types.go b/bootstrap/kubeadm/api/v1beta1/kubeadm_types.go
index f54df29dd..dc2bb6087 100644
--- a/bootstrap/kubeadm/api/v1beta1/kubeadm_types.go
+++ b/bootstrap/kubeadm/api/v1beta1/kubeadm_types.go
@@ -251,16 +251,6 @@ type NodeRegistrationOptions struct {
 	// IgnorePreflightErrors provides a slice of pre-flight errors to be ignored when the current node is registered.
 	// +optional
 	IgnorePreflightErrors []string `json:"ignorePreflightErrors,omitempty"`
-
-	// ImagePullPolicy specifies the policy for image pulling
-	// during kubeadm "init" and "join" operations. The value of
-	// this field must be one of "Always", "IfNotPresent" or
-	// "Never". Defaults to "IfNotPresent". This can be used only
-	// with Kubernetes version equal to 1.22 and later.
-	// +kubebuilder:validation:Enum=Always;IfNotPresent;Never
-	// +kubebuilder:default=IfNotPresent
-	// +optional
-	ImagePullPolicy string `json:"imagePullPolicy,omitempty"`
 }
 
 // MarshalJSON marshals NodeRegistrationOptions in a way that an empty slice in Taints is preserved.
@@ -281,14 +271,12 @@ func (n *NodeRegistrationOptions) MarshalJSON() ([]byte, error) {
 			Taints                []corev1.Taint    `json:"taints"`
 			KubeletExtraArgs      map[string]string `json:"kubeletExtraArgs,omitempty"`
 			IgnorePreflightErrors []string          `json:"ignorePreflightErrors,omitempty"`
-			ImagePullPolicy       string            `json:"imagePullPolicy,omitempty"`
 		}{
 			Name:                  n.Name,
 			CRISocket:             n.CRISocket,
 			Taints:                n.Taints,
 			KubeletExtraArgs:      n.KubeletExtraArgs,
 			IgnorePreflightErrors: n.IgnorePreflightErrors,
-			ImagePullPolicy:       n.ImagePullPolicy,
 		})
 	}
 
@@ -299,14 +287,12 @@ func (n *NodeRegistrationOptions) MarshalJSON() ([]byte, error) {
 		Taints                []corev1.Taint    `json:"taints,omitempty"`
 		KubeletExtraArgs      map[string]string `json:"kubeletExtraArgs,omitempty"`
 		IgnorePreflightErrors []string          `json:"ignorePreflightErrors,omitempty"`
-		ImagePullPolicy       string            `json:"imagePullPolicy,omitempty"`
 	}{
 		Name:                  n.Name,
 		CRISocket:             n.CRISocket,
 		Taints:                n.Taints,
 		KubeletExtraArgs:      n.KubeletExtraArgs,
 		IgnorePreflightErrors: n.IgnorePreflightErrors,
-		ImagePullPolicy:       n.ImagePullPolicy,
 	})
 }
 
diff --git a/bootstrap/kubeadm/config/crd/bases/bootstrap.cluster.x-k8s.io_kubeadmconfigs.yaml b/bootstrap/kubeadm/config/crd/bases/bootstrap.cluster.x-k8s.io_kubeadmconfigs.yaml
index 33a1b3ca5..6075efcf9 100644
--- a/bootstrap/kubeadm/config/crd/bases/bootstrap.cluster.x-k8s.io_kubeadmconfigs.yaml
+++ b/bootstrap/kubeadm/config/crd/bases/bootstrap.cluster.x-k8s.io_kubeadmconfigs.yaml
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   annotations:
-    controller-gen.kubebuilder.io/version: v0.11.2
+    controller-gen.kubebuilder.io/version: v0.11.1
   creationTimestamp: null
   name: kubeadmconfigs.bootstrap.cluster.x-k8s.io
 spec:
@@ -2598,18 +2598,6 @@ spec:
                         items:
                           type: string
                         type: array
-                      imagePullPolicy:
-                        default: IfNotPresent
-                        description: ImagePullPolicy specifies the policy for image
-                          pulling during kubeadm "init" and "join" operations. The
-                          value of this field must be one of "Always", "IfNotPresent"
-                          or "Never". Defaults to "IfNotPresent". This can be used
-                          only with Kubernetes version equal to 1.22 and later.
-                        enum:
-                        - Always
-                        - IfNotPresent
-                        - Never
-                        type: string
                       kubeletExtraArgs:
                         additionalProperties:
                           type: string
@@ -2821,18 +2809,6 @@ spec:
                         items:
                           type: string
                         type: array
-                      imagePullPolicy:
-                        default: IfNotPresent
-                        description: ImagePullPolicy specifies the policy for image
-                          pulling during kubeadm "init" and "join" operations. The
-                          value of this field must be one of "Always", "IfNotPresent"
-                          or "Never". Defaults to "IfNotPresent". This can be used
-                          only with Kubernetes version equal to 1.22 and later.
-                        enum:
-                        - Always
-                        - IfNotPresent
-                        - Never
-                        type: string
                       kubeletExtraArgs:
                         additionalProperties:
                           type: string
diff --git a/bootstrap/kubeadm/config/crd/bases/bootstrap.cluster.x-k8s.io_kubeadmconfigtemplates.yaml b/bootstrap/kubeadm/config/crd/bases/bootstrap.cluster.x-k8s.io_kubeadmconfigtemplates.yaml
index 7eafff7b1..b2737f59b 100644
--- a/bootstrap/kubeadm/config/crd/bases/bootstrap.cluster.x-k8s.io_kubeadmconfigtemplates.yaml
+++ b/bootstrap/kubeadm/config/crd/bases/bootstrap.cluster.x-k8s.io_kubeadmconfigtemplates.yaml
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   annotations:
-    controller-gen.kubebuilder.io/version: v0.11.2
+    controller-gen.kubebuilder.io/version: v0.11.1
   creationTimestamp: null
   name: kubeadmconfigtemplates.bootstrap.cluster.x-k8s.io
 spec:
@@ -2626,19 +2626,6 @@ spec:
                                 items:
                                   type: string
                                 type: array
-                              imagePullPolicy:
-                                default: IfNotPresent
-                                description: ImagePullPolicy specifies the policy
-                                  for image pulling during kubeadm "init" and "join"
-                                  operations. The value of this field must be one
-                                  of "Always", "IfNotPresent" or "Never". Defaults
-                                  to "IfNotPresent". This can be used only with Kubernetes
-                                  version equal to 1.22 and later.
-                                enum:
-                                - Always
-                                - IfNotPresent
-                                - Never
-                                type: string
                               kubeletExtraArgs:
                                 additionalProperties:
                                   type: string
@@ -2865,19 +2852,6 @@ spec:
                                 items:
                                   type: string
                                 type: array
-                              imagePullPolicy:
-                                default: IfNotPresent
-                                description: ImagePullPolicy specifies the policy
-                                  for image pulling during kubeadm "init" and "join"
-                                  operations. The value of this field must be one
-                                  of "Always", "IfNotPresent" or "Never". Defaults
-                                  to "IfNotPresent". This can be used only with Kubernetes
-                                  version equal to 1.22 and later.
-                                enum:
-                                - Always
-                                - IfNotPresent
-                                - Never
-                                type: string
                               kubeletExtraArgs:
                                 additionalProperties:
                                   type: string
diff --git a/bootstrap/kubeadm/internal/controllers/kubeadmconfig_controller.go b/bootstrap/kubeadm/internal/controllers/kubeadmconfig_controller.go
index e12305a9c..e8ebe12a5 100644
--- a/bootstrap/kubeadm/internal/controllers/kubeadmconfig_controller.go
+++ b/bootstrap/kubeadm/internal/controllers/kubeadmconfig_controller.go
@@ -368,7 +368,7 @@ func (r *KubeadmConfigReconciler) handleClusterNotInitialized(ctx context.Contex
 
 	// if the machine has not ClusterConfiguration and InitConfiguration, requeue
 	if scope.Config.Spec.InitConfiguration == nil && scope.Config.Spec.ClusterConfiguration == nil {
-		scope.Info("Control plane is not ready, requeuing joining control planes until ready.")
+		scope.Info("Control plane is not ready, requeing joining control planes until ready.")
 		return ctrl.Result{RequeueAfter: 30 * time.Second}, nil
 	}
 
@@ -381,7 +381,7 @@ func (r *KubeadmConfigReconciler) handleClusterNotInitialized(ctx context.Contex
 	// as control plane get processed here
 	// if not the first, requeue
 	if !r.KubeadmInitLock.Lock(ctx, scope.Cluster, machine) {
-		scope.Info("A control plane is already being initialized, requeuing until control plane is ready")
+		scope.Info("A control plane is already being initialized, requeing until control plane is ready")
 		return ctrl.Result{RequeueAfter: 30 * time.Second}, nil
 	}
 
diff --git a/bootstrap/kubeadm/types/upstreamv1beta1/zz_generated.conversion.go b/bootstrap/kubeadm/types/upstreamv1beta1/zz_generated.conversion.go
index 0497d927b..b340245f6 100644
--- a/bootstrap/kubeadm/types/upstreamv1beta1/zz_generated.conversion.go
+++ b/bootstrap/kubeadm/types/upstreamv1beta1/zz_generated.conversion.go
@@ -806,6 +806,5 @@ func autoConvert_v1beta1_NodeRegistrationOptions_To_upstreamv1beta1_NodeRegistra
 	out.Taints = *(*[]corev1.Taint)(unsafe.Pointer(&in.Taints))
 	out.KubeletExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.KubeletExtraArgs))
 	// WARNING: in.IgnorePreflightErrors requires manual conversion: does not exist in peer-type
-	// WARNING: in.ImagePullPolicy requires manual conversion: does not exist in peer-type
 	return nil
 }
diff --git a/bootstrap/kubeadm/types/upstreamv1beta2/conversion.go b/bootstrap/kubeadm/types/upstreamv1beta2/conversion.go
index fc95ca21d..efbb99b7e 100644
--- a/bootstrap/kubeadm/types/upstreamv1beta2/conversion.go
+++ b/bootstrap/kubeadm/types/upstreamv1beta2/conversion.go
@@ -92,9 +92,3 @@ func Convert_v1beta1_JoinConfiguration_To_upstreamv1beta2_JoinConfiguration(in *
 	// JoinConfiguration.Patches does not exist in kubeadm v1beta2 API
 	return autoConvert_v1beta1_JoinConfiguration_To_upstreamv1beta2_JoinConfiguration(in, out, s)
 }
-
-func Convert_v1beta1_NodeRegistrationOptions_To_upstreamv1beta2_NodeRegistrationOptions(in *bootstrapv1.NodeRegistrationOptions, out *NodeRegistrationOptions, s apimachineryconversion.Scope) error {
-	// NodeRegistrationOptions.ImagePullPolicy does not exit in
-	// kubeadm v1beta2 API.
-	return autoConvert_v1beta1_NodeRegistrationOptions_To_upstreamv1beta2_NodeRegistrationOptions(in, out, s)
-}
diff --git a/bootstrap/kubeadm/types/upstreamv1beta2/zz_generated.conversion.go b/bootstrap/kubeadm/types/upstreamv1beta2/zz_generated.conversion.go
index 9a40da3b2..5000842ff 100644
--- a/bootstrap/kubeadm/types/upstreamv1beta2/zz_generated.conversion.go
+++ b/bootstrap/kubeadm/types/upstreamv1beta2/zz_generated.conversion.go
@@ -213,6 +213,11 @@ func RegisterConversions(s *runtime.Scheme) error {
 	}); err != nil {
 		return err
 	}
+	if err := s.AddGeneratedConversionFunc((*v1beta1.NodeRegistrationOptions)(nil), (*NodeRegistrationOptions)(nil), func(a, b interface{}, scope conversion.Scope) error {
+		return Convert_v1beta1_NodeRegistrationOptions_To_upstreamv1beta2_NodeRegistrationOptions(a.(*v1beta1.NodeRegistrationOptions), b.(*NodeRegistrationOptions), scope)
+	}); err != nil {
+		return err
+	}
 	if err := s.AddConversionFunc((*ClusterConfiguration)(nil), (*v1beta1.ClusterConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error {
 		return Convert_upstreamv1beta2_ClusterConfiguration_To_v1beta1_ClusterConfiguration(a.(*ClusterConfiguration), b.(*v1beta1.ClusterConfiguration), scope)
 	}); err != nil {
@@ -243,11 +248,6 @@ func RegisterConversions(s *runtime.Scheme) error {
 	}); err != nil {
 		return err
 	}
-	if err := s.AddConversionFunc((*v1beta1.NodeRegistrationOptions)(nil), (*NodeRegistrationOptions)(nil), func(a, b interface{}, scope conversion.Scope) error {
-		return Convert_v1beta1_NodeRegistrationOptions_To_upstreamv1beta2_NodeRegistrationOptions(a.(*v1beta1.NodeRegistrationOptions), b.(*NodeRegistrationOptions), scope)
-	}); err != nil {
-		return err
-	}
 	return nil
 }
 
@@ -815,6 +815,10 @@ func autoConvert_v1beta1_NodeRegistrationOptions_To_upstreamv1beta2_NodeRegistra
 	out.Taints = *(*[]corev1.Taint)(unsafe.Pointer(&in.Taints))
 	out.KubeletExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.KubeletExtraArgs))
 	out.IgnorePreflightErrors = *(*[]string)(unsafe.Pointer(&in.IgnorePreflightErrors))
-	// WARNING: in.ImagePullPolicy requires manual conversion: does not exist in peer-type
 	return nil
 }
+
+// Convert_v1beta1_NodeRegistrationOptions_To_upstreamv1beta2_NodeRegistrationOptions is an autogenerated conversion function.
+func Convert_v1beta1_NodeRegistrationOptions_To_upstreamv1beta2_NodeRegistrationOptions(in *v1beta1.NodeRegistrationOptions, out *NodeRegistrationOptions, s conversion.Scope) error {
+	return autoConvert_v1beta1_NodeRegistrationOptions_To_upstreamv1beta2_NodeRegistrationOptions(in, out, s)
+}
diff --git a/bootstrap/kubeadm/types/upstreamv1beta3/types.go b/bootstrap/kubeadm/types/upstreamv1beta3/types.go
index abc993a88..1959b8039 100644
--- a/bootstrap/kubeadm/types/upstreamv1beta3/types.go
+++ b/bootstrap/kubeadm/types/upstreamv1beta3/types.go
@@ -225,13 +225,6 @@ type NodeRegistrationOptions struct {
 	// IgnorePreflightErrors provides a slice of pre-flight errors to be ignored when the current node is registered.
 	// +optional
 	IgnorePreflightErrors []string `json:"ignorePreflightErrors,omitempty"`
-
-	// ImagePullPolicy specifies the policy for image pulling
-	// during kubeadm "init" and "join" operations. The value of
-	// this field must be one of "Always", "IfNotPresent" or
-	// "Never". Defaults to "IfNotPresent".
-	// +optional
-	ImagePullPolicy string `json:"imagePullPolicy,omitempty"`
 }
 
 // Networking contains elements describing cluster's networking configuration.
diff --git a/bootstrap/kubeadm/types/upstreamv1beta3/zz_generated.conversion.go b/bootstrap/kubeadm/types/upstreamv1beta3/zz_generated.conversion.go
index d849616cb..19f5bd3a6 100644
--- a/bootstrap/kubeadm/types/upstreamv1beta3/zz_generated.conversion.go
+++ b/bootstrap/kubeadm/types/upstreamv1beta3/zz_generated.conversion.go
@@ -798,7 +798,6 @@ func autoConvert_upstreamv1beta3_NodeRegistrationOptions_To_v1beta1_NodeRegistra
 	out.Taints = *(*[]corev1.Taint)(unsafe.Pointer(&in.Taints))
 	out.KubeletExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.KubeletExtraArgs))
 	out.IgnorePreflightErrors = *(*[]string)(unsafe.Pointer(&in.IgnorePreflightErrors))
-	out.ImagePullPolicy = in.ImagePullPolicy
 	return nil
 }
 
@@ -808,7 +807,6 @@ func autoConvert_v1beta1_NodeRegistrationOptions_To_upstreamv1beta3_NodeRegistra
 	out.Taints = *(*[]corev1.Taint)(unsafe.Pointer(&in.Taints))
 	out.KubeletExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.KubeletExtraArgs))
 	out.IgnorePreflightErrors = *(*[]string)(unsafe.Pointer(&in.IgnorePreflightErrors))
-	out.ImagePullPolicy = in.ImagePullPolicy
 	return nil
 }
 
diff --git a/cmd/clusterctl/client/client.go b/cmd/clusterctl/client/client.go
index 05d229503..32d42dfac 100644
--- a/cmd/clusterctl/client/client.go
+++ b/cmd/clusterctl/client/client.go
@@ -79,13 +79,13 @@ type Client interface {
 // AlphaClient exposes the alpha features in clusterctl high-level client library.
 type AlphaClient interface {
 	// RolloutRestart provides rollout restart of cluster-api resources
-	RolloutRestart(options RolloutRestartOptions) error
+	RolloutRestart(options RolloutOptions) error
 	// RolloutPause provides rollout pause of cluster-api resources
-	RolloutPause(options RolloutPauseOptions) error
+	RolloutPause(options RolloutOptions) error
 	// RolloutResume provides rollout resume of paused cluster-api resources
-	RolloutResume(options RolloutResumeOptions) error
+	RolloutResume(options RolloutOptions) error
 	// RolloutUndo provides rollout rollback of cluster-api resources
-	RolloutUndo(options RolloutUndoOptions) error
+	RolloutUndo(options RolloutOptions) error
 	// TopologyPlan dry runs the topology reconciler
 	TopologyPlan(options TopologyPlanOptions) (*TopologyPlanOutput, error)
 }
diff --git a/cmd/clusterctl/client/rollout.go b/cmd/clusterctl/client/rollout.go
index c69ef2eda..2a932f82e 100644
--- a/cmd/clusterctl/client/rollout.go
+++ b/cmd/clusterctl/client/rollout.go
@@ -26,50 +26,8 @@ import (
 	"sigs.k8s.io/cluster-api/cmd/clusterctl/internal/util"
 )
 
-// RolloutRestartOptions carries the options supported by RolloutRestart.
-type RolloutRestartOptions struct {
-	// Kubeconfig defines the kubeconfig to use for accessing the management cluster. If empty,
-	// default rules for kubeconfig discovery will be used.
-	Kubeconfig Kubeconfig
-
-	// Resources for the rollout command
-	Resources []string
-
-	// Namespace where the resource(s) live. If unspecified, the namespace name will be inferred
-	// from the current configuration.
-	Namespace string
-}
-
-// RolloutPauseOptions carries the options supported by RolloutPause.
-type RolloutPauseOptions struct {
-	// Kubeconfig defines the kubeconfig to use for accessing the management cluster. If empty,
-	// default rules for kubeconfig discovery will be used.
-	Kubeconfig Kubeconfig
-
-	// Resources for the rollout command
-	Resources []string
-
-	// Namespace where the resource(s) live. If unspecified, the namespace name will be inferred
-	// from the current configuration.
-	Namespace string
-}
-
-// RolloutResumeOptions carries the options supported by RolloutResume.
-type RolloutResumeOptions struct {
-	// Kubeconfig defines the kubeconfig to use for accessing the management cluster. If empty,
-	// default rules for kubeconfig discovery will be used.
-	Kubeconfig Kubeconfig
-
-	// Resources for the rollout command
-	Resources []string
-
-	// Namespace where the resource(s) live. If unspecified, the namespace name will be inferred
-	// from the current configuration.
-	Namespace string
-}
-
-// RolloutUndoOptions carries the options supported by RolloutUndo.
-type RolloutUndoOptions struct {
+// RolloutOptions carries the base set of options supported by rollout command.
+type RolloutOptions struct {
 	// Kubeconfig defines the kubeconfig to use for accessing the management cluster. If empty,
 	// default rules for kubeconfig discovery will be used.
 	Kubeconfig Kubeconfig
@@ -82,15 +40,16 @@ type RolloutUndoOptions struct {
 	Namespace string
 
 	// Revision number to rollback to when issuing the undo command.
+	// Revision number of a specific revision when issuing the history command.
 	ToRevision int64
 }
 
-func (c *clusterctlClient) RolloutRestart(options RolloutRestartOptions) error {
+func (c *clusterctlClient) RolloutRestart(options RolloutOptions) error {
 	clusterClient, err := c.clusterClientFactory(ClusterClientFactoryInput{Kubeconfig: options.Kubeconfig})
 	if err != nil {
 		return err
 	}
-	objRefs, err := getObjectRefs(clusterClient, options.Namespace, options.Resources)
+	objRefs, err := getObjectRefs(clusterClient, options)
 	if err != nil {
 		return err
 	}
@@ -102,12 +61,12 @@ func (c *clusterctlClient) RolloutRestart(options RolloutRestartOptions) error {
 	return nil
 }
 
-func (c *clusterctlClient) RolloutPause(options RolloutPauseOptions) error {
+func (c *clusterctlClient) RolloutPause(options RolloutOptions) error {
 	clusterClient, err := c.clusterClientFactory(ClusterClientFactoryInput{Kubeconfig: options.Kubeconfig})
 	if err != nil {
 		return err
 	}
-	objRefs, err := getObjectRefs(clusterClient, options.Namespace, options.Resources)
+	objRefs, err := getObjectRefs(clusterClient, options)
 	if err != nil {
 		return err
 	}
@@ -119,12 +78,12 @@ func (c *clusterctlClient) RolloutPause(options RolloutPauseOptions) error {
 	return nil
 }
 
-func (c *clusterctlClient) RolloutResume(options RolloutResumeOptions) error {
+func (c *clusterctlClient) RolloutResume(options RolloutOptions) error {
 	clusterClient, err := c.clusterClientFactory(ClusterClientFactoryInput{Kubeconfig: options.Kubeconfig})
 	if err != nil {
 		return err
 	}
-	objRefs, err := getObjectRefs(clusterClient, options.Namespace, options.Resources)
+	objRefs, err := getObjectRefs(clusterClient, options)
 	if err != nil {
 		return err
 	}
@@ -136,12 +95,12 @@ func (c *clusterctlClient) RolloutResume(options RolloutResumeOptions) error {
 	return nil
 }
 
-func (c *clusterctlClient) RolloutUndo(options RolloutUndoOptions) error {
+func (c *clusterctlClient) RolloutUndo(options RolloutOptions) error {
 	clusterClient, err := c.clusterClientFactory(ClusterClientFactoryInput{Kubeconfig: options.Kubeconfig})
 	if err != nil {
 		return err
 	}
-	objRefs, err := getObjectRefs(clusterClient, options.Namespace, options.Resources)
+	objRefs, err := getObjectRefs(clusterClient, options)
 	if err != nil {
 		return err
 	}
@@ -153,21 +112,21 @@ func (c *clusterctlClient) RolloutUndo(options RolloutUndoOptions) error {
 	return nil
 }
 
-func getObjectRefs(clusterClient cluster.Client, namespace string, resources []string) ([]corev1.ObjectReference, error) {
+func getObjectRefs(clusterClient cluster.Client, options RolloutOptions) ([]corev1.ObjectReference, error) {
 	// If the option specifying the Namespace is empty, try to detect it.
-	if namespace == "" {
+	if options.Namespace == "" {
 		currentNamespace, err := clusterClient.Proxy().CurrentNamespace()
 		if err != nil {
 			return []corev1.ObjectReference{}, err
 		}
-		namespace = currentNamespace
+		options.Namespace = currentNamespace
 	}
 
-	if len(resources) == 0 {
+	if len(options.Resources) == 0 {
 		return []corev1.ObjectReference{}, fmt.Errorf("required resource not specified")
 	}
-	normalized := normalizeResources(resources)
-	objRefs, err := util.GetObjectReferences(namespace, normalized...)
+	normalized := normalizeResources(options.Resources)
+	objRefs, err := util.GetObjectReferences(options.Namespace, normalized...)
 	if err != nil {
 		return []corev1.ObjectReference{}, err
 	}
diff --git a/cmd/clusterctl/cmd/rollout/pause.go b/cmd/clusterctl/cmd/rollout/pause.go
index 8e29ed200..958fe494c 100644
--- a/cmd/clusterctl/cmd/rollout/pause.go
+++ b/cmd/clusterctl/cmd/rollout/pause.go
@@ -77,7 +77,7 @@ func runPause(cfgFile string, args []string) error {
 		return err
 	}
 
-	return c.RolloutPause(client.RolloutPauseOptions{
+	return c.RolloutPause(client.RolloutOptions{
 		Kubeconfig: client.Kubeconfig{Path: pauseOpt.kubeconfig, Context: pauseOpt.kubeconfigContext},
 		Namespace:  pauseOpt.namespace,
 		Resources:  pauseOpt.resources,
diff --git a/cmd/clusterctl/cmd/rollout/restart.go b/cmd/clusterctl/cmd/rollout/restart.go
index 7f46fac31..e6d85cc4c 100644
--- a/cmd/clusterctl/cmd/rollout/restart.go
+++ b/cmd/clusterctl/cmd/rollout/restart.go
@@ -76,7 +76,7 @@ func runRestart(cfgFile string, _ *cobra.Command, args []string) error {
 		return err
 	}
 
-	return c.RolloutRestart(client.RolloutRestartOptions{
+	return c.RolloutRestart(client.RolloutOptions{
 		Kubeconfig: client.Kubeconfig{Path: restartOpt.kubeconfig, Context: restartOpt.kubeconfigContext},
 		Namespace:  restartOpt.namespace,
 		Resources:  restartOpt.resources,
diff --git a/cmd/clusterctl/cmd/rollout/resume.go b/cmd/clusterctl/cmd/rollout/resume.go
index 07dc4bc1f..ee5c803c9 100644
--- a/cmd/clusterctl/cmd/rollout/resume.go
+++ b/cmd/clusterctl/cmd/rollout/resume.go
@@ -76,7 +76,7 @@ func runResume(cfgFile string, args []string) error {
 		return err
 	}
 
-	return c.RolloutResume(client.RolloutResumeOptions{
+	return c.RolloutResume(client.RolloutOptions{
 		Kubeconfig: client.Kubeconfig{Path: resumeOpt.kubeconfig, Context: resumeOpt.kubeconfigContext},
 		Namespace:  resumeOpt.namespace,
 		Resources:  resumeOpt.resources,
diff --git a/cmd/clusterctl/cmd/rollout/undo.go b/cmd/clusterctl/cmd/rollout/undo.go
index cfa660300..7e40c4bb7 100644
--- a/cmd/clusterctl/cmd/rollout/undo.go
+++ b/cmd/clusterctl/cmd/rollout/undo.go
@@ -76,7 +76,7 @@ func runUndo(cfgFile string, args []string) error {
 		return err
 	}
 
-	return c.RolloutUndo(client.RolloutUndoOptions{
+	return c.RolloutUndo(client.RolloutOptions{
 		Kubeconfig: client.Kubeconfig{Path: undoOpt.kubeconfig, Context: undoOpt.kubeconfigContext},
 		Namespace:  undoOpt.namespace,
 		Resources:  undoOpt.resources,
diff --git a/cmd/clusterctl/config/crd/bases/clusterctl.cluster.x-k8s.io_metadata.yaml b/cmd/clusterctl/config/crd/bases/clusterctl.cluster.x-k8s.io_metadata.yaml
index bfc784bdd..a7bc6faae 100644
--- a/cmd/clusterctl/config/crd/bases/clusterctl.cluster.x-k8s.io_metadata.yaml
+++ b/cmd/clusterctl/config/crd/bases/clusterctl.cluster.x-k8s.io_metadata.yaml
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   annotations:
-    controller-gen.kubebuilder.io/version: v0.11.2
+    controller-gen.kubebuilder.io/version: v0.11.1
   creationTimestamp: null
   name: metadata.clusterctl.cluster.x-k8s.io
 spec:
diff --git a/cmd/clusterctl/config/crd/bases/clusterctl.cluster.x-k8s.io_providers.yaml b/cmd/clusterctl/config/crd/bases/clusterctl.cluster.x-k8s.io_providers.yaml
index 3d1917e23..ed30f3434 100644
--- a/cmd/clusterctl/config/crd/bases/clusterctl.cluster.x-k8s.io_providers.yaml
+++ b/cmd/clusterctl/config/crd/bases/clusterctl.cluster.x-k8s.io_providers.yaml
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   annotations:
-    controller-gen.kubebuilder.io/version: v0.11.2
+    controller-gen.kubebuilder.io/version: v0.11.1
   creationTimestamp: null
   name: providers.clusterctl.cluster.x-k8s.io
 spec:
diff --git a/cmd/clusterctl/config/manifest/clusterctl-api.yaml b/cmd/clusterctl/config/manifest/clusterctl-api.yaml
index 39f8b3b41..8625909e2 100644
--- a/cmd/clusterctl/config/manifest/clusterctl-api.yaml
+++ b/cmd/clusterctl/config/manifest/clusterctl-api.yaml
@@ -2,7 +2,7 @@ apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   annotations:
-    controller-gen.kubebuilder.io/version: v0.11.2
+    controller-gen.kubebuilder.io/version: v0.11.1
   creationTimestamp: null
   name: providers.clusterctl.cluster.x-k8s.io
 spec:
diff --git a/config/crd/bases/addons.cluster.x-k8s.io_clusterresourcesetbindings.yaml b/config/crd/bases/addons.cluster.x-k8s.io_clusterresourcesetbindings.yaml
index 33ebf3760..d8f1629a0 100644
--- a/config/crd/bases/addons.cluster.x-k8s.io_clusterresourcesetbindings.yaml
+++ b/config/crd/bases/addons.cluster.x-k8s.io_clusterresourcesetbindings.yaml
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   annotations:
-    controller-gen.kubebuilder.io/version: v0.11.2
+    controller-gen.kubebuilder.io/version: v0.11.1
   creationTimestamp: null
   name: clusterresourcesetbindings.addons.cluster.x-k8s.io
 spec:
diff --git a/config/crd/bases/addons.cluster.x-k8s.io_clusterresourcesets.yaml b/config/crd/bases/addons.cluster.x-k8s.io_clusterresourcesets.yaml
index c2ccab2d5..bfb0ec207 100644
--- a/config/crd/bases/addons.cluster.x-k8s.io_clusterresourcesets.yaml
+++ b/config/crd/bases/addons.cluster.x-k8s.io_clusterresourcesets.yaml
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   annotations:
-    controller-gen.kubebuilder.io/version: v0.11.2
+    controller-gen.kubebuilder.io/version: v0.11.1
   creationTimestamp: null
   name: clusterresourcesets.addons.cluster.x-k8s.io
 spec:
diff --git a/config/crd/bases/cluster.x-k8s.io_clusterclasses.yaml b/config/crd/bases/cluster.x-k8s.io_clusterclasses.yaml
index a3084d5e2..fd1557135 100644
--- a/config/crd/bases/cluster.x-k8s.io_clusterclasses.yaml
+++ b/config/crd/bases/cluster.x-k8s.io_clusterclasses.yaml
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   annotations:
-    controller-gen.kubebuilder.io/version: v0.11.2
+    controller-gen.kubebuilder.io/version: v0.11.1
   creationTimestamp: null
   name: clusterclasses.cluster.x-k8s.io
 spec:
@@ -1445,6 +1445,195 @@ spec:
                   by the controller.
                 format: int64
                 type: integer
+              variables:
+                description: Variables is a list of ClusterClassStatusVariable that
+                  are defined for the ClusterClass.
+                items:
+                  description: ClusterClassStatusVariable defines a variable which
+                    appears in the status of a ClusterClass.
+                  properties:
+                    definitions:
+                      description: Definitions is a list of definitions for a variable.
+                      items:
+                        description: ClusterClassStatusVariableDefinition defines
+                          a variable which appears in the status of a ClusterClass.
+                        properties:
+                          from:
+                            description: From specifies the origin of the variable
+                              definition. This will be `inline` for variables defined
+                              in the ClusterClass or the name of a patch defined in
+                              the ClusterClass for variables discovered from a DiscoverVariables
+                              runtime extensions.
+                            type: string
+                          required:
+                            description: 'Required specifies if the variable is required.
+                              Note: this applies to the variable as a whole and thus
+                              the top-level object defined in the schema. If nested
+                              fields are required, this will be specified inside the
+                              schema.'
+                            type: boolean
+                          schema:
+                            description: Schema defines the schema of the variable.
+                            properties:
+                              openAPIV3Schema:
+                                description: OpenAPIV3Schema defines the schema of
+                                  a variable via OpenAPI v3 schema. The schema is
+                                  a subset of the schema used in Kubernetes CRDs.
+                                properties:
+                                  additionalProperties:
+                                    description: 'AdditionalProperties specifies the
+                                      schema of values in a map (keys are always strings).
+                                      NOTE: Can only be set if type is object. NOTE:
+                                      AdditionalProperties is mutually exclusive with
+                                      Properties. NOTE: This field uses PreserveUnknownFields
+                                      and Schemaless, because recursive validation
+                                      is not possible.'
+                                    x-kubernetes-preserve-unknown-fields: true
+                                  default:
+                                    description: 'Default is the default value of
+                                      the variable. NOTE: Can be set for all types.'
+                                    x-kubernetes-preserve-unknown-fields: true
+                                  description:
+                                    description: Description is a human-readable description
+                                      of this variable.
+                                    type: string
+                                  enum:
+                                    description: 'Enum is the list of valid values
+                                      of the variable. NOTE: Can be set for all types.'
+                                    items:
+                                      x-kubernetes-preserve-unknown-fields: true
+                                    type: array
+                                  example:
+                                    description: Example is an example for this variable.
+                                    x-kubernetes-preserve-unknown-fields: true
+                                  exclusiveMaximum:
+                                    description: 'ExclusiveMaximum specifies if the
+                                      Maximum is exclusive. NOTE: Can only be set
+                                      if type is integer or number.'
+                                    type: boolean
+                                  exclusiveMinimum:
+                                    description: 'ExclusiveMinimum specifies if the
+                                      Minimum is exclusive. NOTE: Can only be set
+                                      if type is integer or number.'
+                                    type: boolean
+                                  format:
+                                    description: 'Format is an OpenAPI v3 format string.
+                                      Unknown formats are ignored. For a list of supported
+                                      formats please see: (of the k8s.io/apiextensions-apiserver
+                                      version we''re currently using) https://github.com/kubernetes/apiextensions-apiserver/blob/master/pkg/apiserver/validation/formats.go
+                                      NOTE: Can only be set if type is string.'
+                                    type: string
+                                  items:
+                                    description: 'Items specifies fields of an array.
+                                      NOTE: Can only be set if type is array. NOTE:
+                                      This field uses PreserveUnknownFields and Schemaless,
+                                      because recursive validation is not possible.'
+                                    x-kubernetes-preserve-unknown-fields: true
+                                  maxItems:
+                                    description: 'MaxItems is the max length of an
+                                      array variable. NOTE: Can only be set if type
+                                      is array.'
+                                    format: int64
+                                    type: integer
+                                  maxLength:
+                                    description: 'MaxLength is the max length of a
+                                      string variable. NOTE: Can only be set if type
+                                      is string.'
+                                    format: int64
+                                    type: integer
+                                  maximum:
+                                    description: 'Maximum is the maximum of an integer
+                                      or number variable. If ExclusiveMaximum is false,
+                                      the variable is valid if it is lower than, or
+                                      equal to, the value of Maximum. If ExclusiveMaximum
+                                      is true, the variable is valid if it is strictly
+                                      lower than the value of Maximum. NOTE: Can only
+                                      be set if type is integer or number.'
+                                    format: int64
+                                    type: integer
+                                  minItems:
+                                    description: 'MinItems is the min length of an
+                                      array variable. NOTE: Can only be set if type
+                                      is array.'
+                                    format: int64
+                                    type: integer
+                                  minLength:
+                                    description: 'MinLength is the min length of a
+                                      string variable. NOTE: Can only be set if type
+                                      is string.'
+                                    format: int64
+                                    type: integer
+                                  minimum:
+                                    description: 'Minimum is the minimum of an integer
+                                      or number variable. If ExclusiveMinimum is false,
+                                      the variable is valid if it is greater than,
+                                      or equal to, the value of Minimum. If ExclusiveMinimum
+                                      is true, the variable is valid if it is strictly
+                                      greater than the value of Minimum. NOTE: Can
+                                      only be set if type is integer or number.'
+                                    format: int64
+                                    type: integer
+                                  pattern:
+                                    description: 'Pattern is the regex which a string
+                                      variable must match. NOTE: Can only be set if
+                                      type is string.'
+                                    type: string
+                                  properties:
+                                    description: 'Properties specifies fields of an
+                                      object. NOTE: Can only be set if type is object.
+                                      NOTE: Properties is mutually exclusive with
+                                      AdditionalProperties. NOTE: This field uses
+                                      PreserveUnknownFields and Schemaless, because
+                                      recursive validation is not possible.'
+                                    x-kubernetes-preserve-unknown-fields: true
+                                  required:
+                                    description: 'Required specifies which fields
+                                      of an object are required. NOTE: Can only be
+                                      set if type is object.'
+                                    items:
+                                      type: string
+                                    type: array
+                                  type:
+                                    description: 'Type is the type of the variable.
+                                      Valid values are: object, array, string, integer,
+                                      number or boolean.'
+                                    type: string
+                                  uniqueItems:
+                                    description: 'UniqueItems specifies if items in
+                                      an array must be unique. NOTE: Can only be set
+                                      if type is array.'
+                                    type: boolean
+                                  x-kubernetes-preserve-unknown-fields:
+                                    description: XPreserveUnknownFields allows setting
+                                      fields in a variable object which are not defined
+                                      in the variable schema. This affects fields
+                                      recursively, except if nested properties or
+                                      additionalProperties are specified in the schema.
+                                    type: boolean
+                                required:
+                                - type
+                                type: object
+                            required:
+                            - openAPIV3Schema
+                            type: object
+                        required:
+                        - from
+                        - required
+                        - schema
+                        type: object
+                      type: array
+                    defintionsConflict:
+                      description: DefintionsConflict specifies whether or not there
+                        are conflicting definitions for a single variable name.
+                      type: boolean
+                    name:
+                      description: Name is the name of the variable.
+                      type: string
+                  required:
+                  - definitions
+                  - name
+                  type: object
+                type: array
             type: object
         type: object
     served: true
diff --git a/config/crd/bases/cluster.x-k8s.io_clusters.yaml b/config/crd/bases/cluster.x-k8s.io_clusters.yaml
index 56d2f94bb..227e10093 100644
--- a/config/crd/bases/cluster.x-k8s.io_clusters.yaml
+++ b/config/crd/bases/cluster.x-k8s.io_clusters.yaml
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   annotations:
-    controller-gen.kubebuilder.io/version: v0.11.2
+    controller-gen.kubebuilder.io/version: v0.11.1
   creationTimestamp: null
   name: clusters.cluster.x-k8s.io
 spec:
diff --git a/config/crd/bases/cluster.x-k8s.io_machinedeployments.yaml b/config/crd/bases/cluster.x-k8s.io_machinedeployments.yaml
index bfb949d4b..0db04ab50 100644
--- a/config/crd/bases/cluster.x-k8s.io_machinedeployments.yaml
+++ b/config/crd/bases/cluster.x-k8s.io_machinedeployments.yaml
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   annotations:
-    controller-gen.kubebuilder.io/version: v0.11.2
+    controller-gen.kubebuilder.io/version: v0.11.1
   creationTimestamp: null
   name: machinedeployments.cluster.x-k8s.io
 spec:
diff --git a/config/crd/bases/cluster.x-k8s.io_machinehealthchecks.yaml b/config/crd/bases/cluster.x-k8s.io_machinehealthchecks.yaml
index f8b8494a5..99b6fdf54 100644
--- a/config/crd/bases/cluster.x-k8s.io_machinehealthchecks.yaml
+++ b/config/crd/bases/cluster.x-k8s.io_machinehealthchecks.yaml
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   annotations:
-    controller-gen.kubebuilder.io/version: v0.11.2
+    controller-gen.kubebuilder.io/version: v0.11.1
   creationTimestamp: null
   name: machinehealthchecks.cluster.x-k8s.io
 spec:
diff --git a/config/crd/bases/cluster.x-k8s.io_machinepools.yaml b/config/crd/bases/cluster.x-k8s.io_machinepools.yaml
index ca3d38695..68e2d0d3a 100644
--- a/config/crd/bases/cluster.x-k8s.io_machinepools.yaml
+++ b/config/crd/bases/cluster.x-k8s.io_machinepools.yaml
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   annotations:
-    controller-gen.kubebuilder.io/version: v0.11.2
+    controller-gen.kubebuilder.io/version: v0.11.1
   creationTimestamp: null
   name: machinepools.cluster.x-k8s.io
 spec:
diff --git a/config/crd/bases/cluster.x-k8s.io_machines.yaml b/config/crd/bases/cluster.x-k8s.io_machines.yaml
index e88a5220b..4fdd74ec3 100644
--- a/config/crd/bases/cluster.x-k8s.io_machines.yaml
+++ b/config/crd/bases/cluster.x-k8s.io_machines.yaml
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   annotations:
-    controller-gen.kubebuilder.io/version: v0.11.2
+    controller-gen.kubebuilder.io/version: v0.11.1
   creationTimestamp: null
   name: machines.cluster.x-k8s.io
 spec:
diff --git a/config/crd/bases/cluster.x-k8s.io_machinesets.yaml b/config/crd/bases/cluster.x-k8s.io_machinesets.yaml
index 548010bff..bc02a0794 100644
--- a/config/crd/bases/cluster.x-k8s.io_machinesets.yaml
+++ b/config/crd/bases/cluster.x-k8s.io_machinesets.yaml
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   annotations:
-    controller-gen.kubebuilder.io/version: v0.11.2
+    controller-gen.kubebuilder.io/version: v0.11.1
   creationTimestamp: null
   name: machinesets.cluster.x-k8s.io
 spec:
diff --git a/config/crd/bases/ipam.cluster.x-k8s.io_ipaddressclaims.yaml b/config/crd/bases/ipam.cluster.x-k8s.io_ipaddressclaims.yaml
index ce5a6e3e6..539dcfae4 100644
--- a/config/crd/bases/ipam.cluster.x-k8s.io_ipaddressclaims.yaml
+++ b/config/crd/bases/ipam.cluster.x-k8s.io_ipaddressclaims.yaml
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   annotations:
-    controller-gen.kubebuilder.io/version: v0.11.2
+    controller-gen.kubebuilder.io/version: v0.11.1
   creationTimestamp: null
   name: ipaddressclaims.ipam.cluster.x-k8s.io
 spec:
diff --git a/config/crd/bases/ipam.cluster.x-k8s.io_ipaddresses.yaml b/config/crd/bases/ipam.cluster.x-k8s.io_ipaddresses.yaml
index d60ed805b..4587244d5 100644
--- a/config/crd/bases/ipam.cluster.x-k8s.io_ipaddresses.yaml
+++ b/config/crd/bases/ipam.cluster.x-k8s.io_ipaddresses.yaml
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   annotations:
-    controller-gen.kubebuilder.io/version: v0.11.2
+    controller-gen.kubebuilder.io/version: v0.11.1
   creationTimestamp: null
   name: ipaddresses.ipam.cluster.x-k8s.io
 spec:
diff --git a/config/crd/bases/runtime.cluster.x-k8s.io_extensionconfigs.yaml b/config/crd/bases/runtime.cluster.x-k8s.io_extensionconfigs.yaml
index 8ca0e31d1..3b8de2eff 100644
--- a/config/crd/bases/runtime.cluster.x-k8s.io_extensionconfigs.yaml
+++ b/config/crd/bases/runtime.cluster.x-k8s.io_extensionconfigs.yaml
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   annotations:
-    controller-gen.kubebuilder.io/version: v0.11.2
+    controller-gen.kubebuilder.io/version: v0.11.1
   creationTimestamp: null
   name: extensionconfigs.runtime.cluster.x-k8s.io
 spec:
diff --git a/controlplane/kubeadm/api/v1alpha3/conversion.go b/controlplane/kubeadm/api/v1alpha3/conversion.go
index 0061e6331..dd0b04c5e 100644
--- a/controlplane/kubeadm/api/v1alpha3/conversion.go
+++ b/controlplane/kubeadm/api/v1alpha3/conversion.go
@@ -85,20 +85,6 @@ func (src *KubeadmControlPlane) ConvertTo(dstRaw conversion.Hub) error {
 
 	dst.Spec.RolloutBefore = restored.Spec.RolloutBefore
 
-	if restored.Spec.KubeadmConfigSpec.JoinConfiguration != nil && restored.Spec.KubeadmConfigSpec.JoinConfiguration.NodeRegistration.ImagePullPolicy != "" {
-		if dst.Spec.KubeadmConfigSpec.JoinConfiguration == nil {
-			dst.Spec.KubeadmConfigSpec.JoinConfiguration = &bootstrapv1.JoinConfiguration{}
-		}
-		dst.Spec.KubeadmConfigSpec.JoinConfiguration.NodeRegistration.ImagePullPolicy = restored.Spec.KubeadmConfigSpec.JoinConfiguration.NodeRegistration.ImagePullPolicy
-	}
-
-	if restored.Spec.KubeadmConfigSpec.InitConfiguration != nil && restored.Spec.KubeadmConfigSpec.InitConfiguration.NodeRegistration.ImagePullPolicy != "" {
-		if dst.Spec.KubeadmConfigSpec.InitConfiguration == nil {
-			dst.Spec.KubeadmConfigSpec.InitConfiguration = &bootstrapv1.InitConfiguration{}
-		}
-		dst.Spec.KubeadmConfigSpec.InitConfiguration.NodeRegistration.ImagePullPolicy = restored.Spec.KubeadmConfigSpec.InitConfiguration.NodeRegistration.ImagePullPolicy
-	}
-
 	return nil
 }
 
diff --git a/controlplane/kubeadm/api/v1alpha4/conversion.go b/controlplane/kubeadm/api/v1alpha4/conversion.go
index 9fae7f390..9eaebf63b 100644
--- a/controlplane/kubeadm/api/v1alpha4/conversion.go
+++ b/controlplane/kubeadm/api/v1alpha4/conversion.go
@@ -70,20 +70,6 @@ func (src *KubeadmControlPlane) ConvertTo(dstRaw conversion.Hub) error {
 	dst.Spec.RolloutBefore = restored.Spec.RolloutBefore
 	dst.Spec.MachineTemplate.NodeVolumeDetachTimeout = restored.Spec.MachineTemplate.NodeVolumeDetachTimeout
 
-	if restored.Spec.KubeadmConfigSpec.JoinConfiguration != nil && restored.Spec.KubeadmConfigSpec.JoinConfiguration.NodeRegistration.ImagePullPolicy != "" {
-		if dst.Spec.KubeadmConfigSpec.JoinConfiguration == nil {
-			dst.Spec.KubeadmConfigSpec.JoinConfiguration = &bootstrapv1.JoinConfiguration{}
-		}
-		dst.Spec.KubeadmConfigSpec.JoinConfiguration.NodeRegistration.ImagePullPolicy = restored.Spec.KubeadmConfigSpec.JoinConfiguration.NodeRegistration.ImagePullPolicy
-	}
-
-	if restored.Spec.KubeadmConfigSpec.InitConfiguration != nil && restored.Spec.KubeadmConfigSpec.InitConfiguration.NodeRegistration.ImagePullPolicy != "" {
-		if dst.Spec.KubeadmConfigSpec.InitConfiguration == nil {
-			dst.Spec.KubeadmConfigSpec.InitConfiguration = &bootstrapv1.InitConfiguration{}
-		}
-		dst.Spec.KubeadmConfigSpec.InitConfiguration.NodeRegistration.ImagePullPolicy = restored.Spec.KubeadmConfigSpec.InitConfiguration.NodeRegistration.ImagePullPolicy
-	}
-
 	return nil
 }
 
@@ -159,20 +145,6 @@ func (src *KubeadmControlPlaneTemplate) ConvertTo(dstRaw conversion.Hub) error {
 
 	dst.Spec.Template.Spec.RolloutBefore = restored.Spec.Template.Spec.RolloutBefore
 
-	if restored.Spec.Template.Spec.KubeadmConfigSpec.JoinConfiguration != nil && restored.Spec.Template.Spec.KubeadmConfigSpec.JoinConfiguration.NodeRegistration.ImagePullPolicy != "" {
-		if dst.Spec.Template.Spec.KubeadmConfigSpec.JoinConfiguration == nil {
-			dst.Spec.Template.Spec.KubeadmConfigSpec.JoinConfiguration = &bootstrapv1.JoinConfiguration{}
-		}
-		dst.Spec.Template.Spec.KubeadmConfigSpec.JoinConfiguration.NodeRegistration.ImagePullPolicy = restored.Spec.Template.Spec.KubeadmConfigSpec.JoinConfiguration.NodeRegistration.ImagePullPolicy
-	}
-
-	if restored.Spec.Template.Spec.KubeadmConfigSpec.InitConfiguration != nil && restored.Spec.Template.Spec.KubeadmConfigSpec.InitConfiguration.NodeRegistration.ImagePullPolicy != "" {
-		if dst.Spec.Template.Spec.KubeadmConfigSpec.InitConfiguration == nil {
-			dst.Spec.Template.Spec.KubeadmConfigSpec.InitConfiguration = &bootstrapv1.InitConfiguration{}
-		}
-		dst.Spec.Template.Spec.KubeadmConfigSpec.InitConfiguration.NodeRegistration.ImagePullPolicy = restored.Spec.Template.Spec.KubeadmConfigSpec.InitConfiguration.NodeRegistration.ImagePullPolicy
-	}
-
 	return nil
 }
 
diff --git a/controlplane/kubeadm/config/crd/bases/controlplane.cluster.x-k8s.io_kubeadmcontrolplanes.yaml b/controlplane/kubeadm/config/crd/bases/controlplane.cluster.x-k8s.io_kubeadmcontrolplanes.yaml
index da79fa45d..ed855c19a 100644
--- a/controlplane/kubeadm/config/crd/bases/controlplane.cluster.x-k8s.io_kubeadmcontrolplanes.yaml
+++ b/controlplane/kubeadm/config/crd/bases/controlplane.cluster.x-k8s.io_kubeadmcontrolplanes.yaml
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   annotations:
-    controller-gen.kubebuilder.io/version: v0.11.2
+    controller-gen.kubebuilder.io/version: v0.11.1
   creationTimestamp: null
   name: kubeadmcontrolplanes.controlplane.cluster.x-k8s.io
 spec:
@@ -3070,19 +3070,6 @@ spec:
                             items:
                               type: string
                             type: array
-                          imagePullPolicy:
-                            default: IfNotPresent
-                            description: ImagePullPolicy specifies the policy for
-                              image pulling during kubeadm "init" and "join" operations.
-                              The value of this field must be one of "Always", "IfNotPresent"
-                              or "Never". Defaults to "IfNotPresent". This can be
-                              used only with Kubernetes version equal to 1.22 and
-                              later.
-                            enum:
-                            - Always
-                            - IfNotPresent
-                            - Never
-                            type: string
                           kubeletExtraArgs:
                             additionalProperties:
                               type: string
@@ -3303,19 +3290,6 @@ spec:
                             items:
                               type: string
                             type: array
-                          imagePullPolicy:
-                            default: IfNotPresent
-                            description: ImagePullPolicy specifies the policy for
-                              image pulling during kubeadm "init" and "join" operations.
-                              The value of this field must be one of "Always", "IfNotPresent"
-                              or "Never". Defaults to "IfNotPresent". This can be
-                              used only with Kubernetes version equal to 1.22 and
-                              later.
-                            enum:
-                            - Always
-                            - IfNotPresent
-                            - Never
-                            type: string
                           kubeletExtraArgs:
                             additionalProperties:
                               type: string
diff --git a/controlplane/kubeadm/config/crd/bases/controlplane.cluster.x-k8s.io_kubeadmcontrolplanetemplates.yaml b/controlplane/kubeadm/config/crd/bases/controlplane.cluster.x-k8s.io_kubeadmcontrolplanetemplates.yaml
index a70a55892..668c5f5be 100644
--- a/controlplane/kubeadm/config/crd/bases/controlplane.cluster.x-k8s.io_kubeadmcontrolplanetemplates.yaml
+++ b/controlplane/kubeadm/config/crd/bases/controlplane.cluster.x-k8s.io_kubeadmcontrolplanetemplates.yaml
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   annotations:
-    controller-gen.kubebuilder.io/version: v0.11.2
+    controller-gen.kubebuilder.io/version: v0.11.1
   creationTimestamp: null
   name: kubeadmcontrolplanetemplates.controlplane.cluster.x-k8s.io
 spec:
@@ -1860,20 +1860,6 @@ spec:
                                     items:
                                       type: string
                                     type: array
-                                  imagePullPolicy:
-                                    default: IfNotPresent
-                                    description: ImagePullPolicy specifies the policy
-                                      for image pulling during kubeadm "init" and
-                                      "join" operations. The value of this field must
-                                      be one of "Always", "IfNotPresent" or "Never".
-                                      Defaults to "IfNotPresent". This can be used
-                                      only with Kubernetes version equal to 1.22 and
-                                      later.
-                                    enum:
-                                    - Always
-                                    - IfNotPresent
-                                    - Never
-                                    type: string
                                   kubeletExtraArgs:
                                     additionalProperties:
                                       type: string
@@ -2110,20 +2096,6 @@ spec:
                                     items:
                                       type: string
                                     type: array
-                                  imagePullPolicy:
-                                    default: IfNotPresent
-                                    description: ImagePullPolicy specifies the policy
-                                      for image pulling during kubeadm "init" and
-                                      "join" operations. The value of this field must
-                                      be one of "Always", "IfNotPresent" or "Never".
-                                      Defaults to "IfNotPresent". This can be used
-                                      only with Kubernetes version equal to 1.22 and
-                                      later.
-                                    enum:
-                                    - Always
-                                    - IfNotPresent
-                                    - Never
-                                    type: string
                                   kubeletExtraArgs:
                                     additionalProperties:
                                       type: string
diff --git a/controlplane/kubeadm/internal/controllers/controller.go b/controlplane/kubeadm/internal/controllers/controller.go
index 42be67d9e..c29b46fb3 100644
--- a/controlplane/kubeadm/internal/controllers/controller.go
+++ b/controlplane/kubeadm/internal/controllers/controller.go
@@ -211,7 +211,7 @@ func (r *KubeadmControlPlaneReconciler) Reconcile(ctx context.Context, req ctrl.
 		// Requeue if the reconcile failed because the ClusterCacheTracker was locked for
 		// the current cluster because of concurrent access.
 		if errors.Is(err, remote.ErrClusterLocked) {
-			log.V(5).Info("Requeuing because another worker has the lock on the ClusterCacheTracker")
+			log.V(5).Info("Requeueing because another worker has the lock on the ClusterCacheTracker")
 			return ctrl.Result{Requeue: true}, nil
 		}
 		return res, err
@@ -222,7 +222,7 @@ func (r *KubeadmControlPlaneReconciler) Reconcile(ctx context.Context, req ctrl.
 	// Requeue if the reconcile failed because the ClusterCacheTracker was locked for
 	// the current cluster because of concurrent access.
 	if errors.Is(err, remote.ErrClusterLocked) {
-		log.V(5).Info("Requeuing because another worker has the lock on the ClusterCacheTracker")
+		log.V(5).Info("Requeueing because another worker has the lock on the ClusterCacheTracker")
 		return ctrl.Result{Requeue: true}, nil
 	}
 	return res, err
diff --git a/docs/book/src/developer/providers/v1.3-to-v1.4.md b/docs/book/src/developer/providers/v1.3-to-v1.4.md
index ef339f3ec..c1125535f 100644
--- a/docs/book/src/developer/providers/v1.3-to-v1.4.md
+++ b/docs/book/src/developer/providers/v1.3-to-v1.4.md
@@ -36,20 +36,6 @@ maintainers of providers and consumers of our Go API.
 
 - `Backup(options BackupOptions) error` in the Client interface has been removed.
 - `Restore(options RestoreOptions) error` in the Client interface has been removed.
-- `cmd/clusterctl/client.RolloutOptions` has been removed, `RolloutRestartOptions`, `RolloutPauseOptions` , `RolloutResumeOptions`, and `RolloutUndoOptions` have been added instead.
-- Annotation constant `DisableMachineCreate` has been updated to `DisableMachineCreateAnnotation`
-- Below Label constant have been updated
-  - `ClusterLabelName` to `ClusterNameLabel`
-  - `ClusterTopologyMachineDeploymentLabelName` to `ClusterTopologyMachineDeploymentNameLabel`
-  - `ProviderLabelName` to `ProviderNameLabel`
-  - `MachineControlPlaneLabelName` to `MachineControlPlaneLabel`
-  - `MachineSetLabelName` to `MachineSetNameLabel`
-  - `MachineDeploymentLabelName` to `MachineDeploymentNameLabel`
-- Below Condition and Reason constants have been updated
-  - `ExternalRemediationTemplateAvailable` to `ExternalRemediationTemplateAvailableCondition`
-  - `ExternalRemediationTemplateNotFound` to `ExternalRemediationTemplateNotFoundReason`
-  - `ExternalRemediationRequestAvailable` to  `ExternalRemediationRequestAvailableCondition`
-  - `ExternalRemediationRequestCreationFailed` to `ExternalRemediationRequestCreationFailedReason`
 
 ### Other
 
diff --git a/docs/book/src/tasks/experimental-features/runtime-sdk/implement-topology-mutation-hook.md b/docs/book/src/tasks/experimental-features/runtime-sdk/implement-topology-mutation-hook.md
index 4f7568cfb..7f75f6f83 100644
--- a/docs/book/src/tasks/experimental-features/runtime-sdk/implement-topology-mutation-hook.md
+++ b/docs/book/src/tasks/experimental-features/runtime-sdk/implement-topology-mutation-hook.md
@@ -86,7 +86,7 @@ status:
     variables:
       - name: no-proxy
         definitions:
-          - namespace: inline
+          - from: inline
             required: true
             schema:
               openAPIV3Schema:
@@ -95,15 +95,17 @@ status:
                 example: "internal.com"
                 description: "comma-separated list of machine or domain names excluded from using the proxy."
       - name: http-proxy
+        # definitionsConflict is true if there are non-equal definitions for a variable.
+        definitionsConflict: true
         definitions:
-          - namespace: inline
+          - from: inline
             schema:
               openAPIV3Schema:
                 type: string
                 default: "proxy.example.com"
                 example: "proxy.example.com"
                 description: "proxy for http calls."
-          - namespace: lbImageRepository
+          - from: lbImageRepository
             schema:
               openAPIV3Schema:
                 type: string
@@ -112,19 +114,19 @@ status:
                 description: "proxy for http calls."
 ```
 
-### Variable namespacing
+### Variable definition conflicts
 Variable definitions can be inline in the ClusterClass or from any number of external DiscoverVariables hooks. The source 
-of a variable definition is recorded in the `namespace` field in ClusterClass `.status.variables`.
-Variables that are defined by an external DiscoverVariables hook will have the name of the patch they are associated with as their namespace.
-Variables that are defined in the ClusterClass `.spec.variables` will have the namespace `inline`.
-Note: `inline` is a reserved namespace. It can not be used as the name of an external patch to avoid conflicts.
+of a variable definition is recorded in the `from` field in ClusterClass `.status.variables`.
+Variables that are defined by an external DiscoverVariables hook will have the name of the patch they are associated with as the value of `from`.
+Variables that are defined in the ClusterClass `.spec.variables` will have `inline` as the value of `from`.
+Note: `inline` is a reserved name for patches. It can not be used as the name of an external patch to avoid conflicts.
 
-If all variables that share a name have equivalent schemas the variables are considered `global` . `global` variables can
-be set without providing a namespace - [see below](#setting-values-for-variables-in-the-cluster). The CAPI components will
+If all variables that share a name have equivalent schemas the variable definitions are not in conflict. These variables can
+be set without providing `definitionFrom` value - [see below](#setting-values-for-variables-in-the-cluster). The CAPI components will
 consider variable definitions to be equivalent when they share a name and their schema is exactly equal.
 
 ### Setting values for variables in the Cluster
-Setting variables that are defined with external variable definitions requires attention to be paid to variable namespacing, as exposed in the ClusterClass status. 
+Setting variables that are defined with external variable definitions requires attention to be paid to variable definition conflicts, as exposed in the ClusterClass status. 
 Variable values are set in Cluster `.spec.topology.variables`.
 
 ```yaml
@@ -134,15 +136,15 @@ kind: Cluster
 spec:
     topology:
       variables:
-        # namespace is not needed as this variable is global.
+        # `definitionFrom` is not needed as this variable does not have conflicting definitions.
         - name: no-proxy
           value: "internal.domain.com"
-        # namespaced variables require values for each individual schema.
+        # variables with the same name but different definitions require values for each individual schema.
         - name: http-proxy
-          namespace: inline
+          definitionFrom: inline
           value: http://proxy.example2.com:1234
         - name: http-proxy
-          namespace: lbImageRepository
+          definitionFrom: lbImageRepository
           value:
             host: proxy.example2.com
             port: 1234
diff --git a/docs/proposals/20191017-kubeadm-based-control-plane.md b/docs/proposals/20191017-kubeadm-based-control-plane.md
index 3bc2f35f1..487911a5e 100644
--- a/docs/proposals/20191017-kubeadm-based-control-plane.md
+++ b/docs/proposals/20191017-kubeadm-based-control-plane.md
@@ -479,8 +479,7 @@ When `MaxSurge` is set to 0 the rollout algorithm is as follows:
     replacement for the previous machine is not yet created. 
   - The cluster MUST have no machines with a deletion timestamp. This rule prevents KCP taking actions while the cluster is in a transitional state.
   - Remediation MUST preserve etcd quorum. This rule ensures that we will not remove a member that would result in etcd
-    losing a majority of members and thus become unable to field new requests (note: this rule applies only to CP already
-    initialized and with managed etcd)
+    losing a majority of members and thus become unable to field new requests (note: this rule applies only to CP with at least replicas)
 
 - Additionally following opt-in safeguards will be put in place:
   - If we are remediating the same machine (delete, re-create, replacement machine gets unhealthy), it will be possible
diff --git a/docs/proposals/20220330-topology-mutation-hook.md b/docs/proposals/20220330-topology-mutation-hook.md
index 2af82d743..6e8a0034d 100644
--- a/docs/proposals/20220330-topology-mutation-hook.md
+++ b/docs/proposals/20220330-topology-mutation-hook.md
@@ -235,8 +235,7 @@ Mitigations:
 Variable definitions supplied externally by an External Patch Extension through a Variable Discovery Hook can change when the definition in the External Patch Extension changes. This can lead to a clash where variables that previously had the same name and definition no longer have the same definition.
 
 Mitigations:
-* Variable Discovery Hooks allow addressing variables using namespacing, where the variable value setting in the Cluster
- includes the name of the Patch as a namespace. 
+* Variable Discovery Hooks allow addressing conflicting variables individually by specifying the source of the variable's definition when setting the variable value in the Cluster.
 * ClusterClass authors should pro-actively test any changes to ClusterClasses and associated Runtime Extensions to avoid clashing variable definitions.
 * External Patch extension authors should extensively document their patches, variables and their usage.
 
diff --git a/exp/addons/internal/controllers/clusterresourceset_controller.go b/exp/addons/internal/controllers/clusterresourceset_controller.go
index 6d28c3047..513609224 100644
--- a/exp/addons/internal/controllers/clusterresourceset_controller.go
+++ b/exp/addons/internal/controllers/clusterresourceset_controller.go
@@ -155,7 +155,7 @@ func (r *ClusterResourceSetReconciler) Reconcile(ctx context.Context, req ctrl.R
 			// Requeue if the reconcile failed because the ClusterCacheTracker was locked for
 			// the current cluster because of concurrent access.
 			if errors.Is(err, remote.ErrClusterLocked) {
-				log.V(5).Info("Requeuing because another worker has the lock on the ClusterCacheTracker")
+				log.V(5).Info("Requeueing because another worker has the lock on the ClusterCacheTracker")
 				return ctrl.Result{Requeue: true}, nil
 			}
 			return ctrl.Result{}, err
diff --git a/go.mod b/go.mod
index 920fc50b8..40f5ec7aa 100644
--- a/go.mod
+++ b/go.mod
@@ -20,8 +20,8 @@ require (
 	github.com/google/go-github/v48 v48.2.0
 	github.com/google/gofuzz v1.2.0
 	github.com/mattn/go-runewidth v0.0.14 // indirect
-	github.com/onsi/ginkgo/v2 v2.8.0
-	github.com/onsi/gomega v1.26.0
+	github.com/onsi/ginkgo/v2 v2.7.0
+	github.com/onsi/gomega v1.25.0
 	github.com/pkg/errors v0.9.1
 	github.com/spf13/cobra v1.6.1
 	github.com/spf13/pflag v1.0.5
@@ -32,17 +32,17 @@ require (
 	golang.org/x/net v0.5.0 // indirect
 	golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783
 	google.golang.org/grpc v1.52.0
-	k8s.io/api v0.26.1
-	k8s.io/apiextensions-apiserver v0.26.1
-	k8s.io/apimachinery v0.26.1
-	k8s.io/apiserver v0.26.1
-	k8s.io/client-go v0.26.1
+	k8s.io/api v0.26.0
+	k8s.io/apiextensions-apiserver v0.26.0
+	k8s.io/apimachinery v0.26.0
+	k8s.io/apiserver v0.26.0
+	k8s.io/client-go v0.26.0
 	k8s.io/cluster-bootstrap v0.25.0
-	k8s.io/component-base v0.26.1
+	k8s.io/component-base v0.26.0
 	k8s.io/klog/v2 v2.80.1
 	k8s.io/kubectl v0.25.0
 	k8s.io/utils v0.0.0-20221128185143-99ec85e7a448
-	sigs.k8s.io/controller-runtime v0.14.2
+	sigs.k8s.io/controller-runtime v0.14.1
 	sigs.k8s.io/yaml v1.3.0
 )
 
@@ -73,7 +73,7 @@ require (
 	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
 	github.com/golang/protobuf v1.5.2 // indirect
 	github.com/google/btree v1.0.1 // indirect
-	github.com/google/cel-go v0.12.6 // indirect
+	github.com/google/cel-go v0.12.5 // indirect
 	github.com/google/go-querystring v1.1.0 // indirect
 	github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
 	github.com/google/uuid v1.3.0 // indirect
diff --git a/go.sum b/go.sum
index 981a9948a..6d22709f8 100644
--- a/go.sum
+++ b/go.sum
@@ -235,8 +235,8 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z
 github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
 github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
-github.com/google/cel-go v0.12.6 h1:kjeKudqV0OygrAqA9fX6J55S8gj+Jre2tckIm5RoG4M=
-github.com/google/cel-go v0.12.6/go.mod h1:Jk7ljRzLBhkmiAwBoUxB1sZSCVBAzkqPF25olK/iRDw=
+github.com/google/cel-go v0.12.5 h1:DmzaiSgoaqGCjtpPQWl26/gND+yRpim56H1jCVev6d8=
+github.com/google/cel-go v0.12.5/go.mod h1:Jk7ljRzLBhkmiAwBoUxB1sZSCVBAzkqPF25olK/iRDw=
 github.com/google/gnostic v0.6.9 h1:ZK/5VhkoX835RikCHpSUJV9a+S3e1zLh59YnyWeBW+0=
 github.com/google/gnostic v0.6.9/go.mod h1:Nm8234We1lq6iB9OmlgNv3nH91XLLVZHCDayfA3xq+E=
 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
@@ -411,10 +411,10 @@ github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRW
 github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
 github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
 github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
-github.com/onsi/ginkgo/v2 v2.8.0 h1:pAM+oBNPrpXRs+E/8spkeGx9QgekbRVyr74EUvRVOUI=
-github.com/onsi/ginkgo/v2 v2.8.0/go.mod h1:6JsQiECmxCa3V5st74AL/AmsV482EDdVrGaVW6z3oYU=
-github.com/onsi/gomega v1.26.0 h1:03cDLK28U6hWvCAns6NeydX3zIm4SF3ci69ulidS32Q=
-github.com/onsi/gomega v1.26.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM=
+github.com/onsi/ginkgo/v2 v2.7.0 h1:/XxtEV3I3Eif/HobnVx9YmJgk8ENdRsuUmM+fLCFNow=
+github.com/onsi/ginkgo/v2 v2.7.0/go.mod h1:yjiuMwPokqY1XauOgju45q3sJt6VzQ/Fict1LFVcsAo=
+github.com/onsi/gomega v1.25.0 h1:Vw7br2PCDYijJHSfBOWhov+8cAnUf8MfMaIOV323l6Y=
+github.com/onsi/gomega v1.25.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM=
 github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
 github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
@@ -992,22 +992,22 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
 honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
 honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
-k8s.io/api v0.26.1 h1:f+SWYiPd/GsiWwVRz+NbFyCgvv75Pk9NK6dlkZgpCRQ=
-k8s.io/api v0.26.1/go.mod h1:xd/GBNgR0f707+ATNyPmQ1oyKSgndzXij81FzWGsejg=
-k8s.io/apiextensions-apiserver v0.26.1 h1:cB8h1SRk6e/+i3NOrQgSFij1B2S0Y0wDoNl66bn8RMI=
-k8s.io/apiextensions-apiserver v0.26.1/go.mod h1:AptjOSXDGuE0JICx/Em15PaoO7buLwTs0dGleIHixSM=
-k8s.io/apimachinery v0.26.1 h1:8EZ/eGJL+hY/MYCNwhmDzVqq2lPl3N3Bo8rvweJwXUQ=
-k8s.io/apimachinery v0.26.1/go.mod h1:tnPmbONNJ7ByJNz9+n9kMjNP8ON+1qoAIIC70lztu74=
-k8s.io/apiserver v0.26.1 h1:6vmnAqCDO194SVCPU3MU8NcDgSqsUA62tBUSWrFXhsc=
-k8s.io/apiserver v0.26.1/go.mod h1:wr75z634Cv+sifswE9HlAo5FQ7UoUauIICRlOE+5dCg=
+k8s.io/api v0.26.0 h1:IpPlZnxBpV1xl7TGk/X6lFtpgjgntCg8PJ+qrPHAC7I=
+k8s.io/api v0.26.0/go.mod h1:k6HDTaIFC8yn1i6pSClSqIwLABIcLV9l5Q4EcngKnQg=
+k8s.io/apiextensions-apiserver v0.26.0 h1:Gy93Xo1eg2ZIkNX/8vy5xviVSxwQulsnUdQ00nEdpDo=
+k8s.io/apiextensions-apiserver v0.26.0/go.mod h1:7ez0LTiyW5nq3vADtK6C3kMESxadD51Bh6uz3JOlqWQ=
+k8s.io/apimachinery v0.26.0 h1:1feANjElT7MvPqp0JT6F3Ss6TWDwmcjLypwoPpEf7zg=
+k8s.io/apimachinery v0.26.0/go.mod h1:tnPmbONNJ7ByJNz9+n9kMjNP8ON+1qoAIIC70lztu74=
+k8s.io/apiserver v0.26.0 h1:q+LqIK5EZwdznGZb8bq0+a+vCqdeEEe4Ux3zsOjbc4o=
+k8s.io/apiserver v0.26.0/go.mod h1:aWhlLD+mU+xRo+zhkvP/gFNbShI4wBDHS33o0+JGI84=
 k8s.io/cli-runtime v0.25.0 h1:XBnTc2Fi+w818jcJGzhiJKQuXl8479sZ4FhtV5hVJ1Q=
 k8s.io/cli-runtime v0.25.0/go.mod h1:bHOI5ZZInRHhbq12OdUiYZQN8ml8aKZLwQgt9QlLINw=
-k8s.io/client-go v0.26.1 h1:87CXzYJnAMGaa/IDDfRdhTzxk/wzGZ+/HUQpqgVSZXU=
-k8s.io/client-go v0.26.1/go.mod h1:IWNSglg+rQ3OcvDkhY6+QLeasV4OYHDjdqeWkDQZwGE=
+k8s.io/client-go v0.26.0 h1:lT1D3OfO+wIi9UFolCrifbjUUgu7CpLca0AD8ghRLI8=
+k8s.io/client-go v0.26.0/go.mod h1:I2Sh57A79EQsDmn7F7ASpmru1cceh3ocVT9KlX2jEZg=
 k8s.io/cluster-bootstrap v0.25.0 h1:KJ2/r0dV+bLfTK5EBobAVKvjGel3N4Qqh3bvnzh9qPk=
 k8s.io/cluster-bootstrap v0.25.0/go.mod h1:x/TCtY3EiuR/rODkA3SvVQT3uSssQLf9cXcmSjdDTe0=
-k8s.io/component-base v0.26.1 h1:4ahudpeQXHZL5kko+iDHqLj/FSGAEUnSVO0EBbgDd+4=
-k8s.io/component-base v0.26.1/go.mod h1:VHrLR0b58oC035w6YQiBSbtsf0ThuSwXP+p5dD/kAWU=
+k8s.io/component-base v0.26.0 h1:0IkChOCohtDHttmKuz+EP3j3+qKmV55rM9gIFTXA7Vs=
+k8s.io/component-base v0.26.0/go.mod h1:lqHwlfV1/haa14F/Z5Zizk5QmzaVf23nQzCwVOQpfC8=
 k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4=
 k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
 k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 h1:+70TFaan3hfJzs+7VK2o+OGxg8HsuBr/5f6tVAjDu6E=
@@ -1019,8 +1019,8 @@ k8s.io/utils v0.0.0-20221128185143-99ec85e7a448/go.mod h1:OLgZIPagt7ERELqWJFomSt
 rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
 rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
 rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
-sigs.k8s.io/controller-runtime v0.14.2 h1:P6IwDhbsRWsBClt/8/h8Zy36bCuGuW5Op7MHpFrN/60=
-sigs.k8s.io/controller-runtime v0.14.2/go.mod h1:WqIdsAY6JBsjfc/CqO0CORmNtoCtE4S6qbPc9s68h+0=
+sigs.k8s.io/controller-runtime v0.14.1 h1:vThDes9pzg0Y+UbCPY3Wj34CGIYPgdmspPm2GIpxpzM=
+sigs.k8s.io/controller-runtime v0.14.1/go.mod h1:GaRkrY8a7UZF0kqFFbUKG7n9ICiTY5T55P1RiE3UZlU=
 sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k=
 sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
 sigs.k8s.io/kustomize/api v0.12.1 h1:7YM7gW3kYBwtKvoY216ZzY+8hM+lV53LUayghNRJ0vM=
diff --git a/hack/tools/go.mod b/hack/tools/go.mod
index 1a29b5bd4..16a5f0b77 100644
--- a/hack/tools/go.mod
+++ b/hack/tools/go.mod
@@ -16,16 +16,16 @@ require (
 	golang.org/x/tools v0.2.0
 	google.golang.org/api v0.107.0
 	helm.sh/helm/v3 v3.10.3
-	k8s.io/api v0.26.1
-	k8s.io/apiextensions-apiserver v0.26.1
-	k8s.io/apimachinery v0.26.1
-	k8s.io/client-go v0.26.1
+	k8s.io/api v0.26.0
+	k8s.io/apiextensions-apiserver v0.26.0
+	k8s.io/apimachinery v0.26.0
+	k8s.io/client-go v0.26.0
 	k8s.io/klog/v2 v2.80.1
 	k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280
 	k8s.io/utils v0.0.0-20221128185143-99ec85e7a448
 	sigs.k8s.io/cluster-api v0.0.0-00010101000000-000000000000
 	sigs.k8s.io/cluster-api/test v0.0.0-00010101000000-000000000000
-	sigs.k8s.io/controller-runtime v0.14.2
+	sigs.k8s.io/controller-runtime v0.14.1
 	sigs.k8s.io/controller-tools v0.10.0
 	sigs.k8s.io/kubebuilder/docs/book/utils v0.0.0-20211028165026-57688c578b5d
 	sigs.k8s.io/kustomize/api v0.12.1
@@ -74,7 +74,7 @@ require (
 	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
 	github.com/golang/protobuf v1.5.2 // indirect
 	github.com/google/btree v1.0.1 // indirect
-	github.com/google/cel-go v0.12.6 // indirect
+	github.com/google/cel-go v0.12.5 // indirect
 	github.com/google/gnostic v0.6.9 // indirect
 	github.com/google/go-cmp v0.5.9 // indirect
 	github.com/google/go-github/v48 v48.2.0 // indirect
@@ -108,7 +108,7 @@ require (
 	github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
 	github.com/morikuni/aec v1.0.0 // indirect
 	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
-	github.com/onsi/gomega v1.26.0 // indirect
+	github.com/onsi/gomega v1.25.0 // indirect
 	github.com/opencontainers/go-digest v1.0.0 // indirect
 	github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect
 	github.com/pelletier/go-toml/v2 v2.0.6 // indirect
@@ -148,10 +148,10 @@ require (
 	gopkg.in/ini.v1 v1.67.0 // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
-	k8s.io/apiserver v0.26.1 // indirect
+	k8s.io/apiserver v0.26.0 // indirect
 	k8s.io/cli-runtime v0.25.2 // indirect
 	k8s.io/cluster-bootstrap v0.25.0 // indirect
-	k8s.io/component-base v0.26.1 // indirect
+	k8s.io/component-base v0.26.0 // indirect
 	oras.land/oras-go v1.2.0 // indirect
 	sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect
 	sigs.k8s.io/kustomize/kyaml v0.13.9 // indirect
diff --git a/hack/tools/go.sum b/hack/tools/go.sum
index 3baf09f5d..296171fb2 100644
--- a/hack/tools/go.sum
+++ b/hack/tools/go.sum
@@ -239,8 +239,8 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z
 github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
 github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
-github.com/google/cel-go v0.12.6 h1:kjeKudqV0OygrAqA9fX6J55S8gj+Jre2tckIm5RoG4M=
-github.com/google/cel-go v0.12.6/go.mod h1:Jk7ljRzLBhkmiAwBoUxB1sZSCVBAzkqPF25olK/iRDw=
+github.com/google/cel-go v0.12.5 h1:DmzaiSgoaqGCjtpPQWl26/gND+yRpim56H1jCVev6d8=
+github.com/google/cel-go v0.12.5/go.mod h1:Jk7ljRzLBhkmiAwBoUxB1sZSCVBAzkqPF25olK/iRDw=
 github.com/google/gnostic v0.6.9 h1:ZK/5VhkoX835RikCHpSUJV9a+S3e1zLh59YnyWeBW+0=
 github.com/google/gnostic v0.6.9/go.mod h1:Nm8234We1lq6iB9OmlgNv3nH91XLLVZHCDayfA3xq+E=
 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
@@ -424,9 +424,9 @@ github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRW
 github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
 github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
 github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
-github.com/onsi/ginkgo/v2 v2.8.0 h1:pAM+oBNPrpXRs+E/8spkeGx9QgekbRVyr74EUvRVOUI=
-github.com/onsi/gomega v1.26.0 h1:03cDLK28U6hWvCAns6NeydX3zIm4SF3ci69ulidS32Q=
-github.com/onsi/gomega v1.26.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM=
+github.com/onsi/ginkgo/v2 v2.7.0 h1:/XxtEV3I3Eif/HobnVx9YmJgk8ENdRsuUmM+fLCFNow=
+github.com/onsi/gomega v1.25.0 h1:Vw7br2PCDYijJHSfBOWhov+8cAnUf8MfMaIOV323l6Y=
+github.com/onsi/gomega v1.25.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM=
 github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
 github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 h1:rc3tiVYb5z54aKaDfakKn0dDjIyPpTtszkjuMzyt7ec=
@@ -987,22 +987,22 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
 honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
 honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
-k8s.io/api v0.26.1 h1:f+SWYiPd/GsiWwVRz+NbFyCgvv75Pk9NK6dlkZgpCRQ=
-k8s.io/api v0.26.1/go.mod h1:xd/GBNgR0f707+ATNyPmQ1oyKSgndzXij81FzWGsejg=
-k8s.io/apiextensions-apiserver v0.26.1 h1:cB8h1SRk6e/+i3NOrQgSFij1B2S0Y0wDoNl66bn8RMI=
-k8s.io/apiextensions-apiserver v0.26.1/go.mod h1:AptjOSXDGuE0JICx/Em15PaoO7buLwTs0dGleIHixSM=
-k8s.io/apimachinery v0.26.1 h1:8EZ/eGJL+hY/MYCNwhmDzVqq2lPl3N3Bo8rvweJwXUQ=
-k8s.io/apimachinery v0.26.1/go.mod h1:tnPmbONNJ7ByJNz9+n9kMjNP8ON+1qoAIIC70lztu74=
-k8s.io/apiserver v0.26.1 h1:6vmnAqCDO194SVCPU3MU8NcDgSqsUA62tBUSWrFXhsc=
-k8s.io/apiserver v0.26.1/go.mod h1:wr75z634Cv+sifswE9HlAo5FQ7UoUauIICRlOE+5dCg=
+k8s.io/api v0.26.0 h1:IpPlZnxBpV1xl7TGk/X6lFtpgjgntCg8PJ+qrPHAC7I=
+k8s.io/api v0.26.0/go.mod h1:k6HDTaIFC8yn1i6pSClSqIwLABIcLV9l5Q4EcngKnQg=
+k8s.io/apiextensions-apiserver v0.26.0 h1:Gy93Xo1eg2ZIkNX/8vy5xviVSxwQulsnUdQ00nEdpDo=
+k8s.io/apiextensions-apiserver v0.26.0/go.mod h1:7ez0LTiyW5nq3vADtK6C3kMESxadD51Bh6uz3JOlqWQ=
+k8s.io/apimachinery v0.26.0 h1:1feANjElT7MvPqp0JT6F3Ss6TWDwmcjLypwoPpEf7zg=
+k8s.io/apimachinery v0.26.0/go.mod h1:tnPmbONNJ7ByJNz9+n9kMjNP8ON+1qoAIIC70lztu74=
+k8s.io/apiserver v0.26.0 h1:q+LqIK5EZwdznGZb8bq0+a+vCqdeEEe4Ux3zsOjbc4o=
+k8s.io/apiserver v0.26.0/go.mod h1:aWhlLD+mU+xRo+zhkvP/gFNbShI4wBDHS33o0+JGI84=
 k8s.io/cli-runtime v0.25.2 h1:XOx+SKRjBpYMLY/J292BHTkmyDffl/qOx3YSuFZkTuc=
 k8s.io/cli-runtime v0.25.2/go.mod h1:OQx3+/0st6x5YpkkJQlEWLC73V0wHsOFMC1/roxV8Oc=
-k8s.io/client-go v0.26.1 h1:87CXzYJnAMGaa/IDDfRdhTzxk/wzGZ+/HUQpqgVSZXU=
-k8s.io/client-go v0.26.1/go.mod h1:IWNSglg+rQ3OcvDkhY6+QLeasV4OYHDjdqeWkDQZwGE=
+k8s.io/client-go v0.26.0 h1:lT1D3OfO+wIi9UFolCrifbjUUgu7CpLca0AD8ghRLI8=
+k8s.io/client-go v0.26.0/go.mod h1:I2Sh57A79EQsDmn7F7ASpmru1cceh3ocVT9KlX2jEZg=
 k8s.io/cluster-bootstrap v0.25.0 h1:KJ2/r0dV+bLfTK5EBobAVKvjGel3N4Qqh3bvnzh9qPk=
 k8s.io/cluster-bootstrap v0.25.0/go.mod h1:x/TCtY3EiuR/rODkA3SvVQT3uSssQLf9cXcmSjdDTe0=
-k8s.io/component-base v0.26.1 h1:4ahudpeQXHZL5kko+iDHqLj/FSGAEUnSVO0EBbgDd+4=
-k8s.io/component-base v0.26.1/go.mod h1:VHrLR0b58oC035w6YQiBSbtsf0ThuSwXP+p5dD/kAWU=
+k8s.io/component-base v0.26.0 h1:0IkChOCohtDHttmKuz+EP3j3+qKmV55rM9gIFTXA7Vs=
+k8s.io/component-base v0.26.0/go.mod h1:lqHwlfV1/haa14F/Z5Zizk5QmzaVf23nQzCwVOQpfC8=
 k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4=
 k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
 k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 h1:+70TFaan3hfJzs+7VK2o+OGxg8HsuBr/5f6tVAjDu6E=
@@ -1014,8 +1014,8 @@ oras.land/oras-go v1.2.0/go.mod h1:pFNs7oHp2dYsYMSS82HaX5l4mpnGO7hbpPN6EWH2ltc=
 rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
 rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
 rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
-sigs.k8s.io/controller-runtime v0.14.2 h1:P6IwDhbsRWsBClt/8/h8Zy36bCuGuW5Op7MHpFrN/60=
-sigs.k8s.io/controller-runtime v0.14.2/go.mod h1:WqIdsAY6JBsjfc/CqO0CORmNtoCtE4S6qbPc9s68h+0=
+sigs.k8s.io/controller-runtime v0.14.1 h1:vThDes9pzg0Y+UbCPY3Wj34CGIYPgdmspPm2GIpxpzM=
+sigs.k8s.io/controller-runtime v0.14.1/go.mod h1:GaRkrY8a7UZF0kqFFbUKG7n9ICiTY5T55P1RiE3UZlU=
 sigs.k8s.io/controller-tools v0.10.0 h1:0L5DTDTFB67jm9DkfrONgTGmfc/zYow0ZaHyppizU2U=
 sigs.k8s.io/controller-tools v0.10.0/go.mod h1:uvr0EW6IsprfB0jpQq6evtKy+hHyHCXNfdWI5ONPx94=
 sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k=
diff --git a/internal/controllers/clusterclass/clusterclass_controller.go b/internal/controllers/clusterclass/clusterclass_controller.go
index d1f3b8fba..df64a559b 100644
--- a/internal/controllers/clusterclass/clusterclass_controller.go
+++ b/internal/controllers/clusterclass/clusterclass_controller.go
@@ -177,6 +177,20 @@ func (r *Reconciler) reconcile(ctx context.Context, clusterClass *clusterv1.Clus
 		return ctrl.Result{}, kerrors.NewAggregate(errs)
 	}
 
+	// Ensure the variables are added to the ClusterClass status.
+	clusterClass.Status.Variables = []clusterv1.ClusterClassStatusVariable{}
+	for _, variable := range clusterClass.Spec.Variables {
+		clusterClass.Status.Variables = append(clusterClass.Status.Variables,
+			clusterv1.ClusterClassStatusVariable{
+				Name: variable.Name,
+				Definitions: []clusterv1.ClusterClassStatusVariableDefinition{
+					{
+						From:     clusterv1.VariableDefinitionFromInline,
+						Required: variable.Required,
+						Schema:   variable.Schema,
+					},
+				}})
+	}
 	reconcileConditions(clusterClass, outdatedRefs)
 
 	return ctrl.Result{}, nil
diff --git a/internal/controllers/machine/machine_controller.go b/internal/controllers/machine/machine_controller.go
index fc7159c98..00c302f17 100644
--- a/internal/controllers/machine/machine_controller.go
+++ b/internal/controllers/machine/machine_controller.go
@@ -216,7 +216,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl.Re
 		// Requeue if the reconcile failed because the ClusterCacheTracker was locked for
 		// the current cluster because of concurrent access.
 		if errors.Is(err, remote.ErrClusterLocked) {
-			log.V(5).Info("Requeuing because another worker has the lock on the ClusterCacheTracker")
+			log.V(5).Info("Requeueing because another worker has the lock on the ClusterCacheTracker")
 			return ctrl.Result{Requeue: true}, nil
 		}
 		return res, err
@@ -227,7 +227,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl.Re
 	// Requeue if the reconcile failed because the ClusterCacheTracker was locked for
 	// the current cluster because of concurrent access.
 	if errors.Is(err, remote.ErrClusterLocked) {
-		log.V(5).Info("Requeuing because another worker has the lock on the ClusterCacheTracker")
+		log.V(5).Info("Requeueing because another worker has the lock on the ClusterCacheTracker")
 		return ctrl.Result{Requeue: true}, nil
 	}
 	return res, err
diff --git a/internal/controllers/machine/machine_controller_phases.go b/internal/controllers/machine/machine_controller_phases.go
index ec4d66b17..db961bd4e 100644
--- a/internal/controllers/machine/machine_controller_phases.go
+++ b/internal/controllers/machine/machine_controller_phases.go
@@ -98,7 +98,7 @@ func (r *Reconciler) reconcileExternal(ctx context.Context, cluster *clusterv1.C
 	obj, err := external.Get(ctx, r.Client, ref, m.Namespace)
 	if err != nil {
 		if apierrors.IsNotFound(errors.Cause(err)) {
-			log.Info("could not find external ref, requeuing", ref.Kind, klog.KRef(m.Namespace, ref.Name))
+			log.Info("could not find external ref, requeueing", ref.Kind, klog.KRef(m.Namespace, ref.Name))
 			return external.ReconcileOutput{RequeueAfter: externalReadyWait}, nil
 		}
 		return external.ReconcileOutput{}, err
@@ -251,7 +251,7 @@ func (r *Reconciler) reconcileInfrastructure(ctx context.Context, cluster *clust
 			m.Status.FailureReason = capierrors.MachineStatusErrorPtr(capierrors.InvalidConfigurationMachineError)
 			m.Status.FailureMessage = pointer.String(fmt.Sprintf("Machine infrastructure resource %v with name %q has been deleted after being ready",
 				m.Spec.InfrastructureRef.GroupVersionKind(), m.Spec.InfrastructureRef.Name))
-			return ctrl.Result{}, errors.Errorf("could not find %v %q for Machine %q in namespace %q, requeuing", m.Spec.InfrastructureRef.GroupVersionKind().String(), m.Spec.InfrastructureRef.Name, m.Name, m.Namespace)
+			return ctrl.Result{}, errors.Errorf("could not find %v %q for Machine %q in namespace %q, requeueing", m.Spec.InfrastructureRef.GroupVersionKind().String(), m.Spec.InfrastructureRef.Name, m.Name, m.Namespace)
 		}
 		return ctrl.Result{RequeueAfter: infraReconcileResult.RequeueAfter}, nil
 	}
diff --git a/internal/controllers/machinehealthcheck/machinehealthcheck_controller.go b/internal/controllers/machinehealthcheck/machinehealthcheck_controller.go
index f2b98f34b..14117c3c1 100644
--- a/internal/controllers/machinehealthcheck/machinehealthcheck_controller.go
+++ b/internal/controllers/machinehealthcheck/machinehealthcheck_controller.go
@@ -177,7 +177,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl.Re
 		// Requeue if the reconcile failed because the ClusterCacheTracker was locked for
 		// the current cluster because of concurrent access.
 		if errors.Is(err, remote.ErrClusterLocked) {
-			log.V(5).Info("Requeuing because another worker has the lock on the ClusterCacheTracker")
+			log.V(5).Info("Requeueing because another worker has the lock on the ClusterCacheTracker")
 			return ctrl.Result{Requeue: true}, nil
 		}
 		log.Error(err, "Failed to reconcile MachineHealthCheck")
@@ -322,7 +322,7 @@ func (r *Reconciler) reconcile(ctx context.Context, logger logr.Logger, cluster
 
 	// handle update errors
 	if len(errList) > 0 {
-		logger.V(3).Info("Error(s) marking machine, requeuing")
+		logger.V(3).Info("Error(s) marking machine, requeueing")
 		return reconcile.Result{}, kerrors.NewAggregate(errList)
 	}
 
diff --git a/internal/controllers/machineset/machineset_controller.go b/internal/controllers/machineset/machineset_controller.go
index 08499025b..01dcde08e 100644
--- a/internal/controllers/machineset/machineset_controller.go
+++ b/internal/controllers/machineset/machineset_controller.go
@@ -176,7 +176,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl.Re
 		// Requeue if the reconcile failed because the ClusterCacheTracker was locked for
 		// the current cluster because of concurrent access.
 		if errors.Is(err, remote.ErrClusterLocked) {
-			log.V(5).Info("Requeuing because another worker has the lock on the ClusterCacheTracker")
+			log.V(5).Info("Requeueing because another worker has the lock on the ClusterCacheTracker")
 			return ctrl.Result{Requeue: true}, nil
 		}
 		log.Error(err, "Failed to reconcile MachineSet")


Test patch:
diff --git a/bootstrap/kubeadm/types/upstreamv1beta1/conversion_test.go b/bootstrap/kubeadm/types/upstreamv1beta1/conversion_test.go
index 13d1f1831..80a8b3232 100644
--- a/bootstrap/kubeadm/types/upstreamv1beta1/conversion_test.go
+++ b/bootstrap/kubeadm/types/upstreamv1beta1/conversion_test.go
@@ -88,11 +88,6 @@ func kubeadmNodeRegistrationOptionsFuzzer(obj *bootstrapv1.NodeRegistrationOptio
 	// NodeRegistrationOptions.IgnorePreflightErrors does not exist in kubeadm v1beta1 API, so setting it to nil in order to avoid
 	// v1alpha4 --> v1beta1 -> v1alpha4 round trip errors.
 	obj.IgnorePreflightErrors = nil
-
-	// NodeRegistrationOptions.ImagePullPolicy does not exist in
-	// kubeadm v1beta1 API, so setting it to empty in order to
-	// avoid round trip errors.
-	obj.ImagePullPolicy = ""
 }
 
 func kubeadmInitConfigurationFuzzer(obj *bootstrapv1.InitConfiguration, c fuzz.Continue) {
diff --git a/bootstrap/kubeadm/types/upstreamv1beta2/conversion_test.go b/bootstrap/kubeadm/types/upstreamv1beta2/conversion_test.go
index 9ad0feaa7..d5b72b1f7 100644
--- a/bootstrap/kubeadm/types/upstreamv1beta2/conversion_test.go
+++ b/bootstrap/kubeadm/types/upstreamv1beta2/conversion_test.go
@@ -66,7 +66,6 @@ func fuzzFuncs(_ runtimeserializer.CodecFactory) []interface{} {
 		clusterConfigurationFuzzer,
 		kubeadmInitConfigurationFuzzer,
 		kubeadmJoinConfigurationFuzzer,
-		kubeadmNodeRegistrationOptionsFuzzer,
 	}
 }
 
@@ -121,12 +120,3 @@ func kubeadmJoinConfigurationFuzzer(obj *bootstrapv1.JoinConfiguration, c fuzz.C
 	// v1beta1 --> upstream v1beta2 -> v1beta1 round trip errors.
 	obj.SkipPhases = nil
 }
-
-func kubeadmNodeRegistrationOptionsFuzzer(obj *bootstrapv1.NodeRegistrationOptions, c fuzz.Continue) {
-	c.FuzzNoCustom(obj)
-
-	// NodeRegistrationOptions.ImagePullPolicy does not exist in
-	// kubeadm v1beta2 API, so setting it to empty in order to
-	// avoid round trip errors.
-	obj.ImagePullPolicy = ""
-}
diff --git a/cmd/clusterctl/client/client_test.go b/cmd/clusterctl/client/client_test.go
index b1374b805..486384a2e 100644
--- a/cmd/clusterctl/client/client_test.go
+++ b/cmd/clusterctl/client/client_test.go
@@ -124,7 +124,7 @@ func (f fakeClient) ProcessYAML(options ProcessYAMLOptions) (YamlPrinter, error)
 	return f.internalClient.ProcessYAML(options)
 }
 
-func (f fakeClient) RolloutRestart(options RolloutRestartOptions) error {
+func (f fakeClient) RolloutRestart(options RolloutOptions) error {
 	return f.internalClient.RolloutRestart(options)
 }
 
@@ -132,15 +132,15 @@ func (f fakeClient) DescribeCluster(options DescribeClusterOptions) (*tree.Objec
 	return f.internalClient.DescribeCluster(options)
 }
 
-func (f fakeClient) RolloutPause(options RolloutPauseOptions) error {
+func (f fakeClient) RolloutPause(options RolloutOptions) error {
 	return f.internalClient.RolloutPause(options)
 }
 
-func (f fakeClient) RolloutResume(options RolloutResumeOptions) error {
+func (f fakeClient) RolloutResume(options RolloutOptions) error {
 	return f.internalClient.RolloutResume(options)
 }
 
-func (f fakeClient) RolloutUndo(options RolloutUndoOptions) error {
+func (f fakeClient) RolloutUndo(options RolloutOptions) error {
 	return f.internalClient.RolloutUndo(options)
 }
 
diff --git a/cmd/clusterctl/client/rollout_test.go b/cmd/clusterctl/client/rollout_test.go
index 00a7a77d2..d4c21926e 100644
--- a/cmd/clusterctl/client/rollout_test.go
+++ b/cmd/clusterctl/client/rollout_test.go
@@ -28,65 +28,29 @@ import (
 	"sigs.k8s.io/cluster-api/cmd/clusterctl/client/config"
 )
 
-func fakeClientForRollout() *fakeClient {
-	core := config.NewProvider("cluster-api", "https://somewhere.com", clusterctlv1.CoreProviderType)
-	infra := config.NewProvider("infra", "https://somewhere.com", clusterctlv1.InfrastructureProviderType)
-	md1 := &clusterv1.MachineDeployment{
-		TypeMeta: metav1.TypeMeta{
-			Kind:       "MachineDeployment",
-			APIVersion: "cluster.x-k8s.io/v1beta1",
-		},
-		ObjectMeta: metav1.ObjectMeta{
-			Namespace: "default",
-			Name:      "md-1",
-		},
-	}
-	md2 := &clusterv1.MachineDeployment{
-		TypeMeta: metav1.TypeMeta{
-			Kind:       "MachineDeployment",
-			APIVersion: "cluster.x-k8s.io/v1beta1",
-		},
-		ObjectMeta: metav1.ObjectMeta{
-			Namespace: "default",
-			Name:      "md-2",
-		},
-	}
-	config1 := newFakeConfig().
-		WithProvider(core).
-		WithProvider(infra)
-
-	cluster1 := newFakeCluster(cluster.Kubeconfig{Path: "kubeconfig", Context: "mgmt-context"}, config1).
-		WithProviderInventory(core.Name(), core.Type(), "v1.0.0", "cluster-api-system").
-		WithProviderInventory(infra.Name(), infra.Type(), "v2.0.0", "infra-system").
-		WithObjs(md1).
-		WithObjs(md2)
-
-	client := newFakeClient(config1).
-		WithCluster(cluster1)
-
-	return client
+type rolloutTest struct {
+	name    string
+	fields  fields
+	args    args
+	wantErr bool
+}
+type fields struct {
+	client *fakeClient
+}
+type args struct {
+	options RolloutOptions
 }
 
-func Test_clusterctlClient_RolloutRestart(t *testing.T) {
-	type fields struct {
-		client *fakeClient
-	}
-	type args struct {
-		options RolloutRestartOptions
-	}
-	tests := []struct {
-		name    string
-		fields  fields
-		args    args
-		wantErr bool
-	}{
+// genericTestCases are test cases that can be passed to any of the rollout subcommands.
+func genericTestCases() []rolloutTest {
+	return []rolloutTest{
 		{
 			name: "return an error if machinedeployment is not found",
 			fields: fields{
 				client: fakeClientForRollout(),
 			},
 			args: args{
-				options: RolloutRestartOptions{
+				options: RolloutOptions{
 					Kubeconfig: Kubeconfig{Path: "kubeconfig", Context: "mgmt-context"},
 					Resources:  []string{"machinedeployment/foo"},
 					Namespace:  "default",
@@ -100,7 +64,7 @@ func Test_clusterctlClient_RolloutRestart(t *testing.T) {
 				client: fakeClientForRollout(),
 			},
 			args: args{
-				options: RolloutRestartOptions{
+				options: RolloutOptions{
 					Kubeconfig: Kubeconfig{Path: "kubeconfig", Context: "mgmt-context"},
 					Resources:  []string{"machinedeployment/md-1", "machinedeployment/md-does-not-exist"},
 					Namespace:  "default",
@@ -114,7 +78,7 @@ func Test_clusterctlClient_RolloutRestart(t *testing.T) {
 				client: fakeClientForRollout(),
 			},
 			args: args{
-				options: RolloutRestartOptions{
+				options: RolloutOptions{
 					Kubeconfig: Kubeconfig{Path: "kubeconfig", Context: "mgmt-context"},
 					Resources:  []string{"foo/bar"},
 					Namespace:  "default",
@@ -128,20 +92,65 @@ func Test_clusterctlClient_RolloutRestart(t *testing.T) {
 				client: fakeClientForRollout(),
 			},
 			args: args{
-				options: RolloutRestartOptions{
+				options: RolloutOptions{
 					Kubeconfig: Kubeconfig{Path: "kubeconfig", Context: "mgmt-context"},
 					Namespace:  "default",
 				},
 			},
 			wantErr: true,
 		},
+	}
+}
+
+func fakeClientForRollout() *fakeClient {
+	core := config.NewProvider("cluster-api", "https://somewhere.com", clusterctlv1.CoreProviderType)
+	infra := config.NewProvider("infra", "https://somewhere.com", clusterctlv1.InfrastructureProviderType)
+	md1 := &clusterv1.MachineDeployment{
+		TypeMeta: metav1.TypeMeta{
+			Kind:       "MachineDeployment",
+			APIVersion: "cluster.x-k8s.io/v1beta1",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Namespace: "default",
+			Name:      "md-1",
+		},
+	}
+	md2 := &clusterv1.MachineDeployment{
+		TypeMeta: metav1.TypeMeta{
+			Kind:       "MachineDeployment",
+			APIVersion: "cluster.x-k8s.io/v1beta1",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Namespace: "default",
+			Name:      "md-2",
+		},
+	}
+	config1 := newFakeConfig().
+		WithProvider(core).
+		WithProvider(infra)
+
+	cluster1 := newFakeCluster(cluster.Kubeconfig{Path: "kubeconfig", Context: "mgmt-context"}, config1).
+		WithProviderInventory(core.Name(), core.Type(), "v1.0.0", "cluster-api-system").
+		WithProviderInventory(infra.Name(), infra.Type(), "v2.0.0", "infra-system").
+		WithObjs(md1).
+		WithObjs(md2)
+
+	client := newFakeClient(config1).
+		WithCluster(cluster1)
+
+	return client
+}
+
+func Test_clusterctlClient_RolloutRestart(t *testing.T) {
+	tests := genericTestCases()
+	additionalTests := []rolloutTest{
 		{
 			name: "do not return error if machinedeployment found",
 			fields: fields{
 				client: fakeClientForRollout(),
 			},
 			args: args{
-				options: RolloutRestartOptions{
+				options: RolloutOptions{
 					Kubeconfig: Kubeconfig{Path: "kubeconfig", Context: "mgmt-context"},
 					Resources:  []string{"machinedeployment/md-1"},
 					Namespace:  "default",
@@ -155,7 +164,7 @@ func Test_clusterctlClient_RolloutRestart(t *testing.T) {
 				client: fakeClientForRollout(),
 			},
 			args: args{
-				options: RolloutRestartOptions{
+				options: RolloutOptions{
 					Kubeconfig: Kubeconfig{Path: "kubeconfig", Context: "mgmt-context"},
 					Resources:  []string{"machinedeployment/md-1", "machinedeployment/md-2"},
 					Namespace:  "default",
@@ -165,6 +174,8 @@ func Test_clusterctlClient_RolloutRestart(t *testing.T) {
 		},
 	}
 
+	tests = append(tests, additionalTests...)
+
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
 			g := NewWithT(t)
@@ -180,74 +191,7 @@ func Test_clusterctlClient_RolloutRestart(t *testing.T) {
 }
 
 func Test_clusterctlClient_RolloutPause(t *testing.T) {
-	type fields struct {
-		client *fakeClient
-	}
-	type args struct {
-		options RolloutPauseOptions
-	}
-	tests := []struct {
-		name    string
-		fields  fields
-		args    args
-		wantErr bool
-	}{
-		{
-			name: "return an error if machinedeployment is not found",
-			fields: fields{
-				client: fakeClientForRollout(),
-			},
-			args: args{
-				options: RolloutPauseOptions{
-					Kubeconfig: Kubeconfig{Path: "kubeconfig", Context: "mgmt-context"},
-					Resources:  []string{"machinedeployment/foo"},
-					Namespace:  "default",
-				},
-			},
-			wantErr: true,
-		},
-		{
-			name: "return error if one of the machinedeployments is not found",
-			fields: fields{
-				client: fakeClientForRollout(),
-			},
-			args: args{
-				options: RolloutPauseOptions{
-					Kubeconfig: Kubeconfig{Path: "kubeconfig", Context: "mgmt-context"},
-					Resources:  []string{"machinedeployment/md-1", "machinedeployment/md-does-not-exist"},
-					Namespace:  "default",
-				},
-			},
-			wantErr: true,
-		},
-		{
-			name: "return error if unknown resource specified",
-			fields: fields{
-				client: fakeClientForRollout(),
-			},
-			args: args{
-				options: RolloutPauseOptions{
-					Kubeconfig: Kubeconfig{Path: "kubeconfig", Context: "mgmt-context"},
-					Resources:  []string{"foo/bar"},
-					Namespace:  "default",
-				},
-			},
-			wantErr: true,
-		},
-		{
-			name: "return error if no resource specified",
-			fields: fields{
-				client: fakeClientForRollout(),
-			},
-			args: args{
-				options: RolloutPauseOptions{
-					Kubeconfig: Kubeconfig{Path: "kubeconfig", Context: "mgmt-context"},
-					Namespace:  "default",
-				},
-			},
-			wantErr: true,
-		},
-	}
+	tests := genericTestCases()
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
 			g := NewWithT(t)
@@ -263,74 +207,7 @@ func Test_clusterctlClient_RolloutPause(t *testing.T) {
 }
 
 func Test_clusterctlClient_RolloutResume(t *testing.T) {
-	type fields struct {
-		client *fakeClient
-	}
-	type args struct {
-		options RolloutResumeOptions
-	}
-	tests := []struct {
-		name    string
-		fields  fields
-		args    args
-		wantErr bool
-	}{
-		{
-			name: "return an error if machinedeployment is not found",
-			fields: fields{
-				client: fakeClientForRollout(),
-			},
-			args: args{
-				options: RolloutResumeOptions{
-					Kubeconfig: Kubeconfig{Path: "kubeconfig", Context: "mgmt-context"},
-					Resources:  []string{"machinedeployment/foo"},
-					Namespace:  "default",
-				},
-			},
-			wantErr: true,
-		},
-		{
-			name: "return error if one of the machinedeployments is not found",
-			fields: fields{
-				client: fakeClientForRollout(),
-			},
-			args: args{
-				options: RolloutResumeOptions{
-					Kubeconfig: Kubeconfig{Path: "kubeconfig", Context: "mgmt-context"},
-					Resources:  []string{"machinedeployment/md-1", "machinedeployment/md-does-not-exist"},
-					Namespace:  "default",
-				},
-			},
-			wantErr: true,
-		},
-		{
-			name: "return error if unknown resource specified",
-			fields: fields{
-				client: fakeClientForRollout(),
-			},
-			args: args{
-				options: RolloutResumeOptions{
-					Kubeconfig: Kubeconfig{Path: "kubeconfig", Context: "mgmt-context"},
-					Resources:  []string{"foo/bar"},
-					Namespace:  "default",
-				},
-			},
-			wantErr: true,
-		},
-		{
-			name: "return error if no resource specified",
-			fields: fields{
-				client: fakeClientForRollout(),
-			},
-			args: args{
-				options: RolloutResumeOptions{
-					Kubeconfig: Kubeconfig{Path: "kubeconfig", Context: "mgmt-context"},
-					Namespace:  "default",
-				},
-			},
-			wantErr: true,
-		},
-	}
+	tests := genericTestCases()
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
 			g := NewWithT(t)
diff --git a/exp/runtime/topologymutation/walker_test.go b/exp/runtime/topologymutation/walkler_test.go
similarity index 98%
rename from exp/runtime/topologymutation/walker_test.go
rename to exp/runtime/topologymutation/walkler_test.go
index 52c0e60fb..7644ef1ab 100644
--- a/exp/runtime/topologymutation/walker_test.go
+++ b/exp/runtime/topologymutation/walkler_test.go
@@ -130,7 +130,7 @@ func Test_WalkTemplates(t *testing.T) {
 				CommonResponse: runtimehooksv1.CommonResponse{
 					Status: runtimehooksv1.ResponseStatusFailure,
 					Message: "no kind \"Unknown\" is registered for version \"controlplane.cluster.x-k8s." +
-						"io/v1beta1\" in scheme",
+						"io/v1beta1\" in scheme \"pkg/runtime/scheme.go:100\"",
 				},
 			},
 			options: []WalkTemplatesOption{
@@ -225,7 +225,7 @@ func Test_WalkTemplates(t *testing.T) {
 			WalkTemplates(context.Background(), decoder, request, response, mutatingFunc, tt.options...)
 
 			g.Expect(response.Status).To(Equal(tt.expectedResponse.Status))
-			g.Expect(response.Message).To(ContainSubstring(tt.expectedResponse.Message))
+			g.Expect(response.Message).To(Equal(tt.expectedResponse.Message))
 			for i, item := range response.Items {
 				expectedItem := tt.expectedResponse.Items[i]
 				g.Expect(item.PatchType).To(Equal(expectedItem.PatchType))
diff --git a/internal/controllers/clusterclass/clusterclass_controller_test.go b/internal/controllers/clusterclass/clusterclass_controller_test.go
index aa3dfe7c2..dbb4f2e56 100644
--- a/internal/controllers/clusterclass/clusterclass_controller_test.go
+++ b/internal/controllers/clusterclass/clusterclass_controller_test.go
@@ -19,10 +19,12 @@ package clusterclass
 import (
 	"context"
 	"fmt"
+	"reflect"
 	"testing"
 	"time"
 
 	. "github.com/onsi/gomega"
+	"github.com/pkg/errors"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"sigs.k8s.io/controller-runtime/pkg/client"
@@ -75,6 +77,24 @@ func TestClusterClassReconciler_reconcile(t *testing.T) {
 		WithControlPlaneTemplate(controlPlaneTemplate).
 		WithControlPlaneInfrastructureMachineTemplate(infraMachineTemplateControlPlane).
 		WithWorkerMachineDeploymentClasses(*machineDeploymentClass1, *machineDeploymentClass2).
+		WithVariables(
+			clusterv1.ClusterClassVariable{
+				Name:     "hdd",
+				Required: true,
+				Schema: clusterv1.VariableSchema{
+					OpenAPIV3Schema: clusterv1.JSONSchemaProps{
+						Type: "string",
+					},
+				},
+			},
+			clusterv1.ClusterClassVariable{
+				Name: "cpu",
+				Schema: clusterv1.VariableSchema{
+					OpenAPIV3Schema: clusterv1.JSONSchemaProps{
+						Type: "integer",
+					},
+				},
+			}).
 		Build()
 
 	// Create the set of initObjects from the objects above to add to the API server when the test environment starts.
@@ -106,10 +126,44 @@ func TestClusterClassReconciler_reconcile(t *testing.T) {
 
 		g.Expect(assertMachineDeploymentClasses(ctx, actualClusterClass, ns)).Should(Succeed())
 
+		g.Expect(assertStatusVariables(actualClusterClass)).Should(Succeed())
 		return nil
 	}, timeout).Should(Succeed())
 }
 
+func assertStatusVariables(actualClusterClass *clusterv1.ClusterClass) error {
+	// Assert that each inline variable definition has been exposed in the ClusterClass status.
+	for _, specVar := range actualClusterClass.Spec.Variables {
+		var found bool
+		for _, statusVar := range actualClusterClass.Status.Variables {
+			if specVar.Name != statusVar.Name {
+				continue
+			}
+			found = true
+			if statusVar.DefintionsConflict {
+				return errors.Errorf("ClusterClass status %s variable RequiresNamespace does not match. Expected %t , got %t", specVar.Name, false, statusVar.DefintionsConflict)
+			}
+			if len(statusVar.Definitions) != 1 {
+				return errors.Errorf("ClusterClass status has multiple definitions for variable %s. Expected a single definition", specVar.Name)
+			}
+			// For this test assume there is only one status variable definition, and that it should match the spec.
+			statusVarDefinition := statusVar.Definitions[0]
+			if statusVarDefinition.From != clusterv1.VariableDefinitionFromInline {
+				return errors.Errorf("ClusterClass status variable %s namespace field does not match. Expected %s. Got %s", statusVar.Name, clusterv1.VariableDefinitionFromInline, statusVarDefinition.From)
+			}
+			if specVar.Required != statusVarDefinition.Required {
+				return errors.Errorf("ClusterClass status variable %s required field does not match. Expecte %v. Got %v", specVar.Name, statusVarDefinition.Required, statusVarDefinition.Required)
+			}
+			if !reflect.DeepEqual(specVar.Schema, statusVarDefinition.Schema) {
+				return errors.Errorf("ClusterClass status variable %s schema does not match. Expected %v. Got %v", specVar.Name, specVar.Schema, statusVarDefinition.Schema)
+			}
+		}
+		if !found {
+			return errors.Errorf("ClusterClass does not define variable %s", specVar.Name)
+		}
+	}
+	return nil
+}
 func assertInfrastructureClusterTemplate(ctx context.Context, actualClusterClass *clusterv1.ClusterClass, ns *corev1.Namespace) error {
 	// Assert the infrastructure cluster template has the correct owner reference.
 	actualInfraClusterTemplate := builder.InfrastructureClusterTemplate("", "").Build()
diff --git a/test/go.mod b/test/go.mod
index 5f049d2c8..0b37aac71 100644
--- a/test/go.mod
+++ b/test/go.mod
@@ -10,20 +10,20 @@ require (
 	github.com/docker/go-connections v0.4.0
 	github.com/flatcar/ignition v0.36.2
 	github.com/go-logr/logr v1.2.3
-	github.com/onsi/ginkgo/v2 v2.8.0
-	github.com/onsi/gomega v1.26.0
+	github.com/onsi/ginkgo/v2 v2.7.0
+	github.com/onsi/gomega v1.25.0
 	github.com/pkg/errors v0.9.1
 	github.com/spf13/pflag v1.0.5
 	github.com/vincent-petithory/dataurl v1.0.0
-	k8s.io/api v0.26.1
-	k8s.io/apiextensions-apiserver v0.26.1
-	k8s.io/apimachinery v0.26.1
-	k8s.io/client-go v0.26.1
-	k8s.io/component-base v0.26.1
+	k8s.io/api v0.26.0
+	k8s.io/apiextensions-apiserver v0.26.0
+	k8s.io/apimachinery v0.26.0
+	k8s.io/client-go v0.26.0
+	k8s.io/component-base v0.26.0
 	k8s.io/klog/v2 v2.80.1
 	k8s.io/utils v0.0.0-20221128185143-99ec85e7a448
 	sigs.k8s.io/cluster-api v0.0.0-00010101000000-000000000000
-	sigs.k8s.io/controller-runtime v0.14.2
+	sigs.k8s.io/controller-runtime v0.14.1
 	sigs.k8s.io/kind v0.17.0
 	sigs.k8s.io/yaml v1.3.0
 )
@@ -61,7 +61,7 @@ require (
 	github.com/gogo/protobuf v1.3.2 // indirect
 	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
 	github.com/golang/protobuf v1.5.2 // indirect
-	github.com/google/cel-go v0.12.6 // indirect
+	github.com/google/cel-go v0.12.5 // indirect
 	github.com/google/gnostic v0.6.9 // indirect
 	github.com/google/go-cmp v0.5.9 // indirect
 	github.com/google/go-github/v48 v48.2.0 // indirect
@@ -122,7 +122,7 @@ require (
 	gopkg.in/ini.v1 v1.67.0 // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
-	k8s.io/apiserver v0.26.1 // indirect
+	k8s.io/apiserver v0.26.0 // indirect
 	k8s.io/cluster-bootstrap v0.25.0 // indirect
 	k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect
 	sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect
diff --git a/test/go.sum b/test/go.sum
index 3dd19a6a0..e7d1506a1 100644
--- a/test/go.sum
+++ b/test/go.sum
@@ -220,8 +220,8 @@ github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw
 github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
 github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
-github.com/google/cel-go v0.12.6 h1:kjeKudqV0OygrAqA9fX6J55S8gj+Jre2tckIm5RoG4M=
-github.com/google/cel-go v0.12.6/go.mod h1:Jk7ljRzLBhkmiAwBoUxB1sZSCVBAzkqPF25olK/iRDw=
+github.com/google/cel-go v0.12.5 h1:DmzaiSgoaqGCjtpPQWl26/gND+yRpim56H1jCVev6d8=
+github.com/google/cel-go v0.12.5/go.mod h1:Jk7ljRzLBhkmiAwBoUxB1sZSCVBAzkqPF25olK/iRDw=
 github.com/google/gnostic v0.6.9 h1:ZK/5VhkoX835RikCHpSUJV9a+S3e1zLh59YnyWeBW+0=
 github.com/google/gnostic v0.6.9/go.mod h1:Nm8234We1lq6iB9OmlgNv3nH91XLLVZHCDayfA3xq+E=
 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
@@ -381,10 +381,10 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW
 github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
 github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
-github.com/onsi/ginkgo/v2 v2.8.0 h1:pAM+oBNPrpXRs+E/8spkeGx9QgekbRVyr74EUvRVOUI=
-github.com/onsi/ginkgo/v2 v2.8.0/go.mod h1:6JsQiECmxCa3V5st74AL/AmsV482EDdVrGaVW6z3oYU=
-github.com/onsi/gomega v1.26.0 h1:03cDLK28U6hWvCAns6NeydX3zIm4SF3ci69ulidS32Q=
-github.com/onsi/gomega v1.26.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM=
+github.com/onsi/ginkgo/v2 v2.7.0 h1:/XxtEV3I3Eif/HobnVx9YmJgk8ENdRsuUmM+fLCFNow=
+github.com/onsi/ginkgo/v2 v2.7.0/go.mod h1:yjiuMwPokqY1XauOgju45q3sJt6VzQ/Fict1LFVcsAo=
+github.com/onsi/gomega v1.25.0 h1:Vw7br2PCDYijJHSfBOWhov+8cAnUf8MfMaIOV323l6Y=
+github.com/onsi/gomega v1.25.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM=
 github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
 github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
@@ -935,20 +935,20 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
 honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
 honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
-k8s.io/api v0.26.1 h1:f+SWYiPd/GsiWwVRz+NbFyCgvv75Pk9NK6dlkZgpCRQ=
-k8s.io/api v0.26.1/go.mod h1:xd/GBNgR0f707+ATNyPmQ1oyKSgndzXij81FzWGsejg=
-k8s.io/apiextensions-apiserver v0.26.1 h1:cB8h1SRk6e/+i3NOrQgSFij1B2S0Y0wDoNl66bn8RMI=
-k8s.io/apiextensions-apiserver v0.26.1/go.mod h1:AptjOSXDGuE0JICx/Em15PaoO7buLwTs0dGleIHixSM=
-k8s.io/apimachinery v0.26.1 h1:8EZ/eGJL+hY/MYCNwhmDzVqq2lPl3N3Bo8rvweJwXUQ=
-k8s.io/apimachinery v0.26.1/go.mod h1:tnPmbONNJ7ByJNz9+n9kMjNP8ON+1qoAIIC70lztu74=
-k8s.io/apiserver v0.26.1 h1:6vmnAqCDO194SVCPU3MU8NcDgSqsUA62tBUSWrFXhsc=
-k8s.io/apiserver v0.26.1/go.mod h1:wr75z634Cv+sifswE9HlAo5FQ7UoUauIICRlOE+5dCg=
-k8s.io/client-go v0.26.1 h1:87CXzYJnAMGaa/IDDfRdhTzxk/wzGZ+/HUQpqgVSZXU=
-k8s.io/client-go v0.26.1/go.mod h1:IWNSglg+rQ3OcvDkhY6+QLeasV4OYHDjdqeWkDQZwGE=
+k8s.io/api v0.26.0 h1:IpPlZnxBpV1xl7TGk/X6lFtpgjgntCg8PJ+qrPHAC7I=
+k8s.io/api v0.26.0/go.mod h1:k6HDTaIFC8yn1i6pSClSqIwLABIcLV9l5Q4EcngKnQg=
+k8s.io/apiextensions-apiserver v0.26.0 h1:Gy93Xo1eg2ZIkNX/8vy5xviVSxwQulsnUdQ00nEdpDo=
+k8s.io/apiextensions-apiserver v0.26.0/go.mod h1:7ez0LTiyW5nq3vADtK6C3kMESxadD51Bh6uz3JOlqWQ=
+k8s.io/apimachinery v0.26.0 h1:1feANjElT7MvPqp0JT6F3Ss6TWDwmcjLypwoPpEf7zg=
+k8s.io/apimachinery v0.26.0/go.mod h1:tnPmbONNJ7ByJNz9+n9kMjNP8ON+1qoAIIC70lztu74=
+k8s.io/apiserver v0.26.0 h1:q+LqIK5EZwdznGZb8bq0+a+vCqdeEEe4Ux3zsOjbc4o=
+k8s.io/apiserver v0.26.0/go.mod h1:aWhlLD+mU+xRo+zhkvP/gFNbShI4wBDHS33o0+JGI84=
+k8s.io/client-go v0.26.0 h1:lT1D3OfO+wIi9UFolCrifbjUUgu7CpLca0AD8ghRLI8=
+k8s.io/client-go v0.26.0/go.mod h1:I2Sh57A79EQsDmn7F7ASpmru1cceh3ocVT9KlX2jEZg=
 k8s.io/cluster-bootstrap v0.25.0 h1:KJ2/r0dV+bLfTK5EBobAVKvjGel3N4Qqh3bvnzh9qPk=
 k8s.io/cluster-bootstrap v0.25.0/go.mod h1:x/TCtY3EiuR/rODkA3SvVQT3uSssQLf9cXcmSjdDTe0=
-k8s.io/component-base v0.26.1 h1:4ahudpeQXHZL5kko+iDHqLj/FSGAEUnSVO0EBbgDd+4=
-k8s.io/component-base v0.26.1/go.mod h1:VHrLR0b58oC035w6YQiBSbtsf0ThuSwXP+p5dD/kAWU=
+k8s.io/component-base v0.26.0 h1:0IkChOCohtDHttmKuz+EP3j3+qKmV55rM9gIFTXA7Vs=
+k8s.io/component-base v0.26.0/go.mod h1:lqHwlfV1/haa14F/Z5Zizk5QmzaVf23nQzCwVOQpfC8=
 k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4=
 k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
 k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 h1:+70TFaan3hfJzs+7VK2o+OGxg8HsuBr/5f6tVAjDu6E=
@@ -958,8 +958,8 @@ k8s.io/utils v0.0.0-20221128185143-99ec85e7a448/go.mod h1:OLgZIPagt7ERELqWJFomSt
 rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
 rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
 rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
-sigs.k8s.io/controller-runtime v0.14.2 h1:P6IwDhbsRWsBClt/8/h8Zy36bCuGuW5Op7MHpFrN/60=
-sigs.k8s.io/controller-runtime v0.14.2/go.mod h1:WqIdsAY6JBsjfc/CqO0CORmNtoCtE4S6qbPc9s68h+0=
+sigs.k8s.io/controller-runtime v0.14.1 h1:vThDes9pzg0Y+UbCPY3Wj34CGIYPgdmspPm2GIpxpzM=
+sigs.k8s.io/controller-runtime v0.14.1/go.mod h1:GaRkrY8a7UZF0kqFFbUKG7n9ICiTY5T55P1RiE3UZlU=
 sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k=
 sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
 sigs.k8s.io/kind v0.17.0 h1:CScmGz/wX66puA06Gj8OZb76Wmk7JIjgWf5JDvY7msM=
diff --git a/test/infrastructure/docker/config/crd/bases/infrastructure.cluster.x-k8s.io_dockerclusters.yaml b/test/infrastructure/docker/config/crd/bases/infrastructure.cluster.x-k8s.io_dockerclusters.yaml
index 98cc5947b..a2ed0cb08 100644
--- a/test/infrastructure/docker/config/crd/bases/infrastructure.cluster.x-k8s.io_dockerclusters.yaml
+++ b/test/infrastructure/docker/config/crd/bases/infrastructure.cluster.x-k8s.io_dockerclusters.yaml
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   annotations:
-    controller-gen.kubebuilder.io/version: v0.11.2
+    controller-gen.kubebuilder.io/version: v0.11.1
   creationTimestamp: null
   name: dockerclusters.infrastructure.cluster.x-k8s.io
 spec:
diff --git a/test/infrastructure/docker/config/crd/bases/infrastructure.cluster.x-k8s.io_dockerclustertemplates.yaml b/test/infrastructure/docker/config/crd/bases/infrastructure.cluster.x-k8s.io_dockerclustertemplates.yaml
index 0d0df13dd..496b50da6 100644
--- a/test/infrastructure/docker/config/crd/bases/infrastructure.cluster.x-k8s.io_dockerclustertemplates.yaml
+++ b/test/infrastructure/docker/config/crd/bases/infrastructure.cluster.x-k8s.io_dockerclustertemplates.yaml
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   annotations:
-    controller-gen.kubebuilder.io/version: v0.11.2
+    controller-gen.kubebuilder.io/version: v0.11.1
   creationTimestamp: null
   name: dockerclustertemplates.infrastructure.cluster.x-k8s.io
 spec:
diff --git a/test/infrastructure/docker/config/crd/bases/infrastructure.cluster.x-k8s.io_dockermachinepools.yaml b/test/infrastructure/docker/config/crd/bases/infrastructure.cluster.x-k8s.io_dockermachinepools.yaml
index 8893482ce..1752fc535 100644
--- a/test/infrastructure/docker/config/crd/bases/infrastructure.cluster.x-k8s.io_dockermachinepools.yaml
+++ b/test/infrastructure/docker/config/crd/bases/infrastructure.cluster.x-k8s.io_dockermachinepools.yaml
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   annotations:
-    controller-gen.kubebuilder.io/version: v0.11.2
+    controller-gen.kubebuilder.io/version: v0.11.1
   creationTimestamp: null
   name: dockermachinepools.infrastructure.cluster.x-k8s.io
 spec:
diff --git a/test/infrastructure/docker/config/crd/bases/infrastructure.cluster.x-k8s.io_dockermachines.yaml b/test/infrastructure/docker/config/crd/bases/infrastructure.cluster.x-k8s.io_dockermachines.yaml
index 6c25858ef..1c2b99831 100644
--- a/test/infrastructure/docker/config/crd/bases/infrastructure.cluster.x-k8s.io_dockermachines.yaml
+++ b/test/infrastructure/docker/config/crd/bases/infrastructure.cluster.x-k8s.io_dockermachines.yaml
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   annotations:
-    controller-gen.kubebuilder.io/version: v0.11.2
+    controller-gen.kubebuilder.io/version: v0.11.1
   creationTimestamp: null
   name: dockermachines.infrastructure.cluster.x-k8s.io
 spec:
diff --git a/test/infrastructure/docker/config/crd/bases/infrastructure.cluster.x-k8s.io_dockermachinetemplates.yaml b/test/infrastructure/docker/config/crd/bases/infrastructure.cluster.x-k8s.io_dockermachinetemplates.yaml
index ae37cf6c2..80ec348e6 100644
--- a/test/infrastructure/docker/config/crd/bases/infrastructure.cluster.x-k8s.io_dockermachinetemplates.yaml
+++ b/test/infrastructure/docker/config/crd/bases/infrastructure.cluster.x-k8s.io_dockermachinetemplates.yaml
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   annotations:
-    controller-gen.kubebuilder.io/version: v0.11.2
+    controller-gen.kubebuilder.io/version: v0.11.1
   creationTimestamp: null
   name: dockermachinetemplates.infrastructure.cluster.x-k8s.io
 spec:
diff --git a/test/infrastructure/docker/exp/internal/controllers/dockermachinepool_controller.go b/test/infrastructure/docker/exp/internal/controllers/dockermachinepool_controller.go
index e6d5938d5..9c59ccd53 100644
--- a/test/infrastructure/docker/exp/internal/controllers/dockermachinepool_controller.go
+++ b/test/infrastructure/docker/exp/internal/controllers/dockermachinepool_controller.go
@@ -132,7 +132,7 @@ func (r *DockerMachinePoolReconciler) Reconcile(ctx context.Context, req ctrl.Re
 	// Requeue if the reconcile failed because the ClusterCacheTracker was locked for
 	// the current cluster because of concurrent access.
 	if errors.Is(err, remote.ErrClusterLocked) {
-		log.V(5).Info("Requeuing because another worker has the lock on the ClusterCacheTracker")
+		log.V(5).Info("Requeueing because another worker has the lock on the ClusterCacheTracker")
 		return ctrl.Result{Requeue: true}, nil
 	}
 	return res, err
diff --git a/test/infrastructure/docker/internal/controllers/dockermachine_controller.go b/test/infrastructure/docker/internal/controllers/dockermachine_controller.go
index edc90b423..2724a81dd 100644
--- a/test/infrastructure/docker/internal/controllers/dockermachine_controller.go
+++ b/test/infrastructure/docker/internal/controllers/dockermachine_controller.go
@@ -171,7 +171,7 @@ func (r *DockerMachineReconciler) Reconcile(ctx context.Context, req ctrl.Reques
 	// Requeue if the reconcile failed because the ClusterCacheTracker was locked for
 	// the current cluster because of concurrent access.
 	if errors.Is(err, remote.ErrClusterLocked) {
-		log.V(5).Info("Requeuing because another worker has the lock on the ClusterCacheTracker")
+		log.V(5).Info("Requeueing because another worker has the lock on the ClusterCacheTracker")
 		return ctrl.Result{Requeue: true}, nil
 	}
 	return res, err