Repository: kubernetes-sigs/cluster-api
Pull request #7855 (2023-01-05 14:16:47)

📖 Amend KCP proposal with remediation while provisioning the CP
**What this PR does / why we need it**:
This PR proposes an amendment to KCP remediation so it will be possible to remediate failure happening while provisioning the CP (both first CP and CP machines while current replica < desired replica); 

It also introduces opt-in control of remediation retry behavior.

**Which issue(s) this PR fixes**:
Rif https://github.com/kubernetes-sigs/cluster-api/issues/7496


sbueringer: Thx!

/lgtm
k8s-ci-robot: LGTM label has been added.  <details>Git tree hash: 0e6c9a507def215537b3239e3e82a3c70d727137</details>
k8s-ci-robot: @g-gaston: changing LGTM is restricted to collaborators

<details>

In response to [this](https://github.com/kubernetes-sigs/cluster-api/pull/7855#pullrequestreview-1247457490):

>/lgtm
>Thanks for this!


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>

Patch:
diff --git a/.github/workflows/dependabot.yml b/.github/workflows/dependabot.yml
index 9df43336c..3f123df94 100644
--- a/.github/workflows/dependabot.yml
+++ b/.github/workflows/dependabot.yml
@@ -23,8 +23,8 @@ jobs:
         go-version: '1.19'
       id: go
     - name: Check out code into the Go module directory
-      uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # tag=v3.3.0
-    - uses: actions/cache@58c146cc91c5b9e778e71775dfe9bf1442ad9a12 # tag=v3.2.3
+      uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # tag=v3.2.0
+    - uses: actions/cache@4723a57e26efda3a62cbde1812113b730952852d # tag=v3.2.2
       name: Restore go cache
       with:
         path: |
diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml
index 8de946dfe..a6206ee6e 100644
--- a/.github/workflows/golangci-lint.yml
+++ b/.github/workflows/golangci-lint.yml
@@ -18,7 +18,7 @@ jobs:
           - test
           - hack/tools
     steps:
-      - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # tag=v3.3.0
+      - uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # tag=v3.2.0
       - uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # tag=v3.5.0
         with:
           go-version: 1.19
diff --git a/.github/workflows/lint-docs-pr.yaml b/.github/workflows/lint-docs-pr.yaml
index c6038fd72..bb540ce75 100644
--- a/.github/workflows/lint-docs-pr.yaml
+++ b/.github/workflows/lint-docs-pr.yaml
@@ -14,7 +14,7 @@ jobs:
     name: Broken Links
     runs-on: ubuntu-latest
     steps:
-    - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # tag=v3.3.0
+    - uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # tag=v3.2.0
     - uses: gaurav-nelson/github-action-markdown-link-check@5c5dfc0ac2e225883c0e5f03a85311ec2830d368 # tag=v1
       with:
         use-quiet-mode: 'yes'
diff --git a/.github/workflows/lint-docs-weekly.yml b/.github/workflows/lint-docs-weekly.yml
index 1a375ddb9..3b35199b9 100644
--- a/.github/workflows/lint-docs-weekly.yml
+++ b/.github/workflows/lint-docs-weekly.yml
@@ -12,7 +12,7 @@ jobs:
     name: Broken Links
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # tag=v3.3.0
+      - uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # tag=v3.2.0
       - uses: gaurav-nelson/github-action-markdown-link-check@5c5dfc0ac2e225883c0e5f03a85311ec2830d368 # tag=v1
         with:
           use-quiet-mode: 'yes'
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 817309621..c400a7b89 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -17,7 +17,7 @@ jobs:
       - name: Set env
         run:  echo "RELEASE_TAG=${GITHUB_REF:10}" >> $GITHUB_ENV
       - name: checkout code
-        uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # tag=v3.3.0
+        uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # tag=v3.2.0
         with:
           fetch-depth: 0
       - name: Install go
diff --git a/.github/workflows/scan.yml b/.github/workflows/scan.yml
index abc86e2d2..648f337b1 100644
--- a/.github/workflows/scan.yml
+++ b/.github/workflows/scan.yml
@@ -9,19 +9,14 @@ permissions: {}
 
 jobs:
   scan:
-    strategy:
-      matrix:
-        branch: [ main, release-1.3, release-1.2 ]
     name: Trivy
     runs-on: ubuntu-latest
     steps:
     - name: Check out code
-      uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # tag=v3.3.0
-      with:
-        ref: ${{ matrix.branch }}
+      uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3.1.0
     - name: Setup go
       uses: actions/setup-go@d0a58c1c4d2b25278816e339b944508c875f3613 # tag=v3.4.0
       with:
         go-version: 1.19
     - name: Run verify container script
-      run: make verify-container-images
\ No newline at end of file
+      run: make verify-container-images
diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml
index 4f84c354c..0d0e27023 100644
--- a/.github/workflows/verify.yml
+++ b/.github/workflows/verify.yml
@@ -14,6 +14,9 @@ jobs:
     steps:
     - name: Verifier action
       id: verifier
-      uses: kubernetes-sigs/kubebuilder-release-tools@e90dee822876042e99b7584f99a9cc9f9d74ed17 # tag=v0.3.0
+      uses: kubernetes-sigs/kubebuilder-release-tools@v0.2.0
+      # Don't run this step on PRs created by the cherry-pick bot,
+      # as the PR description is too short, but otherwise the PRs are fine.
+      if: ${{ github.event.pull_request.head.repo.owner.login != 'k8s-infra-cherrypick-robot' }}
       with:
         github_token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.gitignore b/.gitignore
index f39502bcb..04c05814a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,11 +11,11 @@ hack/tools/bin
 *.test
 
 # E2E test templates
-test/e2e/data/infrastructure-docker/main/cluster-template*.yaml
-test/e2e/data/infrastructure-docker/v0.3/cluster-template*.yaml
-test/e2e/data/infrastructure-docker/v0.4/cluster-template*.yaml
-test/e2e/data/infrastructure-docker/v1.2/cluster-template*.yaml
-test/e2e/data/infrastructure-docker/v1.3/cluster-template*.yaml
+test/e2e/data/infrastructure-docker/v1alpha3/cluster-template*.yaml
+test/e2e/data/infrastructure-docker/v1alpha4/cluster-template*.yaml
+test/e2e/data/infrastructure-docker/v1beta1/v1.2/cluster-template*.yaml
+test/e2e/data/infrastructure-docker/v1beta1/v1.3/cluster-template*.yaml
+test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template*.yaml
 
 # E2e test extension deployment
 test/e2e/data/test-extension/deployment.yaml
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 92d53aa3f..b4d10edd4 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -82,26 +82,16 @@ The test module, clusterctl, and experiments do not provide any backward compati
 
 #### Backporting a patch
 
-We generally do not accept PRs directly against release branches, while we might accept backports of fixes/changes already
-merged into the main branch.
-
-Any backport MUST not be breaking for either API or behavioral changes. 
-
-We generally allow backports of following changes to all supported branches:
-- Critical bugs fixes, security issue fixes, or fixes for bugs without easy workarounds.
-- Dependency bumps for CVE (usually limited to CVE resolution; backports of non-CVE related version bumps are considered exceptions to be evaluated case by case)
-- Cert-manager version bumps (to avoid having releases with cert-manager versions that are out of support, when possible)
-- Changes required to support new Kubernetes versions, when possible. See [supported Kubernetes versions](https://cluster-api.sigs.k8s.io/reference/versions.html#supported-kubernetes-versions) for more details.
-
-We generally allow backports of following changes only to the latest supported branch:
-- Improvements to existing docs (the latest supported branch hosts the current version of the book)
+We only accept backports of critical bugs, security issues, or bugs without easy workarounds, any
+backport MUST not be breaking for either API or behavioral changes. In order to improve user/developer experience
+maintainers can choose to backport:
+- Doc improvements
 - Improvements to CI signal
-- Improvements to the test framework
-
-Like any other activity in the project, backporting a fix/change is a community-driven effort and requires that someone volunteers to own the task. 
-In most cases, the cherry-pick bot can (and should) be used to automate opening a cherry-pick PR.
+- Improvements to the test framework (enabling improvements to provider's test signal)
+- Cert-manager bump (to avoid having branch using cert-manager versions out of support, when possible)
+- Changes required to support new Kubernetes versions, when possible.
 
-We generally do not accept backports to Cluster API release branches that are [out of support](https://github.com/kubernetes-sigs/cluster-api/blob/main/CONTRIBUTING.md#support-and-guarantees).
+We generally do not accept PRs against older release branches.
 
 ### APIs
 
diff --git a/Makefile b/Makefile
index 8be1b93ae..3eadbeb66 100644
--- a/Makefile
+++ b/Makefile
@@ -82,8 +82,8 @@ GINKGO_FOCUS ?=
 GINKGO_SKIP ?=
 GINKGO_NODES ?= 1
 GINKGO_TIMEOUT ?= 2h
-GINKGO_POLL_PROGRESS_AFTER ?= 60m
-GINKGO_POLL_PROGRESS_INTERVAL ?= 5m
+GINKGO_POLL_PROGRESS_AFTER ?= 10m
+GINKGO_POLL_PROGRESS_INTERVAL ?= 1m
 E2E_CONF_FILE ?= $(ROOT_DIR)/$(TEST_DIR)/e2e/config/docker.yaml
 SKIP_RESOURCE_CLEANUP ?= false
 USE_EXISTING_CLUSTER ?= false
@@ -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.1
+CONTROLLER_GEN_VER := v0.10.0
 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
@@ -472,46 +472,56 @@ generate-modules: ## Run go mod tidy to ensure modules are up to date
 	cd $(TEST_DIR); go mod tidy
 
 .PHONY: generate-e2e-templates
-generate-e2e-templates: $(KUSTOMIZE) $(addprefix generate-e2e-templates-, v0.3 v0.4 v1.2 v1.3 main) ## Generate cluster templates for all versions
+generate-e2e-templates: $(KUSTOMIZE) $(addprefix generate-e2e-templates-, v1alpha3 v1alpha4 v1.2 v1.3 v1beta1) ## Generate cluster templates for all versions
 
 DOCKER_TEMPLATES := test/e2e/data/infrastructure-docker
 
-.PHONY: generate-e2e-templates-v0.3
-generate-e2e-templates-v0.3: $(KUSTOMIZE)
-	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/v0.3/cluster-template --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/v0.3/cluster-template.yaml
-
-.PHONY: generate-e2e-templates-v0.4
-generate-e2e-templates-v0.4: $(KUSTOMIZE)
-	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/v0.4/cluster-template --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/v0.4/cluster-template.yaml
+.PHONY: generate-e2e-templates-v1alpha3
+generate-e2e-templates-v1alpha3: $(KUSTOMIZE)
+	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/v1alpha3/cluster-template --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/v1alpha3/cluster-template.yaml
+
+.PHONY: generate-e2e-templates-v1alpha4
+generate-e2e-templates-v1alpha4: $(KUSTOMIZE)
+	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/v1alpha4/cluster-template --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/v1alpha4/cluster-template.yaml
+	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/v1alpha4/cluster-template-md-remediation --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/v1alpha4/cluster-template-md-remediation.yaml
+	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/v1alpha4/cluster-template-kcp-remediation --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/v1alpha4/cluster-template-kcp-remediation.yaml
+	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/v1alpha4/cluster-template-kcp-adoption/step1 --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/v1alpha4/cluster-template-kcp-adoption.yaml
+	echo "---" >> $(DOCKER_TEMPLATES)/v1alpha4/cluster-template-kcp-adoption.yaml
+	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/v1alpha4/cluster-template-kcp-adoption/step2 --load-restrictor LoadRestrictionsNone >> $(DOCKER_TEMPLATES)/v1alpha4/cluster-template-kcp-adoption.yaml
+	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/v1alpha4/cluster-template-machine-pool --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/v1alpha4/cluster-template-machine-pool.yaml
+	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/v1alpha4/cluster-template-node-drain --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/v1alpha4/cluster-template-node-drain.yaml
+	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/v1alpha4/cluster-template-upgrades --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/v1alpha4/cluster-template-upgrades.yaml
+	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/v1alpha4/cluster-template-kcp-scale-in --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/v1alpha4/cluster-template-kcp-scale-in.yaml
+	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/v1alpha4/cluster-template-ipv6 --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/v1alpha4/cluster-template-ipv6.yaml
 
 .PHONY: generate-e2e-templates-v1.2
 generate-e2e-templates-v1.2: $(KUSTOMIZE)
-	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/v1.2/cluster-template --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/v1.2/cluster-template.yaml
-	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/v1.2/cluster-template-topology --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/v1.2/cluster-template-topology.yaml
+	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/v1beta1/v1.2/cluster-template --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/v1beta1/v1.2/cluster-template.yaml
+	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/v1beta1/v1.2/cluster-template-topology --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/v1beta1/v1.2/cluster-template-topology.yaml
 
 .PHONY: generate-e2e-templates-v1.3
 generate-e2e-templates-v1.3: $(KUSTOMIZE)
-	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/v1.3/cluster-template --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/v1.3/cluster-template.yaml
-	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/v1.3/cluster-template-topology --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/v1.3/cluster-template-topology.yaml
-
-.PHONY: generate-e2e-templates-main
-generate-e2e-templates-main: $(KUSTOMIZE)
-	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/main/cluster-template --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/main/cluster-template.yaml
-	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/main/cluster-template-md-remediation --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/main/cluster-template-md-remediation.yaml
-	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/main/cluster-template-kcp-remediation --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/main/cluster-template-kcp-remediation.yaml
-	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/main/cluster-template-kcp-adoption/step1 --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/main/cluster-template-kcp-adoption.yaml
-	echo "---" >> $(DOCKER_TEMPLATES)/main/cluster-template-kcp-adoption.yaml
-	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/main/cluster-template-kcp-adoption/step2 --load-restrictor LoadRestrictionsNone >> $(DOCKER_TEMPLATES)/main/cluster-template-kcp-adoption.yaml
-	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/main/cluster-template-machine-pool --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/main/cluster-template-machine-pool.yaml
-	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/main/cluster-template-node-drain --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/main/cluster-template-node-drain.yaml
-	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/main/cluster-template-upgrades --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/main/cluster-template-upgrades.yaml
-	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/main/cluster-template-upgrades-cgroupfs --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/main/cluster-template-upgrades-cgroupfs.yaml
-	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/main/cluster-template-upgrades-runtimesdk --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/main/cluster-template-upgrades-runtimesdk.yaml
-	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/main/cluster-template-kcp-scale-in --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/main/cluster-template-kcp-scale-in.yaml
-	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/main/cluster-template-ipv6 --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/main/cluster-template-ipv6.yaml
-	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/main/cluster-template-topology-single-node-cluster --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/main/cluster-template-topology-single-node-cluster.yaml
-	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/main/cluster-template-topology --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/main/cluster-template-topology.yaml
-	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/main/cluster-template-ignition --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/main/cluster-template-ignition.yaml
+	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/v1beta1/v1.3/cluster-template --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/v1beta1/v1.3/cluster-template.yaml
+	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/v1beta1/v1.3/cluster-template-topology --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/v1beta1/v1.3/cluster-template-topology.yaml
+
+.PHONY: generate-e2e-templates-v1beta1
+generate-e2e-templates-v1beta1: $(KUSTOMIZE)
+	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/v1beta1/main/cluster-template --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/v1beta1/main/cluster-template.yaml
+	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/v1beta1/main/cluster-template-md-remediation --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/v1beta1/main/cluster-template-md-remediation.yaml
+	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/v1beta1/main/cluster-template-kcp-remediation --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/v1beta1/main/cluster-template-kcp-remediation.yaml
+	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/v1beta1/main/cluster-template-kcp-adoption/step1 --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/v1beta1/main/cluster-template-kcp-adoption.yaml
+	echo "---" >> $(DOCKER_TEMPLATES)/v1beta1/main/cluster-template-kcp-adoption.yaml
+	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/v1beta1/main/cluster-template-kcp-adoption/step2 --load-restrictor LoadRestrictionsNone >> $(DOCKER_TEMPLATES)/v1beta1/main/cluster-template-kcp-adoption.yaml
+	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/v1beta1/main/cluster-template-machine-pool --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/v1beta1/main/cluster-template-machine-pool.yaml
+	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/v1beta1/main/cluster-template-node-drain --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/v1beta1/main/cluster-template-node-drain.yaml
+	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/v1beta1/main/cluster-template-upgrades --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/v1beta1/main/cluster-template-upgrades.yaml
+	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/v1beta1/main/cluster-template-upgrades-cgroupfs --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/v1beta1/main/cluster-template-upgrades-cgroupfs.yaml
+	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/v1beta1/main/cluster-template-upgrades-runtimesdk --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/v1beta1/main/cluster-template-upgrades-runtimesdk.yaml
+	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/v1beta1/main/cluster-template-kcp-scale-in --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/v1beta1/main/cluster-template-kcp-scale-in.yaml
+	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/v1beta1/main/cluster-template-ipv6 --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/v1beta1/main/cluster-template-ipv6.yaml
+	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/v1beta1/main/cluster-template-topology-single-node-cluster --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/v1beta1/main/cluster-template-topology-single-node-cluster.yaml
+	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/v1beta1/main/cluster-template-topology --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/v1beta1/main/cluster-template-topology.yaml
+	$(KUSTOMIZE) build $(DOCKER_TEMPLATES)/v1beta1/main/cluster-template-ignition --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/v1beta1/main/cluster-template-ignition.yaml
 
 .PHONY: generate-test-extension-deployment
 generate-test-extension-deployment: $(KUSTOMIZE)
diff --git a/bootstrap/kubeadm/api/v1beta1/kubeadm_types.go b/bootstrap/kubeadm/api/v1beta1/kubeadm_types.go
index dc2bb6087..f8ea593ec 100644
--- a/bootstrap/kubeadm/api/v1beta1/kubeadm_types.go
+++ b/bootstrap/kubeadm/api/v1beta1/kubeadm_types.go
@@ -125,16 +125,9 @@ type ClusterConfiguration struct {
 	CertificatesDir string `json:"certificatesDir,omitempty"`
 
 	// ImageRepository sets the container registry to pull images from.
-	// * If not set, the default registry of kubeadm will be used, i.e.
-	//   * registry.k8s.io (new registry): >= v1.22.17, >= v1.23.15, >= v1.24.9, >= v1.25.0
-	//   * k8s.gcr.io (old registry): all older versions
-	//   Please note that when imageRepository is not set we don't allow upgrades to
-	//   versions >= v1.22.0 which use the old registry (k8s.gcr.io). Please use
-	//   a newer patch version with the new registry instead (i.e. >= v1.22.17,
-	//   >= v1.23.15, >= v1.24.9, >= v1.25.0).
-	// * If the version is a CI build (kubernetes version starts with `ci/` or `ci-cross/`)
-	//  `gcr.io/k8s-staging-ci-images` will be used as a default for control plane components
-	//   and for kube-proxy, while `registry.k8s.io` will be used for all the other images.
+	// If empty, `registry.k8s.io` will be used by default; in case of kubernetes version is a CI build (kubernetes version starts with `ci/` or `ci-cross/`)
+	// `gcr.io/k8s-staging-ci-images` will be used as a default for control plane components and for kube-proxy, while `registry.k8s.io`
+	// will be used for all the other images.
 	// +optional
 	ImageRepository string `json:"imageRepository,omitempty"`
 
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 6075efcf9..71c60711a 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.1
+    controller-gen.kubebuilder.io/version: v0.10.0
   creationTimestamp: null
   name: kubeadmconfigs.bootstrap.cluster.x-k8s.io
 spec:
@@ -2243,19 +2243,13 @@ spec:
                     description: FeatureGates enabled by the user.
                     type: object
                   imageRepository:
-                    description: 'ImageRepository sets the container registry to pull
-                      images from. * If not set, the default registry of kubeadm will
-                      be used, i.e. * registry.k8s.io (new registry): >= v1.22.17,
-                      >= v1.23.15, >= v1.24.9, >= v1.25.0 * k8s.gcr.io (old registry):
-                      all older versions Please note that when imageRepository is
-                      not set we don''t allow upgrades to versions >= v1.22.0 which
-                      use the old registry (k8s.gcr.io). Please use a newer patch
-                      version with the new registry instead (i.e. >= v1.22.17, >=
-                      v1.23.15, >= v1.24.9, >= v1.25.0). * If the version is a CI
-                      build (kubernetes version starts with `ci/` or `ci-cross/`)
-                      `gcr.io/k8s-staging-ci-images` will be used as a default for
-                      control plane components and for kube-proxy, while `registry.k8s.io`
-                      will be used for all the other images.'
+                    description: ImageRepository sets the container registry to pull
+                      images from. If empty, `registry.k8s.io` will be used by default;
+                      in case of kubernetes version is a CI build (kubernetes version
+                      starts with `ci/` or `ci-cross/`) `gcr.io/k8s-staging-ci-images`
+                      will be used as a default for control plane components and for
+                      kube-proxy, while `registry.k8s.io` will be used for all the
+                      other images.
                     type: string
                   kind:
                     description: 'Kind is a string value representing the REST resource
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 b2737f59b..85493b712 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.1
+    controller-gen.kubebuilder.io/version: v0.10.0
   creationTimestamp: null
   name: kubeadmconfigtemplates.bootstrap.cluster.x-k8s.io
 spec:
@@ -2246,21 +2246,14 @@ spec:
                             description: FeatureGates enabled by the user.
                             type: object
                           imageRepository:
-                            description: 'ImageRepository sets the container registry
-                              to pull images from. * If not set, the default registry
-                              of kubeadm will be used, i.e. * registry.k8s.io (new
-                              registry): >= v1.22.17, >= v1.23.15, >= v1.24.9, >=
-                              v1.25.0 * k8s.gcr.io (old registry): all older versions
-                              Please note that when imageRepository is not set we
-                              don''t allow upgrades to versions >= v1.22.0 which use
-                              the old registry (k8s.gcr.io). Please use a newer patch
-                              version with the new registry instead (i.e. >= v1.22.17,
-                              >= v1.23.15, >= v1.24.9, >= v1.25.0). * If the version
-                              is a CI build (kubernetes version starts with `ci/`
-                              or `ci-cross/`) `gcr.io/k8s-staging-ci-images` will
-                              be used as a default for control plane components and
-                              for kube-proxy, while `registry.k8s.io` will be used
-                              for all the other images.'
+                            description: ImageRepository sets the container registry
+                              to pull images from. If empty, `registry.k8s.io` will
+                              be used by default; in case of kubernetes version is
+                              a CI build (kubernetes version starts with `ci/` or
+                              `ci-cross/`) `gcr.io/k8s-staging-ci-images` will be
+                              used as a default for control plane components and for
+                              kube-proxy, while `registry.k8s.io` will be used for
+                              all the other images.
                             type: string
                           kind:
                             description: 'Kind is a string value representing the
diff --git a/bootstrap/kubeadm/config/manager/manager.yaml b/bootstrap/kubeadm/config/manager/manager.yaml
index d54386179..19b342985 100644
--- a/bootstrap/kubeadm/config/manager/manager.yaml
+++ b/bootstrap/kubeadm/config/manager/manager.yaml
@@ -16,35 +16,27 @@ spec:
         control-plane: controller-manager
     spec:
       containers:
-        - command:
-            - /manager
-          args:
-            - "--leader-elect"
-            - "--metrics-bind-addr=localhost:8080"
-            - "--feature-gates=MachinePool=${EXP_MACHINE_POOL:=false},KubeadmBootstrapFormatIgnition=${EXP_KUBEADM_BOOTSTRAP_FORMAT_IGNITION:=false}"
-            - "--bootstrap-token-ttl=${KUBEADM_BOOTSTRAP_TOKEN_TTL:=15m}"
-          image: controller:latest
-          name: manager
-          ports:
-            - containerPort: 9440
-              name: healthz
-              protocol: TCP
-          readinessProbe:
-            httpGet:
-              path: /readyz
-              port: healthz
-          livenessProbe:
-            httpGet:
-              path: /healthz
-              port: healthz
-          securityContext:
-            allowPrivilegeEscalation: false
-            capabilities:
-              drop:
-                - ALL
-            privileged: false
-            runAsUser: 65532
-            runAsGroup: 65532
+      - command:
+        - /manager
+        args:
+        - "--leader-elect"
+        - "--metrics-bind-addr=localhost:8080"
+        - "--feature-gates=MachinePool=${EXP_MACHINE_POOL:=false},KubeadmBootstrapFormatIgnition=${EXP_KUBEADM_BOOTSTRAP_FORMAT_IGNITION:=false}"
+        - "--bootstrap-token-ttl=${KUBEADM_BOOTSTRAP_TOKEN_TTL:=15m}"
+        image: controller:latest
+        name: manager
+        ports:
+        - containerPort: 9440
+          name: healthz
+          protocol: TCP
+        readinessProbe:
+          httpGet:
+            path: /readyz
+            port: healthz
+        livenessProbe:
+          httpGet:
+            path: /healthz
+            port: healthz
       terminationGracePeriodSeconds: 10
       serviceAccountName: manager
       tolerations:
@@ -52,7 +44,3 @@ spec:
           key: node-role.kubernetes.io/master
         - effect: NoSchedule
           key: node-role.kubernetes.io/control-plane
-      securityContext:
-        runAsNonRoot: true
-        seccompProfile:
-          type: RuntimeDefault
diff --git a/cmd/clusterctl/client/config/cert_manager_client.go b/cmd/clusterctl/client/config/cert_manager_client.go
index 990b93824..179bb31bc 100644
--- a/cmd/clusterctl/client/config/cert_manager_client.go
+++ b/cmd/clusterctl/client/config/cert_manager_client.go
@@ -29,7 +29,7 @@ const (
 	CertManagerConfigKey = "cert-manager"
 
 	// CertManagerDefaultVersion defines the default cert-manager version to be used by clusterctl.
-	CertManagerDefaultVersion = "v1.11.0"
+	CertManagerDefaultVersion = "v1.10.1"
 
 	// CertManagerDefaultURL defines the default cert-manager repository url to be used by clusterctl.
 	// NOTE: At runtime CertManagerDefaultVersion may be replaced with the
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 a7bc6faae..85a646a87 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.1
+    controller-gen.kubebuilder.io/version: v0.10.0
   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 ed30f3434..6ca01a62d 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.1
+    controller-gen.kubebuilder.io/version: v0.10.0
   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 8625909e2..7a2f4afcd 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.1
+    controller-gen.kubebuilder.io/version: v0.10.0
   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 d8f1629a0..d84f599e4 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.1
+    controller-gen.kubebuilder.io/version: v0.10.0
   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 bfb0ec207..d495f3aa2 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.1
+    controller-gen.kubebuilder.io/version: v0.10.0
   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 3dd293e2d..be491b3e6 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.1
+    controller-gen.kubebuilder.io/version: v0.10.0
   creationTimestamp: null
   name: clusterclasses.cluster.x-k8s.io
 spec:
diff --git a/config/crd/bases/cluster.x-k8s.io_clusters.yaml b/config/crd/bases/cluster.x-k8s.io_clusters.yaml
index d08afa6cc..688dd7ed4 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.1
+    controller-gen.kubebuilder.io/version: v0.10.0
   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 0db04ab50..901d811fc 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.1
+    controller-gen.kubebuilder.io/version: v0.10.0
   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 99b6fdf54..eae496c8b 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.1
+    controller-gen.kubebuilder.io/version: v0.10.0
   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 68e2d0d3a..e13b44064 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.1
+    controller-gen.kubebuilder.io/version: v0.10.0
   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 7c7e683a2..09c6282f0 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.1
+    controller-gen.kubebuilder.io/version: v0.10.0
   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 bc02a0794..56ec3259a 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.1
+    controller-gen.kubebuilder.io/version: v0.10.0
   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 539dcfae4..f6e600c72 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.1
+    controller-gen.kubebuilder.io/version: v0.10.0
   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 4587244d5..d90142bbe 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.1
+    controller-gen.kubebuilder.io/version: v0.10.0
   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 3b8de2eff..8c2b8f79b 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.1
+    controller-gen.kubebuilder.io/version: v0.10.0
   creationTimestamp: null
   name: extensionconfigs.runtime.cluster.x-k8s.io
 spec:
diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml
index 5ac9b3967..9fd19a385 100644
--- a/config/manager/manager.yaml
+++ b/config/manager/manager.yaml
@@ -17,47 +17,39 @@ spec:
         control-plane: controller-manager
     spec:
       containers:
-        - command:
-            - /manager
-          args:
-            - "--leader-elect"
-            - "--metrics-bind-addr=localhost:8080"
-            - "--feature-gates=MachinePool=${EXP_MACHINE_POOL:=false},ClusterResourceSet=${EXP_CLUSTER_RESOURCE_SET:=false},ClusterTopology=${CLUSTER_TOPOLOGY:=false},RuntimeSDK=${EXP_RUNTIME_SDK:=false}"
-          image: controller:latest
-          name: manager
-          env:
-            - name: POD_NAMESPACE
-              valueFrom:
-                fieldRef:
-                  fieldPath: metadata.namespace
-            - name: POD_NAME
-              valueFrom:
-                fieldRef:
-                  fieldPath: metadata.name
-            - name: POD_UID
-              valueFrom:
-                fieldRef:
-                  fieldPath: metadata.uid
-          ports:
-            - containerPort: 9440
-              name: healthz
-              protocol: TCP
-          readinessProbe:
-            httpGet:
-              path: /readyz
-              port: healthz
-          livenessProbe:
-            httpGet:
-              path: /healthz
-              port: healthz
-          securityContext:
-            allowPrivilegeEscalation: false
-            capabilities:
-              drop:
-                - ALL
-            privileged: false
-            runAsUser: 65532
-            runAsGroup: 65532
+      - command:
+        - /manager
+        args:
+        - "--leader-elect"
+        - "--metrics-bind-addr=localhost:8080"
+        - "--feature-gates=MachinePool=${EXP_MACHINE_POOL:=false},ClusterResourceSet=${EXP_CLUSTER_RESOURCE_SET:=false},ClusterTopology=${CLUSTER_TOPOLOGY:=false},RuntimeSDK=${EXP_RUNTIME_SDK:=false}"
+        image: controller:latest
+        name: manager
+        env:
+        - name: POD_NAMESPACE
+          valueFrom:
+            fieldRef:
+              fieldPath: metadata.namespace
+        - name: POD_NAME
+          valueFrom:
+            fieldRef:
+              fieldPath: metadata.name
+        - name: POD_UID
+          valueFrom:
+            fieldRef:
+              fieldPath: metadata.uid
+        ports:
+        - containerPort: 9440
+          name: healthz
+          protocol: TCP
+        readinessProbe:
+          httpGet:
+            path: /readyz
+            port: healthz
+        livenessProbe:
+          httpGet:
+            path: /healthz
+            port: healthz
       terminationGracePeriodSeconds: 10
       serviceAccountName: manager
       tolerations:
@@ -65,7 +57,3 @@ spec:
           key: node-role.kubernetes.io/master
         - effect: NoSchedule
           key: node-role.kubernetes.io/control-plane
-      securityContext:
-        runAsNonRoot: true
-        seccompProfile:
-          type: RuntimeDefault
diff --git a/controlplane/kubeadm/api/v1beta1/kubeadm_control_plane_types.go b/controlplane/kubeadm/api/v1beta1/kubeadm_control_plane_types.go
index 823fb123f..8f07c1dd9 100644
--- a/controlplane/kubeadm/api/v1beta1/kubeadm_control_plane_types.go
+++ b/controlplane/kubeadm/api/v1beta1/kubeadm_control_plane_types.go
@@ -60,11 +60,6 @@ type KubeadmControlPlaneSpec struct {
 	Replicas *int32 `json:"replicas,omitempty"`
 
 	// Version defines the desired Kubernetes version.
-	// Please note that if kubeadmConfigSpec.ClusterConfiguration.imageRepository is not set
-	// we don't allow upgrades to versions >= v1.22.0 for which kubeadm uses the old registry (k8s.gcr.io).
-	// Please use a newer patch version with the new registry instead. The default registries of kubeadm are:
-	//   * registry.k8s.io (new registry): >= v1.22.17, >= v1.23.15, >= v1.24.9, >= v1.25.0
-	//   * k8s.gcr.io (old registry): all older versions
 	Version string `json:"version"`
 
 	// MachineTemplate contains information about how machines
diff --git a/controlplane/kubeadm/api/v1beta1/kubeadm_control_plane_webhook.go b/controlplane/kubeadm/api/v1beta1/kubeadm_control_plane_webhook.go
index dd512fe26..e8f4a6836 100644
--- a/controlplane/kubeadm/api/v1beta1/kubeadm_control_plane_webhook.go
+++ b/controlplane/kubeadm/api/v1beta1/kubeadm_control_plane_webhook.go
@@ -33,7 +33,6 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/webhook"
 
 	bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1beta1"
-	"sigs.k8s.io/cluster-api/internal/util/kubeadm"
 	"sigs.k8s.io/cluster-api/util/container"
 	"sigs.k8s.io/cluster-api/util/version"
 )
@@ -602,10 +601,7 @@ func (in *KubeadmControlPlane) validateVersion(previousVersion string) (allErrs
 		return allErrs
 	}
 
-	// Validate that the update is upgrading at most one minor version.
-	// Note: Skipping a minor version is not allowed.
-	// Note: Checking against this ceilVersion allows upgrading to the next minor
-	// version irrespective of the patch version.
+	// Since upgrades to the next minor version are allowed, irrespective of the patch version.
 	ceilVersion := semver.Version{
 		Major: fromVersion.Major,
 		Minor: fromVersion.Minor + 2,
@@ -620,31 +616,6 @@ func (in *KubeadmControlPlane) validateVersion(previousVersion string) (allErrs
 		)
 	}
 
-	// The Kubernetes ecosystem has been requested to move users to the new registry due to cost issues.
-	// This validation enforces the move to the new registry by forcing users to upgrade to kubeadm versions
-	// with the new registry.
-	// NOTE: This only affects users relying on the community maintained registry.
-	// NOTE: Pinning to the upstream registry is not recommended because it could lead to issues
-	// given how the migration has been implemented in kubeadm.
-	//
-	// Block if imageRepository is not set (i.e. the default registry should be used),
-	if (in.Spec.KubeadmConfigSpec.ClusterConfiguration == nil ||
-		in.Spec.KubeadmConfigSpec.ClusterConfiguration.ImageRepository == "") &&
-		// the version changed (i.e. we have an upgrade),
-		toVersion.NE(fromVersion) &&
-		// the version is >= v1.22.0 and < v1.26.0
-		toVersion.GTE(kubeadm.MinKubernetesVersionImageRegistryMigration) &&
-		toVersion.LT(kubeadm.NextKubernetesVersionImageRegistryMigration) &&
-		// and the default registry of the new Kubernetes/kubeadm version is the old default registry.
-		kubeadm.GetDefaultRegistry(toVersion) == kubeadm.OldDefaultImageRepository {
-		allErrs = append(allErrs,
-			field.Forbidden(
-				field.NewPath("spec", "version"),
-				"cannot upgrade to a Kubernetes/kubeadm version which is using the old default registry. Please use a newer Kubernetes patch release which is using the new default registry (>= v1.22.17, >= v1.23.15, >= v1.24.9)",
-			),
-		)
-	}
-
 	return allErrs
 }
 
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 ed855c19a..533231db6 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.1
+    controller-gen.kubebuilder.io/version: v0.10.0
   creationTimestamp: null
   name: kubeadmcontrolplanes.controlplane.cluster.x-k8s.io
 spec:
@@ -2701,20 +2701,13 @@ spec:
                         description: FeatureGates enabled by the user.
                         type: object
                       imageRepository:
-                        description: 'ImageRepository sets the container registry
-                          to pull images from. * If not set, the default registry
-                          of kubeadm will be used, i.e. * registry.k8s.io (new registry):
-                          >= v1.22.17, >= v1.23.15, >= v1.24.9, >= v1.25.0 * k8s.gcr.io
-                          (old registry): all older versions Please note that when
-                          imageRepository is not set we don''t allow upgrades to versions
-                          >= v1.22.0 which use the old registry (k8s.gcr.io). Please
-                          use a newer patch version with the new registry instead
-                          (i.e. >= v1.22.17, >= v1.23.15, >= v1.24.9, >= v1.25.0).
-                          * If the version is a CI build (kubernetes version starts
-                          with `ci/` or `ci-cross/`) `gcr.io/k8s-staging-ci-images`
+                        description: ImageRepository sets the container registry to
+                          pull images from. If empty, `registry.k8s.io` will be used
+                          by default; in case of kubernetes version is a CI build
+                          (kubernetes version starts with `ci/` or `ci-cross/`) `gcr.io/k8s-staging-ci-images`
                           will be used as a default for control plane components and
                           for kube-proxy, while `registry.k8s.io` will be used for
-                          all the other images.'
+                          all the other images.
                         type: string
                       kind:
                         description: 'Kind is a string value representing the REST
@@ -3646,13 +3639,7 @@ spec:
                     type: string
                 type: object
               version:
-                description: 'Version defines the desired Kubernetes version. Please
-                  note that if kubeadmConfigSpec.ClusterConfiguration.imageRepository
-                  is not set we don''t allow upgrades to versions >= v1.22.0 for which
-                  kubeadm uses the old registry (k8s.gcr.io). Please use a newer patch
-                  version with the new registry instead. The default registries of
-                  kubeadm are: * registry.k8s.io (new registry): >= v1.22.17, >= v1.23.15,
-                  >= v1.24.9, >= v1.25.0 * k8s.gcr.io (old registry): all older versions'
+                description: Version defines the desired Kubernetes version.
                 type: string
             required:
             - kubeadmConfigSpec
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 668c5f5be..52663f04e 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.1
+    controller-gen.kubebuilder.io/version: v0.10.0
   creationTimestamp: null
   name: kubeadmcontrolplanetemplates.controlplane.cluster.x-k8s.io
 spec:
@@ -1466,21 +1466,14 @@ spec:
                                 description: FeatureGates enabled by the user.
                                 type: object
                               imageRepository:
-                                description: 'ImageRepository sets the container registry
-                                  to pull images from. * If not set, the default registry
-                                  of kubeadm will be used, i.e. * registry.k8s.io
-                                  (new registry): >= v1.22.17, >= v1.23.15, >= v1.24.9,
-                                  >= v1.25.0 * k8s.gcr.io (old registry): all older
-                                  versions Please note that when imageRepository is
-                                  not set we don''t allow upgrades to versions >=
-                                  v1.22.0 which use the old registry (k8s.gcr.io).
-                                  Please use a newer patch version with the new registry
-                                  instead (i.e. >= v1.22.17, >= v1.23.15, >= v1.24.9,
-                                  >= v1.25.0). * If the version is a CI build (kubernetes
-                                  version starts with `ci/` or `ci-cross/`) `gcr.io/k8s-staging-ci-images`
-                                  will be used as a default for control plane components
+                                description: ImageRepository sets the container registry
+                                  to pull images from. If empty, `registry.k8s.io`
+                                  will be used by default; in case of kubernetes version
+                                  is a CI build (kubernetes version starts with `ci/`
+                                  or `ci-cross/`) `gcr.io/k8s-staging-ci-images` will
+                                  be used as a default for control plane components
                                   and for kube-proxy, while `registry.k8s.io` will
-                                  be used for all the other images.'
+                                  be used for all the other images.
                                 type: string
                               kind:
                                 description: 'Kind is a string value representing
diff --git a/controlplane/kubeadm/config/manager/manager.yaml b/controlplane/kubeadm/config/manager/manager.yaml
index 34e85f86e..1566e010a 100644
--- a/controlplane/kubeadm/config/manager/manager.yaml
+++ b/controlplane/kubeadm/config/manager/manager.yaml
@@ -16,47 +16,39 @@ spec:
         control-plane: controller-manager
     spec:
       containers:
-        - command:
-            - /manager
-          args:
-            - "--leader-elect"
-            - "--metrics-bind-addr=localhost:8080"
-            - "--feature-gates=ClusterTopology=${CLUSTER_TOPOLOGY:=false},KubeadmBootstrapFormatIgnition=${EXP_KUBEADM_BOOTSTRAP_FORMAT_IGNITION:=false}"
-          image: controller:latest
-          name: manager
-          env:
-            - name: POD_NAMESPACE
-              valueFrom:
-                fieldRef:
-                  fieldPath: metadata.namespace
-            - name: POD_NAME
-              valueFrom:
-                fieldRef:
-                  fieldPath: metadata.name
-            - name: POD_UID
-              valueFrom:
-                fieldRef:
-                  fieldPath: metadata.uid
-          ports:
-            - containerPort: 9440
-              name: healthz
-              protocol: TCP
-          readinessProbe:
-            httpGet:
-              path: /readyz
-              port: healthz
-          livenessProbe:
-            httpGet:
-              path: /healthz
-              port: healthz
-          securityContext:
-            allowPrivilegeEscalation: false
-            capabilities:
-              drop:
-                - ALL
-            privileged: false
-            runAsUser: 65532
-            runAsGroup: 65532
+      - command:
+        - /manager
+        args:
+        - "--leader-elect"
+        - "--metrics-bind-addr=localhost:8080"
+        - "--feature-gates=ClusterTopology=${CLUSTER_TOPOLOGY:=false},KubeadmBootstrapFormatIgnition=${EXP_KUBEADM_BOOTSTRAP_FORMAT_IGNITION:=false}"
+        image: controller:latest
+        name: manager
+        env:
+        - name: POD_NAMESPACE
+          valueFrom:
+            fieldRef:
+              fieldPath: metadata.namespace
+        - name: POD_NAME
+          valueFrom:
+            fieldRef:
+              fieldPath: metadata.name
+        - name: POD_UID
+          valueFrom:
+            fieldRef:
+              fieldPath: metadata.uid
+        ports:
+        - containerPort: 9440
+          name: healthz
+          protocol: TCP
+        readinessProbe:
+          httpGet:
+            path: /readyz
+            port: healthz
+        livenessProbe:
+          httpGet:
+            path: /healthz
+            port: healthz
       terminationGracePeriodSeconds: 10
       serviceAccountName: manager
       tolerations:
@@ -64,7 +56,3 @@ spec:
           key: node-role.kubernetes.io/master
         - effect: NoSchedule
           key: node-role.kubernetes.io/control-plane
-      securityContext:
-        runAsNonRoot: true
-        seccompProfile:
-          type: RuntimeDefault
diff --git a/controlplane/kubeadm/internal/workload_cluster.go b/controlplane/kubeadm/internal/workload_cluster.go
index 99f3fc1b3..64e9477bb 100644
--- a/controlplane/kubeadm/internal/workload_cluster.go
+++ b/controlplane/kubeadm/internal/workload_cluster.go
@@ -47,7 +47,6 @@ import (
 	kubeadmtypes "sigs.k8s.io/cluster-api/bootstrap/kubeadm/types"
 	controlplanev1 "sigs.k8s.io/cluster-api/controlplane/kubeadm/api/v1beta1"
 	"sigs.k8s.io/cluster-api/controlplane/kubeadm/internal/proxy"
-	"sigs.k8s.io/cluster-api/internal/util/kubeadm"
 	"sigs.k8s.io/cluster-api/util"
 	"sigs.k8s.io/cluster-api/util/certs"
 	containerutil "sigs.k8s.io/cluster-api/util/container"
@@ -86,6 +85,14 @@ var (
 	// NOTE: The following assumes that kubeadm version equals to Kubernetes version.
 	minVerUnversionedKubeletConfig = semver.MustParse("1.24.0")
 
+	// minKubernetesVersionImageRegistryMigration is first kubernetes version where
+	// the default image registry is registry.k8s.io instead of k8s.gcr.io.
+	minKubernetesVersionImageRegistryMigration = semver.MustParse("1.22.0")
+
+	// nextKubernetesVersionImageRegistryMigration is the next minor version after
+	// the default image registry changed to registry.k8s.io.
+	nextKubernetesVersionImageRegistryMigration = semver.MustParse("1.26.0")
+
 	// ErrControlPlaneMinNodes signals that a cluster doesn't meet the minimum required nodes
 	// to remove an etcd member.
 	ErrControlPlaneMinNodes = errors.New("cluster has fewer than 2 control plane nodes; removing an etcd member is not supported")
@@ -616,15 +623,12 @@ func yamlToUnstructured(rawYAML []byte) (*unstructured.Unstructured, error) {
 }
 
 // ImageRepositoryFromClusterConfig returns the image repository to use. It returns:
-//   - clusterConfig.ImageRepository if set.
-//   - else either k8s.gcr.io or registry.k8s.io depending on the default registry of the kubeadm
-//     binary of the given kubernetes version. This is only done for Kubernetes versions >= v1.22.0
-//     and < v1.26.0 because in this version range the default registry was changed.
-//
-// Note: Please see the following issue for more context: https://github.com/kubernetes-sigs/cluster-api/issues/7833
-// tl;dr is that the imageRepository must be in sync with the default registry of kubeadm.
-// Otherwise kubeadm preflight checks will fail because kubeadm is trying to pull the CoreDNS image
-// from the wrong repository (<registry>/coredns instead of <registry>/coredns/coredns).
+// * clusterConfig.ImageRepository if set.
+// * "registry.k8s.io" if v1.22 <= version < v1.26 to migrate to the new registry
+// * "" otherwise.
+// Beginning with kubernetes v1.22, the default registry for kubernetes is registry.k8s.io
+// instead of k8s.gcr.io which is why references should get migrated when upgrading to v1.22.
+// The migration follows the behavior of `kubeadm upgrade`.
 func ImageRepositoryFromClusterConfig(clusterConfig *bootstrapv1.ClusterConfiguration, kubernetesVersion semver.Version) string {
 	// If ImageRepository is explicitly specified, return early.
 	if clusterConfig != nil &&
@@ -632,11 +636,11 @@ func ImageRepositoryFromClusterConfig(clusterConfig *bootstrapv1.ClusterConfigur
 		return clusterConfig.ImageRepository
 	}
 
-	// If v1.22.0 <= version < v1.26.0 return the default registry of the
-	// corresponding kubeadm binary.
-	if kubernetesVersion.GTE(kubeadm.MinKubernetesVersionImageRegistryMigration) &&
-		kubernetesVersion.LT(kubeadm.NextKubernetesVersionImageRegistryMigration) {
-		return kubeadm.GetDefaultRegistry(kubernetesVersion)
+	// If v1.22 <= version < v1.26 return the default Kubernetes image repository to
+	// migrate to the new location and not cause changes else.
+	if kubernetesVersion.GTE(minKubernetesVersionImageRegistryMigration) &&
+		kubernetesVersion.LT(nextKubernetesVersionImageRegistryMigration) {
+		return kubernetesImageRepository
 	}
 
 	// Use defaulting or current values otherwise.
diff --git a/controlplane/kubeadm/internal/workload_cluster_coredns.go b/controlplane/kubeadm/internal/workload_cluster_coredns.go
index 9ebf7eda9..a105a61a8 100644
--- a/controlplane/kubeadm/internal/workload_cluster_coredns.go
+++ b/controlplane/kubeadm/internal/workload_cluster_coredns.go
@@ -35,7 +35,6 @@ import (
 
 	bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1beta1"
 	controlplanev1 "sigs.k8s.io/cluster-api/controlplane/kubeadm/api/v1beta1"
-	"sigs.k8s.io/cluster-api/internal/util/kubeadm"
 	containerutil "sigs.k8s.io/cluster-api/util/container"
 	"sigs.k8s.io/cluster-api/util/patch"
 	"sigs.k8s.io/cluster-api/util/version"
@@ -48,8 +47,11 @@ const (
 	coreDNSVolumeKey       = "config-volume"
 	coreDNSClusterRoleName = "system:coredns"
 
-	oldCoreDNSImageName = "coredns"
-	coreDNSImageName    = "coredns/coredns"
+	// kubernetesImageRepository is the default Kubernetes image repository for build artifacts.
+	kubernetesImageRepository    = "registry.k8s.io"
+	oldKubernetesImageRepository = "k8s.gcr.io"
+	oldCoreDNSImageName          = "coredns"
+	coreDNSImageName             = "coredns/coredns"
 
 	oldControlPlaneTaint = "node-role.kubernetes.io/master" // Deprecated: https://github.com/kubernetes/kubeadm/issues/2200
 	controlPlaneTaint    = "node-role.kubernetes.io/control-plane"
@@ -198,11 +200,11 @@ func (w *Workload) getCoreDNSInfo(ctx context.Context, clusterConfig *bootstrapv
 	toImageRepository := parsedImage.Repository
 	// Overwrite the image repository if a value was explicitly set or an upgrade is required.
 	if imageRegistryRepository := ImageRepositoryFromClusterConfig(clusterConfig, version); imageRegistryRepository != "" {
-		if imageRegistryRepository == kubeadm.DefaultImageRepository {
-			// Only patch to DefaultImageRepository if OldDefaultImageRepository is set as prefix.
-			if strings.HasPrefix(toImageRepository, kubeadm.OldDefaultImageRepository) {
-				// Ensure to keep the repository subpaths when patching from OldDefaultImageRepository to new DefaultImageRepository.
-				toImageRepository = strings.TrimSuffix(imageRegistryRepository+strings.TrimPrefix(toImageRepository, kubeadm.OldDefaultImageRepository), "/")
+		if imageRegistryRepository == kubernetesImageRepository {
+			// Only patch to KubernetesImageRepository if oldKubernetesImageRepository is set as prefix.
+			if strings.HasPrefix(toImageRepository, oldKubernetesImageRepository) {
+				// Ensure to keep the repository subpaths when patching from oldKubernetesImageRepository to new KubernetesImageRepository.
+				toImageRepository = strings.TrimSuffix(imageRegistryRepository+strings.TrimPrefix(toImageRepository, oldKubernetesImageRepository), "/")
 			}
 		} else {
 			toImageRepository = strings.TrimSuffix(imageRegistryRepository, "/")
@@ -233,7 +235,7 @@ func (w *Workload) getCoreDNSInfo(ctx context.Context, clusterConfig *bootstrapv
 	// * "registry.k8s.io/coredns" to "registry.k8s.io/coredns/coredns" or
 	// * "k8s.gcr.io/coredns" to "k8s.gcr.io/coredns/coredns"
 	toImageName := parsedImage.Name
-	if (toImageRepository == kubeadm.OldDefaultImageRepository || toImageRepository == kubeadm.DefaultImageRepository) &&
+	if (toImageRepository == oldKubernetesImageRepository || toImageRepository == kubernetesImageRepository) &&
 		toImageName == oldCoreDNSImageName && targetMajorMinorPatch.GTE(semver.MustParse("1.8.0")) {
 		toImageName = coreDNSImageName
 	}
diff --git a/docs/book/src/clusterctl/commands/init.md b/docs/book/src/clusterctl/commands/init.md
index 9acf8c0a5..ee4b9501f 100644
--- a/docs/book/src/clusterctl/commands/init.md
+++ b/docs/book/src/clusterctl/commands/init.md
@@ -190,7 +190,7 @@ If this happens, there are no guarantees about the proper functioning of `cluste
 Cluster API providers require a cert-manager version supporting the `cert-manager.io/v1` API to be installed in the cluster.
 
 While doing init, clusterctl checks if there is a version of cert-manager already installed. If not, clusterctl will
-install a default version (currently cert-manager v1.11.0). See [clusterctl configuration](../configuration.md) for
+install a default version (currently cert-manager v1.10.1). See [clusterctl configuration](../configuration.md) for
 available options to customize this operation.
 
 <aside class="note warning">
diff --git a/docs/book/src/developer/guide.md b/docs/book/src/developer/guide.md
index f5b1de61b..fbb6e275a 100644
--- a/docs/book/src/developer/guide.md
+++ b/docs/book/src/developer/guide.md
@@ -81,7 +81,7 @@ The generated binary can be found at ./hack/tools/bin/envsubst
 You'll need to deploy [cert-manager] components on your [management cluster][mcluster], using `kubectl`
 
 ```bash
-kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.11.0/cert-manager.yaml
+kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.10.1/cert-manager.yaml
 ```
 
 Ensure the cert-manager webhook service is ready before creating the Cluster API components.
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 1f783808e..fd19f69e2 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
@@ -5,13 +5,12 @@ maintainers of providers and consumers of our Go API.
 
 ## Minimum Go version
 
-- The Go version used by Cluster API is still Go 1.19.x
+* The Go version used by Cluster API is still Go 1.19.x
 
 ## Dependencies
 
 **Note**: Only the most relevant dependencies are listed, `k8s.io/` and `ginkgo`/`gomega` dependencies in Cluster API are kept in sync with the versions used by `sigs.k8s.io/controller-runtime`.
 
-- sigs.k8s.io/controller-tools: v0.10.x => v0.11.x
 
 ## Changes by Kind
 
@@ -36,9 +35,5 @@ maintainers of providers and consumers of our Go API.
 ### Other
 
 - `clusterctl upgrade apply` no longer requires a namespace when updating providers. It is now optional and in a future release it will be deprecated. The new syntax is `[namespace/]provider:version`.
-- `WatchDeploymentLogs` is changed to `WatchDeploymentLogsByName`, it works same as before. Another function `WatchDeploymentLogsByLabelSelector` is added to stream logs of deployment by label selector.
-- Cluster API controllers are now using an explicit security context by default.
 
 ### Suggested changes for providers
-
-- Providers should add an explicit security context to their controllers deployment, see [#7831](https://github.com/kubernetes-sigs/cluster-api/pull/7831) for reference.
diff --git a/docs/book/src/developer/tilt.md b/docs/book/src/developer/tilt.md
index ca75e22d0..e5474228c 100644
--- a/docs/book/src/developer/tilt.md
+++ b/docs/book/src/developer/tilt.md
@@ -365,7 +365,7 @@ A provider must supply a `tilt-provider.yaml` file describing how to build it. H
 ```yaml
 name: aws
 config:
-  image: "gcr.io/k8s-staging-cluster-api-aws/cluster-api-aws-controller"
+  image: "gcr.io/k8s-staging-cluster-api-aws/cluster-api-aws-controller",
   live_reload_deps: ["main.go", "go.mod", "go.sum", "api", "cmd", "controllers", "pkg"]
   label: CAPA
 ```
diff --git a/docs/book/src/reference/glossary.md b/docs/book/src/reference/glossary.md
index c0e948f84..ef2bd3bba 100644
--- a/docs/book/src/reference/glossary.md
+++ b/docs/book/src/reference/glossary.md
@@ -142,7 +142,7 @@ The set of Kubernetes services that form the basis of a cluster. See also [https
 
 ### Control plane provider
 
-Refers to a [provider](#provider) that implements a solution for the management of a Kubernetes [control plane](#control-plane).
+Refers to a [provider](#provider) that implements a solution for the management of a Kubernetes [control plane][#control-plane].
 Control plane provider's interaction with Cluster API is based on what is defined in the [Cluster API contract](#contract).
 
 See [KCP](#KCP).
@@ -172,7 +172,9 @@ A feature implementation offered as part of the Cluster API project and maintain
 
 ### External patch extension
 
-A [runtime extension](#runtime-extension) that implements a [topology mutation hook](#topology-mutation-hook).
+A [runtime extension] that implements a [topology mutation hook]
+
+See [Runtime Extension](#runtime-extension), [topology mutation hook](#topology-mutation-hook)
 
 # H
 ---
@@ -197,7 +199,8 @@ Infrastructure provider's interaction with Cluster API is based on what is defin
 Clouds infrastructure providers include AWS, Azure, or Google; while VMware, MAAS, or metal3.io can be defined as bare metal providers.
 When there is more than one way to obtain resources from the same infrastructure provider (e.g. EC2 vs. EKS in AWS) each way is referred to as a variant.
 
-For a complete list of providers see [Provider Implementations](providers.md).
+See e.g. [CAPA][#CAPA], [CAPC][#CAPC], [CAPD][#CAPD], [CAPG][#CAPG], [CAPH][#CAPH], [CAPIBM][#CAPIBM], [CAPN][#CAPN],
+[CAPX][#CAPX], [CAPK][#CAPK], [CAPO][#CAPO], [CAPOCI][#CAPOCI], [CAPV][#CAPV], [CAPVC][#CAPVC], [CAPVCD][#CAPVCD], [CAPZ][#CAPZ]
 
 ### Inline patch 
 
diff --git a/docs/book/src/reference/jobs.md b/docs/book/src/reference/jobs.md
index e2dc03516..f318b78f9 100644
--- a/docs/book/src/reference/jobs.md
+++ b/docs/book/src/reference/jobs.md
@@ -27,7 +27,7 @@ Prow Presubmits:
   * GINKGO_FOCUS: `[IPv6] [PR-Informing]`, IP_FAMILY: `IPv6`
 * ✳️️ ✴️ [pull-cluster-api-e2e-full-main] `./scripts/ci-e2e.sh`
   * GINKGO_SKIP: `[PR-Blocking] [Conformance] [K8s-Upgrade] [IPv6]` (i.e. "no tags")
-* ✳️️ ✴️ [pull-cluster-api-e2e-workload-upgrade-1-26-latest-main] `./scripts/ci-e2e.sh` FROM: `stable-1.26` TO: `ci/latest-1.27`
+* ✳️️ ✴️ [pull-cluster-api-e2e-workload-upgrade-1-25-latest-main] `./scripts/ci-e2e.sh` FROM: `stable-1.25` TO: `ci/latest-1.26`
   * GINKGO_FOCUS: `[K8s-Upgrade]`
 
 GitHub Presubmit Workflows:
@@ -37,7 +37,7 @@ GitHub Presubmit Workflows:
   * Verifies the PR titles have a valid format, i.e. contains one of the valid icons.
   * Verifies the PR description is valid, i.e. is long enough.
 * Check PR Markdown links (run when markdown files changed)
-  * Checks markdown for broken links.
+  * Checks markdown for broken links. 
 * dependabot (run on dependabot PRs)
   * Regenerates Go modules and code.
 * release (run on tags)
@@ -60,7 +60,7 @@ Prow Postsubmits:
 Prow Periodics:
 * [periodic-cluster-api-test-main] `./scripts/ci-test.sh`
 * [periodic-cluster-api-test-mink8s-main] `./scripts/ci-test.sh`
-  * KUBEBUILDER_ENVTEST_KUBERNETES_VERSION: `1.20.2`
+  * KUBEBUILDER_ENVTEST_KUBERNETES_VERSION: `1.20.2` 
 * [periodic-cluster-api-e2e-main] `./scripts/ci-e2e.sh`
   * GINKGO_SKIP: `[Conformance] [K8s-Upgrade]|[IPv6]`
 * [periodic-cluster-api-e2e-mink8s-main] `./scripts/ci-e2e.sh`
@@ -80,20 +80,18 @@ Prow Periodics:
   * GINKGO_FOCUS: `[K8s-Upgrade]`
 * [periodic-cluster-api-e2e-workload-upgrade-1-24-1-25-main] `./scripts/ci-e2e.sh` FROM: `stable-1.24` TO: `stable-1.25`
   * GINKGO_FOCUS: `[K8s-Upgrade]`
-* [periodic-cluster-api-e2e-workload-upgrade-1-25-1-26-main] `./scripts/ci-e2e.sh` FROM: `stable-1.25` TO: `stable-1.26`
-  * GINKGO_FOCUS: `[K8s-Upgrade]`
-* [periodic-cluster-api-e2e-workload-upgrade-1-26-latest-main] `./scripts/ci-e2e.sh` FROM: `stable-1.26` TO: `ci/latest-1.27`
+* [periodic-cluster-api-e2e-workload-upgrade-1-25-latest-main] `./scripts/ci-e2e.sh` FROM: `stable-1.25` TO: `ci/latest-1.26`
   * GINKGO_FOCUS: `[K8s-Upgrade]`
 * [cluster-api-push-images-nightly] Google Cloud Build: `make release-staging-nightly`
 
 ## Test-infra configuration
 
 * config/jobs/image-pushing/k8s-staging-cluster-api.yaml
-  * Configures nightly and postsubmit jobs to push images and manifests.
+  * Configures nightly and postsubmit jobs to push images and manifests. 
 * config/jobs/kubernetes-sigs/cluster-api/
   * Configures Cluster API  presubmit and periodic jobs.
 * config/testgrids/kubernetes/sig-cluster-lifecycle/config.yaml
-  * Configures Cluster API testgrid dashboards.
+  * Configures Cluster API testgrid dashboards.  
 * config/prow/config.yaml
   * `branch-protection` and `tide` are configured to make the golangci-lint GitHub action mandatory for merge
 * config/prow/plugins.yaml
@@ -105,7 +103,7 @@ Prow Periodics:
   * `plugins`: enables `milestone`, `override` and `require-matching-label` plugins
   * `external_plugins`: enables `cherrypicker`
 * label_sync/labels.yaml
-  * Configures labels for the `cluster-api` repository.
+  * Configures labels for the `cluster-api` repository. 
 
 
 <!-- links -->
@@ -118,7 +116,7 @@ Prow Periodics:
 [pull-cluster-api-e2e-informing-main]: https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api#capi-pr-e2e-informing-main
 [pull-cluster-api-e2e-informing-ipv6-main]: https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api#capi-pr-e2e-informing-ipv6-main
 [pull-cluster-api-e2e-full-main]: https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api#capi-pr-e2e-full-main
-[pull-cluster-api-e2e-workload-upgrade-1-26-latest-main]: https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api#capi-pr-e2e-main-1-26-latest
+[pull-cluster-api-e2e-workload-upgrade-1-25-latest-main]: https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api#capi-pr-e2e-main-1-25-latest
 [periodic-cluster-api-test-main]: https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api#capi-test-main
 [periodic-cluster-api-test-mink8s-main]: https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api#capi-test-mink8s-main
 [periodic-cluster-api-e2e-main]: https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api#capi-e2e-main
@@ -132,7 +130,6 @@ Prow Periodics:
 [periodic-cluster-api-e2e-workload-upgrade-1-22-1-23-main]: https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api#capi-e2e-main-1-22-1-23
 [periodic-cluster-api-e2e-workload-upgrade-1-23-1-24-main]: https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api#capi-e2e-main-1-23-1-24
 [periodic-cluster-api-e2e-workload-upgrade-1-24-1-25-main]: https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api#capi-e2e-main-1-24-1-25
-[periodic-cluster-api-e2e-workload-upgrade-1-25-1-26-main]: https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api#capi-e2e-main-1-25-1-26
-[periodic-cluster-api-e2e-workload-upgrade-1-26-latest-main]: https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api#capi-e2e-main-1-26-latest
+[periodic-cluster-api-e2e-workload-upgrade-1-25-latest-main]: https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api#capi-e2e-main-1-25-latest
 [cluster-api-push-images-nightly]: https://testgrid.k8s.io/sig-cluster-lifecycle-image-pushes#cluster-api-push-images-nightly
 [post-cluster-api-push-images]: https://testgrid.k8s.io/sig-cluster-lifecycle-image-pushes#post-cluster-api-push-images
diff --git a/docs/book/src/reference/versions.md b/docs/book/src/reference/versions.md
index 5e3b8ca65..4c53ff08e 100644
--- a/docs/book/src/reference/versions.md
+++ b/docs/book/src/reference/versions.md
@@ -15,17 +15,17 @@ All Infrastructure Providers are maintained by independent teams. Other Bootstra
 
 ## Supported Kubernetes Versions
 
-The project aims to keep the current minor release compatible with the actively supported Kubernetes minor releases, i.e.,
+The project aims to keep the current minor release compatible with the actively supported Kubernetes minor releases, i.e., 
 the current release (N), N-1, and N-2. Where possible we will go above and beyond this trying to help our users support
 both newer and older versions of Kubernetes:
 
-- For a given Cluster API minor release, we give best-effort support for future Kubernetes versions that are released
+- For a given Cluster API minor release, we give best-effort support for future Kubernetes versions that are released 
 	during the release's active support period. Depending on the changes made in Kubernetes, users may be required to
 	update to the next patch release of Cluster API to maintain compatibility.
 - In some cases where support for future Kubernetes versions can't be added, or is considered not practical or too risky
 	adding it, the Kubernetes version support matrix for an existing Cluster API minor release will stop at the last supported version.
 - Cluster API offers best-effort support for older Kubernetes versions where possible.
-	Important! this is not a replacement/alternative for upstream Kubernetes support policies!
+	Important! this is not a replacement/alternative for upstream Kubernetes support policies! 
 	Support for older version of Kubernetes is limited to "Cluster API can start a Cluster with this Kubernetes version"
 	and "Cluster API  can upgrade to the next Kubernetes version"; it does not include any extended support to Kubernetes itself.
 
@@ -33,7 +33,7 @@ Whenever a new Cluster API release is cut, we will document the Kubernetes versi
 has been tested with. Summaries of Kubernetes versions supported by each component are additionally maintained in
 the [tables](#release-components) below.
 
-On a final comment, let's praise all the contributors keeping care of such a wide support matrix.
+On a final comment, let's praise all the contributors keeping care of such a wide support matrix. 
 If someone is looking for opportunities to help with the project, this is definitely an area where additional hands
 and eyes will be more than welcome and greatly beneficial to the entire community.
 
@@ -75,7 +75,7 @@ These diagrams show the relationships between components in a Cluster API releas
 | Kubernetes v1.23* |                      | ✓                 | ✓                 | ✓                 | ✓                 | ✓                 | ✓                 |
 | Kubernetes v1.24  |                      |                   |                   | ✓                 | ✓                 | ✓                 | ✓                 |
 | Kubernetes v1.25  |                      |                   |                   |                   | ✓                 | ✓                 | ✓                 |
-| Kubernetes v1.26  |                      |                   |                   |                   | ✓                 | ✓                 | ✓                 |
+| Kubernetes v1.26  |                      |                   |                   |                   |                   |                   |                   |
 
 
 \* There is an issue with CRDs in Kubernetes v1.23.{0-2}. ClusterClass with patches is affected by that (for more details please see [this issue](https://github.com/kubernetes-sigs/cluster-api/issues/5990)). Therefore we recommend to use Kubernetes v1.23.3+ with ClusterClass.
@@ -99,7 +99,7 @@ The Core Provider also talks to API server of every Workload Cluster. Therefore,
 | Kubernetes v1.23 + kubeadm/v1beta3                                 |                      | ✓                    | ✓                   | ✓                 | ✓                 | ✓                 | ✓                 |
 | Kubernetes v1.24 + kubeadm/v1beta3                                 |                      |                      |                     | ✓                 | ✓                 | ✓                 | ✓                 |
 | Kubernetes v1.25 + kubeadm/v1beta3                                 |                      |                      |                     |                   | ✓                 | ✓                 | ✓                 |
-| Kubernetes v1.26 + kubeadm/v1beta3                                 |                      |                      |                     |                   | ✓                 | ✓                 | ✓                 |
+| Kubernetes v1.26 + kubeadm/v1beta3                                 |                      |                      |                     |                   |                   |                   |                   |
 
 The Kubeadm Bootstrap Provider generates kubeadm configuration using the API version recommended for the target Kubernetes version.
 
@@ -117,7 +117,7 @@ The Kubeadm Bootstrap Provider generates kubeadm configuration using the API ver
 | Kubernetes v1.23 + etcd/v3 |                      | ✓*                   | ✓*                  | ✓                 | ✓                 | ✓                 | ✓                 |
 | Kubernetes v1.24 + etcd/v3 |                      |                      |                     | ✓                 | ✓                 | ✓                 | ✓                 |
 | Kubernetes v1.25 + etcd/v3 |                      |                      |                     |                   | ✓                 | ✓                 | ✓                 |
-| Kubernetes v1.26 + etcd/v3 |                      |                      |                     |                   | ✓                 | ✓                 | ✓                 |
+| Kubernetes v1.26 + etcd/v3 |                      |                      |                     |                   |                   |                   |                   |
 
 The Kubeadm Control Plane Provider talks to the API server and etcd members of every Workload Cluster whose control plane it owns. It uses the etcd v3 API.
 
diff --git a/docs/book/src/tasks/experimental-features/runtime-sdk/implement-extensions.md b/docs/book/src/tasks/experimental-features/runtime-sdk/implement-extensions.md
index 307e9a621..ae8a9a3bd 100644
--- a/docs/book/src/tasks/experimental-features/runtime-sdk/implement-extensions.md
+++ b/docs/book/src/tasks/experimental-features/runtime-sdk/implement-extensions.md
@@ -46,8 +46,8 @@ import (
 	"github.com/spf13/pflag"
 	cliflag "k8s.io/component-base/cli/flag"
 	"k8s.io/component-base/logs"
-	logsv1 "k8s.io/component-base/logs/api/v1"
 	"k8s.io/klog/v2"
+	"k8s.io/utils/pointer"
 	ctrl "sigs.k8s.io/controller-runtime"
 
 	runtimecatalog "sigs.k8s.io/cluster-api/exp/runtime/catalog"
@@ -75,7 +75,7 @@ func init() {
 func InitFlags(fs *pflag.FlagSet) {
 	// Initialize logs flags using Kubernetes component-base machinery.
 	logs.AddFlags(fs, logs.SkipLoggingConfigurationFlags())
-	logsv1.AddFlags(logOptions, fs)
+	logOptions.AddFlags(fs)
 
 	// Add test-extension specific flags
 	fs.StringVar(&profilerAddress, "profiler-address", "",
@@ -99,7 +99,7 @@ func main() {
 	pflag.Parse()
 
 	// Validates logs flags using Kubernetes component-base machinery and applies them
-	if err := logsv1.ValidateAndApply(logOptions, nil); err != nil {
+	if err := logOptions.ValidateAndApply(nil); err != nil {
 		setupLog.Error(err, "unable to start extension")
 		os.Exit(1)
 	}
@@ -128,17 +128,17 @@ func main() {
 
 	// Register extension handlers.
 	if err := webhookServer.AddExtensionHandler(server.ExtensionHandler{
-		Hook:        runtimehooksv1.BeforeClusterCreate,
-		Name:        "before-cluster-create",
-		HandlerFunc: DoBeforeClusterCreate,
+		Hook:           runtimehooksv1.BeforeClusterCreate,
+		Name:           "before-cluster-create",
+		HandlerFunc:    DoBeforeClusterCreate,
 	}); err != nil {
 		setupLog.Error(err, "error adding handler")
 		os.Exit(1)
 	}
 	if err := webhookServer.AddExtensionHandler(server.ExtensionHandler{
-		Hook:        runtimehooksv1.BeforeClusterUpgrade,
-		Name:        "before-cluster-upgrade",
-		HandlerFunc: DoBeforeClusterUpgrade,
+		Hook:           runtimehooksv1.BeforeClusterUpgrade,
+		Name:           "before-cluster-upgrade",
+		HandlerFunc:    DoBeforeClusterUpgrade,
 	}); err != nil {
 		setupLog.Error(err, "error adding handler")
 		os.Exit(1)
diff --git a/docs/book/src/user/quick-start.md b/docs/book/src/user/quick-start.md
index 598fe5fa5..2d571d0cb 100644
--- a/docs/book/src/user/quick-start.md
+++ b/docs/book/src/user/quick-start.md
@@ -734,7 +734,7 @@ The output of `clusterctl init` is similar to this:
 
 ```bash
 Fetching providers
-Installing cert-manager Version="v1.11.0"
+Installing cert-manager Version="v1.10.1"
 Waiting for cert-manager to be available...
 Installing Provider="cluster-api" Version="v1.0.0" TargetNamespace="capi-system"
 Installing Provider="bootstrap-kubeadm" Version="v1.0.0" TargetNamespace="capi-kubeadm-bootstrap-system"
diff --git a/docs/book/src/user/troubleshooting.md b/docs/book/src/user/troubleshooting.md
index 599336ff4..f57b8a4af 100644
--- a/docs/book/src/user/troubleshooting.md
+++ b/docs/book/src/user/troubleshooting.md
@@ -161,7 +161,7 @@ clusterctl init --infrastructure docker
 ```
 ```bash
 Fetching providers
-Installing cert-manager Version="v1.11.0"
+Installing cert-manager Version="v1.10.1"
 Error: action failed after 10 attempts: failed to get cert-manager object /, Kind=, /: Object 'Kind' is missing in 'unstructured object has no kind'
 ```
 
@@ -173,7 +173,7 @@ cert-manager:
   url: "https://github.com/cert-manager/cert-manager/releases/latest/cert-manager.yaml"
 ```
 
-Alternatively a Cert Manager yaml file can be placed in the [clusterctl overrides layer](../clusterctl/configuration.md#overrides-layer) which is by default in `$HOME/.cluster-api/overrides`. A Cert Manager yaml file can be placed at `$(HOME)/.cluster-api/overrides/cert-manager/v1.11.0/cert-manager.yaml`
+Alternatively a Cert Manager yaml file can be placed in the [clusterctl overrides layer](../clusterctl/configuration.md#overrides-layer) which is by default in `$HOME/.cluster-api/overrides`. A Cert Manager yaml file can be placed at `$(HOME)/.cluster-api/overrides/cert-manager/v1.10.1/cert-manager.yaml`
 
 More information on the clusterctl config file can be found at [its page in the book](../clusterctl/configuration.md#clusterctl-configuration-file)
 
diff --git a/docs/proposals/20191017-kubeadm-based-control-plane.md b/docs/proposals/20191017-kubeadm-based-control-plane.md
index f9d5b912e..487911a5e 100644
--- a/docs/proposals/20191017-kubeadm-based-control-plane.md
+++ b/docs/proposals/20191017-kubeadm-based-control-plane.md
@@ -472,12 +472,20 @@ When `MaxSurge` is set to 0 the rollout algorithm is as follows:
   for additional details. When there are multiple machines that are marked for remediation, the oldest one will be remediated first.
 
 - Following rules should be satisfied in order to start remediation
-  - The cluster MUST have at least two control plane machines, because this is the smallest cluster size that can be remediated.
-  - The number of replicas MUST be equal to or greater than the desired replicas. This rule ensures that when the cluster
-    is missing replicas, we skip remediation and instead perform regular scale up/rollout operations first.
+  - One of the following apply:
+    - The cluster MUST not be initialized yet (the failure happens before KCP reaches the initialized state) 
+    - The cluster MUST have at least two control plane machines, because this is the smallest cluster size that can be remediated.
+  - Previous remediation (delete and re-create) MUST have been completed. This rule prevents KCP to remediate more machines while the
+    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.
+    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
+    to define a maximum number of retries, thus preventing unnecessary load on infrastructure provider e.g. in case of quota problems.
+  - If we are remediating the same machine (delete, re-create, replacement machine gets unhealthy), it will be possible
+    to define a delay between each retry, thus allowing the infrastructure provider to stabilize in case of temporary problems.
 
 - When all the conditions for starting remediation are satisfied, KCP temporarily suspend any operation in progress
   in order to perform remediation.
@@ -634,4 +642,5 @@ For the purposes of designing upgrades, two existing lifecycle managers were exa
 - [x] 12/04/2019: Initial stubbed KubeadmControlPlane controller added [#1826](https://github.com/kubernetes-sigs/cluster-api/pull/1826)
 - [x] 07/09/2020: Document updated to reflect changes up to v0.3.9 release
 - [x] 22/09/2020: KCP remediation added
-- [x] XX/XX/2020: KCP rollout strategies added
+- [x] 10/05/2021: Support for remediation of failures while upgrading 1 node CP
+- [x] 05/01/2022: Support for remediation while provisioning the CP (both first CP and CP machines while current replica < desired replica); Allow control of remediation retry behavior.
diff --git a/docs/release/release-tasks.md b/docs/release/release-tasks.md
index c5fbc7e9e..7db7d86dc 100644
--- a/docs/release/release-tasks.md
+++ b/docs/release/release-tasks.md
@@ -90,29 +90,13 @@ The goal of this issue is to bump the versions on the main branch so that the up
 is used for e.g. local development and e2e tests. We also modify tests so that they are testing the previous release.
 
 This comes down to changing occurrences of the old version to the new version, e.g. `v1.3` to `v1.4`:
-1. Setup E2E tests for the new release:
-   1. Goal is that we have clusterctl upgrade tests for the latest stable versions of each contract / for each supported branch. For `v1.4` this means:
-      * v1alpha3: `v0.3`
-      * v1alpha4: `v0.4`
-      * v1beta1: `v1.2`, `v1.3` (will change with each new release)
-   2. Update providers in `docker.yaml`:
-       1. Add a new `v1.3.0` entry.
-       2. Remove providers that are not used anymore (for `v1.4` we don't have to remove any).
-       3. Change `v1.3.99` to `v1.4.99`.
-   3. Adjust `metadata.yaml`'s:
-      1. Create a new `v1.3` `metadata.yaml` (`test/e2e/data/shared/v1.3/metadata.yaml`) by copying
-   `test/e2e/data/shared/main/metadata.yaml`
-      2. Add the new release to the main `metadata.yaml` (`test/e2e/data/shared/main/metadata.yaml`).
-      3. Remove old `metadata.yaml`'s that are not used anymore (for `v1.4` we don't have to remove any).
-   4. Adjust cluster templates in `test/e2e/data/infrastructure-docker`:
-      1. Create a new `v1.3` folder. It should be created based on the `main` folder and only contain the templates
-         we use in the clusterctl upgrade tests (as of today `cluster-template` and `cluster-template-topology`).
-      2. Remove old folders that are not used anymore (for `v1.4` we don't have to remove any).
-   5. Modify the test specs in `test/e2e/clusterctl_upgrade_test.go` (according to the versions we want to test described above).
-      Please note that `InitWithKubernetesVersion` should be the highest mgmt cluster version supported by the respective Cluster API version. 
-2. Update `create-local-repository.py` and `tools/tilt-prepare/main.go`: `v1.3.99` => `v1.4.99`.
-3. Update `.github/workflows/scan.yml` to setup Trivy scanning for the currently supported branches.
-4. Make sure all tests are green (also run `pull-cluster-api-e2e-full-main` and `pull-cluster-api-e2e-workload-upgrade-1-23-latest-main`).
+1. Add new release to top-level `metadata.yaml` and `test/e2e/data/shared/v1beta1/metadata.yaml`.
+2. Update providers in docker.yaml. Roughly:
+   1. Create a new entry for each provider for `v1.3.0`.
+   2. Rename `v1.3.99` to `v1.4.99`.
+3. Modify the test specs in `test/e2e/clusterctl_upgrade_test.go`.
+4. Update `create-local-repository.py` and `tools/tilt-prepare/main.go`: `v1.3.99` => `v1.4.99`.
+5. Make sure all tests are green (also run `pull-cluster-api-e2e-full-main` and `pull-cluster-api-e2e-workload-upgrade-1-23-latest-main`).
 
 Prior art: https://github.com/kubernetes-sigs/cluster-api/pull/6834/files
 
@@ -214,10 +198,9 @@ This can be done by:
 5. Publish `clusterctl` to Homebrew by bumping the version in [clusterctl.rb](https://github.com/Homebrew/homebrew-core/blob/master/Formula/clusterctl.rb).
    <br>**Notes**:
     * This is only done for new latest stable releases, not for beta / RC releases and not for previous release branches.
-    * Check if homebrew already has a PR to update the version (homebrew introduced automation that picks it up). Open one if no PR exists. 
-      * For an example please see: [PR: clusterctl 1.1.5](https://github.com/Homebrew/homebrew-core/pull/105075/files).
-      * Homebrew has [conventions for commit messages](https://docs.brew.sh/Formula-Cookbook#commit) usually
-        the commit message for us should look like: `clusterctl 1.1.5`.
+    * For an example please see: [PR: clusterctl 1.1.5](https://github.com/Homebrew/homebrew-core/pull/105075/files).
+    * Homebrew has [conventions for commit messages](https://docs.brew.sh/Formula-Cookbook#commit) usually
+      the commit message for us should look like: `clusterctl 1.1.5`.
 6. Set EOL date for previous release (prior art: https://github.com/kubernetes-sigs/cluster-api/issues/7146).
 
 Additional information:
diff --git a/go.mod b/go.mod
index 2f28bb38a..9b2abcd33 100644
--- a/go.mod
+++ b/go.mod
@@ -15,12 +15,12 @@ require (
 	github.com/flatcar/container-linux-config-transpiler v0.9.4
 	github.com/flatcar/ignition v0.36.2
 	github.com/go-logr/logr v1.2.3
-	github.com/gobuffalo/flect v1.0.0
+	github.com/gobuffalo/flect v0.3.0
 	github.com/google/go-cmp v0.5.9
 	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.7.0
+	github.com/onsi/ginkgo/v2 v2.6.1
 	github.com/onsi/gomega v1.24.2
 	github.com/pkg/errors v0.9.1
 	github.com/spf13/cobra v1.6.1
@@ -132,7 +132,7 @@ require (
 	golang.org/x/crypto v0.3.0 // indirect
 	golang.org/x/sys v0.3.0 // indirect
 	golang.org/x/term v0.3.0 // indirect
-	golang.org/x/text v0.6.0
+	golang.org/x/text v0.5.0
 	golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 // indirect
 	gomodules.xyz/jsonpatch/v2 v2.2.0
 	google.golang.org/appengine v1.6.7 // indirect
diff --git a/go.sum b/go.sum
index 8ad889e70..8480bfa1b 100644
--- a/go.sum
+++ b/go.sum
@@ -213,8 +213,8 @@ github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh
 github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
 github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
-github.com/gobuffalo/flect v1.0.0 h1:eBFmskjXZgAOagiTXJH25Nt5sdFwNRcb8DKZsIsAUQI=
-github.com/gobuffalo/flect v1.0.0/go.mod h1:l9V6xSb4BlXwsxEMj3FVEub2nkdQjWhPvD8XTTlHPQc=
+github.com/gobuffalo/flect v0.3.0 h1:erfPWM+K1rFNIQeRPdeEXxo8yFr/PO17lhRnS8FUrtk=
+github.com/gobuffalo/flect v0.3.0/go.mod h1:5pf3aGnsvqvCj50AVni7mJJF8ICxGZ8HomberC3pXLE=
 github.com/godbus/dbus v0.0.0-20181025153459-66d97aec3384/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
 github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
 github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
@@ -439,8 +439,8 @@ github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn
 github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
 github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
 github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
-github.com/onsi/ginkgo/v2 v2.7.0 h1:/XxtEV3I3Eif/HobnVx9YmJgk8ENdRsuUmM+fLCFNow=
-github.com/onsi/ginkgo/v2 v2.7.0/go.mod h1:yjiuMwPokqY1XauOgju45q3sJt6VzQ/Fict1LFVcsAo=
+github.com/onsi/ginkgo/v2 v2.6.1 h1:1xQPCjcqYw/J5LchOcp4/2q/jzJFjiAOc25chhnDw+Q=
+github.com/onsi/ginkgo/v2 v2.6.1/go.mod h1:yjiuMwPokqY1XauOgju45q3sJt6VzQ/Fict1LFVcsAo=
 github.com/onsi/gomega v1.24.2 h1:J/tulyYK6JwBldPViHJReihxxZ+22FHs0piGjQAvoUE=
 github.com/onsi/gomega v1.24.2/go.mod h1:gs3J10IS7Z7r7eXRoNJIrNqU4ToQukCJhFtKrWgHWnk=
 github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
@@ -549,9 +549,8 @@ github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ai
 github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4=
 github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
-github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
-github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
@@ -560,7 +559,6 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
-github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
 github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
 github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs=
 github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
@@ -816,8 +814,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
-golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
+golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
diff --git a/hack/boilerplate/boilerplate.py b/hack/boilerplate/boilerplate.py
index 830fcdd3e..0c01379b7 100755
--- a/hack/boilerplate/boilerplate.py
+++ b/hack/boilerplate/boilerplate.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 
 # Copyright 2015 The Kubernetes Authors.
 #
@@ -150,7 +150,9 @@ def file_passes(filename, refs, regexs):
 def file_extension(filename):
     return os.path.splitext(filename)[1].split(".")[-1].lower()
 
-skipped_dirs = ['_output', '.git', "hack/boilerplate/test"]
+skipped_dirs = ['_gopath', '_output', '.git', 'cluster/env.sh',
+                "vendor", "test/e2e/generated/bindata.go", "hack/boilerplate/test",
+                "staging/src/k8s.io/kubectl/pkg/generated/bindata.go"]
 
 # list all the files contain 'DO NOT EDIT', but are not generated
 skipped_ungenerated_files = [
diff --git a/hack/tools/go.mod b/hack/tools/go.mod
index 27731f29f..e25d68865 100644
--- a/hack/tools/go.mod
+++ b/hack/tools/go.mod
@@ -67,7 +67,7 @@ require (
 	github.com/go-openapi/jsonpointer v0.19.5 // indirect
 	github.com/go-openapi/jsonreference v0.20.0 // indirect
 	github.com/go-openapi/swag v0.22.3 // indirect
-	github.com/gobuffalo/flect v1.0.0 // indirect
+	github.com/gobuffalo/flect v0.3.0 // indirect
 	github.com/gogo/protobuf v1.3.2 // indirect
 	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
 	github.com/golang/protobuf v1.5.2 // indirect
@@ -136,7 +136,7 @@ require (
 	golang.org/x/sync v0.1.0 // indirect
 	golang.org/x/sys v0.3.0 // indirect
 	golang.org/x/term v0.3.0 // indirect
-	golang.org/x/text v0.6.0 // indirect
+	golang.org/x/text v0.5.0 // indirect
 	golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 // indirect
 	golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
 	gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect
diff --git a/hack/tools/go.sum b/hack/tools/go.sum
index 95cbe26e0..1a64ef98a 100644
--- a/hack/tools/go.sum
+++ b/hack/tools/go.sum
@@ -207,8 +207,8 @@ github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh
 github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
 github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
-github.com/gobuffalo/flect v1.0.0 h1:eBFmskjXZgAOagiTXJH25Nt5sdFwNRcb8DKZsIsAUQI=
-github.com/gobuffalo/flect v1.0.0/go.mod h1:l9V6xSb4BlXwsxEMj3FVEub2nkdQjWhPvD8XTTlHPQc=
+github.com/gobuffalo/flect v0.3.0 h1:erfPWM+K1rFNIQeRPdeEXxo8yFr/PO17lhRnS8FUrtk=
+github.com/gobuffalo/flect v0.3.0/go.mod h1:5pf3aGnsvqvCj50AVni7mJJF8ICxGZ8HomberC3pXLE=
 github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
 github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
@@ -415,7 +415,7 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW
 github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
 github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
-github.com/onsi/ginkgo/v2 v2.7.0 h1:/XxtEV3I3Eif/HobnVx9YmJgk8ENdRsuUmM+fLCFNow=
+github.com/onsi/ginkgo/v2 v2.6.1 h1:1xQPCjcqYw/J5LchOcp4/2q/jzJFjiAOc25chhnDw+Q=
 github.com/onsi/gomega v1.24.2 h1:J/tulyYK6JwBldPViHJReihxxZ+22FHs0piGjQAvoUE=
 github.com/onsi/gomega v1.24.2/go.mod h1:gs3J10IS7Z7r7eXRoNJIrNqU4ToQukCJhFtKrWgHWnk=
 github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
@@ -499,9 +499,8 @@ github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ai
 github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4=
 github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
-github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
-github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
@@ -511,7 +510,6 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
-github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
 github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs=
 github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
 github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ=
@@ -774,8 +772,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
-golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
+golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
diff --git a/hack/tools/runtime-openapi-gen/vendored_openapi.go b/hack/tools/runtime-openapi-gen/vendored_openapi.go
index c9e5b9d32..c8d69f0b9 100644
--- a/hack/tools/runtime-openapi-gen/vendored_openapi.go
+++ b/hack/tools/runtime-openapi-gen/vendored_openapi.go
@@ -1,19 +1,3 @@
-/*
-Copyright 2021 The Kubernetes Authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
 package main
 
 import (
diff --git a/internal/util/kubeadm/kubeadm.go b/internal/util/kubeadm/kubeadm.go
deleted file mode 100644
index bd6902711..000000000
--- a/internal/util/kubeadm/kubeadm.go
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
-Copyright 2021 The Kubernetes Authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-// Package kubeadm contains utils related to kubeadm.
-package kubeadm
-
-import "github.com/blang/semver"
-
-const (
-	// DefaultImageRepository is the new default Kubernetes image registry.
-	DefaultImageRepository = "registry.k8s.io"
-	// OldDefaultImageRepository is the old default Kubernetes image registry.
-	OldDefaultImageRepository = "k8s.gcr.io"
-)
-
-var (
-	// MinKubernetesVersionImageRegistryMigration is the first Kubernetes minor version which
-	// has patch versions where the default image registry in kubeadm is registry.k8s.io instead of k8s.gcr.io.
-	MinKubernetesVersionImageRegistryMigration = semver.MustParse("1.22.0")
-
-	// NextKubernetesVersionImageRegistryMigration is the next minor version after
-	// the default image registry in kubeadm changed to registry.k8s.io.
-	NextKubernetesVersionImageRegistryMigration = semver.MustParse("1.26.0")
-)
-
-// GetDefaultRegistry returns the default registry of the given kubeadm version.
-func GetDefaultRegistry(version semver.Version) string {
-	// If version <= v1.22.16 return k8s.gcr.io
-	if version.LTE(semver.MustParse("1.22.16")) {
-		return OldDefaultImageRepository
-	}
-
-	// If v1.22.17 <= version < v1.23.0 return registry.k8s.io
-	if version.GTE(semver.MustParse("1.22.17")) &&
-		version.LT(semver.MustParse("1.23.0")) {
-		return DefaultImageRepository
-	}
-
-	// If v1.23.0  <= version <= v1.23.14 return k8s.gcr.io
-	if version.GTE(semver.MustParse("1.23.0")) &&
-		version.LTE(semver.MustParse("1.23.14")) {
-		return OldDefaultImageRepository
-	}
-
-	// If v1.23.15 <= version < v1.24.0 return registry.k8s.io
-	if version.GTE(semver.MustParse("1.23.15")) &&
-		version.LT(semver.MustParse("1.24.0")) {
-		return DefaultImageRepository
-	}
-
-	// If v1.24.0  <= version <= v1.24.8 return k8s.gcr.io
-	if version.GTE(semver.MustParse("1.24.0")) &&
-		version.LTE(semver.MustParse("1.24.8")) {
-		return OldDefaultImageRepository
-	}
-
-	// If v1.24.9  <= version return registry.k8s.io
-	return DefaultImageRepository
-}


Test patch:
diff --git a/controlplane/kubeadm/api/v1beta1/kubeadm_control_plane_webhook_test.go b/controlplane/kubeadm/api/v1beta1/kubeadm_control_plane_webhook_test.go
index 3ead11553..f13d34b36 100644
--- a/controlplane/kubeadm/api/v1beta1/kubeadm_control_plane_webhook_test.go
+++ b/controlplane/kubeadm/api/v1beta1/kubeadm_control_plane_webhook_test.go
@@ -451,6 +451,14 @@ func TestKubeadmControlPlaneValidateUpdate(t *testing.T) {
 	kubernetesVersion := before.DeepCopy()
 	kubernetesVersion.Spec.KubeadmConfigSpec.ClusterConfiguration.KubernetesVersion = "some kubernetes version"
 
+	prevKCPWithVersion := func(version string) *KubeadmControlPlane {
+		prev := before.DeepCopy()
+		prev.Spec.Version = version
+		return prev
+	}
+	skipMinorControlPlaneVersion := prevKCPWithVersion("v1.18.1")
+	emptyControlPlaneVersion := prevKCPWithVersion("")
+
 	controlPlaneEndpoint := before.DeepCopy()
 	controlPlaneEndpoint.Spec.KubeadmConfigSpec.ClusterConfiguration.ControlPlaneEndpoint = "some control plane endpoint"
 
@@ -603,6 +611,13 @@ func TestKubeadmControlPlaneValidateUpdate(t *testing.T) {
 		DataDir: "/data",
 	}
 
+	disallowedUpgrade118Prev := prevKCPWithVersion("v1.18.8")
+	disallowedUpgrade119Version := before.DeepCopy()
+	disallowedUpgrade119Version.Spec.Version = "v1.19.0"
+
+	disallowedUpgrade120AlphaVersion := before.DeepCopy()
+	disallowedUpgrade120AlphaVersion.Spec.Version = "v1.20.0-alpha.0.734_ba502ee555924a"
+
 	updateNTPServers := before.DeepCopy()
 	updateNTPServers.Spec.KubeadmConfigSpec.NTP.Servers = []string{"new-server"}
 
@@ -910,6 +925,36 @@ func TestKubeadmControlPlaneValidateUpdate(t *testing.T) {
 			before:    withoutClusterConfiguration,
 			kcp:       afterEtcdLocalDirAddition,
 		},
+		{
+			name:      "should fail when skipping control plane minor versions",
+			expectErr: true,
+			before:    before,
+			kcp:       skipMinorControlPlaneVersion,
+		},
+		{
+			name:      "should fail when no control plane version is passed",
+			expectErr: true,
+			before:    before,
+			kcp:       emptyControlPlaneVersion,
+		},
+		{
+			name:      "should pass if control plane version is the same",
+			expectErr: false,
+			before:    before,
+			kcp:       before.DeepCopy(),
+		},
+		{
+			name:      "should return error when trying to upgrade to v1.19.0",
+			expectErr: true,
+			before:    disallowedUpgrade118Prev,
+			kcp:       disallowedUpgrade119Version,
+		},
+		{
+			name:      "should return error when trying to upgrade two minor versions",
+			expectErr: true,
+			before:    disallowedUpgrade118Prev,
+			kcp:       disallowedUpgrade120AlphaVersion,
+		},
 		{
 			name:      "should not return an error when maxSurge value is updated to 0",
 			expectErr: false,
@@ -1006,162 +1051,6 @@ func TestKubeadmControlPlaneValidateUpdate(t *testing.T) {
 	}
 }
 
-func TestValidateVersion(t *testing.T) {
-	tests := []struct {
-		name                 string
-		clusterConfiguration *bootstrapv1.ClusterConfiguration
-		oldVersion           string
-		newVersion           string
-		expectErr            bool
-	}{
-		// Basic validation of old and new version.
-		{
-			name:       "error when old version is empty",
-			oldVersion: "",
-			newVersion: "v1.16.6",
-			expectErr:  true,
-		},
-		{
-			name:       "error when old version is invalid",
-			oldVersion: "invalid-version",
-			newVersion: "v1.18.1",
-			expectErr:  true,
-		},
-		{
-			name:       "error when new version is empty",
-			oldVersion: "v1.16.6",
-			newVersion: "",
-			expectErr:  true,
-		},
-		{
-			name:       "error when new version is invalid",
-			oldVersion: "v1.18.1",
-			newVersion: "invalid-version",
-			expectErr:  true,
-		},
-		// Validation that we block upgrade to v1.19.0.
-		// Note: Upgrading to v1.19.0 is not supported, because of issues in v1.19.0,
-		// see: https://github.com/kubernetes-sigs/cluster-api/issues/3564
-		{
-			name:       "error when upgrading to v1.19.0",
-			oldVersion: "v1.18.8",
-			newVersion: "v1.19.0",
-			expectErr:  true,
-		},
-		{
-			name:       "pass when both versions are v1.19.0",
-			oldVersion: "v1.19.0",
-			newVersion: "v1.19.0",
-			expectErr:  false,
-		},
-		// Validation for skip-level upgrades.
-		{
-			name:       "error when upgrading two minor versions",
-			oldVersion: "v1.18.8",
-			newVersion: "v1.20.0-alpha.0.734_ba502ee555924a",
-			expectErr:  true,
-		},
-		{
-			name:       "pass when upgrading one minor version",
-			oldVersion: "v1.20.1",
-			newVersion: "v1.21.18",
-			expectErr:  false,
-		},
-		// Validation for usage of the old registry.
-		// Notes:
-		// * kubeadm versions < v1.22 are always using the old registry.
-		// * kubeadm versions >= v1.25.0 are always using the new registry.
-		// * kubeadm versions in between are using the new registry
-		//   starting with certain patch versions.
-		// This test validates that we don't block upgrades for < v1.22.0 and >= v1.25.0
-		// and block upgrades to kubeadm versions in between with the old registry.
-		{
-			name: "pass when imageRepository is set",
-			clusterConfiguration: &bootstrapv1.ClusterConfiguration{
-				ImageRepository: "k8s.gcr.io",
-			},
-			oldVersion: "v1.21.1",
-			newVersion: "v1.22.16",
-			expectErr:  false,
-		},
-		{
-			name:       "pass when version didn't change",
-			oldVersion: "v1.22.16",
-			newVersion: "v1.22.16",
-			expectErr:  false,
-		},
-		{
-			name:       "pass when new version is < v1.22.0",
-			oldVersion: "v1.20.10",
-			newVersion: "v1.21.5",
-			expectErr:  false,
-		},
-		{
-			name:       "error when new version is using old registry (v1.22.0 <= version <= v1.22.16)",
-			oldVersion: "v1.21.1",
-			newVersion: "v1.22.16", // last patch release using old registry
-			expectErr:  true,
-		},
-		{
-			name:       "pass when new version is using new registry (>= v1.22.17)",
-			oldVersion: "v1.21.1",
-			newVersion: "v1.22.17", // first patch release using new registry
-			expectErr:  false,
-		},
-		{
-			name:       "error when new version is using old registry (v1.23.0 <= version <= v1.23.14)",
-			oldVersion: "v1.22.17",
-			newVersion: "v1.23.14", // last patch release using old registry
-			expectErr:  true,
-		},
-		{
-			name:       "pass when new version is using new registry (>= v1.23.15)",
-			oldVersion: "v1.22.17",
-			newVersion: "v1.23.15", // first patch release using new registry
-			expectErr:  false,
-		},
-		{
-			name:       "error when new version is using old registry (v1.24.0 <= version <= v1.24.8)",
-			oldVersion: "v1.23.1",
-			newVersion: "v1.24.8", // last patch release using old registry
-			expectErr:  true,
-		},
-		{
-			name:       "pass when new version is using new registry (>= v1.24.9)",
-			oldVersion: "v1.23.1",
-			newVersion: "v1.24.9", // first patch release using new registry
-			expectErr:  false,
-		},
-		{
-			name:       "pass when new version is using new registry (>= v1.25.0)",
-			oldVersion: "v1.24.8",
-			newVersion: "v1.25.0", // uses new registry
-			expectErr:  false,
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			g := NewWithT(t)
-
-			kcp := KubeadmControlPlane{
-				Spec: KubeadmControlPlaneSpec{
-					KubeadmConfigSpec: bootstrapv1.KubeadmConfigSpec{
-						ClusterConfiguration: tt.clusterConfiguration,
-					},
-					Version: tt.newVersion,
-				},
-			}
-
-			allErrs := kcp.validateVersion(tt.oldVersion)
-			if tt.expectErr {
-				g.Expect(allErrs).ToNot(HaveLen(0))
-			} else {
-				g.Expect(allErrs).To(HaveLen(0))
-			}
-		})
-	}
-}
 func TestKubeadmControlPlaneValidateUpdateAfterDefaulting(t *testing.T) {
 	before := &KubeadmControlPlane{
 		ObjectMeta: metav1.ObjectMeta{
diff --git a/controlplane/kubeadm/internal/workload_cluster_coredns_test.go b/controlplane/kubeadm/internal/workload_cluster_coredns_test.go
index bac6124e7..2436bc024 100644
--- a/controlplane/kubeadm/internal/workload_cluster_coredns_test.go
+++ b/controlplane/kubeadm/internal/workload_cluster_coredns_test.go
@@ -1213,10 +1213,8 @@ func TestGetCoreDNSInfo(t *testing.T) {
 				},
 			},
 			{
-				name: "rename to coredns/coredns when upgrading to coredns=1.8.0 and kubernetesVersion=1.22.16",
-				// 1.22.16 uses k8s.gcr.io as default registry. Thus the registry doesn't get changed as
-				// FromImage is already using k8s.gcr.io.
-				objs: []client.Object{newCoreDNSInfoDeploymentWithimage("k8s.gcr.io/coredns:1.6.2"), cm},
+				name: "rename to coredns/coredns when upgrading to coredns=1.8.0 and kubernetesVersion=1.21.0",
+				objs: []client.Object{newCoreDNSInfoDeploymentWithimage(image162), cm},
 				clusterConfig: &bootstrapv1.ClusterConfiguration{
 					DNS: bootstrapv1.DNS{
 						ImageMeta: bootstrapv1.ImageMeta{
@@ -1224,42 +1222,18 @@ func TestGetCoreDNSInfo(t *testing.T) {
 						},
 					},
 				},
-				kubernetesVersion: semver.MustParse("1.22.16"),
+				kubernetesVersion: semver.MustParse("1.21.0"),
 				expectedInfo: coreDNSInfo{
 					CurrentMajorMinorPatch: "1.6.2",
 					FromImageTag:           "1.6.2",
 					TargetMajorMinorPatch:  "1.8.0",
-					FromImage:              "k8s.gcr.io/coredns:1.6.2",
+					FromImage:              image162,
 					ToImage:                "k8s.gcr.io/coredns/coredns:1.8.0",
 					ToImageTag:             "1.8.0",
 				},
 			},
-			{
-				name: "rename to coredns/coredns when upgrading to coredns=1.8.0 and kubernetesVersion=1.22.17",
-				// 1.22.17 has registry.k8s.io as default registry. Thus the registry gets changed as
-				// FromImage is using k8s.gcr.io.
-				objs: []client.Object{newCoreDNSInfoDeploymentWithimage("k8s.gcr.io/coredns:1.6.2"), cm},
-				clusterConfig: &bootstrapv1.ClusterConfiguration{
-					DNS: bootstrapv1.DNS{
-						ImageMeta: bootstrapv1.ImageMeta{
-							ImageTag: "1.8.0",
-						},
-					},
-				},
-				kubernetesVersion: semver.MustParse("1.22.17"),
-				expectedInfo: coreDNSInfo{
-					CurrentMajorMinorPatch: "1.6.2",
-					FromImageTag:           "1.6.2",
-					TargetMajorMinorPatch:  "1.8.0",
-					FromImage:              "k8s.gcr.io/coredns:1.6.2",
-					ToImage:                "registry.k8s.io/coredns/coredns:1.8.0",
-					ToImageTag:             "1.8.0",
-				},
-			},
 			{
 				name: "rename to coredns/coredns when upgrading to coredns=1.8.0 and kubernetesVersion=1.26.0",
-				// 1.26.0 uses registry.k8s.io as default registry. Thus the registry doesn't get changed as
-				// FromImage is already using registry.k8s.io.
 				objs: []client.Object{newCoreDNSInfoDeploymentWithimage("registry.k8s.io/coredns:1.6.2"), cm},
 				clusterConfig: &bootstrapv1.ClusterConfiguration{
 					DNS: bootstrapv1.DNS{
@@ -1268,7 +1242,7 @@ func TestGetCoreDNSInfo(t *testing.T) {
 						},
 					},
 				},
-				kubernetesVersion: semver.MustParse("1.26.0"),
+				kubernetesVersion: semver.MustParse("1.24.0"),
 				expectedInfo: coreDNSInfo{
 					CurrentMajorMinorPatch: "1.6.2",
 					FromImageTag:           "1.6.2",
@@ -1279,9 +1253,7 @@ func TestGetCoreDNSInfo(t *testing.T) {
 				},
 			},
 			{
-				name: "patches ImageRepository to registry.k8s.io if it's set on neither global nor DNS-level and kubernetesVersion >= v1.22.17 and rename to coredns/coredns",
-				// 1.22.17 has registry.k8s.io as default registry. Thus the registry gets changed as
-				// FromImage is using k8s.gcr.io.
+				name: "patches ImageRepository to registry.k8s.io if it's set on neither global nor DNS-level and kubernetesVersion >= v1.22 and rename to coredns/coredns",
 				objs: []client.Object{newCoreDNSInfoDeploymentWithimage(image162), cm},
 				clusterConfig: &bootstrapv1.ClusterConfiguration{
 					DNS: bootstrapv1.DNS{
@@ -1290,7 +1262,7 @@ func TestGetCoreDNSInfo(t *testing.T) {
 						},
 					},
 				},
-				kubernetesVersion: semver.MustParse("1.22.17"),
+				kubernetesVersion: semver.MustParse("1.22.0"),
 				expectedInfo: coreDNSInfo{
 					CurrentMajorMinorPatch: "1.6.2",
 					FromImageTag:           "1.6.2",
diff --git a/hack/boilerplate/boilerplate_test.py b/hack/boilerplate/boilerplate_test.py
old mode 100755
new mode 100644
index 49dc60c8c..b8d5b8e9e
--- a/hack/boilerplate/boilerplate_test.py
+++ b/hack/boilerplate/boilerplate_test.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 
 # Copyright 2016 The Kubernetes Authors.
 #
diff --git a/internal/util/kubeadm/kubeadm_test.go b/internal/util/kubeadm/kubeadm_test.go
deleted file mode 100644
index 4e1f7c81f..000000000
--- a/internal/util/kubeadm/kubeadm_test.go
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
-Copyright 2021 The Kubernetes Authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package kubeadm
-
-import (
-	"fmt"
-	"testing"
-
-	"github.com/blang/semver"
-	. "github.com/onsi/gomega"
-)
-
-func TestGetDefaultRegistry(t *testing.T) {
-	tests := []struct {
-		version          string
-		expectedRegistry string
-	}{
-		// < v1.22
-		{version: "1.19.1", expectedRegistry: OldDefaultImageRepository},
-		{version: "1.20.5", expectedRegistry: OldDefaultImageRepository},
-		{version: "1.21.85", expectedRegistry: OldDefaultImageRepository},
-
-		// v1.22
-		{version: "1.22.0", expectedRegistry: OldDefaultImageRepository},
-		{version: "1.22.16", expectedRegistry: OldDefaultImageRepository},
-		{version: "1.22.17", expectedRegistry: DefaultImageRepository},
-		{version: "1.22.99", expectedRegistry: DefaultImageRepository},
-
-		// v1.23
-		{version: "1.23.0", expectedRegistry: OldDefaultImageRepository},
-		{version: "1.23.14", expectedRegistry: OldDefaultImageRepository},
-		{version: "1.23.15", expectedRegistry: DefaultImageRepository},
-		{version: "1.23.99", expectedRegistry: DefaultImageRepository},
-
-		// v1.24
-		{version: "1.24.0", expectedRegistry: OldDefaultImageRepository},
-		{version: "1.24.8", expectedRegistry: OldDefaultImageRepository},
-		{version: "1.24.9", expectedRegistry: DefaultImageRepository},
-		{version: "1.24.99", expectedRegistry: DefaultImageRepository},
-
-		// > v1.24
-		{version: "1.25.0", expectedRegistry: DefaultImageRepository},
-		{version: "1.26.1", expectedRegistry: DefaultImageRepository},
-		{version: "1.27.2", expectedRegistry: DefaultImageRepository},
-	}
-
-	for _, tt := range tests {
-		t.Run(fmt.Sprintf("%s => %s", tt.version, tt.expectedRegistry), func(t *testing.T) {
-			g := NewWithT(t)
-
-			g.Expect(GetDefaultRegistry(semver.MustParse(tt.version))).To(Equal(tt.expectedRegistry))
-		})
-	}
-}
diff --git a/scripts/ci-e2e-lib.sh b/scripts/ci-e2e-lib.sh
index aad29cdfa..ee6db3203 100644
--- a/scripts/ci-e2e-lib.sh
+++ b/scripts/ci-e2e-lib.sh
@@ -201,9 +201,9 @@ EOL
 # the actual test run less sensible to the network speed.
 kind:prepullAdditionalImages () {
   # Pulling cert manager images so we can pre-load in kind nodes
-  kind::prepullImage "quay.io/jetstack/cert-manager-cainjector:v1.11.0"
-  kind::prepullImage "quay.io/jetstack/cert-manager-webhook:v1.11.0"
-  kind::prepullImage "quay.io/jetstack/cert-manager-controller:v1.11.0"
+  kind::prepullImage "quay.io/jetstack/cert-manager-cainjector:v1.10.1"
+  kind::prepullImage "quay.io/jetstack/cert-manager-webhook:v1.10.1"
+  kind::prepullImage "quay.io/jetstack/cert-manager-controller:v1.10.1"
 }
 
 # kind:prepullImage pre-pull a docker image if no already present locally.
diff --git a/test/e2e/clusterctl_upgrade.go b/test/e2e/clusterctl_upgrade.go
index 8e85e336b..a7a2c9363 100644
--- a/test/e2e/clusterctl_upgrade.go
+++ b/test/e2e/clusterctl_upgrade.go
@@ -293,9 +293,9 @@ func ClusterctlUpgradeSpec(ctx context.Context, inputGetter func() ClusterctlUpg
 			input.E2EConfig.GetProviderLatestVersionsByContract(initContract, config.KubeadmControlPlaneProviderName))
 		infrastructureProviders = getValueOrFallback(input.InitWithInfrastructureProviders,
 			input.E2EConfig.GetProviderLatestVersionsByContract(initContract, input.E2EConfig.InfrastructureProviders()...))
-		ipamProviders = getValueOrFallback(input.InitWithIPAMProviders,
+		ipamProviders = getValueOrFallback(input.IPAMProviders,
 			input.E2EConfig.GetProviderLatestVersionsByContract(initContract, input.E2EConfig.IPAMProviders()...))
-		runtimeExtensionProviders = getValueOrFallback(input.InitWithRuntimeExtensionProviders,
+		runtimeExtensionProviders = getValueOrFallback(input.RuntimeExtensionProviders,
 			input.E2EConfig.GetProviderLatestVersionsByContract(initContract, input.E2EConfig.RuntimeExtensionProviders()...))
 
 		clusterctl.InitManagementClusterAndWatchControllerLogs(ctx, clusterctl.InitManagementClusterAndWatchControllerLogsInput{
@@ -735,7 +735,7 @@ func matchUnstructuredLists(l1 *unstructured.UnstructuredList, l2 *unstructured.
 
 // getValueOrFallback returns the input value unless it is empty, then it returns the fallback input.
 func getValueOrFallback(value []string, fallback []string) []string {
-	if value != nil {
+	if len(value) > 0 {
 		return value
 	}
 	return fallback
diff --git a/test/e2e/clusterctl_upgrade_test.go b/test/e2e/clusterctl_upgrade_test.go
index 805b8f5a1..193d7f155 100644
--- a/test/e2e/clusterctl_upgrade_test.go
+++ b/test/e2e/clusterctl_upgrade_test.go
@@ -53,7 +53,7 @@ var _ = Describe("When testing clusterctl upgrades (v0.4=>current)", func() {
 			SkipCleanup:               skipCleanup,
 			InitWithBinary:            "https://github.com/kubernetes-sigs/cluster-api/releases/download/v0.4.8/clusterctl-{OS}-{ARCH}",
 			InitWithProvidersContract: "v1alpha4",
-			InitWithKubernetesVersion: "v1.23.13",
+			InitWithKubernetesVersion: "v1.25.3",
 		}
 	})
 })
@@ -61,53 +61,34 @@ var _ = Describe("When testing clusterctl upgrades (v0.4=>current)", func() {
 var _ = Describe("When testing clusterctl upgrades (v1.2=>current)", func() {
 	ClusterctlUpgradeSpec(ctx, func() ClusterctlUpgradeSpecInput {
 		return ClusterctlUpgradeSpecInput{
-			E2EConfig:             e2eConfig,
-			ClusterctlConfigPath:  clusterctlConfigPath,
-			BootstrapClusterProxy: bootstrapClusterProxy,
-			ArtifactFolder:        artifactFolder,
-			SkipCleanup:           skipCleanup,
-			InitWithBinary:        "https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.2.7/clusterctl-{OS}-{ARCH}",
-			// We have to pin the providers because with `InitWithProvidersContract` the test would
-			// use the latest version for the contract (which is v1.3.0 for v1beta1).
-			InitWithCoreProvider:            "cluster-api:v1.2.8",
-			InitWithBootstrapProviders:      []string{"kubeadm:v1.2.8"},
-			InitWithControlPlaneProviders:   []string{"kubeadm:v1.2.8"},
-			InitWithInfrastructureProviders: []string{"docker:v1.2.8"},
-			// We have to set this to an empty array as clusterctl v1.2 doesn't support
-			// runtime extension providers. If we don't do this the test will automatically
-			// try to deploy the latest version of our test-extension from docker.yaml.
-			InitWithRuntimeExtensionProviders: []string{},
-			InitWithKubernetesVersion:         "v1.26.0",
+			E2EConfig:                 e2eConfig,
+			ClusterctlConfigPath:      clusterctlConfigPath,
+			BootstrapClusterProxy:     bootstrapClusterProxy,
+			ArtifactFolder:            artifactFolder,
+			SkipCleanup:               skipCleanup,
+			InitWithBinary:            "https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.2.7/clusterctl-{OS}-{ARCH}",
+			InitWithProvidersContract: "v1beta1",
+			InitWithKubernetesVersion: "v1.26.0",
 		}
 	})
 })
 
-var _ = Describe("When testing clusterctl upgrades using ClusterClass (v1.2=>current) [ClusterClass]", func() {
+var _ = Describe("When testing clusterctl upgrades (v1.3=>current)", func() {
 	ClusterctlUpgradeSpec(ctx, func() ClusterctlUpgradeSpecInput {
 		return ClusterctlUpgradeSpecInput{
-			E2EConfig:             e2eConfig,
-			ClusterctlConfigPath:  clusterctlConfigPath,
-			BootstrapClusterProxy: bootstrapClusterProxy,
-			ArtifactFolder:        artifactFolder,
-			SkipCleanup:           skipCleanup,
-			InitWithBinary:        "https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.2.7/clusterctl-{OS}-{ARCH}",
-			// We have to pin the providers because with `InitWithProvidersContract` the test would
-			// use the latest version for the contract (which is v1.3.0 for v1beta1).
-			InitWithCoreProvider:            "cluster-api:v1.2.8",
-			InitWithBootstrapProviders:      []string{"kubeadm:v1.2.8"},
-			InitWithControlPlaneProviders:   []string{"kubeadm:v1.2.8"},
-			InitWithInfrastructureProviders: []string{"docker:v1.2.8"},
-			// We have to set this to an empty array as clusterctl v1.2 doesn't support
-			// runtime extension providers. If we don't do this the test will automatically
-			// try to deploy the latest version of our test-extension from docker.yaml.
-			InitWithRuntimeExtensionProviders: []string{},
-			InitWithKubernetesVersion:         "v1.26.0",
-			WorkloadFlavor:                    "topology",
+			E2EConfig:                 e2eConfig,
+			ClusterctlConfigPath:      clusterctlConfigPath,
+			BootstrapClusterProxy:     bootstrapClusterProxy,
+			ArtifactFolder:            artifactFolder,
+			SkipCleanup:               skipCleanup,
+			InitWithBinary:            "https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.3.0/clusterctl-{OS}-{ARCH}",
+			InitWithProvidersContract: "v1beta1",
+			InitWithKubernetesVersion: "v1.26.0",
 		}
 	})
 })
 
-var _ = Describe("When testing clusterctl upgrades (v1.3=>current)", func() {
+var _ = Describe("When testing clusterctl upgrades using ClusterClass (v1.2=>current) [ClusterClass]", func() {
 	ClusterctlUpgradeSpec(ctx, func() ClusterctlUpgradeSpecInput {
 		return ClusterctlUpgradeSpecInput{
 			E2EConfig:                 e2eConfig,
@@ -115,9 +96,10 @@ var _ = Describe("When testing clusterctl upgrades (v1.3=>current)", func() {
 			BootstrapClusterProxy:     bootstrapClusterProxy,
 			ArtifactFolder:            artifactFolder,
 			SkipCleanup:               skipCleanup,
-			InitWithBinary:            "https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.3.0/clusterctl-{OS}-{ARCH}",
+			InitWithBinary:            "https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.2.7/clusterctl-{OS}-{ARCH}",
 			InitWithProvidersContract: "v1beta1",
 			InitWithKubernetesVersion: "v1.26.0",
+			WorkloadFlavor:            "topology",
 		}
 	})
 })
diff --git a/test/e2e/config/docker.yaml b/test/e2e/config/docker.yaml
index 708ecef3b..44ba0e2ab 100644
--- a/test/e2e/config/docker.yaml
+++ b/test/e2e/config/docker.yaml
@@ -19,11 +19,11 @@ images:
   loadBehavior: tryLoad
 - name: gcr.io/k8s-staging-cluster-api/test-extension-{ARCH}:dev
   loadBehavior: tryLoad
-- name: quay.io/jetstack/cert-manager-cainjector:v1.11.0
+- name: quay.io/jetstack/cert-manager-cainjector:v1.10.1
   loadBehavior: tryLoad
-- name: quay.io/jetstack/cert-manager-webhook:v1.11.0
+- name: quay.io/jetstack/cert-manager-webhook:v1.10.1
   loadBehavior: tryLoad
-- name: quay.io/jetstack/cert-manager-controller:v1.11.0
+- name: quay.io/jetstack/cert-manager-controller:v1.10.1
   loadBehavior: tryLoad
 
 providers:
@@ -39,7 +39,7 @@ providers:
     - old: --metrics-addr=127.0.0.1:8080
       new: --metrics-addr=:8080
     files:
-    - sourcePath: "../data/shared/v0.3/metadata.yaml"
+    - sourcePath: "../data/shared/v1alpha3/metadata.yaml"
   - name: v0.4.8 # latest published release in the v1alpha4 series; this is used for v1alpha4 --> v1beta1 clusterctl upgrades test only.
     value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v0.4.8/core-components.yaml"
     type: "url"
@@ -48,16 +48,16 @@ providers:
     - old: --metrics-addr=127.0.0.1:8080
       new: --metrics-addr=:8080
     files:
-    - sourcePath: "../data/shared/v0.4/metadata.yaml"
-  - name: v1.2.8 # supported release in the v1beta1 series; this is used for v1beta1 --> main clusterctl upgrades test only.
-    value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.2.8/core-components.yaml"
+    - sourcePath: "../data/shared/v1alpha4/metadata.yaml"
+  - name: v1.2.7 # supported release in the v1beta1 series; this is used for v1beta1 --> main clusterctl upgrades test only.
+    value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.2.7/core-components.yaml"
     type: "url"
     contract: v1beta1
     replacements:
     - old: --metrics-addr=127.0.0.1:8080
       new: --metrics-addr=:8080
     files:
-    - sourcePath: "../data/shared/v1.2/metadata.yaml"
+    - sourcePath: "../data/shared/v1beta1/v1.2/metadata.yaml"
   - name: v1.3.0 # latest published release in the v1beta1 series; this is used for v1beta1 --> main clusterctl upgrades test only.
     value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.3.0/core-components.yaml"
     type: "url"
@@ -66,14 +66,14 @@ providers:
       - old: --metrics-addr=127.0.0.1:8080
         new: --metrics-addr=:8080
     files:
-      - sourcePath: "../data/shared/v1.3/metadata.yaml"
+      - sourcePath: "../data/shared/v1beta1/v1.3/metadata.yaml"
   - name: v1.4.99 # next; use manifest from source files
     value: ../../../config/default
     replacements:
     - old: --metrics-bind-addr=localhost:8080
       new: "--metrics-bind-addr=:8080\n        - --logging-format=json"
     files:
-    - sourcePath: "../data/shared/main/metadata.yaml"
+    - sourcePath: "../data/shared/v1beta1/main/metadata.yaml"
 
 - name: kubeadm
   type: BootstrapProvider
@@ -86,7 +86,7 @@ providers:
     - old: --metrics-addr=127.0.0.1:8080
       new: --metrics-addr=:8080
     files:
-    - sourcePath: "../data/shared/v0.3/metadata.yaml"
+    - sourcePath: "../data/shared/v1alpha3/metadata.yaml"
   - name: v0.4.8 # latest published release in the v1alpha4 series; this is used for v1alpha4 --> v1beta1 clusterctl upgrades test only.
     value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v0.4.8/bootstrap-components.yaml"
     type: "url"
@@ -95,16 +95,16 @@ providers:
     - old: --metrics-addr=127.0.0.1:8080
       new: --metrics-addr=:8080
     files:
-    - sourcePath: "../data/shared/v0.4/metadata.yaml"
-  - name: v1.2.8 # supported release in the v1beta1 series; this is used for v1beta1 --> main clusterctl upgrades test only.
-    value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.2.8/bootstrap-components.yaml"
+    - sourcePath: "../data/shared/v1alpha4/metadata.yaml"
+  - name: v1.2.7 # supported release in the v1beta1 series; this is used for v1beta1 --> main clusterctl upgrades test only.
+    value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.2.7/bootstrap-components.yaml"
     type: "url"
     contract: v1beta1
     replacements:
     - old: --metrics-addr=127.0.0.1:8080
       new: --metrics-addr=:8080
     files:
-    - sourcePath: "../data/shared/v1.2/metadata.yaml"
+    - sourcePath: "../data/shared/v1beta1/v1.2/metadata.yaml"
   - name: v1.3.0 # latest published release in the v1beta1 series; this is used for v1beta1 --> main clusterctl upgrades test only.
     value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.3.0/bootstrap-components.yaml"
     type: "url"
@@ -113,14 +113,14 @@ providers:
     - old: --metrics-addr=127.0.0.1:8080
       new: --metrics-addr=:8080
     files:
-    - sourcePath: "../data/shared/v1.3/metadata.yaml"
+    - sourcePath: "../data/shared/v1beta1/v1.3/metadata.yaml"
   - name: v1.4.99 # next; use manifest from source files
     value: ../../../bootstrap/kubeadm/config/default
     replacements:
     - old: --metrics-bind-addr=localhost:8080
       new: "--metrics-bind-addr=:8080\n        - --logging-format=json"
     files:
-    - sourcePath: "../data/shared/main/metadata.yaml"
+    - sourcePath: "../data/shared/v1beta1/main/metadata.yaml"
 
 - name: kubeadm
   type: ControlPlaneProvider
@@ -133,7 +133,7 @@ providers:
     - old: --metrics-addr=127.0.0.1:8080
       new: --metrics-addr=:8080
     files:
-    - sourcePath: "../data/shared/v0.3/metadata.yaml"
+    - sourcePath: "../data/shared/v1alpha3/metadata.yaml"
   - name: v0.4.8 # latest published release in the v1alpha4 series; this is used for v1alpha4 --> v1beta1 clusterctl upgrades test only.
     value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v0.4.8/control-plane-components.yaml"
     type: "url"
@@ -142,16 +142,16 @@ providers:
     - old: --metrics-addr=127.0.0.1:8080
       new: --metrics-addr=:8080
     files:
-    - sourcePath: "../data/shared/v0.4/metadata.yaml"
-  - name: v1.2.8 # supported release in the v1beta1 series; this is used for v1beta1 --> main clusterctl upgrades test only.
-    value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.2.8/control-plane-components.yaml"
+    - sourcePath: "../data/shared/v1alpha4/metadata.yaml"
+  - name: v1.2.7 # supported release in the v1beta1 series; this is used for v1beta1 --> main clusterctl upgrades test only.
+    value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.2.7/control-plane-components.yaml"
     type: "url"
     contract: v1beta1
     replacements:
     - old: --metrics-addr=127.0.0.1:8080
       new: --metrics-addr=:8080
     files:
-    - sourcePath: "../data/shared/v1.2/metadata.yaml"
+    - sourcePath: "../data/shared/v1beta1/v1.2/metadata.yaml"
   - name: v1.3.0 # latest published release in the v1beta1 series; this is used for v1beta1 --> main clusterctl upgrades test only.
     value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.3.0/control-plane-components.yaml"
     type: "url"
@@ -160,14 +160,14 @@ providers:
     - old: --metrics-addr=127.0.0.1:8080
       new: --metrics-addr=:8080
     files:
-    - sourcePath: "../data/shared/v1.3/metadata.yaml"
+    - sourcePath: "../data/shared/v1beta1/v1.3/metadata.yaml"
   - name: v1.4.99 # next; use manifest from source files
     value: ../../../controlplane/kubeadm/config/default
     replacements:
     - old: --metrics-bind-addr=localhost:8080
       new: "--metrics-bind-addr=:8080\n        - --logging-format=json"
     files:
-    - sourcePath: "../data/shared/main/metadata.yaml"
+    - sourcePath: "../data/shared/v1beta1/main/metadata.yaml"
 
 - name: docker
   type: InfrastructureProvider
@@ -180,8 +180,8 @@ providers:
     - old: --metrics-addr=127.0.0.1:8080
       new: --metrics-addr=:8080
     files:
-    - sourcePath: "../data/shared/v0.3/metadata.yaml"
-    - sourcePath: "../data/infrastructure-docker/v0.3/cluster-template.yaml"
+    - sourcePath: "../data/shared/v1alpha3/metadata.yaml"
+    - sourcePath: "../data/infrastructure-docker/v1alpha3/cluster-template.yaml"
   - name: v0.4.8 # latest published release in the v1alpha4 series; this is used for v1alpha4 --> v1beta1 clusterctl upgrades test only.
     value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v0.4.8/infrastructure-components-development.yaml"
     type: "url"
@@ -190,20 +190,20 @@ providers:
     - old: --metrics-addr=127.0.0.1:8080
       new: --metrics-addr=:8080
     files:
-    - sourcePath: "../data/shared/v0.4/metadata.yaml"
-    - sourcePath: "../data/infrastructure-docker/v0.4/cluster-template.yaml"
-  - name: v1.2.8 # supported release in the v1beta1 series; this is used for v1beta1 --> main clusterctl upgrades test only.
-    value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.2.8/infrastructure-components-development.yaml"
+    - sourcePath: "../data/shared/v1alpha4/metadata.yaml"
+    - sourcePath: "../data/infrastructure-docker/v1alpha4/cluster-template.yaml"
+  - name: v1.2.7 # supported release in the v1beta1 series; this is used for v1beta1 --> main clusterctl upgrades test only.
+    value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.2.7/infrastructure-components-development.yaml"
     type: "url"
     contract: v1beta1
     replacements:
     - old: --metrics-addr=127.0.0.1:8080
       new: --metrics-addr=:8080
     files:
-    - sourcePath: "../data/shared/v1.2/metadata.yaml"
-    - sourcePath: "../data/infrastructure-docker/v1.2/cluster-template.yaml"
-    - sourcePath: "../data/infrastructure-docker/v1.2/cluster-template-topology.yaml"
-    - sourcePath: "../data/infrastructure-docker/v1.2/clusterclass-quick-start.yaml"
+    - sourcePath: "../data/shared/v1beta1/v1.2/metadata.yaml"
+    - sourcePath: "../data/infrastructure-docker/v1beta1/v1.2/cluster-template.yaml"
+    - sourcePath: "../data/infrastructure-docker/v1beta1/v1.2/cluster-template-topology.yaml"
+    - sourcePath: "../data/infrastructure-docker/v1beta1/v1.2/clusterclass-quick-start.yaml"
   - name: v1.3.0 # latest published release in the v1beta1 series; this is used for v1beta1 --> main clusterctl upgrades test only.
     value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.3.0/infrastructure-components-development.yaml"
     type: "url"
@@ -212,10 +212,10 @@ providers:
     - old: --metrics-addr=127.0.0.1:8080
       new: --metrics-addr=:8080
     files:
-    - sourcePath: "../data/shared/v1.3/metadata.yaml"
-    - sourcePath: "../data/infrastructure-docker/v1.3/cluster-template.yaml"
-    - sourcePath: "../data/infrastructure-docker/v1.3/cluster-template-topology.yaml"
-    - sourcePath: "../data/infrastructure-docker/v1.3/clusterclass-quick-start.yaml"
+    - sourcePath: "../data/shared/v1beta1/v1.3/metadata.yaml"
+    - sourcePath: "../data/infrastructure-docker/v1beta1/v1.3/cluster-template.yaml"
+    - sourcePath: "../data/infrastructure-docker/v1beta1/v1.3/cluster-template-topology.yaml"
+    - sourcePath: "../data/infrastructure-docker/v1beta1/v1.3/clusterclass-quick-start.yaml"
   - name: v1.4.99 # next; use manifest from source files
     value: ../../../test/infrastructure/docker/config/default
     replacements:
@@ -223,23 +223,23 @@ providers:
       new: "--metrics-bind-addr=:8080\n        - --logging-format=json"
     files:
     # Add cluster templates
-    - sourcePath: "../data/infrastructure-docker/main/cluster-template.yaml"
-    - sourcePath: "../data/infrastructure-docker/main/cluster-template-md-remediation.yaml"
-    - sourcePath: "../data/infrastructure-docker/main/cluster-template-kcp-remediation.yaml"
-    - sourcePath: "../data/infrastructure-docker/main/cluster-template-kcp-adoption.yaml"
-    - sourcePath: "../data/infrastructure-docker/main/cluster-template-machine-pool.yaml"
-    - sourcePath: "../data/infrastructure-docker/main/cluster-template-node-drain.yaml"
-    - sourcePath: "../data/infrastructure-docker/main/cluster-template-upgrades.yaml"
-    - sourcePath: "../data/infrastructure-docker/main/cluster-template-upgrades-cgroupfs.yaml"
-    - sourcePath: "../data/infrastructure-docker/main/cluster-template-upgrades-runtimesdk.yaml"
-    - sourcePath: "../data/infrastructure-docker/main/cluster-template-kcp-scale-in.yaml"
-    - sourcePath: "../data/infrastructure-docker/main/cluster-template-ipv6.yaml"
-    - sourcePath: "../data/infrastructure-docker/main/cluster-template-topology-single-node-cluster.yaml"
-    - sourcePath: "../data/infrastructure-docker/main/cluster-template-topology.yaml"
-    - sourcePath: "../data/infrastructure-docker/main/cluster-template-ignition.yaml"
-    - sourcePath: "../data/infrastructure-docker/main/clusterclass-quick-start.yaml"
-    - sourcePath: "../data/infrastructure-docker/main/clusterclass-quick-start-runtimesdk.yaml"
-    - sourcePath: "../data/shared/main/metadata.yaml"
+    - sourcePath: "../data/infrastructure-docker/v1beta1/main/cluster-template.yaml"
+    - sourcePath: "../data/infrastructure-docker/v1beta1/main/cluster-template-md-remediation.yaml"
+    - sourcePath: "../data/infrastructure-docker/v1beta1/main/cluster-template-kcp-remediation.yaml"
+    - sourcePath: "../data/infrastructure-docker/v1beta1/main/cluster-template-kcp-adoption.yaml"
+    - sourcePath: "../data/infrastructure-docker/v1beta1/main/cluster-template-machine-pool.yaml"
+    - sourcePath: "../data/infrastructure-docker/v1beta1/main/cluster-template-node-drain.yaml"
+    - sourcePath: "../data/infrastructure-docker/v1beta1/main/cluster-template-upgrades.yaml"
+    - sourcePath: "../data/infrastructure-docker/v1beta1/main/cluster-template-upgrades-cgroupfs.yaml"
+    - sourcePath: "../data/infrastructure-docker/v1beta1/main/cluster-template-upgrades-runtimesdk.yaml"
+    - sourcePath: "../data/infrastructure-docker/v1beta1/main/cluster-template-kcp-scale-in.yaml"
+    - sourcePath: "../data/infrastructure-docker/v1beta1/main/cluster-template-ipv6.yaml"
+    - sourcePath: "../data/infrastructure-docker/v1beta1/main/cluster-template-topology-single-node-cluster.yaml"
+    - sourcePath: "../data/infrastructure-docker/v1beta1/main/cluster-template-topology.yaml"
+    - sourcePath: "../data/infrastructure-docker/v1beta1/main/cluster-template-ignition.yaml"
+    - sourcePath: "../data/infrastructure-docker/v1beta1/main/clusterclass-quick-start.yaml"
+    - sourcePath: "../data/infrastructure-docker/v1beta1/main/clusterclass-quick-start-runtimesdk.yaml"
+    - sourcePath: "../data/shared/v1beta1/main/metadata.yaml"
 
 - name: test-extension
   type: RuntimeExtensionProvider
@@ -247,7 +247,7 @@ providers:
     - name: v1.4.99 # next; use manifest from source files
       value: ../../../test/extension/config/default
       files:
-      - sourcePath: "../data/shared/main/metadata.yaml"
+      - sourcePath: "../data/shared/v1beta1/main/metadata.yaml"
 
 variables:
   # Default variables for the e2e test; those values could be overridden via env variables, thus
diff --git a/test/e2e/data/infrastructure-docker/README.md b/test/e2e/data/infrastructure-docker/README.md
index 1c14e0a27..da1c0a604 100644
--- a/test/e2e/data/infrastructure-docker/README.md
+++ b/test/e2e/data/infrastructure-docker/README.md
@@ -1,17 +1,12 @@
 # CAPD cluster templates
 
 This folder contains cluster templates used in e2e tests. Each sub-folder contains cluster templates
-for the corresponding Cluster API version.
+complying to the corresponding contract.
 
-Sub-folders for the old versions of Cluster API should contain only templates used for the clusterctl upgrade tests.
-In those tests we first deploy an old version of Cluster API, create a workload cluster and then upgrade Cluster API
-to the version from the current branch (and check that the workload cluster still works).
+The templates in [v1alpha3](./v1alpha3), [v1alpha4](./v1alpha4) and [v1.2](./v1beta1/v1.2) are used for 
+clusterctl upgrade tests. In those tests we first deploy an old version of Cluster API and then upgrade 
+to the version from the current branch. We can't use the same templates for `v1.2` and `main` as not all 
+fields we use in `main` already existed in `v1.2`.
 
-As of today we have clusterctl upgrade tests for the latest stable versions of each contract / for each supported branch, i.e.:
-* v1alpha3: v0.3
-* v1alpha4: v0.4
-* v1beta1: v1.2, v1.3 (will change with each new release)
-
-We cannot use the same cluster templates for all Cluster API versions as not each Cluster API version supports
-the same API fields. For example `KubeadmControlPlane.spec.rolloutBefore.certificatesExpiryDays` was introduced 
-with v1.3 so we couldn't have used it in v1.2 cluster templates.
+Note: The `v1.2` folder can be deleted once we start developing on v1.4.x. Depending on if we introduce and 
+use new fields in v1.4.x we might have to introduce a `v1.3` folder then.
diff --git a/test/e2e/data/infrastructure-docker/v1.3/bases/mp.yaml b/test/e2e/data/infrastructure-docker/v1.3/bases/mp.yaml
deleted file mode 100644
index 6dc8170ed..000000000
--- a/test/e2e/data/infrastructure-docker/v1.3/bases/mp.yaml
+++ /dev/null
@@ -1,49 +0,0 @@
----
-# MachinePool which references the DockerMachinePool and KubeadmConfigTemplate below
-apiVersion: cluster.x-k8s.io/v1beta1
-kind: MachinePool
-metadata:
-  name: "${CLUSTER_NAME}-mp-0"
-spec:
-  clusterName: '${CLUSTER_NAME}'
-  replicas: ${WORKER_MACHINE_COUNT}
-  template:
-    spec:
-      bootstrap:
-        configRef:
-          apiVersion: bootstrap.cluster.x-k8s.io/v1beta1
-          kind: KubeadmConfig
-          name: "${CLUSTER_NAME}-mp-0-config"
-      clusterName: '${CLUSTER_NAME}'
-      infrastructureRef:
-        apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
-        kind: DockerMachinePool
-        name: "${CLUSTER_NAME}-dmp-0"
-      version: "${KUBERNETES_VERSION}"
-  failureDomains:
-    - fd4
-    - fd5
-    - fd6
-    - fd7
-    - fd8
----
-# DockerMachinePool using default values referenced by the MachinePool
-apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
-kind: DockerMachinePool
-metadata:
-  name: "${CLUSTER_NAME}-dmp-0"
-spec:
-  template:
-      # The DOCKER_PRELOAD_IMAGES variable gets set in self-hosted E2E tests to the list of images of the E2E configuration.
-      preLoadImages: ${DOCKER_PRELOAD_IMAGES:-[]}
----
-# KubeadmConfigTemplate referenced by the MachinePool
-apiVersion: bootstrap.cluster.x-k8s.io/v1beta1
-kind: KubeadmConfig
-metadata:
-  name: "${CLUSTER_NAME}-mp-0-config"
-spec:
-  joinConfiguration:
-    nodeRegistration:
-      kubeletExtraArgs:
-        eviction-hard: nodefs.available<0%,nodefs.inodesFree<0%,imagefs.available<0%
diff --git a/test/e2e/data/infrastructure-docker/v0.3/bases/cluster-with-kcp.yaml b/test/e2e/data/infrastructure-docker/v1alpha3/bases/cluster-with-kcp.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/v0.3/bases/cluster-with-kcp.yaml
rename to test/e2e/data/infrastructure-docker/v1alpha3/bases/cluster-with-kcp.yaml
diff --git a/test/e2e/data/infrastructure-docker/v0.3/bases/crs.yaml b/test/e2e/data/infrastructure-docker/v1alpha3/bases/crs.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/v0.3/bases/crs.yaml
rename to test/e2e/data/infrastructure-docker/v1alpha3/bases/crs.yaml
diff --git a/test/e2e/data/infrastructure-docker/v0.3/bases/md.yaml b/test/e2e/data/infrastructure-docker/v1alpha3/bases/md.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/v0.3/bases/md.yaml
rename to test/e2e/data/infrastructure-docker/v1alpha3/bases/md.yaml
diff --git a/test/e2e/data/infrastructure-docker/main/cluster-template/kustomization.yaml b/test/e2e/data/infrastructure-docker/v1alpha3/cluster-template/kustomization.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/main/cluster-template/kustomization.yaml
rename to test/e2e/data/infrastructure-docker/v1alpha3/cluster-template/kustomization.yaml
diff --git a/test/e2e/data/infrastructure-docker/v0.4/bases/cluster-with-kcp.yaml b/test/e2e/data/infrastructure-docker/v1alpha4/bases/cluster-with-kcp.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/v0.4/bases/cluster-with-kcp.yaml
rename to test/e2e/data/infrastructure-docker/v1alpha4/bases/cluster-with-kcp.yaml
diff --git a/test/e2e/data/infrastructure-docker/v0.4/bases/crs.yaml b/test/e2e/data/infrastructure-docker/v1alpha4/bases/crs.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/v0.4/bases/crs.yaml
rename to test/e2e/data/infrastructure-docker/v1alpha4/bases/crs.yaml
diff --git a/test/e2e/data/infrastructure-docker/v0.4/bases/md.yaml b/test/e2e/data/infrastructure-docker/v1alpha4/bases/md.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/v0.4/bases/md.yaml
rename to test/e2e/data/infrastructure-docker/v1alpha4/bases/md.yaml
diff --git a/test/e2e/data/infrastructure-docker/v0.4/bases/mp.yaml b/test/e2e/data/infrastructure-docker/v1alpha4/bases/mp.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/v0.4/bases/mp.yaml
rename to test/e2e/data/infrastructure-docker/v1alpha4/bases/mp.yaml
diff --git a/test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-ipv6/kcp-ipv6.yaml b/test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-ipv6/kcp-ipv6.yaml
new file mode 100644
index 000000000..62f0a89c3
--- /dev/null
+++ b/test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-ipv6/kcp-ipv6.yaml
@@ -0,0 +1,22 @@
+---
+kind: KubeadmControlPlane
+apiVersion: controlplane.cluster.x-k8s.io/v1alpha4
+metadata:
+  name: "${CLUSTER_NAME}-control-plane"
+spec:
+  kubeadmConfigSpec:
+    clusterConfiguration:
+      apiServer:
+        # host.docker.internal is required by kubetest when running on MacOS because of the way ports are proxied.
+        certSANs: [localhost, "::", "::1", host.docker.internal]
+    initConfiguration:
+      localAPIEndpoint:
+        advertiseAddress: '::'
+        bindPort: 6443
+      nodeRegistration:
+        kubeletExtraArgs:
+          node-ip: "::"
+    joinConfiguration:
+      nodeRegistration:
+        kubeletExtraArgs:
+          node-ip: "::"
diff --git a/test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-ipv6/kustomization.yaml b/test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-ipv6/kustomization.yaml
new file mode 100644
index 000000000..1a233e731
--- /dev/null
+++ b/test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-ipv6/kustomization.yaml
@@ -0,0 +1,8 @@
+bases:
+  - ../bases/cluster-with-kcp.yaml
+  - ../bases/md.yaml
+  - ../bases/crs.yaml
+
+patchesStrategicMerge:
+  - ./md-ipv6.yaml
+  - ./kcp-ipv6.yaml
diff --git a/test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-ipv6/md-ipv6.yaml b/test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-ipv6/md-ipv6.yaml
new file mode 100644
index 000000000..106f4968b
--- /dev/null
+++ b/test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-ipv6/md-ipv6.yaml
@@ -0,0 +1,16 @@
+---
+apiVersion: bootstrap.cluster.x-k8s.io/v1alpha4
+kind: KubeadmConfigTemplate
+metadata:
+  name: "${CLUSTER_NAME}-md-0"
+spec:
+  template:
+    spec:
+      initConfiguration:
+        nodeRegistration:
+          kubeletExtraArgs:
+            node-ip: "::"
+      joinConfiguration:
+        nodeRegistration:
+          kubeletExtraArgs:
+            node-ip: "::"
diff --git a/test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-kcp-adoption/step1/cluster-with-cp0.yaml b/test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-kcp-adoption/step1/cluster-with-cp0.yaml
new file mode 100644
index 000000000..aa272922b
--- /dev/null
+++ b/test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-kcp-adoption/step1/cluster-with-cp0.yaml
@@ -0,0 +1,76 @@
+---
+# DockerCluster object referenced by the Cluster object
+apiVersion: infrastructure.cluster.x-k8s.io/v1alpha4
+kind: DockerCluster
+metadata:
+  name: '${CLUSTER_NAME}'
+---
+# Cluster object with
+# - No reference to the KubeadmControlPlane object
+# - the label cni=${CLUSTER_NAME}-crs-0, so the cluster can be selected by the ClusterResourceSet.
+apiVersion: cluster.x-k8s.io/v1alpha4
+kind: Cluster
+metadata:
+  name: '${CLUSTER_NAME}'
+  labels:
+    cni: "${CLUSTER_NAME}-crs-0"
+spec:
+  clusterNetwork:
+    services:
+      cidrBlocks: ['${DOCKER_SERVICE_CIDRS}']
+    pods:
+      cidrBlocks: ['${DOCKER_POD_CIDRS}']
+    serviceDomain: '${DOCKER_SERVICE_DOMAIN}'
+  infrastructureRef:
+    apiVersion: infrastructure.cluster.x-k8s.io/v1alpha4
+    kind: DockerCluster
+    name: '${CLUSTER_NAME}'
+---
+# DockerMachine referenced by the Machine cp0
+apiVersion: infrastructure.cluster.x-k8s.io/v1alpha4
+kind: DockerMachine
+metadata:
+  name: "${CLUSTER_NAME}-control-plane-0"
+spec: {}
+---
+# KubeadmConfig referenced by the Machine cp0
+apiVersion: bootstrap.cluster.x-k8s.io/v1alpha4
+kind: KubeadmConfig
+metadata:
+  name: "${CLUSTER_NAME}-control-plane-0"
+spec:
+  clusterConfiguration:
+    controllerManager:
+      extraArgs: {enable-hostpath-provisioner: 'true'}
+    apiServer:
+      certSANs: [localhost, 127.0.0.1]
+  initConfiguration:
+    nodeRegistration:
+      criSocket: unix:///var/run/containerd/containerd.sock
+      kubeletExtraArgs:
+        eviction-hard: 'nodefs.available<0%,nodefs.inodesFree<0%,imagefs.available<0%'
+  joinConfiguration:
+    nodeRegistration:
+      criSocket: unix:///var/run/containerd/containerd.sock
+      kubeletExtraArgs:
+        eviction-hard: 'nodefs.available<0%,nodefs.inodesFree<0%,imagefs.available<0%'
+---
+# cp0 Machine
+apiVersion: cluster.x-k8s.io/v1alpha4
+kind: Machine
+metadata:
+  name: "${CLUSTER_NAME}-control-plane-0"
+  labels:
+    cluster.x-k8s.io/control-plane: ''
+spec:
+  clusterName: "${ CLUSTER_NAME }"
+  version: "${ KUBERNETES_VERSION }"
+  bootstrap:
+    configRef:
+      name: "${ CLUSTER_NAME }-control-plane-0"
+      apiVersion: bootstrap.cluster.x-k8s.io/v1alpha4
+      kind: KubeadmConfig
+  infrastructureRef:
+    name: "${ CLUSTER_NAME }-control-plane-0"
+    apiVersion: infrastructure.cluster.x-k8s.io/v1alpha4
+    kind: DockerMachine
diff --git a/test/e2e/data/infrastructure-docker/main/cluster-template-kcp-adoption/step1/kustomization.yaml b/test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-kcp-adoption/step1/kustomization.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/main/cluster-template-kcp-adoption/step1/kustomization.yaml
rename to test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-kcp-adoption/step1/kustomization.yaml
diff --git a/test/e2e/data/infrastructure-docker/main/cluster-template-kcp-adoption/step2/kustomization.yaml b/test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-kcp-adoption/step2/kustomization.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/main/cluster-template-kcp-adoption/step2/kustomization.yaml
rename to test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-kcp-adoption/step2/kustomization.yaml
diff --git a/test/e2e/data/infrastructure-docker/main/cluster-template-kcp-remediation/kustomization.yaml b/test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-kcp-remediation/kustomization.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/main/cluster-template-kcp-remediation/kustomization.yaml
rename to test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-kcp-remediation/kustomization.yaml
diff --git a/test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-kcp-remediation/mhc.yaml b/test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-kcp-remediation/mhc.yaml
new file mode 100644
index 000000000..1de43a1ef
--- /dev/null
+++ b/test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-kcp-remediation/mhc.yaml
@@ -0,0 +1,18 @@
+---
+# MachineHealthCheck object with
+# - a selector that targets all the machines with label cluster.x-k8s.io/control-plane=""
+# - unhealthyConditions triggering remediation after 10s the condition is set
+apiVersion: cluster.x-k8s.io/v1alpha4
+kind: MachineHealthCheck
+metadata:
+  name: "${CLUSTER_NAME}-mhc-0"
+spec:
+  clusterName: "${CLUSTER_NAME}"
+  maxUnhealthy: 100%
+  selector:
+    matchLabels:
+      cluster.x-k8s.io/control-plane: ""
+  unhealthyConditions:
+    - type: e2e.remediation.condition
+      status: "False"
+      timeout: 10s
diff --git a/test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-kcp-scale-in/cluster-with-kcp.yaml b/test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-kcp-scale-in/cluster-with-kcp.yaml
new file mode 100644
index 000000000..81426d787
--- /dev/null
+++ b/test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-kcp-scale-in/cluster-with-kcp.yaml
@@ -0,0 +1,9 @@
+# KubeadmControlPlane referenced by the Cluster object with
+kind: KubeadmControlPlane
+apiVersion: controlplane.cluster.x-k8s.io/v1alpha4
+metadata:
+  name: "${CLUSTER_NAME}-control-plane"
+spec:
+  rolloutStrategy:
+    rollingUpdate:
+      maxSurge: 0
diff --git a/test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-kcp-scale-in/kustomization.yaml b/test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-kcp-scale-in/kustomization.yaml
new file mode 100644
index 000000000..50546094a
--- /dev/null
+++ b/test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-kcp-scale-in/kustomization.yaml
@@ -0,0 +1,7 @@
+bases:
+- ../bases/crs.yaml
+- ../bases/md.yaml
+- ../bases/cluster-with-kcp.yaml
+
+patchesStrategicMerge:
+- ./cluster-with-kcp.yaml
\ No newline at end of file
diff --git a/test/e2e/data/infrastructure-docker/main/cluster-template-machine-pool/kustomization.yaml b/test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-machine-pool/kustomization.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/main/cluster-template-machine-pool/kustomization.yaml
rename to test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-machine-pool/kustomization.yaml
diff --git a/test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-md-remediation/kustomization.yaml b/test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-md-remediation/kustomization.yaml
new file mode 100644
index 000000000..82c8029e8
--- /dev/null
+++ b/test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-md-remediation/kustomization.yaml
@@ -0,0 +1,8 @@
+bases:
+  - ../bases/cluster-with-kcp.yaml
+  - ../bases/md.yaml
+  - ../bases/crs.yaml
+  - mhc.yaml
+
+patchesStrategicMerge:
+- ./md.yaml
diff --git a/test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-md-remediation/md.yaml b/test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-md-remediation/md.yaml
new file mode 100644
index 000000000..9b93a2288
--- /dev/null
+++ b/test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-md-remediation/md.yaml
@@ -0,0 +1,9 @@
+apiVersion: cluster.x-k8s.io/v1alpha4
+kind: MachineDeployment
+metadata:
+  name: "${CLUSTER_NAME}-md-0"
+spec:
+  template:
+    metadata:
+      labels:
+        "e2e.remediation.label": ""
diff --git a/test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-md-remediation/mhc.yaml b/test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-md-remediation/mhc.yaml
new file mode 100644
index 000000000..236c3632d
--- /dev/null
+++ b/test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-md-remediation/mhc.yaml
@@ -0,0 +1,18 @@
+---
+# MachineHealthCheck object with
+# - a selector that targets all the machines with label e2e.remediation.label=""
+# - unhealthyConditions triggering remediation after 10s the condition is set
+apiVersion: cluster.x-k8s.io/v1alpha4
+kind: MachineHealthCheck
+metadata:
+  name: "${CLUSTER_NAME}-mhc-0"
+spec:
+  clusterName: "${CLUSTER_NAME}"
+  maxUnhealthy: 100%
+  selector:
+    matchLabels:
+      e2e.remediation.label: ""
+  unhealthyConditions:
+    - type: e2e.remediation.condition
+      status: "False"
+      timeout: 10s
diff --git a/test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-node-drain/cluster-with-kcp.yaml b/test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-node-drain/cluster-with-kcp.yaml
new file mode 100644
index 000000000..16cffd0eb
--- /dev/null
+++ b/test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-node-drain/cluster-with-kcp.yaml
@@ -0,0 +1,9 @@
+# KubeadmControlPlane referenced by the Cluster object with
+# - the label kcp-adoption.step2, because it should be created in the second step of the kcp-adoption test.
+kind: KubeadmControlPlane
+apiVersion: controlplane.cluster.x-k8s.io/v1alpha4
+metadata:
+  name: "${CLUSTER_NAME}-control-plane"
+spec:
+  machineTemplate:
+    nodeDrainTimeout: ${NODE_DRAIN_TIMEOUT}
diff --git a/test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-node-drain/kustomization.yaml b/test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-node-drain/kustomization.yaml
new file mode 100644
index 000000000..dde0c51f0
--- /dev/null
+++ b/test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-node-drain/kustomization.yaml
@@ -0,0 +1,8 @@
+bases:
+- ../bases/crs.yaml
+- ../bases/md.yaml
+- ../bases/cluster-with-kcp.yaml
+
+patchesStrategicMerge:
+- ./md.yaml
+- ./cluster-with-kcp.yaml
diff --git a/test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-node-drain/md.yaml b/test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-node-drain/md.yaml
new file mode 100644
index 000000000..4f4ca9c2f
--- /dev/null
+++ b/test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-node-drain/md.yaml
@@ -0,0 +1,8 @@
+apiVersion: cluster.x-k8s.io/v1alpha4
+kind: MachineDeployment
+metadata:
+  name: "${CLUSTER_NAME}-md-0"
+spec:
+  template:
+    spec:
+      nodeDrainTimeout: "${NODE_DRAIN_TIMEOUT}"
diff --git a/test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-upgrades/kustomization.yaml b/test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-upgrades/kustomization.yaml
new file mode 100644
index 000000000..08c1848d7
--- /dev/null
+++ b/test/e2e/data/infrastructure-docker/v1alpha4/cluster-template-upgrades/kustomization.yaml
@@ -0,0 +1,5 @@
+resources:
+  - ../bases/cluster-with-kcp.yaml
+  - ../bases/crs.yaml
+  - ../bases/md.yaml
+  - ../bases/mp.yaml
diff --git a/test/e2e/data/infrastructure-docker/v0.3/cluster-template/kustomization.yaml b/test/e2e/data/infrastructure-docker/v1alpha4/cluster-template/kustomization.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/v0.3/cluster-template/kustomization.yaml
rename to test/e2e/data/infrastructure-docker/v1alpha4/cluster-template/kustomization.yaml
diff --git a/test/e2e/data/infrastructure-docker/main/bases/cluster-with-kcp.yaml b/test/e2e/data/infrastructure-docker/v1beta1/main/bases/cluster-with-kcp.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/main/bases/cluster-with-kcp.yaml
rename to test/e2e/data/infrastructure-docker/v1beta1/main/bases/cluster-with-kcp.yaml
diff --git a/test/e2e/data/infrastructure-docker/main/bases/cluster-with-topology.yaml b/test/e2e/data/infrastructure-docker/v1beta1/main/bases/cluster-with-topology.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/main/bases/cluster-with-topology.yaml
rename to test/e2e/data/infrastructure-docker/v1beta1/main/bases/cluster-with-topology.yaml
diff --git a/test/e2e/data/infrastructure-docker/main/bases/crs.yaml b/test/e2e/data/infrastructure-docker/v1beta1/main/bases/crs.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/main/bases/crs.yaml
rename to test/e2e/data/infrastructure-docker/v1beta1/main/bases/crs.yaml
diff --git a/test/e2e/data/infrastructure-docker/main/bases/md.yaml b/test/e2e/data/infrastructure-docker/v1beta1/main/bases/md.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/main/bases/md.yaml
rename to test/e2e/data/infrastructure-docker/v1beta1/main/bases/md.yaml
diff --git a/test/e2e/data/infrastructure-docker/main/bases/mp.yaml b/test/e2e/data/infrastructure-docker/v1beta1/main/bases/mp.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/main/bases/mp.yaml
rename to test/e2e/data/infrastructure-docker/v1beta1/main/bases/mp.yaml
diff --git a/test/e2e/data/infrastructure-docker/main/cluster-template-ignition/ignition.yaml b/test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-ignition/ignition.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/main/cluster-template-ignition/ignition.yaml
rename to test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-ignition/ignition.yaml
diff --git a/test/e2e/data/infrastructure-docker/main/cluster-template-ignition/kustomization.yaml b/test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-ignition/kustomization.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/main/cluster-template-ignition/kustomization.yaml
rename to test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-ignition/kustomization.yaml
diff --git a/test/e2e/data/infrastructure-docker/main/cluster-template-ipv6/cluster-ipv6.yaml b/test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-ipv6/cluster-ipv6.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/main/cluster-template-ipv6/cluster-ipv6.yaml
rename to test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-ipv6/cluster-ipv6.yaml
diff --git a/test/e2e/data/infrastructure-docker/main/cluster-template-ipv6/kcp-ipv6.yaml b/test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-ipv6/kcp-ipv6.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/main/cluster-template-ipv6/kcp-ipv6.yaml
rename to test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-ipv6/kcp-ipv6.yaml
diff --git a/test/e2e/data/infrastructure-docker/main/cluster-template-ipv6/kustomization.yaml b/test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-ipv6/kustomization.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/main/cluster-template-ipv6/kustomization.yaml
rename to test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-ipv6/kustomization.yaml
diff --git a/test/e2e/data/infrastructure-docker/main/cluster-template-ipv6/md-ipv6.yaml b/test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-ipv6/md-ipv6.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/main/cluster-template-ipv6/md-ipv6.yaml
rename to test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-ipv6/md-ipv6.yaml
diff --git a/test/e2e/data/infrastructure-docker/main/cluster-template-kcp-adoption/step1/cluster-with-cp0.yaml b/test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-kcp-adoption/step1/cluster-with-cp0.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/main/cluster-template-kcp-adoption/step1/cluster-with-cp0.yaml
rename to test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-kcp-adoption/step1/cluster-with-cp0.yaml
diff --git a/test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-kcp-adoption/step1/kustomization.yaml b/test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-kcp-adoption/step1/kustomization.yaml
new file mode 100644
index 000000000..fa5f1edcc
--- /dev/null
+++ b/test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-kcp-adoption/step1/kustomization.yaml
@@ -0,0 +1,8 @@
+commonLabels:
+  kcp-adoption.step1: ""
+
+bases:
+  - cluster-with-cp0.yaml
+  - ../../bases/crs.yaml
+
+
diff --git a/test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-kcp-adoption/step2/kustomization.yaml b/test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-kcp-adoption/step2/kustomization.yaml
new file mode 100644
index 000000000..f7b1382cf
--- /dev/null
+++ b/test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-kcp-adoption/step2/kustomization.yaml
@@ -0,0 +1,6 @@
+commonLabels:
+  kcp-adoption.step2: ""
+
+bases:
+  - ../../bases/cluster-with-kcp.yaml
+
diff --git a/test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-kcp-remediation/kustomization.yaml b/test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-kcp-remediation/kustomization.yaml
new file mode 100644
index 000000000..e234e37be
--- /dev/null
+++ b/test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-kcp-remediation/kustomization.yaml
@@ -0,0 +1,5 @@
+bases:
+  - ../bases/cluster-with-kcp.yaml
+  - ../bases/md.yaml
+  - ../bases/crs.yaml
+  - mhc.yaml
diff --git a/test/e2e/data/infrastructure-docker/main/cluster-template-kcp-remediation/mhc.yaml b/test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-kcp-remediation/mhc.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/main/cluster-template-kcp-remediation/mhc.yaml
rename to test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-kcp-remediation/mhc.yaml
diff --git a/test/e2e/data/infrastructure-docker/main/cluster-template-kcp-scale-in/kcp-scale-in-variable.yaml b/test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-kcp-scale-in/kcp-scale-in-variable.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/main/cluster-template-kcp-scale-in/kcp-scale-in-variable.yaml
rename to test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-kcp-scale-in/kcp-scale-in-variable.yaml
diff --git a/test/e2e/data/infrastructure-docker/main/cluster-template-kcp-scale-in/kustomization.yaml b/test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-kcp-scale-in/kustomization.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/main/cluster-template-kcp-scale-in/kustomization.yaml
rename to test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-kcp-scale-in/kustomization.yaml
diff --git a/test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-machine-pool/kustomization.yaml b/test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-machine-pool/kustomization.yaml
new file mode 100644
index 000000000..2323bae83
--- /dev/null
+++ b/test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-machine-pool/kustomization.yaml
@@ -0,0 +1,4 @@
+resources:
+  - ../bases/cluster-with-kcp.yaml
+  - ../bases/mp.yaml
+  - ../bases/crs.yaml
diff --git a/test/e2e/data/infrastructure-docker/main/cluster-template-md-remediation/kustomization.yaml b/test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-md-remediation/kustomization.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/main/cluster-template-md-remediation/kustomization.yaml
rename to test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-md-remediation/kustomization.yaml
diff --git a/test/e2e/data/infrastructure-docker/main/cluster-template-md-remediation/md.yaml b/test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-md-remediation/md.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/main/cluster-template-md-remediation/md.yaml
rename to test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-md-remediation/md.yaml
diff --git a/test/e2e/data/infrastructure-docker/main/cluster-template-md-remediation/mhc.yaml b/test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-md-remediation/mhc.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/main/cluster-template-md-remediation/mhc.yaml
rename to test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-md-remediation/mhc.yaml
diff --git a/test/e2e/data/infrastructure-docker/main/cluster-template-node-drain/cluster-with-kcp.yaml b/test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-node-drain/cluster-with-kcp.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/main/cluster-template-node-drain/cluster-with-kcp.yaml
rename to test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-node-drain/cluster-with-kcp.yaml
diff --git a/test/e2e/data/infrastructure-docker/main/cluster-template-node-drain/kustomization.yaml b/test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-node-drain/kustomization.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/main/cluster-template-node-drain/kustomization.yaml
rename to test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-node-drain/kustomization.yaml
diff --git a/test/e2e/data/infrastructure-docker/main/cluster-template-node-drain/md.yaml b/test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-node-drain/md.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/main/cluster-template-node-drain/md.yaml
rename to test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-node-drain/md.yaml
diff --git a/test/e2e/data/infrastructure-docker/main/cluster-template-topology-single-node-cluster/disable-control-plane-taint-variable.yaml b/test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-topology-single-node-cluster/disable-control-plane-taint-variable.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/main/cluster-template-topology-single-node-cluster/disable-control-plane-taint-variable.yaml
rename to test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-topology-single-node-cluster/disable-control-plane-taint-variable.yaml
diff --git a/test/e2e/data/infrastructure-docker/main/cluster-template-topology-single-node-cluster/kustomization.yaml b/test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-topology-single-node-cluster/kustomization.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/main/cluster-template-topology-single-node-cluster/kustomization.yaml
rename to test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-topology-single-node-cluster/kustomization.yaml
diff --git a/test/e2e/data/infrastructure-docker/main/cluster-template-topology/kustomization.yaml b/test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-topology/kustomization.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/main/cluster-template-topology/kustomization.yaml
rename to test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-topology/kustomization.yaml
diff --git a/test/e2e/data/infrastructure-docker/main/cluster-template-upgrades-cgroupfs/kustomization.yaml b/test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-upgrades-cgroupfs/kustomization.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/main/cluster-template-upgrades-cgroupfs/kustomization.yaml
rename to test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-upgrades-cgroupfs/kustomization.yaml
diff --git a/test/e2e/data/infrastructure-docker/main/cluster-template-upgrades-cgroupfs/mp-cgroupfs.yaml b/test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-upgrades-cgroupfs/mp-cgroupfs.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/main/cluster-template-upgrades-cgroupfs/mp-cgroupfs.yaml
rename to test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-upgrades-cgroupfs/mp-cgroupfs.yaml
diff --git a/test/e2e/data/infrastructure-docker/main/cluster-template-upgrades-cgroupfs/mp-default-cgroupfs.yaml b/test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-upgrades-cgroupfs/mp-default-cgroupfs.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/main/cluster-template-upgrades-cgroupfs/mp-default-cgroupfs.yaml
rename to test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-upgrades-cgroupfs/mp-default-cgroupfs.yaml
diff --git a/test/e2e/data/infrastructure-docker/main/cluster-template-upgrades-runtimesdk/cluster-runtimesdk.yaml b/test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-upgrades-runtimesdk/cluster-runtimesdk.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/main/cluster-template-upgrades-runtimesdk/cluster-runtimesdk.yaml
rename to test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-upgrades-runtimesdk/cluster-runtimesdk.yaml
diff --git a/test/e2e/data/infrastructure-docker/main/cluster-template-upgrades-runtimesdk/kustomization.yaml b/test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-upgrades-runtimesdk/kustomization.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/main/cluster-template-upgrades-runtimesdk/kustomization.yaml
rename to test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-upgrades-runtimesdk/kustomization.yaml
diff --git a/test/e2e/data/infrastructure-docker/main/cluster-template-upgrades/kustomization.yaml b/test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-upgrades/kustomization.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/main/cluster-template-upgrades/kustomization.yaml
rename to test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template-upgrades/kustomization.yaml
diff --git a/test/e2e/data/infrastructure-docker/v0.4/cluster-template/kustomization.yaml b/test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template/kustomization.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/v0.4/cluster-template/kustomization.yaml
rename to test/e2e/data/infrastructure-docker/v1beta1/main/cluster-template/kustomization.yaml
diff --git a/test/e2e/data/infrastructure-docker/main/clusterclass-quick-start-runtimesdk.yaml b/test/e2e/data/infrastructure-docker/v1beta1/main/clusterclass-quick-start-runtimesdk.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/main/clusterclass-quick-start-runtimesdk.yaml
rename to test/e2e/data/infrastructure-docker/v1beta1/main/clusterclass-quick-start-runtimesdk.yaml
diff --git a/test/e2e/data/infrastructure-docker/main/clusterclass-quick-start.yaml b/test/e2e/data/infrastructure-docker/v1beta1/main/clusterclass-quick-start.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/main/clusterclass-quick-start.yaml
rename to test/e2e/data/infrastructure-docker/v1beta1/main/clusterclass-quick-start.yaml
diff --git a/test/e2e/data/infrastructure-docker/v1.2/bases/cluster-with-kcp.yaml b/test/e2e/data/infrastructure-docker/v1beta1/v1.2/bases/cluster-with-kcp.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/v1.2/bases/cluster-with-kcp.yaml
rename to test/e2e/data/infrastructure-docker/v1beta1/v1.2/bases/cluster-with-kcp.yaml
diff --git a/test/e2e/data/infrastructure-docker/v1.2/bases/cluster-with-topology.yaml b/test/e2e/data/infrastructure-docker/v1beta1/v1.2/bases/cluster-with-topology.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/v1.2/bases/cluster-with-topology.yaml
rename to test/e2e/data/infrastructure-docker/v1beta1/v1.2/bases/cluster-with-topology.yaml
diff --git a/test/e2e/data/infrastructure-docker/v1.2/bases/crs.yaml b/test/e2e/data/infrastructure-docker/v1beta1/v1.2/bases/crs.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/v1.2/bases/crs.yaml
rename to test/e2e/data/infrastructure-docker/v1beta1/v1.2/bases/crs.yaml
diff --git a/test/e2e/data/infrastructure-docker/v1.2/bases/md.yaml b/test/e2e/data/infrastructure-docker/v1beta1/v1.2/bases/md.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/v1.2/bases/md.yaml
rename to test/e2e/data/infrastructure-docker/v1beta1/v1.2/bases/md.yaml
diff --git a/test/e2e/data/infrastructure-docker/v1.2/cluster-template-topology/kustomization.yaml b/test/e2e/data/infrastructure-docker/v1beta1/v1.2/cluster-template-topology/kustomization.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/v1.2/cluster-template-topology/kustomization.yaml
rename to test/e2e/data/infrastructure-docker/v1beta1/v1.2/cluster-template-topology/kustomization.yaml
diff --git a/test/e2e/data/infrastructure-docker/v1.3/cluster-template/kustomization.yaml b/test/e2e/data/infrastructure-docker/v1beta1/v1.2/cluster-template/kustomization.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/v1.3/cluster-template/kustomization.yaml
rename to test/e2e/data/infrastructure-docker/v1beta1/v1.2/cluster-template/kustomization.yaml
diff --git a/test/e2e/data/infrastructure-docker/v1.2/clusterclass-quick-start.yaml b/test/e2e/data/infrastructure-docker/v1beta1/v1.2/clusterclass-quick-start.yaml
similarity index 99%
rename from test/e2e/data/infrastructure-docker/v1.2/clusterclass-quick-start.yaml
rename to test/e2e/data/infrastructure-docker/v1beta1/v1.2/clusterclass-quick-start.yaml
index ed0d800b7..bdc001d85 100644
--- a/test/e2e/data/infrastructure-docker/v1.2/clusterclass-quick-start.yaml
+++ b/test/e2e/data/infrastructure-docker/v1beta1/v1.2/clusterclass-quick-start.yaml
@@ -290,3 +290,4 @@ spec:
           criSocket: unix:///var/run/containerd/containerd.sock
           kubeletExtraArgs:
             eviction-hard: 'nodefs.available<0%,nodefs.inodesFree<0%,imagefs.available<0%'
+
diff --git a/test/e2e/data/infrastructure-docker/v1.3/bases/cluster-with-kcp.yaml b/test/e2e/data/infrastructure-docker/v1beta1/v1.3/bases/cluster-with-kcp.yaml
similarity index 94%
rename from test/e2e/data/infrastructure-docker/v1.3/bases/cluster-with-kcp.yaml
rename to test/e2e/data/infrastructure-docker/v1beta1/v1.3/bases/cluster-with-kcp.yaml
index b266cddcd..fa8720f98 100644
--- a/test/e2e/data/infrastructure-docker/v1.3/bases/cluster-with-kcp.yaml
+++ b/test/e2e/data/infrastructure-docker/v1beta1/v1.3/bases/cluster-with-kcp.yaml
@@ -59,8 +59,6 @@ spec:
       extraMounts:
         - containerPath: "/var/run/docker.sock"
           hostPath: "/var/run/docker.sock"
-      # The DOCKER_PRELOAD_IMAGES variable gets set in self-hosted E2E tests to the list of images of the E2E configuration.
-      preLoadImages: ${DOCKER_PRELOAD_IMAGES:-[]}
 ---
 # KubeadmControlPlane referenced by the Cluster object with
 # - the label kcp-adoption.step2, because it should be created in the second step of the kcp-adoption test.
diff --git a/test/e2e/data/infrastructure-docker/v1.3/bases/cluster-with-topology.yaml b/test/e2e/data/infrastructure-docker/v1beta1/v1.3/bases/cluster-with-topology.yaml
similarity index 71%
rename from test/e2e/data/infrastructure-docker/v1.3/bases/cluster-with-topology.yaml
rename to test/e2e/data/infrastructure-docker/v1beta1/v1.3/bases/cluster-with-topology.yaml
index 96574eab7..804d3c7c8 100644
--- a/test/e2e/data/infrastructure-docker/v1.3/bases/cluster-with-topology.yaml
+++ b/test/e2e/data/infrastructure-docker/v1beta1/v1.3/bases/cluster-with-topology.yaml
@@ -18,21 +18,14 @@ spec:
     controlPlane:
       metadata: {}
       nodeDeletionTimeout: "30s"
-      nodeVolumeDetachTimeout: "5m"
       replicas: ${CONTROL_PLANE_MACHINE_COUNT}
     workers:
       machineDeployments:
       - class: "default-worker"
         name: "md-0"
         nodeDeletionTimeout: "30s"
-        nodeVolumeDetachTimeout: "5m"
-        minReadySeconds: 5
         replicas: ${WORKER_MACHINE_COUNT}
         failureDomain: fd4
-        strategy:
-          rollingUpdate:
-            maxSurge: "20%"
-            maxUnavailable: 0
     variables:
       # We set an empty value to use the default tag kubeadm init is using.
     - name: etcdImageTag
@@ -40,6 +33,3 @@ spec:
       # We set an empty value to use the default tag kubeadm init is using.
     - name: coreDNSImageTag
       value: ""
-    - name: preLoadImages
-      # The DOCKER_PRELOAD_IMAGES variable gets set in self-hosted E2E tests to the list of images of the E2E configuration.
-      value: ${DOCKER_PRELOAD_IMAGES:-[]}
diff --git a/test/e2e/data/infrastructure-docker/v1.3/bases/crs.yaml b/test/e2e/data/infrastructure-docker/v1beta1/v1.3/bases/crs.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/v1.3/bases/crs.yaml
rename to test/e2e/data/infrastructure-docker/v1beta1/v1.3/bases/crs.yaml
diff --git a/test/e2e/data/infrastructure-docker/v1.3/bases/md.yaml b/test/e2e/data/infrastructure-docker/v1beta1/v1.3/bases/md.yaml
similarity index 89%
rename from test/e2e/data/infrastructure-docker/v1.3/bases/md.yaml
rename to test/e2e/data/infrastructure-docker/v1beta1/v1.3/bases/md.yaml
index eacb23f08..11abbc292 100644
--- a/test/e2e/data/infrastructure-docker/v1.3/bases/md.yaml
+++ b/test/e2e/data/infrastructure-docker/v1beta1/v1.3/bases/md.yaml
@@ -11,8 +11,6 @@ spec:
       extraMounts:
         - containerPath: "/var/run/docker.sock"
           hostPath: "/var/run/docker.sock"
-      # The DOCKER_PRELOAD_IMAGES variable gets set in self-hosted E2E tests to the list of images of the E2E configuration.
-      preLoadImages: ${DOCKER_PRELOAD_IMAGES:-[]}
 ---
 # KubeadmConfigTemplate referenced by the MachineDeployment
 apiVersion: bootstrap.cluster.x-k8s.io/v1beta1
diff --git a/test/e2e/data/infrastructure-docker/v1.3/cluster-template-topology/kustomization.yaml b/test/e2e/data/infrastructure-docker/v1beta1/v1.3/cluster-template-topology/kustomization.yaml
similarity index 100%
rename from test/e2e/data/infrastructure-docker/v1.3/cluster-template-topology/kustomization.yaml
rename to test/e2e/data/infrastructure-docker/v1beta1/v1.3/cluster-template-topology/kustomization.yaml
diff --git a/test/e2e/data/infrastructure-docker/v1.2/cluster-template/kustomization.yaml b/test/e2e/data/infrastructure-docker/v1beta1/v1.3/cluster-template/kustomization.yaml
similarity index 98%
rename from test/e2e/data/infrastructure-docker/v1.2/cluster-template/kustomization.yaml
rename to test/e2e/data/infrastructure-docker/v1beta1/v1.3/cluster-template/kustomization.yaml
index 3c0dd51f9..adb5919ce 100644
--- a/test/e2e/data/infrastructure-docker/v1.2/cluster-template/kustomization.yaml
+++ b/test/e2e/data/infrastructure-docker/v1beta1/v1.3/cluster-template/kustomization.yaml
@@ -2,3 +2,4 @@ bases:
 - ../bases/cluster-with-kcp.yaml
 - ../bases/md.yaml
 - ../bases/crs.yaml
+
diff --git a/test/e2e/data/infrastructure-docker/v1.3/clusterclass-quick-start.yaml b/test/e2e/data/infrastructure-docker/v1beta1/v1.3/clusterclass-quick-start.yaml
similarity index 73%
rename from test/e2e/data/infrastructure-docker/v1.3/clusterclass-quick-start.yaml
rename to test/e2e/data/infrastructure-docker/v1beta1/v1.3/clusterclass-quick-start.yaml
index f11423db1..6a8a505a7 100644
--- a/test/e2e/data/infrastructure-docker/v1.3/clusterclass-quick-start.yaml
+++ b/test/e2e/data/infrastructure-docker/v1beta1/v1.3/clusterclass-quick-start.yaml
@@ -75,21 +75,6 @@ spec:
         default: ""
         example: "0"
         description: "kubeadmControlPlaneMaxSurge is the maximum number of control planes that can be scheduled above or under the desired number of control plane machines."
-  - name: preLoadImages
-    required: false
-    schema:
-      openAPIV3Schema:
-        default: []
-        type: array
-        items:
-          type: string
-        description: "preLoadImages sets the images for the docker machines to preload."
-  - name: controlPlaneTaint
-    required: false
-    schema:
-      openAPIV3Schema:
-        type: boolean
-        default: true
   patches:
   - name: lbImageRepository
     definitions:
@@ -198,25 +183,6 @@ spec:
         valueFrom:
           template: |
             kindest/node:{{ .builtin.controlPlane.version | replace "+" "_" }}
-  - name: preloadImages
-    description: |
-      Sets the container images to preload to the node that is used for running dockerMachines.
-      This is especially required for self-hosted e2e tests to ensure the required controller images to be available
-      and reduce load to public registries.
-    definitions:
-    - selector:
-        apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
-        kind: DockerMachineTemplate
-        matchResources:
-          controlPlane: true
-          machineDeploymentClass:
-            names:
-            - default-worker
-      jsonPatches:
-      - op: add
-        path: "/spec/template/spec/preLoadImages"
-        valueFrom:
-          variable: preLoadImages
   - name: kubeadmControlPlaneMaxSurge
     description: "Sets the maxSurge value used for rolloutStrategy in the KubeadmControlPlane."
     enabledIf: '{{ ne .kubeadmControlPlaneMaxSurge "" }}'
@@ -231,67 +197,6 @@ spec:
         path: /spec/template/spec/rolloutStrategy/rollingUpdate/maxSurge
         valueFrom:
           template: "{{ .kubeadmControlPlaneMaxSurge }}"
-  - name: controlPlaneTaint
-    enabledIf: "{{ not .controlPlaneTaint }}"
-    definitions:
-    - selector:
-        apiVersion: controlplane.cluster.x-k8s.io/v1beta1
-        kind: KubeadmControlPlaneTemplate
-        matchResources:
-          controlPlane: true
-      jsonPatches:
-      - op: add
-        path: "/spec/template/spec/kubeadmConfigSpec/initConfiguration/nodeRegistration/taints"
-        value: []
-      - op: add
-        path: "/spec/template/spec/kubeadmConfigSpec/joinConfiguration/nodeRegistration/taints"
-        value: []
-  - name: podSecurityStandard
-    description: "Adds an admission configuration for PodSecurity to the kube-apiserver."
-    definitions:
-    - selector:
-        apiVersion: controlplane.cluster.x-k8s.io/v1beta1
-        kind: KubeadmControlPlaneTemplate
-        matchResources:
-          controlPlane: true
-      jsonPatches:
-      - op: add
-        path: "/spec/template/spec/kubeadmConfigSpec/clusterConfiguration/apiServer/extraArgs"
-        value:
-          admission-control-config-file: "/etc/kubernetes/kube-apiserver-admission-pss.yaml"
-      - op: add
-        path: "/spec/template/spec/kubeadmConfigSpec/clusterConfiguration/apiServer/extraVolumes"
-        value:
-        - name: admission-pss
-          hostPath: /etc/kubernetes/kube-apiserver-admission-pss.yaml
-          mountPath: /etc/kubernetes/kube-apiserver-admission-pss.yaml
-          readOnly: true
-          pathType: "File"
-      - op: add
-        path: "/spec/template/spec/kubeadmConfigSpec/files"
-        valueFrom:
-          template: |
-            - content: |
-                apiVersion: apiserver.config.k8s.io/v1
-                kind: AdmissionConfiguration
-                plugins:
-                - name: PodSecurity
-                  configuration:
-                    apiVersion: pod-security.admission.config.k8s.io/v1{{ if semverCompare "< v1.25" .builtin.controlPlane.version }}beta1{{ end }}
-                    kind: PodSecurityConfiguration
-                    defaults:
-                      enforce: "baseline"
-                      enforce-version: "latest"
-                      audit: "baseline"
-                      audit-version: "latest"
-                      warn: "baseline"
-                      warn-version: "latest"
-                    exemptions:
-                      usernames: []
-                      runtimeClasses: []
-                      namespaces: [kube-system]
-              path: /etc/kubernetes/kube-apiserver-admission-pss.yaml
-    enabledIf: '{{ semverCompare ">= v1.24" .builtin.controlPlane.version }}'
 ---
 apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
 kind: DockerClusterTemplate
diff --git a/test/e2e/data/shared/v1.2/metadata.yaml b/test/e2e/data/shared/v1.2/metadata.yaml
deleted file mode 100644
index c45f8cf0c..000000000
--- a/test/e2e/data/shared/v1.2/metadata.yaml
+++ /dev/null
@@ -1,21 +0,0 @@
-apiVersion: clusterctl.cluster.x-k8s.io/v1alpha3
-kind: Metadata
-releaseSeries:
-- major: 1
-  minor: 2
-  contract: v1beta1
-- major: 1
-  minor: 1
-  contract: v1beta1
-- major: 1
-  minor: 0
-  contract: v1beta1
-- major: 0
-  minor: 4
-  contract: v1alpha4
-- major: 0
-  minor: 3
-  contract: v1alpha3
-- major: 0
-  minor: 2
-  contract: v1alpha2
diff --git a/test/e2e/data/shared/v0.3/metadata.yaml b/test/e2e/data/shared/v1alpha3/metadata.yaml
similarity index 100%
rename from test/e2e/data/shared/v0.3/metadata.yaml
rename to test/e2e/data/shared/v1alpha3/metadata.yaml
diff --git a/test/e2e/data/shared/v0.4/metadata.yaml b/test/e2e/data/shared/v1alpha4/metadata.yaml
similarity index 100%
rename from test/e2e/data/shared/v0.4/metadata.yaml
rename to test/e2e/data/shared/v1alpha4/metadata.yaml
diff --git a/test/e2e/data/shared/main/metadata.yaml b/test/e2e/data/shared/v1beta1/main/metadata.yaml
similarity index 100%
rename from test/e2e/data/shared/main/metadata.yaml
rename to test/e2e/data/shared/v1beta1/main/metadata.yaml
diff --git a/test/e2e/data/shared/v1beta1/v1.2/metadata.yaml b/test/e2e/data/shared/v1beta1/v1.2/metadata.yaml
new file mode 100644
index 000000000..10afb3250
--- /dev/null
+++ b/test/e2e/data/shared/v1beta1/v1.2/metadata.yaml
@@ -0,0 +1,21 @@
+apiVersion: clusterctl.cluster.x-k8s.io/v1alpha3
+kind: Metadata
+releaseSeries:
+  - major: 1
+    minor: 2
+    contract: v1beta1
+  - major: 1
+    minor: 1
+    contract: v1beta1
+  - major: 1
+    minor: 0
+    contract: v1beta1
+  - major: 0
+    minor: 4
+    contract: v1alpha4
+  - major: 0
+    minor: 3
+    contract: v1alpha3
+  - major: 0
+    minor: 2
+    contract: v1alpha2
diff --git a/test/e2e/data/shared/v1.3/metadata.yaml b/test/e2e/data/shared/v1beta1/v1.3/metadata.yaml
similarity index 100%
rename from test/e2e/data/shared/v1.3/metadata.yaml
rename to test/e2e/data/shared/v1beta1/v1.3/metadata.yaml
diff --git a/test/extension/config/default/manager.yaml b/test/extension/config/default/manager.yaml
index 7d7afd36e..26acbe94e 100644
--- a/test/extension/config/default/manager.yaml
+++ b/test/extension/config/default/manager.yaml
@@ -14,18 +14,10 @@ spec:
         app: test-extension-manager
     spec:
       containers:
-        - command:
-            - /manager
-          image: controller:latest
-          name: manager
-          securityContext:
-            allowPrivilegeEscalation: false
-            capabilities:
-              drop:
-                - ALL
-            privileged: false
-            runAsUser: 65532
-            runAsGroup: 65532
+      - command:
+        - /manager
+        image: controller:latest
+        name: manager
       terminationGracePeriodSeconds: 10
       serviceAccountName: manager
       tolerations:
@@ -33,7 +25,3 @@ spec:
           key: node-role.kubernetes.io/master
         - effect: NoSchedule
           key: node-role.kubernetes.io/control-plane
-      securityContext:
-        runAsNonRoot: true
-        seccompProfile:
-          type: RuntimeDefault
diff --git a/test/framework/clusterctl/client.go b/test/framework/clusterctl/client.go
index 1fc7c3128..5c0e20b5f 100644
--- a/test/framework/clusterctl/client.go
+++ b/test/framework/clusterctl/client.go
@@ -61,8 +61,16 @@ type InitInput struct {
 
 // Init calls clusterctl init with the list of providers defined in the local repository.
 func Init(_ context.Context, input InitInput) {
-	args := calculateClusterCtlInitArgs(input)
-	log.Logf("clusterctl %s", strings.Join(args, " "))
+	log.Logf("clusterctl init --core %s --bootstrap %s --control-plane %s --infrastructure %s --ipam %s --runtime-extension %s --config %s --kubeconfig %s",
+		input.CoreProvider,
+		strings.Join(input.BootstrapProviders, ","),
+		strings.Join(input.ControlPlaneProviders, ","),
+		strings.Join(input.InfrastructureProviders, ","),
+		strings.Join(input.IPAMProviders, ","),
+		strings.Join(input.RuntimeExtensionProviders, ","),
+		input.ClusterctlConfigPath,
+		input.KubeconfigPath,
+	)
 
 	initOpt := clusterctlclient.InitOptions{
 		Kubeconfig: clusterctlclient.Kubeconfig{
@@ -88,23 +96,17 @@ func Init(_ context.Context, input InitInput) {
 
 // InitWithBinary uses clusterctl binary to run init with the list of providers defined in the local repository.
 func InitWithBinary(_ context.Context, binary string, input InitInput) {
-	args := calculateClusterCtlInitArgs(input)
-	log.Logf("clusterctl %s", strings.Join(args, " "))
-
-	cmd := exec.Command(binary, args...) //nolint:gosec // We don't care about command injection here.
-
-	out, err := cmd.CombinedOutput()
-	_ = os.WriteFile(filepath.Join(input.LogFolder, "clusterctl-init.log"), out, 0644) //nolint:gosec // this is a log file to be shared via prow artifacts
-	var stdErr string
-	if err != nil {
-		if exitErr, ok := err.(*exec.ExitError); ok {
-			stdErr = string(exitErr.Stderr)
-		}
-	}
-	Expect(err).ToNot(HaveOccurred(), "failed to run clusterctl init:\nstdout:\n%s\nstderr:\n%s", string(out), stdErr)
-}
+	log.Logf("clusterctl init --core %s --bootstrap %s --control-plane %s --infrastructure %s --ipam %s --runtime-extension %s --config %s --kubeconfig %s",
+		input.CoreProvider,
+		strings.Join(input.BootstrapProviders, ","),
+		strings.Join(input.ControlPlaneProviders, ","),
+		strings.Join(input.InfrastructureProviders, ","),
+		strings.Join(input.IPAMProviders, ","),
+		strings.Join(input.RuntimeExtensionProviders, ","),
+		input.ClusterctlConfigPath,
+		input.KubeconfigPath,
+	)
 
-func calculateClusterCtlInitArgs(input InitInput) []string {
 	args := []string{"init", "--config", input.ClusterctlConfigPath, "--kubeconfig", input.KubeconfigPath}
 	if input.CoreProvider != "" {
 		args = append(args, "--core", input.CoreProvider)
@@ -112,9 +114,6 @@ func calculateClusterCtlInitArgs(input InitInput) []string {
 	if len(input.BootstrapProviders) > 0 {
 		args = append(args, "--bootstrap", strings.Join(input.BootstrapProviders, ","))
 	}
-	if len(input.ControlPlaneProviders) > 0 {
-		args = append(args, "--control-plane", strings.Join(input.ControlPlaneProviders, ","))
-	}
 	if len(input.InfrastructureProviders) > 0 {
 		args = append(args, "--infrastructure", strings.Join(input.InfrastructureProviders, ","))
 	}
@@ -124,7 +123,18 @@ func calculateClusterCtlInitArgs(input InitInput) []string {
 	if len(input.RuntimeExtensionProviders) > 0 {
 		args = append(args, "--runtime-extension", strings.Join(input.RuntimeExtensionProviders, ","))
 	}
-	return args
+
+	cmd := exec.Command(binary, args...) //nolint:gosec // We don't care about command injection here.
+
+	out, err := cmd.CombinedOutput()
+	_ = os.WriteFile(filepath.Join(input.LogFolder, "clusterctl-init.log"), out, 0644) //nolint:gosec // this is a log file to be shared via prow artifacts
+	var stdErr string
+	if err != nil {
+		if exitErr, ok := err.(*exec.ExitError); ok {
+			stdErr = string(exitErr.Stderr)
+		}
+	}
+	Expect(err).ToNot(HaveOccurred(), "failed to run clusterctl init:\nstdout:\n%s\nstderr:\n%s", string(out), stdErr)
 }
 
 // UpgradeInput is the input for Upgrade.
diff --git a/test/framework/clusterctl/clusterctl_helpers.go b/test/framework/clusterctl/clusterctl_helpers.go
index a5c25b377..25f142533 100644
--- a/test/framework/clusterctl/clusterctl_helpers.go
+++ b/test/framework/clusterctl/clusterctl_helpers.go
@@ -107,7 +107,7 @@ func InitManagementClusterAndWatchControllerLogs(ctx context.Context, input Init
 		}, intervals...)
 
 		// Start streaming logs from all controller providers
-		framework.WatchDeploymentLogsByName(ctx, framework.WatchDeploymentLogsByNameInput{
+		framework.WatchDeploymentLogs(ctx, framework.WatchDeploymentLogsInput{
 			GetLister:  client,
 			ClientSet:  input.ClusterProxy.GetClientSet(),
 			Deployment: deployment,
@@ -188,7 +188,7 @@ func UpgradeManagementClusterAndWait(ctx context.Context, input UpgradeManagemen
 		}, intervals...)
 
 		// Start streaming logs from all controller providers
-		framework.WatchDeploymentLogsByName(ctx, framework.WatchDeploymentLogsByNameInput{
+		framework.WatchDeploymentLogs(ctx, framework.WatchDeploymentLogsInput{
 			GetLister:  client,
 			ClientSet:  input.ClusterProxy.GetClientSet(),
 			Deployment: deployment,
diff --git a/test/framework/daemonset_helpers.go b/test/framework/daemonset_helpers.go
index 40510638e..2a4dca7e0 100644
--- a/test/framework/daemonset_helpers.go
+++ b/test/framework/daemonset_helpers.go
@@ -26,7 +26,6 @@ import (
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 
-	"sigs.k8s.io/cluster-api/internal/util/kubeadm"
 	containerutil "sigs.k8s.io/cluster-api/util/container"
 )
 
@@ -43,9 +42,15 @@ func WaitForKubeProxyUpgrade(ctx context.Context, input WaitForKubeProxyUpgradeI
 	parsedVersion, err := semver.ParseTolerant(input.KubernetesVersion)
 	Expect(err).ToNot(HaveOccurred())
 
-	// Validate the kube-proxy image according to how KCP sets the image in the kube-proxy DaemonSet.
-	// KCP does this based on the default registry of the Kubernetes/kubeadm version.
-	wantKubeProxyImage := kubeadm.GetDefaultRegistry(parsedVersion) + "/kube-proxy:" + containerutil.SemverToOCIImageTag(input.KubernetesVersion)
+	// Beginning with kubernetes v1.22, kubernetes images including kube-proxy get published to registry.k8s.io.
+	// This ensures that the imageRepository setting gets patched to registry.k8s.io when upgrading from v1.21 or lower,
+	// but only if there was no imageRepository explicitly set at the KubeadmControlPlanes ClusterConfiguration.
+	// This follows the behavior of `kubeadm upgrade`.
+	wantKubeProxyRegistry := "registry.k8s.io"
+	if parsedVersion.LT(semver.Version{Major: 1, Minor: 22, Patch: 0, Pre: []semver.PRVersion{{VersionStr: "alpha"}}}) {
+		wantKubeProxyRegistry = "k8s.gcr.io"
+	}
+	wantKubeProxyImage := wantKubeProxyRegistry + "/kube-proxy:" + containerutil.SemverToOCIImageTag(input.KubernetesVersion)
 
 	Eventually(func() (bool, error) {
 		ds := &appsv1.DaemonSet{}
diff --git a/test/framework/deployment_helpers.go b/test/framework/deployment_helpers.go
index eeb14dc27..0b5d89f6f 100644
--- a/test/framework/deployment_helpers.go
+++ b/test/framework/deployment_helpers.go
@@ -95,56 +95,34 @@ func DescribeFailedDeployment(input WaitForDeploymentsAvailableInput, deployment
 	return b.String()
 }
 
-// WatchDeploymentLogsByLabelSelectorInput is the input for WatchDeploymentLogsByLabelSelector.
-type WatchDeploymentLogsByLabelSelectorInput struct {
-	GetLister GetLister
-	ClientSet *kubernetes.Clientset
-	Labels    map[string]string
-	LogPath   string
-}
-
-// WatchDeploymentLogsByLabelSelector streams logs for all containers for all pods belonging to a deployment on the basis of label. Each container's logs are streamed
-// in a separate goroutine so they can all be streamed concurrently. This only causes a test failure if there are errors
-// retrieving the deployment, its pods, or setting up a log file. If there is an error with the log streaming itself,
-// that does not cause the test to fail.
-func WatchDeploymentLogsByLabelSelector(ctx context.Context, input WatchDeploymentLogsByLabelSelectorInput) {
-	Expect(ctx).NotTo(BeNil(), "ctx is required for WatchDeploymentLogsByLabelSelector")
-	Expect(input.ClientSet).NotTo(BeNil(), "input.ClientSet is required for WatchDeploymentLogsByLabelSelector")
-	Expect(input.Labels).NotTo(BeNil(), "input.Selector is required for WatchDeploymentLogsByLabelSelector")
-
-	deploymentList := &appsv1.DeploymentList{}
-	Eventually(func() error {
-		return input.GetLister.List(ctx, deploymentList, client.MatchingLabels(input.Labels))
-	}, retryableOperationTimeout, retryableOperationInterval).Should(Succeed(), "Failed to get deployment for labels")
-
-	for _, deployment := range deploymentList.Items {
-		watchPodLogs(ctx, watchPodLogsInput{
-			GetLister:      input.GetLister,
-			ClientSet:      input.ClientSet,
-			Namespace:      deployment.Namespace,
-			DeploymentName: deployment.Name,
-			Labels:         deployment.Spec.Selector.MatchLabels,
-			LogPath:        input.LogPath,
-		})
-	}
-}
-
-// WatchDeploymentLogsByNameInput is the input for WatchDeploymentLogsByName.
-type WatchDeploymentLogsByNameInput struct {
+// WatchDeploymentLogsInput is the input for WatchDeploymentLogs.
+type WatchDeploymentLogsInput struct {
 	GetLister  GetLister
 	ClientSet  *kubernetes.Clientset
 	Deployment *appsv1.Deployment
 	LogPath    string
 }
 
-// WatchDeploymentLogsByName streams logs for all containers for all pods belonging to a deployment. Each container's logs are streamed
+// logMetadata contains metadata about the logs.
+// The format is very similar to the one used by promtail.
+type logMetadata struct {
+	Job       string `json:"job"`
+	Namespace string `json:"namespace"`
+	App       string `json:"app"`
+	Pod       string `json:"pod"`
+	Container string `json:"container"`
+	NodeName  string `json:"node_name"`
+	Stream    string `json:"stream"`
+}
+
+// WatchDeploymentLogs streams logs for all containers for all pods belonging to a deployment. Each container's logs are streamed
 // in a separate goroutine so they can all be streamed concurrently. This only causes a test failure if there are errors
 // retrieving the deployment, its pods, or setting up a log file. If there is an error with the log streaming itself,
 // that does not cause the test to fail.
-func WatchDeploymentLogsByName(ctx context.Context, input WatchDeploymentLogsByNameInput) {
-	Expect(ctx).NotTo(BeNil(), "ctx is required for WatchDeploymentLogsByName")
-	Expect(input.ClientSet).NotTo(BeNil(), "input.ClientSet is required for WatchDeploymentLogsByName")
-	Expect(input.Deployment).NotTo(BeNil(), "input.Deployment is required for WatchDeploymentLogsByName")
+func WatchDeploymentLogs(ctx context.Context, input WatchDeploymentLogsInput) {
+	Expect(ctx).NotTo(BeNil(), "ctx is required for WatchControllerLogs")
+	Expect(input.ClientSet).NotTo(BeNil(), "input.ClientSet is required for WatchControllerLogs")
+	Expect(input.Deployment).NotTo(BeNil(), "input.Deployment is required for WatchControllerLogs")
 
 	deployment := &appsv1.Deployment{}
 	key := client.ObjectKeyFromObject(input.Deployment)
@@ -153,47 +131,23 @@ func WatchDeploymentLogsByName(ctx context.Context, input WatchDeploymentLogsByN
 	}, retryableOperationTimeout, retryableOperationInterval).Should(Succeed(), "Failed to get deployment %s", klog.KObj(input.Deployment))
 
 	selector, err := metav1.LabelSelectorAsMap(deployment.Spec.Selector)
-	Expect(err).NotTo(HaveOccurred(), "Failed to create Pods selector for deployment %s", klog.KObj(input.Deployment))
-	watchPodLogs(ctx, watchPodLogsInput{
-		GetLister:      input.GetLister,
-		ClientSet:      input.ClientSet,
-		Namespace:      deployment.Namespace,
-		DeploymentName: deployment.Name,
-		Labels:         selector,
-		LogPath:        input.LogPath,
-	})
-}
+	Expect(err).NotTo(HaveOccurred(), "Failed to Pods selector for deployment %s", klog.KObj(input.Deployment))
 
-// watchPodLogsInput is the input for watchPodLogs.
-type watchPodLogsInput struct {
-	GetLister      GetLister
-	ClientSet      *kubernetes.Clientset
-	Namespace      string
-	DeploymentName string
-	Labels         map[string]string
-	LogPath        string
-}
-
-// watchPodLogs streams logs for all containers for all pods belonging to a deployment with the given label. Each container's logs are streamed
-// in a separate goroutine so they can all be streamed concurrently. This only causes a test failure if there are errors
-// retrieving the deployment, its pods, or setting up a log file. If there is an error with the log streaming itself,
-// that does not cause the test to fail.
-func watchPodLogs(ctx context.Context, input watchPodLogsInput) {
 	pods := &corev1.PodList{}
-	Expect(input.GetLister.List(ctx, pods, client.InNamespace(input.Namespace), client.MatchingLabels(input.Labels))).To(Succeed(), "Failed to list Pods for deployment %s", klog.KRef(input.Namespace, input.DeploymentName))
+	Expect(input.GetLister.List(ctx, pods, client.InNamespace(input.Deployment.Namespace), client.MatchingLabels(selector))).To(Succeed(), "Failed to list Pods for deployment %s", klog.KObj(input.Deployment))
 
 	for _, pod := range pods.Items {
-		for _, container := range pod.Spec.Containers {
-			log.Logf("Creating log watcher for controller %s, pod %s, container %s", klog.KRef(input.Namespace, input.DeploymentName), pod.Name, container.Name)
+		for _, container := range deployment.Spec.Template.Spec.Containers {
+			log.Logf("Creating log watcher for controller %s, pod %s, container %s", klog.KObj(input.Deployment), pod.Name, container.Name)
 
 			// Create log metadata file.
-			logMetadataFile := filepath.Clean(path.Join(input.LogPath, input.DeploymentName, pod.Name, container.Name+"-log-metadata.json"))
+			logMetadataFile := filepath.Clean(path.Join(input.LogPath, input.Deployment.Name, pod.Name, container.Name+"-log-metadata.json"))
 			Expect(os.MkdirAll(filepath.Dir(logMetadataFile), 0750)).To(Succeed())
 
 			metadata := logMetadata{
-				Job:       input.Namespace + "/" + input.DeploymentName,
-				Namespace: input.Namespace,
-				App:       input.DeploymentName,
+				Job:       input.Deployment.Namespace + "/" + input.Deployment.Name,
+				Namespace: input.Deployment.Namespace,
+				App:       input.Deployment.Name,
 				Pod:       pod.Name,
 				Container: container.Name,
 				NodeName:  pod.Spec.NodeName,
@@ -207,7 +161,7 @@ func watchPodLogs(ctx context.Context, input watchPodLogsInput) {
 			go func(pod corev1.Pod, container corev1.Container) {
 				defer GinkgoRecover()
 
-				logFile := filepath.Clean(path.Join(input.LogPath, input.DeploymentName, pod.Name, container.Name+".log"))
+				logFile := filepath.Clean(path.Join(input.LogPath, input.Deployment.Name, pod.Name, container.Name+".log"))
 				Expect(os.MkdirAll(filepath.Dir(logFile), 0750)).To(Succeed())
 
 				f, err := os.OpenFile(logFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600)
@@ -219,7 +173,7 @@ func watchPodLogs(ctx context.Context, input watchPodLogsInput) {
 					Follow:    true,
 				}
 
-				podLogs, err := input.ClientSet.CoreV1().Pods(input.Namespace).GetLogs(pod.Name, opts).Stream(ctx)
+				podLogs, err := input.ClientSet.CoreV1().Pods(input.Deployment.Namespace).GetLogs(pod.Name, opts).Stream(ctx)
 				if err != nil {
 					// Failing to stream logs should not cause the test to fail
 					log.Logf("Error starting logs stream for pod %s, container %s: %v", klog.KRef(pod.Namespace, pod.Name), container.Name, err)
@@ -239,19 +193,6 @@ func watchPodLogs(ctx context.Context, input watchPodLogsInput) {
 	}
 }
 
-// logMetadata contains metadata about the logs.
-// The format is very similar to the one used by promtail.
-type logMetadata struct {
-	Job       string            `json:"job"`
-	Namespace string            `json:"namespace"`
-	App       string            `json:"app"`
-	Pod       string            `json:"pod"`
-	Container string            `json:"container"`
-	NodeName  string            `json:"node_name"`
-	Stream    string            `json:"stream"`
-	Labels    map[string]string `json:"labels,omitempty"`
-}
-
 type WatchPodMetricsInput struct {
 	GetLister   GetLister
 	ClientSet   *kubernetes.Clientset
@@ -274,7 +215,7 @@ func WatchPodMetrics(ctx context.Context, input WatchPodMetricsInput) {
 	}, retryableOperationTimeout, retryableOperationInterval).Should(Succeed(), "Failed to get deployment %s", klog.KObj(input.Deployment))
 
 	selector, err := metav1.LabelSelectorAsMap(deployment.Spec.Selector)
-	Expect(err).NotTo(HaveOccurred(), "Failed to create Pods selector for deployment %s", klog.KObj(input.Deployment))
+	Expect(err).NotTo(HaveOccurred(), "Failed to Pods selector for deployment %s", klog.KObj(input.Deployment))
 
 	pods := &corev1.PodList{}
 	Eventually(func() error {
diff --git a/test/go.mod b/test/go.mod
index 6c4c8833a..23d00964a 100644
--- a/test/go.mod
+++ b/test/go.mod
@@ -10,7 +10,7 @@ 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.7.0
+	github.com/onsi/ginkgo/v2 v2.6.1
 	github.com/onsi/gomega v1.24.2
 	github.com/pkg/errors v0.9.1
 	github.com/spf13/pflag v1.0.5
@@ -59,7 +59,7 @@ require (
 	github.com/go-openapi/jsonpointer v0.19.5 // indirect
 	github.com/go-openapi/jsonreference v0.20.0 // indirect
 	github.com/go-openapi/swag v0.22.3 // indirect
-	github.com/gobuffalo/flect v1.0.0 // indirect
+	github.com/gobuffalo/flect v0.3.0 // indirect
 	github.com/gogo/protobuf v1.3.2 // indirect
 	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
 	github.com/golang/protobuf v1.5.2 // indirect
@@ -114,7 +114,7 @@ require (
 	golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 // indirect
 	golang.org/x/sys v0.3.0 // indirect
 	golang.org/x/term v0.3.0 // indirect
-	golang.org/x/text v0.6.0 // indirect
+	golang.org/x/text v0.5.0 // indirect
 	golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 // indirect
 	gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect
 	google.golang.org/appengine v1.6.7 // indirect
diff --git a/test/go.sum b/test/go.sum
index 4a84d8fdf..42542f7a3 100644
--- a/test/go.sum
+++ b/test/go.sum
@@ -185,8 +185,8 @@ github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh
 github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
 github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
-github.com/gobuffalo/flect v1.0.0 h1:eBFmskjXZgAOagiTXJH25Nt5sdFwNRcb8DKZsIsAUQI=
-github.com/gobuffalo/flect v1.0.0/go.mod h1:l9V6xSb4BlXwsxEMj3FVEub2nkdQjWhPvD8XTTlHPQc=
+github.com/gobuffalo/flect v0.3.0 h1:erfPWM+K1rFNIQeRPdeEXxo8yFr/PO17lhRnS8FUrtk=
+github.com/gobuffalo/flect v0.3.0/go.mod h1:5pf3aGnsvqvCj50AVni7mJJF8ICxGZ8HomberC3pXLE=
 github.com/godbus/dbus v0.0.0-20181025153459-66d97aec3384/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
 github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
@@ -388,8 +388,8 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA
 github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
 github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
 github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
-github.com/onsi/ginkgo/v2 v2.7.0 h1:/XxtEV3I3Eif/HobnVx9YmJgk8ENdRsuUmM+fLCFNow=
-github.com/onsi/ginkgo/v2 v2.7.0/go.mod h1:yjiuMwPokqY1XauOgju45q3sJt6VzQ/Fict1LFVcsAo=
+github.com/onsi/ginkgo/v2 v2.6.1 h1:1xQPCjcqYw/J5LchOcp4/2q/jzJFjiAOc25chhnDw+Q=
+github.com/onsi/ginkgo/v2 v2.6.1/go.mod h1:yjiuMwPokqY1XauOgju45q3sJt6VzQ/Fict1LFVcsAo=
 github.com/onsi/gomega v1.24.2 h1:J/tulyYK6JwBldPViHJReihxxZ+22FHs0piGjQAvoUE=
 github.com/onsi/gomega v1.24.2/go.mod h1:gs3J10IS7Z7r7eXRoNJIrNqU4ToQukCJhFtKrWgHWnk=
 github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
@@ -496,7 +496,6 @@ github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
-github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
@@ -505,7 +504,6 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
-github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
 github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
 github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs=
 github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
@@ -742,8 +740,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
-golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
+golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
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 a2ed0cb08..668271447 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.1
+    controller-gen.kubebuilder.io/version: v0.10.0
   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 496b50da6..d1356009f 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.1
+    controller-gen.kubebuilder.io/version: v0.10.0
   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 c3ba6423a..bcc150805 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.1
+    controller-gen.kubebuilder.io/version: v0.10.0
   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 5951bdc6f..80700534d 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.1
+    controller-gen.kubebuilder.io/version: v0.10.0
   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 80ec348e6..b8ad20785 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.1
+    controller-gen.kubebuilder.io/version: v0.10.0
   creationTimestamp: null
   name: dockermachinetemplates.infrastructure.cluster.x-k8s.io
 spec:
diff --git a/test/infrastructure/docker/internal/docker/machine.go b/test/infrastructure/docker/internal/docker/machine.go
index 054483264..132abbb86 100644
--- a/test/infrastructure/docker/internal/docker/machine.go
+++ b/test/infrastructure/docker/internal/docker/machine.go
@@ -254,7 +254,7 @@ func (m *Machine) Create(ctx context.Context, image string, role string, version
 		if err != nil {
 			log.Info("Failed running command", "command", "crictl ps")
 			logContainerDebugInfo(ctx, log, m.ContainerName())
-			return errors.Wrap(err, "failed to run crictl ps")
+			return errors.Wrap(errors.WithStack(err), "failed to run crictl ps")
 		}
 		return nil
 	}
@@ -381,7 +381,7 @@ func (m *Machine) CheckForBootstrapSuccess(ctx context.Context, logResult bool)
 		if logResult {
 			log.Info("Failed running command", "command", "test -f /run/cluster-api/bootstrap-success.complete", "stdout", outStd.String(), "stderr", outErr.String())
 		}
-		return errors.Wrap(err, "failed to run bootstrap check")
+		return errors.Wrap(errors.WithStack(err), "failed to run bootstrap check")
 	}
 	return nil
 }
diff --git a/test/infrastructure/docker/internal/provisioning/cloudinit/runcmd.go b/test/infrastructure/docker/internal/provisioning/cloudinit/runcmd.go
index 9b0127085..74e211666 100644
--- a/test/infrastructure/docker/internal/provisioning/cloudinit/runcmd.go
+++ b/test/infrastructure/docker/internal/provisioning/cloudinit/runcmd.go
@@ -17,6 +17,7 @@ limitations under the License.
 package cloudinit
 
 import (
+	"fmt"
 	"strings"
 
 	"github.com/pkg/errors"
@@ -53,21 +54,27 @@ func (a *runCmd) Commands() ([]provisioning.Cmd, error) {
 	return cmds, nil
 }
 
+// ignorePreflightErrors are preflight errors that fail in CAPD and thus we have to ignore them.
+const ignorePreflightErrors = "SystemVerification,Swap,FileContent--proc-sys-net-bridge-bridge-nf-call-iptables"
+
 func hackKubeadmIgnoreErrors(c provisioning.Cmd) provisioning.Cmd {
 	// case kubeadm commands are defined as a string
 	if c.Cmd == "/bin/sh" && len(c.Args) >= 2 {
 		if c.Args[0] == "-c" {
-			c.Args[1] = strings.Replace(c.Args[1], "kubeadm init", "kubeadm init --ignore-preflight-errors=all", 1)
-			c.Args[1] = strings.Replace(c.Args[1], "kubeadm join", "kubeadm join --ignore-preflight-errors=all", 1)
+			c.Args[1] = strings.Replace(c.Args[1], "kubeadm init", fmt.Sprintf("kubeadm init --ignore-preflight-errors=%s", ignorePreflightErrors), 1)
+			c.Args[1] = strings.Replace(c.Args[1], "kubeadm join", fmt.Sprintf("kubeadm join --ignore-preflight-errors=%s", ignorePreflightErrors), 1)
 		}
 	}
 
 	// case kubeadm commands are defined as a list
 	if c.Cmd == "kubeadm" && len(c.Args) >= 1 {
 		if c.Args[0] == "init" || c.Args[0] == "join" {
-			c.Args = append(c.Args, "")                 // make space
-			copy(c.Args[2:], c.Args[1:])                // shift elements
-			c.Args[1] = "--ignore-preflight-errors=all" // insert the additional arg
+			// make space
+			c.Args = append(c.Args, "")
+			// shift elements
+			copy(c.Args[2:], c.Args[1:])
+			// insert the additional arg
+			c.Args[1] = fmt.Sprintf("--ignore-preflight-errors=%s", ignorePreflightErrors)
 		}
 	}
 
diff --git a/test/infrastructure/docker/internal/provisioning/cloudinit/runcmd_test.go b/test/infrastructure/docker/internal/provisioning/cloudinit/runcmd_test.go
index 1afb666a0..8e9ebcb0c 100644
--- a/test/infrastructure/docker/internal/provisioning/cloudinit/runcmd_test.go
+++ b/test/infrastructure/docker/internal/provisioning/cloudinit/runcmd_test.go
@@ -70,7 +70,7 @@ func TestRunCmdRun(t *testing.T) {
 				},
 			},
 			expectedCmds: []provisioning.Cmd{
-				{Cmd: "/bin/sh", Args: []string{"-c", "kubeadm init --ignore-preflight-errors=all --config /run/kubeadm/kubeadm.yaml"}},
+				{Cmd: "/bin/sh", Args: []string{"-c", "kubeadm init --ignore-preflight-errors=SystemVerification,Swap,FileContent--proc-sys-net-bridge-bridge-nf-call-iptables --config /run/kubeadm/kubeadm.yaml"}},
 			},
 		},
 	}
@@ -100,11 +100,11 @@ runcmd:
 
 	r.Cmds[0] = hackKubeadmIgnoreErrors(r.Cmds[0])
 
-	expected0 := provisioning.Cmd{Cmd: "/bin/sh", Args: []string{"-c", "kubeadm init --ignore-preflight-errors=all --config=/run/kubeadm/kubeadm.yaml"}}
+	expected0 := provisioning.Cmd{Cmd: "/bin/sh", Args: []string{"-c", "kubeadm init --ignore-preflight-errors=SystemVerification,Swap,FileContent--proc-sys-net-bridge-bridge-nf-call-iptables --config=/run/kubeadm/kubeadm.yaml"}}
 	g.Expect(r.Cmds[0]).To(Equal(expected0))
 
 	r.Cmds[1] = hackKubeadmIgnoreErrors(r.Cmds[1])
 
-	expected1 := provisioning.Cmd{Cmd: "kubeadm", Args: []string{"join", "--ignore-preflight-errors=all", "--config=/run/kubeadm/kubeadm-controlplane-join-config.yaml"}}
+	expected1 := provisioning.Cmd{Cmd: "kubeadm", Args: []string{"join", "--ignore-preflight-errors=SystemVerification,Swap,FileContent--proc-sys-net-bridge-bridge-nf-call-iptables", "--config=/run/kubeadm/kubeadm-controlplane-join-config.yaml"}}
 	g.Expect(r.Cmds[1]).To(Equal(expected1))
 }