Repository: kubernetes-sigs/cluster-api
Pull request #9897 (2023-12-18 17:54:13)

📖 book: Update versions doc adding Kubernetes 1.29 and prowjob documentation
<!-- 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**:

* Update supported versions in versions.md
* Update job documentation in jobs.md

**Which issue(s) this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close the issue(s) when PR gets merged)*:

part of #9578

/area documentation

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 [oscr](https://github.com/oscr) 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:

- **[docs/OWNERS](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/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":["oscr"]} -->
chrischdi: /assign @sbueringer 

Patch:
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index 9c4edccaa..38197842f 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@56284d80811fb5963a972b438f2870f175e5b7c8 # tag=v40.2.3
+        uses: tj-actions/changed-files@94549999469dbfa032becf298d95c87a14c34394 # tag=v40.2.2
       - name: Get release version
         id: release-version
         run: |
diff --git a/Tiltfile b/Tiltfile
index 43bfcb870..1e29f8c41 100644
--- a/Tiltfile
+++ b/Tiltfile
@@ -3,7 +3,7 @@
 envsubst_cmd = "./hack/tools/bin/envsubst"
 clusterctl_cmd = "./bin/clusterctl"
 kubectl_cmd = "kubectl"
-kubernetes_version = "v1.29.0"
+kubernetes_version = "v1.28.0"
 
 load("ext://uibutton", "cmd_button", "location", "text_input")
 
diff --git a/docs/book/src/developer/providers/version-migration.md b/docs/book/src/developer/providers/version-migration.md
index dcfb364dd..6297363d7 100644
--- a/docs/book/src/developer/providers/version-migration.md
+++ b/docs/book/src/developer/providers/version-migration.md
@@ -11,3 +11,4 @@ maintainers of other providers and consumers of the Go API in upgrading from one
 - [v1.3 to v1.4](migrations/v1.3-to-v1.4.md)
 - [v1.4 to v1.5](migrations/v1.4-to-v1.5.md)
 - [v1.5 to v1.6](migrations/v1.5-to-v1.6.md)
+- [v1.6 to v1.7](migrations/v1.6-to-v1.7.md)
diff --git a/docs/book/src/developer/tilt.md b/docs/book/src/developer/tilt.md
index 1b632738a..7d4b89db1 100644
--- a/docs/book/src/developer/tilt.md
+++ b/docs/book/src/developer/tilt.md
@@ -337,7 +337,7 @@ Custom values for variable substitutions can be set using `kustomize_substitutio
 ```yaml
 kustomize_substitutions:
   NAMESPACE: "default"
-  KUBERNETES_VERSION: "v1.29.0"
+  KUBERNETES_VERSION: "v1.28.0"
   CONTROL_PLANE_MACHINE_COUNT: "1"
   WORKER_MACHINE_COUNT: "3"
 # Note: kustomize substitutions expects the values to be strings. This can be achieved by wrapping the values in quotation marks.
diff --git a/docs/book/src/reference/jobs.md b/docs/book/src/reference/jobs.md
index 40b50d81a..a69e549f1 100644
--- a/docs/book/src/reference/jobs.md
+++ b/docs/book/src/reference/jobs.md
@@ -30,7 +30,7 @@ Prow Presubmits:
     * GINKGO_SKIP: `[PR-Blocking] [Conformance] [K8s-Upgrade]`
   * [pull-cluster-api-e2e-full-main] `./scripts/ci-e2e.sh`
     * GINKGO_SKIP: `[PR-Blocking] [Conformance] [K8s-Upgrade]|[IPv6]`
-  * [pull-cluster-api-e2e-workload-upgrade-1-28-latest-main] `./scripts/ci-e2e.sh` FROM: `stable-1.28` TO: `ci/latest-1.29`
+  * [pull-cluster-api-e2e-workload-upgrade-1-29-latest-main] `./scripts/ci-e2e.sh` FROM: `stable-1.29` TO: `ci/latest-1.30`
     * GINKGO_FOCUS: `[K8s-Upgrade]`
 * optional for merge, run if manually triggered:
   * [pull-cluster-api-e2e-scale-main-experimental] `./scripts/ci-e2e-scale.sh`
@@ -79,8 +79,6 @@ Prow Periodics:
 * [periodic-cluster-api-e2e-dualstack-and-ipv6-main] `./scripts/ci-e2e.sh`
   * DOCKER_IN_DOCKER_IPV6_ENABLED: `true`
   * GINKGO_SKIP: `[Conformance] [K8s-Upgrade]`
-* [periodic-cluster-api-e2e-workload-upgrade-1-23-1-24-main] `./scripts/ci-e2e.sh` FROM: `stable-1.23` TO: `stable-1.24`
-  * GINKGO_FOCUS: `[K8s-Upgrade]`
 * [periodic-cluster-api-e2e-workload-upgrade-1-24-1-25-main] `./scripts/ci-e2e.sh` FROM: `stable-1.24` TO: `stable-1.25`
   * GINKGO_FOCUS: `[K8s-Upgrade]`
 * [periodic-cluster-api-e2e-workload-upgrade-1-25-1-26-main] `./scripts/ci-e2e.sh` FROM: `stable-1.25` TO: `stable-1.26`
@@ -89,7 +87,9 @@ Prow Periodics:
   * GINKGO_FOCUS: `[K8s-Upgrade]`
 * [periodic-cluster-api-e2e-workload-upgrade-1-27-1-28-main] `./scripts/ci-e2e.sh` FROM: `stable-1.27` TO: `stable-1.28`
   * GINKGO_FOCUS: `[K8s-Upgrade]`
-* [periodic-cluster-api-e2e-workload-upgrade-1-28-latest-main] `./scripts/ci-e2e.sh` FROM: `stable-1.28` TO: `ci/latest-1.29`
+* [periodic-cluster-api-e2e-workload-upgrade-1-28-1-29-main] `./scripts/ci-e2e.sh` FROM: `stable-1.28` TO: `stable-1.29`
+  * GINKGO_FOCUS: `[K8s-Upgrade]`
+* [periodic-cluster-api-e2e-workload-upgrade-1-29-latest-main] `./scripts/ci-e2e.sh` FROM: `stable-1.29` TO: `ci/latest-1.30`
   * GINKGO_FOCUS: `[K8s-Upgrade]`
 * [cluster-api-push-images-nightly] Google Cloud Build: `make release-staging-nightly`
 
@@ -126,18 +126,18 @@ Prow Periodics:
 [pull-cluster-api-e2e-informing-main]: https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api#capi-pr-e2e-informing-main
 [pull-cluster-api-e2e-full-dualstack-and-ipv6-main]: https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api#capi-pr-e2e-full-dualstack-and-ipv6-main
 [pull-cluster-api-e2e-full-main]: https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api#capi-pr-e2e-full-main
-[pull-cluster-api-e2e-workload-upgrade-1-28-latest-main]: https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api#capi-pr-e2e-main-1-28-latest
+[pull-cluster-api-e2e-workload-upgrade-1-29-latest-main]: https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api#capi-pr-e2e-main-1-29-latest
 [pull-cluster-api-e2e-scale-main-experimental]: https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api#capi-pr-e2e-scale-main-experimental
 [periodic-cluster-api-test-main]: https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api#capi-test-main
 [periodic-cluster-api-test-mink8s-main]: https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api#capi-test-mink8s-main
 [periodic-cluster-api-e2e-main]: https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api#capi-e2e-main
 [periodic-cluster-api-e2e-mink8s-main]: https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api#capi-e2e-mink8s-main
 [periodic-cluster-api-e2e-dualstack-and-ipv6-main]: https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api#capi-e2e-dualstack-and-ipv6-main
-[periodic-cluster-api-e2e-workload-upgrade-1-23-1-24-main]: https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api#capi-e2e-main-1-23-1-24
 [periodic-cluster-api-e2e-workload-upgrade-1-24-1-25-main]: https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api#capi-e2e-main-1-24-1-25
 [periodic-cluster-api-e2e-workload-upgrade-1-25-1-26-main]: https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api#capi-e2e-main-1-25-1-26
 [periodic-cluster-api-e2e-workload-upgrade-1-26-1-27-main]: https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api#capi-e2e-main-1-26-1-27
 [periodic-cluster-api-e2e-workload-upgrade-1-27-1-28-main]: https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api#capi-e2e-main-1-27-1-28
-[periodic-cluster-api-e2e-workload-upgrade-1-28-latest-main]: https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api#capi-e2e-main-1-28-latest
+[periodic-cluster-api-e2e-workload-upgrade-1-28-1-29-main]: https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api#capi-e2e-main-1-28-1-29
+[periodic-cluster-api-e2e-workload-upgrade-1-29-latest-main]: https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api#capi-e2e-main-1-29-latest
 [cluster-api-push-images-nightly]: https://testgrid.k8s.io/sig-cluster-lifecycle-image-pushes#cluster-api-push-images-nightly
 [post-cluster-api-push-images]: https://testgrid.k8s.io/sig-cluster-lifecycle-image-pushes#post-cluster-api-push-images
diff --git a/docs/book/src/reference/versions.md b/docs/book/src/reference/versions.md
index bb2001296..9b88ff6b4 100644
--- a/docs/book/src/reference/versions.md
+++ b/docs/book/src/reference/versions.md
@@ -82,6 +82,7 @@ These diagrams show the relationships between components in a Cluster API releas
 | Kubernetes v1.26  | ✓                    | ✓                 | ✓                 | ✓                 |
 | Kubernetes v1.27  | ✓                    | ✓                 | ✓                 | ✓                 |
 | Kubernetes v1.28  |                      | ✓                 | ✓                 | ✓                 |
+| Kubernetes v1.29  |                      |                   | ✓                 | ✓                 |
 
 
 \* There is an issue with CRDs in Kubernetes v1.23.{0-2}. ClusterClass with patches is affected by that (for more details please see [this issue](https://github.com/kubernetes-sigs/cluster-api/issues/5990)). Therefore we recommend to use Kubernetes v1.23.3+ with ClusterClass.
@@ -101,6 +102,7 @@ The Core Provider also talks to API server of every Workload Cluster. Therefore,
 | Kubernetes v1.26 + kubeadm/v1beta3 | ✓                    | ✓                  | ✓                  | ✓                  |
 | Kubernetes v1.27 + kubeadm/v1beta3 | ✓                    | ✓                  | ✓                  | ✓                  |
 | Kubernetes v1.28 + kubeadm/v1beta3 |                      | ✓                  | ✓                  | ✓                  |
+| Kubernetes v1.29 + kubeadm/v1beta3 |                      |                    | ✓                  | ✓                  |
 
 The Kubeadm Bootstrap Provider generates kubeadm configuration using the API version recommended for the target Kubernetes version.
 
@@ -116,6 +118,7 @@ The Kubeadm Bootstrap Provider generates kubeadm configuration using the API ver
 | Kubernetes v1.26 + etcd/v3 | ✓                    | ✓                 | ✓                 | ✓                 |
 | Kubernetes v1.27 + etcd/v3 | ✓                    | ✓                 | ✓                 | ✓                 |
 | Kubernetes v1.28 + etcd/v3 |                      | ✓                 | ✓                 | ✓                 |
+| Kubernetes v1.29 + etcd/v3 |                      |                   | ✓                 | ✓                 |
 
 The Kubeadm Control Plane Provider talks to the API server and etcd members of every Workload Cluster whose control plane it owns. It uses the etcd v3 API.
 
@@ -133,9 +136,6 @@ The Kubeadm Control Plane requires the Kubeadm Bootstrap Provider.
 
 #### Kubernetes version specific notes
 
-**1.29**:
-* In-tree cloud providers are now switched off by default. Please use DisableCloudProviders and DisableKubeletCloudCredentialProvider feature flags if you still need this functionality. (https://github.com/kubernetes/kubernetes/pull/117503)
-
 **1.28**:
 * No specific notes
 
diff --git a/docs/book/src/user/quick-start.md b/docs/book/src/user/quick-start.md
index 8527e3354..f039a9f06 100644
--- a/docs/book/src/user/quick-start.md
+++ b/docs/book/src/user/quick-start.md
@@ -1288,7 +1288,7 @@ The Docker provider is not designed for production use and is intended for devel
 
 ```bash
 clusterctl generate cluster capi-quickstart --flavor development \
-  --kubernetes-version v1.29.0 \
+  --kubernetes-version v1.28.0 \
   --control-plane-machine-count=3 \
   --worker-machine-count=3 \
   > capi-quickstart.yaml
@@ -1331,7 +1331,7 @@ clusterctl generate cluster capi-quickstart \
 
 ```bash
 clusterctl generate cluster capi-quickstart \
-  --kubernetes-version v1.29.0 \
+  --kubernetes-version v1.28.0 \
   --control-plane-machine-count=3 \
   --worker-machine-count=3 \
   > capi-quickstart.yaml
@@ -1385,7 +1385,7 @@ and see an output similar to this:
 
 ```bash
 NAME              PHASE         AGE   VERSION
-capi-quickstart   Provisioned   8s    v1.29.0
+capi-quickstart   Provisioned   8s    v1.28.0
 ```
 
 To verify the first control plane is up:
@@ -1398,7 +1398,7 @@ You should see an output is similar to this:
 
 ```bash
 NAME                    CLUSTER           INITIALIZED   API SERVER AVAILABLE   REPLICAS   READY   UPDATED   UNAVAILABLE   AGE    VERSION
-capi-quickstart-g2trk   capi-quickstart   true                                 3                  3         3             4m7s   v1.29.0
+capi-quickstart-g2trk   capi-quickstart   true                                 3                  3         3             4m7s   v1.28.0
 ```
 
 <aside class="note warning">
@@ -1618,12 +1618,12 @@ kubectl --kubeconfig=./capi-quickstart.kubeconfig get nodes
 ```
 ```bash
 NAME                                          STATUS   ROLES           AGE    VERSION
-capi-quickstart-vs89t-gmbld                   Ready    control-plane   5m33s  v1.29.0
-capi-quickstart-vs89t-kf9l5                   Ready    control-plane   6m20s  v1.29.0
-capi-quickstart-vs89t-t8cfn                   Ready    control-plane   7m10s  v1.29.0
-capi-quickstart-md-0-55x6t-5649968bd7-8tq9v   Ready    <none>          6m5s   v1.29.0
-capi-quickstart-md-0-55x6t-5649968bd7-glnjd   Ready    <none>          6m9s   v1.29.0
-capi-quickstart-md-0-55x6t-5649968bd7-sfzp6   Ready    <none>          6m9s   v1.29.0
+capi-quickstart-vs89t-gmbld                   Ready    control-plane   5m33s  v1.28.0
+capi-quickstart-vs89t-kf9l5                   Ready    control-plane   6m20s  v1.28.0
+capi-quickstart-vs89t-t8cfn                   Ready    control-plane   7m10s  v1.28.0
+capi-quickstart-md-0-55x6t-5649968bd7-8tq9v   Ready    <none>          6m5s   v1.28.0
+capi-quickstart-md-0-55x6t-5649968bd7-glnjd   Ready    <none>          6m9s   v1.28.0
+capi-quickstart-md-0-55x6t-5649968bd7-sfzp6   Ready    <none>          6m9s   v1.28.0
 ```
 
 {{#/tab }}
diff --git a/go.mod b/go.mod
index 737bc1ffd..cc7985bce 100644
--- a/go.mod
+++ b/go.mod
@@ -27,7 +27,7 @@ require (
 	github.com/prometheus/client_golang v1.17.0
 	github.com/spf13/cobra v1.8.0
 	github.com/spf13/pflag v1.0.5
-	github.com/spf13/viper v1.18.2
+	github.com/spf13/viper v1.18.1
 	github.com/valyala/fastjson v1.6.4
 	go.etcd.io/etcd/api/v3 v3.5.11
 	go.etcd.io/etcd/client/v3 v3.5.11
diff --git a/go.sum b/go.sum
index f2d5d3994..d89d7eb99 100644
--- a/go.sum
+++ b/go.sum
@@ -471,8 +471,8 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn
 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.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
-github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ=
-github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk=
+github.com/spf13/viper v1.18.1 h1:rmuU42rScKWlhhJDyXZRKJQHXFX02chSVW1IvkPGiVM=
+github.com/spf13/viper v1.18.1/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk=
 github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU=
 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=
diff --git a/hack/tools/go.mod b/hack/tools/go.mod
index 504a2c60a..5f933eb89 100644
--- a/hack/tools/go.mod
+++ b/hack/tools/go.mod
@@ -106,7 +106,7 @@ require (
 	github.com/sourcegraph/conc v0.3.0 // indirect
 	github.com/spf13/afero v1.11.0 // indirect
 	github.com/spf13/cast v1.6.0 // indirect
-	github.com/spf13/viper v1.18.2 // indirect
+	github.com/spf13/viper v1.18.1 // indirect
 	github.com/stoewer/go-strcase v1.2.0 // indirect
 	github.com/subosito/gotenv v1.6.0 // indirect
 	go.opencensus.io v0.24.0 // indirect
diff --git a/hack/tools/go.sum b/hack/tools/go.sum
index 31ab4ced2..8960321be 100644
--- a/hack/tools/go.sum
+++ b/hack/tools/go.sum
@@ -234,8 +234,8 @@ github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
 github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
 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.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ=
-github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk=
+github.com/spf13/viper v1.18.1 h1:rmuU42rScKWlhhJDyXZRKJQHXFX02chSVW1IvkPGiVM=
+github.com/spf13/viper v1.18.1/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk=
 github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU=
 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=


Test patch:
diff --git a/test/e2e/clusterctl_upgrade_test.go b/test/e2e/clusterctl_upgrade_test.go
index 1a23f56a4..bca33dcde 100644
--- a/test/e2e/clusterctl_upgrade_test.go
+++ b/test/e2e/clusterctl_upgrade_test.go
@@ -129,7 +129,7 @@ var _ = Describe("When testing clusterctl upgrades using ClusterClass (v1.5=>cur
 })
 
 var _ = Describe("When testing clusterctl upgrades (v1.6=>current)", func() {
-	// Get v1.6 latest stable release
+	// Get v1.5 latest stable release
 	version := "1.6"
 	stableRelease, err := GetStableReleaseOfMinor(ctx, version)
 	Expect(err).ToNot(HaveOccurred(), "Failed to get stable version for minor release : %s", version)
@@ -144,7 +144,7 @@ var _ = Describe("When testing clusterctl upgrades (v1.6=>current)", func() {
 			InitWithBinary:            fmt.Sprintf(clusterctlDownloadURL, stableRelease),
 			InitWithProvidersContract: "v1beta1",
 			//  Note: Both InitWithKubernetesVersion and WorkloadKubernetesVersion should be the highest mgmt cluster version supported by the source Cluster API version.
-			InitWithKubernetesVersion: "v1.29.0",
+			InitWithKubernetesVersion: "v1.28.0",
 			WorkloadKubernetesVersion: "v1.28.0",
 			MgmtFlavor:                "topology",
 			WorkloadFlavor:            "",
@@ -153,7 +153,7 @@ var _ = Describe("When testing clusterctl upgrades (v1.6=>current)", func() {
 })
 
 var _ = Describe("When testing clusterctl upgrades using ClusterClass (v1.6=>current) [ClusterClass]", func() {
-	// Get v1.6 latest stable release
+	// Get v1.5 latest stable release
 	version := "1.6"
 	stableRelease, err := GetStableReleaseOfMinor(ctx, version)
 	Expect(err).ToNot(HaveOccurred(), "Failed to get stable version for minor release : %s", version)
@@ -168,7 +168,7 @@ var _ = Describe("When testing clusterctl upgrades using ClusterClass (v1.6=>cur
 			InitWithBinary:            fmt.Sprintf(clusterctlDownloadURL, stableRelease),
 			InitWithProvidersContract: "v1beta1",
 			// Note: Both InitWithKubernetesVersion and WorkloadKubernetesVersion should be the highest mgmt cluster version supported by the source Cluster API version.
-			InitWithKubernetesVersion: "v1.29.0",
+			InitWithKubernetesVersion: "v1.28.0",
 			WorkloadKubernetesVersion: "v1.28.0",
 			MgmtFlavor:                "topology",
 			WorkloadFlavor:            "topology",
diff --git a/test/e2e/config/docker.yaml b/test/e2e/config/docker.yaml
index 670020316..19fb3043b 100644
--- a/test/e2e/config/docker.yaml
+++ b/test/e2e/config/docker.yaml
@@ -237,12 +237,14 @@ variables:
   # allowing the same e2e config file to be re-used in different Prow jobs e.g. each one with a K8s version permutation.
   # The following Kubernetes versions should be the latest versions with already published kindest/node images.
   # This avoids building node images in the default case which improves the test duration significantly.
-  KUBERNETES_VERSION_MANAGEMENT: "v1.29.0"
-  KUBERNETES_VERSION: "v1.29.0"
-  KUBERNETES_VERSION_UPGRADE_FROM: "v1.28.0"
-  KUBERNETES_VERSION_UPGRADE_TO: "v1.29.0"
-  ETCD_VERSION_UPGRADE_TO: "3.5.10-0"
-  COREDNS_VERSION_UPGRADE_TO: "v1.11.1"
+  KUBERNETES_VERSION_MANAGEMENT: "v1.28.0"
+  # TODO when bumping this version: also remove the pinning of the conformanceImage at the [ipv6] tests in
+  # `test/e2e/quick_start_test.go`.
+  KUBERNETES_VERSION: "v1.28.0"
+  KUBERNETES_VERSION_UPGRADE_FROM: "v1.27.3"
+  KUBERNETES_VERSION_UPGRADE_TO: "v1.28.0"
+  ETCD_VERSION_UPGRADE_TO: "3.5.9-0"
+  COREDNS_VERSION_UPGRADE_TO: "v1.10.1"
   DOCKER_SERVICE_DOMAIN: "cluster.local"
   IP_FAMILY: "dual"
   DOCKER_SERVICE_CIDRS: "10.128.0.0/12"
diff --git a/test/e2e/quick_start_test.go b/test/e2e/quick_start_test.go
index 2d1644802..ea030ce55 100644
--- a/test/e2e/quick_start_test.go
+++ b/test/e2e/quick_start_test.go
@@ -143,6 +143,9 @@ var _ = Describe("When following the Cluster API quick-start with dualstack and
 						ClusterProxy:       proxy.GetWorkloadCluster(ctx, namespace, clusterName),
 						ArtifactsDirectory: artifactFolder,
 						ConfigFilePath:     "./data/kubetest/dualstack.yaml",
+						// Pin the conformance image to workaround https://github.com/kubernetes-sigs/cluster-api/issues/9240 .
+						// This should get dropped again when bumping to a version post v1.28.0 in `test/e2e/config/docker.yaml`.
+						ConformanceImage: "gcr.io/k8s-staging-ci-images/conformance:v1.29.0-alpha.0.190_18290bfdc8fbe1",
 					},
 				)).To(Succeed())
 			},
@@ -169,6 +172,9 @@ var _ = Describe("When following the Cluster API quick-start with dualstack and
 						ClusterProxy:       proxy.GetWorkloadCluster(ctx, namespace, clusterName),
 						ArtifactsDirectory: artifactFolder,
 						ConfigFilePath:     "./data/kubetest/dualstack.yaml",
+						// Pin the conformance image to workaround https://github.com/kubernetes-sigs/cluster-api/issues/9240 .
+						// This should get dropped again when bumping to a version post v1.28.0 in `test/e2e/config/docker.yaml`.
+						ConformanceImage: "gcr.io/k8s-staging-ci-images/conformance:v1.29.0-alpha.0.190_18290bfdc8fbe1",
 					},
 				)).To(Succeed())
 			},
diff --git a/test/framework/bootstrap/kind_provider.go b/test/framework/bootstrap/kind_provider.go
index c3e451d9d..7dbd9f0bb 100644
--- a/test/framework/bootstrap/kind_provider.go
+++ b/test/framework/bootstrap/kind_provider.go
@@ -37,7 +37,7 @@ const (
 	DefaultNodeImageRepository = "kindest/node"
 
 	// DefaultNodeImageVersion is the default Kubernetes version to be used for creating a kind cluster.
-	DefaultNodeImageVersion = "v1.29.0@sha256:eaa1450915475849a73a9227b8f201df25e55e268e5d619312131292e324d570"
+	DefaultNodeImageVersion = "v1.28.0@sha256:b7a4cad12c197af3ba43202d3efe03246b3f0793f162afb40a33c923952d5b31"
 )
 
 // KindClusterOption is a NewKindClusterProvider option.
diff --git a/test/go.mod b/test/go.mod
index d9f75adec..dbac99e82 100644
--- a/test/go.mod
+++ b/test/go.mod
@@ -117,7 +117,7 @@ require (
 	github.com/spf13/afero v1.11.0 // indirect
 	github.com/spf13/cast v1.6.0 // indirect
 	github.com/spf13/cobra v1.8.0 // indirect
-	github.com/spf13/viper v1.18.2 // indirect
+	github.com/spf13/viper v1.18.1 // indirect
 	github.com/stoewer/go-strcase v1.2.0 // indirect
 	github.com/subosito/gotenv v1.6.0 // indirect
 	github.com/valyala/fastjson v1.6.4 // indirect
diff --git a/test/go.sum b/test/go.sum
index 0e7540523..a893fe217 100644
--- a/test/go.sum
+++ b/test/go.sum
@@ -273,8 +273,8 @@ 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.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ=
-github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk=
+github.com/spf13/viper v1.18.1 h1:rmuU42rScKWlhhJDyXZRKJQHXFX02chSVW1IvkPGiVM=
+github.com/spf13/viper v1.18.1/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk=
 github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU=
 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=
diff --git a/test/infrastructure/docker/examples/machine-pool.yaml b/test/infrastructure/docker/examples/machine-pool.yaml
index 5f3e63d5b..2ed4ba3cb 100644
--- a/test/infrastructure/docker/examples/machine-pool.yaml
+++ b/test/infrastructure/docker/examples/machine-pool.yaml
@@ -35,7 +35,7 @@ metadata:
   namespace: default
 spec:
   replicas: 1
-  version: v1.29.0
+  version: v1.28.0
   machineTemplate:
     infrastructureRef:
       apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
@@ -80,7 +80,7 @@ spec:
   replicas: 2
   template:
     spec:
-      version: v1.29.0
+      version: v1.28.0
       clusterName: my-cluster
       bootstrap:
         configRef:
diff --git a/test/infrastructure/docker/examples/simple-cluster-ipv6.yaml b/test/infrastructure/docker/examples/simple-cluster-ipv6.yaml
index 792b413f9..68681b2dd 100644
--- a/test/infrastructure/docker/examples/simple-cluster-ipv6.yaml
+++ b/test/infrastructure/docker/examples/simple-cluster-ipv6.yaml
@@ -35,7 +35,7 @@ metadata:
   namespace: default
 spec:
   replicas: 1
-  version: v1.29.0
+  version: v1.28.0
   machineTemplate:
     infrastructureRef:
       apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
@@ -90,7 +90,7 @@ spec:
       cluster.x-k8s.io/cluster-name: my-cluster
   template:
     spec:
-      version: v1.29.0
+      version: v1.28.0
       clusterName: my-cluster
       bootstrap:
         configRef:
diff --git a/test/infrastructure/docker/examples/simple-cluster-without-kcp.yaml b/test/infrastructure/docker/examples/simple-cluster-without-kcp.yaml
index be56c248a..682bf097b 100644
--- a/test/infrastructure/docker/examples/simple-cluster-without-kcp.yaml
+++ b/test/infrastructure/docker/examples/simple-cluster-without-kcp.yaml
@@ -32,7 +32,7 @@ metadata:
   name: controlplane-0
   namespace: default
 spec:
-  version: v1.29.0
+  version: v1.28.0
   clusterName: my-cluster
   bootstrap:
     configRef:
@@ -80,7 +80,7 @@ spec:
       cluster.x-k8s.io/cluster-name: my-cluster
   template:
     spec:
-      version: v1.29.0
+      version: v1.28.0
       clusterName: my-cluster
       bootstrap:
         configRef:
diff --git a/test/infrastructure/docker/examples/simple-cluster.yaml b/test/infrastructure/docker/examples/simple-cluster.yaml
index 134ec23fd..4259a332b 100644
--- a/test/infrastructure/docker/examples/simple-cluster.yaml
+++ b/test/infrastructure/docker/examples/simple-cluster.yaml
@@ -35,7 +35,7 @@ metadata:
   namespace: default
 spec:
   replicas: 1
-  version: v1.29.0
+  version: v1.28.0
   machineTemplate:
     infrastructureRef:
       apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
@@ -83,7 +83,7 @@ spec:
       cluster.x-k8s.io/cluster-name: my-cluster
   template:
     spec:
-      version: v1.29.0
+      version: v1.28.0
       clusterName: my-cluster
       bootstrap:
         configRef:
diff --git a/test/infrastructure/kind/mapper.go b/test/infrastructure/kind/mapper.go
index b596b79fa..71927f6c9 100644
--- a/test/infrastructure/kind/mapper.go
+++ b/test/infrastructure/kind/mapper.go
@@ -81,11 +81,6 @@ var preBuiltMappings = []Mapping{
 	// TODO: Add pre-built images for newer Kind versions on top
 
 	// Pre-built images for Kind v1.20.
-	{
-		KubernetesVersion: semver.MustParse("1.29.0"),
-		Mode:              Mode0_20,
-		Image:             "kindest/node:v1.29.0@sha256:eaa1450915475849a73a9227b8f201df25e55e268e5d619312131292e324d570",
-	},
 	{
 		KubernetesVersion: semver.MustParse("1.28.0"),
 		Mode:              Mode0_20,