Repository: kubernetes-sigs/cluster-api
Pull request #9725 (2023-11-15 18:19:12)

🌱 Remove requeues in DockerMachinePool
<!-- Thanks for sending a pull request! Here are some tips for you:
    1. If this is your first time, please read our contributor guidelines: https://github.com/kubernetes-sigs/cluster-api/blob/main/CONTRIBUTING.md#contributing-a-patch and developer guide https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/book/src/developer/guide.md

    2. Please add an icon to the title of this PR (see https://sigs.k8s.io/cluster-api/CONTRIBUTING.md#contributing-a-patch), and delete this line and similar ones
    the icon will be either ⚠️ (:warning:, major or breaking changes), ✨ (:sparkles:, feature additions), 🐛 (:bug:, patch and bugfixes), 📖 (:book:, documentation or proposals), or 🌱 (:seedling:, minor or other) 
-->

**What this PR does / why we need it**: Follow up cleanups from #9096. 
- fix ensure ownerRefs to make sure DockerMachines always have an owner
- Don't use RequeueAfter in DockerMachinePool controller delete, instead watch DockerMachines
- Use ssa.Patch() for creating Machines in MachinePool controller

**Which issue(s) this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close the issue(s) when PR gets merged)*:
Fixes #

<!-- 
Please label this pull request according to what area(s) you are addressing. For reference on PR/issue labels, see: https://github.com/kubernetes-sigs/cluster-api/labels?q=area+

Area example:
/area runtime-sdk
-->

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

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

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

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

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

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["vincepri"]} -->
Jont828: /area provider/infrastructure-docker
Jont828: /test pull-cluster-api-e2e-full-main
Jont828: /test pull-cluster-api-e2e-full-main
Jont828: /test pull-cluster-api-e2e-full-main
Jont828: /retest
CecileRobertMichon: /test pull-cluster-api-e2e-full-main
CecileRobertMichon: /test pull-cluster-api-e2e-full-main
CecileRobertMichon: /test pull-cluster-api-e2e-full-main
k8s-ci-robot: @Jont828: The following test **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

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

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

<details>

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

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

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

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

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

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["fabriziopandini"]} -->
CecileRobertMichon: /test pull-cluster-api-e2e-full-main
sbueringer: /lgtm

/assign @fabriziopandini 
k8s-ci-robot: LGTM label has been added.  <details>Git tree hash: ad4bd80869ea7aa00ad5263d95fef7726b730435</details>
k8s-ci-robot: New changes are detected. LGTM label has been removed.
CecileRobertMichon: Removed `conditions.WithStepCounterIf(false)` since https://github.com/kubernetes-sigs/cluster-api/pull/9772 already merged

/test pull-cluster-api-e2e-full-main
k8s-ci-robot: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/kubernetes-sigs/cluster-api/pull/9725#pullrequestreview-1756221287" title="Approved">vincepri</a>*

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

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

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

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

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->
CecileRobertMichon: @Jont828 feel free to push those changes

Patch:
diff --git a/.github/ISSUE_TEMPLATE/release_tracking.md b/.github/ISSUE_TEMPLATE/release_tracking.md
index 9a815602b..5c09abd3c 100644
--- a/.github/ISSUE_TEMPLATE/release_tracking.md
+++ b/.github/ISSUE_TEMPLATE/release_tracking.md
@@ -14,11 +14,14 @@ Please see the corresponding section in [release-tasks.md](https://github.com/ku
 
 **Notes**:
 * Weeks are only specified to give some orientation.
-* The following is based on the v1.6 release cycle. Modify according to the tracked release cycle.
+* The following is based on the v1.4 release cycle. Modify according to the tracked release cycle.
+
+Week -3 to 1:
+* [ ] [Release Lead] [Set a tentative release date for the minor release](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/release/release-tasks.md#set-a-tentative-release-date-for-the-minor-release)
+* [ ] [Release Lead] [Assemble release team](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/release/release-tasks.md#assemble-release-team)
 
 Week 1:
 * [ ] [Release Lead] [Finalize release schedule and team](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/release/release-tasks.md#finalize-release-schedule-and-team)
-* [ ] [Release Lead] [Add/remove release team members](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/release/release-tasks.md#addremove-release-team-members)
 * [ ] [Release Lead] [Prepare main branch for development of the new release](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/release/release-tasks.md#prepare-main-branch-for-development-of-the-new-release)
 * [ ] [Communications Manager] [Add docs to collect release notes for users and migration notes for provider implementers](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/release/release-tasks.md#add-docs-to-collect-release-notes-for-users-and-migration-notes-for-provider-implementers)
 * [ ] [Communications Manager] [Update supported versions](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/release/release-tasks.md#update-supported-versions)
@@ -27,42 +30,39 @@ Week 1 to 4:
 * [ ] [Release Lead] [Track] [Remove previously deprecated code](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/release/release-tasks.md#track-remove-previously-deprecated-code)
 
 Week 6:
-* [ ] [Release Lead] [Cut the v1.5.1 & v1.4.6 releases](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/release/release-tasks.md#repeatedly-cut-a-release)
+* [ ] [Release Lead] [Cut the v1.3.1 release](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/release/release-tasks.md#repeatedly-cut-a-release)
 
 Week 9:
-* [ ] [Release Lead] [Cut the v1.5.2 & v1.4.7 releases](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/release/release-tasks.md#repeatedly-cut-a-release)
+* [ ] [Release Lead] [Cut the v1.3.2 release](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/release/release-tasks.md#repeatedly-cut-a-release)
 
 Week 11 to 12:
 * [ ] [Release Lead] [Track] [Bump dependencies](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/release/release-tasks.md#track-bump-dependencies)
 
 Week 13:
-* [ ] [Release Lead] [Cut the v1.6.0-beta.0 release](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/release/release-tasks.md#repeatedly-cut-a-release)
-* [ ] [Release Lead] [Cut the v1.5.3 & v1.4.8 releases](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/release/release-tasks.md#repeatedly-cut-a-release)
+* [ ] [Release Lead] [Cut the v1.4.0-beta.0 release](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/release/release-tasks.md#repeatedly-cut-a-release)
+* [ ] [Release Lead] [Cut the v1.3.3 release](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/release/release-tasks.md#repeatedly-cut-a-release)
 * [ ] [Release Lead] [Create a new GitHub milestone for the next release](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/release/release-tasks.md#create-a-new-github-milestone-for-the-next-release)
 * [ ] [Communications Manager] [Communicate beta to providers](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/release/release-tasks.md#communicate-beta-to-providers)
 
 Week 14:
-* [ ] [Release Lead] [Cut the v1.6.0-beta.1 release](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/release/release-tasks.md#repeatedly-cut-a-release)
-* [ ] [Release Lead] [Set a tentative release date for the next minor release](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/release/release-tasks.md#set-a-tentative-release-date-for-the-next-minor-release)
-* [ ] [Release Lead] [Assemble next release team](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/release/release-tasks.md#assemble-next-release-team)
+* [ ] [Release Lead] [Cut the v1.4.0-beta.1 release](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/release/release-tasks.md#repeatedly-cut-a-release)
 * [ ] [Release Lead] Select release lead for the next release cycle
 
 Week 15:
+* [ ] [Release Lead] [Create the release-1.4 release branch](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/release/release-tasks.md#create-a-release-branch)
+* [ ] [Release Lead] [Cut the v1.4.0-rc.0 release](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/release/release-tasks.md#repeatedly-cut-a-release)
+* [ ] [CI Manager] [Setup jobs and dashboards for the release-1.4 release branch](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/release/release-tasks.md#setup-jobs-and-dashboards-for-a-new-release-branch)
+* [ ] [Communications Manager] [Ensure the book for the new release is available](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/release/release-tasks.md#ensure-the-book-for-the-new-release-is-available)
 
-* KubeCon idle week
+Week 15 to 17:
+* [ ] [Communications Manager] [Polish release notes](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/release/release-tasks.md#polish-release-notes)
 
 Week 16:
-* [ ] [Release Lead] [Cut the v1.6.0-rc.0 release](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/release/release-tasks.md#repeatedly-cut-a-release)
-* [ ] [Release Lead] [Update milestone applier and GitHub Actions](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/release/release-tasks.md#update-milestone-applier-and-github-actions)
-* [ ] [CI Manager] [Setup jobs and dashboards for the release-1.6 release branch](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/release/release-tasks.md#setup-jobs-and-dashboards-for-a-new-release-branch)
-* [ ] [Communications Manager] [Ensure the book for the new release is available](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/release/release-tasks.md#ensure-the-book-for-the-new-release-is-available)
+* [ ] [Release Lead] [Cut the v1.4.0-rc.1 release](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/release/release-tasks.md#repeatedly-cut-a-release)
 
 Week 17:
-* [ ] [Release Lead] [Cut the v1.6.0-rc.1 release](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/release/release-tasks.md#repeatedly-cut-a-release)
-
-Week 18:
-* [ ] [Release Lead] [Cut the v1.6.0 release](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/release/release-tasks.md#repeatedly-cut-a-release)
-* [ ] [Release Lead] [Cut the v1.5.4 & v1.4.9 releases](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/release/release-tasks.md#repeatedly-cut-a-release)
+* [ ] [Release Lead] [Cut the v1.4.0 release](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/release/release-tasks.md#repeatedly-cut-a-release)
+* [ ] [Release Lead] [Cut the v1.3.4 release](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/release/release-tasks.md#repeatedly-cut-a-release)
 * [ ] [Release Lead] Organize release retrospective
 * [ ] [Communications Manager] [Change production branch in Netlify to the new release branch](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/release/release-tasks.md#change-production-branch-in-netlify-to-the-new-release-branch)
 * [ ] [Communications Manager] [Update clusterctl links in the quickstart](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/release/release-tasks.md#update-clusterctl-links-in-the-quickstart)
@@ -81,4 +81,3 @@ Continuously:
 If and when necessary:
 * [ ] [Release Lead] [Track] [Bump the Cluster API apiVersion](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/release/release-tasks.md#optional-track-bump-the-cluster-api-apiversion)
 * [ ] [Release Lead] [Track] [Bump the Kubernetes version](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/release/release-tasks.md#optional-track-bump-the-kubernetes-version)
-* [ ] [Release Lead] [Track Release and Improvement tasks](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/release/release-tasks.md#optional-track-release-and-improvement-tasks)
\ No newline at end of file
diff --git a/.github/workflows/pr-gh-workflow-approve.yaml b/.github/workflows/pr-gh-workflow-approve.yaml
index d91cd21b8..d6c843b82 100644
--- a/.github/workflows/pr-gh-workflow-approve.yaml
+++ b/.github/workflows/pr-gh-workflow-approve.yaml
@@ -17,7 +17,7 @@ jobs:
       actions: write
     steps:
       - name: Update PR
-        uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
+        uses: actions/github-script@e69ef5462fd455e02edcaf4dd7708eda96b9eda0 # v7.0.0
         continue-on-error: true
         with:
           github-token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/pr-golangci-lint.yaml b/.github/workflows/pr-golangci-lint.yaml
index 290a0d2fa..577427d7d 100644
--- a/.github/workflows/pr-golangci-lint.yaml
+++ b/.github/workflows/pr-golangci-lint.yaml
@@ -30,6 +30,6 @@ jobs:
       - name: golangci-lint
         uses: golangci/golangci-lint-action@3a919529898de77ec3da873e3063ca4b10e7f5cc # tag=v3.7.0
         with:
-          version: v1.55.2
+          version: v1.55.1
           args: --out-format=colored-line-number
           working-directory: ${{matrix.working-directory}}
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index 226025f12..5742ea2e2 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -22,7 +22,7 @@ jobs:
           fetch-depth: 0
       - name: Get changed files
         id: changed-files
-        uses: tj-actions/changed-files@da093c1609db0edd0a037ce9664e135f74bf30d9 # tag=v40.2.0
+        uses: tj-actions/changed-files@25ef3926d147cd02fc7e931c1ef50772bbb0d25d # tag=v40.1.1
       - name: Get release version
         id: release-version
         run: |
diff --git a/.github/workflows/weekly-md-link-check.yaml b/.github/workflows/weekly-md-link-check.yaml
index c7a1df15c..04b030727 100644
--- a/.github/workflows/weekly-md-link-check.yaml
+++ b/.github/workflows/weekly-md-link-check.yaml
@@ -14,7 +14,7 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        branch: [ main, release-1.6, release-1.5 ]
+        branch: [ main, release-1.5, release-1.4 ]
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # tag=v4.1.1
diff --git a/.github/workflows/weekly-security-scan.yaml b/.github/workflows/weekly-security-scan.yaml
index 6d8a87063..24fddda35 100644
--- a/.github/workflows/weekly-security-scan.yaml
+++ b/.github/workflows/weekly-security-scan.yaml
@@ -13,7 +13,7 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        branch: [ main, release-1.6, release-1.5 ]
+        branch: [ main, release-1.5, release-1.4 ]
     name: Trivy
     runs-on: ubuntu-latest
     steps:
diff --git a/.golangci.yml b/.golangci.yml
index 122542783..82057f946 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -139,9 +139,6 @@ linters-settings:
         alias: addonsv1alpha4
       - pkg: sigs.k8s.io/cluster-api/exp/addons/api/v1beta1
         alias: addonsv1
-      # CAPI exp IPAM
-      - pkg: sigs.k8s.io/cluster-api/exp/ipam/api/v1beta1
-        alias: ipamv1
       # CAPI exp runtime
       - pkg: sigs.k8s.io/cluster-api/exp/runtime/api/v1alpha1
         alias: runtimev1
diff --git a/CHANGELOG/v1.6.0-rc.0.md b/CHANGELOG/v1.6.0-rc.0.md
deleted file mode 100644
index 851585a50..000000000
--- a/CHANGELOG/v1.6.0-rc.0.md
+++ /dev/null
@@ -1,370 +0,0 @@
-🚨 This is a RELEASE CANDIDATE. Use it only for testing purposes. If you find any bugs, file an [issue](https://github.com/kubernetes-sigs/cluster-api/issues/new).
-
-## Highlights
-
-* MachinePools are now supported in ClusterClass Clusters
-* Metrics, profiling and other diagnostics are now served securely by default
-* Types in `ipam.cluster.x-k8s.io` have graduated to `v1beta1`
-
-## Deprecation Warning
-
-- The API version `v1alpha4` is no longer served in this release.
-  - [Version migration guide](https://main.cluster-api.sigs.k8s.io/developer/providers/version-migration.html)
-  - [GitHub issue #8038](https://github.com/kubernetes-sigs/cluster-api/issues/8038)
-  - [API deprecation details](https://main.cluster-api.sigs.k8s.io/contributing#removal-of-v1alpha3--v1alpha4-apiversions)
-- The API version `v1alpha3` has been completely removed in this release.
-- Flag `--metrics-bind-addr` is [deprecated](https://github.com/kubernetes-sigs/cluster-api/pull/9264) for all controller
-
-## Changes since v1.6.0-beta.1
-## :chart_with_upwards_trend: Overview
-- 53 new commits merged
-- 2 feature additions ✨
-- 5 bugs fixed 🐛
-
-## :memo: Proposals
-- Documentation: CAEP: Flexible Managed Kubernetes Endpoints (#8500)
-
-## :sparkles: New Features
-- CAPD: Add MachinePool Machine implementation to CAPD components (#8842)
-- clusterctl: Add RKE2 bootstrap provider to clusterctl (#9720)
-
-## :bug: Bug Fixes
-- CAPD: Fix DockerMachine panic (#9673)
-- CI: Fix reporting bug in verify-container-image script (#9676)
-- clusterctl: Fix provider namespace secret not included in clusterctl move (#9694)
-- KCP: Allow dropping patches KubeadmControlPlane KubeadmConfig (#9698)
-- MachinePool: Fix bug where MachinePool Machine ownerRefs weren't updating (#9619)
-
-## :seedling: Others
-- CAPD: Set Condition, if creating external LB failed. (#9697)
-- CI: Bump actions/github-script from 7.0.0 to 7.0.1 (#9738)
-- CI: Bump tj-actions/changed-files from 40.1.0 to 40.1.1 (#9714)
-- ClusterClass: Add additional test cases for nested variable defaulting (#9728)
-- clusterctl: Propagate ctx to retryWithExponentialBackoff in clusterctl (#9437)
-- Dependency: Bump cert-manager to v1.13.2 (#9653)
-- Dependency: Bump cloud.google.com/go/storage from 1.34.1 to 1.35.1 in /hack/tools (#9727)
-- Dependency: Bump github.com/docker/docker from 24.0.6+incompatible to 24.0.7+incompatible in /test (#9652)
-- Dependency: Bump github.com/fatih/color from 1.15.0 to 1.16.0 (#9681)
-- Dependency: Bump github.com/onsi/ginkgo/v2 from 2.13.0 to 2.13.1 (#9716)
-- Dependency: Bump github.com/onsi/gomega from 1.29.0 to 1.30.0 (#9717)
-- Dependency: Bump github.com/spf13/cobra from 1.7.0 to 1.8.0 (#9679)
-- Dependency: Bump Go version to v1.20.11 (#9683)
-- Dependency: Bump golang.org/x/oauth2 from 0.13.0 to 0.14.0 (#9715)
-- Dependency: Bump golang.org/x/text from 0.13.0 to 0.14.0 (#9680)
-- Dependency: Bump golangci-lint to v1.55.2 (#9740)
-- Dependency: Bump google.golang.org/api from 0.149.0 to 0.150.0 in /hack/tools (#9691)
-- Dependency: Bump opentelemetry to fix CVEs (#9709)
-- Dependency: Bump the kubernetes group with 4 updates (#9739)
-- Dependency: Bump Trivy to v0.47.0 (#9671)
-- Devtools: Allow non-provider Deployments in Tilt (#9404)
-- Documentation: Add k3s providers to cluster-api book (#9577)
-- Documentation: Fix typo in 'ginkgo' in Makefile (#9654)
-- e2e: Add MP timeouts to cluster upgrade tests (#9723)
-- e2e: Improve logging for self-hosted e2e test (#9637)
-- e2e: Test/conformance: add variables to overwrite parralelization of kubetest runs (#9667)
-- IPAM: Add age also to v1beta1 IPAM types (#9729)
-- IPAM: Enforce IPAM import alias (#9730)
-- KCP: Support admin config for Kubeadm v1.29 (#9682)
-- Release: Add adilGhaffarDev to release-team owner alias (#9718)
-- Release: Avoid cd'ing into test folders when building Docker images (#9744)
-- Release: Bump kubebuilder tools to v0.4.2 (#9665)
-- Release: Give permissions for release notes tool to release team (#9563)
-- Release: Hack: add weekly update script for Slack (#9343)
-- Testing: Add MachinePools to Runtime SDK and Rollout tests (#9703)
-- Testing: Add WaitForMachinePoolToBeUpgraded to self-hosted test (#9540)
-- Testing: Follow-up fixes: Add MachinePools to Runtime SDK and Rollout tests (#9719)
-
-:book: Additionally, there have been 3 contributions to our documentation and book. (#9559, #9635, #9706) 
-
-<details>
-<summary>More details about the release</summary>
-
-:warning: **RELEASE CANDIDATE NOTES** :warning:
-
-## 👌 Kubernetes version support
-
-- Management Cluster: v1.25.x -> v1.28.x
-- Workload Cluster: v1.23.x -> v1.28.x
-
-[More information about version support can be found here](https://cluster-api.sigs.k8s.io/reference/versions.html)
-
-## Changes since v1.5.0
-## :chart_with_upwards_trend: Overview
-- 382 new commits merged
-- 6 breaking changes :warning:
-- 15 feature additions ✨
-- 34 bugs fixed 🐛
-
-## :memo: Proposals
-- Community meeting: Add proposal for karpenter integration feature group (#9571)
-- Documentation: Flexible Managed Kubernetes Endpoints (#8500)
-
-## :warning: Breaking Changes
-- API: Remove v1alpha3 API Version (#8997)
-- API: Stop serving v1alpha4 API Versions (#8996)
-- clusterctl: Improve Context handling in clusterctl (#8939)
-- Dependency: Bump to controller-runtime v0.16 (#8999)
-- Metrics/Logging: Implement secure diagnostics (metrics, pprof, log level changes) (#9264)
-- util: : Remove go-vcs dependency from releaselink tool (#9288)
-
-## :sparkles: New Features
-- API: Add validation to nested ObjectMeta fields (#8431)
-- CAPD: Add config maps to CAPD RBAC (#9528)
-- CAPD: Add MachinePool Machine implementation to CAPD components (#8842)
-- CAPD: Allow adding custom HA proxy config for CAPD load balancer (#8785)
-- CAPD: Initialize configmap object before getting it (#9529)
-- ClusterClass: Add topology-owned label to MachineHealthChecks. (#9191)
-- ClusterClass: Introduce NamingStrategy and allow generating names using go templates (#9340)
-- ClusterClass: Update API with ClusterClass MachinePool support (#8820)
-- clusterctl: Add RKE2 bootstrap provider to clusterctl (#9720)
-- clusterctl: Block move with annotation (#8690)
-- IPAM: Promote IPAM types to v1beta1 (#9525)
-- MachinePool: Add MachinePool workers support in ClusterClass (#9016)
-- MachineSet: Adjust preflight check to allow kubelet version skew of 3 for clusters running v1.28 and above (#9222)
-- Testing/Documentation: V1.28: Prepare quickstart, capd and tests for the new release including kind bump (#9160)
-- Release: Add automation to create release branch and tags (#9111)
-
-## :bug: Bug Fixes
-- CAPBK: Certificate paths in cloud-init scripts should not use a platform-dependent path separator (#9167)
-- CAPD: Delete container after failed start to work around port allocation issues (#9125)
-- CAPD: Fix DockerMachine panic (#9673)
-- CI: Fix reporting bug in verify-container-image script (#9676)
-- ClusterClass: Fix ClusterClass enqueue for ExtensionConfig (#9133)
-- ClusterClass: Topology: fix namingstrategy webhook to not use uppercase characters for testing the template and align unit test to e2e test (#9425)
-- clusterctl: Fix provider namespace secret not included in clusterctl move (#9694)
-- Dependency: Bump golang.org/x/net to v0.13.0 (#9121)
-- Dependency: Bump to docker v24.0.5-0.20230714235725-36e9e796c6fc (#9038)
-- Devtools: Adding metrics container port in tilt-prepare only if it's missing (#9308)
-- Devtools: Allow duplicate objects in Tiltfile (#9302)
-- Devtools: Change tilt debug base image to golang (#9070)
-- Devtools: Fix tilt-prepare leader-elect setting (#9315)
-- Devtools: Pin Plantuml version (#9424)
-- Devtools: Tilt: ensure .tiltbuild/bin directory is created early enough, add tilt troubleshooting guide (#9165)
-- Documentation: Fix doctoc detection in verify-doctoc.sh (#9112)
-- e2e: Drop MachinePools from Dualstack tests (#9477)
-- e2e: Fix autoscaler image repo (#9353)
-- e2e: Test: pin conformance image to a version which includes a fix for the dualstack tests (#9252)
-- KCP: Allow dropping patches KubeadmControlPlane KubeadmConfig (#9698)
-- KCP: Allow to drop useExperimentalRetryJoin field from KubeadmControlPlane.kubeadmConfigSpec (#9170)
-- KCP: Fix KCP Controller reconcile always return error when workload cluster is unreachable (#9342)
-- KCP: Requeue KCP object if ControlPlaneComponentsHealthyCondition is not yet true (#9032)
-- Machine: Retry Node delete when CCT is locked (#9570)
-- MachineDeployment: MD controller: use regular random suffix for MachineSets, ensure max length 63 (#9298)
-- MachineHealthCheck: Fix excessive trace logging in the machine health check controller (#9419)
-- MachinePool: Fix bug where MachinePool Machine ownerRefs weren't updating (#9619)
-- Release: Alphabetically sorting release tool output (#9055)
-- Release: Deduplicating area in pr title in release notes (#9186)
-- Release: Fix go install path for kpromo v4.0.4 (#9336)
-- Release: Generate warning when release notes can not be generated (#9163)
-- Release: Hack/release-notes: ensure relase notes tool can be used for external projects again (#9018)
-- Release: Reverts pull request from cahillsf/improve-release-speed (#9465)
-- util: Fix AddAnnotations for unstructured.Unstructured (#9164)
-
-## :seedling: Others
-- API: Add ClusterClass column to Cluster CRD (#9120)
-- API: Add verify-import-restrictions to enforce import restrictions (#9407)
-- API: Enforce import restrictions in all API packages (#9461)
-- API: Ensure we generate all webhook manifests.yaml (#9621)
-- API: Move API v1beta1 webhooks to a separate package (#9047)
-- API: Move docker  infrastructure experimental API v1beta1 webhooks to sepa… (#9460)
-- API: Move docker infrastructure API v1beta1 webhooks to separate package (#9458)
-- API: Move experimental addons API v1beta1 webhooks to separate package (#9438)
-- API: Move experimental API v1beta1 webhooks to separate package (#9417)
-- API: Move inmemory infrastructure API v1beta1 webhooks to separate package (#9459)
-- API: Move Kubeadm API v1beta1 webhooks to separate package (#9410)
-- API: Remove files and markers for Kubebuilder (#9344)
-- API: Remove reliance on controller-runtime scheme builder (#9045)
-- API: Remove reliance on controller-runtime scheme builder for experimental APIs (#9185)
-- API: Remove reliance on controller-runtime scheme builder for remaining API groups (#9266)
-- API: Remove the dependency on cluster-api/utils from addons API (#9482)
-- API: Test and document controller ownerReferences (#9153)
-- CAPBK: Remove Kubeadm upstream v1beta1 types (#9345)
-- CAPD: Fix multi error handling in RunContainer (#9139)
-- CAPD: Set Condition, if creating external LB failed. (#9697)
-- CI: Add colored-line-number output for golangci-lint action (#9147)
-- CI: Add dependabot for test and hack/tools module (#9041)
-- CI: Add exclude for Kustomize API to dependabot config (#9059)
-- CI: Add licence-scan for pull requests (#9184)
-- CI: Add loggercheck linter and fix findings (#9446)
-- CI: Add verify-govulncheck and verify-vulnerabilities targets and integrate to scan action (#9144)
-- CI: Bump actions/cache from 3.3.1 to 3.3.2 (#9395)
-- CI: Bump actions/checkout from 4.1.0 to 4.1.1 (#9611)
-- CI: Bump actions/github-script from 7.0.0 to 7.0.1 (#9738)
-- CI: Bump actions/setup-go from 4.0.1 to 4.1.0 (#9187)
-- CI: Bump apidiff to v0.7.0 (#9472)
-- CI: Bump golangci-lint to v1.54.1 (#9174)
-- CI: Bump golangci/golangci-lint-action from 3.6.0 to 3.7.0 (#9261)
-- CI: Bump tj-actions/changed-files from 40.1.0 to 40.1.1 (#9714)
-- CI: Bump Trivy to v0.45.1 (#9445)
-- CI: Fix .golangci.yml comments (#9499)
-- CI: Ginkgolinter: forbid focus container (#9320)
-- CI: Github: add edited and reopened as triggers for the GH workflow approval (#9259)
-- CI: Github: add workflow to auto-approve golangci-lint if ok-to-test label is set (#9244)
-- CI: Go.mod reformat to have only two require blocks (#9192)
-- CI: Golangci-lint: replace deprecated local-prefixes setting for gci (#9339)
-- CI: Make GO_ARCH explicit in verify_containter script (#9341)
-- CI: Update actions for 1.5 and make names consistent (#9115)
-- CI: Verify plantuml image generation in CI (#9363)
-- ClusterCacheTracker: Add separate concurrency flag for cluster cache tracker (#9116)
-- ClusterCacheTracker: Ensure Get/List calls are not getting stuck when apiserver is unreachable (#9028)
-- ClusterCacheTracker: Fix accessor deletion on health check failure (#9025)
-- ClusterClass: Add additional test cases for nested variable defaulting (#9728)
-- ClusterClass: Add ownerRefs to BootstrapConfig/InfraMachinePool in classy Clusters (#9389)
-- ClusterClass: Embed ssa.FilterObjectInput into HelperOption to remove duplication (#9512)
-- ClusterClass: Fix some nits in Cluster topology engine tests (#9464)
-- ClusterClass: Improve Cluster variable defaulting/validation errors (#9452)
-- ClusterClass: Improve message for TopologyReconciledCondition (#9400)
-- ClusterClass: Make ClusterClass generated object names consistent (#9254)
-- ClusterClass: Minor fixes for CC+MP implementation (#9318)
-- clusterctl: Check resource blocking clusterctl move during discovery (#9246)
-- clusterctl: Propagate ctx to retryWithExponentialBackoff in clusterctl (#9437)
-- clusterctl: Use goproxy to check version in clusterctl (#9237)
-- clusterctl: Use http get to download files from GitHub in clusterctl (#9236)
-- Dependency: Bump cert-manager to v1.13.2 (#9653)
-- Dependency: Bump cloud.google.com/go/storage from 1.34.1 to 1.35.1 in /hack/tools (#9727)
-- Dependency: Bump controller tools to v1.13.0 (#9221)
-- Dependency: Bump controller-runtime to v0.16.3 (#9592)
-- Dependency: Bump conversion-gen to v0.28 (#9267)
-- Dependency: Bump corefile-migration library to v1.0.21 (#9307)
-- Dependency: Bump docker to v24.0.5 (#9064)
-- Dependency: Bump envtest binaries to 1.28 (#9268)
-- Dependency: Bump github.com/blang/semver to v4 (#9189)
-- Dependency: Bump github.com/docker/distribution (#9544)
-- Dependency: Bump github.com/docker/docker from 24.0.6+incompatible to 24.0.7+incompatible in /test (#9652)
-- Dependency: Bump github.com/emicklei/go-restful/v3 from 3.10.2 to 3.11.0 in /test (#9272)
-- Dependency: Bump github.com/evanphx/json-patch/v5 from 5.6.0 to 5.7.0 (#9397)
-- Dependency: Bump github.com/fatih/color from 1.15.0 to 1.16.0 (#9681)
-- Dependency: Bump github.com/go-logr/logr from 1.2.4 to 1.3.0 (#9644)
-- Dependency: Bump github.com/google/go-cmp from 0.5.9 to 0.6.0 (#9562)
-- Dependency: Bump github.com/onsi/ginkgo/v2 from 2.13.0 to 2.13.1 (#9716)
-- Dependency: Bump github.com/onsi/gomega from 1.29.0 to 1.30.0 (#9717)
-- Dependency: Bump github.com/prometheus/client_golang from 1.16.0 to 1.17.0 (#9517)
-- Dependency: Bump github.com/spf13/cobra from 1.7.0 to 1.8.0 (#9679)
-- Dependency: Bump github.com/spf13/viper from 1.16.0 to 1.17.0 (#9561)
-- Dependency: Bump Go to v1.20.10 (#9551)
-- Dependency: Bump Go version to v1.20.11 (#9683)
-- Dependency: Bump go-github dependency to version v53 (#8995)
-- Dependency: Bump go.etcd.io/etcd/api/v3 from 3.5.9 to 3.5.10 (#9642)
-- Dependency: Bump go.etcd.io/etcd/client/v3 from 3.5.9 to 3.5.10 (#9646)
-- Dependency: Bump go.opentelemetry.io/* dependencies (#9598)
-- Dependency: Bump golang.org/grpc to v1.59.0 (#9626)
-- Dependency: Bump golang.org/x/net from 0.15.0 to 0.17.0 in /test (#9537)
-- Dependency: Bump golang.org/x/oauth2 from 0.13.0 to 0.14.0 (#9715)
-- Dependency: Bump golang.org/x/text from 0.13.0 to 0.14.0 (#9680)
-- Dependency: Bump golangci-lint to v1.55.2 (#9740)
-- Dependency: Bump gomodules.xyz/jsonpatch/v2 from 2.3.0 to 2.4.0 (#9188)
-- Dependency: Bump google.golang.org/api from 0.149.0 to 0.150.0 in /hack/tools (#9691)
-- Dependency: Bump google.golang.org/grpc from 1.58.2 to 1.58.3 (#9607)
-- Dependency: Bump kpromo to v4.0.4 (#9241)
-- Dependency: Bump opentelemetry to fix CVEs (#9709)
-- Dependency: Bump sigs.k8s.io/yaml from 1.3.0 to 1.4.0 (#9645)
-- Dependency: Bump some dependencies in Makefile (#9549)
-- Dependency: Bump the kubernetes group in /hack/tools with 2 updates (#9420)
-- Dependency: Bump the kubernetes group in /hack/tools with 2 updates (#9574)
-- Dependency: Bump the kubernetes group with 2 updates (#9606)
-- Dependency: Bump the kubernetes group with 4 updates (#9326)
-- Dependency: Bump the kubernetes group with 4 updates (#9455)
-- Dependency: Bump the kubernetes group with 4 updates (#9739)
-- Dependency: Bump to Go 1.20.8 (#9381)
-- Dependency: Bump Trivy to v0.47.0 (#9671)
-- Dependency: Replace hashicorp/go-multierror with kerrors (#9175)
-- Dependency: Update ensure-kubectl.sh to 1.28 (#9275)
-- Devtools: Add .PHONY for doctoc target (#9148)
-- Devtools: Allow non-provider Deployments in Tilt (#9404)
-- Devtools: Bump CAPI visualizer to v1.2.0 (#9195)
-- Devtools: Drop duplicate pprof and unused linter excludes (#9156)
-- Devtools: Improve Performance dashboard (#9387)
-- Devtools: Make dev cluster networking configurable (#9183)
-- Devtools: Makefile: run doctoc only once (#9182)
-- Devtools: Move tilt-prepare and log-push to tools/internal (#9020)
-- Devtools: Observability: move metrics to config and use sidecar in kube-state-metrics (#9390)
-- Devtools: Refactor docker-push* Makefile targets so users can control with ALL_DOCKER_BUILD which images are pushed (#8586)
-- Devtools: Tiltfile: rename deploy_kustomizations to additional_kustomizations (#9439)
-- Documentation: Add k3s providers to cluster-api book (#9577)
-- Documentation: Add more links to release-tasks doc (#9029)
-- Documentation: Extend docs for patch.NewHelper (#9001)
-- Documentation: Fix typo in 'ginkgo' in Makefile (#9654)
-- Documentation: Fixed grammatically incorrect plurals in release tools (#9024)
-- Documentation: Promote chrischdi to cluster-api reviewer (#9286)
-- Documentation: Use official plantuml image for diagrams (#9328)
-- e2e: Add back flavor to Waiter interface (#9166)
-- e2e: Add CRS re-reconcile to ownerReference test (#9296)
-- e2e: Add log level for kube components patch to ClusterClass (#9493)
-- e2e: Add MachinePools to Topology Quickstart E2E Templates (#9393)
-- e2e: Add MP timeouts to cluster upgrade tests (#9723)
-- e2e: Add test for ownerReference apiVersion update (#9269)
-- e2e: Add test for scale testing machinery (#9510)
-- e2e: Bump autoscaler to v1.28.0 (#9349)
-- e2e: Drop PR-Informing test tag and job description (#9362)
-- e2e: Dump all pods in e2e test clusters (#9441)
-- e2e: Dump all resource information for self-hosted tests (#9547)
-- e2e: Ensure finalizers are resilient on reconciliation (#9471)
-- e2e: Fail tests if test env version check fails (#9388)
-- e2e: Fix broken e2e test clusterclass (#9506)
-- e2e: Improve labels/annotations in CAPD test ClusterClass (#9469)
-- e2e: Improve logging for self-hosted e2e test (#9637)
-- e2e: Refactor e2e ownerRef test utils (#9313)
-- e2e: Test/conformance: add variables to overwrite parralelization of kubetest runs (#9667)
-- e2e: Test/e2e: structure resources by namespace/kind again (#9462)
-- e2e: Use existing value of `SKIP_RESOURCE_CLEANUP` if set in environment (#9152)
-- IPAM: Add age also to v1beta1 IPAM types (#9729)
-- IPAM: Add age column to kubectl output (#9521)
-- IPAM: Enforce IPAM import alias (#9730)
-- KCP: Controlplane: add a test case for syncMachines where the InfraMachine does not exist. (#8992)
-- KCP: Remove disableInPlacePropagation field in KCP controller (#9099)
-- KCP: Remove redundant GetRESTConfig in KCP Management.GetWorkloadCluster (#9448)
-- KCP: Support admin config for Kubeadm v1.29 (#9682)
-- Logging: Change default log level to 2 (#9093)
-- Logging: Fix patch errors not being logged (#9224)
-- Logging: Set controller-runtime logger for clusterctl (#9107)
-- MachinePool: Add MachinePool Builders (#9346)
-- MachinePool: Add util function to get MachinePool by label (#9219)
-- Metrics: Hack/observability: add capi_machine_status_certificatesexpirydate metric (#9084)
-- Metrics: Hack: bump kube-state-metrics and prometheus charts (#9352)
-- Release: Add additional blocks to release note generation (#9247)
-- Release: Add adilGhaffarDev to release-team owner alias (#9718)
-- Release: Add Release Team OWNERS file to docs/release folder (#9294)
-- Release: Avoid cd'ing into test folders when building Docker images (#9744)
-- Release: Bump kubebuilder tools to v0.4.2 (#9665)
-- Release: Bump PR title checker github action to v0.4.1 (#9650)
-- Release: Capitalize area prefix in release notes by default (#9614)
-- Release: Capitalized title in release notes (#9086)
-- Release: Clarify release team vs k8s/k8s-SIGs org membership (#9089)
-- Release: Exclude release trigger PRs from release notes (#9444)
-- Release: Format Community meeting area in release notes (#9648)
-- Release: Format MachineHealthCheck area in release notes (#9500)
-- Release: Give permissions for release notes tool to release team (#9563)
-- Release: Hack: add weekly update script for Slack (#9343)
-- Release: Improve multiple areas PRs with user friendly subs (#9071)
-- Release: Improve release notes formatting (#9337)
-- Release: Improve release speed (#9392)
-- Release: Improve release staging build speed (#9536)
-- Release: Prepare main branch for v1.6 development (#9097)
-- Release: Remove auto-labelling for clusterctl (#8990)
-- Release: Remove extra separator after title in release notes (#9605)
-- Release: Update instructions checklist to generate release notes (#9443)
-- Release: Update kubebuilder-release-tools to v0.4.0 (#9531)
-- Release: Update release-notes make target + corresponding doc (#9573)
-- Runtime SDK: Improve ClusterClass watch for ExtensionConfigs (#9338)
-- Testing: Add MachinePool test cases (#9474)
-- Testing: Add MachinePool test cases to engine tests (#9373)
-- Testing: Add MachinePool test cases to variables tests (#9372)
-- Testing: Add MachinePools to Runtime SDK and Rollout tests (#9703)
-- Testing: Add MachinePools to topology upgrade test (#9502)
-- Testing: Add test for required properties in clusterclass variables (#9113)
-- Testing: Add unit tests for CC MP blueprint, current_state, & desired_state (#9348)
-- Testing: Add unit tests for reconcile_state, cluster_controller, & conditions (#9380)
-- Testing: Add WaitForMachinePoolToBeUpgraded to self-hosted test (#9540)
-- Testing: Extend test/framework to collect workload cluster nodes (#9416)
-- Testing: Follow-up fixes: Add MachinePools to Runtime SDK and Rollout tests (#9719)
-- Testing: Replacing gomega.Equal with gomega.BeComparableTo (#9015)
-- util: Adjust naming in SortForCreate implementation (#9311)
-- util: Move `internal.labels` to `format` package for use by providers (#9002)
-- util: Refactor SortForCreate to use sort.Slice (#9251)
-- util: Remove previously deprecated code (#9136)
-
-:book: Additionally, there have been 46 contributions to our documentation and book. (#8260, #8678, #8819, #8988, #9013, #9014, #9080, #9081, #9087, #9119, #9141, #9146, #9150, #9161, #9173, #9208, #9209, #9213, #9214, #9232, #9270, #9291, #9305, #9364, #9386, #9403, #9415, #9429, #9433, #9463, #9487, #9488, #9490, #9511, #9513, #9514, #9527, #9550, #9559, #9565, #9572, #9590, #9593, #9613, #9635, #9706)
-</details>
-<br/>
-
-_Thanks to all our contributors!_ 😊
diff --git a/CHANGELOG/v1.6.0-rc.1.md b/CHANGELOG/v1.6.0-rc.1.md
deleted file mode 100644
index 32b10b8b4..000000000
--- a/CHANGELOG/v1.6.0-rc.1.md
+++ /dev/null
@@ -1,314 +0,0 @@
-🚨 This is a RELEASE CANDIDATE. Use it only for testing purposes. If you find any bugs, file an [issue](https://github.com/kubernetes-sigs/cluster-api/issues/new).
-
-## Highlights
-
-* MachinePools are now supported in ClusterClass Clusters
-* Metrics, profiling and other diagnostics are now served securely by default
-* Types in `ipam.cluster.x-k8s.io` have graduated to `v1beta1`
-
-## Deprecation Warning
-
-- The API version `v1alpha4` is no longer served in this release.
-  - [Version migration guide](https://main.cluster-api.sigs.k8s.io/developer/providers/version-migration.html)
-  - [GitHub issue #8038](https://github.com/kubernetes-sigs/cluster-api/issues/8038)
-  - [API deprecation details](https://main.cluster-api.sigs.k8s.io/contributing#removal-of-v1alpha3--v1alpha4-apiversions)
-- The API version `v1alpha3` has been completely removed in this release.
-- Flag `--metrics-bind-addr` is [deprecated](https://github.com/kubernetes-sigs/cluster-api/pull/9264) for all controllers
-
-## Changes since v1.6.0-rc.0
-## :chart_with_upwards_trend: Overview
-- 2 new commits merged
-- 1 bug fixed 🐛
-
-## :bug: Bug Fixes
-- ClusterResourceSet: Requeue after 1 minute if ErrClusterLocked got hit (#9788)
-
-## :seedling: Others
-- e2e: Improve output of exec.KubectlApply (#9761)
-
-<details>
-<summary>More details about the release</summary>
-
-:warning: **RELEASE CANDIDATE NOTES** :warning:
-
-## 👌 Kubernetes version support
-
-- Management Cluster: v1.25.x -> v1.28.x
-- Workload Cluster: v1.23.x -> v1.28.x
-
-[More information about version support can be found here](https://cluster-api.sigs.k8s.io/reference/versions.html)
-
-## Changes since v1.5.0
-## :chart_with_upwards_trend: Overview
-- 387 new commits merged
-- 6 breaking changes :warning:
-- 15 feature additions ✨
-- 35 bugs fixed 🐛
-
-## :memo: Proposals
-- Community meeting: Add proposal for karpenter integration feature group (#9571)
-
-## :warning: Breaking Changes
-- API: Remove v1alpha3 API Version (#8997)
-- API: Stop serving v1alpha4 API Versions (#8996)
-- clusterctl: Improve Context handling in clusterctl (#8939)
-- Dependency: Bump to controller-runtime v0.16 (#8999)
-- Metrics/Logging: Implement secure diagnostics (metrics, pprof, log level changes) (#9264)
-- util: Remove go-vcs dependency from releaselink tool (#9288)
-
-## :sparkles: New Features
-- API: Add validation to nested ObjectMeta fields (#8431)
-- CAPD: Add config maps to CAPD RBAC (#9528)
-- CAPD: Add MachinePool Machine implementation to CAPD components (#8842)
-- CAPD: Allow adding custom HA proxy config for CAPD load balancer (#8785)
-- CAPD: Initialize configmap object before getting it (#9529)
-- ClusterClass: Add topology-owned label to MachineHealthChecks. (#9191)
-- ClusterClass: Introduce NamingStrategy and allow generating names using go templates (#9340)
-- ClusterClass: Update API with ClusterClass MachinePool support (#8820)
-- clusterctl: Add RKE2 bootstrap provider to clusterctl (#9720)
-- clusterctl: Block move with annotation (#8690)
-- IPAM: Promote IPAM types to v1beta1 (#9525)
-- MachinePool: Add MachinePool workers support in ClusterClass (#9016)
-- MachineSet: Adjust preflight check to allow kubelet version skew of 3 for clusters running v1.28 and above (#9222)
-- Release: Add automation to create release branch and tags (#9111)
-- Testing/Documentation: V1.28: Prepare quickstart, capd and tests for the new release including kind bump (#9160)
-
-## :bug: Bug Fixes
-- CAPBK: Certificate paths in cloud-init scripts should not use a platform-dependent path separator (#9167)
-- CAPD: Delete container after failed start to work around port allocation issues (#9125)
-- CAPD: Fix DockerMachine panic (#9673)
-- CI: Fix reporting bug in verify-container-image script (#9676)
-- ClusterClass: Fix ClusterClass enqueue for ExtensionConfig (#9133)
-- ClusterClass: Topology: fix namingstrategy webhook to not use uppercase characters for testing the template and align unit test to e2e test (#9425)
-- clusterctl: Fix provider namespace secret not included in clusterctl move (#9694)
-- ClusterResourceSet: Requeue after 1 minute if ErrClusterLocked got hit (#9788)
-- Dependency: Bump golang.org/x/net to v0.13.0 (#9121)
-- Dependency: Bump to docker v24.0.5-0.20230714235725-36e9e796c6fc (#9038)
-- Devtools: Adding metrics container port in tilt-prepare only if it's missing (#9308)
-- Devtools: Allow duplicate objects in Tiltfile (#9302)
-- Devtools: Change tilt debug base image to golang (#9070)
-- Devtools: Fix tilt-prepare leader-elect setting (#9315)
-- Devtools: Pin Plantuml version (#9424)
-- Devtools: Tilt: ensure .tiltbuild/bin directory is created early enough, add tilt troubleshooting guide (#9165)
-- e2e: Drop MachinePools from Dualstack tests (#9477)
-- e2e: Fix autoscaler image repo (#9353)
-- e2e: Test: pin conformance image to a version which includes a fix for the dualstack tests (#9252)
-- KCP: Allow dropping patches KubeadmControlPlane KubeadmConfig (#9698)
-- KCP: Allow to drop useExperimentalRetryJoin field from KubeadmControlPlane.kubeadmConfigSpec (#9170)
-- KCP: Fix KCP Controller reconcile always return error when workload cluster is unreachable (#9342)
-- KCP: Requeue KCP object if ControlPlaneComponentsHealthyCondition is not yet true (#9032)
-- Machine: Retry Node delete when CCT is locked (#9570)
-- MachineDeployment: MD controller: use regular random suffix for MachineSets, ensure max length 63 (#9298)
-- MachineHealthCheck: Fix excessive trace logging in the machine health check controller (#9419)
-- MachinePool: Fix bug where MachinePool Machine ownerRefs weren't updating (#9619)
-- Release: Alphabetically sorting release tool output (#9055)
-- Release: Deduplicating area in pr title in release notes (#9186)
-- Release: Fix go install path for kpromo v4.0.4 (#9336)
-- Release: Generate warning when release notes can not be generated (#9163)
-- Release: Hack/release-notes: ensure relase notes tool can be used for external projects again (#9018)
-- Release: Reverts pull request from cahillsf/improve-release-speed (#9465)
-- Runtime SDK: Set User Agent for test extension correctly (#9748)
-- util: Fix AddAnnotations for unstructured.Unstructured (#9164)
-
-## :seedling: Others
-- API: Add ClusterClass column to Cluster CRD (#9120)
-- API: Add verify-import-restrictions to enforce import restrictions (#9407)
-- API: Enforce import restrictions in all API packages (#9461)
-- API: Ensure we generate all webhook manifests.yaml (#9621)
-- API: Move API v1beta1 webhooks to a separate package (#9047)
-- API: Move docker infrastructure API v1beta1 webhooks to separate package (#9458)
-- API: Move docker infrastructure experimental API v1beta1 webhooks to separate package (#9460)
-- API: Move experimental addons API v1beta1 webhooks to separate package (#9438)
-- API: Move experimental API v1beta1 webhooks to separate package (#9417)
-- API: Move inmemory infrastructure API v1beta1 webhooks to separate package (#9459)
-- API: Move Kubeadm API v1beta1 webhooks to separate package (#9410)
-- API: Remove files and markers for Kubebuilder (#9344)
-- API: Remove reliance on controller-runtime scheme builder (#9045)
-- API: Remove reliance on controller-runtime scheme builder for experimental APIs (#9185)
-- API: Remove reliance on controller-runtime scheme builder for remaining API groups (#9266)
-- API: Remove the dependency on cluster-api/utils from addons API (#9482)
-- API: Test and document controller ownerReferences (#9153)
-- CAPBK: Remove Kubeadm upstream v1beta1 types (#9345)
-- CAPD: Fix multi error handling in RunContainer (#9139)
-- CAPD: Set Condition, if creating external LB failed. (#9697)
-- CI: Add colored-line-number output for golangci-lint action (#9147)
-- CI: Add dependabot for test and hack/tools module (#9041)
-- CI: Add exclude for Kustomize API to dependabot config (#9059)
-- CI: Add licence-scan for pull requests (#9184)
-- CI: Add loggercheck linter and fix findings (#9446)
-- CI: Add verify-govulncheck and verify-vulnerabilities targets and integrate to scan action (#9144)
-- CI: Bump actions/cache from 3.3.1 to 3.3.2 (#9395)
-- CI: Bump actions/checkout from 4.1.0 to 4.1.1 (#9611)
-- CI: Bump actions/github-script from 7.0.0 to 7.0.1 (#9738)
-- CI: Bump actions/setup-go from 4.0.1 to 4.1.0 (#9187)
-- CI: Bump apidiff to v0.7.0 (#9472)
-- CI: Bump golangci-lint to v1.54.1 (#9174)
-- CI: Bump golangci/golangci-lint-action from 3.6.0 to 3.7.0 (#9261)
-- CI: Bump tj-actions/changed-files from 40.1.0 to 40.1.1 (#9714)
-- CI: Bump Trivy to v0.45.1 (#9445)
-- CI: Fix .golangci.yml comments (#9499)
-- CI: Ginkgolinter: forbid focus container (#9320)
-- CI: Github: add edited and reopened as triggers for the GH workflow approval (#9259)
-- CI: Github: add workflow to auto-approve golangci-lint if ok-to-test label is set (#9244)
-- CI: Go.mod reformat to have only two require blocks (#9192)
-- CI: Golangci-lint: replace deprecated local-prefixes setting for gci (#9339)
-- CI: Make GO_ARCH explicit in verify_containter script (#9341)
-- CI: Update actions for 1.5 and make names consistent (#9115)
-- CI: Verify plantuml image generation in CI (#9363)
-- ClusterCacheTracker: Add separate concurrency flag for cluster cache tracker (#9116)
-- ClusterCacheTracker: Ensure Get/List calls are not getting stuck when apiserver is unreachable (#9028)
-- ClusterCacheTracker: Fix accessor deletion on health check failure (#9025)
-- ClusterClass: Add additional test cases for nested variable defaulting (#9728)
-- ClusterClass: Add ownerRefs to BootstrapConfig/InfraMachinePool in classy Clusters (#9389)
-- ClusterClass: Embed ssa.FilterObjectInput into HelperOption to remove duplication (#9512)
-- ClusterClass: Fix some nits in Cluster topology engine tests (#9464)
-- ClusterClass: Improve Cluster variable defaulting/validation errors (#9452)
-- ClusterClass: Improve message for TopologyReconciledCondition (#9400)
-- ClusterClass: Make ClusterClass generated object names consistent (#9254)
-- ClusterClass: Minor fixes for CC+MP implementation (#9318)
-- clusterctl: Check resource blocking clusterctl move during discovery (#9246)
-- clusterctl: Propagate ctx to retryWithExponentialBackoff in clusterctl (#9437)
-- clusterctl: Use goproxy to check version in clusterctl (#9237)
-- clusterctl: Use http get to download files from GitHub in clusterctl (#9236)
-- Dependency: Bump cert-manager to v1.13.2 (#9653)
-- Dependency: Bump cloud.google.com/go/storage from 1.34.1 to 1.35.1 in /hack/tools (#9727)
-- Dependency: Bump controller tools to v1.13.0 (#9221)
-- Dependency: Bump controller-runtime to v0.16.3 (#9592)
-- Dependency: Bump conversion-gen to v0.28 (#9267)
-- Dependency: Bump corefile-migration library to v1.0.21 (#9307)
-- Dependency: Bump docker to v24.0.5 (#9064)
-- Dependency: Bump envtest binaries to 1.28 (#9268)
-- Dependency: Bump github.com/blang/semver to v4 (#9189)
-- Dependency: Bump github.com/docker/distribution (#9544)
-- Dependency: Bump github.com/docker/docker from 24.0.6+incompatible to 24.0.7+incompatible in /test (#9652)
-- Dependency: Bump github.com/emicklei/go-restful/v3 from 3.10.2 to 3.11.0 in /test (#9272)
-- Dependency: Bump github.com/evanphx/json-patch/v5 from 5.6.0 to 5.7.0 (#9397)
-- Dependency: Bump github.com/fatih/color from 1.15.0 to 1.16.0 (#9681)
-- Dependency: Bump github.com/go-logr/logr from 1.2.4 to 1.3.0 (#9644)
-- Dependency: Bump github.com/google/go-cmp from 0.5.9 to 0.6.0 (#9562)
-- Dependency: Bump github.com/onsi/ginkgo/v2 from 2.13.0 to 2.13.1 (#9716)
-- Dependency: Bump github.com/onsi/gomega from 1.29.0 to 1.30.0 (#9717)
-- Dependency: Bump github.com/prometheus/client_golang from 1.16.0 to 1.17.0 (#9517)
-- Dependency: Bump github.com/spf13/cobra from 1.7.0 to 1.8.0 (#9679)
-- Dependency: Bump github.com/spf13/viper from 1.16.0 to 1.17.0 (#9561)
-- Dependency: Bump Go version to v1.20.11 (#9683)
-- Dependency: Bump go-github dependency to version v53 (#8995)
-- Dependency: Bump go.etcd.io/etcd/api/v3 from 3.5.9 to 3.5.10 (#9642)
-- Dependency: Bump go.etcd.io/etcd/client/v3 from 3.5.9 to 3.5.10 (#9646)
-- Dependency: Bump go.opentelemetry.io/* dependencies (#9598)
-- Dependency: Bump golang.org/grpc to v1.59.0 (#9626)
-- Dependency: Bump golang.org/x/net from 0.15.0 to 0.17.0 in /test (#9537)
-- Dependency: Bump golang.org/x/oauth2 from 0.13.0 to 0.14.0 (#9715)
-- Dependency: Bump golang.org/x/text from 0.13.0 to 0.14.0 (#9680)
-- Dependency: Bump golangci-lint to v1.55.2 (#9740)
-- Dependency: Bump gomodules.xyz/jsonpatch/v2 from 2.3.0 to 2.4.0 (#9188)
-- Dependency: Bump google.golang.org/api from 0.149.0 to 0.150.0 in /hack/tools (#9691)
-- Dependency: Bump google.golang.org/grpc from 1.58.2 to 1.58.3 (#9607)
-- Dependency: Bump kpromo to v4.0.4 (#9241)
-- Dependency: Bump opentelemetry to fix CVEs (#9709)
-- Dependency: Bump sigs.k8s.io/yaml from 1.3.0 to 1.4.0 (#9645)
-- Dependency: Bump some dependencies in Makefile (#9549)
-- Dependency: Bump the kubernetes group in /hack/tools with 2 updates (#9420)
-- Dependency: Bump the kubernetes group in /hack/tools with 2 updates (#9574)
-- Dependency: Bump the kubernetes group with 2 updates (#9606)
-- Dependency: Bump the kubernetes group with 4 updates (#9326)
-- Dependency: Bump the kubernetes group with 4 updates (#9455)
-- Dependency: Bump the kubernetes group with 4 updates (#9739)
-- Dependency: Bump Trivy to v0.47.0 (#9671)
-- Dependency: Replace hashicorp/go-multierror with kerrors (#9175)
-- Dependency: Update ensure-kubectl.sh to 1.28 (#9275)
-- Devtools: Add .PHONY for doctoc target (#9148)
-- Devtools: Allow non-provider Deployments in Tilt (#9404)
-- Devtools: Bump CAPI visualizer to v1.2.0 (#9195)
-- Devtools: Drop duplicate pprof and unused linter excludes (#9156)
-- Devtools: Improve Performance dashboard (#9387)
-- Devtools: Make dev cluster networking configurable (#9183)
-- Devtools: Makefile: run doctoc only once (#9182)
-- Devtools: Move tilt-prepare and log-push to tools/internal (#9020)
-- Devtools: Observability: move metrics to config and use sidecar in kube-state-metrics (#9390)
-- Devtools: Refactor docker-push* Makefile targets so users can control with ALL_DOCKER_BUILD which images are pushed (#8586)
-- Devtools: Tiltfile: rename deploy_kustomizations to additional_kustomizations (#9439)
-- e2e: Add back flavor to Waiter interface (#9166)
-- e2e: Add CRS re-reconcile to ownerReference test (#9296)
-- e2e: Add log level for kube components patch to ClusterClass (#9493)
-- e2e: Add MachinePools to Topology Quickstart E2E Templates (#9393)
-- e2e: Add MP timeouts to cluster upgrade tests (#9723)
-- e2e: Add test for ownerReference apiVersion update (#9269)
-- e2e: Add test for scale testing machinery (#9510)
-- e2e: Bump autoscaler to v1.28.0 (#9349)
-- e2e: Drop PR-Informing test tag and job description (#9362)
-- e2e: Dump all pods in e2e test clusters (#9441)
-- e2e: Dump all resource information for self-hosted tests (#9547)
-- e2e: Ensure finalizers are resilient on reconciliation (#9471)
-- e2e: Fail tests if test env version check fails (#9388)
-- e2e: Fix broken e2e test clusterclass (#9506)
-- e2e: Improve labels/annotations in CAPD test ClusterClass (#9469)
-- e2e: Improve logging for self-hosted e2e test (#9637)
-- e2e: Improve output of exec.KubectlApply (#9761)
-- e2e: Refactor e2e ownerRef test utils (#9313)
-- e2e: Test/conformance: add variables to overwrite parralelization of kubetest runs (#9667)
-- e2e: Test/e2e: structure resources by namespace/kind again (#9462)
-- e2e: Use existing value of `SKIP_RESOURCE_CLEANUP` if set in environment (#9152)
-- IPAM: Add age also to v1beta1 IPAM types (#9729)
-- IPAM: Add age column to kubectl output (#9521)
-- IPAM: Enforce IPAM import alias (#9730)
-- KCP: Controlplane: add a test case for syncMachines where the InfraMachine does not exist. (#8992)
-- KCP: Remove disableInPlacePropagation field in KCP controller (#9099)
-- KCP: Remove redundant GetRESTConfig in KCP Management.GetWorkloadCluster (#9448)
-- KCP: Support admin config for Kubeadm v1.29 (#9682)
-- Logging: Change default log level to 2 (#9093)
-- Logging: Fix patch errors not being logged (#9224)
-- Logging: Set controller-runtime logger for clusterctl (#9107)
-- MachinePool: Add MachinePool Builders (#9346)
-- MachinePool: Add util function to get MachinePool by label (#9219)
-- Metrics: Hack/observability: add capi_machine_status_certificatesexpirydate metric (#9084)
-- Metrics: Hack: bump kube-state-metrics and prometheus charts (#9352)
-- Release: Add additional blocks to release note generation (#9247)
-- Release: Add adilGhaffarDev to release-team owner alias (#9718)
-- Release: Add Release Team OWNERS file to docs/release folder (#9294)
-- Release: Avoid cd'ing into test folders when building Docker images (#9744)
-- Release: Bump kubebuilder tools to v0.4.2 (#9665)
-- Release: Bump PR title checker github action to v0.4.1 (#9650)
-- Release: Capitalize area prefix in release notes by default (#9614)
-- Release: Capitalized title in release notes (#9086)
-- Release: Clarify release team vs k8s/k8s-SIGs org membership (#9089)
-- Release: Exclude release trigger PRs from release notes (#9444)
-- Release: Format Community meeting area in release notes (#9648)
-- Release: Format MachineHealthCheck area in release notes (#9500)
-- Release: Give permissions for release notes tool to release team (#9563)
-- Release: Hack: add weekly update script for Slack (#9343)
-- Release: Improve multiple areas PRs with user friendly subs (#9071)
-- Release: Improve release notes formatting (#9337)
-- Release: Improve release speed (#9392)
-- Release: Improve release staging build speed (#9536)
-- Release: Prepare main branch for v1.6 development (#9097)
-- Release: Remove auto-labelling for clusterctl (#8990)
-- Release: Remove extra separator after title in release notes (#9605)
-- Release: Revert "Improve release staging build speed" (#9753)
-- Release: Update instructions checklist to generate release notes (#9443)
-- Release: Update kubebuilder-release-tools to v0.4.0 (#9531)
-- Release: Update release-notes make target + corresponding doc (#9573)
-- Runtime SDK: Improve ClusterClass watch for ExtensionConfigs (#9338)
-- Testing: Add MachinePool test cases (#9474)
-- Testing: Add MachinePool test cases to engine tests (#9373)
-- Testing: Add MachinePool test cases to variables tests (#9372)
-- Testing: Add MachinePools to Runtime SDK and Rollout tests (#9703)
-- Testing: Add MachinePools to topology upgrade test (#9502)
-- Testing: Add test for required properties in clusterclass variables (#9113)
-- Testing: Add unit tests for CC MP blueprint, current_state, & desired_state (#9348)
-- Testing: Add unit tests for reconcile_state, cluster_controller, & conditions (#9380)
-- Testing: Add WaitForMachinePoolToBeUpgraded to self-hosted test (#9540)
-- Testing: Extend test/framework to collect workload cluster nodes (#9416)
-- Testing: Follow-up fixes: Add MachinePools to Runtime SDK and Rollout tests (#9719)
-- Testing: Replacing gomega.Equal with gomega.BeComparableTo (#9015)
-- util: Adjust naming in SortForCreate implementation (#9311)
-- util: Move `internal.labels` to `format` package for use by providers (#9002)
-- util: Refactor SortForCreate to use sort.Slice (#9251)
-- util: Remove previously deprecated code (#9136)
-
-:book: Additionally, there have been 55 contributions to our documentation and book. (#8260, #8500, #8678, #8819, #8988, #9001, #9013, #9014, #9024, #9029, #9080, #9081, #9087, #9112, #9119, #9141, #9146, #9150, #9161, #9173, #9208, #9209, #9213, #9214, #9232, #9270, #9286, #9291, #9305, #9328, #9364, #9386, #9403, #9415, #9429, #9433, #9463, #9487, #9488, #9490, #9511, #9513, #9514, #9527, #9550, #9559, #9565, #9572, #9577, #9590, #9593, #9613, #9635, #9654, #9706) 
-
-</details>
-<br/>
-
-_Thanks to all our contributors!_ 😊
diff --git a/Makefile b/Makefile
index 1c9c5ea84..89890bbff 100644
--- a/Makefile
+++ b/Makefile
@@ -801,13 +801,13 @@ docker-build-kubeadm-control-plane: ## Build the docker image for kubeadm contro
 
 .PHONY: docker-build-docker-infrastructure
 docker-build-docker-infrastructure: ## Build the docker image for docker infrastructure controller manager
-	DOCKER_BUILDKIT=1 docker build --build-arg builder_image=$(GO_CONTAINER_IMAGE) --build-arg goproxy=$(GOPROXY) --build-arg ARCH=$(ARCH) --build-arg ldflags="$(LDFLAGS)" . -t $(CAPD_CONTROLLER_IMG)-$(ARCH):$(TAG) --file $(CAPD_DIR)/Dockerfile
+	cd $(CAPD_DIR); DOCKER_BUILDKIT=1 docker build --build-arg builder_image=$(GO_CONTAINER_IMAGE) --build-arg goproxy=$(GOPROXY) --build-arg ARCH=$(ARCH) --build-arg ldflags="$(LDFLAGS)" ../../.. -t $(CAPD_CONTROLLER_IMG)-$(ARCH):$(TAG) --file Dockerfile
 	$(MAKE) set-manifest-image MANIFEST_IMG=$(CAPD_CONTROLLER_IMG)-$(ARCH) MANIFEST_TAG=$(TAG) TARGET_RESOURCE="$(CAPD_DIR)/config/default/manager_image_patch.yaml"
 	$(MAKE) set-manifest-pull-policy TARGET_RESOURCE="$(CAPD_DIR)/config/default/manager_pull_policy.yaml"
 
 .PHONY: docker-build-in-memory-infrastructure
 docker-build-in-memory-infrastructure: ## Build the docker image for in-memory infrastructure controller manager
-	DOCKER_BUILDKIT=1 docker build --build-arg builder_image=$(GO_CONTAINER_IMAGE) --build-arg goproxy=$(GOPROXY) --build-arg ARCH=$(ARCH) --build-arg ldflags="$(LDFLAGS)" . -t $(CAPIM_CONTROLLER_IMG)-$(ARCH):$(TAG) --file $(CAPIM_DIR)/Dockerfile
+	cd $(CAPIM_DIR); DOCKER_BUILDKIT=1 docker build --build-arg builder_image=$(GO_CONTAINER_IMAGE) --build-arg goproxy=$(GOPROXY) --build-arg ARCH=$(ARCH) --build-arg ldflags="$(LDFLAGS)" ../../.. -t $(CAPIM_CONTROLLER_IMG)-$(ARCH):$(TAG) --file Dockerfile
 	$(MAKE) set-manifest-image MANIFEST_IMG=$(CAPIM_CONTROLLER_IMG)-$(ARCH) MANIFEST_TAG=$(TAG) TARGET_RESOURCE="$(CAPIM_DIR)/config/default/manager_image_patch.yaml"
 	$(MAKE) set-manifest-pull-policy TARGET_RESOURCE="$(CAPIM_DIR)/config/default/manager_pull_policy.yaml"
 
@@ -1068,7 +1068,9 @@ release-binary: $(RELEASE_DIR)
 
 .PHONY: release-staging
 release-staging: ## Build and push container images to the staging bucket
-	REGISTRY=$(STAGING_REGISTRY) $(MAKE) docker-build-all docker-push-all release-alias-tag
+	REGISTRY=$(STAGING_REGISTRY) $(MAKE) docker-build-all
+	REGISTRY=$(STAGING_REGISTRY) $(MAKE) docker-push-all
+	REGISTRY=$(STAGING_REGISTRY) $(MAKE) release-alias-tag
 
 .PHONY: release-staging-nightly
 release-staging-nightly: ## Tag and push container images to the staging bucket. Example image tag: cluster-api-controller:nightly_main_20210121
@@ -1098,15 +1100,11 @@ release-alias-tag: ## Add the release alias tag to the last build tag
 
 .PHONY: release-notes-tool
 release-notes-tool:
-	go build -o bin/notes -tags tools sigs.k8s.io/cluster-api/hack/tools/release/notes
-
-.PHONY: test-release-notes-tool
-test-release-notes-tool:
-	go test -v -tags tools,integration sigs.k8s.io/cluster-api/hack/tools/release/notes
+	go build -o bin/notes hack/tools/release/notes/main.go
 
 .PHONY: release-weekly-update-tool
 release-weekly-update-tool:
-	go build -o bin/weekly -tags tools sigs.k8s.io/cluster-api/hack/tools/release/weekly
+	go build -o bin/weekly hack/tools/release/weekly/main.go
 
 .PHONY: promote-images
 promote-images: $(KPROMO)
diff --git a/Tiltfile b/Tiltfile
index 1ee422acd..2ddd3140c 100644
--- a/Tiltfile
+++ b/Tiltfile
@@ -382,8 +382,7 @@ def enable_provider(name, debug):
 
 def find_object_name(objs, kind):
     for o in objs:
-        # Ignore objects that are not part of the provider, e.g. the ASO Deployment in CAPZ.
-        if o["kind"] == kind and "cluster.x-k8s.io/provider" in o["metadata"]["labels"]:
+        if o["kind"] == kind:
             return o["metadata"]["name"]
     return ""
 
diff --git a/api/v1beta1/machineset_types.go b/api/v1beta1/machineset_types.go
index f10e44f4f..843b343a4 100644
--- a/api/v1beta1/machineset_types.go
+++ b/api/v1beta1/machineset_types.go
@@ -41,22 +41,9 @@ type MachineSetSpec struct {
 
 	// Replicas is the number of desired replicas.
 	// This is a pointer to distinguish between explicit zero and unspecified.
-	//
-	// Defaults to:
-	// * if the Kubernetes autoscaler min size and max size annotations are set:
-	//   - if it's a new MachineSet, use min size
-	//   - if the replicas field of the old MachineSet is < min size, use min size
-	//   - if the replicas field of the old MachineSet is > max size, use max size
-	//   - if the replicas field of the old MachineSet is in the (min size, max size) range, keep the value from the oldMS
-	// * otherwise use 1
-	// Note: Defaulting will be run whenever the replicas field is not set:
-	// * A new MachineSet is created with replicas not set.
-	// * On an existing MachineSet the replicas field was first set and is now unset.
-	// Those cases are especially relevant for the following Kubernetes autoscaler use cases:
-	// * A new MachineSet is created and replicas should be managed by the autoscaler
-	// * An existing MachineSet which initially wasn't controlled by the autoscaler
-	//   should be later controlled by the autoscaler
+	// Defaults to 1.
 	// +optional
+	// +kubebuilder:default=1
 	Replicas *int32 `json:"replicas,omitempty"`
 
 	// MinReadySeconds is the minimum number of seconds for which a Node for a newly created machine should be ready before considering the replica available.
diff --git a/api/v1beta1/zz_generated.openapi.go b/api/v1beta1/zz_generated.openapi.go
index 073de884b..d49f8303a 100644
--- a/api/v1beta1/zz_generated.openapi.go
+++ b/api/v1beta1/zz_generated.openapi.go
@@ -2877,7 +2877,7 @@ func schema_sigsk8sio_cluster_api_api_v1beta1_MachineSetSpec(ref common.Referenc
 					},
 					"replicas": {
 						SchemaProps: spec.SchemaProps{
-							Description: "Replicas is the number of desired replicas. This is a pointer to distinguish between explicit zero and unspecified.\n\nDefaults to: * if the Kubernetes autoscaler min size and max size annotations are set:\n  - if it's a new MachineSet, use min size\n  - if the replicas field of the old MachineSet is < min size, use min size\n  - if the replicas field of the old MachineSet is > max size, use max size\n  - if the replicas field of the old MachineSet is in the (min size, max size) range, keep the value from the oldMS\n* otherwise use 1 Note: Defaulting will be run whenever the replicas field is not set: * A new MachineSet is created with replicas not set. * On an existing MachineSet the replicas field was first set and is now unset. Those cases are especially relevant for the following Kubernetes autoscaler use cases: * A new MachineSet is created and replicas should be managed by the autoscaler * An existing MachineSet which initially wasn't controlled by the autoscaler\n  should be later controlled by the autoscaler",
+							Description: "Replicas is the number of desired replicas. This is a pointer to distinguish between explicit zero and unspecified. Defaults to 1.",
 							Type:        []string{"integer"},
 							Format:      "int32",
 						},
diff --git a/cloudbuild.yaml b/cloudbuild.yaml
index 66e341880..1ef17030d 100644
--- a/cloudbuild.yaml
+++ b/cloudbuild.yaml
@@ -12,10 +12,9 @@ steps:
     - TAG=$_GIT_TAG
     - PULL_BASE_REF=$_PULL_BASE_REF
     - DOCKER_BUILDKIT=1
-    args:
-    - release-staging
+    args: ['release-staging', '-j', '8', '-O']
 substitutions:
   # _GIT_TAG will be filled with a git-based tag for the image, of the form vYYYYMMDD-hash, and
   # can be used as a substitution
   _GIT_TAG: '12345'
-  _PULL_BASE_REF: 'dev'
+  _PULL_BASE_REF: 'dev'
\ No newline at end of file
diff --git a/cmd/clusterctl/client/cluster/objectgraph.go b/cmd/clusterctl/client/cluster/objectgraph.go
index 454ec27a8..360ca8999 100644
--- a/cmd/clusterctl/client/cluster/objectgraph.go
+++ b/cmd/clusterctl/client/cluster/objectgraph.go
@@ -439,7 +439,7 @@ func (o *objectGraph) Discovery(ctx context.Context, namespace string) error {
 		}
 
 		// if we are discovering Secrets, also secrets from the providers namespace should be included.
-		if discoveryType.typeMeta.GetObjectKind().GroupVersionKind().GroupKind() == corev1.SchemeGroupVersion.WithKind("Secret").GroupKind() {
+		if discoveryType.typeMeta.GetObjectKind().GroupVersionKind().GroupKind() == corev1.SchemeGroupVersion.WithKind("SecretList").GroupKind() {
 			providers, err := o.providerInventory.List(ctx)
 			if err != nil {
 				return err
diff --git a/cmd/clusterctl/cmd/topology_plan.go b/cmd/clusterctl/cmd/topology_plan.go
index 0fe62a1ab..cec055946 100644
--- a/cmd/clusterctl/cmd/topology_plan.go
+++ b/cmd/clusterctl/cmd/topology_plan.go
@@ -18,7 +18,6 @@ package cmd
 
 import (
 	"context"
-	"errors"
 	"fmt"
 	"io"
 	"os"
@@ -29,7 +28,7 @@ import (
 	"strings"
 
 	"github.com/olekukonko/tablewriter"
-	pkgerrors "github.com/pkg/errors"
+	"github.com/pkg/errors"
 	"github.com/spf13/cobra"
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 	"k8s.io/utils/exec"
@@ -122,11 +121,11 @@ func runTopologyPlan() error {
 	for _, f := range tp.files {
 		raw, err := os.ReadFile(f) //nolint:gosec
 		if err != nil {
-			return pkgerrors.Wrapf(err, "failed to read input file %q", f)
+			return errors.Wrapf(err, "failed to read input file %q", f)
 		}
 		objects, err := utilyaml.ToUnstructured(raw)
 		if err != nil {
-			return pkgerrors.Wrapf(err, "failed to convert file %q to list of objects", f)
+			return errors.Wrapf(err, "failed to convert file %q to list of objects", f)
 		}
 		objs = append(objs, objects...)
 	}
@@ -155,7 +154,7 @@ func printTopologyPlanOutput(out *cluster.TopologyPlanOutput, outdir string) err
 	} else {
 		printChangeSummary(out)
 		if err := writeOutputFiles(out, outdir); err != nil {
-			return pkgerrors.Wrap(err, "failed to write output files of target cluster changes")
+			return errors.Wrap(err, "failed to write output files of target cluster changes")
 		}
 	}
 	fmt.Printf("\n")
@@ -234,17 +233,17 @@ func writeOutputFiles(out *cluster.TopologyPlanOutput, outDir string) error {
 	// Write created files
 	createdDir := path.Join(outDir, "created")
 	if err := os.MkdirAll(createdDir, 0750); err != nil {
-		return pkgerrors.Wrapf(err, "failed to create %q directory", createdDir)
+		return errors.Wrapf(err, "failed to create %q directory", createdDir)
 	}
 	for _, c := range out.Created {
 		yaml, err := utilyaml.FromUnstructured([]unstructured.Unstructured{*c})
 		if err != nil {
-			return pkgerrors.Wrap(err, "failed to convert object to yaml")
+			return errors.Wrap(err, "failed to convert object to yaml")
 		}
 		fileName := fmt.Sprintf("%s_%s_%s.yaml", c.GetKind(), c.GetNamespace(), c.GetName())
 		filePath := path.Join(createdDir, fileName)
 		if err := os.WriteFile(filePath, yaml, 0600); err != nil {
-			return pkgerrors.Wrapf(err, "failed to write yaml to file %q", filePath)
+			return errors.Wrapf(err, "failed to write yaml to file %q", filePath)
 		}
 	}
 	if len(out.Created) != 0 {
@@ -254,33 +253,33 @@ func writeOutputFiles(out *cluster.TopologyPlanOutput, outDir string) error {
 	// Write modified files
 	modifiedDir := path.Join(outDir, "modified")
 	if err := os.MkdirAll(modifiedDir, 0750); err != nil {
-		return pkgerrors.Wrapf(err, "failed to create %q directory", modifiedDir)
+		return errors.Wrapf(err, "failed to create %q directory", modifiedDir)
 	}
 	for _, m := range out.Modified {
 		// Write the modified object to file.
 		fileNameModified := fmt.Sprintf("%s_%s_%s.modified.yaml", m.After.GetKind(), m.After.GetNamespace(), m.After.GetName())
 		filePathModified := path.Join(modifiedDir, fileNameModified)
 		if err := writeObjectToFile(filePathModified, m.After); err != nil {
-			return pkgerrors.Wrap(err, "failed to write modified object to file")
+			return errors.Wrap(err, "failed to write modified object to file")
 		}
 
 		// Write the original object to file.
 		fileNameOriginal := fmt.Sprintf("%s_%s_%s.original.yaml", m.Before.GetKind(), m.Before.GetNamespace(), m.Before.GetName())
 		filePathOriginal := path.Join(modifiedDir, fileNameOriginal)
 		if err := writeObjectToFile(filePathOriginal, m.Before); err != nil {
-			return pkgerrors.Wrap(err, "failed to write original object to file")
+			return errors.Wrap(err, "failed to write original object to file")
 		}
 
 		// Calculate the jsonpatch and write to a file.
 		patch := crclient.MergeFrom(m.Before)
 		jsonPatch, err := patch.Data(m.After)
 		if err != nil {
-			return pkgerrors.Wrapf(err, "failed to calculate jsonpatch of modified object %s/%s", m.After.GetNamespace(), m.After.GetName())
+			return errors.Wrapf(err, "failed to calculate jsonpatch of modified object %s/%s", m.After.GetNamespace(), m.After.GetName())
 		}
 		patchFileName := fmt.Sprintf("%s_%s_%s.jsonpatch", m.After.GetKind(), m.After.GetNamespace(), m.After.GetName())
 		patchFilePath := path.Join(modifiedDir, patchFileName)
 		if err := os.WriteFile(patchFilePath, jsonPatch, 0600); err != nil {
-			return pkgerrors.Wrapf(err, "failed to write jsonpatch to file %q", patchFilePath)
+			return errors.Wrapf(err, "failed to write jsonpatch to file %q", patchFilePath)
 		}
 
 		// Calculate the diff and write to a file.
@@ -288,10 +287,10 @@ func writeOutputFiles(out *cluster.TopologyPlanOutput, outDir string) error {
 		diffFilePath := path.Join(modifiedDir, diffFileName)
 		diffFile, err := os.OpenFile(filepath.Clean(diffFilePath), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
 		if err != nil {
-			return pkgerrors.Wrapf(err, "unable to open file %q", diffFilePath)
+			return errors.Wrapf(err, "unable to open file %q", diffFilePath)
 		}
 		if err := writeDiffToFile(filePathOriginal, filePathModified, diffFile); err != nil {
-			return pkgerrors.Wrapf(err, "failed to write diff to file %q", diffFilePath)
+			return errors.Wrapf(err, "failed to write diff to file %q", diffFilePath)
 		}
 	}
 	if len(out.Modified) != 0 {
@@ -304,10 +303,10 @@ func writeOutputFiles(out *cluster.TopologyPlanOutput, outDir string) error {
 func writeObjectToFile(filePath string, obj *unstructured.Unstructured) error {
 	yaml, err := utilyaml.FromUnstructured([]unstructured.Unstructured{*obj})
 	if err != nil {
-		return pkgerrors.Wrap(err, "failed to convert object to yaml")
+		return errors.Wrap(err, "failed to convert object to yaml")
 	}
 	if err := os.WriteFile(filePath, yaml, 0600); err != nil {
-		return pkgerrors.Wrapf(err, "failed to write yaml to file %q", filePath)
+		return errors.Wrapf(err, "failed to write yaml to file %q", filePath)
 	}
 	return nil
 }
@@ -349,7 +348,7 @@ func writeDiffToFile(from, to string, out io.Writer) error {
 	cmd.SetStdout(out)
 
 	if err := cmd.Run(); err != nil && !isDiffError(err) {
-		return pkgerrors.Wrapf(err, "failed to run %q", diff)
+		return errors.Wrapf(err, "failed to run %q", diff)
 	}
 	return nil
 }
@@ -383,8 +382,7 @@ func getDiffCommand(args ...string) (string, exec.Cmd) {
 // This makes use of the exit code of diff programs which is 0 for no diff, 1 for
 // modified and 2 for other errors.
 func isDiffError(err error) bool {
-	var exitErr exec.ExitError
-	if errors.As(err, &exitErr) && exitErr.ExitStatus() <= 1 {
+	if err, ok := err.(exec.ExitError); ok && err.ExitStatus() <= 1 {
 		return true
 	}
 	return false
diff --git a/config/crd/bases/cluster.x-k8s.io_machinesets.yaml b/config/crd/bases/cluster.x-k8s.io_machinesets.yaml
index e01642f07..d670ba9ae 100644
--- a/config/crd/bases/cluster.x-k8s.io_machinesets.yaml
+++ b/config/crd/bases/cluster.x-k8s.io_machinesets.yaml
@@ -487,22 +487,10 @@ spec:
                 format: int32
                 type: integer
               replicas:
-                description: "Replicas is the number of desired replicas. This is
-                  a pointer to distinguish between explicit zero and unspecified.
-                  \n Defaults to: * if the Kubernetes autoscaler min size and max
-                  size annotations are set: - if it's a new MachineSet, use min size
-                  - if the replicas field of the old MachineSet is < min size, use
-                  min size - if the replicas field of the old MachineSet is > max
-                  size, use max size - if the replicas field of the old MachineSet
-                  is in the (min size, max size) range, keep the value from the oldMS
-                  * otherwise use 1 Note: Defaulting will be run whenever the replicas
-                  field is not set: * A new MachineSet is created with replicas not
-                  set. * On an existing MachineSet the replicas field was first set
-                  and is now unset. Those cases are especially relevant for the following
-                  Kubernetes autoscaler use cases: * A new MachineSet is created and
-                  replicas should be managed by the autoscaler * An existing MachineSet
-                  which initially wasn't controlled by the autoscaler should be later
-                  controlled by the autoscaler"
+                default: 1
+                description: Replicas is the number of desired replicas. This is a
+                  pointer to distinguish between explicit zero and unspecified. Defaults
+                  to 1.
                 format: int32
                 type: integer
               selector:
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 0e2fa201d..b0b82fe56 100644
--- a/config/crd/bases/ipam.cluster.x-k8s.io_ipaddressclaims.yaml
+++ b/config/crd/bases/ipam.cluster.x-k8s.io_ipaddressclaims.yaml
@@ -146,10 +146,6 @@ spec:
       jsonPath: .spec.poolRef.kind
       name: Pool Kind
       type: string
-    - description: Time duration since creation of IPAdressClaim
-      jsonPath: .metadata.creationTimestamp
-      name: Age
-      type: date
     name: v1beta1
     schema:
       openAPIV3Schema:
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 68802c550..6496c8d75 100644
--- a/config/crd/bases/ipam.cluster.x-k8s.io_ipaddresses.yaml
+++ b/config/crd/bases/ipam.cluster.x-k8s.io_ipaddresses.yaml
@@ -117,10 +117,6 @@ spec:
       jsonPath: .spec.poolRef.kind
       name: Pool Kind
       type: string
-    - description: Time duration since creation of IPAdress
-      jsonPath: .metadata.creationTimestamp
-      name: Age
-      type: date
     name: v1beta1
     schema:
       openAPIV3Schema:
diff --git a/controlplane/kubeadm/internal/controllers/controller.go b/controlplane/kubeadm/internal/controllers/controller.go
index c6cb714b6..f71bdec07 100644
--- a/controlplane/kubeadm/internal/controllers/controller.go
+++ b/controlplane/kubeadm/internal/controllers/controller.go
@@ -375,7 +375,7 @@ func (r *KubeadmControlPlaneReconciler) reconcile(ctx context.Context, controlPl
 
 	// Aggregate the operational state of all the machines; while aggregating we are adding the
 	// source ref (reason@machine/name) so the problem can be easily tracked down to its source machine.
-	conditions.SetAggregate(controlPlane.KCP, controlplanev1.MachinesReadyCondition, controlPlane.Machines.ConditionGetters(), conditions.AddSourceRef())
+	conditions.SetAggregate(controlPlane.KCP, controlplanev1.MachinesReadyCondition, controlPlane.Machines.ConditionGetters(), conditions.AddSourceRef(), conditions.WithStepCounterIf(false))
 
 	// Updates conditions reporting the status of static pods and the status of the etcd cluster.
 	// NOTE: Conditions reporting KCP operation progress like e.g. Resized or SpecUpToDate are inlined with the rest of the execution.
@@ -528,7 +528,7 @@ func (r *KubeadmControlPlaneReconciler) reconcileDelete(ctx context.Context, con
 	// source ref (reason@machine/name) so the problem can be easily tracked down to its source machine.
 	// However, during delete we are hiding the counter (1 of x) because it does not make sense given that
 	// all the machines are deleted in parallel.
-	conditions.SetAggregate(controlPlane.KCP, controlplanev1.MachinesReadyCondition, controlPlane.Machines.ConditionGetters(), conditions.AddSourceRef())
+	conditions.SetAggregate(controlPlane.KCP, controlplanev1.MachinesReadyCondition, controlPlane.Machines.ConditionGetters(), conditions.AddSourceRef(), conditions.WithStepCounterIf(false))
 
 	// Gets all machines, not just control plane machines.
 	allMachines, err := r.managementCluster.GetMachinesForCluster(ctx, controlPlane.Cluster)
diff --git a/docs/book/src/introduction.md b/docs/book/src/introduction.md
index 12d1164fa..e45cb45b6 100644
--- a/docs/book/src/introduction.md
+++ b/docs/book/src/introduction.md
@@ -40,9 +40,8 @@ contributing guide for more details.
 
 <h1>ClusterAPI documentation versions</h1>
 
-This book documents ClusterAPI v1.6. For other Cluster API versions please see the corresponding documentation:
+This book documents ClusterAPI v1.5. For other Cluster API versions please see the corresponding documentation:
 * [main.cluster-api.sigs.k8s.io](https://main.cluster-api.sigs.k8s.io)
-* [release-1-5.cluster-api.sigs.k8s.io](https://release-1-5.cluster-api.sigs.k8s.io)
 * [release-1-4.cluster-api.sigs.k8s.io](https://release-1-4.cluster-api.sigs.k8s.io)
 * [release-1-3.cluster-api.sigs.k8s.io](https://release-1-3.cluster-api.sigs.k8s.io)
 * [release-1-2.cluster-api.sigs.k8s.io](https://release-1-2.cluster-api.sigs.k8s.io)
diff --git a/docs/book/src/tasks/automated-machine-management/autoscaling.md b/docs/book/src/tasks/automated-machine-management/autoscaling.md
index 442152b71..ca8dbca37 100644
--- a/docs/book/src/tasks/automated-machine-management/autoscaling.md
+++ b/docs/book/src/tasks/automated-machine-management/autoscaling.md
@@ -12,17 +12,17 @@ from the [Autoscaler project documentation](https://github.com/kubernetes/autosc
 
 <aside class="note warning">
 
-<h1>Defaulting of the MachineDeployment, MachineSet replicas field</h1>
+<h1>Defaulting of the MachineDeployment replicas field</h1>
 
-Please note that the MachineDeployment and MachineSet replicas field has special defaulting logic to provide a smooth integration with the autoscaler.
-The replica field is defaulted based on the autoscaler min and max size annotations.The goal is to pick a default value which is inside
+Please note that the MachineDeployment replicas field has special defaulting logic to provide a smooth integration with the autoscaler.
+The replica field is defaulted based on the autoscaler min and max size annotations.The goal is to pick a default value which is inside 
 the (min size, max size) range so the autoscaler can take control of the replicase field.
 
 The defaulting logic is as follows:
 * if the autoscaler min size and max size annotations are set:
-  * if it's a new MachineDeployment or MachineSet, use min size
-  * if the replicas field of the old MachineDeployment or MachineSet is < min size, use min size
-  * if the replicas field of the old MachineDeployment or MachineSet is > max size, use max size
-  * if the replicas field of the old MachineDeployment or MachineSet is in the (min size, max size) range, keep the value from the oldMD or oldMS
+  * if it's a new MachineDeployment, use min size
+  * if the replicas field of the old MachineDeployment is < min size, use min size
+  * if the replicas field of the old MachineDeployment is > max size, use max size
+  * if the replicas field of the old MachineDeployment is in the (min size, max size) range, keep the value from the oldMD
 * otherwise, use 1
 </aside>
diff --git a/docs/book/src/tasks/bootstrap/microk8s-bootstrap.md b/docs/book/src/tasks/bootstrap/microk8s-bootstrap.md
index 5dfc80503..65734b5fe 100644
--- a/docs/book/src/tasks/bootstrap/microk8s-bootstrap.md
+++ b/docs/book/src/tasks/bootstrap/microk8s-bootstrap.md
@@ -85,7 +85,7 @@ Some of the configuration options available via `MicroK8sConfig` are:
 
 ### How does CABPM work?
 
-The main purpose of the MicroK8s bootstrap provider is to translate the users needs to a number of cloud-init files applicable for each type of cluster nodes. There are three types of cloud-inits:
+The main purpose of the MicroK8s bootstrap provider is to translate the users needs to the a number of cloud-init files applicable for each type of cluster nodes. There are three types of cloud-inits:
 
   - The first node cloud-init. That node will be a control plane node and will be the one where the addons are enabled.
   - The control plane node cloud-init. The control plane nodes need to join a cluster and contribute to its HA.
diff --git a/docs/proposals/20230407-flexible-managed-k8s-endpoints.md b/docs/proposals/20230407-flexible-managed-k8s-endpoints.md
index 3ff08ba06..9b3e1b706 100644
--- a/docs/proposals/20230407-flexible-managed-k8s-endpoints.md
+++ b/docs/proposals/20230407-flexible-managed-k8s-endpoints.md
@@ -24,6 +24,16 @@
       - [Core Cluster API Controllers changes](#core-cluster-api-controllers-changes)
       - [Provider controller changes](#provider-controller-changes)
     - [Guidelines for infra providers implementation](#guidelines-for-infra-providers-implementation)
+  - [Background work](#background-work)
+    - [EKS in CAPA](#eks-in-capa)
+      - [AKS in CAPZ](#aks-in-capz)
+      - [GKE in CAPG](#gke-in-capg)
+      - [Learnings from original Proposal: Two kinds with a Managed Control Plane & Managed Infra Cluster adhering to the current CAPI contracts](#learnings-from-original-proposal-two-kinds-with-a-managed-control-plane--managed-infra-cluster-adhering-to-the-current-capi-contracts)
+    - [Two New Flows](#two-new-flows)
+      - [Flow 1: `<Infra>Cluster` and `<Infra>ControlPlane`, with `ControlPlaneEndpoint` reported via `<Infra>ControlPlane`](#flow-1-infracluster-and-infracontrolplane-with-controlplaneendpoint-reported-via-infracontrolplane)
+      - [Flow 2: Change CAPI to make `<Infra>Cluster` optional](#flow-2-change-capi-to-make-infracluster-optional)
+      - [Alternative Option: Introduce a new Managed Kubernetes provider type (with contract)](#alternative-option-introduce-a-new-managed-kubernetes-provider-type-with-contract)
+  - [Recommendations](#recommendations)
   - [Implementation History](#implementation-history)
 
 <!-- END doctoc generated TOC please keep comment here to allow auto update -->
diff --git a/docs/proposals/YYYYMMDD-template.md b/docs/proposals/YYYYMMDD-template.md
index 3f88ae61d..60efe124a 100644
--- a/docs/proposals/YYYYMMDD-template.md
+++ b/docs/proposals/YYYYMMDD-template.md
@@ -159,7 +159,7 @@ considerations for performance, reliability and security.
 - What are some important details that didn't come across above.
 - What are the caveats to the implementation?
 - Go in to as much detail as necessary here.
-- Talk about core concepts and how they relate.
+- Talk about core concepts and how they releate.
 
 ### Security Model
 
diff --git a/docs/release/release-tasks.md b/docs/release/release-tasks.md
index dd6fa9ee6..924c12eb8 100644
--- a/docs/release/release-tasks.md
+++ b/docs/release/release-tasks.md
@@ -15,29 +15,27 @@ This document details the responsibilities and tasks for each role in the releas
 - [Release Lead](#release-lead)
   - [Responsibilities](#responsibilities)
   - [Tasks](#tasks)
-    - [Finalize release schedule and team](#finalize-release-schedule-and-team)
+    - [Set a tentative release date for the minor release](#set-a-tentative-release-date-for-the-minor-release)
+    - [Assemble release team](#assemble-release-team)
     - [Add/remove release team members](#addremove-release-team-members)
+    - [Finalize release schedule and team](#finalize-release-schedule-and-team)
     - [Prepare main branch for development of the new release](#prepare-main-branch-for-development-of-the-new-release)
     - [Create a new GitHub milestone for the next release](#create-a-new-github-milestone-for-the-next-release)
     - [[Track] Remove previously deprecated code](#track-remove-previously-deprecated-code)
     - [[Track] Bump dependencies](#track-bump-dependencies)
-    - [Set a tentative release date for the next minor release](#set-a-tentative-release-date-for-the-next-minor-release)
-    - [Assemble next release team](#assemble-next-release-team)
-    - [Update milestone applier and GitHub Actions](#update-milestone-applier-and-github-actions)
+    - [Create a release branch](#create-a-release-branch)
     - [[Continuously] Maintain the GitHub release milestone](#continuously-maintain-the-github-release-milestone)
     - [[Continuously] Bump the Go version](#continuously-bump-the-go-version)
     - [[Repeatedly] Cut a release](#repeatedly-cut-a-release)
     - [[Optional] Public release session](#optional-public-release-session)
     - [[Optional] [Track] Bump the Cluster API apiVersion](#optional-track-bump-the-cluster-api-apiversion)
     - [[Optional] [Track] Bump the Kubernetes version](#optional-track-bump-the-kubernetes-version)
-    - [[Optional] Track Release and Improvement tasks](#optional-track-release-and-improvement-tasks)
 - [Communications/Docs/Release Notes Manager](#communicationsdocsrelease-notes-manager)
   - [Responsibilities](#responsibilities-1)
   - [Tasks](#tasks-1)
     - [Add docs to collect release notes for users and migration notes for provider implementers](#add-docs-to-collect-release-notes-for-users-and-migration-notes-for-provider-implementers)
     - [Update supported versions](#update-supported-versions)
     - [Ensure the book for the new release is available](#ensure-the-book-for-the-new-release-is-available)
-    - [Generate weekly PR updates to post in Slack](#generate-weekly-pr-updates-to-post-in-slack)
     - [Create PR for release notes](#create-pr-for-release-notes)
     - [Change production branch in Netlify to the new release branch](#change-production-branch-in-netlify-to-the-new-release-branch)
     - [Update clusterctl links in the quickstart](#update-clusterctl-links-in-the-quickstart)
@@ -50,6 +48,10 @@ This document details the responsibilities and tasks for each role in the releas
     - [[Continuously] Monitor CI signal](#continuously-monitor-ci-signal)
     - [[Continuously] Reduce the amount of flaky tests](#continuously-reduce-the-amount-of-flaky-tests)
     - [[Continuously] Bug triage](#continuously-bug-triage)
+- [Maintainer](#maintainer)
+  - [Responsibilities](#responsibilities-3)
+  - [Tasks](#tasks-3)
+    - [[Repeatedly] Publish the release](#repeatedly-publish-the-release)
 
 <!-- END doctoc generated TOC please keep comment here to allow auto update -->
 
@@ -63,9 +65,9 @@ This document details the responsibilities and tasks for each role in the releas
   * Create and maintain the GitHub release milestone
   * Track tasks needed to add support for new Kubernetes versions in upcoming releases
   * Ensure a retrospective happens
-  * Ensure one of the [maintainers](https://github.com/kubernetes-sigs/cluster-api/blob/main/OWNERS_ALIASES) is available when a release needs to be cut.
+  * Ensure a maintainer is available when a release needs to be cut.
 * Staffing:
-  * Assemble the release team for the next release cycle
+  * Assemble the release team for the current release cycle
   * Ensure a release lead for the next release cycle is selected and trained
   * Set a tentative release date for the next release cycle
 * Cutting releases:
@@ -75,18 +77,26 @@ This document details the responsibilities and tasks for each role in the releas
 
 ### Tasks
 
-#### Finalize release schedule and team
+#### Set a tentative release date for the minor release
 
-1. Finalize release schedule and team in the [docs/release/releases](../../docs/release/releases), e.g. [release-1.4.md](../../docs/release/releases/release-1.4.md).
-2. Update @cluster-api-release-team Slack user group and GitHub team accordingly.
-   <br>Prior art: https://github.com/kubernetes-sigs/cluster-api/issues/7476
-3. Update @cluster-api-release-lead and @cluster-api-release-team aliases in root OWNERS_ALIASES file with Release Team members.
-4. Announce the _release team_ and _release schedule_ to the mailing list.
+1. Set a tentative release date for the release and document it by creating a `release-1.4.md`.
+
+#### Assemble release team
+
+There is currently no formalized process to assemble the release team.
+As of now we ask for volunteers in Slack and office hours.
 
 #### Add/remove release team members
 
 If necessary, the release lead can adjust the release team during the cycle to handle unexpected changes in staffing due to personal/professional issues, no-shows, or unplanned work spikes. Adding/removing members can be done by opening a PR to update the release team members list for the release cycle in question.
 
+#### Finalize release schedule and team
+
+1. Finalize release schedule and team in the [docs/release/releases](../../docs/release/releases), e.g. [release-1.4.md](../../docs/release/releases/release-1.4.md).
+2. Update @cluster-api-release-team Slack user group and GitHub team accordingly.
+   <br>Prior art: https://github.com/kubernetes-sigs/cluster-api/issues/7476
+3. Announce the _release team_ and _release schedule_ to the mailing list.
+
 #### Prepare main branch for development of the new release
 
 The goal of this issue is to bump the versions on the main branch so that the upcoming release version
@@ -149,26 +159,17 @@ We should take a look at the following dependencies:
 * Go dependencies in `go.mod` files.
 * Tools used in our Makefile (e.g. kustomize).
 
-#### Set a tentative release date for the next minor release
-
-1. Set a tentative release date for the next minor release and document it by creating a `release-X.Y.md` in [docs/release/releases](../../docs/release/releases).
-   <br>Prior art: https://github.com/kubernetes-sigs/cluster-api/pull/9635
+#### Create a release branch
 
-#### Assemble next release team
-
-There is currently no formalized process to assemble the release team.
-As of now we ask for volunteers in Slack and office hours.
-
-#### Update milestone applier and GitHub Actions
-
-Once release branch is created by GitHub Automation, the goal of this task would be to ensure we have the milestone
-applier that applies milestones accordingly and to update GitHub actions to work with new release version.
+The goal of this task is to ensure we have a release branch and the milestone applier applies milestones accordingly.
 From this point forward changes which should land in the release have to be cherry-picked into the release branch.
+
+1. Ask the [Maintainer](#maintainer) to create a new release branch.
   
-1. Update the [milestone applier config](https://github.com/kubernetes/test-infra/blob/0b17ef5ffd6c7aa7d8ca1372d837acfb85f7bec6/config/prow/plugins.yaml#L371) accordingly (e.g. `release-1.4: v1.4` and `main: v1.5`)
+2. Update the [milestone applier config](https://github.com/kubernetes/test-infra/blob/0b17ef5ffd6c7aa7d8ca1372d837acfb85f7bec6/config/prow/plugins.yaml#L371) accordingly (e.g. `release-1.4: v1.4` and `main: v1.5`)
    <br>Prior art: [cluster-api: update milestone applier config for v1.3](https://github.com/kubernetes/test-infra/pull/26631)
 
-2. Update the GitHub Actions to work with the new release version.
+3. Update the GitHub Actions to work with the new release version.
    <br>Prior art: [Update actions for 1.5 and make names consistent](https://github.com/kubernetes-sigs/cluster-api/pull/9115)
 
 #### [Continuously] Maintain the GitHub release milestone
@@ -228,10 +229,7 @@ to a newer Go minor version according to our [backport policy](./../../CONTRIBUT
          ```
 
 4. Publish the release in GitHub:
-   1. Reach out to one of the maintainers over the Slack to publish the release in GitHub.
-      * **NOTE:** clusterctl will have issues installing providers between the time the release tag is cut and the Github release is published. See [issue 7889](https://github.com/kubernetes-sigs/cluster-api/issues/7889) for more details.
-      * The draft release should be automatically created via the [Create Release GitHub Action](https://github.com/kubernetes-sigs/cluster-api/actions/workflows/release.yaml) with release notes previously committed to the repo by the release team. Ensure by reminding the maintainer that release is flagged as `pre-release` for all `beta` and `rc` releases or `latest` for a new release in the most recent release branch.
-   
+   1. Ask the [Maintainer](#maintainer) to publish the release in GitHub.
 5. Publish `clusterctl` to Homebrew by bumping the version in [clusterctl.rb](https://github.com/Homebrew/homebrew-core/blob/master/Formula/c/clusterctl.rb).
    <br>**Notes**:
     * This is only done for new latest stable releases, not for beta / RC releases and not for previous release branches.
@@ -279,20 +277,6 @@ Additional information:
 1. Create an issue for the new Kubernetes version via: [New Issue: Kubernetes bump](https://github.com/kubernetes-sigs/cluster-api/issues/new/choose).
 2. Track the issue to ensure the work is completed in time.
 
-#### [Optional] Track Release and Improvement tasks
-
-1. Create an issue for easier tracking of all the tasks for the release cycle in question.
-   <br>Prior art: [Tasks for v1.6 release cycle](https://github.com/kubernetes-sigs/cluster-api/issues/9094)
-2. Create a release improvement tasks [GitHub Project Board](https://github.com/orgs/kubernetes-sigs/projects/55) to track 
-   the current status of all improvement tasks planned for the release, their priorities, status (i.e `Done`/`In Progress`)
-   and to distribute the work among the Release Team members.
-
-   **Notes**:
-   * At the beginning of the cycle, Release Team Lead should prepare the improvement tasks board for the ongoing release cycle.
-     The following steps can be taken:
-      - Edit improvement tasks board name for current cycle (e.g. `CAPI vX.Y release improvement tasks`)
-      - Add/move all individual missing issues to the board
-
 ## Communications/Docs/Release Notes Manager
 
 ### Responsibilities
@@ -329,8 +313,8 @@ The goal of this task to make the book for the current release available under e
 
 1. Add a DNS entry for the book of the new release (should be available under e.g. `https://release-1-4.cluster-api.sigs.k8s.io`).
    <br>Prior art: [Add DNS for CAPI release-1.2 release branch](https://github.com/kubernetes/k8s.io/pull/3872)
-2. Open `https://release-1-4.cluster-api.sigs.k8s.io/` and verify that the certificates are valid.  If they are not, talk to someone with access to Netlify, they have to [click the `renew certificate` button](https://app.netlify.com/sites/kubernetes-sigs-cluster-api/settings/domain#https) in the Netlify UI.
-    - To add new subdomains to the certificate config, checkout the email snippet [template](https://github.com/kubernetes-sigs/cluster-api/issues/6017#issuecomment-1823306891) for reference.
+2. Open `https://release-1-4.cluster-api.sigs.k8s.io/` and verify that the certificates are valid
+   If they are not, talk to someone with access to Netlify, they have to [click the `renew certificate` button](https://app.netlify.com/sites/kubernetes-sigs-cluster-api/settings/domain#https) in the Netlify UI.
 3. Update references in introduction.md only on the main branch (drop unsupported versions, add the new release version).
    <br>Prior art: [Add release 1.2 book link](https://github.com/kubernetes-sigs/cluster-api/pull/6697)
 
@@ -542,4 +526,25 @@ The goal of bug triage is to triage incoming issues and if necessary flag them w
 and add them to the milestone of the current release.
 
 We probably have to figure out some details about the overlap between the bug triage task here, release leads
-and Cluster API maintainers.
\ No newline at end of file
+and Cluster API maintainers.
+
+## Maintainer
+
+The Maintainer must be a person with write access to the Cluster API repo. They can hold another role in the release team. The Maintainer need only be involved in the release as required on days when releases are cut. They are not expected to take part in release team meetings or other activities, but should feel free to do so.
+
+### Responsibilities
+
+* Be available on release day in case the release team needs help with tag creation.
+* Publish the release.
+* Ensure a substitute is nominated if they are not available.
+
+### Tasks
+
+#### [Repeatedly] Publish the release
+
+**NOTE:** clusterctl will have issues installing providers between the time the release tag is cut and the Github release is published. See [issue 7889](https://github.com/kubernetes-sigs/cluster-api/issues/7889) for more details
+
+Publish the release.
+
+   1. The draft release should be automatically created via the [Create Release GitHub Action](https://github.com/kubernetes-sigs/cluster-api/actions/workflows/release.yml) with release notes previously committed to the repo by the release team.
+   2. Publish the release. Ensure release is flagged as `pre-release` for all `beta` and `rc` releases or `latest` for a new release in the most recent release branch.
diff --git a/docs/release/release-team.md b/docs/release/release-team.md
index 36bbef0ed..cf2422b16 100644
--- a/docs/release/release-team.md
+++ b/docs/release/release-team.md
@@ -12,7 +12,6 @@
   - [Team Selection](#team-selection)
     - [Selection Criteria](#selection-criteria)
   - [Time Commitment](#time-commitment)
-  - [Release Team/Day Meetings](#release-teamday-meetings)
   - [Suggestions for Team Leads](#suggestions-for-team-leads)
   - [Why should I volunteer?](#why-should-i-volunteer)
   - [Cluster API release team vs kubernetes/kubernetes-SIG membership](#cluster-api-release-team-vs-kuberneteskubernetes-sig-membership)
@@ -71,7 +70,8 @@ As noted above, making changes to  the CAPI release cadence is out of scope for
 - **Communications/Docs/Release Notes Manager**: Responsible for communicating key dates to the community, improving release process documentation, and polishing release notes. Also responsible for ensuring the user-facing Netlify book and provider upgrade documentation are up to date.
 - **CI Signal/Bug Triage/Automation Manager**: Assumes the responsibility of the quality gate for the release and makes sure blocking issues and bugs are triaged and dealt with in a timely fashion. Helps improve release automation and tools.
 - **Team member**: Any Release Team lead or manager may select one or more additional members to help with their tasks. These team members will help fulfill future Release Team staffing requirements and continue to grow the CAPI community in general.
-*Note*: This is also documented in [Release tasks](./release-tasks.md) together with a mapping to specific tasks.
+- **Maintainer**: Responsible for tasks which require write access to the Cluster API repo including creating release tags and creating a release branch. This role must be filled by someone on the [`cluster-api-maintainers` list](https://github.com/kubernetes-sigs/cluster-api/blob/main/OWNERS_ALIASES).
+*Note*: This is also documented in [Release tasks](./release-tasks.md) together with a mapping to specific tasks.  
 
 ## Team repo permissions
 - Release notes (`CHANGELOG` folder)
@@ -110,27 +110,9 @@ While we don't anticipate individuals to be available every week during the rele
 
 Before you volunteer to be part of a CAPI release team, please make certain that your employer is aware and supportive of your commitment to the release team.
 
-## Release Team/Day Meetings
-
-Release Team Members meet and share team specific updates, news and all release specific items in the Release Team Meetings.
-
-- Release Team Meetings happen once a week every Wednesday, half an hour before office hours using the CAPI meeting zoom [link](https://zoom.us/j/861487554?pwd=dTVGVVFCblFJc0VBbkFqQlU0dHpiUT09). 
-- Release Team Meeting notes can be found [here](https://docs.google.com/document/d/1AUiuvapS3ldYVJfKucDhIoH6IJIPS009jqwnSTwS0EI).
-- Reach out to maintainers to get the zoom meeting host key to be able to share the screen when office hours zoom link is used.
-
-*Note:* For now, we don't have a calendar invite for Release Team Meetings to be sent out to all Release Team Members. Create a recurring calendar invite for the period of whole release cycle and send it to all Release Team Members.
-
-Release Day meetings is used to cut the releases as a group following the release cycle timeline.
-
-- Release Day Meetings happen on the release date specified in the release timeline document, using the CAPI meeting zoom [link](https://zoom.us/j/861487554?pwd=dTVGVVFCblFJc0VBbkFqQlU0dHpiUT09) at the time depending on the Release Team Members timezone and availability.
-
 ## Suggestions for Team Leads
 
   * In the first week of the release cycle, organize an onboarding session with members of your team (i.e CI Lead with CI team members) to go over the general responsibilities and expectations.
-  * Public communication should be default: all the Release Team specific topics, issues, discussion have to be public and discussed openly in the communication channels the Release Team uses. It gives visibility on the work being done, it is inclusive and track of record. All other communication
-  within the Release Team Members can de carried out using a private group/chat.
-  * For Release Lead: Inform Release Team Members about the upcoming release, a day prior to the actual release cutting date over a common communication
-  channel (usually Cluster API Slack) and ask for team specific updates from Team Leads (i.e status of CI signal from CI Team Lead or preparing a PR for release notes with new desired tag in advance) to ensure smoother release cutting process and avoid unexpected surprises. 
   * Clearly communicate with the team members you are responsible for, that the majority of the work during the release cycle will be a collaborative effort.
   * Establish an ownership rotation policy in consultation with respective team members.
   * Provide opportunities for team members to take the lead in cutting a release within the cycle, based on feasibility.
diff --git a/docs/release/releases/release-1.6.md b/docs/release/releases/release-1.6.md
index 9ed95b04a..61d2987b1 100644
--- a/docs/release/releases/release-1.6.md
+++ b/docs/release/releases/release-1.6.md
@@ -16,13 +16,13 @@ The following table shows the preliminary dates for the `v1.6` release cycle.
 | *v1.4.x & v1.5.x released*                           | Release Lead | Tuesday 24th October 2023   | week 13  |
 | v1.6.0-beta.x released                               | Release Lead | Tuesday 31st October 2023   | week 14  |
 | KubeCon idle week                                    |      -       | 6th - 10th November 2023    | week 15  |
-| release-1.6 branch created (**Begin [Code Freeze]**) | Release Lead | Tuesday 21st November 2023  | week 17  |
-| v1.6.0-rc.0 released                                 | Release Lead | Tuesday 21st November 2023  | week 17  |
-| release-1.6 jobs created                             | CI Manager   | Tuesday 21st November 2023  | week 17  |
-| v1.6.0-rc.x released                                 | Release Lead | Tuesday 28th November 2023  | week 18  |
-| **v1.6.0 released**                                  | Release Lead | Tuesday 5th December 2023   | week 19  |
-| *v1.4.x & v1.5.x released*                           | Release Lead | Tuesday 5th December 2023   | week 19  |
-| Organize release retrospective                       | Release Lead | TBC                         | week 19  |
+| release-1.6 branch created (**Begin [Code Freeze]**) | Release Lead | Tuesday 14th November 2023  | week 16  |
+| v1.6.0-rc.0 released                                 | Release Lead | Tuesday 14th November 2023  | week 16  |
+| release-1.6 jobs created                             | CI Manager   | Tuesday 14th November 2023  | week 16  |
+| v1.6.0-rc.x released                                 | Release Lead | Tuesday 21st November 2023  | week 17  |
+| **v1.6.0 released**                                  | Release Lead | Tuesday 28th November 2023  | week 18  |
+| *v1.4.x & v1.5.x released*                           | Release Lead | Tuesday 28th November 2023  | week 18  |
+| Organize release retrospective                       | Release Lead | TBC                         | week 18  |
 
 After the `.0` release monthly patch release will be created.
 
diff --git a/docs/release/releases/release-1.7.md b/docs/release/releases/release-1.7.md
index 7c7f1a968..62e434d5f 100644
--- a/docs/release/releases/release-1.7.md
+++ b/docs/release/releases/release-1.7.md
@@ -6,22 +6,22 @@ The following table shows the preliminary dates for the `v1.7` release cycle.
 
 | **What**                                             | **Who**      | **When**                    | **Week** |
 |------------------------------------------------------|--------------|-----------------------------|----------|
-| Start of Release Cycle                               | Release Lead | Monday 11th December 2023   | week 1   |
-| Schedule finalized                                   | Release Lead | Friday 15th December 2023   | week 1   |
-| Team finalized                                       | Release Lead | Friday 15th December 2023   | week 1   |
-| *v1.5.x & v1.6.x released*                           | Release Lead | Tuesday 16th January 2024   | week 6   |
-| *v1.5.x & v1.6.x released*                           | Release Lead | Tuesday 13th February 2024  | week 10  |
-| v1.7.0-beta.0 released                               | Release Lead | Tuesday 12th March 2024     | week 14  |
-| Communicate beta to providers                        | Comms Manager| Tuesday 12th March 2024     | week 14  |
-| *v1.5.x & v1.6.x released*                           | Release Lead | Tuesday 12th March 2024     | week 14  |
-| KubeCon idle week                                    |      -       | 19th - 22nd March 2024      | week 15  |
-| v1.7.0-beta.x released                               | Release Lead | Tuesday 26th March 2024     | week 16  |
-| release-1.7 branch created (**Begin [Code Freeze]**) | Release Lead | Tuesday 2nd April 2024      | week 17  |
-| v1.7.0-rc.0 released                                 | Release Lead | Tuesday 2nd April 2024      | week 17  |
-| release-1.7 jobs created                             | CI Manager   | Tuesday 2nd April 2024      | week 17  |
-| v1.7.0-rc.x released                                 | Release Lead | Tuesday 9th April 2024      | week 18  |
-| **v1.7.0 released**                                  | Release Lead | Tuesday 16th April 2024     | week 19  |
-| *v1.5.x & v1.6.x released*                           | Release Lead | Tuesday 16th April 2024     | week 19  |
+| Start of Release Cycle                               | Release Lead | Monday 4th December 2023    | week 1   |
+| Schedule finalized                                   | Release Lead | Friday 8th December 2023    | week 1   |
+| Team finalized                                       | Release Lead | Friday 8th December 2023    | week 1   |
+| *v1.5.x & v1.6.x released*                           | Release Lead | Tuesday 9th January 2024    | week 6   |
+| *v1.5.x & v1.6.x released*                           | Release Lead | Tuesday 6th February 2024   | week 10  |
+| v1.7.0-beta.0 released                               | Release Lead | Tuesday 5th March 2024      | week 14  |
+| Communicate beta to providers                        | Comms Manager| Tuesday 5th March 2024      | week 14  |
+| *v1.5.x & v1.6.x released*                           | Release Lead | Tuesday 5th March 2024      | week 14  |
+| v1.7.0-beta.x released                               | Release Lead | Tuesday 12th March 2024     | week 15  |
+| KubeCon idle week                                    |      -       | 19th - 22nd March 2024      | week 16  |
+| release-1.7 branch created (**Begin [Code Freeze]**) | Release Lead | Tuesday 26th March 2024     | week 17  |
+| v1.7.0-rc.0 released                                 | Release Lead | Tuesday 26th March 2024     | week 17  |
+| release-1.7 jobs created                             | CI Manager   | Tuesday 26th March 2024     | week 17  |
+| v1.7.0-rc.x released                                 | Release Lead | Tuesday 2nd April 2024      | week 18  |
+| **v1.7.0 released**                                  | Release Lead | Tuesday 9th April 2024      | week 19  |
+| *v1.5.x & v1.6.x released*                           | Release Lead | Tuesday 9th April 2024      | week 19  |
 | Organize release retrospective                       | Release Lead | TBC                         | week 19  |
 
 After the `.0` release monthly patch release will be created.
@@ -32,4 +32,5 @@ After the `.0` release monthly patch release will be created.
 |-------------------------------------------|-------------------------------------------------------------------------------------------|----------------------------------------|
 | Release Lead                              | TBD | TBD                                    |
 | Communications/Docs/Release Notes Manager | TBD | TBD                                    |
-| CI Signal/Bug Triage/Automation Manager   | TBD | TBD                                    |
\ No newline at end of file
+| CI Signal/Bug Triage/Automation Manager   | TBD | TBD                                    |
+| Maintainer                                | TBD | TBD                                    |
\ No newline at end of file
diff --git a/exp/addons/internal/controllers/clusterresourceset_controller.go b/exp/addons/internal/controllers/clusterresourceset_controller.go
index 0eb41a6b4..3e30f5d75 100644
--- a/exp/addons/internal/controllers/clusterresourceset_controller.go
+++ b/exp/addons/internal/controllers/clusterresourceset_controller.go
@@ -166,9 +166,7 @@ func (r *ClusterResourceSetReconciler) Reconcile(ctx context.Context, req ctrl.R
 
 	// Requeue if ErrClusterLocked was returned for one of the clusters.
 	if errClusterLockedOccurred {
-		// Requeue after a minute to not end up in exponential delayed requeue which
-		// could take up to 16m40s.
-		return ctrl.Result{RequeueAfter: time.Minute}, nil
+		return ctrl.Result{Requeue: true}, nil
 	}
 
 	return ctrl.Result{}, nil
diff --git a/exp/ipam/api/v1alpha1/conversion.go b/exp/ipam/api/v1alpha1/conversion.go
index 72df082a5..3cc2a0d3a 100644
--- a/exp/ipam/api/v1alpha1/conversion.go
+++ b/exp/ipam/api/v1alpha1/conversion.go
@@ -19,53 +19,53 @@ package v1alpha1
 import (
 	"sigs.k8s.io/controller-runtime/pkg/conversion"
 
-	ipamv1 "sigs.k8s.io/cluster-api/exp/ipam/api/v1beta1"
+	ipamv1beta1 "sigs.k8s.io/cluster-api/exp/ipam/api/v1beta1"
 )
 
 func (src *IPAddress) ConvertTo(dstRaw conversion.Hub) error {
-	dst := dstRaw.(*ipamv1.IPAddress)
+	dst := dstRaw.(*ipamv1beta1.IPAddress)
 
 	return Convert_v1alpha1_IPAddress_To_v1beta1_IPAddress(src, dst, nil)
 }
 
 func (dst *IPAddress) ConvertFrom(srcRaw conversion.Hub) error {
-	src := srcRaw.(*ipamv1.IPAddress)
+	src := srcRaw.(*ipamv1beta1.IPAddress)
 
 	return Convert_v1beta1_IPAddress_To_v1alpha1_IPAddress(src, dst, nil)
 }
 
 func (src *IPAddressList) ConvertTo(dstRaw conversion.Hub) error {
-	dst := dstRaw.(*ipamv1.IPAddressList)
+	dst := dstRaw.(*ipamv1beta1.IPAddressList)
 
 	return Convert_v1alpha1_IPAddressList_To_v1beta1_IPAddressList(src, dst, nil)
 }
 
 func (dst *IPAddressList) ConvertFrom(srcRaw conversion.Hub) error {
-	src := srcRaw.(*ipamv1.IPAddressList)
+	src := srcRaw.(*ipamv1beta1.IPAddressList)
 
 	return Convert_v1beta1_IPAddressList_To_v1alpha1_IPAddressList(src, dst, nil)
 }
 
 func (src *IPAddressClaim) ConvertTo(dstRaw conversion.Hub) error {
-	dst := dstRaw.(*ipamv1.IPAddressClaim)
+	dst := dstRaw.(*ipamv1beta1.IPAddressClaim)
 
 	return Convert_v1alpha1_IPAddressClaim_To_v1beta1_IPAddressClaim(src, dst, nil)
 }
 
 func (dst *IPAddressClaim) ConvertFrom(srcRaw conversion.Hub) error {
-	src := srcRaw.(*ipamv1.IPAddressClaim)
+	src := srcRaw.(*ipamv1beta1.IPAddressClaim)
 
 	return Convert_v1beta1_IPAddressClaim_To_v1alpha1_IPAddressClaim(src, dst, nil)
 }
 
 func (src *IPAddressClaimList) ConvertTo(dstRaw conversion.Hub) error {
-	dst := dstRaw.(*ipamv1.IPAddressClaimList)
+	dst := dstRaw.(*ipamv1beta1.IPAddressClaimList)
 
 	return Convert_v1alpha1_IPAddressClaimList_To_v1beta1_IPAddressClaimList(src, dst, nil)
 }
 
 func (dst *IPAddressClaimList) ConvertFrom(srcRaw conversion.Hub) error {
-	src := srcRaw.(*ipamv1.IPAddressClaimList)
+	src := srcRaw.(*ipamv1beta1.IPAddressClaimList)
 
 	return Convert_v1beta1_IPAddressClaimList_To_v1alpha1_IPAddressClaimList(src, dst, nil)
 }
diff --git a/exp/ipam/api/v1beta1/ipaddress_types.go b/exp/ipam/api/v1beta1/ipaddress_types.go
index 4f37aad81..c14860cdc 100644
--- a/exp/ipam/api/v1beta1/ipaddress_types.go
+++ b/exp/ipam/api/v1beta1/ipaddress_types.go
@@ -46,7 +46,6 @@ type IPAddressSpec struct {
 // +kubebuilder:printcolumn:name="Address",type="string",JSONPath=".spec.address",description="Address"
 // +kubebuilder:printcolumn:name="Pool Name",type="string",JSONPath=".spec.poolRef.name",description="Name of the pool the address is from"
 // +kubebuilder:printcolumn:name="Pool Kind",type="string",JSONPath=".spec.poolRef.kind",description="Kind of the pool the address is from"
-// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="Time duration since creation of IPAdress"
 
 // IPAddress is the Schema for the ipaddress API.
 type IPAddress struct {
diff --git a/exp/ipam/api/v1beta1/ipaddressclaim_types.go b/exp/ipam/api/v1beta1/ipaddressclaim_types.go
index 8910e9118..fd8c7e424 100644
--- a/exp/ipam/api/v1beta1/ipaddressclaim_types.go
+++ b/exp/ipam/api/v1beta1/ipaddressclaim_types.go
@@ -46,7 +46,6 @@ type IPAddressClaimStatus struct {
 // +kubebuilder:storageversion
 // +kubebuilder:printcolumn:name="Pool Name",type="string",JSONPath=".spec.poolRef.name",description="Name of the pool to allocate an address from"
 // +kubebuilder:printcolumn:name="Pool Kind",type="string",JSONPath=".spec.poolRef.kind",description="Kind of the pool to allocate an address from"
-// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="Time duration since creation of IPAdressClaim"
 
 // IPAddressClaim is the Schema for the ipaddressclaim API.
 type IPAddressClaim struct {
diff --git a/go.mod b/go.mod
index 5fd1ce90d..6cca59021 100644
--- a/go.mod
+++ b/go.mod
@@ -35,16 +35,16 @@ require (
 	golang.org/x/text v0.14.0
 	gomodules.xyz/jsonpatch/v2 v2.4.0
 	google.golang.org/grpc v1.59.0
-	k8s.io/api v0.28.4
-	k8s.io/apiextensions-apiserver v0.28.4
-	k8s.io/apimachinery v0.28.4
-	k8s.io/apiserver v0.28.4
-	k8s.io/client-go v0.28.4
-	k8s.io/cluster-bootstrap v0.28.4
-	k8s.io/component-base v0.28.4
+	k8s.io/api v0.28.3
+	k8s.io/apiextensions-apiserver v0.28.3
+	k8s.io/apimachinery v0.28.3
+	k8s.io/apiserver v0.28.3
+	k8s.io/client-go v0.28.3
+	k8s.io/cluster-bootstrap v0.28.3
+	k8s.io/component-base v0.28.3
 	k8s.io/klog/v2 v2.100.1
 	k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9
-	k8s.io/kubectl v0.28.4
+	k8s.io/kubectl v0.28.3
 	k8s.io/utils v0.0.0-20230406110748-d93618cff8a2
 	sigs.k8s.io/controller-runtime v0.16.3
 	sigs.k8s.io/yaml v1.4.0
@@ -174,10 +174,10 @@ require (
 	gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
-	k8s.io/cli-runtime v0.28.4 // indirect
-	k8s.io/component-helpers v0.28.4 // indirect
-	k8s.io/kms v0.28.4 // indirect
-	k8s.io/metrics v0.28.4 // indirect
+	k8s.io/cli-runtime v0.28.3 // indirect
+	k8s.io/component-helpers v0.28.3 // indirect
+	k8s.io/kms v0.28.3 // indirect
+	k8s.io/metrics v0.28.3 // indirect
 	sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.1.2 // indirect
 	sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
 	sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3 // indirect
diff --git a/go.sum b/go.sum
index d8012dfe8..18a9bb3b0 100644
--- a/go.sum
+++ b/go.sum
@@ -1007,34 +1007,34 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
 honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
 honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
-k8s.io/api v0.28.4 h1:8ZBrLjwosLl/NYgv1P7EQLqoO8MGQApnbgH8tu3BMzY=
-k8s.io/api v0.28.4/go.mod h1:axWTGrY88s/5YE+JSt4uUi6NMM+gur1en2REMR7IRj0=
-k8s.io/apiextensions-apiserver v0.28.4 h1:AZpKY/7wQ8n+ZYDtNHbAJBb+N4AXXJvyZx6ww6yAJvU=
-k8s.io/apiextensions-apiserver v0.28.4/go.mod h1:pgQIZ1U8eJSMQcENew/0ShUTlePcSGFq6dxSxf2mwPM=
-k8s.io/apimachinery v0.28.4 h1:zOSJe1mc+GxuMnFzD4Z/U1wst50X28ZNsn5bhgIIao8=
-k8s.io/apimachinery v0.28.4/go.mod h1:wI37ncBvfAoswfq626yPTe6Bz1c22L7uaJ8dho83mgg=
-k8s.io/apiserver v0.28.4 h1:BJXlaQbAU/RXYX2lRz+E1oPe3G3TKlozMMCZWu5GMgg=
-k8s.io/apiserver v0.28.4/go.mod h1:Idq71oXugKZoVGUUL2wgBCTHbUR+FYTWa4rq9j4n23w=
-k8s.io/cli-runtime v0.28.4 h1:IW3aqSNFXiGDllJF4KVYM90YX4cXPGxuCxCVqCD8X+Q=
-k8s.io/cli-runtime v0.28.4/go.mod h1:MLGRB7LWTIYyYR3d/DOgtUC8ihsAPA3P8K8FDNIqJ0k=
-k8s.io/client-go v0.28.4 h1:Np5ocjlZcTrkyRJ3+T3PkXDpe4UpatQxj85+xjaD2wY=
-k8s.io/client-go v0.28.4/go.mod h1:0VDZFpgoZfelyP5Wqu0/r/TRYcLYuJ2U1KEeoaPa1N4=
-k8s.io/cluster-bootstrap v0.28.4 h1:4MKNy1Qd9QY7pl47rSMGIORF+tm3CUaqC1M8U9bjn4Q=
-k8s.io/cluster-bootstrap v0.28.4/go.mod h1:/c4ro/R4yf4EtJgFgFtvnHkbDOHwubeKJXh5R1c89Bc=
-k8s.io/component-base v0.28.4 h1:c/iQLWPdUgI90O+T9TeECg8o7N3YJTiuz2sKxILYcYo=
-k8s.io/component-base v0.28.4/go.mod h1:m9hR0uvqXDybiGL2nf/3Lf0MerAfQXzkfWhUY58JUbU=
-k8s.io/component-helpers v0.28.4 h1:+X9VXT5+jUsRdC26JyMZ8Fjfln7mSjgumafocE509C4=
-k8s.io/component-helpers v0.28.4/go.mod h1:8LzMalOQ0K10tkBJWBWq8h0HTI9HDPx4WT3QvTFn9Ro=
+k8s.io/api v0.28.3 h1:Gj1HtbSdB4P08C8rs9AR94MfSGpRhJgsS+GF9V26xMM=
+k8s.io/api v0.28.3/go.mod h1:MRCV/jr1dW87/qJnZ57U5Pak65LGmQVkKTzf3AtKFHc=
+k8s.io/apiextensions-apiserver v0.28.3 h1:Od7DEnhXHnHPZG+W9I97/fSQkVpVPQx2diy+2EtmY08=
+k8s.io/apiextensions-apiserver v0.28.3/go.mod h1:NE1XJZ4On0hS11aWWJUTNkmVB03j9LM7gJSisbRt8Lc=
+k8s.io/apimachinery v0.28.3 h1:B1wYx8txOaCQG0HmYF6nbpU8dg6HvA06x5tEffvOe7A=
+k8s.io/apimachinery v0.28.3/go.mod h1:uQTKmIqs+rAYaq+DFaoD2X7pcjLOqbQX2AOiO0nIpb8=
+k8s.io/apiserver v0.28.3 h1:8Ov47O1cMyeDzTXz0rwcfIIGAP/dP7L8rWbEljRcg5w=
+k8s.io/apiserver v0.28.3/go.mod h1:YIpM+9wngNAv8Ctt0rHG4vQuX/I5rvkEMtZtsxW2rNM=
+k8s.io/cli-runtime v0.28.3 h1:lvuJYVkwCqHEvpS6KuTZsUVwPePFjBfSGvuaLl2SxzA=
+k8s.io/cli-runtime v0.28.3/go.mod h1:jeX37ZPjIcENVuXDDTskG3+FnVuZms5D9omDXS/2Jjc=
+k8s.io/client-go v0.28.3 h1:2OqNb72ZuTZPKCl+4gTKvqao0AMOl9f3o2ijbAj3LI4=
+k8s.io/client-go v0.28.3/go.mod h1:LTykbBp9gsA7SwqirlCXBWtK0guzfhpoW4qSm7i9dxo=
+k8s.io/cluster-bootstrap v0.28.3 h1:hGK3mJsmVGGvRJ61nyQcYNR9g/IYax75TbJcylTmZts=
+k8s.io/cluster-bootstrap v0.28.3/go.mod h1:s1B3FTw713b9iw67yGFiVF3zCfw5obrZXWl3EMelvdg=
+k8s.io/component-base v0.28.3 h1:rDy68eHKxq/80RiMb2Ld/tbH8uAE75JdCqJyi6lXMzI=
+k8s.io/component-base v0.28.3/go.mod h1:fDJ6vpVNSk6cRo5wmDa6eKIG7UlIQkaFmZN2fYgIUD8=
+k8s.io/component-helpers v0.28.3 h1:te9ieTGzcztVktUs92X53P6BamAoP73MK0qQP0WmDqc=
+k8s.io/component-helpers v0.28.3/go.mod h1:oJR7I9ist5UAQ3y/CTdbw6CXxdMZ1Lw2Ua/EZEwnVLs=
 k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg=
 k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
-k8s.io/kms v0.28.4 h1:PMgY/3CQTWP9eIKmNQiTgjLIZ0ns6O+voagzD2/4mSg=
-k8s.io/kms v0.28.4/go.mod h1:HL4/lR/bhjAJPbqycKtfhWiKh1Sp21cpHOL8P4oo87w=
+k8s.io/kms v0.28.3 h1:jYwwAe96XELNjYWv1G4kNzizcFoZ50OOElvPansbw70=
+k8s.io/kms v0.28.3/go.mod h1:kSMjU2tg7vjqqoWVVCcmPmNZ/CofPsoTbSxAipCvZuE=
 k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 h1:LyMgNKD2P8Wn1iAwQU5OhxCKlKJy0sHc+PcDwFB24dQ=
 k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9/go.mod h1:wZK2AVp1uHCp4VamDVgBP2COHZjqD1T68Rf0CM3YjSM=
-k8s.io/kubectl v0.28.4 h1:gWpUXW/T7aFne+rchYeHkyB8eVDl5UZce8G4X//kjUQ=
-k8s.io/kubectl v0.28.4/go.mod h1:CKOccVx3l+3MmDbkXtIUtibq93nN2hkDR99XDCn7c/c=
-k8s.io/metrics v0.28.4 h1:u36fom9+6c8jX2sk8z58H0hFaIUfrPWbXIxN7GT2blk=
-k8s.io/metrics v0.28.4/go.mod h1:bBqAJxH20c7wAsTQxDXOlVqxGMdce49d7WNr1WeaLac=
+k8s.io/kubectl v0.28.3 h1:H1Peu1O3EbN9zHkJCcvhiJ4NUj6lb88sGPO5wrWIM6k=
+k8s.io/kubectl v0.28.3/go.mod h1:RDAudrth/2wQ3Sg46fbKKl4/g+XImzvbsSRZdP2RiyE=
+k8s.io/metrics v0.28.3 h1:w2s3kVi7HulXqCVDFkF4hN/OsL1tXTTb4Biif995h/g=
+k8s.io/metrics v0.28.3/go.mod h1:OZZ23AHFojPzU6r3xoHGRUcV3I9pauLua+07sAUbwLc=
 k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 h1:qY1Ad8PODbnymg2pRbkyMT/ylpTrCM8P2RJ0yroCyIk=
 k8s.io/utils v0.0.0-20230406110748-d93618cff8a2/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
 rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
diff --git a/hack/generate-doctoc.sh b/hack/generate-doctoc.sh
index d4b8ee40a..ddd26e3d6 100755
--- a/hack/generate-doctoc.sh
+++ b/hack/generate-doctoc.sh
@@ -28,4 +28,4 @@ if [[ -z "$(command -v doctoc)" ]]; then
   exit 0
 fi
 
-doctoc ./CONTRIBUTING.md docs/release/release-tasks.md docs/release/release-team-onboarding.md docs/release/release-team.md docs/scope-and-objectives.md docs/staging-use-cases.md docs/proposals
+doctoc ./CONTRIBUTING.md docs/release/release-tasks.md docs/release/release-team-onboarding.md docs/scope-and-objectives.md docs/staging-use-cases.md docs/proposals
diff --git a/hack/tools/go.mod b/hack/tools/go.mod
index 11271bca2..4f096fc9a 100644
--- a/hack/tools/go.mod
+++ b/hack/tools/go.mod
@@ -7,20 +7,18 @@ replace sigs.k8s.io/cluster-api => ../../
 replace sigs.k8s.io/cluster-api/test => ../../test
 
 require (
-	cloud.google.com/go/storage v1.35.1
+	cloud.google.com/go/storage v1.34.1
 	github.com/blang/semver/v4 v4.0.0
-	github.com/onsi/gomega v1.30.0
 	github.com/pkg/errors v0.9.1
 	github.com/spf13/pflag v1.0.5
 	github.com/valyala/fastjson v1.6.4
-	google.golang.org/api v0.151.0
-	k8s.io/api v0.28.4
-	k8s.io/apiextensions-apiserver v0.28.4
-	k8s.io/apimachinery v0.28.4
-	k8s.io/client-go v0.28.4
+	google.golang.org/api v0.150.0
+	k8s.io/api v0.28.3
+	k8s.io/apiextensions-apiserver v0.28.3
+	k8s.io/apimachinery v0.28.3
+	k8s.io/client-go v0.28.3
 	k8s.io/klog/v2 v2.100.1
 	k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9
-	k8s.io/kubectl v0.28.4
 	k8s.io/utils v0.0.0-20230406110748-d93618cff8a2
 	sigs.k8s.io/cluster-api v0.0.0-00010101000000-000000000000
 	sigs.k8s.io/cluster-api/test v0.0.0-00010101000000-000000000000
@@ -36,7 +34,6 @@ require (
 	cloud.google.com/go/compute v1.23.1 // indirect
 	cloud.google.com/go/compute/metadata v0.2.3 // indirect
 	cloud.google.com/go/iam v1.1.3 // indirect
-	github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
 	github.com/MakeNowJust/heredoc v1.0.0 // indirect
 	github.com/Masterminds/goutils v1.1.1 // indirect
 	github.com/Masterminds/semver/v3 v3.2.0 // indirect
@@ -48,10 +45,8 @@ require (
 	github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 // indirect
 	github.com/beorn7/perks v1.0.1 // indirect
 	github.com/cespare/xxhash/v2 v2.2.0 // indirect
-	github.com/chai2010/gettext-go v1.0.2 // indirect
 	github.com/cloudflare/circl v1.3.3 // indirect
 	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
-	github.com/daviddengcn/go-colortext v1.0.0 // indirect
 	github.com/distribution/reference v0.5.0 // indirect
 	github.com/docker/distribution v2.8.3+incompatible // indirect
 	github.com/docker/docker v24.0.7+incompatible // indirect
@@ -61,10 +56,7 @@ require (
 	github.com/emicklei/go-restful/v3 v3.11.0 // indirect
 	github.com/evanphx/json-patch v5.6.0+incompatible // indirect
 	github.com/evanphx/json-patch/v5 v5.7.0 // indirect
-	github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect
-	github.com/fatih/camelcase v1.0.0 // indirect
 	github.com/fsnotify/fsnotify v1.6.0 // indirect
-	github.com/fvbommel/sortorder v1.1.0 // indirect
 	github.com/go-errors/errors v1.4.2 // indirect
 	github.com/go-logr/logr v1.3.0 // indirect
 	github.com/go-openapi/jsonpointer v0.19.6 // indirect
@@ -74,7 +66,6 @@ require (
 	github.com/gogo/protobuf v1.3.2 // indirect
 	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
 	github.com/golang/protobuf v1.5.3 // indirect
-	github.com/google/btree v1.0.1 // indirect
 	github.com/google/cel-go v0.16.1 // indirect
 	github.com/google/gnostic-models v0.6.8 // indirect
 	github.com/google/go-cmp v0.6.0 // indirect
@@ -82,56 +73,41 @@ require (
 	github.com/google/go-querystring v1.1.0 // indirect
 	github.com/google/gofuzz v1.2.0 // indirect
 	github.com/google/s2a-go v0.1.7 // indirect
-	github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
 	github.com/google/uuid v1.4.0 // indirect
 	github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
 	github.com/googleapis/gax-go/v2 v2.12.0 // indirect
-	github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect
 	github.com/hashicorp/hcl v1.0.0 // indirect
 	github.com/huandu/xstrings v1.3.3 // indirect
 	github.com/imdario/mergo v0.3.13 // indirect
-	github.com/inconshreveable/mousetrap v1.1.0 // indirect
-	github.com/jonboulle/clockwork v0.2.2 // indirect
 	github.com/josharian/intern v1.0.0 // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
-	github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
-	github.com/lithammer/dedent v1.1.0 // indirect
 	github.com/magiconair/properties v1.8.7 // indirect
 	github.com/mailru/easyjson v0.7.7 // indirect
 	github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
 	github.com/mitchellh/copystructure v1.2.0 // indirect
-	github.com/mitchellh/go-wordwrap v1.0.1 // indirect
 	github.com/mitchellh/mapstructure v1.5.0 // indirect
 	github.com/mitchellh/reflectwalk v1.0.2 // indirect
-	github.com/moby/spdystream v0.2.0 // indirect
-	github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect
 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 	github.com/modern-go/reflect2 v1.0.2 // indirect
-	github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
 	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
-	github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
+	github.com/onsi/gomega v1.30.0 // indirect
 	github.com/opencontainers/go-digest v1.0.0 // indirect
 	github.com/opencontainers/image-spec v1.1.0-rc2 // indirect
 	github.com/pelletier/go-toml/v2 v2.1.0 // indirect
-	github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
 	github.com/prometheus/client_golang v1.17.0 // indirect
 	github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect
 	github.com/prometheus/common v0.44.0 // indirect
 	github.com/prometheus/procfs v0.11.1 // indirect
-	github.com/russross/blackfriday/v2 v2.1.0 // indirect
 	github.com/sagikazarmark/locafero v0.3.0 // indirect
 	github.com/sagikazarmark/slog-shim v0.1.0 // indirect
 	github.com/shopspring/decimal v1.3.1 // indirect
 	github.com/sourcegraph/conc v0.3.0 // indirect
 	github.com/spf13/afero v1.10.0 // indirect
 	github.com/spf13/cast v1.5.1 // indirect
-	github.com/spf13/cobra v1.8.0 // indirect
 	github.com/spf13/viper v1.17.0 // indirect
 	github.com/stoewer/go-strcase v1.2.0 // indirect
 	github.com/subosito/gotenv v1.6.0 // indirect
-	github.com/xlab/treeprint v1.2.0 // indirect
 	go.opencensus.io v0.24.0 // indirect
-	go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect
 	go.uber.org/multierr v1.11.0 // indirect
 	golang.org/x/crypto v0.15.0 // indirect
 	golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
@@ -156,14 +132,10 @@ require (
 	gopkg.in/ini.v1 v1.67.0 // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
-	k8s.io/apiserver v0.28.4 // indirect
-	k8s.io/cli-runtime v0.28.4 // indirect
-	k8s.io/cluster-bootstrap v0.28.4 // indirect
-	k8s.io/component-base v0.28.4 // indirect
-	k8s.io/component-helpers v0.28.4 // indirect
-	k8s.io/metrics v0.28.4 // indirect
+	k8s.io/apiserver v0.28.3 // indirect
+	k8s.io/cluster-bootstrap v0.28.3 // indirect
+	k8s.io/component-base v0.28.3 // indirect
 	sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
-	sigs.k8s.io/kustomize/kustomize/v5 v5.0.4-0.20230601165947-6ce0bf390ce3 // indirect
 	sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3 // indirect
 	sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
 )
diff --git a/hack/tools/go.sum b/hack/tools/go.sum
index d3ca2a26f..5fefb0a80 100644
--- a/hack/tools/go.sum
+++ b/hack/tools/go.sum
@@ -43,11 +43,10 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
 cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
 cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
 cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
-cloud.google.com/go/storage v1.35.1 h1:B59ahL//eDfx2IIKFBeT5Atm9wnNmj3+8xG/W4WB//w=
-cloud.google.com/go/storage v1.35.1/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8=
+cloud.google.com/go/storage v1.34.1 h1:H2Af2dU5J0PF7A5B+ECFIce+RqxVnrVilO+cu0TS3MI=
+cloud.google.com/go/storage v1.34.1/go.mod h1:VN1ElqqvR9adg1k9xlkUJ55cMOP1/QjnNNuT5xQL6dY=
 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
 github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
-github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
 github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
@@ -66,7 +65,6 @@ github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls=
 github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E=
 github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df h1:7RFfzj4SSt6nnvCPbCqijJi1nWCd+TqAT3bYCStRC18=
 github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM=
-github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
 github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 h1:4daAzAu0S6Vi7/lbWECcX0j45yZReDZ56BQsrVBOEEY=
 github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
 github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
@@ -77,8 +75,6 @@ github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7N
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
 github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
 github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk=
-github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA=
 github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
 github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
 github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
@@ -91,15 +87,11 @@ github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnht
 github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
 github.com/coredns/caddy v1.1.0 h1:ezvsPrT/tA/7pYDBZxu0cT0VmWk75AfIaf6GSYCNMf0=
 github.com/coredns/corefile-migration v1.0.21 h1:W/DCETrHDiFo0Wj03EyMkaQ9fwsmSgqTCQDHpceaSsE=
-github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
-github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/daviddengcn/go-colortext v1.0.0 h1:ANqDyC0ys6qCSvuEK7l3g5RaehL/Xck9EX8ATG8oKsE=
-github.com/daviddengcn/go-colortext v1.0.0/go.mod h1:zDqEI5NVUop5QPpVJUxE9UO10hRnmkD5G4Pmri9+m4c=
 github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0=
 github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
 github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
@@ -124,15 +116,9 @@ github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCv
 github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
 github.com/evanphx/json-patch/v5 v5.7.0 h1:nJqP7uwL84RJInrohHfW0Fx3awjbm8qZeFv0nW9SYGc=
 github.com/evanphx/json-patch/v5 v5.7.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ=
-github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM=
-github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4=
-github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8=
-github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
 github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
 github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
 github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
-github.com/fvbommel/sortorder v1.1.0 h1:fUmoe+HLsBTctBDoaBwpQo5N+nrCp8g/BjKb/6ZQmYw=
-github.com/fvbommel/sortorder v1.1.0/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
 github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
 github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
 github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
@@ -183,15 +169,8 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
 github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
 github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
 github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
-github.com/golangplus/bytes v0.0.0-20160111154220-45c989fe5450/go.mod h1:Bk6SMAONeMXrxql8uvOKuAZSu8aM5RUGv+1C6IJaEho=
-github.com/golangplus/bytes v1.0.0/go.mod h1:AdRaCFwmc/00ZzELMWb01soso6W1R/++O1XL80yAn+A=
-github.com/golangplus/fmt v1.0.0/go.mod h1:zpM0OfbMCjPtd2qkTD/jX2MgiFCqklhSUFyDW44gVQE=
-github.com/golangplus/testing v1.0.0 h1:+ZeeiKZENNOMkTTELoSySazi+XaEhVO0mb+eanrSEUQ=
-github.com/golangplus/testing v1.0.0/go.mod h1:ZDreixUV3YzhoVraIDyOzHrr76p6NUh6k/pPg/Q3gYA=
 github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
-github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
-github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
 github.com/google/cel-go v0.16.1 h1:3hZfSNiAU3KOiNtxuFXVp5WFy4hf/Ly3Sa4/7F8SXNo=
 github.com/google/cel-go v0.16.1/go.mod h1:HXZKzB0LXqer5lHHgfWAnlYwJaQBDKMjxjulNQzhwhY=
 github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
@@ -236,8 +215,6 @@ github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJY
 github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
 github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
 github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
-github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
-github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
 github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
@@ -249,9 +226,6 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m
 github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas=
 github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU=
 github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
-github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
-github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM=
-github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
 github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
@@ -263,10 +237,6 @@ github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:
 github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
 github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
 github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
-github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
-github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
-github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ=
-github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
 github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
 github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
 github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
@@ -283,10 +253,6 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
-github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0=
-github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE=
-github.com/lithammer/dedent v1.1.0 h1:VNzHMVCBNG1j0fh3OrsFRkVUwStdDArbgBWoPAffktY=
-github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc=
 github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
 github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
 github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
@@ -296,29 +262,20 @@ github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfr
 github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
 github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
 github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
-github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
-github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
 github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
 github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
 github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
 github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
 github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
-github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8=
-github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
 github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA=
-github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
 github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
-github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0=
-github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4=
 github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
-github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus=
-github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
 github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
 github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
 github.com/onsi/ginkgo/v2 v2.13.1 h1:LNGfMbR2OVGBfXjvRZIZ2YCTQdGKtPLvuI1rMCCj3OU=
@@ -330,8 +287,6 @@ github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7X
 github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ=
 github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
 github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
-github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
-github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
@@ -348,13 +303,10 @@ github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwa
 github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY=
 github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
-github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
-github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/sagikazarmark/locafero v0.3.0 h1:zT7VEGWC2DTflmccN/5T1etyKvxSxpHsjb9cJvm4SvQ=
 github.com/sagikazarmark/locafero v0.3.0/go.mod h1:w+v7UsPNFwzF1cHuOajOOzoq4U7v/ig1mpRjqV+Bu1U=
 github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
 github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
-github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
 github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
 github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
 github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
@@ -366,8 +318,6 @@ github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb
 github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
 github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA=
 github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48=
-github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
-github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
 github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
 github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 github.com/spf13/viper v1.17.0 h1:I5txKw7MJasPL/BrfkbA0Jyo/oELqVmux4pR/UxOMfI=
@@ -376,7 +326,6 @@ github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ai
 github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.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=
@@ -392,8 +341,6 @@ github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8
 github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
 github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ=
 github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
-github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ=
-github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0=
 github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@@ -407,8 +354,6 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
 go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
 go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
 go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
-go.starlark.net v0.0.0-20230525235612-a134d8f9ddca h1:VdD38733bfYv5tUZwEIskMM93VanwNIi5bIKnDrJdEY=
-go.starlark.net v0.0.0-20230525235612-a134d8f9ddca/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds=
 go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
 go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
 go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
@@ -560,11 +505,9 @@ golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -572,7 +515,6 @@ golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
 golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
 golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8=
 golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww=
@@ -671,8 +613,8 @@ google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz513
 google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
 google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
 google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
-google.golang.org/api v0.151.0 h1:FhfXLO/NFdJIzQtCqjpysWwqKk8AzGWBUhMIx67cVDU=
-google.golang.org/api v0.151.0/go.mod h1:ccy+MJ6nrYFgE3WgRx/AMXOxOmU8Q4hSa+jjibzhxcg=
+google.golang.org/api v0.150.0 h1:Z9k22qD289SZ8gCJrk4DrWXkNjtfvKAUo/l1ma8eBYE=
+google.golang.org/api v0.150.0/go.mod h1:ccy+MJ6nrYFgE3WgRx/AMXOxOmU8Q4hSa+jjibzhxcg=
 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -782,32 +724,24 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
 honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
 honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
-k8s.io/api v0.28.4 h1:8ZBrLjwosLl/NYgv1P7EQLqoO8MGQApnbgH8tu3BMzY=
-k8s.io/api v0.28.4/go.mod h1:axWTGrY88s/5YE+JSt4uUi6NMM+gur1en2REMR7IRj0=
-k8s.io/apiextensions-apiserver v0.28.4 h1:AZpKY/7wQ8n+ZYDtNHbAJBb+N4AXXJvyZx6ww6yAJvU=
-k8s.io/apiextensions-apiserver v0.28.4/go.mod h1:pgQIZ1U8eJSMQcENew/0ShUTlePcSGFq6dxSxf2mwPM=
-k8s.io/apimachinery v0.28.4 h1:zOSJe1mc+GxuMnFzD4Z/U1wst50X28ZNsn5bhgIIao8=
-k8s.io/apimachinery v0.28.4/go.mod h1:wI37ncBvfAoswfq626yPTe6Bz1c22L7uaJ8dho83mgg=
-k8s.io/apiserver v0.28.4 h1:BJXlaQbAU/RXYX2lRz+E1oPe3G3TKlozMMCZWu5GMgg=
-k8s.io/apiserver v0.28.4/go.mod h1:Idq71oXugKZoVGUUL2wgBCTHbUR+FYTWa4rq9j4n23w=
-k8s.io/cli-runtime v0.28.4 h1:IW3aqSNFXiGDllJF4KVYM90YX4cXPGxuCxCVqCD8X+Q=
-k8s.io/cli-runtime v0.28.4/go.mod h1:MLGRB7LWTIYyYR3d/DOgtUC8ihsAPA3P8K8FDNIqJ0k=
-k8s.io/client-go v0.28.4 h1:Np5ocjlZcTrkyRJ3+T3PkXDpe4UpatQxj85+xjaD2wY=
-k8s.io/client-go v0.28.4/go.mod h1:0VDZFpgoZfelyP5Wqu0/r/TRYcLYuJ2U1KEeoaPa1N4=
-k8s.io/cluster-bootstrap v0.28.4 h1:4MKNy1Qd9QY7pl47rSMGIORF+tm3CUaqC1M8U9bjn4Q=
-k8s.io/cluster-bootstrap v0.28.4/go.mod h1:/c4ro/R4yf4EtJgFgFtvnHkbDOHwubeKJXh5R1c89Bc=
-k8s.io/component-base v0.28.4 h1:c/iQLWPdUgI90O+T9TeECg8o7N3YJTiuz2sKxILYcYo=
-k8s.io/component-base v0.28.4/go.mod h1:m9hR0uvqXDybiGL2nf/3Lf0MerAfQXzkfWhUY58JUbU=
-k8s.io/component-helpers v0.28.4 h1:+X9VXT5+jUsRdC26JyMZ8Fjfln7mSjgumafocE509C4=
-k8s.io/component-helpers v0.28.4/go.mod h1:8LzMalOQ0K10tkBJWBWq8h0HTI9HDPx4WT3QvTFn9Ro=
+k8s.io/api v0.28.3 h1:Gj1HtbSdB4P08C8rs9AR94MfSGpRhJgsS+GF9V26xMM=
+k8s.io/api v0.28.3/go.mod h1:MRCV/jr1dW87/qJnZ57U5Pak65LGmQVkKTzf3AtKFHc=
+k8s.io/apiextensions-apiserver v0.28.3 h1:Od7DEnhXHnHPZG+W9I97/fSQkVpVPQx2diy+2EtmY08=
+k8s.io/apiextensions-apiserver v0.28.3/go.mod h1:NE1XJZ4On0hS11aWWJUTNkmVB03j9LM7gJSisbRt8Lc=
+k8s.io/apimachinery v0.28.3 h1:B1wYx8txOaCQG0HmYF6nbpU8dg6HvA06x5tEffvOe7A=
+k8s.io/apimachinery v0.28.3/go.mod h1:uQTKmIqs+rAYaq+DFaoD2X7pcjLOqbQX2AOiO0nIpb8=
+k8s.io/apiserver v0.28.3 h1:8Ov47O1cMyeDzTXz0rwcfIIGAP/dP7L8rWbEljRcg5w=
+k8s.io/apiserver v0.28.3/go.mod h1:YIpM+9wngNAv8Ctt0rHG4vQuX/I5rvkEMtZtsxW2rNM=
+k8s.io/client-go v0.28.3 h1:2OqNb72ZuTZPKCl+4gTKvqao0AMOl9f3o2ijbAj3LI4=
+k8s.io/client-go v0.28.3/go.mod h1:LTykbBp9gsA7SwqirlCXBWtK0guzfhpoW4qSm7i9dxo=
+k8s.io/cluster-bootstrap v0.28.3 h1:hGK3mJsmVGGvRJ61nyQcYNR9g/IYax75TbJcylTmZts=
+k8s.io/cluster-bootstrap v0.28.3/go.mod h1:s1B3FTw713b9iw67yGFiVF3zCfw5obrZXWl3EMelvdg=
+k8s.io/component-base v0.28.3 h1:rDy68eHKxq/80RiMb2Ld/tbH8uAE75JdCqJyi6lXMzI=
+k8s.io/component-base v0.28.3/go.mod h1:fDJ6vpVNSk6cRo5wmDa6eKIG7UlIQkaFmZN2fYgIUD8=
 k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg=
 k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
 k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 h1:LyMgNKD2P8Wn1iAwQU5OhxCKlKJy0sHc+PcDwFB24dQ=
 k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9/go.mod h1:wZK2AVp1uHCp4VamDVgBP2COHZjqD1T68Rf0CM3YjSM=
-k8s.io/kubectl v0.28.4 h1:gWpUXW/T7aFne+rchYeHkyB8eVDl5UZce8G4X//kjUQ=
-k8s.io/kubectl v0.28.4/go.mod h1:CKOccVx3l+3MmDbkXtIUtibq93nN2hkDR99XDCn7c/c=
-k8s.io/metrics v0.28.4 h1:u36fom9+6c8jX2sk8z58H0hFaIUfrPWbXIxN7GT2blk=
-k8s.io/metrics v0.28.4/go.mod h1:bBqAJxH20c7wAsTQxDXOlVqxGMdce49d7WNr1WeaLac=
 k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 h1:qY1Ad8PODbnymg2pRbkyMT/ylpTrCM8P2RJ0yroCyIk=
 k8s.io/utils v0.0.0-20230406110748-d93618cff8a2/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
 rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
@@ -823,8 +757,6 @@ sigs.k8s.io/kubebuilder/docs/book/utils v0.0.0-20211028165026-57688c578b5d h1:KL
 sigs.k8s.io/kubebuilder/docs/book/utils v0.0.0-20211028165026-57688c578b5d/go.mod h1:NRdZafr4zSCseLQggdvIMXa7umxf+Q+PJzrj3wFwiGE=
 sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3 h1:XX3Ajgzov2RKUdc5jW3t5jwY7Bo7dcRm+tFxT+NfgY0=
 sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3/go.mod h1:9n16EZKMhXBNSiUC5kSdFQJkdH3zbxS/JoO619G1VAY=
-sigs.k8s.io/kustomize/kustomize/v5 v5.0.4-0.20230601165947-6ce0bf390ce3 h1:vq2TtoDcQomhy7OxXLUOzSbHMuMYq0Bjn93cDtJEdKw=
-sigs.k8s.io/kustomize/kustomize/v5 v5.0.4-0.20230601165947-6ce0bf390ce3/go.mod h1:/d88dHCvoy7d0AKFT0yytezSGZKjsZBVs9YTkBHSGFk=
 sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3 h1:W6cLQc5pnqM7vh3b7HvGNfXrJ/xL6BDMS0v1V/HHg5U=
 sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3/go.mod h1:JWP1Fj0VWGHyw3YUPjXSQnRnrwezrZSrApfX5S0nIag=
 sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE=
diff --git a/hack/tools/internal/tilt-prepare/main.go b/hack/tools/internal/tilt-prepare/main.go
index 9b914ed68..52d8d41e1 100644
--- a/hack/tools/internal/tilt-prepare/main.go
+++ b/hack/tools/internal/tilt-prepare/main.go
@@ -842,10 +842,6 @@ func updateDeployment(prefix string, objs []unstructured.Unstructured, f updateD
 		if obj.GetKind() != "Deployment" {
 			continue
 		}
-		// Ignore Deployments that are not part of the provider, eg. ASO in CAPZ.
-		if _, exists := obj.GetLabels()[clusterv1.ProviderNameLabel]; !exists {
-			continue
-		}
 
 		// Convert Unstructured into a typed object
 		d := &appsv1.Deployment{}
diff --git a/hack/tools/release/notes/main.go b/hack/tools/release/notes/main.go
index d4163c6ae..376870d78 100644
--- a/hack/tools/release/notes/main.go
+++ b/hack/tools/release/notes/main.go
@@ -96,7 +96,7 @@ var (
 		"machinehealthcheck":                "MachineHealthCheck",
 		"clusterctl":                        "clusterctl", // Preserve lowercase
 		"util":                              "util",       // Preserve lowercase
-		"community-meeting":                 "Community meeting",
+		"Community-meeting":                 "Community meeting",
 	}
 
 	releaseBackportMarker = regexp.MustCompile(`(?m)^\[release-\d\.\d\]\s*`)
@@ -143,7 +143,7 @@ const (
 	missingAreaLabelPrefix   = "MISSING_AREA"
 	areaLabelPrefix          = "area/"
 	multipleAreaLabelsPrefix = "MULTIPLE_AREAS["
-	documentationAreaLabel   = "Documentation"
+	documentationAreaLabel   = "documentation"
 )
 
 type githubPullRequest struct {
@@ -293,7 +293,7 @@ func run() int {
 	for result := range results {
 		if result.err != nil {
 			fmt.Println(result.err)
-			return -1
+			os.Exit(0)
 		}
 
 		if result.prEntry == nil || result.prEntry.title == "" {
diff --git a/internal/contract/infrastructure_machinepool_template.go b/internal/contract/infrastructure_machinepool_template.go
deleted file mode 100644
index 773190480..000000000
--- a/internal/contract/infrastructure_machinepool_template.go
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
-Copyright 2022 The Kubernetes Authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package contract
-
-import (
-	"sync"
-)
-
-// InfrastructureMachinePoolTemplateContract encodes information about the Cluster API contract for InfrastructureMachinePoolTemplate objects
-// like DockerMachinePoolTemplates, AWSMachinePoolTemplates, etc.
-type InfrastructureMachinePoolTemplateContract struct{}
-
-var infrastructureMachinePoolTemplate *InfrastructureMachinePoolTemplateContract
-var onceInfrastructureMachinePoolTemplate sync.Once
-
-// InfrastructureMachinePoolTemplate provide access to the information about the Cluster API contract for InfrastructureMachinePoolTemplate objects.
-func InfrastructureMachinePoolTemplate() *InfrastructureMachinePoolTemplateContract {
-	onceInfrastructureMachinePoolTemplate.Do(func() {
-		infrastructureMachinePoolTemplate = &InfrastructureMachinePoolTemplateContract{}
-	})
-	return infrastructureMachinePoolTemplate
-}
-
-// Template provides access to the template.
-func (c *InfrastructureMachinePoolTemplateContract) Template() *InfrastructureMachinePoolTemplateTemplate {
-	return &InfrastructureMachinePoolTemplateTemplate{}
-}
-
-// InfrastructureMachinePoolTemplateTemplate provides a helper struct for working with the template in an InfrastructureMachinePoolTemplate.
-type InfrastructureMachinePoolTemplateTemplate struct{}
-
-// Metadata provides access to the metadata of a template.
-func (c *InfrastructureMachinePoolTemplateTemplate) Metadata() *Metadata {
-	return &Metadata{
-		path: Path{"spec", "template", "metadata"},
-	}
-}
diff --git a/internal/controllers/machineset/machineset_controller.go b/internal/controllers/machineset/machineset_controller.go
index a72cb00f0..c499808e7 100644
--- a/internal/controllers/machineset/machineset_controller.go
+++ b/internal/controllers/machineset/machineset_controller.go
@@ -942,7 +942,7 @@ func (r *Reconciler) updateStatus(ctx context.Context, cluster *clusterv1.Cluste
 
 	// Aggregate the operational state of all the machines; while aggregating we are adding the
 	// source ref (reason@machine/name) so the problem can be easily tracked down to its source machine.
-	conditions.SetAggregate(ms, clusterv1.MachinesReadyCondition, collections.FromMachines(filteredMachines...).ConditionGetters(), conditions.AddSourceRef())
+	conditions.SetAggregate(ms, clusterv1.MachinesReadyCondition, collections.FromMachines(filteredMachines...).ConditionGetters(), conditions.AddSourceRef(), conditions.WithStepCounterIf(false))
 
 	return nil
 }
diff --git a/internal/webhooks/machinedeployment.go b/internal/webhooks/machinedeployment.go
index ed7a9ca41..0e8e86377 100644
--- a/internal/webhooks/machinedeployment.go
+++ b/internal/webhooks/machinedeployment.go
@@ -42,8 +42,8 @@ import (
 )
 
 func (webhook *MachineDeployment) SetupWebhookWithManager(mgr ctrl.Manager) error {
-	if webhook.decoder == nil {
-		webhook.decoder = admission.NewDecoder(mgr.GetScheme())
+	if webhook.Decoder == nil {
+		webhook.Decoder = admission.NewDecoder(mgr.GetScheme())
 	}
 
 	return ctrl.NewWebhookManagedBy(mgr).
@@ -58,7 +58,7 @@ func (webhook *MachineDeployment) SetupWebhookWithManager(mgr ctrl.Manager) erro
 
 // MachineDeployment implements a validation and defaulting webhook for MachineDeployment.
 type MachineDeployment struct {
-	decoder *admission.Decoder
+	Decoder *admission.Decoder
 }
 
 var _ webhook.CustomDefaulter = &MachineDeployment{}
@@ -83,7 +83,7 @@ func (webhook *MachineDeployment) Default(ctx context.Context, obj runtime.Objec
 	var oldMD *clusterv1.MachineDeployment
 	if req.Operation == v1.Update {
 		oldMD = &clusterv1.MachineDeployment{}
-		if err := webhook.decoder.DecodeRaw(req.OldObject, oldMD); err != nil {
+		if err := webhook.Decoder.DecodeRaw(req.OldObject, oldMD); err != nil {
 			return errors.Wrapf(err, "failed to decode oldObject to MachineDeployment")
 		}
 	}
diff --git a/internal/webhooks/machineset.go b/internal/webhooks/machineset.go
index f0f38f84e..b9156544a 100644
--- a/internal/webhooks/machineset.go
+++ b/internal/webhooks/machineset.go
@@ -19,18 +19,14 @@ package webhooks
 import (
 	"context"
 	"fmt"
-	"strconv"
 	"strings"
 
-	"github.com/pkg/errors"
-	v1 "k8s.io/api/admission/v1"
 	apierrors "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/labels"
 	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/util/sets"
 	"k8s.io/apimachinery/pkg/util/validation/field"
-	"k8s.io/utils/pointer"
 	ctrl "sigs.k8s.io/controller-runtime"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	"sigs.k8s.io/controller-runtime/pkg/webhook"
@@ -43,10 +39,6 @@ import (
 )
 
 func (webhook *MachineSet) SetupWebhookWithManager(mgr ctrl.Manager) error {
-	if webhook.decoder == nil {
-		webhook.decoder = admission.NewDecoder(mgr.GetScheme())
-	}
-
 	return ctrl.NewWebhookManagedBy(mgr).
 		For(&clusterv1.MachineSet{}).
 		WithDefaulter(webhook).
@@ -58,49 +50,23 @@ func (webhook *MachineSet) SetupWebhookWithManager(mgr ctrl.Manager) error {
 // +kubebuilder:webhook:verbs=create;update,path=/mutate-cluster-x-k8s-io-v1beta1-machineset,mutating=true,failurePolicy=fail,matchPolicy=Equivalent,groups=cluster.x-k8s.io,resources=machinesets,versions=v1beta1,name=default.machineset.cluster.x-k8s.io,sideEffects=None,admissionReviewVersions=v1;v1beta1
 
 // MachineSet implements a validation and defaulting webhook for MachineSet.
-type MachineSet struct {
-	decoder *admission.Decoder
-}
+type MachineSet struct{}
 
 var _ webhook.CustomDefaulter = &MachineSet{}
 var _ webhook.CustomValidator = &MachineSet{}
 
 // Default sets default MachineSet field values.
-func (webhook *MachineSet) Default(ctx context.Context, obj runtime.Object) error {
+func (webhook *MachineSet) Default(_ context.Context, obj runtime.Object) error {
 	m, ok := obj.(*clusterv1.MachineSet)
 	if !ok {
 		return apierrors.NewBadRequest(fmt.Sprintf("expected a MachineSet but got a %T", obj))
 	}
 
-	req, err := admission.RequestFromContext(ctx)
-	if err != nil {
-		return err
-	}
-
-	dryRun := false
-	if req.DryRun != nil {
-		dryRun = *req.DryRun
-	}
-
-	var oldMS *clusterv1.MachineSet
-	if req.Operation == v1.Update {
-		oldMS = &clusterv1.MachineSet{}
-		if err := webhook.decoder.DecodeRaw(req.OldObject, oldMS); err != nil {
-			return errors.Wrapf(err, "failed to decode oldObject to MachineSet")
-		}
-	}
-
 	if m.Labels == nil {
 		m.Labels = make(map[string]string)
 	}
 	m.Labels[clusterv1.ClusterNameLabel] = m.Spec.ClusterName
 
-	replicas, err := calculateMachineSetReplicas(ctx, oldMS, m, dryRun)
-	if err != nil {
-		return err
-	}
-	m.Spec.Replicas = pointer.Int32(replicas)
-
 	if m.Spec.DeletePolicy == "" {
 		randomPolicy := string(clusterv1.RandomMachineSetDeletePolicy)
 		m.Spec.DeletePolicy = randomPolicy
@@ -253,104 +219,3 @@ func validateSkippedMachineSetPreflightChecks(o client.Object) *field.Error {
 	}
 	return nil
 }
-
-// calculateMachineSetReplicas calculates the default value of the replicas field.
-// The value will be calculated based on the following logic:
-// * if replicas is already set on newMS, keep the current value
-// * if the autoscaler min size and max size annotations are set:
-//   - if it's a new MachineSet, use min size
-//   - if the replicas field of the old MachineSet is < min size, use min size
-//   - if the replicas field of the old MachineSet is > max size, use max size
-//   - if the replicas field of the old MachineSet is in the (min size, max size) range, keep the value from the oldMS
-//
-// * otherwise use 1
-//
-// The goal of this logic is to provide a smoother UX for clusters using the Kubernetes autoscaler.
-// Note: Autoscaler only takes over control of the replicas field if the replicas value is in the (min size, max size) range.
-//
-// We are supporting the following use cases:
-// * A new MS is created and replicas should be managed by the autoscaler
-//   - Either via the default annotation or via the min size and max size annotations the replicas field
-//     is defaulted to a value which is within the (min size, max size) range so the autoscaler can take control.
-//
-// * An existing MS which initially wasn't controlled by the autoscaler should be later controlled by the autoscaler
-//   - To adopt an existing MS users can use the default, min size and max size annotations to enable the autoscaler
-//     and to ensure the replicas field is within the (min size, max size) range. Without the annotations handing over
-//     control to the autoscaler by unsetting the replicas field would lead to the field being set to 1. This is very
-//     disruptive for existing Machines and if 1 is outside the (min size, max size) range the autoscaler won't take
-//     control.
-//
-// Notes:
-//   - While the min size and max size annotations of the autoscaler provide the best UX, other autoscalers can use the
-//     DefaultReplicasAnnotation if they have similar use cases.
-func calculateMachineSetReplicas(ctx context.Context, oldMS *clusterv1.MachineSet, newMS *clusterv1.MachineSet, dryRun bool) (int32, error) {
-	// If replicas is already set => Keep the current value.
-	if newMS.Spec.Replicas != nil {
-		return *newMS.Spec.Replicas, nil
-	}
-
-	log := ctrl.LoggerFrom(ctx)
-
-	// If both autoscaler annotations are set, use them to calculate the default value.
-	minSizeString, hasMinSizeAnnotation := newMS.Annotations[clusterv1.AutoscalerMinSizeAnnotation]
-	maxSizeString, hasMaxSizeAnnotation := newMS.Annotations[clusterv1.AutoscalerMaxSizeAnnotation]
-	if hasMinSizeAnnotation && hasMaxSizeAnnotation {
-		minSize, err := strconv.ParseInt(minSizeString, 10, 32)
-		if err != nil {
-			return 0, errors.Wrapf(err, "failed to caculate MachineSet replicas value: could not parse the value of the %q annotation", clusterv1.AutoscalerMinSizeAnnotation)
-		}
-		maxSize, err := strconv.ParseInt(maxSizeString, 10, 32)
-		if err != nil {
-			return 0, errors.Wrapf(err, "failed to caculate MachineSet replicas value: could not parse the value of the %q annotation", clusterv1.AutoscalerMaxSizeAnnotation)
-		}
-
-		// If it's a new MachineSet => Use the min size.
-		// Note: This will result in a scale up to get into the range where autoscaler takes over.
-		if oldMS == nil {
-			if !dryRun {
-				log.V(2).Info(fmt.Sprintf("Replica field has been defaulted to %d based on the %s annotation (MS is a new MS)", minSize, clusterv1.AutoscalerMinSizeAnnotation))
-			}
-			return int32(minSize), nil
-		}
-
-		// Otherwise we are handing over the control for the replicas field for an existing MachineSet
-		// to the autoscaler.
-
-		switch {
-		// If the old MachineSet doesn't have replicas set => Use the min size.
-		// Note: As defaulting always sets the replica field, this case should not be possible
-		// We only have this handling to be 100% safe against panics.
-		case oldMS.Spec.Replicas == nil:
-			if !dryRun {
-				log.V(2).Info(fmt.Sprintf("Replica field has been defaulted to %d based on the %s annotation (old MS didn't have replicas set)", minSize, clusterv1.AutoscalerMinSizeAnnotation))
-			}
-			return int32(minSize), nil
-		// If the old MachineSet replicas are lower than min size => Use the min size.
-		// Note: This will result in a scale up to get into the range where autoscaler takes over.
-		case *oldMS.Spec.Replicas < int32(minSize):
-			if !dryRun {
-				log.V(2).Info(fmt.Sprintf("Replica field has been defaulted to %d based on the %s annotation (old MS had replicas below min size)", minSize, clusterv1.AutoscalerMinSizeAnnotation))
-			}
-			return int32(minSize), nil
-		// If the old MachineSet replicas are higher than max size => Use the max size.
-		// Note: This will result in a scale down to get into the range where autoscaler takes over.
-		case *oldMS.Spec.Replicas > int32(maxSize):
-			if !dryRun {
-				log.V(2).Info(fmt.Sprintf("Replica field has been defaulted to %d based on the %s annotation (old MS had replicas above max size)", maxSize, clusterv1.AutoscalerMaxSizeAnnotation))
-			}
-			return int32(maxSize), nil
-		// If the old MachineSet replicas are between min and max size => Keep the current value.
-		default:
-			if !dryRun {
-				log.V(2).Info(fmt.Sprintf("Replica field has been defaulted to %d based on replicas of the old MachineSet (old MS had replicas within min size / max size range)", *oldMS.Spec.Replicas))
-			}
-			return *oldMS.Spec.Replicas, nil
-		}
-	}
-
-	// If neither the default nor the autoscaler annotations are set => Default to 1.
-	if !dryRun {
-		log.V(2).Info("Replica field has been defaulted to 1")
-	}
-	return 1, nil
-}
diff --git a/webhooks/alias.go b/webhooks/alias.go
index b1688d95c..ee0646bdc 100644
--- a/webhooks/alias.go
+++ b/webhooks/alias.go
@@ -19,6 +19,7 @@ package webhooks
 import (
 	ctrl "sigs.k8s.io/controller-runtime"
 	"sigs.k8s.io/controller-runtime/pkg/client"
+	"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
 
 	"sigs.k8s.io/cluster-api/internal/webhooks"
 )
@@ -56,11 +57,15 @@ func (webhook *Machine) SetupWebhookWithManager(mgr ctrl.Manager) error {
 }
 
 // MachineDeployment implements a validating and defaulting webhook for MachineDeployment.
-type MachineDeployment struct{}
+type MachineDeployment struct {
+	Decoder *admission.Decoder
+}
 
 // SetupWebhookWithManager sets up MachineDeployment webhooks.
 func (webhook *MachineDeployment) SetupWebhookWithManager(mgr ctrl.Manager) error {
-	return (&webhooks.MachineDeployment{}).SetupWebhookWithManager(mgr)
+	return (&webhooks.MachineDeployment{
+		Decoder: webhook.Decoder,
+	}).SetupWebhookWithManager(mgr)
 }
 
 // MachineSet implements a validating and defaulting webhook for MachineSet.


Test patch:
diff --git a/.github/workflows/weekly-test-release.yaml b/.github/workflows/weekly-test-release.yaml
index d32cd8440..44327bc8f 100644
--- a/.github/workflows/weekly-test-release.yaml
+++ b/.github/workflows/weekly-test-release.yaml
@@ -17,7 +17,7 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        branch: [ main, release-1.6, release-1.5 ]
+        branch: [ main, release-1.5, release-1.4 ]
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # tag=v4.1.1
diff --git a/cmd/clusterctl/client/cluster/mover_test.go b/cmd/clusterctl/client/cluster/mover_test.go
index b4100b007..08481760a 100644
--- a/cmd/clusterctl/client/cluster/mover_test.go
+++ b/cmd/clusterctl/client/cluster/mover_test.go
@@ -735,7 +735,7 @@ func Test_objectMover_backupTargetObject(t *testing.T) {
 			graph := getObjectGraphWithObjs(tt.fields.objs)
 
 			// Get all the types to be considered for discovery
-			g.Expect(graph.getDiscoveryTypes(ctx)).To(Succeed())
+			g.Expect(getFakeDiscoveryTypes(ctx, graph)).To(Succeed())
 
 			// trigger discovery the content of the source cluster
 			g.Expect(graph.Discovery(ctx, "")).To(Succeed())
@@ -826,7 +826,7 @@ func Test_objectMover_restoreTargetObject(t *testing.T) {
 			graph := getObjectGraph()
 
 			// Get all the types to be considered for discovery
-			g.Expect(graph.getDiscoveryTypes(ctx)).To(Succeed())
+			g.Expect(getFakeDiscoveryTypes(ctx, graph)).To(Succeed())
 
 			// trigger discovery the content of the source cluster
 			g.Expect(graph.Discovery(ctx, "")).To(Succeed())
@@ -934,7 +934,7 @@ func Test_objectMover_toDirectory(t *testing.T) {
 			graph := getObjectGraphWithObjs(tt.fields.objs)
 
 			// Get all the types to be considered for discovery
-			g.Expect(graph.getDiscoveryTypes(ctx)).To(Succeed())
+			g.Expect(getFakeDiscoveryTypes(ctx, graph)).To(Succeed())
 
 			// trigger discovery the content of the source cluster
 			g.Expect(graph.Discovery(ctx, "")).To(Succeed())
@@ -1077,7 +1077,7 @@ func Test_objectMover_fromDirectory(t *testing.T) {
 			graph := getObjectGraph()
 
 			// Get all the types to be considered for discovery
-			g.Expect(graph.getDiscoveryTypes(ctx)).To(Succeed())
+			g.Expect(getFakeDiscoveryTypes(ctx, graph)).To(Succeed())
 
 			// gets a fakeProxy to an empty cluster with all the required CRDs
 			toProxy := getFakeProxyWithCRDs()
@@ -1155,7 +1155,7 @@ func Test_getMoveSequence(t *testing.T) {
 			graph := getObjectGraphWithObjs(tt.fields.objs)
 
 			// Get all the types to be considered for discovery
-			g.Expect(graph.getDiscoveryTypes(ctx)).To(Succeed())
+			g.Expect(getFakeDiscoveryTypes(ctx, graph)).To(Succeed())
 
 			// trigger discovery the content of the source cluster
 			g.Expect(graph.Discovery(ctx, "")).To(Succeed())
@@ -1188,7 +1188,7 @@ func Test_objectMover_move_dryRun(t *testing.T) {
 			graph := getObjectGraphWithObjs(tt.fields.objs)
 
 			// Get all the types to be considered for discovery
-			g.Expect(graph.getDiscoveryTypes(ctx)).To(Succeed())
+			g.Expect(getFakeDiscoveryTypes(ctx, graph)).To(Succeed())
 
 			// trigger discovery the content of the source cluster
 			g.Expect(graph.Discovery(ctx, "")).To(Succeed())
@@ -1263,7 +1263,7 @@ func Test_objectMover_move(t *testing.T) {
 			graph := getObjectGraphWithObjs(tt.fields.objs)
 
 			// Get all the types to be considered for discovery
-			g.Expect(graph.getDiscoveryTypes(ctx)).To(Succeed())
+			g.Expect(getFakeDiscoveryTypes(ctx, graph)).To(Succeed())
 
 			// trigger discovery the content of the source cluster
 			g.Expect(graph.Discovery(ctx, "")).To(Succeed())
@@ -1375,7 +1375,7 @@ func Test_objectMover_move_with_Mutator(t *testing.T) {
 			graph := getObjectGraphWithObjs(tt.fields.objs)
 
 			// Get all the types to be considered for discovery
-			g.Expect(graph.getDiscoveryTypes(ctx)).To(Succeed())
+			g.Expect(getFakeDiscoveryTypes(ctx, graph)).To(Succeed())
 
 			// trigger discovery the content of the source cluster
 			g.Expect(graph.Discovery(ctx, "")).To(Succeed())
@@ -1660,7 +1660,7 @@ func Test_objectMover_checkProvisioningCompleted(t *testing.T) {
 			graph := getObjectGraphWithObjs(tt.fields.objs)
 
 			// Get all the types to be considered for discovery
-			g.Expect(graph.getDiscoveryTypes(ctx)).To(Succeed())
+			g.Expect(getFakeDiscoveryTypes(ctx, graph)).To(Succeed())
 
 			// trigger discovery the content of the source cluster
 			g.Expect(graph.Discovery(ctx, "")).To(Succeed())
@@ -1906,7 +1906,7 @@ func Test_objectMoverService_ensureNamespaces(t *testing.T) {
 			graph := getObjectGraphWithObjs(tt.fields.objs)
 
 			// Get all the types to be considered for discovery
-			g.Expect(graph.getDiscoveryTypes(ctx)).To(Succeed())
+			g.Expect(getFakeDiscoveryTypes(ctx, graph)).To(Succeed())
 
 			// Trigger discovery the content of the source cluster
 			g.Expect(graph.Discovery(ctx, "")).To(Succeed())
@@ -2385,7 +2385,7 @@ func TestWaitReadyForMove(t *testing.T) {
 			}
 
 			// Get all the types to be considered for discovery
-			g.Expect(graph.getDiscoveryTypes(ctx)).To(Succeed())
+			g.Expect(getFakeDiscoveryTypes(ctx, graph)).To(Succeed())
 
 			// trigger discovery the content of the source cluster
 			g.Expect(graph.Discovery(ctx, "")).To(Succeed())
diff --git a/cmd/clusterctl/client/cluster/objectgraph_test.go b/cmd/clusterctl/client/cluster/objectgraph_test.go
index 258acda9d..be2486913 100644
--- a/cmd/clusterctl/client/cluster/objectgraph_test.go
+++ b/cmd/clusterctl/client/cluster/objectgraph_test.go
@@ -1774,6 +1774,18 @@ func getFakeProxyWithCRDs() *test.FakeProxy {
 	return proxy
 }
 
+func getFakeDiscoveryTypes(ctx context.Context, graph *objectGraph) error {
+	if err := graph.getDiscoveryTypes(ctx); err != nil {
+		return err
+	}
+
+	// Given that the Fake client behaves in a different way than real client, for this test we are required to add the List suffix to all the types.
+	for _, discoveryType := range graph.types {
+		discoveryType.typeMeta.Kind = fmt.Sprintf("%sList", discoveryType.typeMeta.Kind)
+	}
+	return nil
+}
+
 func TestObjectGraph_Discovery(t *testing.T) {
 	// NB. we are testing the graph is properly built starting from objects (TestGraphBuilder_addObj_WithFakeObjects) or from the same objects read from the cluster (this test).
 	for _, tt := range objectGraphsTests {
@@ -1786,7 +1798,7 @@ func TestObjectGraph_Discovery(t *testing.T) {
 			graph := getObjectGraphWithObjs(tt.args.objs)
 
 			// Get all the types to be considered for discovery
-			err := graph.getDiscoveryTypes(ctx)
+			err := getFakeDiscoveryTypes(ctx, graph)
 			g.Expect(err).ToNot(HaveOccurred())
 
 			// finally test discovery
@@ -1944,7 +1956,7 @@ func TestObjectGraph_DiscoveryByNamespace(t *testing.T) {
 			graph := getObjectGraphWithObjs(tt.args.objs)
 
 			// Get all the types to be considered for discovery
-			err := graph.getDiscoveryTypes(ctx)
+			err := getFakeDiscoveryTypes(ctx, graph)
 			g.Expect(err).ToNot(HaveOccurred())
 
 			// finally test discovery
diff --git a/exp/ipam/api/v1alpha1/conversion_test.go b/exp/ipam/api/v1alpha1/conversion_test.go
index 6d298ed2a..181109b39 100644
--- a/exp/ipam/api/v1alpha1/conversion_test.go
+++ b/exp/ipam/api/v1alpha1/conversion_test.go
@@ -21,18 +21,18 @@ import (
 
 	"k8s.io/apimachinery/pkg/api/apitesting/fuzzer"
 
-	ipamv1 "sigs.k8s.io/cluster-api/exp/ipam/api/v1beta1"
+	ipamv1beta1 "sigs.k8s.io/cluster-api/exp/ipam/api/v1beta1"
 	utilconversion "sigs.k8s.io/cluster-api/util/conversion"
 )
 
 func TestFuzzyConversion(t *testing.T) {
 	t.Run("for IPAddress", utilconversion.FuzzTestFunc(utilconversion.FuzzTestFuncInput{
-		Hub:         &ipamv1.IPAddress{},
+		Hub:         &ipamv1beta1.IPAddress{},
 		Spoke:       &IPAddress{},
 		FuzzerFuncs: []fuzzer.FuzzerFuncs{},
 	}))
 	t.Run("for IPAddressClaim", utilconversion.FuzzTestFunc(utilconversion.FuzzTestFuncInput{
-		Hub:         &ipamv1.IPAddressClaim{},
+		Hub:         &ipamv1beta1.IPAddressClaim{},
 		Spoke:       &IPAddressClaim{},
 		FuzzerFuncs: []fuzzer.FuzzerFuncs{},
 	}))
diff --git a/hack/tools/release/notes/release_notes_integration_test.go b/hack/tools/release/notes/release_notes_integration_test.go
deleted file mode 100644
index 457f1d19f..000000000
--- a/hack/tools/release/notes/release_notes_integration_test.go
+++ /dev/null
@@ -1,131 +0,0 @@
-//go:build tools && integration
-
-/*
-Copyright 2023 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 (
-	"flag"
-	"fmt"
-	"io"
-	"os"
-	"os/exec"
-	"testing"
-
-	. "github.com/onsi/gomega"
-	"k8s.io/kubectl/pkg/cmd"
-)
-
-func TestReleaseNotesIntegration(t *testing.T) {
-	testCases := []struct {
-		name                 string
-		previousRelease      string
-		releaseBranchForTest string
-		expected             string
-	}{
-		{
-			// This tests a patch release computing the PR list from previous patch tag
-			// to HEAD. Since v1.3 is out of support, we won't be backporting new PRs
-			// so the branch should remain untouched and the test valid.
-			name:                 "new patch",
-			previousRelease:      "v1.3.9",
-			releaseBranchForTest: "release-1.3",
-			expected:             "test/golden/v1.3.10.md",
-		},
-		{
-			// The release notes command computes everything from last tag
-			// to HEAD. Hence if we use the head of release-1.5, this test will
-			// become invalid everytime we backport some PR to release branch release-1.5.
-			// Here we cheat a little by poiting to worktree to v1.5.0, which is the
-			// release that this test is simulating, so it should not exist yet. But
-			// it represents accurately the HEAD of release-1.5 when we released v1.5.0.
-			name:                 "new minor",
-			previousRelease:      "v1.4.0",
-			releaseBranchForTest: "v1.5.0",
-			expected:             "test/golden/v1.5.0.md",
-		},
-	}
-	for _, tc := range testCases {
-		t.Run(tc.name, func(t *testing.T) {
-			setupNotesTest(t, tc.previousRelease, tc.releaseBranchForTest)
-			g := NewWithT(t)
-
-			expectedOutput, err := os.ReadFile(tc.expected)
-			g.Expect(err).ToNot(HaveOccurred())
-
-			orgCurrentDir, err := os.Getwd()
-			g.Expect(err).To(Succeed())
-			t.Cleanup(func() {
-				g.Expect(os.Chdir(orgCurrentDir)).To(Succeed())
-			})
-			g.Expect(os.Chdir(tc.releaseBranchForTest)).To(Succeed())
-
-			// a two workers config is slow but it guarantees no rate limiting
-			os.Args = []string{os.Args[0], "--from", tc.previousRelease, "--workers", "2"}
-
-			old := os.Stdout // keep backup of the real stdout to restore later
-			r, w, err := os.Pipe()
-			g.Expect(err).To(Succeed())
-			os.Stdout = w
-
-			g.Expect(runReleaseNotesCmd()).To(Succeed())
-
-			w.Close()
-			output, err := io.ReadAll(r)
-			g.Expect(err).NotTo(HaveOccurred())
-			os.Stdout = old
-
-			g.Expect(string(output)).To(BeComparableTo(string(expectedOutput)))
-		})
-	}
-}
-
-func runReleaseNotesCmd() error {
-	// we replicate the main function here so we don't get os.Exit
-	flag.Parse()
-	if code := run(); code != 0 {
-		return fmt.Errorf("release notes command exited with code %d", code)
-	}
-
-	return nil
-}
-
-func setupNotesTest(tb testing.TB, previousRelease, releaseBranchForTest string) {
-	g := NewWithT(tb)
-
-	_, err := os.Stat(releaseBranchForTest)
-	if os.IsNotExist(err) {
-		runCommand(tb, exec.Command("git", "worktree", "add", releaseBranchForTest))
-	} else {
-		g.Expect(err).To(Succeed())
-	}
-
-	pull := exec.Command("git", "pull", "upstream", releaseBranchForTest)
-	pull.Dir = releaseBranchForTest
-	runCommand(tb, pull)
-
-	tb.Cleanup(func() {
-		runCommand(tb, cmd.Command("git", "worktree", "remove", releaseBranchForTest))
-	})
-}
-
-func runCommand(tb testing.TB, cmd *exec.Cmd) {
-	out, err := cmd.CombinedOutput()
-	if err != nil {
-		tb.Fatalf("Command %s failed: %s", cmd.Args, string(out))
-	}
-}
diff --git a/hack/tools/release/notes/test/golden/v1.3.10.md b/hack/tools/release/notes/test/golden/v1.3.10.md
deleted file mode 100644
index 282850019..000000000
--- a/hack/tools/release/notes/test/golden/v1.3.10.md
+++ /dev/null
@@ -1,34 +0,0 @@
-## 👌 Kubernetes version support
-
-- Management Cluster: v1.**X**.x -> v1.**X**.x
-- Workload Cluster: v1.**X**.x -> v1.**X**.x
-
-[More information about version support can be found here](https://cluster-api.sigs.k8s.io/reference/versions.html)
-
-## Highlights
-
-* REPLACE ME
-
-## Deprecation Warning
-
-REPLACE ME: A couple sentences describing the deprecation, including links to docs.
-
-* [GitHub issue #REPLACE ME](REPLACE ME)
-
-## Changes since v1.3.9
-## :chart_with_upwards_trend: Overview
-- 6 new commits merged
-- 2 bugs fixed 🐛
-
-## :bug: Bug Fixes
-- Dependency: Bump to docker v24.0.5-0.20230714235725-36e9e796c6fc (#9046)
-- KCP: Requeue KCP object if ControlPlaneComponentsHealthyCondition is not yet true (#9034)
-
-## :seedling: Others
-- ClusterCacheTracker: Ensure Get/List calls are not getting stuck when apiserver is unreachable (#9033)
-- Dependency: Bump docker to v24.0.5 (#9067)
-- Dependency: Bump google.golang.org/grpc to v1.55.0 (#8971)
-- Dependency: Change tilt debug base image to golang (#9075)
-
-
-_Thanks to all our contributors!_ 😊
diff --git a/hack/tools/release/notes/test/golden/v1.5.0.md b/hack/tools/release/notes/test/golden/v1.5.0.md
deleted file mode 100644
index 714da8a22..000000000
--- a/hack/tools/release/notes/test/golden/v1.5.0.md
+++ /dev/null
@@ -1,299 +0,0 @@
-## 👌 Kubernetes version support
-
-- Management Cluster: v1.**X**.x -> v1.**X**.x
-- Workload Cluster: v1.**X**.x -> v1.**X**.x
-
-[More information about version support can be found here](https://cluster-api.sigs.k8s.io/reference/versions.html)
-
-## Highlights
-
-* REPLACE ME
-
-## Deprecation Warning
-
-REPLACE ME: A couple sentences describing the deprecation, including links to docs.
-
-* [GitHub issue #REPLACE ME](REPLACE ME)
-
-## Changes since v1.4.0
-## :chart_with_upwards_trend: Overview
-- 335 new commits merged
-- 4 breaking changes :warning:
-- 19 feature additions ✨
-- 67 bugs fixed 🐛
-
-## :warning: Breaking Changes
-- API: Stop serving v1alpha3 API types (#8549)
-- CAPD: Use k8s v1.27.0 in quickstart docs and CAPD (#8518)
-- clusterctl: Follow XDG Directory standard for config/data/... files (#6913)
-- Dependency: Bump to CR v0.15.0 (#8007)
-
-## :sparkles: New Features
-- CAPBK: Use caching read for bootstrap config owner (#8867)
-- CAPIM: Enable Kubernetes upgrades in CAPIM (#8938)
-- ClusterClass: Add support or concurrent MD upgrades in classy clusters (#8432)
-- ClusterClass: Add webhook warning for missing ClusterClass (#8746)
-- clusterctl: Add in memory provider (#8799)
-- clusterctl: Add MachinePool Machine support in clusterctl discovery (#8836)
-- clusterctl: Add support for add-on providers in clusterctl (#8472)
-- clusterctl: Feat: accept resource mutators in Move operation (#7966)
-- clusterctl: Introduce possibility to create clusterctl plugins (#8957)
-- Devtools: Hack/observability bump kube-state-metrics chart to get version v2.9.2 (#8979)
-- Devtools: Hack/observability: Add Parca, Bump observability tools, Add Grafana scale dashboard (#8754)
-- Devtools: Hack/observability: improve dashboards (#8964)
-- e2e: Ci: collect debug information about leftover processes (#8734)
-- IPAM: Implements Getter interface for IPAddressClaim object (#8374)
-- Machine: Enable configuring the kubernetes rest client timeout for draining nodes (#8917)
-- MachinePool: Add MachinePool Machine implementation in core CAPI components (#8828)
-- MachineSet: MS preflight checks to improve cluster stability (#8595)
-- MULTIPLE_AREAS[KCP/Core/CAPD]: Add flags for configuring rate limits (#8579)
-- util: Move mergeMap to util (#8377)
-
-## :bug: Bug Fixes
-- API: All: only set finalizers if deletionTimestamp is not set (#8949)
-- API: Ensure ownerReference apiVersions are always up to date (#8256)
-- CAPBK: Ignition: start kubeadm after network.target (#8772)
-- CAPBK: Set uninitialized taint only on worker nodes (#8358)
-- CAPD: Add kind mapper (#8880)
-- CAPD: Change the haproxy entrypoint to prevent getting stopped immediately after start (#8685)
-- CAPD: Delegate CAPD port selection to the container runtime (#8642)
-- CAPD: Implement watch filter (#8789)
-- CAPD: Test/capd: fix kind mapper entry for v1.25.11 (#8914)
-- CAPD: Test/e2e fix fail-swap-on=false flag not being part of kind images anymore (#8767)
-- CAPIM: Fix cluster deletion in the in-memory API server (#8818)
-- CAPIM: Fix inmemory provider docker build (#8822)
-- CAPIM: Test/e2e/in-memory: set providerID after VM is provisioned (#8879)
-- CI: Always build Kind images for upgrade tests (#8859)
-- CI: Ensure ALWAYS_BUILD_KIND_IMAGES is defaulted correctly in ci script (#8873)
-- CI: Verifier: use pull_request instead of pull_request_target (#8635)
-- ClusterCacheTracker: Stop pod caching when checking workload cluster (#8850)
-- ClusterClass: Avoid errors when MHC and upgrade occur together in classy clusters (#8464)
-- ClusterClass: Drop uid from error messages (#8333)
-- ClusterClass: Topology controller should avoid unnecessary rollouts during upgrades (#8628)
-- ClusterClass: Update TopologyReconciled condition on cluster deletion (#8422)
-- ClusterClass: Validate variables in defaulting webhook (#8332)
-- clusterctl: Add soft ownership from clusters to ClusterResourceSetBinding (#8318)
-- clusterctl: Ensure all GitHub releases are fetched when searching provider versions (#8240)
-- clusterctl: Handle nil pointer in clusterctl describe (#8724)
-- clusterctl: Return early if release for latest tag does not exist yet (#8253)
-- clusterctl: Return error on infra cluster and control plane discovery (#8604)
-- clusterctl: Use local kustomize version in create-local-repository.py (#8414)
-- ClusterResourceSet: : Make sure the Kubernetes API Server service already created on remote cluster before applying ClusterResourceSets (#8048)
-- ClusterResourceSet: Continue applying when apply for a single cluster failed (#8611)
-- Dependency: Bump to docker v24.0.5-0.20230714235725-36e9e796c6fc (#9042)
-- Dependency: Update kindnetd and kindest/haproxy (#8469)
-- Devtools: Change tilt debug base image to golang (#9072)
-- Devtools: Tilt: fix build_engine setting when using podman (#8972)
-- Devtools: Tilt: set CR logger in tilt-prepare, fix allowed contexts (#8919)
-- Devtools: Update clean-charts make target for new paths (#8904)
-- e2e: Adjust machinepool helper e2e timeout (#8739)
-- e2e: Fix length check in MD helper (#8352)
-- e2e: Ignore cert annotation in rollout test (#8761)
-- e2e: Increase timeout for clusterclass rollout test (#8897)
-- e2e: Increased assertClusterObjects timeout (#8758)
-- e2e: Pin kindest/node images to known good versions in clusterctl upgrade tests (#8860)
-- e2e: Remove non-functioning ginkgo.no-color flag (#8703)
-- e2e: Revert "Add calico as e2e test CNI" (#8575)
-- e2e: Test/e2e check for machines being ready after provisioning on Runtime SDK test (#8625)
-- e2e: Test/e2e: use topology cluster-template for clusterctl upgrade mgmt cluster (#8311)
-- e2e: Test/framework fix docker pod log collector (#8634)
-- IPAM: Fix gateway being required for IPAddress (#8506)
-- KCP: Allow machine rollout if cert reconcile fails (#8711)
-- KCP: Enable updates from CloudInit (mostly ubuntu) based KCPs to Ignition (flatcar) based KCPs (#8339)
-- KCP: Ensure nil-pointer check in KCP syncMachines (#8481)
-- KCP: Prevent KCP to create many private keys for each reconcile (#8617)
-- KCP: Requeue KCP object if ControlPlaneComponentsHealthyCondition is not yet true (#9035)
-- Machine: Bug fix to set node interruptible label (#8297)
-- Machine: Fix node label propagation (#8427)
-- MachinePool: Add node watcher to MachinePool controller (#8443)
-- MachinePool: Always patch owned conditions, fix GetTypedPhase, doc fixes (#8627)
-- MachinePool: Fix MachinePool node taint patching (#8462)
-- MachinePool: Update MachinePool bootstrap dataSecretName when bootstrap config changes (#8667)
-- Release: Alphabetically sorting release tool output (#9060)
-- Release: Hack/release-notes: ensure relase notes tool can be used for external projects again (#9021)
-- Testing: Catch failure when fetching component URLs (#8455)
-- Testing: Fix flake in machine phases test (#8370)
-- Testing: Fix flaky wait in CRS test (#8334)
-- Testing: Internal/machine-controller fix flakes in phases test (#8576)
-- Testing: Machine-controller: fix phase tests race condition in tests on lastUpdated field (#8484)
-- Testing: Use CreateAndWait in MP ScaleFromZero test (#8927)
-
-## :seedling: Others
-- API: Deprecate v1alpha3 and v1alpha4 in CRDs (#8687)
-- CAPBK: Replace reflect deepequal in bootstrap kubeadm (#8266)
-- CAPD: Automatically set kubelet args for capd (#8881)
-- CAPD: Only ignore necessary kubeadm preflight errors (#7911)
-- CAPIM: Add ClusterClass support for in-memory provider (#8807)
-- CAPIM: Add startup timeout to the in memory provider (#8831)
-- CAPIM: Add watch to in-memory server multiplexer (#8851)
-- CAPIM: Enable update for coreDNS and kube-proxy (#8899)
-- CAPIM: Fix readme for in-memory provider (#8905)
-- CAPIM: Fixups for watch in in-memory apiServer (#8898)
-- CAPIM: Improve etcd management in CAPIM (#8906)
-- CAPIM: In-memory provider: add apiserver & etcd metrics (#8962)
-- CAPIM: Test/e2e/in-memory: enable unit tests (#8886)
-- CAPIM: Test/e2e/in-memory: improve locking, return errors instead of panic (#8945)
-- CAPIM: Test/e2e/in-memory: improve performance by fixing locking issues (#8895)
-- CAPIM: Test/e2e/in-memory: sync in-memory provider ClusterClass with test (#8892)
-- CAPIM: Test/infra/inmemory: rename controller files (#8830)
-- CI: Add area/dependency label for dependabot when bumping gomod (#8410)
-- CI: Add dependency label to github actions update (#8845)
-- CI: Switch cloudbuild.yaml to e2 machine type (#8428)
-- CI: Verifier: change back to pull_request_target to make it work again (#8637)
-- ClusterCacheTracker: Deprecate DefaultIndex usage and remove where not needed (#8855)
-- ClusterCacheTracker: Ensure Get/List calls are not getting stuck when apiserver is unreachable (#9030)
-- ClusterCacheTracker: Fix accessor deletion on health check failure (#9031)
-- ClusterCacheTracker: Improve error when workload cluster is not reachable (#8801)
-- ClusterCacheTracker: Use ClusterCacheTracker consistently (instead of NewClusterClient) (#8744)
-- ClusterCacheTracker: Use rest config from ClusterCacheTracker consistently (#8894)
-- ClusterClass: Cluster/topology: use cached Cluster get in Reconcile (#8936)
-- ClusterClass: Cluster/topology: use cached MD list in get current state (#8922)
-- ClusterClass: Deprecate rolloutAfter in cluster topology (#8324)
-- ClusterClass: Upgrading control plane should only be blocked if MD are upgrading (not just rolling out) (#8658)
-- clusterctl: Add labels to OWNERS file (#8342)
-- clusterctl: Add move annotation on objects for cluster move operation (#8322)
-- clusterctl: Bump controller-tools to v0.12 (#8581)
-- clusterctl: CAPIM: use consistent spelling for release artifacts (#8853)
-- clusterctl: Drop cgroup-driver patch for CAPD (#8921)
-- clusterctl: Fix spelling mistake in clusterctl comment (#8344)
-- clusterctl: Prepare main branch for v1.5 development (#8430)
-- clusterctl: Update cert-manager to v1.12.2 (#8883)
-- Core: Cache unstructured in Cluster, MD and MS controller (#8916)
-- Core: Remove unnecessary requeues (#8743)
-- Dependency: Bump actions/checkout from 3.3.0 to 3.4.0 (#8321)
-- Dependency: Bump actions/checkout from 3.4.0 to 3.5.0 (#8389)
-- Dependency: Bump actions/checkout from 3.5.0 to 3.5.2 (#8540)
-- Dependency: Bump actions/checkout from 3.5.2 to 3.5.3 (#8837)
-- Dependency: Bump actions/setup-go from 3.5.0 to 4.0.1 (#8664)
-- Dependency: Bump docker to v24.0.5 (#9065)
-- Dependency: Bump docker/distribution to v2.8.2 (#8645)
-- Dependency: Bump EndBug/add-and-commit from 9.1.1 to 9.1.2 (#8584)
-- Dependency: Bump EndBug/add-and-commit from 9.1.2 to 9.1.3 (#8621)
-- Dependency: Bump gcb-docker-gcloud from v20230424-910a2a439d to v20230522-312425ae46 (#8770)
-- Dependency: Bump gcb-docker-gcloud image (#8570)
-- Dependency: Bump github.com/emicklei/go-restful/v3  from 3.9.0 to 3.10.2 in /test (#9056)
-- Dependency: Bump github.com/go-logr/logr from 1.2.3 to 1.2.4 (#8461)
-- Dependency: Bump github.com/onsi/ginkgo/v2 from 2.10.0 to 2.11.0 (#8891)
-- Dependency: Bump github.com/onsi/ginkgo/v2 from 2.9.2 to 2.9.4 (#8622)
-- Dependency: Bump github.com/onsi/ginkgo/v2 from 2.9.4 to 2.9.5 (#8666)
-- Dependency: Bump github.com/onsi/ginkgo/v2 from 2.9.5 to 2.9.7 (#8792)
-- Dependency: Bump github.com/onsi/ginkgo/v2 from 2.9.7 to 2.10.0 (#8839)
-- Dependency: Bump github.com/onsi/gomega from 1.27.4 to 1.27.5 (#8390)
-- Dependency: Bump github.com/onsi/gomega from 1.27.6 to 1.27.7 (#8715)
-- Dependency: Bump github.com/onsi/gomega from 1.27.7 to 1.27.8 (#8841)
-- Dependency: Bump github.com/prometheus/client_golang from 1.14.0 to 1.15.0 (#8541)
-- Dependency: Bump github.com/prometheus/client_golang from 1.15.0 to 1.15.1 (#8623)
-- Dependency: Bump github.com/prometheus/client_golang from 1.15.1 to 1.16.0 (#8890)
-- Dependency: Bump github.com/spf13/cobra from 1.6.1 to 1.7.0 (#8502)
-- Dependency: Bump github.com/spf13/viper from 1.15.0 to 1.16.0 (#8791)
-- Dependency: Bump golang version (1.20.3 -> 1.20.4) (#8749)
-- Dependency: Bump golang.org/x/oauth2 from 0.6.0 to 0.7.0 (#8503)
-- Dependency: Bump golang.org/x/oauth2 from 0.7.0 to 0.8.0 (#8665)
-- Dependency: Bump golang.org/x/oauth2 from 0.8.0 to 0.9.0 (#8889)
-- Dependency: Bump golang.org/x/oauth2 from 0.9.0 to 0.10.0 (#8985)
-- Dependency: Bump golang.org/x/text from 0.8.0 to 0.9.0 (#8504)
-- Dependency: Bump golang.org/x/text from 0.9.0 to 0.10.0 (#8840)
-- Dependency: Bump golangci-lint to 1.52.1 and fix findings (#8331)
-- Dependency: Bump golangci-lint to v1.51.2 (#8312)
-- Dependency: Bump golangci/golangci-lint-action from 3.4.0 to 3.5.0 (#8790)
-- Dependency: Bump golangci/golangci-lint-action from 3.5.0 to 3.6.0 (#8838)
-- Dependency: Bump gomodules.xyz/jsonpatch/v2 from 2.2.0 to 2.3.0 (#8716)
-- Dependency: Bump kind to v0.19.0 (#8681)
-- Dependency: Bump kindnet and haproxy images to latest (#8676)
-- Dependency: Bump to Go 1.20.6 (#9058)
-- Dependency: Update cert manager to v1.11.1 (#8529)
-- Dependency: Update cert-manager to v1.11.2 (#8638)
-- Dependency: Update cert-manager to v1.12.0 (#8702)
-- Dependency: Update cert-manager to v1.12.1 (#8748)
-- Dependency: Update controller runtime to 0.14.6 (#8405)
-- Dependency: Update controller tools to v0.11.4 (#8547)
-- Dependency: Update conversion-gen version (v0.26.0 -> v0.27.1) (#8573)
-- Dependency: Update docker to v20.10.24 (#8475)
-- Dependency: Update golangci-lint to v1.53.3 (#8796)
-- Dependency: Update kind to v0.18.0 (#8433)
-- Dependency: Update KIND to v0.20.0 (#8908)
-- Dependency: Update kpromo to v3.5.2 (#8487)
-- Dependency: Update kpromo to v3.6.0 (#8680)
-- Dependency: Update kubebuilder envtest (1.26.0 -> 1.27.1) (#8599)
-- Dependency: Upgrade golang version (1.19.6 -> 1.20.3) (#8527)
-- Devtools: Add go.work to .gitignore and .dockerignore (#8155)
-- Devtools: Align kind registry script with upstream (#8731)
-- Devtools: All: Add flags to enable block profiling (#8934)
-- Devtools: Build golangci-lint locally (#8933)
-- Devtools: Bump containerd to 1.6.18 in hack/tools (#8523)
-- Devtools: Hack/observability: add apiserver request dashboards (#8978)
-- Devtools: Hack/observability: Add Grafana state dashboard, improve metrics (#8834)
-- Devtools: Hack/observability: add metrics-server chart (#8950)
-- Devtools: Hack/observability: add tempo (for traces) (#8920)
-- Devtools: Hack/observability: Bump Loki, Grafana and promtail charts (#7182)
-- Devtools: Hack/observability: disable unused loki-gateway (#8794)
-- Devtools: Hack/observability: improve Grafana dashboards (#8861)
-- Devtools: Hack/observability: move charts so they are ignored by go mod (#8795)
-- Devtools: Hack/observability: skip preload images on non-kind clusters (#8931)
-- Devtools: Hack/observability: use 20 Gi of storage for Prometheus (#8961)
-- Devtools: Pin delve to match go minor version (#8725)
-- Devtools: Protect well know registry from accidental push during tilt up (#8930)
-- e2e: Add E2E test for MachineSet Preflight checks (#8698)
-- e2e: Add improvements to scale e2e (#8910)
-- e2e: Add Scale e2e - development only (#8833)
-- e2e: Add SHA to default KIND image used in e2e tests (#8975)
-- e2e: Assert E2E error responses when waiting for MD nodes (#8501)
-- e2e: Autoscaler e2e test (#8514)
-- e2e: Disable fail-fast by default for e2e tests (#8548)
-- e2e: Improve gomega fail handling in clusterClass rollout (#8769)
-- e2e: Improve logging for MachinePool upgrade test (#8728)
-- e2e: Log leftover processes to eventually detect zombies (#8662)
-- e2e: Modify scale e2e test defaults (#8902)
-- e2e: Move ClusterClass quickstart test from e2e-informing to e2e-main (#8765)
-- e2e: Pin cgroup driver used in v0.3 and v0.4 templates (#8684)
-- e2e: Test/e2e: add field to scale test to deploy Clusters in separate namespace (#8963)
-- e2e: Test/e2e: also dump workload cluster kube-system pods (#8800)
-- e2e: Test/e2e: cleanup test files (#8557)
-- e2e: Test/e2e: Fixup dump kube-system pods (#8809)
-- e2e: Test/e2e: improve logging for a detected rollout (#8953)
-- e2e: Test/e2e: increase apply timeout to 1m (#8941)
-- e2e: Test/framework: add functions to collect infrastructure logs in tests (#8675)
-- e2e: Unhold CI packages before installing (#7910)
-- IPAM: Make IPAddressClaim.Status.AddressRef optional (#8493)
-- KCP: Avoid panic if etcd server misbehaves (#8862)
-- KCP: Cache secrets between LookupOrGenerate and ensureCertificatesOwnerRef (#8926)
-- KCP: Cache unstructured (#8913)
-- KCP: Change endpoints to endpoint in the etcd client (#8909)
-- KCP: Drop redundant get machines (#8912)
-- KCP: Fix noisy error log triggered by missing patch helper (#8863)
-- KCP: Improve error log on scale up when Machine doesn't have a nodeRef (#8564)
-- KCP: Kcp use one workload cluster for reconcile (#8900)
-- KCP: Log the reason of a Machine rollout (#8959)
-- KCP: Remove v1alpha2 adoption logic from KCP (#8328)
-- Machine: Controller/machine: use unstructured caching client (#8896)
-- Machine: Optimize `reconcileInterruptibleNodeLabel` of machine controller (#8852)
-- Machine: Use providerID string as-is (#8577)
-- MachineDeployment: Drop the first return value from FindOldMachineSets (#8415)
-- MachineSet: Add MachineFinalizer during machine computation (#8463)
-- MachineSet: Surface failed preflight checks on MachineSet in `MachinesCreated` condition (#8669)
-- MULTIPLE_AREAS[ClusterCacheTracker/KCP/CAPBK]: Cache secrets in KCP, CABPK and ClusterCacheTracker (#8940)
-- Release: Add area label lookup for prefixes to release notes tool (#8780)
-- Release: Add runtime extension components to release (#8438)
-- Release: Bump kpromo to v3.5.1 (#8301)
-- Release: Fixed grammatically incorrect plurals in release tools (#9054)
-- Release: Improve release notes formatting (#8826)
-- Release: Improve release notes output (#8928)
-- Release: Make release notes generation concurrent (#8817)
-- Runtime SDK: Update handler name validation rules (#8280)
-- Testing: Add calico as e2e test CNI (#8420)
-- Testing: Add dualstack e2e test (#8517)
-- Testing: Add unit test for health check during creation of a new cluster accessor (#8269)
-- Testing: Allow overriding gingko args in e2e tests (#8365)
-- Testing: Fix typos in phrase "to exist" (#8688)
-- Testing: Merge ownerReference tests with quick start (#8264)
-- Testing: Update e2e upgrade test versions (#8361)
-- Testing: Update version matrix for github workflows (#8398)
-- Testing: Validating the warnings in tests (#8778)
-- util: Cache list calls in cluster to objects mapper (#8918)
-- util: Move `internal.labels` to `format` package for use by providers (#9006)
-- util: Rename internal/util/ssa util functions for better naming consistency (#8423)
-
-:book: Additionally, there have been 72 contributions to our documentation and book. (#8252, #8279, #8284, #8288, #8293, #8307, #8308, #8309, #8319, #8327, #8351, #8355, #8363, #8375, #8383, #8397, #8416, #8419, #8439, #8446, #8447, #8454, #8508, #8509, #8510, #8511, #8520, #8521, #8552, #8554, #8559, #8580, #8587, #8593, #8596, #8597, #8612, #8613, #8630, #8632, #8651, #8661, #8673, #8686, #8699, #8701, #8712, #8719, #8729, #8740, #8753, #8760, #8762, #8763, #8775, #8779, #8781, #8782, #8787, #8798, #8802, #8805, #8812, #8843, #8854, #8901, #8924, #8932, #8955, #8956, #8958, #8960) 
-
-
-_Thanks to all our contributors!_ 😊
diff --git a/internal/controllers/machinehealthcheck/machinehealthcheck_controller_test.go b/internal/controllers/machinehealthcheck/machinehealthcheck_controller_test.go
index 1e716bc02..3cbea665c 100644
--- a/internal/controllers/machinehealthcheck/machinehealthcheck_controller_test.go
+++ b/internal/controllers/machinehealthcheck/machinehealthcheck_controller_test.go
@@ -40,7 +40,6 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/client/fake"
 	"sigs.k8s.io/controller-runtime/pkg/log"
 	"sigs.k8s.io/controller-runtime/pkg/reconcile"
-	"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
 
 	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
 	"sigs.k8s.io/cluster-api/api/v1beta1/index"
@@ -1250,9 +1249,7 @@ func TestMachineHealthCheck_Reconcile(t *testing.T) {
 				},
 			},
 		}
-
-		reqCtx := admission.NewContextWithRequest(ctx, admission.Request{})
-		g.Expect((&webhooks.MachineSet{}).Default(reqCtx, machineSet)).Should(Succeed())
+		g.Expect((&webhooks.MachineSet{}).Default(ctx, machineSet)).Should(Succeed())
 		g.Expect(env.Create(ctx, machineSet)).To(Succeed())
 
 		// Ensure machines have been created.
diff --git a/internal/controllers/topology/cluster/reconcile_state_test.go b/internal/controllers/topology/cluster/reconcile_state_test.go
index cdbda35e6..d9171c490 100644
--- a/internal/controllers/topology/cluster/reconcile_state_test.go
+++ b/internal/controllers/topology/cluster/reconcile_state_test.go
@@ -3241,8 +3241,9 @@ func newFakeMachineDeploymentTopologyState(name string, infrastructureMachineTem
 
 	scheme := runtime.NewScheme()
 	_ = clusterv1.AddToScheme(scheme)
-	if err := (&webhooks.MachineDeployment{}).
-		Default(admission.NewContextWithRequest(ctx, admission.Request{}), mdState.Object); err != nil {
+	if err := (&webhooks.MachineDeployment{
+		Decoder: admission.NewDecoder(scheme),
+	}).Default(admission.NewContextWithRequest(ctx, admission.Request{}), mdState.Object); err != nil {
 		panic(err)
 	}
 	return mdState
diff --git a/internal/topology/variables/cluster_variable_defaulting_test.go b/internal/topology/variables/cluster_variable_defaulting_test.go
index b6a7e72e5..f441264e6 100644
--- a/internal/topology/variables/cluster_variable_defaulting_test.go
+++ b/internal/topology/variables/cluster_variable_defaulting_test.go
@@ -929,6 +929,7 @@ func Test_DefaultClusterVariable(t *testing.T) {
 					Schema: clusterv1.VariableSchema{
 						OpenAPIV3Schema: clusterv1.JSONSchemaProps{
 							Type: "object",
+
 							Properties: map[string]clusterv1.JSONSchemaProps{
 								"enabled": {
 									Type:    "boolean",
@@ -948,76 +949,6 @@ func Test_DefaultClusterVariable(t *testing.T) {
 			createVariable: true,
 			want:           nil,
 		},
-		{
-			name: "Default new object variable if there is a top-level default",
-			clusterClassVariable: &statusVariableDefinition{
-				Name: "httpProxy",
-				ClusterClassStatusVariableDefinition: &clusterv1.ClusterClassStatusVariableDefinition{
-					Required: true,
-					Schema: clusterv1.VariableSchema{
-						OpenAPIV3Schema: clusterv1.JSONSchemaProps{
-							Type:    "object",
-							Default: &apiextensionsv1.JSON{Raw: []byte(`{"url":"test-url"}`)},
-							Properties: map[string]clusterv1.JSONSchemaProps{
-								"enabled": {
-									Type:    "boolean",
-									Default: &apiextensionsv1.JSON{Raw: []byte(`false`)},
-								},
-								"url": {
-									Type: "string",
-								},
-								"noProxy": {
-									Type: "string",
-								},
-							},
-						},
-					},
-				},
-			},
-			createVariable: true,
-			want: &clusterv1.ClusterVariable{
-				Name: "httpProxy",
-				Value: apiextensionsv1.JSON{
-					// Defaulting first adds the top-level object and then the enabled field.
-					Raw: []byte(`{"enabled":false,"url":"test-url"}`),
-				},
-			},
-		},
-		{
-			name: "Default new object variable if there is a top-level default (which is an empty object)",
-			clusterClassVariable: &statusVariableDefinition{
-				Name: "httpProxy",
-				ClusterClassStatusVariableDefinition: &clusterv1.ClusterClassStatusVariableDefinition{
-					Required: true,
-					Schema: clusterv1.VariableSchema{
-						OpenAPIV3Schema: clusterv1.JSONSchemaProps{
-							Type:    "object",
-							Default: &apiextensionsv1.JSON{Raw: []byte(`{}`)},
-							Properties: map[string]clusterv1.JSONSchemaProps{
-								"enabled": {
-									Type:    "boolean",
-									Default: &apiextensionsv1.JSON{Raw: []byte(`false`)},
-								},
-								"url": {
-									Type: "string",
-								},
-								"noProxy": {
-									Type: "string",
-								},
-							},
-						},
-					},
-				},
-			},
-			createVariable: true,
-			want: &clusterv1.ClusterVariable{
-				Name: "httpProxy",
-				Value: apiextensionsv1.JSON{
-					// Defaulting first adds the top-level empty object and then the enabled field.
-					Raw: []byte(`{"enabled":false}`),
-				},
-			},
-		},
 		{
 			name: "Don't default existing object variable",
 			clusterClassVariable: &statusVariableDefinition{
diff --git a/internal/webhooks/machinedeployment_test.go b/internal/webhooks/machinedeployment_test.go
index 34125b5b7..ff2823167 100644
--- a/internal/webhooks/machinedeployment_test.go
+++ b/internal/webhooks/machinedeployment_test.go
@@ -52,7 +52,7 @@ func TestMachineDeploymentDefault(t *testing.T) {
 	scheme := runtime.NewScheme()
 	g.Expect(clusterv1.AddToScheme(scheme)).To(Succeed())
 	webhook := &MachineDeployment{
-		decoder: admission.NewDecoder(scheme),
+		Decoder: admission.NewDecoder(scheme),
 	}
 
 	reqCtx := admission.NewContextWithRequest(ctx, admission.Request{
@@ -404,7 +404,7 @@ func TestMachineDeploymentValidation(t *testing.T) {
 			scheme := runtime.NewScheme()
 			g.Expect(clusterv1.AddToScheme(scheme)).To(Succeed())
 			webhook := MachineDeployment{
-				decoder: admission.NewDecoder(scheme),
+				Decoder: admission.NewDecoder(scheme),
 			}
 
 			if tt.expectErr {
@@ -476,7 +476,7 @@ func TestMachineDeploymentVersionValidation(t *testing.T) {
 			scheme := runtime.NewScheme()
 			g.Expect(clusterv1.AddToScheme(scheme)).To(Succeed())
 			webhook := MachineDeployment{
-				decoder: admission.NewDecoder(scheme),
+				Decoder: admission.NewDecoder(scheme),
 			}
 
 			if tt.expectErr {
@@ -538,7 +538,7 @@ func TestMachineDeploymentClusterNameImmutable(t *testing.T) {
 			scheme := runtime.NewScheme()
 			g.Expect(clusterv1.AddToScheme(scheme)).To(Succeed())
 			webhook := MachineDeployment{
-				decoder: admission.NewDecoder(scheme),
+				Decoder: admission.NewDecoder(scheme),
 			}
 
 			warnings, err := webhook.ValidateUpdate(ctx, oldMD, newMD)
@@ -590,7 +590,7 @@ func TestMachineDeploymentTemplateMetadataValidation(t *testing.T) {
 			scheme := runtime.NewScheme()
 			g.Expect(clusterv1.AddToScheme(scheme)).To(Succeed())
 			webhook := MachineDeployment{
-				decoder: admission.NewDecoder(scheme),
+				Decoder: admission.NewDecoder(scheme),
 			}
 
 			if tt.expectErr {
diff --git a/internal/webhooks/machineset_test.go b/internal/webhooks/machineset_test.go
index d9779b799..8ee8ab916 100644
--- a/internal/webhooks/machineset_test.go
+++ b/internal/webhooks/machineset_test.go
@@ -17,15 +17,12 @@ limitations under the License.
 package webhooks
 
 import (
-	"context"
 	"strings"
 	"testing"
 
 	. "github.com/onsi/gomega"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/utils/pointer"
-	"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
 
 	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
 	"sigs.k8s.io/cluster-api/internal/webhooks/util"
@@ -45,16 +42,10 @@ func TestMachineSetDefault(t *testing.T) {
 			},
 		},
 	}
+	webhook := &MachineSet{}
 
-	scheme := runtime.NewScheme()
-	g.Expect(clusterv1.AddToScheme(scheme)).To(Succeed())
-	webhook := &MachineSet{
-		decoder: admission.NewDecoder(scheme),
-	}
-
-	reqCtx := admission.NewContextWithRequest(ctx, admission.Request{})
-	t.Run("for MachineSet", util.CustomDefaultValidateTest(reqCtx, ms, webhook))
-	g.Expect(webhook.Default(reqCtx, ms)).To(Succeed())
+	t.Run("for MachineSet", util.CustomDefaultValidateTest(ctx, ms, webhook))
+	g.Expect(webhook.Default(ctx, ms)).To(Succeed())
 
 	g.Expect(ms.Labels[clusterv1.ClusterNameLabel]).To(Equal(ms.Spec.ClusterName))
 	g.Expect(ms.Spec.DeletePolicy).To(Equal(string(clusterv1.RandomMachineSetDeletePolicy)))
@@ -63,169 +54,6 @@ func TestMachineSetDefault(t *testing.T) {
 	g.Expect(*ms.Spec.Template.Spec.Version).To(Equal("v1.19.10"))
 }
 
-func TestCalculateMachineSetReplicas(t *testing.T) {
-	tests := []struct {
-		name             string
-		newMS            *clusterv1.MachineSet
-		oldMS            *clusterv1.MachineSet
-		expectedReplicas int32
-		expectErr        bool
-	}{
-		{
-			name: "if new MS has replicas set, keep that value",
-			newMS: &clusterv1.MachineSet{
-				Spec: clusterv1.MachineSetSpec{
-					Replicas: pointer.Int32(5),
-				},
-			},
-			expectedReplicas: 5,
-		},
-		{
-			name:             "if new MS does not have replicas set and no annotations, use 1",
-			newMS:            &clusterv1.MachineSet{},
-			expectedReplicas: 1,
-		},
-		{
-			name: "if new MS only has min size annotation, fallback to 1",
-			newMS: &clusterv1.MachineSet{
-				ObjectMeta: metav1.ObjectMeta{
-					Annotations: map[string]string{
-						clusterv1.AutoscalerMinSizeAnnotation: "3",
-					},
-				},
-			},
-			expectedReplicas: 1,
-		},
-		{
-			name: "if new MS only has max size annotation, fallback to 1",
-			newMS: &clusterv1.MachineSet{
-				ObjectMeta: metav1.ObjectMeta{
-					Annotations: map[string]string{
-						clusterv1.AutoscalerMaxSizeAnnotation: "7",
-					},
-				},
-			},
-			expectedReplicas: 1,
-		},
-		{
-			name: "if new MS has min and max size annotation and min size is invalid, fail",
-			newMS: &clusterv1.MachineSet{
-				ObjectMeta: metav1.ObjectMeta{
-					Annotations: map[string]string{
-						clusterv1.AutoscalerMinSizeAnnotation: "abc",
-						clusterv1.AutoscalerMaxSizeAnnotation: "7",
-					},
-				},
-			},
-			expectErr: true,
-		},
-		{
-			name: "if new MS has min and max size annotation and max size is invalid, fail",
-			newMS: &clusterv1.MachineSet{
-				ObjectMeta: metav1.ObjectMeta{
-					Annotations: map[string]string{
-						clusterv1.AutoscalerMinSizeAnnotation: "3",
-						clusterv1.AutoscalerMaxSizeAnnotation: "abc",
-					},
-				},
-			},
-			expectErr: true,
-		},
-		{
-			name: "if new MS has min and max size annotation and new MS is a new MS, use min size",
-			newMS: &clusterv1.MachineSet{
-				ObjectMeta: metav1.ObjectMeta{
-					Annotations: map[string]string{
-						clusterv1.AutoscalerMinSizeAnnotation: "3",
-						clusterv1.AutoscalerMaxSizeAnnotation: "7",
-					},
-				},
-			},
-			expectedReplicas: 3,
-		},
-		{
-			name: "if new MS has min and max size annotation and old MS doesn't have replicas set, use min size",
-			newMS: &clusterv1.MachineSet{
-				ObjectMeta: metav1.ObjectMeta{
-					Annotations: map[string]string{
-						clusterv1.AutoscalerMinSizeAnnotation: "3",
-						clusterv1.AutoscalerMaxSizeAnnotation: "7",
-					},
-				},
-			},
-			oldMS:            &clusterv1.MachineSet{},
-			expectedReplicas: 3,
-		},
-		{
-			name: "if new MS has min and max size annotation and old MS replicas is below min size, use min size",
-			newMS: &clusterv1.MachineSet{
-				ObjectMeta: metav1.ObjectMeta{
-					Annotations: map[string]string{
-						clusterv1.AutoscalerMinSizeAnnotation: "3",
-						clusterv1.AutoscalerMaxSizeAnnotation: "7",
-					},
-				},
-			},
-			oldMS: &clusterv1.MachineSet{
-				Spec: clusterv1.MachineSetSpec{
-					Replicas: pointer.Int32(1),
-				},
-			},
-			expectedReplicas: 3,
-		},
-		{
-			name: "if new MS has min and max size annotation and old MS replicas is above max size, use max size",
-			newMS: &clusterv1.MachineSet{
-				ObjectMeta: metav1.ObjectMeta{
-					Annotations: map[string]string{
-						clusterv1.AutoscalerMinSizeAnnotation: "3",
-						clusterv1.AutoscalerMaxSizeAnnotation: "7",
-					},
-				},
-			},
-			oldMS: &clusterv1.MachineSet{
-				Spec: clusterv1.MachineSetSpec{
-					Replicas: pointer.Int32(15),
-				},
-			},
-			expectedReplicas: 7,
-		},
-		{
-			name: "if new MS has min and max size annotation and old MS replicas is between min and max size, use old MS replicas",
-			newMS: &clusterv1.MachineSet{
-				ObjectMeta: metav1.ObjectMeta{
-					Annotations: map[string]string{
-						clusterv1.AutoscalerMinSizeAnnotation: "3",
-						clusterv1.AutoscalerMaxSizeAnnotation: "7",
-					},
-				},
-			},
-			oldMS: &clusterv1.MachineSet{
-				Spec: clusterv1.MachineSetSpec{
-					Replicas: pointer.Int32(4),
-				},
-			},
-			expectedReplicas: 4,
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			g := NewWithT(t)
-
-			replicas, err := calculateMachineSetReplicas(context.Background(), tt.oldMS, tt.newMS, false)
-
-			if tt.expectErr {
-				g.Expect(err).To(HaveOccurred())
-				return
-			}
-
-			g.Expect(err).ToNot(HaveOccurred())
-			g.Expect(replicas).To(Equal(tt.expectedReplicas))
-		})
-	}
-}
-
 func TestMachineSetLabelSelectorMatchValidation(t *testing.T) {
 	tests := []struct {
 		name      string
diff --git a/test/e2e/clusterclass_changes.go b/test/e2e/clusterclass_changes.go
index 99f0d6c86..3f2e9cd5e 100644
--- a/test/e2e/clusterclass_changes.go
+++ b/test/e2e/clusterclass_changes.go
@@ -88,24 +88,6 @@ type ClusterClassChangesSpecInput struct {
 	//   "spec.template.spec.path.to.field": <value>,
 	// }
 	ModifyMachineDeploymentInfrastructureMachineTemplateFields map[string]interface{}
-
-	// ModifyMachinePoolBootstrapConfigTemplateFields are the fields which will be set on the
-	// BootstrapConfigTemplate of all MachinePoolClasses of the ClusterClass after the initial Cluster creation.
-	// The test verifies that these fields are rolled out to the MachinePools.
-	// NOTE: The fields are configured in the following format:
-	// map[string]interface{}{
-	//   "spec.template.spec.path.to.field": <value>,
-	// }
-	ModifyMachinePoolBootstrapConfigTemplateFields map[string]interface{}
-
-	// ModifyMachinePoolInfrastructureMachinePoolTemplateFields are the fields which will be set on the
-	// InfrastructureMachinePoolTemplate of all MachinePoolClasses of the ClusterClass after the initial Cluster creation.
-	// The test verifies that these fields are rolled out to the MachinePools.
-	// NOTE: The fields are configured in the following format:
-	// map[string]interface{}{
-	//   "spec.template.spec.path.to.field": <value>,
-	// }
-	ModifyMachinePoolInfrastructureMachinePoolTemplateFields map[string]interface{}
 }
 
 // ClusterClassChangesSpec implements a test that verifies that ClusterClass changes are rolled out successfully.
@@ -199,16 +181,6 @@ func ClusterClassChangesSpec(ctx context.Context, inputGetter func() ClusterClas
 			WaitForMachineDeployments:                 input.E2EConfig.GetIntervals(specName, "wait-worker-nodes"),
 		})
 
-		By("Modifying the MachinePool configuration in ClusterClass and wait for changes to be applied to the MachinePool objects")
-		modifyMachinePoolViaClusterClassAndWait(ctx, modifyMachinePoolViaClusterClassAndWaitInput{
-			ClusterProxy:                        input.BootstrapClusterProxy,
-			ClusterClass:                        clusterResources.ClusterClass,
-			Cluster:                             clusterResources.Cluster,
-			ModifyBootstrapConfigTemplateFields: input.ModifyMachinePoolBootstrapConfigTemplateFields,
-			ModifyInfrastructureMachinePoolTemplateFields: input.ModifyMachinePoolInfrastructureMachinePoolTemplateFields,
-			WaitForMachinePools:                           input.E2EConfig.GetIntervals(specName, "wait-machine-pool-nodes"),
-		})
-
 		By("Rebasing the Cluster to a ClusterClass with a modified label for MachineDeployments and wait for changes to be applied to the MachineDeployment objects")
 		rebaseClusterClassAndWait(ctx, rebaseClusterClassAndWaitInput{
 			ClusterProxy:              input.BootstrapClusterProxy,
@@ -563,14 +535,13 @@ func modifyMachinePoolViaClusterClassAndWait(ctx context.Context, input modifyMa
 				}
 
 				// Get the corresponding InfrastructureMachinePoolTemplate.
-				infrastructureMachinePoolRef := mp.Spec.Template.Spec.InfrastructureRef
-				infrastructureMachinePool, err := external.Get(ctx, mgmtClient, &infrastructureMachinePoolRef, input.Cluster.Namespace)
+				infrastructureMachinePoolTemplateRef := mp.Spec.Template.Spec.InfrastructureRef
+				infrastructureMachinePoolTemplate, err := external.Get(ctx, mgmtClient, &infrastructureMachinePoolTemplateRef, input.Cluster.Namespace)
 				g.Expect(err).ToNot(HaveOccurred())
 
 				// Verify that ModifyInfrastructureMachinePoolTemplateFields have been set.
 				for fieldPath, expectedValue := range input.ModifyInfrastructureMachinePoolTemplateFields {
-					fieldPath = strings.TrimPrefix(fieldPath, "spec.template.")
-					currentValue, ok, err := unstructured.NestedFieldNoCopy(infrastructureMachinePool.Object, strings.Split(fieldPath, ".")...)
+					currentValue, ok, err := unstructured.NestedFieldNoCopy(infrastructureMachinePoolTemplate.Object, strings.Split(fieldPath, ".")...)
 					g.Expect(err).ToNot(HaveOccurred(), fmt.Sprintf("failed to get field %q", fieldPath))
 					g.Expect(ok).To(BeTrue(), fmt.Sprintf("failed to get field %q", fieldPath))
 					g.Expect(currentValue).To(Equal(expectedValue), fmt.Sprintf("field %q should be equal", fieldPath))
@@ -648,9 +619,8 @@ func assertMachinePoolTopologyFields(g Gomega, mp expv1.MachinePool, mpTopology
 		g.Expect(mp.Spec.MinReadySeconds).To(Equal(mpTopology.MinReadySeconds))
 	}
 
-	if mpTopology.FailureDomains != nil {
-		g.Expect(mp.Spec.FailureDomains).NotTo(BeNil())
-		g.Expect(mpTopology.FailureDomains).To(Equal(mp.Spec.FailureDomains))
+	if mpTopology.FailureDomains != nil && mp.Spec.Template.Spec.FailureDomain != nil {
+		g.Expect(mpTopology.FailureDomains).To(ContainElement(mp.Spec.Template.Spec.FailureDomain))
 	}
 }
 
diff --git a/test/e2e/clusterclass_changes_test.go b/test/e2e/clusterclass_changes_test.go
index d4a33c243..5d8fbab9a 100644
--- a/test/e2e/clusterclass_changes_test.go
+++ b/test/e2e/clusterclass_changes_test.go
@@ -63,29 +63,6 @@ var _ = Describe("When testing ClusterClass changes [ClusterClass]", func() {
 					},
 				},
 			},
-			// ModifyMachinePoolBootstrapConfigTemplateFields are the fields which will be set on the
-			// BootstrapConfigTemplate of all MachinePoolClasses of the ClusterClass after the initial Cluster creation.
-			// The test verifies that these fields are rolled out to the MachinePools.
-			ModifyMachinePoolBootstrapConfigTemplateFields: map[string]interface{}{
-				"spec.template.spec.verbosity": int64(4),
-			},
-			// ModifyMachinePoolInfrastructureMachineTemplateFields are the fields which will be set on the
-			// InfrastructureMachinePoolTemplate of all MachinePoolClasses of the ClusterClass after the initial Cluster creation.
-			// The test verifies that these fields are rolled out to the MachinePools.
-			ModifyMachinePoolInfrastructureMachinePoolTemplateFields: map[string]interface{}{
-				"spec.template.spec.template.extraMounts": []interface{}{
-					map[string]interface{}{
-						"containerPath": "/var/run/docker.sock",
-						"hostPath":      "/var/run/docker.sock",
-					},
-					map[string]interface{}{
-						// /tmp cannot be used as containerPath as
-						// it already exists.
-						"containerPath": "/test",
-						"hostPath":      "/tmp",
-					},
-				},
-			},
 		}
 	})
 })
diff --git a/test/e2e/clusterclass_rollout.go b/test/e2e/clusterclass_rollout.go
index 01e7cbe3b..11be32180 100644
--- a/test/e2e/clusterclass_rollout.go
+++ b/test/e2e/clusterclass_rollout.go
@@ -187,7 +187,7 @@ func ClusterClassRolloutSpec(ctx context.Context, inputGetter func() ClusterClas
 			},
 			WaitForMachineDeployments: input.E2EConfig.GetIntervals(specName, "wait-worker-nodes"),
 		})
-		By("Modifying the MachinePool configuration via Cluster topology and wait for changes to be applied to the MachinePools (in-place)")
+		By("Modifying the MachinePool configuration via Cluster topology and wait for changes to be applied to the MachinePool (in-place)")
 		modifyMachinePoolViaClusterAndWait(ctx, modifyMachinePoolViaClusterAndWaitInput{
 			ClusterProxy: input.BootstrapClusterProxy,
 			Cluster:      clusterResources.Cluster,
@@ -204,7 +204,7 @@ func ClusterClassRolloutSpec(ctx context.Context, inputGetter func() ClusterClas
 				topology.NodeVolumeDetachTimeout = &metav1.Duration{Duration: time.Duration(rand.Intn(20)) * time.Second} //nolint:gosec
 				topology.MinReadySeconds = pointer.Int32(rand.Int31n(20))                                                 //nolint:gosec
 			},
-			WaitForMachinePools: input.E2EConfig.GetIntervals(specName, "wait-machine-pool-nodes"),
+			WaitForMachinePools: input.E2EConfig.GetIntervals(specName, "wait-worker-nodes"),
 		})
 		By("Verifying there are no unexpected rollouts through in-place rollout")
 		Consistently(func(g Gomega) {
@@ -243,7 +243,7 @@ func ClusterClassRolloutSpec(ctx context.Context, inputGetter func() ClusterClas
 			ModifyBootstrapConfigTemplateFields: map[string]interface{}{
 				"spec.template.spec.verbosity": int64(4),
 			},
-			WaitForMachinePools: input.E2EConfig.GetIntervals(specName, "wait-machine-pool-nodes"),
+			WaitForMachinePools: input.E2EConfig.GetIntervals(specName, "wait-worker-nodes"),
 		})
 		By("Verifying all Machines are replaced through rollout")
 		Eventually(func(g Gomega) {
@@ -647,7 +647,7 @@ func assertMachinePools(g Gomega, clusterClassObjects clusterClassObjects, clust
 		g.Expect(
 			union(
 				machinePool.Annotations,
-			),
+			).without(g, clusterv1.RevisionAnnotation),
 		).To(BeEquivalentTo(
 			union(
 				mpTopology.Metadata.Annotations,
@@ -674,52 +674,68 @@ func assertMachinePools(g Gomega, clusterClassObjects clusterClassObjects, clust
 			),
 		))
 
-		// MachinePool InfrastructureMachinePool.metadata
-		ccInfrastructureMachinePoolTemplate := clusterClassObjects.InfrastructureMachinePoolTemplateByMachinePoolClass[mpClass.Class]
-		ccInfrastructureMachinePoolTemplateTemplateMetadata := mustMetadata(contract.InfrastructureMachinePoolTemplate().Template().Metadata().Get(ccInfrastructureMachinePoolTemplate))
-		infrastructureMachinePool := clusterObjects.InfrastructureMachinePoolByMachinePool[machinePool.Name]
-		g.Expect(infrastructureMachinePool.GetLabels()).To(BeEquivalentTo(
+		// MachinePool InfrastructureMachineTemplate.metadata
+		ccInfrastructureMachineTemplate := clusterClassObjects.InfrastructureMachinePoolTemplateByMachinePoolClass[mpClass.Class]
+		ccInfrastructureMachineTemplateTemplateMetadata := mustMetadata(contract.InfrastructureMachineTemplate().Template().Metadata().Get(ccInfrastructureMachineTemplate))
+		infrastructureMachineTemplate := clusterObjects.InfrastructureMachinePoolTemplateByMachinePool[machinePool.Name]
+		infrastructureMachineTemplateTemplateMetadata := mustMetadata(contract.InfrastructureMachineTemplate().Template().Metadata().Get(infrastructureMachineTemplate))
+		g.Expect(infrastructureMachineTemplate.GetLabels()).To(BeEquivalentTo(
 			union(
 				map[string]string{
 					clusterv1.ClusterNameLabel:                    cluster.Name,
 					clusterv1.ClusterTopologyOwnedLabel:           "",
 					clusterv1.ClusterTopologyMachinePoolNameLabel: mpTopology.Name,
 				},
-				ccInfrastructureMachinePoolTemplateTemplateMetadata.Labels,
+				ccInfrastructureMachineTemplate.GetLabels(),
 			),
 		))
-		g.Expect(infrastructureMachinePool.GetAnnotations()).To(BeEquivalentTo(
+		g.Expect(infrastructureMachineTemplate.GetAnnotations()).To(BeEquivalentTo(
 			union(
 				map[string]string{
 					clusterv1.TemplateClonedFromGroupKindAnnotation: groupKind(mpClass.Template.Infrastructure.Ref),
 					clusterv1.TemplateClonedFromNameAnnotation:      mpClass.Template.Infrastructure.Ref.Name,
 				},
-				ccInfrastructureMachinePoolTemplateTemplateMetadata.Annotations,
-			),
+				ccInfrastructureMachineTemplate.GetAnnotations(),
+			).without(g, corev1.LastAppliedConfigAnnotation),
+		))
+		// MachinePool InfrastructureMachineTemplate.spec.template.metadata
+		g.Expect(infrastructureMachineTemplateTemplateMetadata.Labels).To(BeEquivalentTo(
+			ccInfrastructureMachineTemplateTemplateMetadata.Labels,
+		))
+		g.Expect(infrastructureMachineTemplateTemplateMetadata.Annotations).To(BeEquivalentTo(
+			ccInfrastructureMachineTemplateTemplateMetadata.Annotations,
 		))
 
-		// MachinePool BootstrapConfig.metadata
+		// MachinePool BootstrapConfigTemplate.metadata
 		ccBootstrapConfigTemplate := clusterClassObjects.BootstrapConfigTemplateByMachinePoolClass[mpClass.Class]
 		ccBootstrapConfigTemplateTemplateMetadata := mustMetadata(contract.BootstrapConfigTemplate().Template().Metadata().Get(ccBootstrapConfigTemplate))
-		bootstrapConfig := clusterObjects.BootstrapConfigByMachinePool[machinePool.Name]
-		g.Expect(bootstrapConfig.GetLabels()).To(BeEquivalentTo(
+		bootstrapConfigTemplate := clusterObjects.BootstrapConfigTemplateByMachinePool[machinePool.Name]
+		bootstrapConfigTemplateTemplateMetadata := mustMetadata(contract.BootstrapConfigTemplate().Template().Metadata().Get(bootstrapConfigTemplate))
+		g.Expect(bootstrapConfigTemplate.GetLabels()).To(BeEquivalentTo(
 			union(
 				map[string]string{
 					clusterv1.ClusterNameLabel:                    cluster.Name,
 					clusterv1.ClusterTopologyOwnedLabel:           "",
 					clusterv1.ClusterTopologyMachinePoolNameLabel: mpTopology.Name,
 				},
-				ccBootstrapConfigTemplateTemplateMetadata.Labels,
+				ccBootstrapConfigTemplate.GetLabels(),
 			),
 		))
-		g.Expect(bootstrapConfig.GetAnnotations()).To(BeEquivalentTo(
+		g.Expect(bootstrapConfigTemplate.GetAnnotations()).To(BeEquivalentTo(
 			union(
 				map[string]string{
 					clusterv1.TemplateClonedFromGroupKindAnnotation: groupKind(mpClass.Template.Bootstrap.Ref),
 					clusterv1.TemplateClonedFromNameAnnotation:      mpClass.Template.Bootstrap.Ref.Name,
 				},
-				ccBootstrapConfigTemplateTemplateMetadata.Annotations,
-			),
+				ccBootstrapConfigTemplate.GetAnnotations(),
+			).without(g, corev1.LastAppliedConfigAnnotation),
+		))
+		// MachinePool BootstrapConfigTemplate.spec.template.metadata
+		g.Expect(bootstrapConfigTemplateTemplateMetadata.Labels).To(BeEquivalentTo(
+			ccBootstrapConfigTemplateTemplateMetadata.Labels,
+		))
+		g.Expect(bootstrapConfigTemplateTemplateMetadata.Annotations).To(BeEquivalentTo(
+			ccBootstrapConfigTemplateTemplateMetadata.Annotations,
 		))
 	}
 }
@@ -1031,8 +1047,6 @@ func getClusterClassObjects(ctx context.Context, g Gomega, clusterProxy framewor
 	res := clusterClassObjects{
 		InfrastructureMachineTemplateByMachineDeploymentClass: map[string]*unstructured.Unstructured{},
 		BootstrapConfigTemplateByMachineDeploymentClass:       map[string]*unstructured.Unstructured{},
-		InfrastructureMachinePoolTemplateByMachinePoolClass:   map[string]*unstructured.Unstructured{},
-		BootstrapConfigTemplateByMachinePoolClass:             map[string]*unstructured.Unstructured{},
 	}
 	var err error
 
@@ -1055,16 +1069,6 @@ func getClusterClassObjects(ctx context.Context, g Gomega, clusterProxy framewor
 		res.BootstrapConfigTemplateByMachineDeploymentClass[mdClass.Class] = bootstrapConfigTemplate
 	}
 
-	for _, mpClass := range clusterClass.Spec.Workers.MachinePools {
-		infrastructureMachinePoolTemplate, err := external.Get(ctx, mgmtClient, mpClass.Template.Infrastructure.Ref, clusterClass.Namespace)
-		g.Expect(err).ToNot(HaveOccurred())
-		res.InfrastructureMachinePoolTemplateByMachinePoolClass[mpClass.Class] = infrastructureMachinePoolTemplate
-
-		bootstrapConfigTemplate, err := external.Get(ctx, mgmtClient, mpClass.Template.Bootstrap.Ref, clusterClass.Namespace)
-		g.Expect(err).ToNot(HaveOccurred())
-		res.BootstrapConfigTemplateByMachinePoolClass[mpClass.Class] = bootstrapConfigTemplate
-	}
-
 	return res
 }
 
@@ -1085,8 +1089,8 @@ type clusterObjects struct {
 	InfrastructureMachineTemplateByMachineDeployment map[string]*unstructured.Unstructured
 	BootstrapConfigTemplateByMachineDeployment       map[string]*unstructured.Unstructured
 
-	InfrastructureMachinePoolByMachinePool map[string]*unstructured.Unstructured
-	BootstrapConfigByMachinePool           map[string]*unstructured.Unstructured
+	InfrastructureMachinePoolTemplateByMachinePool map[string]*unstructured.Unstructured
+	BootstrapConfigTemplateByMachinePool           map[string]*unstructured.Unstructured
 
 	InfrastructureMachineByMachine map[string]*unstructured.Unstructured
 	BootstrapConfigByMachine       map[string]*unstructured.Unstructured
@@ -1105,8 +1109,6 @@ func getClusterObjects(ctx context.Context, g Gomega, clusterProxy framework.Clu
 		InfrastructureMachineTemplateByMachineDeployment: map[string]*unstructured.Unstructured{},
 		BootstrapConfigByMachine:                         map[string]*unstructured.Unstructured{},
 		InfrastructureMachineByMachine:                   map[string]*unstructured.Unstructured{},
-		BootstrapConfigByMachinePool:                     map[string]*unstructured.Unstructured{},
-		InfrastructureMachinePoolByMachinePool:           map[string]*unstructured.Unstructured{},
 	}
 	var err error
 
@@ -1175,26 +1177,6 @@ func getClusterObjects(ctx context.Context, g Gomega, clusterProxy framework.Clu
 		}
 	}
 
-	// MachinePools.
-	for _, mpTopology := range cluster.Spec.Topology.Workers.MachinePools {
-		// Get MachinePool for the current MachinePoolTopology.
-		mpList := &expv1.MachinePoolList{}
-		g.Expect(mgmtClient.List(ctx, mpList, client.InNamespace(cluster.Namespace), client.MatchingLabels{
-			clusterv1.ClusterTopologyMachinePoolNameLabel: mpTopology.Name,
-		})).To(Succeed())
-		g.Expect(mpList.Items).To(HaveLen(1), fmt.Sprintf("expected one MachinePool for topology %q, but got %d", mpTopology.Name, len(mpList.Items)))
-		mp := mpList.Items[0]
-		res.MachinePools = append(res.MachinePools, &mp)
-
-		bootstrapConfig, err := external.Get(ctx, mgmtClient, mp.Spec.Template.Spec.Bootstrap.ConfigRef, cluster.Namespace)
-		g.Expect(err).ToNot(HaveOccurred())
-		res.BootstrapConfigByMachinePool[mp.Name] = bootstrapConfig
-
-		infrastructureMachinePool, err := external.Get(ctx, mgmtClient, &mp.Spec.Template.Spec.InfrastructureRef, cluster.Namespace)
-		g.Expect(err).ToNot(HaveOccurred())
-		res.InfrastructureMachinePoolByMachinePool[mp.Name] = infrastructureMachinePool
-	}
-
 	return res
 }
 
diff --git a/test/extension/main.go b/test/extension/main.go
index 10ca719cd..82af08549 100644
--- a/test/extension/main.go
+++ b/test/extension/main.go
@@ -38,7 +38,6 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/client"
 
 	bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1beta1"
-	"sigs.k8s.io/cluster-api/controllers/remote"
 	controlplanev1 "sigs.k8s.io/cluster-api/controlplane/kubeadm/api/v1beta1"
 	runtimecatalog "sigs.k8s.io/cluster-api/exp/runtime/catalog"
 	runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1"
@@ -56,8 +55,7 @@ var (
 	// scheme is a Kubernetes runtime scheme containing all the information about API types used by the test extension.
 	// NOTE: it is not mandatory to use scheme in custom RuntimeExtension, but working with typed API objects makes code
 	// easier to read and less error prone than using unstructured or working with raw json/yaml.
-	scheme         = runtime.NewScheme()
-	controllerName = "cluster-api-test-extension-manager"
+	scheme = runtime.NewScheme()
 
 	// Flags.
 	profilerAddress string
@@ -204,7 +202,6 @@ func main() {
 		setupLog.Error(err, "error getting config for the cluster")
 		os.Exit(1)
 	}
-	restConfig.UserAgent = remote.DefaultClusterAPIUserAgent(controllerName)
 
 	client, err := client.New(restConfig, client.Options{})
 	if err != nil {
diff --git a/test/framework/cluster_proxy.go b/test/framework/cluster_proxy.go
index b52943f79..6de289af4 100644
--- a/test/framework/cluster_proxy.go
+++ b/test/framework/cluster_proxy.go
@@ -22,7 +22,6 @@ import (
 	"fmt"
 	"net/url"
 	"os"
-	"os/exec"
 	"path"
 	goruntime "runtime"
 	"sync"
@@ -30,7 +29,6 @@ import (
 
 	. "github.com/onsi/ginkgo/v2"
 	. "github.com/onsi/gomega"
-	pkgerrors "github.com/pkg/errors"
 	corev1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/util/wait"
@@ -44,7 +42,7 @@ import (
 
 	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
 	expv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1"
-	testexec "sigs.k8s.io/cluster-api/test/framework/exec"
+	"sigs.k8s.io/cluster-api/test/framework/exec"
 	"sigs.k8s.io/cluster-api/test/framework/internal/log"
 	"sigs.k8s.io/cluster-api/test/infrastructure/container"
 )
@@ -252,15 +250,7 @@ func (p *clusterProxy) Apply(ctx context.Context, resources []byte, args ...stri
 	Expect(ctx).NotTo(BeNil(), "ctx is required for Apply")
 	Expect(resources).NotTo(BeNil(), "resources is required for Apply")
 
-	if err := testexec.KubectlApply(ctx, p.kubeconfigPath, resources, args...); err != nil {
-		var exitErr *exec.ExitError
-		if errors.As(err, &exitErr) {
-			return pkgerrors.New(fmt.Sprintf("%s: stderr: %s", err.Error(), exitErr.Stderr))
-		}
-		return err
-	}
-
-	return nil
+	return exec.KubectlApply(ctx, p.kubeconfigPath, resources, args...)
 }
 
 func (p *clusterProxy) GetRESTConfig() *rest.Config {
diff --git a/test/framework/clusterctl/client.go b/test/framework/clusterctl/client.go
index 8419689de..92dc77ae5 100644
--- a/test/framework/clusterctl/client.go
+++ b/test/framework/clusterctl/client.go
@@ -18,7 +18,6 @@ package clusterctl
 
 import (
 	"context"
-	"errors"
 	"fmt"
 	"os"
 	"os/exec"
@@ -100,8 +99,7 @@ func InitWithBinary(_ context.Context, binary string, input InitInput) {
 	_ = 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 {
-		var exitErr *exec.ExitError
-		if errors.As(err, &exitErr) {
+		if exitErr, ok := err.(*exec.ExitError); ok {
 			stdErr = string(exitErr.Stderr)
 		}
 	}
@@ -335,8 +333,7 @@ func ConfigClusterWithBinary(_ context.Context, clusterctlBinaryPath string, inp
 	_ = os.WriteFile(filepath.Join(input.LogFolder, fmt.Sprintf("%s-cluster-template.yaml", input.ClusterName)), out, 0644) //nolint:gosec // this is a log file to be shared via prow artifacts
 	var stdErr string
 	if err != nil {
-		var exitErr *exec.ExitError
-		if errors.As(err, &exitErr) {
+		if exitErr, ok := err.(*exec.ExitError); ok {
 			stdErr = string(exitErr.Stderr)
 		}
 	}
diff --git a/test/framework/exec/kubectl.go b/test/framework/exec/kubectl.go
index 98c831379..0b3069348 100644
--- a/test/framework/exec/kubectl.go
+++ b/test/framework/exec/kubectl.go
@@ -21,7 +21,6 @@ import (
 	"context"
 	"fmt"
 	"os"
-	"strings"
 )
 
 // KubectlApply shells out to kubectl apply.
@@ -35,12 +34,13 @@ func KubectlApply(ctx context.Context, kubeconfigPath string, resources []byte,
 		WithArgs(aargs...),
 		WithStdin(rbytes),
 	)
-
-	fmt.Printf("Running kubectl %s\n", strings.Join(aargs, " "))
 	stdout, stderr, err := applyCmd.Run(ctx)
-	fmt.Printf("stderr:\n%s\n", string(stderr))
-	fmt.Printf("stdout:\n%s\n", string(stdout))
-	return err
+	if err != nil {
+		fmt.Println(string(stderr))
+		return err
+	}
+	fmt.Println(string(stdout))
+	return nil
 }
 
 // KubectlWait shells out to kubectl wait.
diff --git a/test/go.mod b/test/go.mod
index 320e9d4de..582ab5095 100644
--- a/test/go.mod
+++ b/test/go.mod
@@ -23,12 +23,12 @@ require (
 	go.etcd.io/etcd/client/v3 v3.5.10
 	golang.org/x/net v0.18.0
 	google.golang.org/grpc v1.59.0
-	k8s.io/api v0.28.4
-	k8s.io/apiextensions-apiserver v0.28.4
-	k8s.io/apimachinery v0.28.4
-	k8s.io/apiserver v0.28.4
-	k8s.io/client-go v0.28.4
-	k8s.io/component-base v0.28.4
+	k8s.io/api v0.28.3
+	k8s.io/apiextensions-apiserver v0.28.3
+	k8s.io/apimachinery v0.28.3
+	k8s.io/apiserver v0.28.3
+	k8s.io/client-go v0.28.3
+	k8s.io/component-base v0.28.3
 	k8s.io/klog/v2 v2.100.1
 	k8s.io/utils v0.0.0-20230406110748-d93618cff8a2
 	sigs.k8s.io/cluster-api v0.0.0-00010101000000-000000000000
@@ -154,8 +154,8 @@ require (
 	gopkg.in/yaml.v2 v2.4.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 	gotest.tools/v3 v3.4.0 // indirect
-	k8s.io/cluster-bootstrap v0.28.4 // indirect
-	k8s.io/kms v0.28.4 // indirect
+	k8s.io/cluster-bootstrap v0.28.3 // indirect
+	k8s.io/kms v0.28.3 // indirect
 	k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 // indirect
 	sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.1.2 // indirect
 	sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
diff --git a/test/go.sum b/test/go.sum
index 88a2ba2a8..8d086c16a 100644
--- a/test/go.sum
+++ b/test/go.sum
@@ -838,24 +838,24 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
 honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
 honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
-k8s.io/api v0.28.4 h1:8ZBrLjwosLl/NYgv1P7EQLqoO8MGQApnbgH8tu3BMzY=
-k8s.io/api v0.28.4/go.mod h1:axWTGrY88s/5YE+JSt4uUi6NMM+gur1en2REMR7IRj0=
-k8s.io/apiextensions-apiserver v0.28.4 h1:AZpKY/7wQ8n+ZYDtNHbAJBb+N4AXXJvyZx6ww6yAJvU=
-k8s.io/apiextensions-apiserver v0.28.4/go.mod h1:pgQIZ1U8eJSMQcENew/0ShUTlePcSGFq6dxSxf2mwPM=
-k8s.io/apimachinery v0.28.4 h1:zOSJe1mc+GxuMnFzD4Z/U1wst50X28ZNsn5bhgIIao8=
-k8s.io/apimachinery v0.28.4/go.mod h1:wI37ncBvfAoswfq626yPTe6Bz1c22L7uaJ8dho83mgg=
-k8s.io/apiserver v0.28.4 h1:BJXlaQbAU/RXYX2lRz+E1oPe3G3TKlozMMCZWu5GMgg=
-k8s.io/apiserver v0.28.4/go.mod h1:Idq71oXugKZoVGUUL2wgBCTHbUR+FYTWa4rq9j4n23w=
-k8s.io/client-go v0.28.4 h1:Np5ocjlZcTrkyRJ3+T3PkXDpe4UpatQxj85+xjaD2wY=
-k8s.io/client-go v0.28.4/go.mod h1:0VDZFpgoZfelyP5Wqu0/r/TRYcLYuJ2U1KEeoaPa1N4=
-k8s.io/cluster-bootstrap v0.28.4 h1:4MKNy1Qd9QY7pl47rSMGIORF+tm3CUaqC1M8U9bjn4Q=
-k8s.io/cluster-bootstrap v0.28.4/go.mod h1:/c4ro/R4yf4EtJgFgFtvnHkbDOHwubeKJXh5R1c89Bc=
-k8s.io/component-base v0.28.4 h1:c/iQLWPdUgI90O+T9TeECg8o7N3YJTiuz2sKxILYcYo=
-k8s.io/component-base v0.28.4/go.mod h1:m9hR0uvqXDybiGL2nf/3Lf0MerAfQXzkfWhUY58JUbU=
+k8s.io/api v0.28.3 h1:Gj1HtbSdB4P08C8rs9AR94MfSGpRhJgsS+GF9V26xMM=
+k8s.io/api v0.28.3/go.mod h1:MRCV/jr1dW87/qJnZ57U5Pak65LGmQVkKTzf3AtKFHc=
+k8s.io/apiextensions-apiserver v0.28.3 h1:Od7DEnhXHnHPZG+W9I97/fSQkVpVPQx2diy+2EtmY08=
+k8s.io/apiextensions-apiserver v0.28.3/go.mod h1:NE1XJZ4On0hS11aWWJUTNkmVB03j9LM7gJSisbRt8Lc=
+k8s.io/apimachinery v0.28.3 h1:B1wYx8txOaCQG0HmYF6nbpU8dg6HvA06x5tEffvOe7A=
+k8s.io/apimachinery v0.28.3/go.mod h1:uQTKmIqs+rAYaq+DFaoD2X7pcjLOqbQX2AOiO0nIpb8=
+k8s.io/apiserver v0.28.3 h1:8Ov47O1cMyeDzTXz0rwcfIIGAP/dP7L8rWbEljRcg5w=
+k8s.io/apiserver v0.28.3/go.mod h1:YIpM+9wngNAv8Ctt0rHG4vQuX/I5rvkEMtZtsxW2rNM=
+k8s.io/client-go v0.28.3 h1:2OqNb72ZuTZPKCl+4gTKvqao0AMOl9f3o2ijbAj3LI4=
+k8s.io/client-go v0.28.3/go.mod h1:LTykbBp9gsA7SwqirlCXBWtK0guzfhpoW4qSm7i9dxo=
+k8s.io/cluster-bootstrap v0.28.3 h1:hGK3mJsmVGGvRJ61nyQcYNR9g/IYax75TbJcylTmZts=
+k8s.io/cluster-bootstrap v0.28.3/go.mod h1:s1B3FTw713b9iw67yGFiVF3zCfw5obrZXWl3EMelvdg=
+k8s.io/component-base v0.28.3 h1:rDy68eHKxq/80RiMb2Ld/tbH8uAE75JdCqJyi6lXMzI=
+k8s.io/component-base v0.28.3/go.mod h1:fDJ6vpVNSk6cRo5wmDa6eKIG7UlIQkaFmZN2fYgIUD8=
 k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg=
 k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
-k8s.io/kms v0.28.4 h1:PMgY/3CQTWP9eIKmNQiTgjLIZ0ns6O+voagzD2/4mSg=
-k8s.io/kms v0.28.4/go.mod h1:HL4/lR/bhjAJPbqycKtfhWiKh1Sp21cpHOL8P4oo87w=
+k8s.io/kms v0.28.3 h1:jYwwAe96XELNjYWv1G4kNzizcFoZ50OOElvPansbw70=
+k8s.io/kms v0.28.3/go.mod h1:kSMjU2tg7vjqqoWVVCcmPmNZ/CofPsoTbSxAipCvZuE=
 k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 h1:LyMgNKD2P8Wn1iAwQU5OhxCKlKJy0sHc+PcDwFB24dQ=
 k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9/go.mod h1:wZK2AVp1uHCp4VamDVgBP2COHZjqD1T68Rf0CM3YjSM=
 k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 h1:qY1Ad8PODbnymg2pRbkyMT/ylpTrCM8P2RJ0yroCyIk=
diff --git a/test/infrastructure/docker/exp/internal/controllers/dockermachinepool_controller.go b/test/infrastructure/docker/exp/internal/controllers/dockermachinepool_controller.go
index e23f46b44..d50281f49 100644
--- a/test/infrastructure/docker/exp/internal/controllers/dockermachinepool_controller.go
+++ b/test/infrastructure/docker/exp/internal/controllers/dockermachinepool_controller.go
@@ -20,7 +20,6 @@ package controllers
 import (
 	"context"
 	"fmt"
-	"time"
 
 	"github.com/pkg/errors"
 	apierrors "k8s.io/apimachinery/pkg/api/errors"
@@ -43,6 +42,7 @@ import (
 	"sigs.k8s.io/cluster-api/controllers/remote"
 	expv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1"
 	utilexp "sigs.k8s.io/cluster-api/exp/util"
+	"sigs.k8s.io/cluster-api/internal/util/ssa"
 	"sigs.k8s.io/cluster-api/test/infrastructure/container"
 	infrav1 "sigs.k8s.io/cluster-api/test/infrastructure/docker/api/v1beta1"
 	infraexpv1 "sigs.k8s.io/cluster-api/test/infrastructure/docker/exp/api/v1beta1"
@@ -57,8 +57,7 @@ const (
 	// dockerMachinePoolLabel is the label used to identify the DockerMachinePool that is responsible for a Docker container.
 	dockerMachinePoolLabel = "docker.cluster.x-k8s.io/machine-pool"
 
-	// requeueAfter is how long to wait before checking again to see if the DockerMachines are still provisioning or deleting.
-	requeueAfter = 10 * time.Second
+	dockerMachinePoolControllerName = "dockermachinepool-controller"
 )
 
 // DockerMachinePoolReconciler reconciles a DockerMachinePool object.
@@ -71,6 +70,7 @@ type DockerMachinePoolReconciler struct {
 	// WatchFilterValue is the label value used to filter events prior to reconciliation.
 	WatchFilterValue string
 
+	ssaCache        ssa.Cache
 	recorder        record.EventRecorder
 	externalTracker external.ObjectTracker
 }
@@ -140,7 +140,7 @@ func (r *DockerMachinePoolReconciler) Reconcile(ctx context.Context, req ctrl.Re
 
 	// Handle deleted machines
 	if !dockerMachinePool.ObjectMeta.DeletionTimestamp.IsZero() {
-		return r.reconcileDelete(ctx, cluster, machinePool, dockerMachinePool)
+		return ctrl.Result{}, r.reconcileDelete(ctx, cluster, machinePool, dockerMachinePool)
 	}
 
 	// Add finalizer and the InfrastructureMachineKind if they aren't already present, and requeue if either were added.
@@ -194,21 +194,22 @@ func (r *DockerMachinePoolReconciler) SetupWithManager(ctx context.Context, mgr
 		return errors.Wrap(err, "failed setting up with a controller manager")
 	}
 
-	r.recorder = mgr.GetEventRecorderFor("dockermachinepool-controller")
+	r.recorder = mgr.GetEventRecorderFor(dockerMachinePoolControllerName)
 	r.externalTracker = external.ObjectTracker{
 		Controller: c,
 		Cache:      mgr.GetCache(),
 	}
+	r.ssaCache = ssa.NewCache()
 
 	return nil
 }
 
-func (r *DockerMachinePoolReconciler) reconcileDelete(ctx context.Context, cluster *clusterv1.Cluster, machinePool *expv1.MachinePool, dockerMachinePool *infraexpv1.DockerMachinePool) (ctrl.Result, error) {
+func (r *DockerMachinePoolReconciler) reconcileDelete(ctx context.Context, cluster *clusterv1.Cluster, machinePool *expv1.MachinePool, dockerMachinePool *infraexpv1.DockerMachinePool) error {
 	log := ctrl.LoggerFrom(ctx)
 
 	dockerMachineList, err := getDockerMachines(ctx, r.Client, *cluster, *machinePool, *dockerMachinePool)
 	if err != nil {
-		return ctrl.Result{}, err
+		return err
 	}
 
 	if len(dockerMachineList.Items) > 0 {
@@ -229,10 +230,9 @@ func (r *DockerMachinePoolReconciler) reconcileDelete(ctx context.Context, clust
 		}
 
 		if len(errs) > 0 {
-			return ctrl.Result{}, kerrors.NewAggregate(errs)
+			return kerrors.NewAggregate(errs)
 		}
-
-		return ctrl.Result{RequeueAfter: requeueAfter}, nil
+		return nil
 	}
 
 	// Once there are no DockerMachines left, ensure there are no Docker containers left behind.
@@ -243,21 +243,21 @@ func (r *DockerMachinePoolReconciler) reconcileDelete(ctx context.Context, clust
 	// List Docker containers, i.e. external machines in the cluster.
 	externalMachines, err := docker.ListMachinesByCluster(ctx, cluster, labelFilters)
 	if err != nil {
-		return ctrl.Result{}, errors.Wrapf(err, "failed to list all machines in the cluster with label \"%s:%s\"", dockerMachinePoolLabel, dockerMachinePool.Name)
+		return errors.Wrapf(err, "failed to list all machines in the cluster with label \"%s:%s\"", dockerMachinePoolLabel, dockerMachinePool.Name)
 	}
 
 	// Providers should similarly ensure that all infrastructure instances are deleted even if the InfraMachine has not been created yet.
 	for _, externalMachine := range externalMachines {
 		log.Info("Deleting Docker container", "container", externalMachine.Name())
 		if err := externalMachine.Delete(ctx); err != nil {
-			return ctrl.Result{}, errors.Wrapf(err, "failed to delete machine %s", externalMachine.Name())
+			return errors.Wrapf(err, "failed to delete machine %s", externalMachine.Name())
 		}
 	}
 
 	// Once all DockerMachines and Docker containers are deleted, remove the finalizer.
 	controllerutil.RemoveFinalizer(dockerMachinePool, infraexpv1.MachinePoolFinalizer)
 
-	return ctrl.Result{}, nil
+	return nil
 }
 
 func (r *DockerMachinePoolReconciler) reconcileNormal(ctx context.Context, cluster *clusterv1.Cluster, machinePool *expv1.MachinePool, dockerMachinePool *infraexpv1.DockerMachinePool) (ctrl.Result, error) {
@@ -318,11 +318,7 @@ func (r *DockerMachinePoolReconciler) reconcileNormal(ctx context.Context, clust
 		return ctrl.Result{}, nil
 	}
 
-	dockerMachinePool.Status.Ready = false
-	conditions.MarkFalse(dockerMachinePool, expv1.ReplicasReadyCondition, expv1.WaitingForReplicasReadyReason, clusterv1.ConditionSeverityInfo, "")
-
-	// if some machine is still provisioning, force reconcile in few seconds to check again infrastructure.
-	return ctrl.Result{RequeueAfter: requeueAfter}, nil
+	return r.updateStatus(ctx, cluster, machinePool, dockerMachinePool, dockerMachineList.Items)
 }
 
 func getDockerMachines(ctx context.Context, c client.Client, cluster clusterv1.Cluster, machinePool expv1.MachinePool, dockerMachinePool infraexpv1.DockerMachinePool) (*infrav1.DockerMachineList, error) {
@@ -380,6 +376,64 @@ func dockerMachineToDockerMachinePool(_ context.Context, o client.Object) []ctrl
 	return nil
 }
 
+// updateStatus updates the Status field for the MachinePool object.
+// It checks for the current state of the replicas and updates the Status of the MachineSet.
+func (r *DockerMachinePoolReconciler) updateStatus(ctx context.Context, cluster *clusterv1.Cluster, machinePool *expv1.MachinePool, dockerMachinePool *infraexpv1.DockerMachinePool, dockerMachines []infrav1.DockerMachine) (ctrl.Result, error) {
+	log := ctrl.LoggerFrom(ctx)
+
+	// List the Docker containers. This corresponds to an InfraMachinePool instance for providers.
+	labelFilters := map[string]string{dockerMachinePoolLabel: dockerMachinePool.Name}
+	externalMachines, err := docker.ListMachinesByCluster(ctx, cluster, labelFilters)
+	if err != nil {
+		return ctrl.Result{}, errors.Wrapf(err, "failed to list all external machines in the cluster")
+	}
+
+	externalMachineMap := make(map[string]*docker.Machine)
+	for _, externalMachine := range externalMachines {
+		externalMachineMap[externalMachine.Name()] = externalMachine
+	}
+	// We can use reuse getDeletionCandidates to get the list of ready DockerMachines and avoid another API call, even though we aren't deleting them here.
+	_, readyMachines, err := r.getDeletionCandidates(ctx, dockerMachines, externalMachineMap, machinePool, dockerMachinePool)
+	if err != nil {
+		return ctrl.Result{}, err
+	}
+
+	readyReplicaCount := len(readyMachines)
+	desiredReplicas := int(*machinePool.Spec.Replicas)
+
+	switch {
+	// We are scaling up
+	case readyReplicaCount < desiredReplicas:
+		conditions.MarkFalse(dockerMachinePool, clusterv1.ResizedCondition, clusterv1.ScalingUpReason, clusterv1.ConditionSeverityWarning, "Scaling up MachineSet to %d replicas (actual %d)", desiredReplicas, readyReplicaCount)
+	// We are scaling down
+	case readyReplicaCount > desiredReplicas:
+		conditions.MarkFalse(dockerMachinePool, clusterv1.ResizedCondition, clusterv1.ScalingDownReason, clusterv1.ConditionSeverityWarning, "Scaling down MachineSet to %d replicas (actual %d)", desiredReplicas, readyReplicaCount)
+	default:
+		// Make sure last resize operation is marked as completed.
+		// NOTE: we are checking the number of machines ready so we report resize completed only when the machines
+		// are actually provisioned (vs reporting completed immediately after the last machine object is created). This convention is also used by KCP.
+		if len(dockerMachines) == readyReplicaCount {
+			if conditions.IsFalse(dockerMachinePool, clusterv1.ResizedCondition) {
+				log.Info("All the replicas are ready", "replicas", readyReplicaCount)
+			}
+			conditions.MarkTrue(dockerMachinePool, clusterv1.ResizedCondition)
+		}
+		// This means that there was no error in generating the desired number of machine objects
+		conditions.MarkTrue(dockerMachinePool, clusterv1.MachinesCreatedCondition)
+	}
+
+	getters := make([]conditions.Getter, 0, len(dockerMachines))
+	for i := range dockerMachines {
+		getters = append(getters, &dockerMachines[i])
+	}
+
+	// Aggregate the operational state of all the machines; while aggregating we are adding the
+	// source ref (reason@machine/name) so the problem can be easily tracked down to its source machine.
+	conditions.SetAggregate(dockerMachinePool, expv1.ReplicasReadyCondition, getters, conditions.AddSourceRef())
+
+	return ctrl.Result{}, nil
+}
+
 func patchDockerMachinePool(ctx context.Context, patchHelper *patch.Helper, dockerMachinePool *infraexpv1.DockerMachinePool) error {
 	conditions.SetSummary(dockerMachinePool,
 		conditions.WithConditions(
diff --git a/test/infrastructure/docker/exp/internal/controllers/dockermachinepool_controller_phases.go b/test/infrastructure/docker/exp/internal/controllers/dockermachinepool_controller_phases.go
index 1a7201c53..414a44c16 100644
--- a/test/infrastructure/docker/exp/internal/controllers/dockermachinepool_controller_phases.go
+++ b/test/infrastructure/docker/exp/internal/controllers/dockermachinepool_controller_phases.go
@@ -33,6 +33,7 @@ import (
 
 	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
 	expv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1"
+	"sigs.k8s.io/cluster-api/internal/util/ssa"
 	infrav1 "sigs.k8s.io/cluster-api/test/infrastructure/docker/api/v1beta1"
 	infraexpv1 "sigs.k8s.io/cluster-api/test/infrastructure/docker/exp/api/v1beta1"
 	"sigs.k8s.io/cluster-api/test/infrastructure/docker/internal/docker"
@@ -142,16 +143,22 @@ func (r *DockerMachinePoolReconciler) reconcileDockerMachines(ctx context.Contex
 	// Create a DockerMachine for each Docker container so we surface the information to the user. Use the same name as the Docker container for the Docker Machine for ease of lookup.
 	// Providers should iterate through their infrastructure instances and ensure that each instance has a corresponding InfraMachine.
 	for _, machine := range externalMachines {
-		if _, ok := dockerMachineMap[machine.Name()]; !ok {
+		if existingMachine, ok := dockerMachineMap[machine.Name()]; ok {
+			log.V(2).Info("Patching existing DockerMachine", "name", existingMachine.Name)
+			desiredMachine := computeDesiredDockerMachine(machine.Name(), cluster, machinePool, dockerMachinePool, &existingMachine)
+			if err := ssa.Patch(ctx, r.Client, dockerMachinePoolControllerName, desiredMachine, ssa.WithCachingProxy{Cache: r.ssaCache, Original: &existingMachine}); err != nil {
+				return errors.Wrapf(err, "failed to update DockerMachine %q", klog.KObj(desiredMachine))
+			}
+
+			dockerMachineMap[desiredMachine.Name] = *desiredMachine
+		} else {
 			log.V(2).Info("Creating a new DockerMachine for Docker container", "container", machine.Name())
-			dockerMachine, err := r.createDockerMachine(ctx, machine.Name(), cluster, machinePool, dockerMachinePool)
-			if err != nil {
+			desiredMachine := computeDesiredDockerMachine(machine.Name(), cluster, machinePool, dockerMachinePool, nil)
+			if err := ssa.Patch(ctx, r.Client, dockerMachinePoolControllerName, desiredMachine); err != nil {
 				return errors.Wrap(err, "failed to create a new docker machine")
 			}
 
-			dockerMachineMap[dockerMachine.Name] = *dockerMachine
-		} else {
-			log.V(4).Info("DockerMachine already exists, nothing to do", "name", machine.Name())
+			dockerMachineMap[desiredMachine.Name] = *desiredMachine
 		}
 	}
 
@@ -233,30 +240,15 @@ func (r *DockerMachinePoolReconciler) reconcileDockerMachines(ctx context.Contex
 	return nil
 }
 
-// createDockerMachine creates a DockerMachine to represent a Docker container in a DockerMachinePool.
+// computeDesiredDockerMachine creates a DockerMachine to represent a Docker container in a DockerMachinePool.
 // These DockerMachines have the clusterv1.ClusterNameLabel and clusterv1.MachinePoolNameLabel to support MachinePool Machines.
-func (r *DockerMachinePoolReconciler) createDockerMachine(ctx context.Context, name string, cluster *clusterv1.Cluster, machinePool *expv1.MachinePool, dockerMachinePool *infraexpv1.DockerMachinePool) (*infrav1.DockerMachine, error) {
-	log := ctrl.LoggerFrom(ctx)
-
-	labels := map[string]string{
-		clusterv1.ClusterNameLabel:     cluster.Name,
-		clusterv1.MachinePoolNameLabel: format.MustFormatValue(machinePool.Name),
-	}
+func computeDesiredDockerMachine(name string, cluster *clusterv1.Cluster, machinePool *expv1.MachinePool, dockerMachinePool *infraexpv1.DockerMachinePool, existingDockerMachine *infrav1.DockerMachine) *infrav1.DockerMachine {
 	dockerMachine := &infrav1.DockerMachine{
 		ObjectMeta: metav1.ObjectMeta{
 			Namespace:   dockerMachinePool.Namespace,
 			Name:        name,
-			Labels:      labels,
+			Labels:      make(map[string]string),
 			Annotations: make(map[string]string),
-			OwnerReferences: []metav1.OwnerReference{
-				{
-					APIVersion: dockerMachinePool.APIVersion,
-					Kind:       dockerMachinePool.Kind,
-					Name:       dockerMachinePool.Name,
-					UID:        dockerMachinePool.UID,
-				},
-				// Note: Since the MachinePool controller has not created its owner Machine yet, we want to set the DockerMachinePool as the owner so it's not orphaned.
-			},
 		},
 		Spec: infrav1.DockerMachineSpec{
 			CustomImage:   dockerMachinePool.Spec.Template.CustomImage,
@@ -265,13 +257,22 @@ func (r *DockerMachinePoolReconciler) createDockerMachine(ctx context.Context, n
 		},
 	}
 
-	log.V(2).Info("Creating DockerMachine", "dockerMachine", dockerMachine.Name)
-
-	if err := r.Client.Create(ctx, dockerMachine); err != nil {
-		return nil, errors.Wrap(err, "failed to create dockerMachine")
+	if existingDockerMachine != nil {
+		dockerMachine.SetUID(existingDockerMachine.UID)
+		dockerMachine.SetOwnerReferences(existingDockerMachine.OwnerReferences)
 	}
 
-	return dockerMachine, nil
+	// Note: Since the MachinePool controller has not created its owner Machine yet, we want to set the DockerMachinePool as the owner so it's not orphaned.
+	dockerMachine.SetOwnerReferences(util.EnsureOwnerRef(dockerMachine.OwnerReferences, metav1.OwnerReference{
+		APIVersion: dockerMachinePool.APIVersion,
+		Kind:       dockerMachinePool.Kind,
+		Name:       dockerMachinePool.Name,
+		UID:        dockerMachinePool.UID,
+	}))
+	dockerMachine.Labels[clusterv1.ClusterNameLabel] = cluster.Name
+	dockerMachine.Labels[clusterv1.MachinePoolNameLabel] = format.MustFormatValue(machinePool.Name)
+
+	return dockerMachine
 }
 
 // deleteMachinePoolMachine attempts to delete a DockerMachine and its associated owner Machine if it exists.