Repository: kubernetes-sigs/cluster-api
Pull request #9935 (2023-12-28 12:55:23)

🌱 Small improvements to the inmemory api server
**What this PR does / why we need it**:
Two small improvements to the inmemory api server:
- Add API resource definitions for supporting also Service and Endpoints API objects (and sort alphabetically API resource definitions)
- Properly handle returning well-known API errors

/area testing

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"]} -->
k8s-ci-robot: LGTM label has been added.  <details>Git tree hash: a9a6e6ae762ccce643fcbdf36beb3e20c8e5bc6e</details>
k8s-ci-robot: New changes are detected. LGTM label has been removed.
sbueringer: Up to you, what you want to do with: https://github.com/kubernetes-sigs/cluster-api/pull/9935#discussion_r1444695167

/lgtm
/approve

/hold
k8s-ci-robot: LGTM label has been added.  <details>Git tree hash: 0b0ec3e529f788d476365442beec642d53a74d4f</details>
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/9935#issuecomment-1881073780" title="Approved">sbueringer</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)~~ [sbueringer]

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":[]} -->

Patch:
diff --git a/Makefile b/Makefile
index bba0037d9..4ee4973ee 100644
--- a/Makefile
+++ b/Makefile
@@ -1246,7 +1246,6 @@ clean-tilt: clean-charts clean-kind ## Remove all files generated by Tilt
 	rm -rf ./controlplane/kubeadm/.tiltbuild
 	rm -rf ./bootstrap/kubeadm/.tiltbuild
 	rm -rf ./test/infrastructure/docker/.tiltbuild
-	rm -rf ./test/infrastructure/inmemory/.tiltbuild
 
 .PHONY: clean-charts
 clean-charts: ## Remove all local copies of Helm charts in ./hack/observability
diff --git a/bootstrap/kubeadm/internal/controllers/kubeadmconfig_controller.go b/bootstrap/kubeadm/internal/controllers/kubeadmconfig_controller.go
index 39138ce78..ee75f52c4 100644
--- a/bootstrap/kubeadm/internal/controllers/kubeadmconfig_controller.go
+++ b/bootstrap/kubeadm/internal/controllers/kubeadmconfig_controller.go
@@ -226,7 +226,7 @@ func (r *KubeadmConfigReconciler) Reconcile(ctx context.Context, req ctrl.Reques
 			patchOpts = append(patchOpts, patch.WithStatusObservedGeneration{})
 		}
 		if err := patchHelper.Patch(ctx, config, patchOpts...); err != nil {
-			rerr = kerrors.NewAggregate([]error{rerr, err})
+			rerr = kerrors.NewAggregate([]error{rerr, errors.Wrapf(err, "failed to patch %s", klog.KObj(config))})
 		}
 	}()
 
diff --git a/cmd/clusterctl/client/cluster/mover.go b/cmd/clusterctl/client/cluster/mover.go
index 7316dcf83..a93bf2f86 100644
--- a/cmd/clusterctl/client/cluster/mover.go
+++ b/cmd/clusterctl/client/cluster/mover.go
@@ -741,7 +741,7 @@ func pauseClusterClass(ctx context.Context, proxy Proxy, n *node, pause bool, mu
 
 	patchHelper, err := patch.NewHelper(clusterClass, cFrom)
 	if err != nil {
-		return err
+		return errors.Wrapf(err, "error creating patcher for ClusterClass %s/%s", n.identity.Namespace, n.identity.Name)
 	}
 
 	// Update the annotation to the desired state
@@ -759,8 +759,11 @@ func pauseClusterClass(ctx context.Context, proxy Proxy, n *node, pause bool, mu
 
 	// Update the ClusterClass with the new annotations.
 	clusterClass.SetAnnotations(ccAnnotations)
+	if err := patchHelper.Patch(ctx, clusterClass); err != nil {
+		return errors.Wrapf(err, "error patching ClusterClass %s/%s", n.identity.Namespace, n.identity.Name)
+	}
 
-	return patchHelper.Patch(ctx, clusterClass)
+	return nil
 }
 
 // ensureNamespaces ensures all the expected target namespaces are in place before creating objects.
diff --git a/controlplane/kubeadm/internal/control_plane.go b/controlplane/kubeadm/internal/control_plane.go
index 376e3ceff..1a3297637 100644
--- a/controlplane/kubeadm/internal/control_plane.go
+++ b/controlplane/kubeadm/internal/control_plane.go
@@ -70,7 +70,7 @@ func NewControlPlane(ctx context.Context, managementCluster ManagementCluster, c
 	for _, machine := range ownedMachines {
 		patchHelper, err := patch.NewHelper(machine, client)
 		if err != nil {
-			return nil, err
+			return nil, errors.Wrapf(err, "failed to create patch helper for machine %s", machine.Name)
 		}
 		patchHelpers[machine.Name] = patchHelper
 	}
@@ -271,7 +271,7 @@ func (c *ControlPlane) PatchMachines(ctx context.Context) error {
 				controlplanev1.MachineEtcdPodHealthyCondition,
 				controlplanev1.MachineEtcdMemberHealthyCondition,
 			}}); err != nil {
-				errList = append(errList, err)
+				errList = append(errList, errors.Wrapf(err, "failed to patch machine %s", machine.Name))
 			}
 			continue
 		}
diff --git a/controlplane/kubeadm/internal/controllers/controller.go b/controlplane/kubeadm/internal/controllers/controller.go
index d58125fc9..91d9e8060 100644
--- a/controlplane/kubeadm/internal/controllers/controller.go
+++ b/controlplane/kubeadm/internal/controllers/controller.go
@@ -179,7 +179,8 @@ func (r *KubeadmControlPlaneReconciler) Reconcile(ctx context.Context, req ctrl.
 		// Patch ObservedGeneration only if the reconciliation completed successfully
 		patchOpts := []patch.Option{patch.WithStatusObservedGeneration{}}
 		if err := patchHelper.Patch(ctx, kcp, patchOpts...); err != nil {
-			return ctrl.Result{}, errors.Wrapf(err, "failed to add finalizer")
+			log.Error(err, "Failed to patch KubeadmControlPlane to add finalizer")
+			return ctrl.Result{}, err
 		}
 
 		return ctrl.Result{}, nil
@@ -626,7 +627,7 @@ func (r *KubeadmControlPlaneReconciler) syncMachines(ctx context.Context, contro
 		// TODO: This should be cleaned-up to have a more streamline way of constructing and using patchHelpers.
 		patchHelper, err := patch.NewHelper(updatedMachine, r.Client)
 		if err != nil {
-			return err
+			return errors.Wrapf(err, "failed to create patch helper for Machine %s", klog.KObj(updatedMachine))
 		}
 		patchHelpers[machineName] = patchHelper
 
@@ -811,7 +812,7 @@ func (r *KubeadmControlPlaneReconciler) reconcileCertificateExpiries(ctx context
 		log.V(2).Info(fmt.Sprintf("Setting certificate expiry to %s", expiry))
 		patchHelper, err := patch.NewHelper(kubeadmConfig, r.Client)
 		if err != nil {
-			return errors.Wrapf(err, "failed to reconcile certificate expiry for Machine/%s", m.Name)
+			return errors.Wrapf(err, "failed to reconcile certificate expiry for Machine/%s: failed to create PatchHelper for KubeadmConfig/%s", m.Name, kubeadmConfig.Name)
 		}
 
 		if annotations == nil {
@@ -821,7 +822,7 @@ func (r *KubeadmControlPlaneReconciler) reconcileCertificateExpiries(ctx context
 		kubeadmConfig.SetAnnotations(annotations)
 
 		if err := patchHelper.Patch(ctx, kubeadmConfig); err != nil {
-			return errors.Wrapf(err, "failed to reconcile certificate expiry for Machine/%s", m.Name)
+			return errors.Wrapf(err, "failed to reconcile certificate expiry for Machine/%s: failed to patch KubeadmConfig/%s", m.Name, kubeadmConfig.Name)
 		}
 	}
 
@@ -949,7 +950,7 @@ func (r *KubeadmControlPlaneReconciler) ensureCertificatesOwnerRef(ctx context.C
 
 		patchHelper, err := patch.NewHelper(c.Secret, r.Client)
 		if err != nil {
-			return err
+			return errors.Wrapf(err, "failed to create patchHelper for Secret %s", klog.KObj(c.Secret))
 		}
 
 		controller := metav1.GetControllerOf(c.Secret)
@@ -970,7 +971,7 @@ func (r *KubeadmControlPlaneReconciler) ensureCertificatesOwnerRef(ctx context.C
 			c.Secret.SetOwnerReferences(util.EnsureOwnerRef(c.Secret.GetOwnerReferences(), owner))
 		}
 		if err := patchHelper.Patch(ctx, c.Secret); err != nil {
-			return errors.Wrapf(err, "failed to set ownerReference")
+			return errors.Wrapf(err, "failed to patch Secret %s with ownerReference %s", klog.KObj(c.Secret), owner.String())
 		}
 	}
 	return nil
diff --git a/controlplane/kubeadm/internal/controllers/helpers.go b/controlplane/kubeadm/internal/controllers/helpers.go
index 4d74c70be..d03784ea2 100644
--- a/controlplane/kubeadm/internal/controllers/helpers.go
+++ b/controlplane/kubeadm/internal/controllers/helpers.go
@@ -104,11 +104,11 @@ func (r *KubeadmControlPlaneReconciler) reconcileKubeconfig(ctx context.Context,
 func (r *KubeadmControlPlaneReconciler) adoptKubeconfigSecret(ctx context.Context, configSecret *corev1.Secret, kcp *controlplanev1.KubeadmControlPlane) (reterr error) {
 	patchHelper, err := patch.NewHelper(configSecret, r.Client)
 	if err != nil {
-		return err
+		return errors.Wrap(err, "failed to create patch helper for the kubeconfig secret")
 	}
 	defer func() {
 		if err := patchHelper.Patch(ctx, configSecret); err != nil {
-			reterr = kerrors.NewAggregate([]error{reterr, err})
+			reterr = errors.Wrap(err, "failed to patch the kubeconfig secret")
 		}
 	}()
 	controller := metav1.GetControllerOf(configSecret)
diff --git a/controlplane/kubeadm/internal/controllers/remediation.go b/controlplane/kubeadm/internal/controllers/remediation.go
index 94d153dbc..d920c6481 100644
--- a/controlplane/kubeadm/internal/controllers/remediation.go
+++ b/controlplane/kubeadm/internal/controllers/remediation.go
@@ -55,7 +55,7 @@ func (r *KubeadmControlPlaneReconciler) reconcileUnhealthyMachines(ctx context.C
 			m.DeletionTimestamp.IsZero() {
 			patchHelper, err := patch.NewHelper(m, r.Client)
 			if err != nil {
-				errList = append(errList, err)
+				errList = append(errList, errors.Wrapf(err, "failed to get PatchHelper for machine %s", m.Name))
 				continue
 			}
 
@@ -64,7 +64,7 @@ func (r *KubeadmControlPlaneReconciler) reconcileUnhealthyMachines(ctx context.C
 			if err := patchHelper.Patch(ctx, m, patch.WithOwnedConditions{Conditions: []clusterv1.ConditionType{
 				clusterv1.MachineOwnerRemediatedCondition,
 			}}); err != nil {
-				errList = append(errList, err)
+				errList = append(errList, errors.Wrapf(err, "failed to patch machine %s", m.Name))
 			}
 		}
 	}
@@ -346,6 +346,14 @@ func (r *KubeadmControlPlaneReconciler) checkRetryLimits(log logr.Logger, machin
 	return remediationInProgressData, true, nil
 }
 
+// max calculates the maximum duration.
+func max(x, y time.Duration) time.Duration {
+	if x < y {
+		return y
+	}
+	return x
+}
+
 // canSafelyRemoveEtcdMember assess if it is possible to remove the member hosted on the machine to be remediated
 // without loosing etcd quorum.
 //
diff --git a/docs/book/src/developer/guide.md b/docs/book/src/developer/guide.md
index 93316ae14..3ffda95db 100644
--- a/docs/book/src/developer/guide.md
+++ b/docs/book/src/developer/guide.md
@@ -146,16 +146,6 @@ To test another iteration, you'll need to follow the steps to build, push, updat
 
 **Additional ClusterAPI KubeCon talks**
 
-* [SIG Cluster Lifecycle Intro & Future - November 2023](https://www.youtube.com/watch?v=MM0YPhIel2M)
-* [Cluster API Deep Dive: Improving Performance up to 2k Clusters - November 2023](https://www.youtube.com/watch?v=bRPfmviTi3s)
-* [Leveraging Cluster-API for Production-Ready Multi-Regional Infrastructures - November 2023](https://www.youtube.com/watch?v=BDjhGEVJ0Gs)
-* [The Stars Look Very Different Today”: Kubernetes and Cloud Native at the SKA Observatory - November 2023](https://www.youtube.com/watch?v=quW8FbW1fVM)
-* [15,000 Minecraft Players Vs One K8s Cluster. Who Wins? - November 2023](https://www.youtube.com/watch?v=4YNp2vb9NTA)
-* [Cluster API Providers: Intro, Deep Dive, and Community! - April 2023](https://www.youtube.com/watch?v=QA4OhqLKJn4)
-* [Ephemeral Clusters as a Service with ClusterAPI and GitOps - April 2023](https://www.youtube.com/watch?v=cXIo8C7yWvg)
-* [The Power of Self-Managing Clusters - April 2023](https://www.youtube.com/watch?v=tNUH_8MFyTc)
-* [How to Turn Release Management from Duty to Fun - April 2023](https://www.youtube.com/watch?v=sgP3tyGJ5tQ)
-* [Tilt Your World! Lessons Learned in Improving Dev Productivity with Tilt - April 2023](https://www.youtube.com/watch?v=h6llT5Bg97g)
 * [How Adobe Planned For Scale With Argo CD, Cluster API, And VCluster - October 2022](https://www.youtube.com/watch?v=p8BluR5WT5w)
 * [Bare-Metal Chronicles: Intertwinement Of Tinkerbell, Cluster API And GitOps - October 2022](https://www.youtube.com/watch?v=NCFUUjTw6hA)
 * [Running Isolated VirtualClusters With Kata & Cluster API - October 2022](https://www.youtube.com/watch?v=T6w3YrExorY)
diff --git a/docs/book/src/developer/providers/migrations/v1.6-to-v1.7.md b/docs/book/src/developer/providers/migrations/v1.6-to-v1.7.md
index e7999a086..d57ab0e03 100644
--- a/docs/book/src/developer/providers/migrations/v1.6-to-v1.7.md
+++ b/docs/book/src/developer/providers/migrations/v1.6-to-v1.7.md
@@ -24,6 +24,4 @@ maintainers of providers and consumers of our Go API.
 
 ### Other
 
-* Patch helper now return error with enough error context (https://github.com/kubernetes-sigs/cluster-api/pull/9946). It is recommended to remove redundant error context on call sites if applicable.
-
 ### Suggested changes for providers
diff --git a/docs/book/src/reference/providers.md b/docs/book/src/reference/providers.md
index 3589f7947..99899b496 100644
--- a/docs/book/src/reference/providers.md
+++ b/docs/book/src/reference/providers.md
@@ -12,7 +12,6 @@ updated info about which API version they are supporting.
 - [Oracle Cloud Native Environment (OCNE)](https://github.com/verrazzano/cluster-api-provider-ocne)
 - [Talos](https://github.com/siderolabs/cluster-api-bootstrap-provider-talos)
 - [K3s](https://github.com/cluster-api-provider-k3s/cluster-api-k3s)
-- [k0smotron/k0s](https://github.com/k0sproject/k0smotron)
 
 ## Control Plane
 - [Kubeadm](https://github.com/kubernetes-sigs/cluster-api/tree/main/controlplane/kubeadm)
@@ -22,7 +21,6 @@ updated info about which API version they are supporting.
 - [Talos](https://github.com/siderolabs/cluster-api-control-plane-provider-talos)
 - [Kamaji](https://github.com/clastix/cluster-api-control-plane-provider-kamaji)
 - [K3s](https://github.com/cluster-api-provider-k3s/cluster-api-k3s)
-- [k0smotron/k0s](https://github.com/k0sproject/k0smotron)
 
 ## Infrastructure
 - [AWS](https://cluster-api-aws.sigs.k8s.io/)
@@ -53,7 +51,6 @@ updated info about which API version they are supporting.
 - [Virtink](https://github.com/smartxworks/cluster-api-provider-virtink)
 - [VMware Cloud Director](https://github.com/vmware/cluster-api-provider-cloud-director)  
 - [vSphere](https://github.com/kubernetes-sigs/cluster-api-provider-vsphere)
-- [k0smotron RemoteMachine (SSH)](https://github.com/k0sproject/k0smotron)
 
 ## IP Address Management (IPAM)
 - [In Cluster](https://github.com/kubernetes-sigs/cluster-api-ipam-provider-in-cluster)
diff --git a/exp/addons/internal/controllers/clusterresourceset_controller.go b/exp/addons/internal/controllers/clusterresourceset_controller.go
index 2713dba76..0eb41a6b4 100644
--- a/exp/addons/internal/controllers/clusterresourceset_controller.go
+++ b/exp/addons/internal/controllers/clusterresourceset_controller.go
@@ -212,6 +212,7 @@ func (r *ClusterResourceSetReconciler) reconcileDelete(ctx context.Context, clus
 				log.Error(err, "failed to delete empty ClusterResourceSetBinding")
 			}
 		} else if err := patchHelper.Patch(ctx, clusterResourceSetBinding); err != nil {
+			log.Error(err, "failed to patch ClusterResourceSetBinding")
 			return err
 		}
 	}
diff --git a/exp/addons/internal/controllers/clusterresourcesetbinding_controller.go b/exp/addons/internal/controllers/clusterresourcesetbinding_controller.go
index 93ac69079..3ebdb70d9 100644
--- a/exp/addons/internal/controllers/clusterresourcesetbinding_controller.go
+++ b/exp/addons/internal/controllers/clusterresourcesetbinding_controller.go
@@ -123,7 +123,7 @@ func (r *ClusterResourceSetBindingReconciler) clusterToClusterResourceSetBinding
 func (r *ClusterResourceSetBindingReconciler) updateClusterReference(ctx context.Context, binding *addonsv1.ClusterResourceSetBinding) error {
 	patchHelper, err := patch.NewHelper(binding, r.Client)
 	if err != nil {
-		return err
+		return errors.Wrap(err, "failed to configure the patch helper")
 	}
 
 	// If the `.spec.clusterName` is not set, take the value from the ownerReference.
diff --git a/exp/internal/controllers/machinepool_controller_noderef.go b/exp/internal/controllers/machinepool_controller_noderef.go
index 15791b2cd..f46d1b77e 100644
--- a/exp/internal/controllers/machinepool_controller_noderef.go
+++ b/exp/internal/controllers/machinepool_controller_noderef.go
@@ -228,7 +228,7 @@ func (r *MachinePoolReconciler) patchNodes(ctx context.Context, c client.Client,
 		// Patch the node if needed.
 		if hasAnnotationChanges || hasTaintChanges {
 			if err := patchHelper.Patch(ctx, node); err != nil {
-				log.V(2).Info("Failed patch Node to set annotations and drop taints", "err", err, "node name", node.Name)
+				log.V(2).Info("Failed patch node to set annotations and drop taints", "err", err, "node name", node.Name)
 				return err
 			}
 		}
diff --git a/exp/runtime/internal/controllers/extensionconfig_controller.go b/exp/runtime/internal/controllers/extensionconfig_controller.go
index ffbe2ab3d..95770ba05 100644
--- a/exp/runtime/internal/controllers/extensionconfig_controller.go
+++ b/exp/runtime/internal/controllers/extensionconfig_controller.go
@@ -159,13 +159,17 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
 func patchExtensionConfig(ctx context.Context, client client.Client, original, modified *runtimev1.ExtensionConfig, options ...patch.Option) error {
 	patchHelper, err := patch.NewHelper(original, client)
 	if err != nil {
-		return err
+		return errors.Wrapf(err, "failed to create patch helper for %s", tlog.KObj{Obj: modified})
 	}
 
 	options = append(options, patch.WithOwnedConditions{Conditions: []clusterv1.ConditionType{
 		runtimev1.RuntimeExtensionDiscoveredCondition,
 	}})
-	return patchHelper.Patch(ctx, modified, options...)
+	err = patchHelper.Patch(ctx, modified, options...)
+	if err != nil {
+		return errors.Wrapf(err, "failed to patch %s", tlog.KObj{Obj: modified})
+	}
+	return nil
 }
 
 // reconcileDelete will remove the ExtensionConfig from the registry on deletion of the object. Note this is a best
diff --git a/go.mod b/go.mod
index 5e54d23c6..94dddb31f 100644
--- a/go.mod
+++ b/go.mod
@@ -24,7 +24,7 @@ require (
 	github.com/onsi/ginkgo/v2 v2.13.2
 	github.com/onsi/gomega v1.30.0
 	github.com/pkg/errors v0.9.1
-	github.com/prometheus/client_golang v1.18.0
+	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
@@ -112,7 +112,7 @@ require (
 	github.com/mattn/go-colorable v0.1.13 // indirect
 	github.com/mattn/go-isatty v0.0.20 // indirect
 	github.com/mattn/go-runewidth v0.0.14 // indirect
-	github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // 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
@@ -127,9 +127,9 @@ require (
 	github.com/opencontainers/go-digest v1.0.0 // indirect
 	github.com/pelletier/go-toml/v2 v2.1.0 // indirect
 	github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
-	github.com/prometheus/client_model v0.5.0 // indirect
-	github.com/prometheus/common v0.45.0 // indirect
-	github.com/prometheus/procfs v0.12.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/rivo/uniseg v0.4.2 // indirect
 	github.com/russross/blackfriday/v2 v2.1.0 // indirect
 	github.com/sagikazarmark/locafero v0.4.0 // indirect
@@ -156,7 +156,7 @@ require (
 	go.uber.org/multierr v1.11.0 // indirect
 	go.uber.org/zap v1.25.0 // indirect
 	go4.org v0.0.0-20201209231011-d4a079459e60 // indirect
-	golang.org/x/crypto v0.17.0 // indirect
+	golang.org/x/crypto v0.16.0 // indirect
 	golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
 	golang.org/x/net v0.19.0 // indirect
 	golang.org/x/sync v0.5.0 // indirect
diff --git a/go.sum b/go.sum
index 3a9ed83cc..d68cf7082 100644
--- a/go.sum
+++ b/go.sum
@@ -356,8 +356,8 @@ github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m
 github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
 github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
-github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg=
-github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k=
+github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
+github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
 github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
 github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
 github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
@@ -421,21 +421,21 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH
 github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
 github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
 github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
-github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk=
-github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA=
+github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q=
+github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY=
 github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
 github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
-github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
+github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 h1:v7DLqVdK4VrYkVD5diGdl4sxJurKJEMnODWRJlxV9oM=
+github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
 github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
 github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
-github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM=
-github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY=
+github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY=
+github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
 github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
-github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
-github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
+github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI=
+github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY=
 github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
 github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
 github.com/rivo/uniseg v0.4.2 h1:YwD0ulJSJytLpiaWua0sBDusfsCZohxjxzVTYjwxfV8=
@@ -596,8 +596,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
-golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
-golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
+golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=
+golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
diff --git a/hack/tools/go.mod b/hack/tools/go.mod
index 3927e4bea..7de284f0b 100644
--- a/hack/tools/go.mod
+++ b/hack/tools/go.mod
@@ -7,7 +7,7 @@ replace sigs.k8s.io/cluster-api => ../../
 replace sigs.k8s.io/cluster-api/test => ../../test
 
 require (
-	cloud.google.com/go/storage v1.36.0
+	cloud.google.com/go/storage v1.35.1
 	github.com/blang/semver/v4 v4.0.0
 	github.com/onsi/gomega v1.30.0
 	github.com/pkg/errors v0.9.1
@@ -60,7 +60,7 @@ require (
 	github.com/felixge/httpsnoop v1.0.4 // indirect
 	github.com/fsnotify/fsnotify v1.7.0 // indirect
 	github.com/go-errors/errors v1.4.2 // indirect
-	github.com/go-logr/logr v1.4.1 // indirect
+	github.com/go-logr/logr v1.3.0 // indirect
 	github.com/go-logr/stdr v1.2.2 // indirect
 	github.com/go-openapi/jsonpointer v0.19.6 // indirect
 	github.com/go-openapi/jsonreference v0.20.2 // indirect
@@ -86,7 +86,7 @@ require (
 	github.com/json-iterator/go v1.1.12 // indirect
 	github.com/magiconair/properties v1.8.7 // indirect
 	github.com/mailru/easyjson v0.7.7 // indirect
-	github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
+	github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
 	github.com/mitchellh/copystructure v1.2.0 // indirect
 	github.com/mitchellh/mapstructure v1.5.0 // indirect
 	github.com/mitchellh/reflectwalk v1.0.2 // indirect
@@ -96,10 +96,10 @@ require (
 	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/prometheus/client_golang v1.18.0 // indirect
-	github.com/prometheus/client_model v0.5.0 // indirect
-	github.com/prometheus/common v0.45.0 // indirect
-	github.com/prometheus/procfs v0.12.0 // 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/sagikazarmark/locafero v0.4.0 // indirect
 	github.com/sagikazarmark/slog-shim v0.1.0 // indirect
 	github.com/shopspring/decimal v1.3.1 // indirect
@@ -117,7 +117,7 @@ require (
 	go.opentelemetry.io/otel/trace v1.21.0 // indirect
 	go.uber.org/goleak v1.3.0 // indirect
 	go.uber.org/multierr v1.11.0 // indirect
-	golang.org/x/crypto v0.17.0 // indirect
+	golang.org/x/crypto v0.16.0 // indirect
 	golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
 	golang.org/x/mod v0.13.0 // indirect
 	golang.org/x/net v0.19.0 // indirect
diff --git a/hack/tools/go.sum b/hack/tools/go.sum
index 4d6a1e907..e298da14b 100644
--- a/hack/tools/go.sum
+++ b/hack/tools/go.sum
@@ -7,8 +7,8 @@ cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGB
 cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
 cloud.google.com/go/iam v1.1.5 h1:1jTsCu4bcsNsE4iiqNT5SHwrDRCfRmIaaaVFhRveTJI=
 cloud.google.com/go/iam v1.1.5/go.mod h1:rB6P/Ic3mykPbFio+vo7403drjlgvoWfYpJhMXEbzv8=
-cloud.google.com/go/storage v1.36.0 h1:P0mOkAcaJxhCTvAkMhxMfrTKiNcub4YmmPBtlhAyTr8=
-cloud.google.com/go/storage v1.36.0/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8=
+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=
 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=
@@ -88,8 +88,8 @@ github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxI
 github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
 github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
 github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
-github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
-github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY=
+github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
 github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
 github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
 github.com/go-logr/zapr v1.2.4 h1:QHVo+6stLbfJmYGkQ7uGHUCu5hnAFAj6mDe6Ea0SeOo=
@@ -184,8 +184,8 @@ github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0V
 github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
 github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
 github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
-github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg=
-github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k=
+github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
+github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
 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=
@@ -224,15 +224,15 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk=
-github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA=
+github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q=
+github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY=
 github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
-github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
-github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM=
-github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY=
-github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
-github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
+github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 h1:v7DLqVdK4VrYkVD5diGdl4sxJurKJEMnODWRJlxV9oM=
+github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
+github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY=
+github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
+github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI=
+github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY=
 github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
 github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
 github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
@@ -298,8 +298,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
-golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
-golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
+golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=
+golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
 golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
@@ -331,6 +331,7 @@ golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ=
 golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
diff --git a/hack/tools/internal/log-push/main.go b/hack/tools/internal/log-push/main.go
index bc9caaa9a..7cea499f0 100644
--- a/hack/tools/internal/log-push/main.go
+++ b/hack/tools/internal/log-push/main.go
@@ -431,3 +431,10 @@ func pushStreamToLoki(ctx context.Context, lokiURL, lokiOrgID string, body []byt
 	klog.Infof("Push response: status: %q, body: %q", resp.Status, string(respBody))
 	return nil
 }
+
+func min(a, b int) int {
+	if a <= b {
+		return a
+	}
+	return b
+}
diff --git a/hack/tools/release/notes/main.go b/hack/tools/release/notes/main.go
index 860fd702a..2417667dc 100644
--- a/hack/tools/release/notes/main.go
+++ b/hack/tools/release/notes/main.go
@@ -154,7 +154,7 @@ func validateConfig(config *notesCmdConfig) error {
 // computeConfigDefaults calculates the value the non specified configuration fields
 // based on the set fields.
 func computeConfigDefaults(config *notesCmdConfig) error {
-	if config.fromRef != "" && config.branch != "" && config.toRef != "" {
+	if config.fromRef != "" && config.branch != "" {
 		return nil
 	}
 
diff --git a/internal/controllers/clusterclass/clusterclass_controller.go b/internal/controllers/clusterclass/clusterclass_controller.go
index 4d94939a4..a2823ae6a 100644
--- a/internal/controllers/clusterclass/clusterclass_controller.go
+++ b/internal/controllers/clusterclass/clusterclass_controller.go
@@ -114,7 +114,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl.Re
 
 	patchHelper, err := patch.NewHelper(clusterClass, r.Client)
 	if err != nil {
-		return ctrl.Result{}, err
+		return ctrl.Result{}, errors.Wrapf(err, "failed to create patch helper for %s", tlog.KObj{Obj: clusterClass})
 	}
 
 	defer func() {
@@ -124,7 +124,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl.Re
 			patchOpts = append(patchOpts, patch.WithStatusObservedGeneration{})
 		}
 		if err := patchHelper.Patch(ctx, clusterClass, patchOpts...); err != nil {
-			reterr = kerrors.NewAggregate([]error{reterr, err})
+			reterr = kerrors.NewAggregate([]error{reterr, errors.Wrapf(err, "failed to patch %s", tlog.KObj{Obj: clusterClass})})
 			return
 		}
 	}()
@@ -374,7 +374,7 @@ func (r *Reconciler) reconcileExternal(ctx context.Context, clusterClass *cluste
 	// Initialize the patch helper.
 	patchHelper, err := patch.NewHelper(obj, r.Client)
 	if err != nil {
-		return err
+		return errors.Wrapf(err, "failed to create patch helper for %s", tlog.KObj{Obj: obj})
 	}
 
 	// Set external object ControllerReference to the ClusterClass.
@@ -383,7 +383,11 @@ func (r *Reconciler) reconcileExternal(ctx context.Context, clusterClass *cluste
 	}
 
 	// Patch the external object.
-	return patchHelper.Patch(ctx, obj)
+	if err := patchHelper.Patch(ctx, obj); err != nil {
+		return errors.Wrapf(err, "failed to patch object %s", tlog.KObj{Obj: obj})
+	}
+
+	return nil
 }
 
 func uniqueObjectRefKey(ref *corev1.ObjectReference) string {
diff --git a/internal/controllers/machinedeployment/machinedeployment_rolling.go b/internal/controllers/machinedeployment/machinedeployment_rolling.go
index 0c79bf365..ffc7acc19 100644
--- a/internal/controllers/machinedeployment/machinedeployment_rolling.go
+++ b/internal/controllers/machinedeployment/machinedeployment_rolling.go
@@ -21,6 +21,7 @@ import (
 	"sort"
 
 	"github.com/pkg/errors"
+	"k8s.io/utils/integer"
 	ctrl "sigs.k8s.io/controller-runtime"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 
@@ -216,7 +217,7 @@ func (r *Reconciler) cleanupUnhealthyReplicas(ctx context.Context, oldMSs []*clu
 
 		remainingCleanupCount := maxCleanupCount - totalScaledDown
 		unhealthyCount := oldMSReplicas - oldMSAvailableReplicas
-		scaledDownCount := min(remainingCleanupCount, unhealthyCount)
+		scaledDownCount := integer.Int32Min(remainingCleanupCount, unhealthyCount)
 		newReplicasCount := oldMSReplicas - scaledDownCount
 
 		if newReplicasCount > oldMSReplicas {
@@ -277,7 +278,7 @@ func (r *Reconciler) scaleDownOldMachineSetsForRollingUpdate(ctx context.Context
 		}
 
 		// Scale down.
-		scaleDownCount := min(*(targetMS.Spec.Replicas), totalScaleDownCount-totalScaledDown)
+		scaleDownCount := integer.Int32Min(*(targetMS.Spec.Replicas), totalScaleDownCount-totalScaledDown)
 		newReplicasCount := *(targetMS.Spec.Replicas) - scaleDownCount
 		if newReplicasCount > *(targetMS.Spec.Replicas) {
 			return totalScaledDown, errors.Errorf("when scaling down old MachineSet, got invalid request to scale down %v: %d -> %d",
diff --git a/internal/controllers/machinedeployment/mdutil/util.go b/internal/controllers/machinedeployment/mdutil/util.go
index 772654333..2cf9d3427 100644
--- a/internal/controllers/machinedeployment/mdutil/util.go
+++ b/internal/controllers/machinedeployment/mdutil/util.go
@@ -334,12 +334,12 @@ func GetProportion(ms *clusterv1.MachineSet, md clusterv1.MachineDeployment, dep
 		// Use the minimum between the machine set fraction and the maximum allowed replicas
 		// when scaling up. This way we ensure we will not scale up more than the allowed
 		// replicas we can add.
-		return min(msFraction, allowed)
+		return integer.Int32Min(msFraction, allowed)
 	}
 	// Use the maximum between the machine set fraction and the maximum allowed replicas
 	// when scaling down. This way we ensure we will not scale down more than the allowed
 	// replicas we can remove.
-	return max(msFraction, allowed)
+	return integer.Int32Max(msFraction, allowed)
 }
 
 // getMachineSetFraction estimates the fraction of replicas a machine set can have in
@@ -485,7 +485,7 @@ func TotalMachineSetsReplicaSum(machineSets []*clusterv1.MachineSet) int32 {
 	totalReplicas := int32(0)
 	for _, ms := range machineSets {
 		if ms != nil {
-			totalReplicas += max(*(ms.Spec.Replicas), ms.Status.Replicas)
+			totalReplicas += integer.Int32Max(*(ms.Spec.Replicas), ms.Status.Replicas)
 		}
 	}
 	return totalReplicas
@@ -550,7 +550,7 @@ func NewMSNewReplicas(deployment *clusterv1.MachineDeployment, allMSs []*cluster
 		// Scale up.
 		scaleUpCount := maxTotalMachines - currentMachineCount
 		// Do not exceed the number of desired replicas.
-		scaleUpCount = min(scaleUpCount, *(deployment.Spec.Replicas)-newMSReplicas)
+		scaleUpCount = integer.Int32Min(scaleUpCount, *(deployment.Spec.Replicas)-newMSReplicas)
 		return newMSReplicas + scaleUpCount, nil
 	case clusterv1.OnDeleteMachineDeploymentStrategyType:
 		// Find the total number of machines
diff --git a/internal/controllers/machinehealthcheck/machinehealthcheck_controller.go b/internal/controllers/machinehealthcheck/machinehealthcheck_controller.go
index 08879720d..c62411a07 100644
--- a/internal/controllers/machinehealthcheck/machinehealthcheck_controller.go
+++ b/internal/controllers/machinehealthcheck/machinehealthcheck_controller.go
@@ -148,6 +148,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl.Re
 	// Initialize the patch helper
 	patchHelper, err := patch.NewHelper(m, r.Client)
 	if err != nil {
+		log.Error(err, "Failed to build patch helper")
 		return ctrl.Result{}, err
 	}
 
diff --git a/internal/controllers/machinehealthcheck/machinehealthcheck_targets.go b/internal/controllers/machinehealthcheck/machinehealthcheck_targets.go
index b9ee05827..9b1ff909a 100644
--- a/internal/controllers/machinehealthcheck/machinehealthcheck_targets.go
+++ b/internal/controllers/machinehealthcheck/machinehealthcheck_targets.go
@@ -212,7 +212,7 @@ func (r *Reconciler) getTargetsFromMHC(ctx context.Context, logger logr.Logger,
 
 		patchHelper, err := patch.NewHelper(&machines[k], r.Client)
 		if err != nil {
-			return nil, err
+			return nil, errors.Wrap(err, "unable to initialize patch helper")
 		}
 		target := healthCheckTarget{
 			Cluster:     cluster,
diff --git a/internal/controllers/machineset/machineset_controller.go b/internal/controllers/machineset/machineset_controller.go
index 58682dba2..e2b589749 100644
--- a/internal/controllers/machineset/machineset_controller.go
+++ b/internal/controllers/machineset/machineset_controller.go
@@ -995,12 +995,12 @@ func (r *Reconciler) reconcileUnhealthyMachines(ctx context.Context, cluster *cl
 		for _, m := range machinesToRemediate {
 			patchHelper, err := patch.NewHelper(m, r.Client)
 			if err != nil {
-				errs = append(errs, err)
+				errs = append(errs, errors.Wrapf(err, "failed to create patch helper for Machine %s", klog.KObj(m)))
 				continue
 			}
 			conditions.MarkFalse(m, clusterv1.MachineOwnerRemediatedCondition, clusterv1.WaitingForRemediationReason, clusterv1.ConditionSeverityWarning, preflightCheckErrMessage)
 			if err := patchHelper.Patch(ctx, m); err != nil {
-				errs = append(errs, err)
+				errs = append(errs, errors.Wrapf(err, "failed to patch Machine %s", klog.KObj(m)))
 			}
 		}
 
diff --git a/internal/controllers/topology/cluster/cluster_controller.go b/internal/controllers/topology/cluster/cluster_controller.go
index af564c089..27101dd46 100644
--- a/internal/controllers/topology/cluster/cluster_controller.go
+++ b/internal/controllers/topology/cluster/cluster_controller.go
@@ -191,7 +191,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl.Re
 			patch.WithForceOverwriteConditions{},
 		}
 		if err := patchHelper.Patch(ctx, cluster, options...); err != nil {
-			reterr = kerrors.NewAggregate([]error{reterr, err})
+			reterr = kerrors.NewAggregate([]error{reterr, errors.Wrap(err, "failed to patch cluster")})
 			return
 		}
 	}()
diff --git a/internal/controllers/topology/machinedeployment/machinedeployment_controller.go b/internal/controllers/topology/machinedeployment/machinedeployment_controller.go
index 6ab485f4c..524c08ac4 100644
--- a/internal/controllers/topology/machinedeployment/machinedeployment_controller.go
+++ b/internal/controllers/topology/machinedeployment/machinedeployment_controller.go
@@ -116,11 +116,11 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl.Re
 	// Create a patch helper to add or remove the finalizer from the MachineDeployment.
 	patchHelper, err := patch.NewHelper(md, r.Client)
 	if err != nil {
-		return ctrl.Result{}, err
+		return ctrl.Result{}, errors.Wrapf(err, "failed to create patch helper for %s", tlog.KObj{Obj: md})
 	}
 	defer func() {
 		if err := patchHelper.Patch(ctx, md); err != nil {
-			reterr = kerrors.NewAggregate([]error{reterr, err})
+			reterr = kerrors.NewAggregate([]error{reterr, errors.Wrapf(err, "failed to patch %s", tlog.KObj{Obj: md})})
 		}
 	}()
 
diff --git a/internal/controllers/topology/machineset/machineset_controller.go b/internal/controllers/topology/machineset/machineset_controller.go
index 5437b9489..a6476da44 100644
--- a/internal/controllers/topology/machineset/machineset_controller.go
+++ b/internal/controllers/topology/machineset/machineset_controller.go
@@ -123,11 +123,11 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl.Re
 	// Create a patch helper to add or remove the finalizer from the MachineSet.
 	patchHelper, err := patch.NewHelper(ms, r.Client)
 	if err != nil {
-		return ctrl.Result{}, err
+		return ctrl.Result{}, errors.Wrapf(err, "failed to create patch helper for %s", tlog.KObj{Obj: ms})
 	}
 	defer func() {
 		if err := patchHelper.Patch(ctx, ms); err != nil {
-			reterr = kerrors.NewAggregate([]error{reterr, err})
+			reterr = kerrors.NewAggregate([]error{reterr, errors.Wrapf(err, "failed to patch %s", tlog.KObj{Obj: ms})})
 		}
 	}()
 
diff --git a/internal/hooks/tracking.go b/internal/hooks/tracking.go
index d8d91b1b5..21358ffc0 100644
--- a/internal/hooks/tracking.go
+++ b/internal/hooks/tracking.go
@@ -41,7 +41,7 @@ func MarkAsPending(ctx context.Context, c client.Client, obj client.Object, hook
 
 	patchHelper, err := patch.NewHelper(obj, c)
 	if err != nil {
-		return errors.Wrapf(err, "failed to mark %q hook(s) as pending", strings.Join(hookNames, ","))
+		return errors.Wrapf(err, "failed to mark %q hook(s) as pending: failed to create patch helper for %s", strings.Join(hookNames, ","), tlog.KObj{Obj: obj})
 	}
 
 	// Read the annotation of the objects and add the hook to the comma separated list
@@ -53,7 +53,7 @@ func MarkAsPending(ctx context.Context, c client.Client, obj client.Object, hook
 	obj.SetAnnotations(annotations)
 
 	if err := patchHelper.Patch(ctx, obj); err != nil {
-		return errors.Wrapf(err, "failed to mark %q hook(s) as pending", strings.Join(hookNames, ","))
+		return errors.Wrapf(err, "failed to mark %q hook(s) as pending: failed to patch %s", strings.Join(hookNames, ","), tlog.KObj{Obj: obj})
 	}
 
 	return nil
@@ -80,7 +80,7 @@ func MarkAsDone(ctx context.Context, c client.Client, obj client.Object, hooks .
 
 	patchHelper, err := patch.NewHelper(obj, c)
 	if err != nil {
-		return errors.Wrapf(err, "failed to mark %q hook(s) as done", strings.Join(hookNames, ","))
+		return errors.Wrapf(err, "failed to mark %q hook(s) as done: failed to create patch helper for %s", strings.Join(hookNames, ","), tlog.KObj{Obj: obj})
 	}
 
 	// Read the annotation of the objects and add the hook to the comma separated list
@@ -95,7 +95,7 @@ func MarkAsDone(ctx context.Context, c client.Client, obj client.Object, hooks .
 	obj.SetAnnotations(annotations)
 
 	if err := patchHelper.Patch(ctx, obj); err != nil {
-		return errors.Wrapf(err, "failed to mark %q hook(s) as done", strings.Join(hookNames, ","))
+		return errors.Wrapf(err, "failed to mark %q hook(s) as done: failed to patch %s", strings.Join(hookNames, ","), tlog.KObj{Obj: obj})
 	}
 
 	return nil
@@ -117,7 +117,7 @@ func IsOkToDelete(obj client.Object) bool {
 func MarkAsOkToDelete(ctx context.Context, c client.Client, obj client.Object) error {
 	patchHelper, err := patch.NewHelper(obj, c)
 	if err != nil {
-		return errors.Wrapf(err, "failed to mark %s as ok to delete", tlog.KObj{Obj: obj})
+		return errors.Wrapf(err, "failed to mark %s as ok to delete: failed to create patch helper", tlog.KObj{Obj: obj})
 	}
 
 	annotations := obj.GetAnnotations()
@@ -128,7 +128,7 @@ func MarkAsOkToDelete(ctx context.Context, c client.Client, obj client.Object) e
 	obj.SetAnnotations(annotations)
 
 	if err := patchHelper.Patch(ctx, obj); err != nil {
-		return errors.Wrapf(err, "failed to mark %s as ok to delete", tlog.KObj{Obj: obj})
+		return errors.Wrapf(err, "failed to mark %s as ok to delete: failed to patch", tlog.KObj{Obj: obj})
 	}
 
 	return nil
diff --git a/util/patch/patch.go b/util/patch/patch.go
index 388132273..dcf693495 100644
--- a/util/patch/patch.go
+++ b/util/patch/patch.go
@@ -28,7 +28,6 @@ import (
 	"k8s.io/apimachinery/pkg/runtime/schema"
 	kerrors "k8s.io/apimachinery/pkg/util/errors"
 	"k8s.io/apimachinery/pkg/util/wait"
-	"k8s.io/klog/v2"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
 
@@ -54,20 +53,20 @@ type Helper struct {
 func NewHelper(obj client.Object, crClient client.Client) (*Helper, error) {
 	// Return early if the object is nil.
 	if util.IsNil(obj) {
-		return nil, errors.New("failed to create patch helper: object is nil")
+		return nil, errors.New("helper could not be created: object is nil")
 	}
 
 	// Get the GroupVersionKind of the object,
 	// used to validate against later on.
 	gvk, err := apiutil.GVKForObject(obj, crClient.Scheme())
 	if err != nil {
-		return nil, errors.Wrapf(err, "failed to create patch helper for object %s", klog.KObj(obj))
+		return nil, err
 	}
 
 	// Convert the object to unstructured to compare against our before copy.
 	unstructuredObj, err := toUnstructured(obj)
 	if err != nil {
-		return nil, errors.Wrapf(err, "failed to create patch helper for %s %s: failed to convert object to Unstructured", gvk.Kind, klog.KObj(obj))
+		return nil, err
 	}
 
 	// Check if the object satisfies the Cluster API conditions contract.
@@ -86,16 +85,16 @@ func NewHelper(obj client.Object, crClient client.Client) (*Helper, error) {
 func (h *Helper) Patch(ctx context.Context, obj client.Object, opts ...Option) error {
 	// Return early if the object is nil.
 	if util.IsNil(obj) {
-		return errors.Errorf("failed to patch %s %s: modified object is nil", h.gvk.Kind, klog.KObj(h.beforeObject))
+		return errors.New("Patch could not be completed: object is nil")
 	}
 
 	// Get the GroupVersionKind of the object that we want to patch.
 	gvk, err := apiutil.GVKForObject(obj, h.client.Scheme())
 	if err != nil {
-		return errors.Wrapf(err, "failed to patch %s %s", h.gvk.Kind, klog.KObj(h.beforeObject))
+		return err
 	}
 	if gvk != h.gvk {
-		return errors.Errorf("failed to patch %s %s: unmatched GroupVersionKind, expected %q got %q", h.gvk.Kind, klog.KObj(h.beforeObject), h.gvk, gvk)
+		return errors.Errorf("unmatched GroupVersionKind, expected %q got %q", h.gvk, gvk)
 	}
 
 	// Calculate the options.
@@ -107,7 +106,7 @@ func (h *Helper) Patch(ctx context.Context, obj client.Object, opts ...Option) e
 	// Convert the object to unstructured to compare against our before copy.
 	h.after, err = toUnstructured(obj)
 	if err != nil {
-		return errors.Wrapf(err, "failed to patch %s %s: failed to convert object to Unstructured", h.gvk.Kind, klog.KObj(h.beforeObject))
+		return err
 	}
 
 	// Determine if the object has status.
@@ -115,12 +114,12 @@ func (h *Helper) Patch(ctx context.Context, obj client.Object, opts ...Option) e
 		if options.IncludeStatusObservedGeneration {
 			// Set status.observedGeneration if we're asked to do so.
 			if err := unstructured.SetNestedField(h.after.Object, h.after.GetGeneration(), "status", "observedGeneration"); err != nil {
-				return errors.Wrapf(err, "failed to patch %s %s: failed to set .status.observedGeneration", h.gvk.Kind, klog.KObj(h.beforeObject))
+				return err
 			}
 
 			// Restore the changes back to the original object.
 			if err := runtime.DefaultUnstructuredConverter.FromUnstructured(h.after.Object, obj); err != nil {
-				return errors.Wrapf(err, "failed to patch %s %s: failed to converted object from Unstructured", h.gvk.Kind, klog.KObj(h.beforeObject))
+				return err
 			}
 		}
 	}
@@ -128,31 +127,22 @@ func (h *Helper) Patch(ctx context.Context, obj client.Object, opts ...Option) e
 	// Calculate and store the top-level field changes (e.g. "metadata", "spec", "status") we have before/after.
 	h.changes, err = h.calculateChanges(obj)
 	if err != nil {
-		return errors.Wrapf(err, "failed to patch %s %s", h.gvk.Kind, klog.KObj(h.beforeObject))
+		return err
 	}
 
 	// Issue patches and return errors in an aggregate.
-	var errs []error
-	// Patch the conditions first.
-	//
-	// Given that we pass in metadata.resourceVersion to perform a 3-way-merge conflict resolution,
-	// patching conditions first avoids an extra loop if spec or status patch succeeds first
-	// given that causes the resourceVersion to mutate.
-	if err := h.patchStatusConditions(ctx, obj, options.ForceOverwriteConditions, options.OwnedConditions); err != nil {
-		errs = append(errs, err)
-	}
-	// Then proceed to patch the rest of the object.
-	if err := h.patch(ctx, obj); err != nil {
-		errs = append(errs, err)
-	}
-	if err := h.patchStatus(ctx, obj); err != nil {
-		errs = append(errs, err)
-	}
-
-	if len(errs) > 0 {
-		return errors.Wrapf(kerrors.NewAggregate(errs), "failed to patch %s %s", h.gvk.Kind, klog.KObj(h.beforeObject))
-	}
-	return nil
+	return kerrors.NewAggregate([]error{
+		// Patch the conditions first.
+		//
+		// Given that we pass in metadata.resourceVersion to perform a 3-way-merge conflict resolution,
+		// patching conditions first avoids an extra loop if spec or status patch succeeds first
+		// given that causes the resourceVersion to mutate.
+		h.patchStatusConditions(ctx, obj, options.ForceOverwriteConditions, options.OwnedConditions),
+
+		// Then proceed to patch the rest of the object.
+		h.patch(ctx, obj),
+		h.patchStatus(ctx, obj),
+	})
 }
 
 // patch issues a patch for metadata and spec.


Test patch:
diff --git a/internal/controllers/machinehealthcheck/machinehealthcheck_controller_test.go b/internal/controllers/machinehealthcheck/machinehealthcheck_controller_test.go
index 93fa8332c..fab1d0e34 100644
--- a/internal/controllers/machinehealthcheck/machinehealthcheck_controller_test.go
+++ b/internal/controllers/machinehealthcheck/machinehealthcheck_controller_test.go
@@ -2607,8 +2607,7 @@ func TestPatchTargets(t *testing.T) {
 	// To make the patch fail, create patchHelper with a different client.
 	fakeMachine := machine1.DeepCopy()
 	fakeMachine.Name = "fake"
-	patchHelper, err := patch.NewHelper(fakeMachine, fake.NewClientBuilder().WithObjects(fakeMachine).Build())
-	g.Expect(err).ToNot(HaveOccurred())
+	patchHelper, _ := patch.NewHelper(fakeMachine, fake.NewClientBuilder().WithObjects(fakeMachine).Build())
 	// healthCheckTarget with fake patchHelper, patch should fail on this target.
 	target1 := healthCheckTarget{
 		MHC:         mhc,
@@ -2618,8 +2617,7 @@ func TestPatchTargets(t *testing.T) {
 	}
 
 	// healthCheckTarget with correct patchHelper.
-	patchHelper2, err := patch.NewHelper(machine2, cl)
-	g.Expect(err).ToNot(HaveOccurred())
+	patchHelper2, _ := patch.NewHelper(machine2, cl)
 	target3 := healthCheckTarget{
 		MHC:         mhc,
 		Machine:     machine2,
diff --git a/test/e2e/autoscaler_test.go b/test/e2e/autoscaler_test.go
index 1654f7f26..539815bd1 100644
--- a/test/e2e/autoscaler_test.go
+++ b/test/e2e/autoscaler_test.go
@@ -35,7 +35,7 @@ var _ = Describe("When using the autoscaler with Cluster API using ClusterClass
 			InfrastructureProvider:            ptr.To("docker"),
 			InfrastructureMachineTemplateKind: "dockermachinetemplates",
 			Flavor:                            ptr.To("topology-autoscaler"),
-			AutoscalerVersion:                 "v1.29.0",
+			AutoscalerVersion:                 "v1.28.0",
 		}
 	})
 })
diff --git a/test/e2e/cluster_upgrade.go b/test/e2e/cluster_upgrade.go
index 81ee41180..dbb320783 100644
--- a/test/e2e/cluster_upgrade.go
+++ b/test/e2e/cluster_upgrade.go
@@ -84,9 +84,6 @@ func ClusterUpgradeConformanceSpec(ctx context.Context, inputGetter func() Clust
 		controlPlaneMachineCount int64
 		workerMachineCount       int64
 
-		etcdVersionUpgradeTo    string
-		coreDNSVersionUpgradeTo string
-
 		clusterResources       *clusterctl.ApplyClusterTemplateAndWaitResult
 		kubetestConfigFilePath string
 	)
@@ -101,6 +98,8 @@ func ClusterUpgradeConformanceSpec(ctx context.Context, inputGetter func() Clust
 
 		Expect(input.E2EConfig.Variables).To(HaveKey(KubernetesVersionUpgradeFrom))
 		Expect(input.E2EConfig.Variables).To(HaveKey(KubernetesVersionUpgradeTo))
+		Expect(input.E2EConfig.Variables).To(HaveKey(EtcdVersionUpgradeTo))
+		Expect(input.E2EConfig.Variables).To(HaveKey(CoreDNSVersionUpgradeTo))
 
 		Expect(input.E2EConfig.Variables).To(HaveKey(kubetestConfigurationVariable), "% spec requires a %s variable to be defined in the config file", specName, kubetestConfigurationVariable)
 		kubetestConfigFilePath = input.E2EConfig.GetVariable(kubetestConfigurationVariable)
@@ -118,13 +117,6 @@ func ClusterUpgradeConformanceSpec(ctx context.Context, inputGetter func() Clust
 			workerMachineCount = *input.WorkerMachineCount
 		}
 
-		if input.E2EConfig.HasVariable(EtcdVersionUpgradeTo) {
-			etcdVersionUpgradeTo = input.E2EConfig.GetVariable(EtcdVersionUpgradeTo)
-		}
-		if input.E2EConfig.HasVariable(CoreDNSVersionUpgradeTo) {
-			coreDNSVersionUpgradeTo = input.E2EConfig.GetVariable(CoreDNSVersionUpgradeTo)
-		}
-
 		// Setup a Namespace where to host objects for this spec and create a watcher for the Namespace events.
 		namespace, cancelWatches = setupSpecNamespace(ctx, specName, input.BootstrapClusterProxy, input.ArtifactFolder)
 		clusterResources = new(clusterctl.ApplyClusterTemplateAndWaitResult)
@@ -166,8 +158,8 @@ func ClusterUpgradeConformanceSpec(ctx context.Context, inputGetter func() Clust
 				ClusterProxy:                   input.BootstrapClusterProxy,
 				Cluster:                        clusterResources.Cluster,
 				ControlPlane:                   clusterResources.ControlPlane,
-				EtcdImageTag:                   etcdVersionUpgradeTo,
-				DNSImageTag:                    coreDNSVersionUpgradeTo,
+				EtcdImageTag:                   input.E2EConfig.GetVariable(EtcdVersionUpgradeTo),
+				DNSImageTag:                    input.E2EConfig.GetVariable(CoreDNSVersionUpgradeTo),
 				MachineDeployments:             clusterResources.MachineDeployments,
 				MachinePools:                   clusterResources.MachinePools,
 				KubernetesUpgradeVersion:       input.E2EConfig.GetVariable(KubernetesVersionUpgradeTo),
@@ -197,8 +189,8 @@ func ClusterUpgradeConformanceSpec(ctx context.Context, inputGetter func() Clust
 				ClusterProxy:                input.BootstrapClusterProxy,
 				Cluster:                     clusterResources.Cluster,
 				ControlPlane:                clusterResources.ControlPlane,
-				EtcdImageTag:                etcdVersionUpgradeTo,
-				DNSImageTag:                 coreDNSVersionUpgradeTo,
+				EtcdImageTag:                input.E2EConfig.GetVariable(EtcdVersionUpgradeTo),
+				DNSImageTag:                 input.E2EConfig.GetVariable(CoreDNSVersionUpgradeTo),
 				KubernetesUpgradeVersion:    input.E2EConfig.GetVariable(KubernetesVersionUpgradeTo),
 				UpgradeMachineTemplate:      upgradeCPMachineTemplateTo,
 				WaitForMachinesToBeUpgraded: input.E2EConfig.GetIntervals(specName, "wait-machine-upgrade"),
diff --git a/test/e2e/cluster_upgrade_runtimesdk.go b/test/e2e/cluster_upgrade_runtimesdk.go
index 5d32e5d8a..ada1bb9fe 100644
--- a/test/e2e/cluster_upgrade_runtimesdk.go
+++ b/test/e2e/cluster_upgrade_runtimesdk.go
@@ -116,6 +116,8 @@ func clusterUpgradeWithRuntimeSDKSpec(ctx context.Context, inputGetter func() cl
 
 		Expect(input.E2EConfig.Variables).To(HaveKey(KubernetesVersionUpgradeFrom))
 		Expect(input.E2EConfig.Variables).To(HaveKey(KubernetesVersionUpgradeTo))
+		Expect(input.E2EConfig.Variables).To(HaveKey(EtcdVersionUpgradeTo))
+		Expect(input.E2EConfig.Variables).To(HaveKey(CoreDNSVersionUpgradeTo))
 
 		if input.ControlPlaneMachineCount == nil {
 			controlPlaneMachineCount = 1
diff --git a/test/e2e/self_hosted.go b/test/e2e/self_hosted.go
index 5c8361642..c96f48aa1 100644
--- a/test/e2e/self_hosted.go
+++ b/test/e2e/self_hosted.go
@@ -94,9 +94,6 @@ func SelfHostedSpec(ctx context.Context, inputGetter func() SelfHostedSpecInput)
 		controlPlaneMachineCount int64
 		workerMachineCount       int64
 
-		etcdVersionUpgradeTo    string
-		coreDNSVersionUpgradeTo string
-
 		kubernetesVersion string
 	)
 
@@ -116,15 +113,10 @@ func SelfHostedSpec(ctx context.Context, inputGetter func() SelfHostedSpecInput)
 		} else {
 			Expect(input.E2EConfig.Variables).To(HaveKey(KubernetesVersionUpgradeFrom))
 			Expect(input.E2EConfig.Variables).To(HaveKey(KubernetesVersionUpgradeTo))
+			Expect(input.E2EConfig.Variables).To(HaveKey(EtcdVersionUpgradeTo))
+			Expect(input.E2EConfig.Variables).To(HaveKey(CoreDNSVersionUpgradeTo))
 
 			kubernetesVersion = input.E2EConfig.GetVariable(KubernetesVersionUpgradeFrom)
-
-			if input.E2EConfig.HasVariable(EtcdVersionUpgradeTo) {
-				etcdVersionUpgradeTo = input.E2EConfig.GetVariable(EtcdVersionUpgradeTo)
-			}
-			if input.E2EConfig.HasVariable(CoreDNSVersionUpgradeTo) {
-				coreDNSVersionUpgradeTo = input.E2EConfig.GetVariable(CoreDNSVersionUpgradeTo)
-			}
 		}
 
 		// Setup a Namespace where to host objects for this spec and create a watcher for the namespace events.
@@ -322,8 +314,8 @@ func SelfHostedSpec(ctx context.Context, inputGetter func() SelfHostedSpecInput)
 				ClusterProxy:                   selfHostedClusterProxy,
 				Cluster:                        clusterResources.Cluster,
 				ControlPlane:                   clusterResources.ControlPlane,
-				EtcdImageTag:                   etcdVersionUpgradeTo,
-				DNSImageTag:                    coreDNSVersionUpgradeTo,
+				EtcdImageTag:                   input.E2EConfig.GetVariable(EtcdVersionUpgradeTo),
+				DNSImageTag:                    input.E2EConfig.GetVariable(CoreDNSVersionUpgradeTo),
 				MachineDeployments:             clusterResources.MachineDeployments,
 				MachinePools:                   clusterResources.MachinePools,
 				KubernetesUpgradeVersion:       input.E2EConfig.GetVariable(KubernetesVersionUpgradeTo),
@@ -353,8 +345,8 @@ func SelfHostedSpec(ctx context.Context, inputGetter func() SelfHostedSpecInput)
 				ClusterProxy:                selfHostedClusterProxy,
 				Cluster:                     clusterResources.Cluster,
 				ControlPlane:                clusterResources.ControlPlane,
-				EtcdImageTag:                etcdVersionUpgradeTo,
-				DNSImageTag:                 coreDNSVersionUpgradeTo,
+				EtcdImageTag:                input.E2EConfig.GetVariable(EtcdVersionUpgradeTo),
+				DNSImageTag:                 input.E2EConfig.GetVariable(CoreDNSVersionUpgradeTo),
 				KubernetesUpgradeVersion:    input.E2EConfig.GetVariable(KubernetesVersionUpgradeTo),
 				UpgradeMachineTemplate:      upgradeCPMachineTemplateTo,
 				WaitForMachinesToBeUpgraded: input.E2EConfig.GetIntervals(specName, "wait-machine-upgrade"),
diff --git a/test/framework/autoscaler_helpers.go b/test/framework/autoscaler_helpers.go
index 067e6504e..ba896e9c5 100644
--- a/test/framework/autoscaler_helpers.go
+++ b/test/framework/autoscaler_helpers.go
@@ -277,7 +277,7 @@ func DisableAutoscalerForMachineDeploymentTopologyAndWait(ctx context.Context, i
 
 	log.Logf("Dropping the %s and %s annotations from the MachineDeployments in ClusterTopology", clusterv1.AutoscalerMinSizeAnnotation, clusterv1.AutoscalerMaxSizeAnnotation)
 	patchHelper, err := patch.NewHelper(input.Cluster, mgmtClient)
-	Expect(err).ToNot(HaveOccurred())
+	Expect(err).ToNot(HaveOccurred(), fmt.Sprintf("failed to create patch helper for Cluster %s", klog.KObj(input.Cluster)))
 	for i := range input.Cluster.Spec.Topology.Workers.MachineDeployments {
 		md := input.Cluster.Spec.Topology.Workers.MachineDeployments[i]
 		delete(md.Metadata.Annotations, clusterv1.AutoscalerMinSizeAnnotation)
@@ -321,7 +321,7 @@ func EnableAutoscalerForMachineDeploymentTopologyAndWait(ctx context.Context, in
 
 	log.Logf("Add the %s and %s annotations to the MachineDeployments in ClusterTopology", clusterv1.AutoscalerMinSizeAnnotation, clusterv1.AutoscalerMaxSizeAnnotation)
 	patchHelper, err := patch.NewHelper(input.Cluster, mgmtClient)
-	Expect(err).ToNot(HaveOccurred())
+	Expect(err).ToNot(HaveOccurred(), fmt.Sprintf("failed to create patch helper for Cluster %s", klog.KObj(input.Cluster)))
 	for i := range input.Cluster.Spec.Topology.Workers.MachineDeployments {
 		md := input.Cluster.Spec.Topology.Workers.MachineDeployments[i]
 		if md.Metadata.Annotations == nil {
diff --git a/test/framework/cluster_topology_helpers.go b/test/framework/cluster_topology_helpers.go
index 13364d887..927aea913 100644
--- a/test/framework/cluster_topology_helpers.go
+++ b/test/framework/cluster_topology_helpers.go
@@ -84,6 +84,8 @@ func UpgradeClusterTopologyAndWaitForUpgrade(ctx context.Context, input UpgradeC
 	Expect(input.ClusterProxy).ToNot(BeNil(), "Invalid argument. input.ClusterProxy can't be nil when calling UpgradeClusterTopologyAndWaitForUpgrade")
 	Expect(input.Cluster).ToNot(BeNil(), "Invalid argument. input.Cluster can't be nil when calling UpgradeClusterTopologyAndWaitForUpgrade")
 	Expect(input.ControlPlane).ToNot(BeNil(), "Invalid argument. input.ControlPlane can't be nil when calling UpgradeClusterTopologyAndWaitForUpgrade")
+	Expect(input.MachineDeployments).ToNot(BeEmpty(), "Invalid argument. input.MachineDeployments can't be empty when calling UpgradeClusterTopologyAndWaitForUpgrade")
+	Expect(input.MachinePools).ToNot(BeEmpty(), "Invalid argument. input.MachinePools can't be empty when calling UpgradeClusterTopologyAndWaitForUpgrade")
 	Expect(input.KubernetesUpgradeVersion).ToNot(BeNil(), "Invalid argument. input.KubernetesUpgradeVersion can't be empty when calling UpgradeClusterTopologyAndWaitForUpgrade")
 
 	mgmtClient := input.ClusterProxy.GetClient()
@@ -94,11 +96,11 @@ func UpgradeClusterTopologyAndWaitForUpgrade(ctx context.Context, input UpgradeC
 
 	input.Cluster.Spec.Topology.Version = input.KubernetesUpgradeVersion
 	for i, variable := range input.Cluster.Spec.Topology.Variables {
-		if input.EtcdImageTag != "" && variable.Name == "etcdImageTag" {
+		if variable.Name == "etcdImageTag" {
 			// NOTE: strconv.Quote is used to produce a valid JSON string.
 			input.Cluster.Spec.Topology.Variables[i].Value = apiextensionsv1.JSON{Raw: []byte(strconv.Quote(input.EtcdImageTag))}
 		}
-		if input.DNSImageTag != "" && variable.Name == "coreDNSImageTag" {
+		if variable.Name == "coreDNSImageTag" {
 			// NOTE: strconv.Quote is used to produce a valid JSON string.
 			input.Cluster.Spec.Topology.Variables[i].Value = apiextensionsv1.JSON{Raw: []byte(strconv.Quote(input.DNSImageTag))}
 		}
diff --git a/test/framework/controlplane_helpers.go b/test/framework/controlplane_helpers.go
index bb32c41f6..dcdbf85f4 100644
--- a/test/framework/controlplane_helpers.go
+++ b/test/framework/controlplane_helpers.go
@@ -326,6 +326,8 @@ func UpgradeControlPlaneAndWaitForUpgrade(ctx context.Context, input UpgradeCont
 	Expect(input.Cluster).ToNot(BeNil(), "Invalid argument. input.Cluster can't be nil when calling UpgradeControlPlaneAndWaitForUpgrade")
 	Expect(input.ControlPlane).ToNot(BeNil(), "Invalid argument. input.ControlPlane can't be nil when calling UpgradeControlPlaneAndWaitForUpgrade")
 	Expect(input.KubernetesUpgradeVersion).ToNot(BeNil(), "Invalid argument. input.KubernetesUpgradeVersion can't be empty when calling UpgradeControlPlaneAndWaitForUpgrade")
+	Expect(input.EtcdImageTag).ToNot(BeNil(), "Invalid argument. input.EtcdImageTag can't be empty when calling UpgradeControlPlaneAndWaitForUpgrade")
+	Expect(input.DNSImageTag).ToNot(BeNil(), "Invalid argument. input.DNSImageTag can't be empty when calling UpgradeControlPlaneAndWaitForUpgrade")
 
 	mgmtClient := input.ClusterProxy.GetClient()
 
@@ -346,12 +348,8 @@ func UpgradeControlPlaneAndWaitForUpgrade(ctx context.Context, input UpgradeCont
 		input.ControlPlane.Spec.KubeadmConfigSpec.ClusterConfiguration.Etcd.Local = new(bootstrapv1.LocalEtcd)
 	}
 
-	if input.EtcdImageTag != "" {
-		input.ControlPlane.Spec.KubeadmConfigSpec.ClusterConfiguration.Etcd.Local.ImageMeta.ImageTag = input.EtcdImageTag
-	}
-	if input.DNSImageTag != "" {
-		input.ControlPlane.Spec.KubeadmConfigSpec.ClusterConfiguration.DNS.ImageMeta.ImageTag = input.DNSImageTag
-	}
+	input.ControlPlane.Spec.KubeadmConfigSpec.ClusterConfiguration.Etcd.Local.ImageMeta.ImageTag = input.EtcdImageTag
+	input.ControlPlane.Spec.KubeadmConfigSpec.ClusterConfiguration.DNS.ImageMeta.ImageTag = input.DNSImageTag
 
 	Eventually(func() error {
 		return patchHelper.Patch(ctx, input.ControlPlane)
@@ -373,24 +371,20 @@ func UpgradeControlPlaneAndWaitForUpgrade(ctx context.Context, input UpgradeCont
 		KubernetesVersion: input.KubernetesUpgradeVersion,
 	}, input.WaitForKubeProxyUpgrade...)
 
-	if input.DNSImageTag != "" {
-		log.Logf("Waiting for CoreDNS to have the upgraded image tag")
-		WaitForDNSUpgrade(ctx, WaitForDNSUpgradeInput{
-			Getter:     workloadClient,
-			DNSVersion: input.DNSImageTag,
-		}, input.WaitForDNSUpgrade...)
-	}
+	log.Logf("Waiting for CoreDNS to have the upgraded image tag")
+	WaitForDNSUpgrade(ctx, WaitForDNSUpgradeInput{
+		Getter:     workloadClient,
+		DNSVersion: input.DNSImageTag,
+	}, input.WaitForDNSUpgrade...)
 
-	if input.EtcdImageTag != "" {
-		log.Logf("Waiting for etcd to have the upgraded image tag")
-		lblSelector, err := labels.Parse("component=etcd")
-		Expect(err).ToNot(HaveOccurred())
-		WaitForPodListCondition(ctx, WaitForPodListConditionInput{
-			Lister:      workloadClient,
-			ListOptions: &client.ListOptions{LabelSelector: lblSelector},
-			Condition:   EtcdImageTagCondition(input.EtcdImageTag, int(*input.ControlPlane.Spec.Replicas)),
-		}, input.WaitForEtcdUpgrade...)
-	}
+	log.Logf("Waiting for etcd to have the upgraded image tag")
+	lblSelector, err := labels.Parse("component=etcd")
+	Expect(err).ToNot(HaveOccurred())
+	WaitForPodListCondition(ctx, WaitForPodListConditionInput{
+		Lister:      workloadClient,
+		ListOptions: &client.ListOptions{LabelSelector: lblSelector},
+		Condition:   EtcdImageTagCondition(input.EtcdImageTag, int(*input.ControlPlane.Spec.Replicas)),
+	}, input.WaitForEtcdUpgrade...)
 }
 
 // controlPlaneMachineOptions returns a set of ListOptions that allows to get all machine objects belonging to control plane.
diff --git a/test/go.mod b/test/go.mod
index ced82cd59..f5f398c9c 100644
--- a/test/go.mod
+++ b/test/go.mod
@@ -11,12 +11,12 @@ require (
 	github.com/emicklei/go-restful/v3 v3.11.0
 	github.com/evanphx/json-patch/v5 v5.7.0
 	github.com/flatcar/ignition v0.36.2
-	github.com/go-logr/logr v1.4.1
+	github.com/go-logr/logr v1.3.0
 	github.com/google/go-cmp v0.6.0
 	github.com/onsi/ginkgo/v2 v2.13.2
 	github.com/onsi/gomega v1.30.0
 	github.com/pkg/errors v0.9.1
-	github.com/prometheus/client_golang v1.18.0
+	github.com/prometheus/client_golang v1.17.0
 	github.com/spf13/pflag v1.0.5
 	github.com/vincent-petithory/dataurl v1.0.0
 	go.etcd.io/etcd/api/v3 v3.5.11
@@ -94,7 +94,7 @@ require (
 	github.com/magiconair/properties v1.8.7 // indirect
 	github.com/mailru/easyjson v0.7.7 // indirect
 	github.com/mattn/go-isatty v0.0.20 // indirect
-	github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
+	github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
 	github.com/mitchellh/copystructure v1.2.0 // indirect
 	github.com/mitchellh/mapstructure v1.5.0 // indirect
 	github.com/mitchellh/reflectwalk v1.0.2 // indirect
@@ -107,9 +107,9 @@ require (
 	github.com/opencontainers/image-spec v1.0.2 // indirect
 	github.com/pelletier/go-toml v1.9.5 // indirect
 	github.com/pelletier/go-toml/v2 v2.1.0 // indirect
-	github.com/prometheus/client_model v0.5.0 // indirect
-	github.com/prometheus/common v0.45.0 // indirect
-	github.com/prometheus/procfs v0.12.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/sagikazarmark/locafero v0.4.0 // indirect
 	github.com/sagikazarmark/slog-shim v0.1.0 // indirect
 	github.com/shopspring/decimal v1.3.1 // indirect
@@ -133,7 +133,7 @@ require (
 	go.opentelemetry.io/proto/otlp v1.0.0 // indirect
 	go.uber.org/multierr v1.11.0 // indirect
 	go.uber.org/zap v1.25.0 // indirect
-	golang.org/x/crypto v0.17.0 // indirect
+	golang.org/x/crypto v0.16.0 // indirect
 	golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
 	golang.org/x/oauth2 v0.15.0 // indirect
 	golang.org/x/sync v0.5.0 // indirect
diff --git a/test/go.sum b/test/go.sum
index 59195caab..e7af61545 100644
--- a/test/go.sum
+++ b/test/go.sum
@@ -107,8 +107,8 @@ github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3I
 github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
 github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
 github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
-github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
-github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY=
+github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
 github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
 github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
 github.com/go-logr/zapr v1.2.4 h1:QHVo+6stLbfJmYGkQ7uGHUCu5hnAFAj6mDe6Ea0SeOo=
@@ -133,6 +133,7 @@ github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo=
 github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ=
 github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
 github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
 github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
@@ -211,8 +212,8 @@ github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJ
 github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
 github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
 github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
-github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg=
-github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k=
+github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
+github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
 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=
@@ -256,14 +257,14 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk=
-github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA=
-github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
-github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
-github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM=
-github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY=
-github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
-github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
+github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q=
+github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY=
+github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 h1:v7DLqVdK4VrYkVD5diGdl4sxJurKJEMnODWRJlxV9oM=
+github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
+github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY=
+github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
+github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI=
+github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY=
 github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
 github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@@ -378,8 +379,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
-golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
-golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
+golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=
+golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
 golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
 golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
 golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
@@ -404,6 +405,7 @@ golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
 golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
 golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ=
 golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
diff --git a/test/infrastructure/docker/api/v1alpha4/conversion.go b/test/infrastructure/docker/api/v1alpha4/conversion.go
index 6c7dc8507..e8c9ae8d8 100644
--- a/test/infrastructure/docker/api/v1alpha4/conversion.go
+++ b/test/infrastructure/docker/api/v1alpha4/conversion.go
@@ -122,35 +122,13 @@ func (dst *DockerClusterTemplateList) ConvertFrom(srcRaw conversion.Hub) error {
 func (src *DockerMachine) ConvertTo(dstRaw conversion.Hub) error {
 	dst := dstRaw.(*infrav1.DockerMachine)
 
-	if err := Convert_v1alpha4_DockerMachine_To_v1beta1_DockerMachine(src, dst, nil); err != nil {
-		return err
-	}
-
-	// Manually restore data.
-	restored := &infrav1.DockerMachine{}
-	if ok, err := utilconversion.UnmarshalData(src, restored); err != nil || !ok {
-		return err
-	}
-
-	if restored.Spec.BootstrapTimeout != nil {
-		dst.Spec.BootstrapTimeout = restored.Spec.BootstrapTimeout
-	}
-
-	return nil
+	return Convert_v1alpha4_DockerMachine_To_v1beta1_DockerMachine(src, dst, nil)
 }
 
 func (dst *DockerMachine) ConvertFrom(srcRaw conversion.Hub) error {
 	src := srcRaw.(*infrav1.DockerMachine)
 
-	if err := Convert_v1beta1_DockerMachine_To_v1alpha4_DockerMachine(src, dst, nil); err != nil {
-		return err
-	}
-
-	if err := utilconversion.MarshalData(src, dst); err != nil {
-		return err
-	}
-
-	return nil
+	return Convert_v1beta1_DockerMachine_To_v1alpha4_DockerMachine(src, dst, nil)
 }
 
 func (src *DockerMachineList) ConvertTo(dstRaw conversion.Hub) error {
@@ -179,7 +157,6 @@ func (src *DockerMachineTemplate) ConvertTo(dstRaw conversion.Hub) error {
 	}
 
 	dst.Spec.Template.ObjectMeta = restored.Spec.Template.ObjectMeta
-	dst.Spec.Template.Spec.BootstrapTimeout = restored.Spec.Template.Spec.BootstrapTimeout
 
 	return nil
 }
@@ -224,7 +201,3 @@ func Convert_v1beta1_DockerMachineTemplateResource_To_v1alpha4_DockerMachineTemp
 func Convert_v1beta1_DockerLoadBalancer_To_v1alpha4_DockerLoadBalancer(in *infrav1.DockerLoadBalancer, out *DockerLoadBalancer, s apiconversion.Scope) error {
 	return autoConvert_v1beta1_DockerLoadBalancer_To_v1alpha4_DockerLoadBalancer(in, out, s)
 }
-
-func Convert_v1beta1_DockerMachineSpec_To_v1alpha4_DockerMachineSpec(in *infrav1.DockerMachineSpec, out *DockerMachineSpec, s apiconversion.Scope) error {
-	return autoConvert_v1beta1_DockerMachineSpec_To_v1alpha4_DockerMachineSpec(in, out, s)
-}
diff --git a/test/infrastructure/docker/api/v1alpha4/zz_generated.conversion.go b/test/infrastructure/docker/api/v1alpha4/zz_generated.conversion.go
index 960ed662f..71075f02d 100644
--- a/test/infrastructure/docker/api/v1alpha4/zz_generated.conversion.go
+++ b/test/infrastructure/docker/api/v1alpha4/zz_generated.conversion.go
@@ -153,6 +153,11 @@ func RegisterConversions(s *runtime.Scheme) error {
 	}); err != nil {
 		return err
 	}
+	if err := s.AddGeneratedConversionFunc((*v1beta1.DockerMachineSpec)(nil), (*DockerMachineSpec)(nil), func(a, b interface{}, scope conversion.Scope) error {
+		return Convert_v1beta1_DockerMachineSpec_To_v1alpha4_DockerMachineSpec(a.(*v1beta1.DockerMachineSpec), b.(*DockerMachineSpec), scope)
+	}); err != nil {
+		return err
+	}
 	if err := s.AddGeneratedConversionFunc((*DockerMachineStatus)(nil), (*v1beta1.DockerMachineStatus)(nil), func(a, b interface{}, scope conversion.Scope) error {
 		return Convert_v1alpha4_DockerMachineStatus_To_v1beta1_DockerMachineStatus(a.(*DockerMachineStatus), b.(*v1beta1.DockerMachineStatus), scope)
 	}); err != nil {
@@ -228,11 +233,6 @@ func RegisterConversions(s *runtime.Scheme) error {
 	}); err != nil {
 		return err
 	}
-	if err := s.AddConversionFunc((*v1beta1.DockerMachineSpec)(nil), (*DockerMachineSpec)(nil), func(a, b interface{}, scope conversion.Scope) error {
-		return Convert_v1beta1_DockerMachineSpec_To_v1alpha4_DockerMachineSpec(a.(*v1beta1.DockerMachineSpec), b.(*DockerMachineSpec), scope)
-	}); err != nil {
-		return err
-	}
 	if err := s.AddConversionFunc((*v1beta1.DockerMachineTemplateResource)(nil), (*DockerMachineTemplateResource)(nil), func(a, b interface{}, scope conversion.Scope) error {
 		return Convert_v1beta1_DockerMachineTemplateResource_To_v1alpha4_DockerMachineTemplateResource(a.(*v1beta1.DockerMachineTemplateResource), b.(*DockerMachineTemplateResource), scope)
 	}); err != nil {
@@ -687,10 +687,14 @@ func autoConvert_v1beta1_DockerMachineSpec_To_v1alpha4_DockerMachineSpec(in *v1b
 	out.PreLoadImages = *(*[]string)(unsafe.Pointer(&in.PreLoadImages))
 	out.ExtraMounts = *(*[]Mount)(unsafe.Pointer(&in.ExtraMounts))
 	out.Bootstrapped = in.Bootstrapped
-	// WARNING: in.BootstrapTimeout requires manual conversion: does not exist in peer-type
 	return nil
 }
 
+// Convert_v1beta1_DockerMachineSpec_To_v1alpha4_DockerMachineSpec is an autogenerated conversion function.
+func Convert_v1beta1_DockerMachineSpec_To_v1alpha4_DockerMachineSpec(in *v1beta1.DockerMachineSpec, out *DockerMachineSpec, s conversion.Scope) error {
+	return autoConvert_v1beta1_DockerMachineSpec_To_v1alpha4_DockerMachineSpec(in, out, s)
+}
+
 func autoConvert_v1alpha4_DockerMachineStatus_To_v1beta1_DockerMachineStatus(in *DockerMachineStatus, out *v1beta1.DockerMachineStatus, s conversion.Scope) error {
 	out.Ready = in.Ready
 	out.LoadBalancerConfigured = in.LoadBalancerConfigured
diff --git a/test/infrastructure/docker/api/v1beta1/dockermachine_types.go b/test/infrastructure/docker/api/v1beta1/dockermachine_types.go
index 59b56e3bd..7fbba3276 100644
--- a/test/infrastructure/docker/api/v1beta1/dockermachine_types.go
+++ b/test/infrastructure/docker/api/v1beta1/dockermachine_types.go
@@ -56,11 +56,6 @@ type DockerMachineSpec struct {
 	// When removing also remove from staticcheck exclude-rules for SA1019 in golangci.yml.
 	// +optional
 	Bootstrapped bool `json:"bootstrapped,omitempty"`
-
-	// BootstrapTimeout is the total amount of time to wait for the machine to bootstrap before timing out.
-	// The default value is 3m.
-	// +optional
-	BootstrapTimeout *metav1.Duration `json:"bootstrapTimeout,omitempty"`
 }
 
 // Mount specifies a host volume to mount into a container.
diff --git a/test/infrastructure/docker/api/v1beta1/zz_generated.deepcopy.go b/test/infrastructure/docker/api/v1beta1/zz_generated.deepcopy.go
index 0b6a6efa9..6ae7a8e47 100644
--- a/test/infrastructure/docker/api/v1beta1/zz_generated.deepcopy.go
+++ b/test/infrastructure/docker/api/v1beta1/zz_generated.deepcopy.go
@@ -22,7 +22,6 @@ package v1beta1
 
 import (
 	"k8s.io/api/core/v1"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
 	apiv1beta1 "sigs.k8s.io/cluster-api/api/v1beta1"
 )
@@ -343,11 +342,6 @@ func (in *DockerMachineSpec) DeepCopyInto(out *DockerMachineSpec) {
 		*out = make([]Mount, len(*in))
 		copy(*out, *in)
 	}
-	if in.BootstrapTimeout != nil {
-		in, out := &in.BootstrapTimeout, &out.BootstrapTimeout
-		*out = new(metav1.Duration)
-		**out = **in
-	}
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DockerMachineSpec.
diff --git a/test/infrastructure/docker/config/crd/bases/infrastructure.cluster.x-k8s.io_dockermachines.yaml b/test/infrastructure/docker/config/crd/bases/infrastructure.cluster.x-k8s.io_dockermachines.yaml
index 1b14e74b1..ab73711b2 100644
--- a/test/infrastructure/docker/config/crd/bases/infrastructure.cluster.x-k8s.io_dockermachines.yaml
+++ b/test/infrastructure/docker/config/crd/bases/infrastructure.cluster.x-k8s.io_dockermachines.yaml
@@ -205,11 +205,6 @@ spec:
           spec:
             description: DockerMachineSpec defines the desired state of DockerMachine.
             properties:
-              bootstrapTimeout:
-                description: BootstrapTimeout is the total amount of time to wait
-                  for the machine to bootstrap before timing out. The default value
-                  is 3m.
-                type: string
               bootstrapped:
                 description: "Bootstrapped is true when the kubeadm bootstrapping
                   has been run against this machine \n Deprecated: This field will
diff --git a/test/infrastructure/docker/config/crd/bases/infrastructure.cluster.x-k8s.io_dockermachinetemplates.yaml b/test/infrastructure/docker/config/crd/bases/infrastructure.cluster.x-k8s.io_dockermachinetemplates.yaml
index 2e6613f0e..29ad60adf 100644
--- a/test/infrastructure/docker/config/crd/bases/infrastructure.cluster.x-k8s.io_dockermachinetemplates.yaml
+++ b/test/infrastructure/docker/config/crd/bases/infrastructure.cluster.x-k8s.io_dockermachinetemplates.yaml
@@ -159,11 +159,6 @@ spec:
                     description: Spec is the specification of the desired behavior
                       of the machine.
                     properties:
-                      bootstrapTimeout:
-                        description: BootstrapTimeout is the total amount of time
-                          to wait for the machine to bootstrap before timing out.
-                          The default value is 3m.
-                        type: string
                       bootstrapped:
                         description: "Bootstrapped is true when the kubeadm bootstrapping
                           has been run against this machine \n Deprecated: This field
diff --git a/test/infrastructure/docker/internal/controllers/dockermachine_controller.go b/test/infrastructure/docker/internal/controllers/dockermachine_controller.go
index 8b517275a..f2bfad8ab 100644
--- a/test/infrastructure/docker/internal/controllers/dockermachine_controller.go
+++ b/test/infrastructure/docker/internal/controllers/dockermachine_controller.go
@@ -25,7 +25,6 @@ import (
 	"github.com/pkg/errors"
 	corev1 "k8s.io/api/core/v1"
 	apierrors "k8s.io/apimachinery/pkg/api/errors"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/types"
 	"k8s.io/klog/v2"
 	ctrl "sigs.k8s.io/controller-runtime"
@@ -343,13 +342,7 @@ func (r *DockerMachineReconciler) reconcileNormal(ctx context.Context, cluster *
 
 	// if the machine isn't bootstrapped, only then run bootstrap scripts
 	if !dockerMachine.Spec.Bootstrapped {
-		var bootstrapTimeout metav1.Duration
-		if dockerMachine.Spec.BootstrapTimeout != nil {
-			bootstrapTimeout = *dockerMachine.Spec.BootstrapTimeout
-		} else {
-			bootstrapTimeout = metav1.Duration{Duration: 3 * time.Minute}
-		}
-		timeoutCtx, cancel := context.WithTimeout(ctx, bootstrapTimeout.Duration)
+		timeoutCtx, cancel := context.WithTimeout(ctx, 3*time.Minute)
 		defer cancel()
 
 		// Check for bootstrap success
diff --git a/test/infrastructure/docker/internal/docker/machine.go b/test/infrastructure/docker/internal/docker/machine.go
index daa98e4be..96c9df5ed 100644
--- a/test/infrastructure/docker/internal/docker/machine.go
+++ b/test/infrastructure/docker/internal/docker/machine.go
@@ -441,7 +441,7 @@ func (m *Machine) SetNodeProviderID(ctx context.Context, c client.Client) error
 	node.Spec.ProviderID = m.ProviderID()
 
 	if err = patchHelper.Patch(ctx, node); err != nil {
-		return errors.Wrap(err, "failed to set providerID")
+		return errors.Wrap(err, "failed update providerID")
 	}
 
 	return nil
@@ -508,7 +508,10 @@ func (m *Machine) CloudProviderNodePatch(ctx context.Context, c client.Client, d
 		}
 	}
 
-	return patchHelper.Patch(ctx, node)
+	if err = patchHelper.Patch(ctx, node); err != nil {
+		return errors.Wrap(err, "failed to patch node")
+	}
+	return nil
 }
 
 func (m *Machine) getDockerNode(ctx context.Context) (*types.Node, error) {
diff --git a/test/infrastructure/inmemory/internal/controllers/inmemorycluster_controller.go b/test/infrastructure/inmemory/internal/controllers/inmemorycluster_controller.go
index aae65769c..62364e4ed 100644
--- a/test/infrastructure/inmemory/internal/controllers/inmemorycluster_controller.go
+++ b/test/infrastructure/inmemory/internal/controllers/inmemorycluster_controller.go
@@ -23,7 +23,6 @@ import (
 
 	"github.com/pkg/errors"
 	apierrors "k8s.io/apimachinery/pkg/api/errors"
-	kerrors "k8s.io/apimachinery/pkg/util/errors"
 	"k8s.io/klog/v2"
 	ctrl "sigs.k8s.io/controller-runtime"
 	"sigs.k8s.io/controller-runtime/pkg/builder"
@@ -100,7 +99,10 @@ func (r *InMemoryClusterReconciler) Reconcile(ctx context.Context, req ctrl.Requ
 	// Always attempt to Patch the InMemoryCluster object and status after each reconciliation.
 	defer func() {
 		if err := patchHelper.Patch(ctx, inMemoryCluster); err != nil {
-			rerr = kerrors.NewAggregate([]error{rerr, err})
+			log.Error(err, "failed to patch InMemoryCluster")
+			if rerr == nil {
+				rerr = err
+			}
 		}
 	}()
 
diff --git a/test/infrastructure/inmemory/internal/controllers/inmemorymachine_controller.go b/test/infrastructure/inmemory/internal/controllers/inmemorymachine_controller.go
index 778097d3a..593e7737b 100644
--- a/test/infrastructure/inmemory/internal/controllers/inmemorymachine_controller.go
+++ b/test/infrastructure/inmemory/internal/controllers/inmemorymachine_controller.go
@@ -75,6 +75,8 @@ type InMemoryMachineReconciler struct {
 
 // Reconcile handles InMemoryMachine events.
 func (r *InMemoryMachineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl.Result, rerr error) {
+	log := ctrl.LoggerFrom(ctx)
+
 	// Fetch the InMemoryMachine instance
 	inMemoryMachine := &infrav1.InMemoryMachine{}
 	if err := r.Client.Get(ctx, req.NamespacedName, inMemoryMachine); err != nil {
@@ -160,7 +162,10 @@ func (r *InMemoryMachineReconciler) Reconcile(ctx context.Context, req ctrl.Requ
 			conditions.WithStepCounterIf(inMemoryMachine.ObjectMeta.DeletionTimestamp.IsZero() && inMemoryMachine.Spec.ProviderID == nil),
 		)
 		if err := patchHelper.Patch(ctx, inMemoryMachine, patch.WithOwnedConditions{Conditions: inMemoryMachineConditions}); err != nil {
-			rerr = kerrors.NewAggregate([]error{rerr, err})
+			log.Error(err, "failed to patch InMemoryMachine")
+			if rerr == nil {
+				rerr = err
+			}
 		}
 	}()
 
diff --git a/test/infrastructure/inmemory/internal/server/api/const.go b/test/infrastructure/inmemory/internal/server/api/const.go
index 32ea331fd..98a18e97b 100644
--- a/test/infrastructure/inmemory/internal/server/api/const.go
+++ b/test/infrastructure/inmemory/internal/server/api/const.go
@@ -30,6 +30,46 @@ var (
 	corev1APIResourceList = &metav1.APIResourceList{
 		GroupVersion: "v1",
 		APIResources: []metav1.APIResource{
+			{
+				Name:         "configmaps",
+				SingularName: "",
+				Namespaced:   true,
+				Kind:         "ConfigMap",
+				Verbs: []string{
+					"create",
+					"delete",
+					"deletecollection",
+					"get",
+					"list",
+					"patch",
+					"update",
+					"watch",
+				},
+				ShortNames: []string{
+					"cm",
+				},
+				StorageVersionHash: "",
+			},
+			{
+				Name:         "endpoints",
+				SingularName: "",
+				Namespaced:   true,
+				Kind:         "Endpoints",
+				Verbs: []string{
+					"create",
+					"delete",
+					"deletecollection",
+					"get",
+					"list",
+					"patch",
+					"update",
+					"watch",
+				},
+				ShortNames: []string{
+					"ep",
+				},
+				StorageVersionHash: "",
+			},
 			{
 				Name:         "nodes",
 				SingularName: "",
@@ -66,10 +106,10 @@ var (
 				StorageVersionHash: "",
 			},
 			{
-				Name:         "configmaps",
+				Name:         "secrets",
 				SingularName: "",
 				Namespaced:   true,
-				Kind:         "ConfigMap",
+				Kind:         "Secret",
 				Verbs: []string{
 					"create",
 					"delete",
@@ -80,16 +120,13 @@ var (
 					"update",
 					"watch",
 				},
-				ShortNames: []string{
-					"cm",
-				},
 				StorageVersionHash: "",
 			},
 			{
-				Name:         "secrets",
+				Name:         "services",
 				SingularName: "",
 				Namespaced:   true,
-				Kind:         "Secret",
+				Kind:         "Service",
 				Verbs: []string{
 					"create",
 					"delete",
@@ -100,6 +137,9 @@ var (
 					"update",
 					"watch",
 				},
+				ShortNames: []string{
+					"svc",
+				},
 				StorageVersionHash: "",
 			},
 		},
diff --git a/test/infrastructure/inmemory/internal/server/api/handler.go b/test/infrastructure/inmemory/internal/server/api/handler.go
index efac8b63c..63a07e770 100644
--- a/test/infrastructure/inmemory/internal/server/api/handler.go
+++ b/test/infrastructure/inmemory/internal/server/api/handler.go
@@ -18,6 +18,7 @@ package api
 
 import (
 	"context"
+	"errors"
 	"fmt"
 	"io"
 	"net"
@@ -29,7 +30,6 @@ import (
 
 	"github.com/emicklei/go-restful/v3"
 	"github.com/go-logr/logr"
-	"github.com/pkg/errors"
 	apierrors "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -274,7 +274,11 @@ func (h *apiServerHandler) apiV1Create(req *restful.Request, resp *restful.Respo
 	// TODO: consider check vs enforce for namespace on the object - namespace on the request path
 	obj.SetNamespace(req.PathParameter("namespace"))
 	if err := cloudClient.Create(ctx, obj); err != nil {
-		_ = resp.WriteErrorString(http.StatusInternalServerError, err.Error())
+		if status, ok := err.(apierrors.APIStatus); ok || errors.As(err, &status) {
+			_ = resp.WriteHeaderAndEntity(int(status.Status().Code), status)
+			return
+		}
+		_ = resp.WriteHeaderAndEntity(http.StatusInternalServerError, err.Error())
 		return
 	}
 	if err := resp.WriteEntity(obj); err != nil {
@@ -324,7 +328,11 @@ func (h *apiServerHandler) apiV1List(req *restful.Request, resp *restful.Respons
 	}
 
 	if err := cloudClient.List(ctx, list, listOpts...); err != nil {
-		_ = resp.WriteErrorString(http.StatusInternalServerError, err.Error())
+		if status, ok := err.(apierrors.APIStatus); ok || errors.As(err, &status) {
+			_ = resp.WriteHeaderAndEntity(int(status.Status().Code), status)
+			return
+		}
+		_ = resp.WriteHeaderAndEntity(http.StatusInternalServerError, err.Error())
 		return
 	}
 	if err := resp.WriteEntity(list); err != nil {
@@ -439,7 +447,11 @@ func (h *apiServerHandler) apiV1Update(req *restful.Request, resp *restful.Respo
 	// TODO: consider check vs enforce for namespace on the object - namespace on the request path
 	obj.SetNamespace(req.PathParameter("namespace"))
 	if err := cloudClient.Update(ctx, obj); err != nil {
-		_ = resp.WriteErrorString(http.StatusInternalServerError, err.Error())
+		if status, ok := err.(apierrors.APIStatus); ok || errors.As(err, &status) {
+			_ = resp.WriteHeaderAndEntity(int(status.Status().Code), status)
+			return
+		}
+		_ = resp.WriteHeaderAndEntity(http.StatusInternalServerError, err.Error())
 		return
 	}
 	if err := resp.WriteEntity(obj); err != nil {
@@ -488,7 +500,11 @@ func (h *apiServerHandler) apiV1Patch(req *restful.Request, resp *restful.Respon
 		return
 	}
 	if err := cloudClient.Patch(ctx, obj, patch); err != nil {
-		_ = resp.WriteErrorString(http.StatusInternalServerError, err.Error())
+		if status, ok := err.(apierrors.APIStatus); ok || errors.As(err, &status) {
+			_ = resp.WriteHeaderAndEntity(int(status.Status().Code), status)
+			return
+		}
+		_ = resp.WriteHeaderAndEntity(http.StatusInternalServerError, err.Error())
 		return
 	}
 	if err := resp.WriteEntity(obj); err != nil {
@@ -525,7 +541,11 @@ func (h *apiServerHandler) apiV1Delete(req *restful.Request, resp *restful.Respo
 	obj.SetNamespace(req.PathParameter("namespace"))
 
 	if err := cloudClient.Delete(ctx, obj); err != nil {
-		_ = resp.WriteErrorString(http.StatusInternalServerError, err.Error())
+		if status, ok := err.(apierrors.APIStatus); ok || errors.As(err, &status) {
+			_ = resp.WriteHeaderAndEntity(int(status.Status().Code), status)
+			return
+		}
+		_ = resp.WriteHeaderAndEntity(http.StatusInternalServerError, err.Error())
 		return
 	}
 }
@@ -613,11 +633,11 @@ func (h *apiServerHandler) healthz(_ *restful.Request, resp *restful.Response) {
 func requestToGVK(req *restful.Request) (*schema.GroupVersionKind, error) {
 	resourceList := getAPIResourceList(req)
 	if resourceList == nil {
-		return nil, errors.Errorf("no APIResourceList defined for %s", req.PathParameters())
+		return nil, fmt.Errorf("no APIResourceList defined for %s", req.PathParameters())
 	}
 	gv, err := schema.ParseGroupVersion(resourceList.GroupVersion)
 	if err != nil {
-		return nil, errors.Errorf("invalid group version in APIResourceList: %s", resourceList.GroupVersion)
+		return nil, fmt.Errorf("invalid group version in APIResourceList: %s", resourceList.GroupVersion)
 	}
 
 	resource := req.PathParameter("resource")
@@ -627,7 +647,7 @@ func requestToGVK(req *restful.Request) (*schema.GroupVersionKind, error) {
 			return &gvk, nil
 		}
 	}
-	return nil, errors.Errorf("Resource %s is not defined in the APIResourceList for %s", resource, req.PathParameters())
+	return nil, fmt.Errorf("resource %s is not defined in the APIResourceList for %s", resource, req.PathParameters())
 }
 
 func getAPIResourceList(req *restful.Request) *metav1.APIResourceList {