Repository: kubernetes-sigs/cluster-api
Pull request #9045 (2023-07-21 15:46:07)

🌱 Remove reliance on controller-runtime scheme builder
<!-- please add an icon to the title of this PR (see https://sigs.k8s.io/cluster-api/CONTRIBUTING.md#contributing-a-patch), and delete this line and similar ones -->
<!-- the icon will be either ⚠️ (:warning:, major or breaking changes), ✨ (:sparkles:, feature additions), 🐛 (:bug:, patch and bugfixes), 📖 (:book:, documentation or proposals), or 🌱 (:seedling:, minor or other) -->

**What this PR does / why we need it**:

To work towards #9011, we need to remove the reliance on controller-runtime from the API packages.
This updates the scheme building methods in the core v1beta1 API group to rely only on API machinery types and not controller runtime types.

**Which issue(s) this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close the issue(s) when PR gets merged)*:
Fixes #


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

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

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

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

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

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["neolit123"]} -->
JoelSpeed: I think you're right, i don't think it does need to be exported. Currently we are using it in the test builders so that we can access the `AddToScheme` function, but that can be accessed directly since we alias it at the package level

Patch:
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 5c24e726a..18cf59612 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -12,13 +12,11 @@ updates:
   labels:
     - "area/ci"
     - "ok-to-test"
-
-# Main Go module
+# Go
 - package-ecosystem: "gomod"
   directory: "/"
   schedule:
     interval: "weekly"
-    day: "monday"
   ignore:
   # Ignore controller-runtime as its upgraded manually.
   - dependency-name: "sigs.k8s.io/controller-runtime"
@@ -27,55 +25,6 @@ updates:
   - dependency-name: "k8s.io/*"
   - dependency-name: "go.etcd.io/*"
   - dependency-name: "google.golang.org/grpc"
-  # Bumping the kustomize API independently can break compatibility with client-go as they share k8s.io/kube-openapi
-  # as a dependency.
-  - dependency-name: "sigs.k8s.io/kustomize/api"
-  commit-message:
-    prefix: ":seedling:"
-  labels:
-    - "area/dependency"
-    - "ok-to-test"
-
-  # Test Go module
-- package-ecosystem: "gomod"
-  directory: "/test"
-  schedule:
-    interval: "weekly"
-    day: "tuesday"
-  ignore:
-    # Ignore controller-runtime as its upgraded manually.
-    - dependency-name: "sigs.k8s.io/controller-runtime"
-    # Ignore k8s and its transitives modules as they are upgraded manually
-    # together with controller-runtime.
-    - dependency-name: "k8s.io/*"
-    - dependency-name: "go.etcd.io/*"
-    - dependency-name: "google.golang.org/grpc"
-    # Bumping the kustomize API independently can break compatibility with client-go as they share k8s.io/kube-openapi
-    # as a dependency.
-    - dependency-name: "sigs.k8s.io/kustomize/api"
-  commit-message:
-    prefix: ":seedling:"
-  labels:
-    - "area/dependency"
-    - "ok-to-test"
-
-  # Hack/tools Go module
-- package-ecosystem: "gomod"
-  directory: "/hack/tools"
-  schedule:
-    interval: "weekly"
-    day: "wednesday"
-  ignore:
-    # Ignore controller-runtime as its upgraded manually.
-    - dependency-name: "sigs.k8s.io/controller-runtime"
-    # Ignore k8s and its transitives modules as they are upgraded manually
-    # together with controller-runtime.
-    - dependency-name: "k8s.io/*"
-    - dependency-name: "go.etcd.io/*"
-    - dependency-name: "google.golang.org/grpc"
-    # Bumping the kustomize API independently can break compatibility with client-go as they share k8s.io/kube-openapi
-    # as a dependency.
-    - dependency-name: "sigs.k8s.io/kustomize/api"
   commit-message:
     prefix: ":seedling:"
   labels:
diff --git a/Makefile b/Makefile
index 521cb675d..8f9d3a47c 100644
--- a/Makefile
+++ b/Makefile
@@ -23,7 +23,7 @@ SHELL:=/usr/bin/env bash
 #
 # Go.
 #
-GO_VERSION ?= 1.20.6
+GO_VERSION ?= 1.20.4
 GO_CONTAINER_IMAGE ?= docker.io/library/golang:$(GO_VERSION)
 
 # Use GOPROXY environment variable if set
diff --git a/Tiltfile b/Tiltfile
index b1436887b..b0836e203 100644
--- a/Tiltfile
+++ b/Tiltfile
@@ -184,7 +184,7 @@ def load_provider_tiltfiles():
 
 tilt_helper_dockerfile_header = """
 # Tilt image
-FROM golang:1.20.6 as tilt-helper
+FROM golang:1.20.4 as tilt-helper
 # Install delve. Note this should be kept in step with the Go release minor version.
 RUN go install github.com/go-delve/delve/cmd/dlv@v1.20
 # Support live reloading with Tilt
@@ -195,7 +195,7 @@ RUN wget --output-document /restart.sh --quiet https://raw.githubusercontent.com
 """
 
 tilt_dockerfile_header = """
-FROM golang:1.20.6 as tilt
+FROM gcr.io/distroless/base:debug as tilt
 WORKDIR /
 COPY --from=tilt-helper /process.txt .
 COPY --from=tilt-helper /start.sh .
diff --git a/api/v1beta1/cluster_types.go b/api/v1beta1/cluster_types.go
index ffc1f0506..108e0bfad 100644
--- a/api/v1beta1/cluster_types.go
+++ b/api/v1beta1/cluster_types.go
@@ -599,7 +599,7 @@ type ClusterList struct {
 }
 
 func init() {
-	SchemeBuilder.Register(&Cluster{}, &ClusterList{})
+	objectTypes = append(objectTypes, &Cluster{}, &ClusterList{})
 }
 
 // FailureDomains is a slice of FailureDomains.
diff --git a/api/v1beta1/clusterclass_types.go b/api/v1beta1/clusterclass_types.go
index 326805057..f699df581 100644
--- a/api/v1beta1/clusterclass_types.go
+++ b/api/v1beta1/clusterclass_types.go
@@ -706,5 +706,5 @@ type ClusterClassList struct {
 }
 
 func init() {
-	SchemeBuilder.Register(&ClusterClass{}, &ClusterClassList{})
+	objectTypes = append(objectTypes, &ClusterClass{}, &ClusterClassList{})
 }
diff --git a/api/v1beta1/common_types.go b/api/v1beta1/common_types.go
index 017f7f675..81c95efce 100644
--- a/api/v1beta1/common_types.go
+++ b/api/v1beta1/common_types.go
@@ -18,10 +18,7 @@ package v1beta1
 
 import (
 	corev1 "k8s.io/api/core/v1"
-	apivalidation "k8s.io/apimachinery/pkg/api/validation"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	metav1validation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
-	"k8s.io/apimachinery/pkg/util/validation/field"
 )
 
 const (
@@ -304,16 +301,3 @@ type ObjectMeta struct {
 	// +optional
 	Annotations map[string]string `json:"annotations,omitempty"`
 }
-
-// Validate validates the labels and annotations in ObjectMeta.
-func (metadata *ObjectMeta) Validate(parent *field.Path) field.ErrorList {
-	allErrs := metav1validation.ValidateLabels(
-		metadata.Labels,
-		parent.Child("labels"),
-	)
-	allErrs = append(allErrs, apivalidation.ValidateAnnotations(
-		metadata.Annotations,
-		parent.Child("annotations"),
-	)...)
-	return allErrs
-}
diff --git a/api/v1beta1/groupversion_info.go b/api/v1beta1/groupversion_info.go
index 2d118b8de..95968743c 100644
--- a/api/v1beta1/groupversion_info.go
+++ b/api/v1beta1/groupversion_info.go
@@ -20,17 +20,26 @@ limitations under the License.
 package v1beta1
 
 import (
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/runtime/schema"
-	"sigs.k8s.io/controller-runtime/pkg/scheme"
 )
 
 var (
 	// GroupVersion is group version used to register these objects.
 	GroupVersion = schema.GroupVersion{Group: "cluster.x-k8s.io", Version: "v1beta1"}
 
-	// SchemeBuilder is used to add go types to the GroupVersionKind scheme.
-	SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
+	// schemeBuilder is used to add go types to the GroupVersionKind scheme.
+	schemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
 
 	// AddToScheme adds the types in this group-version to the given scheme.
-	AddToScheme = SchemeBuilder.AddToScheme
+	AddToScheme = schemeBuilder.AddToScheme
+
+	objectTypes = []runtime.Object{}
 )
+
+func addKnownTypes(scheme *runtime.Scheme) error {
+	scheme.AddKnownTypes(GroupVersion, objectTypes...)
+	metav1.AddToGroupVersion(scheme, GroupVersion)
+	return nil
+}
diff --git a/api/v1beta1/machine_types.go b/api/v1beta1/machine_types.go
index ee9597306..e6e0fa8fe 100644
--- a/api/v1beta1/machine_types.go
+++ b/api/v1beta1/machine_types.go
@@ -307,5 +307,5 @@ type MachineList struct {
 }
 
 func init() {
-	SchemeBuilder.Register(&Machine{}, &MachineList{})
+	objectTypes = append(objectTypes, &Machine{}, &MachineList{})
 }
diff --git a/api/v1beta1/machinedeployment_types.go b/api/v1beta1/machinedeployment_types.go
index 3bd0109fb..88d43370b 100644
--- a/api/v1beta1/machinedeployment_types.go
+++ b/api/v1beta1/machinedeployment_types.go
@@ -337,7 +337,7 @@ type MachineDeploymentList struct {
 }
 
 func init() {
-	SchemeBuilder.Register(&MachineDeployment{}, &MachineDeploymentList{})
+	objectTypes = append(objectTypes, &MachineDeployment{}, &MachineDeploymentList{})
 }
 
 // GetConditions returns the set of conditions for the machinedeployment.
diff --git a/api/v1beta1/machinedeployment_webhook.go b/api/v1beta1/machinedeployment_webhook.go
index c749f8c50..472c01afd 100644
--- a/api/v1beta1/machinedeployment_webhook.go
+++ b/api/v1beta1/machinedeployment_webhook.go
@@ -266,9 +266,6 @@ func (m *MachineDeployment) validate(old *MachineDeployment) error {
 		}
 	}
 
-	// Validate the metadata of the template.
-	allErrs = append(allErrs, m.Spec.Template.ObjectMeta.Validate(specPath.Child("template", "metadata"))...)
-
 	if len(allErrs) == 0 {
 		return nil
 	}
diff --git a/api/v1beta1/machinehealthcheck_types.go b/api/v1beta1/machinehealthcheck_types.go
index a8d120351..5e0dffc71 100644
--- a/api/v1beta1/machinehealthcheck_types.go
+++ b/api/v1beta1/machinehealthcheck_types.go
@@ -169,5 +169,5 @@ type MachineHealthCheckList struct {
 }
 
 func init() {
-	SchemeBuilder.Register(&MachineHealthCheck{}, &MachineHealthCheckList{})
+	objectTypes = append(objectTypes, &MachineHealthCheck{}, &MachineHealthCheckList{})
 }
diff --git a/api/v1beta1/machineset_types.go b/api/v1beta1/machineset_types.go
index c888df5c4..843b343a4 100644
--- a/api/v1beta1/machineset_types.go
+++ b/api/v1beta1/machineset_types.go
@@ -240,5 +240,5 @@ type MachineSetList struct {
 }
 
 func init() {
-	SchemeBuilder.Register(&MachineSet{}, &MachineSetList{})
+	objectTypes = append(objectTypes, &MachineSet{}, &MachineSetList{})
 }
diff --git a/api/v1beta1/machineset_webhook.go b/api/v1beta1/machineset_webhook.go
index d9ea53dc3..560ef68af 100644
--- a/api/v1beta1/machineset_webhook.go
+++ b/api/v1beta1/machineset_webhook.go
@@ -152,9 +152,6 @@ func (m *MachineSet) validate(old *MachineSet) error {
 		}
 	}
 
-	// Validate the metadata of the template.
-	allErrs = append(allErrs, m.Spec.Template.ObjectMeta.Validate(specPath.Child("template", "metadata"))...)
-
 	if len(allErrs) == 0 {
 		return nil
 	}
diff --git a/bootstrap/kubeadm/api/v1beta1/kubeadmconfigtemplate_webhook.go b/bootstrap/kubeadm/api/v1beta1/kubeadmconfigtemplate_webhook.go
index cfab2f38a..430923b14 100644
--- a/bootstrap/kubeadm/api/v1beta1/kubeadmconfigtemplate_webhook.go
+++ b/bootstrap/kubeadm/api/v1beta1/kubeadmconfigtemplate_webhook.go
@@ -63,8 +63,6 @@ func (r *KubeadmConfigTemplateSpec) validate(name string) error {
 	var allErrs field.ErrorList
 
 	allErrs = append(allErrs, r.Template.Spec.Validate(field.NewPath("spec", "template", "spec"))...)
-	// Validate the metadata of the template.
-	allErrs = append(allErrs, r.Template.ObjectMeta.Validate(field.NewPath("spec", "template", "metadata"))...)
 
 	if len(allErrs) == 0 {
 		return nil
diff --git a/cmd/clusterctl/client/config/providers_client.go b/cmd/clusterctl/client/config/providers_client.go
index a5b3303f3..0deb14a0e 100644
--- a/cmd/clusterctl/client/config/providers_client.go
+++ b/cmd/clusterctl/client/config/providers_client.go
@@ -67,21 +67,19 @@ const (
 
 // Bootstrap providers.
 const (
-	KubeadmBootstrapProviderName           = "kubeadm"
-	TalosBootstrapProviderName             = "talos"
-	MicroK8sBootstrapProviderName          = "microk8s"
-	OracleCloudNativeBootstrapProviderName = "ocne"
-	KubeKeyK3sBootstrapProviderName        = "kubekey-k3s"
+	KubeadmBootstrapProviderName    = "kubeadm"
+	TalosBootstrapProviderName      = "talos"
+	MicroK8sBootstrapProviderName   = "microk8s"
+	KubeKeyK3sBootstrapProviderName = "kubekey-k3s"
 )
 
 // ControlPlane providers.
 const (
-	KubeadmControlPlaneProviderName           = "kubeadm"
-	TalosControlPlaneProviderName             = "talos"
-	MicroK8sControlPlaneProviderName          = "microk8s"
-	NestedControlPlaneProviderName            = "nested"
-	OracleCloudNativeControlPlaneProviderName = "ocne"
-	KubeKeyK3sControlPlaneProviderName        = "kubekey-k3s"
+	KubeadmControlPlaneProviderName    = "kubeadm"
+	TalosControlPlaneProviderName      = "talos"
+	MicroK8sControlPlaneProviderName   = "microk8s"
+	NestedControlPlaneProviderName     = "nested"
+	KubeKeyK3sControlPlaneProviderName = "kubekey-k3s"
 )
 
 // Add-on providers.
@@ -289,11 +287,6 @@ func (p *providersClient) defaults() []Provider {
 			url:          "https://github.com/canonical/cluster-api-bootstrap-provider-microk8s/releases/latest/bootstrap-components.yaml",
 			providerType: clusterctlv1.BootstrapProviderType,
 		},
-		&provider{
-			name:         OracleCloudNativeBootstrapProviderName,
-			url:          "https://github.com/verrazzano/cluster-api-provider-ocne/releases/latest/bootstrap-components.yaml",
-			providerType: clusterctlv1.BootstrapProviderType,
-		},
 
 		// ControlPlane providers
 		&provider{
@@ -321,11 +314,6 @@ func (p *providersClient) defaults() []Provider {
 			url:          "https://github.com/kubernetes-sigs/cluster-api-provider-nested/releases/latest/control-plane-components.yaml",
 			providerType: clusterctlv1.ControlPlaneProviderType,
 		},
-		&provider{
-			name:         OracleCloudNativeControlPlaneProviderName,
-			url:          "https://github.com/verrazzano/cluster-api-provider-ocne/releases/latest/control-plane-components.yaml",
-			providerType: clusterctlv1.ControlPlaneProviderType,
-		},
 
 		// Add-on providers
 		&provider{
diff --git a/controllers/remote/cluster_cache_tracker.go b/controllers/remote/cluster_cache_tracker.go
index 9bd40ceab..191e09db8 100644
--- a/controllers/remote/cluster_cache_tracker.go
+++ b/controllers/remote/cluster_cache_tracker.go
@@ -457,13 +457,6 @@ func (t *ClusterCacheTracker) createClient(ctx context.Context, config *rest.Con
 		return nil, nil, nil, fmt.Errorf("failed waiting for cache for remote cluster %v to sync: %w", cluster, cacheCtx.Err())
 	}
 
-	// Wrap the cached client with a client that sets timeouts on all Get and List calls
-	// If we don't set timeouts here Get and List calls can get stuck if they lazily create a new informer
-	// and the informer than doesn't sync because the workload cluster apiserver is not reachable.
-	// An alternative would be to set timeouts in the contexts we pass into all Get and List calls.
-	// It should be reasonable to have Get and List calls timeout within the duration configured in the restConfig.
-	cachedClient = newClientWithTimeout(cachedClient, config.Timeout)
-
 	// Start cluster healthcheck!!!
 	go t.healthCheckCluster(cacheCtx, &healthCheckInput{
 		cluster:    cluster,
@@ -654,46 +647,12 @@ func (t *ClusterCacheTracker) healthCheckCluster(ctx context.Context, in *health
 	}
 
 	err := wait.PollUntilContextCancel(ctx, in.interval, true, runHealthCheckWithThreshold)
-	// An error returned implies the health check has failed a sufficient number of times for the cluster
-	// to be considered unhealthy or the cache was stopped and thus the cache context canceled (we pass the
-	// cache context into wait.PollUntilContextCancel).
-	// NB. Log all errors that occurred even if this error might just be from a cancel of the cache context
-	// when the cache is stopped. Logging an error in this case is not a problem and makes debugging easier.
-	if err != nil {
+	// An error returned implies the health check has failed a sufficient number of
+	// times for the cluster to be considered unhealthy
+	// NB. we are ignoring ErrWaitTimeout because this error happens when the channel is close, that in this case
+	// happens when the cache is explicitly stopped.
+	if err != nil && !wait.Interrupted(err) {
 		t.log.Error(err, "Error health checking cluster", "Cluster", klog.KRef(in.cluster.Namespace, in.cluster.Name))
+		t.deleteAccessor(ctx, in.cluster)
 	}
-	// Ensure in any case that the accessor is deleted (even if it is a no-op).
-	// NB. It is crucial to ensure the accessor was deleted, so it can be later recreated when the
-	// cluster is reachable again
-	t.deleteAccessor(ctx, in.cluster)
-}
-
-// newClientWithTimeout returns a new client which sets the specified timeout on all Get and List calls.
-// If we don't set timeouts here Get and List calls can get stuck if they lazily create a new informer
-// and the informer than doesn't sync because the workload cluster apiserver is not reachable.
-// An alternative would be to set timeouts in the contexts we pass into all Get and List calls.
-func newClientWithTimeout(client client.Client, timeout time.Duration) client.Client {
-	return clientWithTimeout{
-		Client:  client,
-		timeout: timeout,
-	}
-}
-
-type clientWithTimeout struct {
-	client.Client
-	timeout time.Duration
-}
-
-var _ client.Client = &clientWithTimeout{}
-
-func (c clientWithTimeout) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error {
-	ctx, cancel := context.WithTimeout(ctx, c.timeout)
-	defer cancel()
-	return c.Client.Get(ctx, key, obj, opts...)
-}
-
-func (c clientWithTimeout) List(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error {
-	ctx, cancel := context.WithTimeout(ctx, c.timeout)
-	defer cancel()
-	return c.Client.List(ctx, list, opts...)
 }
diff --git a/controlplane/kubeadm/api/v1beta1/kubeadm_control_plane_webhook.go b/controlplane/kubeadm/api/v1beta1/kubeadm_control_plane_webhook.go
index 9db8198d2..1120db1ce 100644
--- a/controlplane/kubeadm/api/v1beta1/kubeadm_control_plane_webhook.go
+++ b/controlplane/kubeadm/api/v1beta1/kubeadm_control_plane_webhook.go
@@ -330,9 +330,6 @@ func validateKubeadmControlPlaneSpec(s KubeadmControlPlaneSpec, namespace string
 		)
 	}
 
-	// Validate the metadata of the MachineTemplate
-	allErrs = append(allErrs, s.MachineTemplate.ObjectMeta.Validate(pathPrefix.Child("machineTemplate", "metadata"))...)
-
 	if !version.KubeSemver.MatchString(s.Version) {
 		allErrs = append(allErrs, field.Invalid(pathPrefix.Child("version"), s.Version, "must be a valid semantic version"))
 	}
diff --git a/controlplane/kubeadm/api/v1beta1/kubeadmcontrolplanetemplate_webhook.go b/controlplane/kubeadm/api/v1beta1/kubeadmcontrolplanetemplate_webhook.go
index 02fe7da10..6ff3c43e3 100644
--- a/controlplane/kubeadm/api/v1beta1/kubeadmcontrolplanetemplate_webhook.go
+++ b/controlplane/kubeadm/api/v1beta1/kubeadmcontrolplanetemplate_webhook.go
@@ -69,8 +69,6 @@ func (r *KubeadmControlPlaneTemplate) ValidateCreate() (admission.Warnings, erro
 	allErrs := validateKubeadmControlPlaneTemplateResourceSpec(spec, field.NewPath("spec", "template", "spec"))
 	allErrs = append(allErrs, validateClusterConfiguration(spec.KubeadmConfigSpec.ClusterConfiguration, nil, field.NewPath("spec", "template", "spec", "kubeadmConfigSpec", "clusterConfiguration"))...)
 	allErrs = append(allErrs, spec.KubeadmConfigSpec.Validate(field.NewPath("spec", "template", "spec", "kubeadmConfigSpec"))...)
-	// Validate the metadata of the KubeadmControlPlaneTemplateResource
-	allErrs = append(allErrs, r.Spec.Template.ObjectMeta.Validate(field.NewPath("spec", "template", "metadata"))...)
 	if len(allErrs) > 0 {
 		return nil, apierrors.NewInvalid(GroupVersion.WithKind("KubeadmControlPlaneTemplate").GroupKind(), r.Name, allErrs)
 	}
@@ -110,10 +108,5 @@ func validateKubeadmControlPlaneTemplateResourceSpec(s KubeadmControlPlaneTempla
 	allErrs = append(allErrs, validateRolloutBefore(s.RolloutBefore, pathPrefix.Child("rolloutBefore"))...)
 	allErrs = append(allErrs, validateRolloutStrategy(s.RolloutStrategy, nil, pathPrefix.Child("rolloutStrategy"))...)
 
-	if s.MachineTemplate != nil {
-		// Validate the metadata of the MachineTemplate
-		allErrs = append(allErrs, s.MachineTemplate.ObjectMeta.Validate(pathPrefix.Child("machineTemplate", "metadata"))...)
-	}
-
 	return allErrs
 }
diff --git a/controlplane/kubeadm/internal/controllers/controller.go b/controlplane/kubeadm/internal/controllers/controller.go
index 73c2ae7b9..3e6bc71af 100644
--- a/controlplane/kubeadm/internal/controllers/controller.go
+++ b/controlplane/kubeadm/internal/controllers/controller.go
@@ -222,23 +222,13 @@ func (r *KubeadmControlPlaneReconciler) Reconcile(ctx context.Context, req ctrl.
 			reterr = kerrors.NewAggregate([]error{reterr, err})
 		}
 
-		// Only requeue if there is no error, Requeue or RequeueAfter and the object does not have a deletion timestamp.
-		if reterr == nil && res.IsZero() && kcp.ObjectMeta.DeletionTimestamp.IsZero() {
-			// Make KCP requeue in case node status is not ready, so we can check for node status without waiting for a full
-			// resync (by default 10 minutes).
-			// The alternative solution would be to watch the control plane nodes in the Cluster - similar to how the
-			// MachineSet and MachineHealthCheck controllers watch the nodes under their control.
+		// TODO: remove this as soon as we have a proper remote cluster cache in place.
+		// Make KCP to requeue in case status is not ready, so we can check for node status without waiting for a full resync (by default 10 minutes).
+		// Only requeue if we are not going in exponential backoff due to error, or if we are not already re-queueing, or if the object has a deletion timestamp.
+		if reterr == nil && !res.Requeue && res.RequeueAfter <= 0 && kcp.ObjectMeta.DeletionTimestamp.IsZero() {
 			if !kcp.Status.Ready {
 				res = ctrl.Result{RequeueAfter: 20 * time.Second}
 			}
-
-			// Make KCP requeue if ControlPlaneComponentsHealthyCondition is false so we can check for control plane component
-			// status without waiting for a full resync (by default 10 minutes).
-			// Otherwise this condition can lead to a delay in provisioning MachineDeployments when MachineSet preflight checks are enabled.
-			// The alternative solution to this requeue would be watching the relevant pods inside each workload cluster which would be very expensive.
-			if conditions.IsFalse(kcp, controlplanev1.ControlPlaneComponentsHealthyCondition) {
-				res = ctrl.Result{RequeueAfter: 20 * time.Second}
-			}
 		}
 	}()
 
diff --git a/docs/book/src/SUMMARY.md b/docs/book/src/SUMMARY.md
index e5577a030..eb7304e6a 100644
--- a/docs/book/src/SUMMARY.md
+++ b/docs/book/src/SUMMARY.md
@@ -10,8 +10,7 @@
         - [Generating a Kubeconfig](./tasks/certs/generate-kubeconfig.md)
         - [Auto Rotate Certificates in KCP](./tasks/certs/auto-rotate-certificates-in-kcp.md)
     - [Bootstrap](./tasks/bootstrap/index.md)
-        - [Kubeadm based bootstrap](./tasks/bootstrap/kubeadm-bootstrap/index.md)
-            - [Kubelet configuration](./tasks/bootstrap/kubeadm-bootstrap/kubelet-config.md)
+        - [Kubeadm based bootstrap](./tasks/bootstrap/kubeadm-bootstrap.md)
         - [MicroK8s based bootstrap](./tasks/bootstrap/microk8s-bootstrap.md)
     - [Upgrading management and workload clusters](./tasks/upgrading-clusters.md)
     - [External etcd](./tasks/external-etcd.md)
diff --git a/docs/book/src/clusterctl/commands/move.md b/docs/book/src/clusterctl/commands/move.md
index 5c8f1ff1a..940766b32 100644
--- a/docs/book/src/clusterctl/commands/move.md
+++ b/docs/book/src/clusterctl/commands/move.md
@@ -24,8 +24,6 @@ clusterctl move --to-kubeconfig="path-to-target-kubeconfig.yaml"
 To move the Cluster API objects existing in the current namespace of the source management cluster; in case if you want
 to move the Cluster API objects defined in another namespace, you can use the `--namespace` flag.
 
-The discovery mechanism for determining the objects to be moved is in the [provider contract](../provider-contract.md#move)
-
 <aside class="note">
 
 <h1> Pause Reconciliation </h1>
diff --git a/docs/book/src/clusterctl/provider-contract.md b/docs/book/src/clusterctl/provider-contract.md
index 2f45e2931..af9bf6df0 100644
--- a/docs/book/src/clusterctl/provider-contract.md
+++ b/docs/book/src/clusterctl/provider-contract.md
@@ -251,12 +251,10 @@ providers.
 | CABPK         | cluster.x-k8s.io/provider=bootstrap-kubeadm           |
 | CABPM         | cluster.x-k8s.io/provider=bootstrap-microk8s          |
 | CABPKK3S      | cluster.x-k8s.io/provider=bootstrap-kubekey-k3s       |
-| CABPOCNE      | cluster.x-k8s.io/provider=bootstrap-ocne              |
 | CACPK         | cluster.x-k8s.io/provider=control-plane-kubeadm       |
 | CACPM         | cluster.x-k8s.io/provider=control-plane-microk8s      |
 | CACPN         | cluster.x-k8s.io/provider=control-plane-nested        |
 | CACPKK3S      | cluster.x-k8s.io/provider=control-plane-kubekey-k3s   |
-| CACPOCNE      | cluster.x-k8s.io/provider=control-plane-ocne          |
 | CAPA          | cluster.x-k8s.io/provider=infrastructure-aws          |
 | CAPB          | cluster.x-k8s.io/provider=infrastructure-byoh         |
 | CAPC          | cluster.x-k8s.io/provider=infrastructure-cloudstack   |
diff --git a/docs/book/src/developer/providers/implementers-guide/create_api.md b/docs/book/src/developer/providers/implementers-guide/create_api.md
index 01c13a59c..944e1f9c5 100644
--- a/docs/book/src/developer/providers/implementers-guide/create_api.md
+++ b/docs/book/src/developer/providers/implementers-guide/create_api.md
@@ -62,3 +62,68 @@ As the deleted comments request, run `make manager manifests` to regenerate some
 git add .
 git commit -m "Added cluster types"
 ```
+
+# Registering APIs in the scheme
+
+To enable clients to encode and decode your API, your types must be able to be registered within a [scheme].
+
+[scheme]: https://pkg.go.dev/k8s.io/apimachinery/pkg/runtime#Scheme
+
+By default, Kubebuilder will provide you with a scheme builder like:
+
+```go
+import "sigs.k8s.io/controller-runtime/pkg/scheme"
+
+var (
+	// SchemeBuilder is used to add go types to the GroupVersionKind scheme
+	SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
+
+	// AddToScheme adds the types in this group-version to the given scheme.
+	AddToScheme = SchemeBuilder.AddToScheme
+)
+```
+
+and scheme registration that looks like:
+
+```go
+func init() {
+	SchemeBuilder.Register(&Captain{}, &CaptainList{})
+}
+```
+
+This pattern introduces a dependency on controller-runtime to your API types, which is discouraged for
+API packages as it makes it more difficult for consumers of your API to import your API types.
+In general, you should minimise the imports within the API folder of your package to allow your API types
+to be imported cleanly into other projects.
+
+To mitigate this, use the following schemebuilder pattern:
+
+```go
+import "k8s.io/apimachinery/pkg/runtime"
+
+var (
+	// schemeBuilder is used to add go types to the GroupVersionKind scheme.
+	schemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
+
+	// AddToScheme adds the types in this group-version to the given scheme.
+	AddToScheme = schemeBuilder.AddToScheme
+
+	objectTypes = []runtime.Object{}
+)
+
+func addKnownTypes(scheme *runtime.Scheme) error {
+	scheme.AddKnownTypes(GroupVersion, objectTypes...)
+	metav1.AddToGroupVersion(scheme, GroupVersion)
+	return nil
+}
+```
+
+and register types as below:
+
+```go
+func init() {
+	objectTypes = append(objectTypes, &Captain{}, &CaptainList{})
+}
+```
+
+This pattern reduces the number of dependencies being introduced into the API package within your project.
diff --git a/docs/book/src/reference/glossary.md b/docs/book/src/reference/glossary.md
index 7c21c9211..0b77cdb02 100644
--- a/docs/book/src/reference/glossary.md
+++ b/docs/book/src/reference/glossary.md
@@ -45,12 +45,6 @@ Cluster API Provider AWS
 ### CABPK
 Cluster API Bootstrap Provider Kubeadm
 
-### CABPOCNE
-Cluster API Bootstrap Provider Oracle Cloud Native Environment (OCNE)
-
-### CACPOCNE
-Cluster API Control Plane Provider Oracle Cloud Native Environment (OCNE)
-
 ### CAPC
 Cluster API Provider CloudStack
 
diff --git a/docs/book/src/reference/providers.md b/docs/book/src/reference/providers.md
index fc24bf53f..571f5ccfa 100644
--- a/docs/book/src/reference/providers.md
+++ b/docs/book/src/reference/providers.md
@@ -9,14 +9,12 @@ updated info about which API version they are supporting.
 - [Amazon Elastic Kubernetes Service (EKS)](https://github.com/kubernetes-sigs/cluster-api-provider-aws/tree/main/bootstrap/eks)
 - [Kubeadm](https://github.com/kubernetes-sigs/cluster-api/tree/main/bootstrap/kubeadm)
 - [MicroK8s](https://github.com/canonical/cluster-api-bootstrap-provider-microk8s)
-- [Oracle Cloud Native Environment (OCNE)](https://github.com/verrazzano/cluster-api-provider-ocne)
 - [Talos](https://github.com/siderolabs/cluster-api-bootstrap-provider-talos)
 
 ## Control Plane
 - [Kubeadm](https://github.com/kubernetes-sigs/cluster-api/tree/main/controlplane/kubeadm)
 - [MicroK8s](https://github.com/canonical/cluster-api-control-plane-provider-microk8s)
 - [Nested](https://github.com/kubernetes-sigs/cluster-api-provider-nested)
-- [Oracle Cloud Native Environment (OCNE)](https://github.com/verrazzano/cluster-api-provider-ocne)
 - [Talos](https://github.com/siderolabs/cluster-api-control-plane-provider-talos)
 
 ## Infrastructure
diff --git a/docs/book/src/tasks/bootstrap/kubeadm-bootstrap/index.md b/docs/book/src/tasks/bootstrap/kubeadm-bootstrap.md
similarity index 100%
rename from docs/book/src/tasks/bootstrap/kubeadm-bootstrap/index.md
rename to docs/book/src/tasks/bootstrap/kubeadm-bootstrap.md
diff --git a/docs/book/src/tasks/bootstrap/kubeadm-bootstrap/kubelet-config.md b/docs/book/src/tasks/bootstrap/kubeadm-bootstrap/kubelet-config.md
deleted file mode 100644
index dda906c56..000000000
--- a/docs/book/src/tasks/bootstrap/kubeadm-bootstrap/kubelet-config.md
+++ /dev/null
@@ -1,354 +0,0 @@
-# Kubelet Configuration
-
-CAPBK has several ways to configure kubelet.
-
-- [Pass `KubeletConfiguration` file via `KubeadmConfigSpec.files`](#pass-kubeletconfiguration-file-via-kubeadmconfigspecfiles)
-- [Set kubelet flags via `KubeadmConfigSpec.kubeletExtraArgs`](#set-kubelet-flags-via-kubeadmconfigspeckubeletextraargs)
-- [`kubeletconfiguration` patch target](#use-the-kubeletconfiguration-patch-target)
-
-## Pass `KubeletConfiguration` file via `KubeadmConfigSpec.files`
-
-You can use `KubeadmConfigSpec.files` to put any files on nodes. This example puts a `KubeletConfiguration` file on nodes via `KubeadmConfigSpec.files`, and makes kubelet use it via `KubeadmConfigSpec.kubeletExtraArgs`. You can check available configurations of `KubeletConfiguration` on [Kubelet Configuration (v1beta1) | Kubernetes](https://kubernetes.io/docs/reference/config-api/kubelet-config.v1beta1/#kubelet-config-k8s-io-v1beta1-KubeletConfiguration).
-
-This method is easy to replace the whole kubelet configuration generated by kubeadm, but it is not easy to replace only a part of the kubelet configuration.
-
-### KubeadmControlPlaneTemplate
-
-```yaml
-apiVersion: controlplane.cluster.x-k8s.io/v1beta1
-kind: KubeadmControlPlaneTemplate
-metadata:
-  name: cloudinit-control-plane
-  namespace: default
-spec:
-  template:
-    spec:
-      kubeadmConfigSpec:
-        files:
-        # We put a KubeletConfiguration file on nodes via KubeadmConfigSpec.files
-        # In this example, we directly put the file content in the KubeadmConfigSpec.files.content field.
-        - path: /etc/kubernetes/kubelet/config.yaml
-          owner: "root:root"
-          permissions: "0644"
-          content: |
-            apiVersion: kubelet.config.k8s.io/v1beta1
-            kind: KubeletConfiguration
-            kubeReserved:
-              cpu: "1"
-              memory: "2Gi"
-              ephemeral-storage: "1Gi"
-            systemReserved:
-              cpu: "500m"
-              memory: "1Gi"
-              ephemeral-storage: "1Gi"
-            evictionHard:
-              memory.available: "500Mi"
-              nodefs.available: "10%"
-            authentication:
-              anonymous:
-                enabled: false
-              webhook:
-                cacheTTL: 0s
-                enabled: true
-              x509:
-                clientCAFile: /etc/kubernetes/pki/ca.crt
-            authorization:
-              mode: Webhook
-              webhook:
-                cacheAuthorizedTTL: 0s
-                cacheUnauthorizedTTL: 0s
-            cgroupDriver: systemd
-            clusterDNS:
-            - 10.128.0.10
-            clusterDomain: cluster.local
-            containerRuntimeEndpoint: ""
-            cpuManagerReconcilePeriod: 0s
-            evictionPressureTransitionPeriod: 0s
-            fileCheckFrequency: 0s
-            healthzBindAddress: 127.0.0.1
-            healthzPort: 10248
-            httpCheckFrequency: 0s
-            imageMinimumGCAge: 0s
-            logging:
-              flushFrequency: 0
-              options:
-                json:
-                  infoBufferSize: "0"
-              verbosity: 0
-            memorySwap: {}
-            nodeStatusReportFrequency: 0s
-            nodeStatusUpdateFrequency: 0s
-            rotateCertificates: true
-            runtimeRequestTimeout: 0s
-            shutdownGracePeriod: 0s
-            shutdownGracePeriodCriticalPods: 0s
-            staticPodPath: /etc/kubernetes/manifests
-            streamingConnectionIdleTimeout: 0s
-            syncFrequency: 0s
-            volumeStatsAggPeriod: 0s
-        clusterConfiguration:
-          controllerManager:
-            extraArgs:
-              enable-hostpath-provisioner: "true"
-        initConfiguration:
-          nodeRegistration:
-            criSocket: unix:///var/run/containerd/containerd.sock
-            # Here we configure kubelet to use the KubeletConfiguration file we put on nodes via KubeadmConfigSpec.files
-            kubeletExtraArgs:
-              config: "/etc/kubernetes/kubelet/config.yaml"
-        joinConfiguration:
-          nodeRegistration:
-            criSocket: unix:///var/run/containerd/containerd.sock
-            # Here we configure kubelet to use the KubeletConfiguration file we put on nodes via KubeadmConfigSpec.files
-            kubeletExtraArgs:
-              config: "/etc/kubernetes/kubelet/config.yaml"
-```
-
-### KubeadmConfigTemplate
-
-```yaml
-apiVersion: bootstrap.cluster.x-k8s.io/v1beta1
-kind: KubeadmConfigTemplate
-metadata:
-  name: cloudinit-default-worker-bootstraptemplate
-  namespace: default
-spec:
-  template:
-    spec:
-      files:
-      # We puts a KubeletConfiguration file on nodes via KubeadmConfigSpec.files
-      # In this example, we directly put the file content in the KubeadmConfigSpec.files.content field.
-      - path: /etc/kubernetes/kubelet/config.yaml
-        owner: "root:root"
-        permissions: "0644"
-        content: |
-          apiVersion: kubelet.config.k8s.io/v1beta1
-          kind: KubeletConfiguration
-          kubeReserved:
-            cpu: "1"
-            memory: "2Gi"
-            ephemeral-storage: "1Gi"
-          systemReserved:
-            cpu: "500m"
-            memory: "1Gi"
-            ephemeral-storage: "1Gi"
-          evictionHard:
-            memory.available: "500Mi"
-            nodefs.available: "10%"
-          authentication:
-            anonymous:
-              enabled: false
-            webhook:
-              cacheTTL: 0s
-              enabled: true
-            x509:
-              clientCAFile: /etc/kubernetes/pki/ca.crt
-          authorization:
-            mode: Webhook
-            webhook:
-              cacheAuthorizedTTL: 0s
-              cacheUnauthorizedTTL: 0s
-          cgroupDriver: systemd
-          clusterDNS:
-          - 10.128.0.10
-          clusterDomain: cluster.local
-          containerRuntimeEndpoint: ""
-          cpuManagerReconcilePeriod: 0s
-          evictionPressureTransitionPeriod: 0s
-          fileCheckFrequency: 0s
-          healthzBindAddress: 127.0.0.1
-          healthzPort: 10248
-          httpCheckFrequency: 0s
-          imageMinimumGCAge: 0s
-          logging:
-            flushFrequency: 0
-            options:
-              json:
-                infoBufferSize: "0"
-            verbosity: 0
-          memorySwap: {}
-          nodeStatusReportFrequency: 0s
-          nodeStatusUpdateFrequency: 0s
-          rotateCertificates: true
-          runtimeRequestTimeout: 0s
-          shutdownGracePeriod: 0s
-          shutdownGracePeriodCriticalPods: 0s
-          staticPodPath: /etc/kubernetes/manifests
-          streamingConnectionIdleTimeout: 0s
-          syncFrequency: 0s
-          volumeStatsAggPeriod: 0s
-      joinConfiguration:
-        nodeRegistration:
-          criSocket: unix:///var/run/containerd/containerd.sock
-          # Here we configure kubelet to use the KubeletConfiguration file we put on nodes via KubeadmConfigSpec.files
-          kubeletExtraArgs:
-            config: "/etc/kubernetes/kubelet/config.yaml"
-```
-
-## Set kubelet flags via `KubeadmConfigSpec.kubeletExtraArgs`
-
-We can pass kubelet command-line flags via `KubeadmConfigSpec.kubeletExtraArgs`. This example is equivalent to setting `--kube-reserved`, `--system-reserved`, and `--eviction-hard` flags for the kubelet command.
-
-This method is useful when you want to set kubelet flags that are not configurable via the `KubeletConfiguration` file, however, it is not recommended to use this method to set flags that are configurable via the `KubeletConfiguration` file.
-
-### KubeadmControlPlaneTemplate
-
-```yaml
-apiVersion: controlplane.cluster.x-k8s.io/v1beta1
-kind: KubeadmControlPlaneTemplate
-metadata:
-  name: kubelet-extra-args-control-plane
-  namespace: default
-spec:
-  template:
-    spec:
-      kubeadmConfigSpec:
-        clusterConfiguration:
-          controllerManager:
-            extraArgs:
-              enable-hostpath-provisioner: "true"
-        initConfiguration:
-          nodeRegistration:
-            criSocket: unix:///var/run/containerd/containerd.sock
-            # Set kubelet flags via KubeadmConfigSpec.kubeletExtraArgs
-            kubeletExtraArgs:
-              kube-reserved: cpu=1,memory=2Gi,ephemeral-storage=1Gi
-              system-reserved: cpu=500m,memory=1Gi,ephemeral-storage=1Gi
-              eviction-hard: memory.available<500Mi,nodefs.available<10%
-        joinConfiguration:
-          nodeRegistration:
-            criSocket: unix:///var/run/containerd/containerd.sock
-            # Set kubelet flags via KubeadmConfigSpec.kubeletExtraArgs
-            kubeletExtraArgs:
-              kube-reserved: cpu=1,memory=2Gi,ephemeral-storage=1Gi
-              system-reserved: cpu=500m,memory=1Gi,ephemeral-storage=1Gi
-              eviction-hard: memory.available<500Mi,nodefs.available<10%
-```
-
-### KubeadmConfigTemplate
-
-```yaml
-apiVersion: bootstrap.cluster.x-k8s.io/v1beta1
-kind: KubeadmConfigTemplate
-metadata:
-  name: kubelet-extra-args-default-worker-bootstraptemplate
-  namespace: default
-spec:
-  template:
-    spec:
-      joinConfiguration:
-        nodeRegistration:
-          criSocket: unix:///var/run/containerd/containerd.sock
-          # Set kubelet flags via KubeadmConfigSpec.kubeletExtraArgs
-          kubeletExtraArgs:
-            kube-reserved: cpu=1,memory=2Gi,ephemeral-storage=1Gi
-            system-reserved: cpu=500m,memory=1Gi,ephemeral-storage=1Gi
-            eviction-hard: memory.available<500Mi,nodefs.available<10%
-```
-
-## Use kubeadm's `kubeletconfiguration` patch target
-
-We can use kubeadm's `kubeletconfiguration` patch target to patch the kubelet configuration file. In this example, we put a patch file for `kubeletconfiguration` target in `strategic` `patchtype` on nodes via `KubeadmConfigSpec.files`. For more details, see [Customizing components with the kubeadm API | Kubernetes](https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/control-plane-flags/#patches)
-
-This method is useful when you want to change the kubelet configuration file partially on specific nodes. For example, you can deploy a partially patched kubelet configuration file on specific nodes based on the default configuration used for `kubeadm init` or `kubeadm join`.
-
-### KubeadmControlPlaneTemplate
-
-```yaml
-apiVersion: controlplane.cluster.x-k8s.io/v1beta1
-kind: KubeadmControlPlaneTemplate
-metadata:
-  name: kubeadm-config-template-control-plane
-  namespace: default
-spec:
-  template:
-    spec:
-      kubeadmConfigSpec:
-        files:
-        # Here we put a patch file for kubeletconfiguration target in strategic patchtype on nodes via KubeadmConfigSpec.files
-        # The naming convention of the patch file is kubeletconfiguration{suffix}+{patchtype}.json where {suffix} is an string and {patchtype} is one of the following: strategic, merge, json.
-        # {suffix} determines the order of the patch files. The patches are applied in the alpha-numerical order of the {suffix}.
-        - path: /etc/kubernetes/patches/kubeletconfiguration0+strategic.json
-          owner: "root:root"
-          permissions: "0644"
-          content: |
-            {
-              "apiVersion": "kubelet.config.k8s.io/v1beta1",
-              "kind": "KubeletConfiguration",
-              "kubeReserved": {
-                "cpu": "1",
-                "memory": "2Gi",
-                "ephemeral-storage": "1Gi",
-              },
-              "systemReserved": {
-                "cpu": "500m",
-                "memory": "1Gi",
-                "ephemeral-storage": "1Gi",
-              },
-              "evictionHard": {
-                "memory.available": "500Mi",
-                "nodefs.available": "10%",
-              },
-            }
-        clusterConfiguration:
-          controllerManager:
-            extraArgs:
-              enable-hostpath-provisioner: "true"
-        initConfiguration:
-          nodeRegistration:
-            criSocket: unix:///var/run/containerd/containerd.sock
-          # Here we specify the directory that contains the patch files
-          patches:
-            directory: /etc/kubernetes/patches
-        joinConfiguration:
-          nodeRegistration:
-            criSocket: unix:///var/run/containerd/containerd.sock
-          # Here we specify the directory that contains the patch files
-          patches:
-            directory: /etc/kubernetes/patches
-```
-
-### KubeadmConfigTemplate
-
-```yaml
-apiVersion: bootstrap.cluster.x-k8s.io/v1beta1
-kind: KubeadmConfigTemplate
-metadata:
-  name: kubeadm-config-template-default-worker-bootstraptemplate
-  namespace: default
-spec:
-  template:
-    spec:
-      files:
-      # Here we put a patch file for kubeletconfiguration target in strategic patchtype on nodes via KubeadmConfigSpec.files
-      # The naming convention of the patch file is kubeletconfiguration{suffix}+{patchtype}.json where {suffix} is an string and {patchtype} is one of the following: strategic, merge, json.
-      # {suffix} determines the order of the patch files. The patches are applied in the alpha-numerical order of the {suffix}.
-      - path: /etc/kubernetes/patches/kubeletconfiguration0+strategic.json
-        owner: "root:root"
-        permissions: "0644"
-        content: |
-          {
-            "apiVersion": "kubelet.config.k8s.io/v1beta1",
-            "kind": "KubeletConfiguration",
-            "kubeReserved": {
-              "cpu": "1",
-              "memory": "2Gi",
-              "ephemeral-storage": "1Gi",
-            },
-            "systemReserved": {
-              "cpu": "500m",
-              "memory": "1Gi",
-              "ephemeral-storage": "1Gi",
-            },
-            "evictionHard": {
-              "memory.available": "500Mi",
-              "nodefs.available": "10%",
-            },
-          }
-      joinConfiguration:
-        nodeRegistration:
-          criSocket: unix:///var/run/containerd/containerd.sock
-        # Here we specify the directory that contains the patch files
-        patches:
-          directory: /etc/kubernetes/patches
-```
\ No newline at end of file
diff --git a/docs/release/release-tasks.md b/docs/release/release-tasks.md
index b3f0a3067..636f112de 100644
--- a/docs/release/release-tasks.md
+++ b/docs/release/release-tasks.md
@@ -86,10 +86,9 @@ As of now we ask for volunteers in Slack and office hours.
 
 #### Finalize release schedule and team
 
-1. Finalize release schedule and team in the [docs/release/releases](../../docs/release/releases), e.g. [release-1.4.md](../../docs/release/releases/release-1.4.md).
-2. Update @cluster-api-release-team Slack user group and GitHub team accordingly.
+1. Finalize release schedule and team in `release-1.4.md`.
+2. Update Slack user group and GitHub team accordingly.
    <br>Prior art: https://github.com/kubernetes-sigs/cluster-api/issues/7476
-3. Announce the _release team_ and _release schedule_ to the mailing list.
 
 #### Prepare main branch for development of the new release
 
@@ -124,7 +123,7 @@ Prior art:
 
 #### Create a new GitHub milestone for the next release
 
-The goal of this task is to create [a new GitHub milestone](https://github.com/kubernetes-sigs/cluster-api/milestones) for the next release, so that we can already move tasks
+The goal of this task is to create a new GitHub milestone for the next release, so that we can already move tasks
 out of the current milestone if necessary.
 
 1. Create the milestone for the new release via GitHub UI.
@@ -347,7 +346,6 @@ upcoming code freezes etc based on the [release timeline (1.4 example)](./releas
 
 Information can be distributed via:
 * `sig-cluster-lifecycle` mailing list
-  * Note: The person sending out the email should ensure that they are first part of the mailing list. If the email is sent out is not received by the community, reach out to the maintainers to unblock and approve the email.
 * Slack
 * Office hours
 * Cluster API book
diff --git a/docs/release/release-team.md b/docs/release/release-team.md
index 81433a9e8..b92c8ede6 100644
--- a/docs/release/release-team.md
+++ b/docs/release/release-team.md
@@ -82,7 +82,7 @@ When assembling a release team, the release team lead should look for volunteers
 - Can commit to the amount of time required across the release cycle
 - Are enthusiastic about being on the release team
 - Are members of the Cluster Lifecycle SIG
-- Have some prior experience with contributing to CAPI releases (such having been in the release team for a prior release)
+- Have some prior experience with contributing to CAPI releases (such as shadowing a role for a prior release)
 - Have diverse company affiliations (i.e. not all from the same company)
 
 ## Time Commitment
diff --git a/docs/release/releases/release-1.6.md b/docs/release/releases/release-1.6.md
index 9e9430f6f..50ad0a54d 100644
--- a/docs/release/releases/release-1.6.md
+++ b/docs/release/releases/release-1.6.md
@@ -28,9 +28,9 @@ After the `.0` release monthly patch release will be created.
 
 ## Release team
 
-| **Role**                                  | **Lead** (**GitHub / Slack ID**)                                                      | **Team member(s) (GitHub / Slack ID)** |
-|-------------------------------------------|-------------------------------------------------------------------------------------------|----------------------------------------|
-| Release Lead                              | TBD | TBD                                    |
-| Communications/Docs/Release Notes Manager | TBD | TBD                                    |
-| CI Signal/Bug Triage/Automation Manager   | TBD | TBD                                    |
-| Maintainer                                | TBD | TBD                                    |
\ No newline at end of file
+| **Role**                                  | **Lead Name** (**GitHub / Slack ID**)                                                      | **Shadow Name(s) (GitHub / Slack ID)**                                                                                                                                                                                                                                          |
+|-------------------------------------------|--------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| Release Lead                              | TBD | TBD   |
+| Communications/Docs/Release Notes Manager | TBD | TBD   |
+| CI Signal/Bug Triage/Automation Manager   | TBD | TBD   |
+| Release Writer                            | TBD | TBD   |
\ No newline at end of file
diff --git a/exp/api/v1beta1/machinepool_webhook.go b/exp/api/v1beta1/machinepool_webhook.go
index 8dfd0d6b0..58dc05ef7 100644
--- a/exp/api/v1beta1/machinepool_webhook.go
+++ b/exp/api/v1beta1/machinepool_webhook.go
@@ -152,9 +152,6 @@ func (m *MachinePool) validate(old *MachinePool) error {
 		}
 	}
 
-	// Validate the metadata of the MachinePool template.
-	allErrs = append(allErrs, m.Spec.Template.ObjectMeta.Validate(specPath.Child("template", "metadata"))...)
-
 	if len(allErrs) == 0 {
 		return nil
 	}
diff --git a/go.mod b/go.mod
index 16c67aadf..70f72ccba 100644
--- a/go.mod
+++ b/go.mod
@@ -22,7 +22,7 @@ require (
 	github.com/google/gofuzz v1.2.0
 	github.com/mattn/go-runewidth v0.0.14 // indirect
 	github.com/onsi/ginkgo/v2 v2.11.0
-	github.com/onsi/gomega v1.27.10
+	github.com/onsi/gomega v1.27.8
 	github.com/pkg/errors v0.9.1
 	github.com/spf13/cobra v1.7.0
 	github.com/spf13/pflag v1.0.5
diff --git a/go.sum b/go.sum
index 1d257153d..2dc614f44 100644
--- a/go.sum
+++ b/go.sum
@@ -418,8 +418,8 @@ github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N
 github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
 github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU=
 github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM=
-github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
-github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M=
+github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc=
+github.com/onsi/gomega v1.27.8/go.mod h1:2J8vzI/s+2shY9XHRApDkdgPo1TKT7P2u6fXeJKFnNQ=
 github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
 github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
diff --git a/hack/ensure-go.sh b/hack/ensure-go.sh
index d19b6cd59..17d125b72 100755
--- a/hack/ensure-go.sh
+++ b/hack/ensure-go.sh
@@ -38,7 +38,7 @@ EOF
   local go_version
   IFS=" " read -ra go_version <<< "$(go version)"
   local minimum_go_version
-  minimum_go_version=go1.20
+  minimum_go_version=go1.20.4
   if [[ "${minimum_go_version}" != $(echo -e "${minimum_go_version}\n${go_version[2]}" | sort -s -t. -k 1,1 -k 2,2n -k 3,3n | head -n1) && "${go_version[2]}" != "devel" ]]; then
     cat <<EOF
 Detected go version: ${go_version[*]}.
diff --git a/hack/tools/go.mod b/hack/tools/go.mod
index abf655150..026a5913c 100644
--- a/hack/tools/go.mod
+++ b/hack/tools/go.mod
@@ -7,14 +7,15 @@ replace sigs.k8s.io/cluster-api => ../../
 replace sigs.k8s.io/cluster-api/test => ../../test
 
 require (
-	cloud.google.com/go/storage v1.31.0
+	cloud.google.com/go/storage v1.29.0
 	github.com/blang/semver v3.5.1+incompatible
 	github.com/hashicorp/go-multierror v1.1.1
 	github.com/pkg/errors v0.9.1
 	github.com/spf13/pflag v1.0.5
 	github.com/valyala/fastjson v1.6.4
 	golang.org/x/tools v0.9.3
-	google.golang.org/api v0.132.0
+	google.golang.org/api v0.126.0
+	helm.sh/helm/v3 v3.11.1
 	k8s.io/api v0.27.2
 	k8s.io/apiextensions-apiserver v0.27.2
 	k8s.io/apimachinery v0.27.2
@@ -25,17 +26,18 @@ require (
 	sigs.k8s.io/cluster-api v0.0.0-00010101000000-000000000000
 	sigs.k8s.io/cluster-api/test v0.0.0-00010101000000-000000000000
 	sigs.k8s.io/controller-runtime v0.15.0
-	sigs.k8s.io/controller-tools v0.12.1
+	sigs.k8s.io/controller-tools v0.12.0
 	sigs.k8s.io/kubebuilder/docs/book/utils v0.0.0-20211028165026-57688c578b5d
 	sigs.k8s.io/kustomize/api v0.13.2
 	sigs.k8s.io/yaml v1.3.0
 )
 
 require (
-	cloud.google.com/go v0.110.4 // indirect
+	cloud.google.com/go v0.110.2 // indirect
 	cloud.google.com/go/compute v1.20.1 // indirect
 	cloud.google.com/go/compute/metadata v0.2.3 // indirect
-	cloud.google.com/go/iam v1.1.0 // indirect
+	cloud.google.com/go/iam v0.13.0 // indirect
+	github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
 	github.com/MakeNowJust/heredoc v1.0.0 // indirect
 	github.com/Masterminds/goutils v1.1.1 // indirect
 	github.com/Masterminds/semver/v3 v3.2.0 // indirect
@@ -49,15 +51,19 @@ require (
 	github.com/blang/semver/v4 v4.0.0 // indirect
 	github.com/cespare/xxhash/v2 v2.2.0 // indirect
 	github.com/cloudflare/circl v1.3.3 // indirect
+	github.com/containerd/containerd v1.6.18 // indirect
 	github.com/coredns/caddy v1.1.0 // indirect
 	github.com/coredns/corefile-migration v1.0.20 // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect
+	github.com/docker/cli v20.10.21+incompatible // indirect
 	github.com/docker/distribution v2.8.2+incompatible // indirect
-	github.com/docker/docker v24.0.5+incompatible // indirect
+	github.com/docker/docker v20.10.24+incompatible // indirect
+	github.com/docker/docker-credential-helpers v0.7.0 // indirect
 	github.com/docker/go-connections v0.4.0 // indirect
+	github.com/docker/go-metrics v0.0.1 // indirect
 	github.com/docker/go-units v0.4.0 // indirect
 	github.com/drone/envsubst/v2 v2.0.0-20210730161058-179042472c46 // indirect
-	github.com/emicklei/go-restful/v3 v3.10.2 // indirect
+	github.com/emicklei/go-restful/v3 v3.9.0 // indirect
 	github.com/evanphx/json-patch v5.6.0+incompatible // indirect
 	github.com/evanphx/json-patch/v5 v5.6.0 // indirect
 	github.com/fsnotify/fsnotify v1.6.0 // indirect
@@ -70,6 +76,7 @@ require (
 	github.com/gogo/protobuf v1.3.2 // indirect
 	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
 	github.com/golang/protobuf v1.5.3 // indirect
+	github.com/google/btree v1.0.1 // indirect
 	github.com/google/cel-go v0.12.6 // indirect
 	github.com/google/gnostic v0.6.9 // indirect
 	github.com/google/go-cmp v0.5.9 // indirect
@@ -77,44 +84,60 @@ require (
 	github.com/google/go-querystring v1.1.0 // indirect
 	github.com/google/gofuzz v1.2.0 // indirect
 	github.com/google/s2a-go v0.1.4 // indirect
+	github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
 	github.com/google/uuid v1.3.0 // indirect
-	github.com/googleapis/enterprise-certificate-proxy v0.2.5 // indirect
-	github.com/googleapis/gax-go/v2 v2.12.0 // indirect
+	github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect
+	github.com/googleapis/gax-go/v2 v2.11.0 // indirect
+	github.com/gorilla/mux v1.8.0 // indirect
+	github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect
 	github.com/hashicorp/errwrap v1.1.0 // indirect
 	github.com/hashicorp/hcl v1.0.0 // indirect
 	github.com/huandu/xstrings v1.3.3 // indirect
 	github.com/imdario/mergo v0.3.13 // indirect
+	github.com/inconshreveable/mousetrap v1.1.0 // indirect
 	github.com/josharian/intern v1.0.0 // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
+	github.com/klauspost/compress v1.13.6 // indirect
+	github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
 	github.com/magiconair/properties v1.8.7 // indirect
 	github.com/mailru/easyjson v0.7.7 // indirect
 	github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
 	github.com/mitchellh/copystructure v1.2.0 // indirect
 	github.com/mitchellh/mapstructure v1.5.0 // indirect
 	github.com/mitchellh/reflectwalk v1.0.2 // indirect
+	github.com/moby/locker v1.0.1 // indirect
+	github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect
 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 	github.com/modern-go/reflect2 v1.0.2 // indirect
+	github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
+	github.com/morikuni/aec v1.0.0 // indirect
 	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
-	github.com/onsi/gomega v1.27.10 // indirect
+	github.com/onsi/gomega v1.27.8 // indirect
 	github.com/opencontainers/go-digest v1.0.0 // indirect
 	github.com/opencontainers/image-spec v1.1.0-rc2 // indirect
 	github.com/pelletier/go-toml/v2 v2.0.8 // indirect
+	github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
 	github.com/prometheus/client_golang v1.16.0 // indirect
 	github.com/prometheus/client_model v0.4.0 // indirect
 	github.com/prometheus/common v0.42.0 // indirect
 	github.com/prometheus/procfs v0.10.1 // indirect
 	github.com/shopspring/decimal v1.3.1 // indirect
+	github.com/sirupsen/logrus v1.9.0 // indirect
 	github.com/spf13/afero v1.9.5 // indirect
 	github.com/spf13/cast v1.5.1 // indirect
+	github.com/spf13/cobra v1.7.0 // indirect
 	github.com/spf13/jwalterweatherman v1.1.0 // indirect
 	github.com/spf13/viper v1.16.0 // indirect
 	github.com/stoewer/go-strcase v1.2.0 // indirect
 	github.com/subosito/gotenv v1.4.2 // indirect
+	github.com/xlab/treeprint v1.1.0 // indirect
 	go.opencensus.io v0.24.0 // indirect
+	go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect
 	golang.org/x/crypto v0.11.0 // indirect
 	golang.org/x/mod v0.10.0 // indirect
 	golang.org/x/net v0.12.0 // indirect
 	golang.org/x/oauth2 v0.10.0 // indirect
+	golang.org/x/sync v0.2.0 // indirect
 	golang.org/x/sys v0.10.0 // indirect
 	golang.org/x/term v0.10.0 // indirect
 	golang.org/x/text v0.11.0 // indirect
@@ -122,18 +145,20 @@ require (
 	golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
 	gomodules.xyz/jsonpatch/v2 v2.3.0 // indirect
 	google.golang.org/appengine v1.6.7 // indirect
-	google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130 // indirect
-	google.golang.org/genproto/googleapis/api v0.0.0-20230706204954-ccb25ca9f130 // indirect
-	google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect
-	google.golang.org/grpc v1.56.2 // indirect
+	google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc // indirect
+	google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect
+	google.golang.org/grpc v1.55.0 // indirect
 	google.golang.org/protobuf v1.31.0 // indirect
 	gopkg.in/inf.v0 v0.9.1 // indirect
 	gopkg.in/ini.v1 v1.67.0 // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 	k8s.io/apiserver v0.27.2 // indirect
+	k8s.io/cli-runtime v0.27.2 // indirect
 	k8s.io/cluster-bootstrap v0.27.2 // indirect
 	k8s.io/component-base v0.27.2 // indirect
+	oras.land/oras-go v1.2.2 // indirect
 	sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
 	sigs.k8s.io/kustomize/kyaml v0.14.1 // indirect
 	sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
diff --git a/hack/tools/go.sum b/hack/tools/go.sum
index 771ff6460..d06096c7c 100644
--- a/hack/tools/go.sum
+++ b/hack/tools/go.sum
@@ -17,8 +17,8 @@ cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHOb
 cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
 cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
 cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
-cloud.google.com/go v0.110.4 h1:1JYyxKMN9hd5dR2MYTPWkGUgcoxVVhg0LKNKEo0qvmk=
-cloud.google.com/go v0.110.4/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI=
+cloud.google.com/go v0.110.2 h1:sdFPBr6xG9/wkBbfhmUz/JmZC7X6LavQgcrVINrKiVA=
+cloud.google.com/go v0.110.2/go.mod h1:k04UEeEtb6ZBRTv3dZz4CeJC3jKGxyhl0sAiVVquxiw=
 cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
 cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
 cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
@@ -32,8 +32,8 @@ cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2Aawl
 cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
 cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
 cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
-cloud.google.com/go/iam v1.1.0 h1:67gSqaPukx7O8WLLHMa0PNs3EBGd2eE4d+psbO/CO94=
-cloud.google.com/go/iam v1.1.0/go.mod h1:nxdHjaKfCr7fNYx/HJMM8LgiMugmveWlkatear5gVyk=
+cloud.google.com/go/iam v0.13.0 h1:+CmB+K0J/33d0zSQ9SlFWUeCCEn5XJA0ZMZ3pHE9u8k=
+cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0=
 cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
 cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
 cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
@@ -44,10 +44,11 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
 cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
 cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
 cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
-cloud.google.com/go/storage v1.31.0 h1:+S3LjjEN2zZ+L5hOwj4+1OkGCsLVe0NzpXKQ1pSdTCI=
-cloud.google.com/go/storage v1.31.0/go.mod h1:81ams1PrhW16L4kF7qg+4mTq7SRs5HsbDTM0bWvrwJ0=
+cloud.google.com/go/storage v1.29.0 h1:6weCgzRvMg7lzuUurI4697AqIRPU1SvzHhynwpW31jI=
+cloud.google.com/go/storage v1.29.0/go.mod h1:4puEjyTKnku6gfKoTfNOU/W+a9JyuVNxjpS5GBrB8h4=
 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
 github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
+github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
 github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
@@ -60,9 +61,11 @@ github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj
 github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
 github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA=
 github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
+github.com/Microsoft/hcsshim v0.9.6 h1:VwnDOgLeoi2du6dAznfmspNqTiwczvjv4K7NxuY9jsY=
 github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
 github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 h1:wPbRQzjjwFc0ih8puEVAOFGELsn1zoIIYdxvML7mDxA=
 github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g=
+github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs=
 github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls=
 github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E=
 github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
@@ -85,7 +88,11 @@ github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdn
 github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
 github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
 github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
+github.com/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuPGoOVeF2fE4Og9otCc70=
 github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
+github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd h1:rFt+Y/IK1aEZkEHchZRSq9OQbsSzIT/OrI8YFFmRIng=
+github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembjv71DPz3uX/V/6MMlSyD9JBQ6kQ=
+github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o=
 github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
 github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
@@ -107,6 +114,9 @@ github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWH
 github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
 github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
 github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/containerd/cgroups v1.0.4 h1:jN/mbWBEaz+T1pi5OFtnkQ+8qnmEbAr1Oo1FRm5B0dA=
+github.com/containerd/containerd v1.6.18 h1:qZbsLvmyu+Vlty0/Ex5xc0z2YtKpIsb5n45mAMI+2Ns=
+github.com/containerd/containerd v1.6.18/go.mod h1:1RdCUu95+gc2v9t3IL+zIlpClSmew7/0YS8O5eQZrOw=
 github.com/coredns/caddy v1.1.0 h1:ezvsPrT/tA/7pYDBZxu0cT0VmWk75AfIaf6GSYCNMf0=
 github.com/coredns/caddy v1.1.0/go.mod h1:A6ntJQlAWuQfFlsd9hvigKbo2WS0VUs2l1e2F+BawD4=
 github.com/coredns/corefile-migration v1.0.20 h1:MdOkT6F3ehju/n9tgxlGct8XAajOX2vN+wG7To4BWSI=
@@ -117,25 +127,36 @@ github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee
 github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
 github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
 github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
 github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
+github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2 h1:aBfCb7iqHmDEIp6fBvC/hQUddQfg+3qdYjwzaiP9Hnc=
+github.com/docker/cli v20.10.21+incompatible h1:qVkgyYUnOLQ98LtXBrwd/duVqPT2X4SHndOuGsfwyhU=
+github.com/docker/cli v20.10.21+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
 github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
 github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
-github.com/docker/docker v24.0.5+incompatible h1:WmgcE4fxyI6EEXxBRxsHnZXrO1pQ3smi0k/jho4HLeY=
-github.com/docker/docker v24.0.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v20.10.24+incompatible h1:Ugvxm7a8+Gz6vqQYQQ2W7GYq5EUPaAiuPgIfVyI3dYE=
+github.com/docker/docker v20.10.24+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A=
+github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0=
 github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
 github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
+github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8=
+github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8=
+github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
 github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
 github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
+github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4=
 github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
 github.com/drone/envsubst/v2 v2.0.0-20210730161058-179042472c46 h1:7QPwrLT79GlD5sizHf27aoY2RTvw62mO6x7mxkScNk0=
 github.com/drone/envsubst/v2 v2.0.0-20210730161058-179042472c46/go.mod h1:esf2rsHFNlZlxsqsZDojNBcnNs5REqIvRrWRHqX0vEU=
-github.com/emicklei/go-restful/v3 v3.10.2 h1:hIovbnmBTLjHXkqEBUz3HGpXZdM7ZrE9fJIZIqlJLqE=
-github.com/emicklei/go-restful/v3 v3.10.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
+github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE=
+github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
 github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
@@ -149,6 +170,7 @@ github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLi
 github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww=
 github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4=
 github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
+github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
 github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0=
 github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
 github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
@@ -214,8 +236,11 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS
 github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
 github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
 github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/gomodule/redigo v1.8.2 h1:H5XSIre1MB5NbPYFp+i1NBbb5qN1W8Y8YAQoAYbkm8k=
 github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
+github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
 github.com/google/cel-go v0.12.6 h1:kjeKudqV0OygrAqA9fX6J55S8gj+Jre2tckIm5RoG4M=
 github.com/google/cel-go v0.12.6/go.mod h1:Jk7ljRzLBhkmiAwBoUxB1sZSCVBAzkqPF25olK/iRDw=
 github.com/google/gnostic v0.6.9 h1:ZK/5VhkoX835RikCHpSUJV9a+S3e1zLh59YnyWeBW+0=
@@ -259,19 +284,26 @@ github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJY
 github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
 github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc=
 github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A=
+github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
+github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
 github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
 github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/googleapis/enterprise-certificate-proxy v0.2.5 h1:UR4rDjcgpgEnqpIEvkiqTYKBCKLNmlge2eVjoZfySzM=
-github.com/googleapis/enterprise-certificate-proxy v0.2.5/go.mod h1:RxW0N9901Cko1VOCW3SXCpWP+mlIEkk2tP7jnHy9a3w=
+github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k=
+github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k=
 github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
 github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
-github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas=
-github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU=
+github.com/googleapis/gax-go/v2 v2.11.0 h1:9V9PWXEsWnPpQhu/PeQIkS4eGzMlTLGgt80cUUI8Ki4=
+github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5iydzRfb3peWZJI=
 github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
 github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
+github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
+github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
 github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM=
+github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
 github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
 github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
@@ -295,6 +327,7 @@ github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b
 github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
 github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
 github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
 github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
 github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
@@ -309,11 +342,14 @@ github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH
 github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
 github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
 github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
+github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
 github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
 github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
 github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
 github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
 github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
 github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
 github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
@@ -323,6 +359,8 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V
 github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
 github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
+github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
 github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
@@ -334,6 +372,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0=
+github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE=
 github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc=
 github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
@@ -362,14 +402,22 @@ github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR
 github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
 github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
 github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
+github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
+github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
+github.com/moby/sys/mountinfo v0.5.0 h1:2Ks8/r6lopsxWi9m58nlwjaeSzUX9iiL1vj5qB/9ObI=
 github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA=
+github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
 github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0=
+github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4=
 github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
+github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
 github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
@@ -377,8 +425,8 @@ github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
 github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
 github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
 github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU=
-github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
-github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M=
+github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc=
+github.com/onsi/gomega v1.27.8/go.mod h1:2J8vzI/s+2shY9XHRApDkdgPo1TKT7P2u6fXeJKFnNQ=
 github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
 github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034=
@@ -387,6 +435,9 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI
 github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
 github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
 github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
+github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
+github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
+github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI=
 github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@@ -397,6 +448,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
 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.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
+github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
 github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8=
 github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc=
 github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
@@ -406,10 +459,14 @@ github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUo
 github.com/prometheus/client_model v0.4.0/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.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
 github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM=
 github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc=
 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.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
 github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg=
 github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM=
 github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
@@ -418,14 +475,18 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L
 github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
 github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
 github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
+github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
 github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
 github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
 github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
 github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
 github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
+github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
+github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
 github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
 github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
 github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
@@ -438,6 +499,8 @@ github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU
 github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA=
 github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48=
 github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=
+github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
+github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
 github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
 github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
 github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
@@ -452,6 +515,7 @@ github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
 github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
@@ -473,11 +537,16 @@ github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2
 github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
 github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
 github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
+github.com/xlab/treeprint v1.1.0 h1:G/1DjNkPpfZCFt9CSh6b5/nY4VimlbHF3Rh4obvtzDk=
+github.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0=
 github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 h1:+lm10QQTNSBd8DVTNGHx7o/IKu9HYDvLMffDhbyLccI=
+github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMztlGpl/VA+Zm1AcTPHYkHJPbHqE6WJUXE=
+github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY=
 go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
 go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
@@ -488,6 +557,8 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
 go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
 go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
 go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
+go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 h1:+FNtrFTmVw0YZGpBGX56XDee331t6JAXeK2bcyhLOOc=
+go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o=
 go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
 go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
 go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
@@ -558,6 +629,7 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
 golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -610,7 +682,8 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
+golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
+golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -625,7 +698,9 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -655,9 +730,11 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -768,8 +845,8 @@ google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz513
 google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
 google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
 google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
-google.golang.org/api v0.132.0 h1:8t2/+qZ26kAOGSmOiHwVycqVaDg7q3JDILrNi/Z6rvc=
-google.golang.org/api v0.132.0/go.mod h1:AeTBC6GpJnJSRJjktDcPX0QwtS8pGYZOV6MSuSCusw0=
+google.golang.org/api v0.126.0 h1:q4GJq+cAdMAC7XP7njvQ4tvohGLiSlytuL4BQxbIZ+o=
+google.golang.org/api v0.126.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw=
 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -816,12 +893,12 @@ google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6D
 google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130 h1:Au6te5hbKUV8pIYWHqOUZ1pva5qK/rwbIhoXEUB9Lu8=
-google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:O9kGHb51iE/nOGvQaDUuadVYqovW56s5emA88lQnj6Y=
-google.golang.org/genproto/googleapis/api v0.0.0-20230706204954-ccb25ca9f130 h1:XVeBY8d/FaK4848myy41HBqnDwvxeV3zMZhwN1TvAMU=
-google.golang.org/genproto/googleapis/api v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:mPBs5jNgx2GuQGvFwUvVKqtn6HsUw9nP64BedgvqEsQ=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 h1:bVf09lpb+OJbByTj913DRJioFFAjf/ZGxEz7MajTp2U=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM=
+google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc h1:8DyZCyvI8mE1IdLy/60bS+52xfymkE72wv1asokgtao=
+google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64=
+google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc h1:kVKPf/IiYSBWEWtkIn6wZXwWGCnLKcC8oWfZvXjsGnM=
+google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc h1:XSJ8Vk1SWuNr8S18z1NZSziL0CPIXLCCMDOEFtHBOFc=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
 google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@@ -842,8 +919,8 @@ google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG
 google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
 google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
 google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
-google.golang.org/grpc v1.56.2 h1:fVRFRnXvU+x6C4IlHZewvJOVHoOv1TUuQyoRsYnB4bI=
-google.golang.org/grpc v1.56.2/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
+google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag=
+google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
 google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -888,6 +965,8 @@ gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o=
+helm.sh/helm/v3 v3.11.1 h1:cmL9fFohOoNQf+wnp2Wa0OhNFH0KFnSzEkVxi3fcc3I=
+helm.sh/helm/v3 v3.11.1/go.mod h1:z/Bu/BylToGno/6dtNGuSmjRqxKq5gaH+FU0BPO+AQ8=
 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
@@ -903,6 +982,8 @@ k8s.io/apimachinery v0.27.2 h1:vBjGaKKieaIreI+oQwELalVG4d8f3YAMNpWLzDXkxeg=
 k8s.io/apimachinery v0.27.2/go.mod h1:XNfZ6xklnMCOGGFNqXG7bUrQCoR04dh/E7FprV6pb+E=
 k8s.io/apiserver v0.27.2 h1:p+tjwrcQEZDrEorCZV2/qE8osGTINPuS5ZNqWAvKm5E=
 k8s.io/apiserver v0.27.2/go.mod h1:EsOf39d75rMivgvvwjJ3OW/u9n1/BmUMK5otEOJrb1Y=
+k8s.io/cli-runtime v0.27.2 h1:9HI8gfReNujKXt16tGOAnb8b4NZ5E+e0mQQHKhFGwYw=
+k8s.io/cli-runtime v0.27.2/go.mod h1:9UecpyPDTkhiYY4d9htzRqN+rKomJgyb4wi0OfrmCjw=
 k8s.io/client-go v0.27.2 h1:vDLSeuYvCHKeoQRhCXjxXO45nHVv2Ip4Fe0MfioMrhE=
 k8s.io/client-go v0.27.2/go.mod h1:tY0gVmUsHrAmjzHX9zs7eCjxcBsf8IiNe7KQ52biTcQ=
 k8s.io/cluster-bootstrap v0.27.2 h1:OL3onrOwrUD7NQxBUqQwTl1Uu2GQKCkw9BMHpc4PbiA=
@@ -915,13 +996,15 @@ k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f h1:2kWPakN3i/k81b0gvD5C5F
 k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f/go.mod h1:byini6yhqGC14c3ebc/QwanvYwhuMWF6yz2F8uwW8eg=
 k8s.io/utils v0.0.0-20230209194617-a36077c30491 h1:r0BAOLElQnnFhE/ApUsg3iHdVYYPBjNSSOMowRZxxsY=
 k8s.io/utils v0.0.0-20230209194617-a36077c30491/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
+oras.land/oras-go v1.2.2 h1:0E9tOHUfrNH7TCDk5KU0jVBEzCqbfdyuVfGmJ7ZeRPE=
+oras.land/oras-go v1.2.2/go.mod h1:Apa81sKoZPpP7CDciE006tSZ0x3Q3+dOoBcMZ/aNxvw=
 rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
 rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
 rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
 sigs.k8s.io/controller-runtime v0.15.0 h1:ML+5Adt3qZnMSYxZ7gAverBLNPSMQEibtzAgp0UPojU=
 sigs.k8s.io/controller-runtime v0.15.0/go.mod h1:7ngYvp1MLT+9GeZ+6lH3LOlcHkp/+tzA/fmHa4iq9kk=
-sigs.k8s.io/controller-tools v0.12.1 h1:GyQqxzH5wksa4n3YDIJdJJOopztR5VDM+7qsyg5yE4U=
-sigs.k8s.io/controller-tools v0.12.1/go.mod h1:rXlpTfFHZMpZA8aGq9ejArgZiieHd+fkk/fTatY8A2M=
+sigs.k8s.io/controller-tools v0.12.0 h1:TY6CGE6+6hzO7hhJFte65ud3cFmmZW947jajXkuDfBw=
+sigs.k8s.io/controller-tools v0.12.0/go.mod h1:rXlpTfFHZMpZA8aGq9ejArgZiieHd+fkk/fTatY8A2M=
 sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
 sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
 sigs.k8s.io/kubebuilder/docs/book/utils v0.0.0-20211028165026-57688c578b5d h1:KLiQzLW3RZJR19+j4pw2h5iioyAyqCkDBEAFdnGa3N8=
diff --git a/hack/tools/release/notes.go b/hack/tools/release/notes.go
index 83fa935a3..f4d61df64 100644
--- a/hack/tools/release/notes.go
+++ b/hack/tools/release/notes.go
@@ -63,18 +63,12 @@ var (
 		unknown,
 	}
 
-	repo = flag.String("repository", "kubernetes-sigs/cluster-api", "The tag or commit to start from.")
-
 	fromTag = flag.String("from", "", "The tag or commit to start from.")
 
 	since      = flag.String("since", "", "Include commits starting from and including this date. Accepts format: YYYY-MM-DD")
 	until      = flag.String("until", "", "Include commits up to and including this date. Accepts format: YYYY-MM-DD")
 	numWorkers = flag.Int("workers", 10, "Number of concurrent routines to process PR entries. If running into GitHub rate limiting, use 1.")
 
-	prefixAreaLabel = flag.Bool("prefix-area-label", true, "If enabled, will prefix the area label.")
-
-	addKubernetesVersionSupport = flag.Bool("add-kubernetes-version-support", true, "If enabled, will add the Kubernetes version support header.")
-
 	tagRegex = regexp.MustCompile(`^\[release-[\w-\.]*\]`)
 
 	userFriendlyAreas = map[string]string{
@@ -160,7 +154,7 @@ func getAreaLabel(merge string) (string, error) {
 	// Get pr id from merge commit
 	prID := strings.Replace(strings.TrimSpace(strings.Split(merge, " ")[3]), "#", "", -1)
 
-	cmd := exec.Command("gh", "api", fmt.Sprintf("repos/%s/pulls/%s", *repo, prID)) //nolint:gosec
+	cmd := exec.Command("gh", "api", "repos/kubernetes-sigs/cluster-api/pulls/"+prID) //nolint:gosec
 
 	out, err := cmd.CombinedOutput()
 	if err != nil {
@@ -307,9 +301,8 @@ func run() int {
 		}
 	}
 
-	if *addKubernetesVersionSupport {
-		// TODO Turn this into a link (requires knowing the project name + organization)
-		fmt.Print(`## 👌 Kubernetes version support
+	// TODO Turn this into a link (requires knowing the project name + organization)
+	fmt.Print(`## 👌 Kubernetes version support
 
 - Management Cluster: v1.**X**.x -> v1.**X**.x
 - Workload Cluster: v1.**X**.x -> v1.**X**.x
@@ -317,29 +310,19 @@ func run() int {
 [More information about version support can be found here](https://cluster-api.sigs.k8s.io/reference/versions.html)
 
 `)
-	}
-
 	fmt.Printf("## Changes since %v\n---\n", commitRange)
 
 	fmt.Printf("## :chart_with_upwards_trend: Overview\n")
-	if count := len(commits); count == 1 {
-		fmt.Println("- 1 new commit merged")
-	} else if count > 1 {
+	if count := len(commits); count > 0 {
 		fmt.Printf("- %d new commits merged\n", count)
 	}
-	if count := len(merges[warning]); count == 1 {
-		fmt.Println("- 1 breaking change :warning:")
-	} else if count > 1 {
+	if count := len(merges[warning]); count > 0 {
 		fmt.Printf("- %d breaking changes :warning:\n", count)
 	}
-	if count := len(merges[features]); count == 1 {
-		fmt.Println("- 1 feature addition ✨")
-	} else if count > 1 {
+	if count := len(merges[features]); count > 0 {
 		fmt.Printf("- %d feature additions ✨\n", count)
 	}
-	if count := len(merges[bugs]); count == 1 {
-		fmt.Println("- 1 bug fixed 🐛")
-	} else if count > 1 {
+	if count := len(merges[bugs]); count > 0 {
 		fmt.Printf("- %d bugs fixed 🐛\n", count)
 	}
 	fmt.Println()
@@ -352,25 +335,14 @@ func run() int {
 
 		switch key {
 		case documentation:
-			if len(mergeslice) == 1 {
-				fmt.Printf(
-					":book: Additionally, there has been 1 contribution to our documentation and book. (%s) \n\n",
-					mergeslice[0],
-				)
-			} else {
-				fmt.Printf(
-					":book: Additionally, there have been %d contributions to our documentation and book. (%s) \n\n",
-					len(mergeslice),
-					strings.Join(mergeslice, ", "),
-				)
-			}
+			fmt.Printf(
+				":book: Additionally, there have been %d contributions to our documentation and book. (%s) \n\n",
+				len(mergeslice),
+				strings.Join(mergeslice, ", "),
+			)
 		default:
 			fmt.Println("## " + key)
-			sort.Slice(mergeslice, func(i int, j int) bool {
-				str1 := strings.ToLower(mergeslice[i])
-				str2 := strings.ToLower(mergeslice[j])
-				return str1 < str2
-			})
+			sort.Strings(mergeslice)
 			for _, merge := range mergeslice {
 				fmt.Println(merge)
 			}
@@ -439,14 +411,9 @@ func generateReleaseNoteEntry(c *commit) (*releaseNoteEntry, error) {
 	entry := &releaseNoteEntry{}
 	entry.title = trimTitle(c.body)
 	var fork string
-
-	var area string
-	if *prefixAreaLabel {
-		var err error
-		area, err = getAreaLabel(c.merge)
-		if err != nil {
-			return nil, err
-		}
+	area, err := getAreaLabel(c.merge)
+	if err != nil {
+		return nil, err
 	}
 
 	switch {
@@ -491,13 +458,7 @@ func generateReleaseNoteEntry(c *commit) (*releaseNoteEntry, error) {
 	if entry.title == "" {
 		return entry, nil
 	}
-
-	if *prefixAreaLabel {
-		entry.title = fmt.Sprintf("- %s: %s", area, entry.title)
-	} else {
-		entry.title = fmt.Sprintf("- %s", entry.title)
-	}
-
+	entry.title = fmt.Sprintf("- %s: %s", area, entry.title)
 	_, _ = fmt.Sscanf(c.merge, "Merge pull request %s from %s", &entry.prNumber, &fork)
 	entry.title = formatMerge(entry.title, entry.prNumber)
 
diff --git a/hack/tools/tilt-prepare/main.go b/hack/tools/tilt-prepare/main.go
index 5be7f25bb..195d8cb39 100644
--- a/hack/tools/tilt-prepare/main.go
+++ b/hack/tools/tilt-prepare/main.go
@@ -26,6 +26,7 @@ import (
 	"fmt"
 	"io"
 	"io/fs"
+	"net/http"
 	"os"
 	"os/exec"
 	"path/filepath"
@@ -35,6 +36,7 @@ import (
 
 	"github.com/pkg/errors"
 	"github.com/spf13/pflag"
+	"helm.sh/helm/v3/pkg/repo"
 	appsv1 "k8s.io/api/apps/v1"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -602,7 +604,12 @@ func cleanupChartTask(path string) taskFunction {
 
 				helmChartVersion := helmChart.Version
 				if helmChartVersion == "" {
-					return errors.New("Helm Chart version must be set")
+					// If helmChart.Version is not set lookup the latest version.
+					helmChartVersion, err = resolveLatestHelmChartVersion(ctx, helmChart.Repo, helmChart.Name)
+					if err != nil {
+						return err
+					}
+					klog.Infof("[%s] resolved latest Helm Chart version to %q\n", prefix, helmChartVersion)
 				}
 
 				// Read Chart.yaml
@@ -639,6 +646,42 @@ func cleanupChartTask(path string) taskFunction {
 	}
 }
 
+// resolveLatestHelmChartVersion resolves the latest version of a Helm Chart with name chartName
+// from a given chartRepo.
+func resolveLatestHelmChartVersion(ctx context.Context, chartRepo, chartName string) (string, error) {
+	chartIndexURL := fmt.Sprintf(chartRepo + "/index.yaml")
+
+	req, err := http.NewRequestWithContext(ctx, http.MethodGet, chartIndexURL, http.NoBody)
+	if err != nil {
+		return "", errors.Wrapf(err, "failed to resolve latest Helm Chart version for Chart %q: failed to create request for chart index from URL %q", chartName, chartIndexURL)
+	}
+
+	resp, err := http.DefaultClient.Do(req)
+	if err != nil {
+		return "", errors.Wrapf(err, "failed to resolve latest Helm Chart version for Chart %q: failed to get chart index from URL %q", chartName, chartIndexURL)
+	}
+	defer resp.Body.Close()
+
+	bodyBytes, err := io.ReadAll(resp.Body)
+	if err != nil {
+		return "", errors.Wrapf(err, "failed to resolve latest Helm Chart version for Chart %q: failed to parse chart index from URL %q", chartName, chartIndexURL)
+	}
+
+	indexFile := &repo.IndexFile{}
+	if err := yaml.UnmarshalStrict(bodyBytes, indexFile); err != nil {
+		return "", errors.Wrapf(err, "failed to resolve latest Helm Chart version for Chart %q: failed to unmarshal chart index from URL %q", chartName, chartIndexURL)
+	}
+	// Sort entries to ensure the latest version is on top.
+	// This is required to get the latest version.
+	indexFile.SortEntries()
+
+	chartVersion, err := indexFile.Get(chartName, "")
+	if err != nil {
+		return "", errors.Wrapf(err, "failed to resolve latest Helm Chart version for Chart %q: failed to get latest version from chart index from URL %q", chartName, chartIndexURL)
+	}
+	return chartVersion.Version, nil
+}
+
 // kustomizeTask generates a task for running kustomize build on a path and saving the output on a file.
 func kustomizeTask(path, out string) taskFunction {
 	return func(ctx context.Context, prefix string, errCh chan error) {
diff --git a/internal/webhooks/cluster.go b/internal/webhooks/cluster.go
index 2e0da4f57..9bce93a80 100644
--- a/internal/webhooks/cluster.go
+++ b/internal/webhooks/cluster.go
@@ -255,9 +255,6 @@ func (webhook *Cluster) validateTopology(ctx context.Context, oldCluster, newClu
 		)
 	}
 
-	// metadata in topology should be valid
-	allErrs = append(allErrs, validateTopologyMetadata(newCluster.Spec.Topology, fldPath)...)
-
 	// upgrade concurrency should be a numeric value.
 	if concurrency, ok := newCluster.Annotations[clusterv1.ClusterTopologyUpgradeConcurrencyAnnotation]; ok {
 		concurrencyAnnotationField := field.NewPath("metadata", "annotations", clusterv1.ClusterTopologyUpgradeConcurrencyAnnotation)
@@ -632,16 +629,3 @@ func clusterClassIsReconciled(clusterClass *clusterv1.ClusterClass) error {
 	}
 	return nil
 }
-
-func validateTopologyMetadata(topology *clusterv1.Topology, fldPath *field.Path) field.ErrorList {
-	var allErrs field.ErrorList
-	allErrs = append(allErrs, topology.ControlPlane.Metadata.Validate(fldPath.Child("controlPlane", "metadata"))...)
-	if topology.Workers != nil {
-		for idx, md := range topology.Workers.MachineDeployments {
-			allErrs = append(allErrs, md.Metadata.Validate(
-				fldPath.Child("workers", "machineDeployments").Index(idx).Child("metadata"),
-			)...)
-		}
-	}
-	return allErrs
-}
diff --git a/internal/webhooks/clusterclass.go b/internal/webhooks/clusterclass.go
index d802691d9..a90fa33fb 100644
--- a/internal/webhooks/clusterclass.go
+++ b/internal/webhooks/clusterclass.go
@@ -156,9 +156,6 @@ func (webhook *ClusterClass) validate(ctx context.Context, oldClusterClass, newC
 	// Validate patches.
 	allErrs = append(allErrs, validatePatches(newClusterClass)...)
 
-	// Validate metadata
-	allErrs = append(allErrs, validateClusterClassMetadata(newClusterClass)...)
-
 	// If this is an update run additional validation.
 	if oldClusterClass != nil {
 		// Ensure spec changes are compatible.
@@ -368,17 +365,3 @@ func validateMachineHealthCheckClass(fldPath *field.Path, namepace string, m *cl
 
 	return mhc.ValidateCommonFields(fldPath)
 }
-
-func validateClusterClassMetadata(clusterClass *clusterv1.ClusterClass) field.ErrorList {
-	var allErrs field.ErrorList
-	allErrs = append(allErrs, clusterClass.Spec.ControlPlane.Metadata.Validate(field.NewPath("spec", "controlPlane", "metadata"))...)
-	workerPath := field.NewPath("spec", "workers", "machineDeployments")
-	for idx, m := range clusterClass.Spec.Workers.MachineDeployments {
-		allErrs = append(allErrs, validateMachineDeploymentMetadata(m, workerPath.Index(idx))...)
-	}
-	return allErrs
-}
-
-func validateMachineDeploymentMetadata(m clusterv1.MachineDeploymentClass, fldPath *field.Path) field.ErrorList {
-	return m.Template.Metadata.Validate(fldPath.Child("template", "metadata"))
-}


Test patch:
diff --git a/api/v1beta1/machinedeployment_webhook_test.go b/api/v1beta1/machinedeployment_webhook_test.go
index 9231f0e64..f138be93e 100644
--- a/api/v1beta1/machinedeployment_webhook_test.go
+++ b/api/v1beta1/machinedeployment_webhook_test.go
@@ -18,12 +18,12 @@ package v1beta1
 
 import (
 	"context"
-	"strings"
 	"testing"
 
 	. "github.com/onsi/gomega"
 	admissionv1 "k8s.io/api/admission/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/util/intstr"
 	"k8s.io/utils/pointer"
 	"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
@@ -45,8 +45,8 @@ func TestMachineDeploymentDefault(t *testing.T) {
 		},
 	}
 
-	scheme, err := SchemeBuilder.Build()
-	g.Expect(err).ToNot(HaveOccurred())
+	scheme := runtime.NewScheme()
+	g.Expect(AddToScheme(scheme)).To(Succeed())
 	defaulter := MachineDeploymentDefaulter(scheme)
 
 	t.Run("for MachineDeployment", defaultValidateTestCustomDefaulter(md, defaulter))
@@ -570,56 +570,3 @@ func defaultValidateTestCustomDefaulter(object admission.Validator, customDefaul
 		})
 	}
 }
-
-func TestMachineDeploymentTemplateMetadataValidation(t *testing.T) {
-	tests := []struct {
-		name        string
-		labels      map[string]string
-		annotations map[string]string
-		expectErr   bool
-	}{
-		{
-			name: "should return error for invalid labels and annotations",
-			labels: map[string]string{
-				"foo":          "$invalid-key",
-				"bar":          strings.Repeat("a", 64) + "too-long-value",
-				"/invalid-key": "foo",
-			},
-			annotations: map[string]string{
-				"/invalid-key": "foo",
-			},
-			expectErr: true,
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			g := NewWithT(t)
-			md := &MachineDeployment{
-				Spec: MachineDeploymentSpec{
-					Template: MachineTemplateSpec{
-						ObjectMeta: ObjectMeta{
-							Labels:      tt.labels,
-							Annotations: tt.annotations,
-						},
-					},
-				},
-			}
-			if tt.expectErr {
-				warnings, err := md.ValidateCreate()
-				g.Expect(err).To(HaveOccurred())
-				g.Expect(warnings).To(BeEmpty())
-				warnings, err = md.ValidateUpdate(md)
-				g.Expect(err).To(HaveOccurred())
-				g.Expect(warnings).To(BeEmpty())
-			} else {
-				warnings, err := md.ValidateCreate()
-				g.Expect(err).ToNot(HaveOccurred())
-				g.Expect(warnings).To(BeEmpty())
-				warnings, err = md.ValidateUpdate(md)
-				g.Expect(err).ToNot(HaveOccurred())
-				g.Expect(warnings).To(BeEmpty())
-			}
-		})
-	}
-}
diff --git a/api/v1beta1/machineset_webhook_test.go b/api/v1beta1/machineset_webhook_test.go
index e5d6ef89a..65b86348d 100644
--- a/api/v1beta1/machineset_webhook_test.go
+++ b/api/v1beta1/machineset_webhook_test.go
@@ -17,7 +17,6 @@ limitations under the License.
 package v1beta1
 
 import (
-	"strings"
 	"testing"
 
 	. "github.com/onsi/gomega"
@@ -308,56 +307,3 @@ func TestValidateSkippedMachineSetPreflightChecks(t *testing.T) {
 		})
 	}
 }
-
-func TestMachineSetTemplateMetadataValidation(t *testing.T) {
-	tests := []struct {
-		name        string
-		labels      map[string]string
-		annotations map[string]string
-		expectErr   bool
-	}{
-		{
-			name: "should return error for invalid labels and annotations",
-			labels: map[string]string{
-				"foo":          "$invalid-key",
-				"bar":          strings.Repeat("a", 64) + "too-long-value",
-				"/invalid-key": "foo",
-			},
-			annotations: map[string]string{
-				"/invalid-key": "foo",
-			},
-			expectErr: true,
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			g := NewWithT(t)
-			ms := &MachineSet{
-				Spec: MachineSetSpec{
-					Template: MachineTemplateSpec{
-						ObjectMeta: ObjectMeta{
-							Labels:      tt.labels,
-							Annotations: tt.annotations,
-						},
-					},
-				},
-			}
-			if tt.expectErr {
-				warnings, err := ms.ValidateCreate()
-				g.Expect(err).To(HaveOccurred())
-				g.Expect(warnings).To(BeEmpty())
-				warnings, err = ms.ValidateUpdate(ms)
-				g.Expect(err).To(HaveOccurred())
-				g.Expect(warnings).To(BeEmpty())
-			} else {
-				warnings, err := ms.ValidateCreate()
-				g.Expect(err).ToNot(HaveOccurred())
-				g.Expect(warnings).To(BeEmpty())
-				warnings, err = ms.ValidateUpdate(ms)
-				g.Expect(err).ToNot(HaveOccurred())
-				g.Expect(warnings).To(BeEmpty())
-			}
-		})
-	}
-}
diff --git a/bootstrap/kubeadm/api/v1beta1/kubeadmconfigtemplate_webhook_test.go b/bootstrap/kubeadm/api/v1beta1/kubeadmconfigtemplate_webhook_test.go
index d7d3187f8..258cb8f24 100644
--- a/bootstrap/kubeadm/api/v1beta1/kubeadmconfigtemplate_webhook_test.go
+++ b/bootstrap/kubeadm/api/v1beta1/kubeadmconfigtemplate_webhook_test.go
@@ -17,14 +17,12 @@ limitations under the License.
 package v1beta1_test
 
 import (
-	"strings"
 	"testing"
 
 	. "github.com/onsi/gomega"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/utils/pointer"
 
-	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
 	bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1beta1"
 	utildefaulting "sigs.k8s.io/cluster-api/util/defaulting"
 )
@@ -48,8 +46,7 @@ func TestKubeadmConfigTemplateDefault(t *testing.T) {
 
 func TestKubeadmConfigTemplateValidation(t *testing.T) {
 	cases := map[string]struct {
-		in        *bootstrapv1.KubeadmConfigTemplate
-		expectErr bool
+		in *bootstrapv1.KubeadmConfigTemplate
 	}{
 		"valid configuration": {
 			in: &bootstrapv1.KubeadmConfigTemplate{
@@ -64,21 +61,6 @@ func TestKubeadmConfigTemplateValidation(t *testing.T) {
 				},
 			},
 		},
-		"should return error for invalid labels and annotations": {
-			in: &bootstrapv1.KubeadmConfigTemplate{Spec: bootstrapv1.KubeadmConfigTemplateSpec{
-				Template: bootstrapv1.KubeadmConfigTemplateResource{ObjectMeta: clusterv1.ObjectMeta{
-					Labels: map[string]string{
-						"foo":          "$invalid-key",
-						"bar":          strings.Repeat("a", 64) + "too-long-value",
-						"/invalid-key": "foo",
-					},
-					Annotations: map[string]string{
-						"/invalid-key": "foo",
-					},
-				}},
-			}},
-			expectErr: true,
-		},
 	}
 
 	for name, tt := range cases {
@@ -87,18 +69,10 @@ func TestKubeadmConfigTemplateValidation(t *testing.T) {
 		t.Run(name, func(t *testing.T) {
 			g := NewWithT(t)
 			warnings, err := tt.in.ValidateCreate()
-			if tt.expectErr {
-				g.Expect(err).To(HaveOccurred())
-			} else {
-				g.Expect(err).ToNot(HaveOccurred())
-			}
+			g.Expect(err).ToNot(HaveOccurred())
 			g.Expect(warnings).To(BeEmpty())
 			warnings, err = tt.in.ValidateUpdate(nil)
-			if tt.expectErr {
-				g.Expect(err).To(HaveOccurred())
-			} else {
-				g.Expect(err).ToNot(HaveOccurred())
-			}
+			g.Expect(err).ToNot(HaveOccurred())
 			g.Expect(warnings).To(BeEmpty())
 		})
 	}
diff --git a/cmd/clusterctl/client/config_test.go b/cmd/clusterctl/client/config_test.go
index 2fbc9b191..33d8dcd45 100644
--- a/cmd/clusterctl/client/config_test.go
+++ b/cmd/clusterctl/client/config_test.go
@@ -59,13 +59,11 @@ func Test_clusterctlClient_GetProvidersConfig(t *testing.T) {
 				config.KubeadmBootstrapProviderName,
 				config.KubeKeyK3sBootstrapProviderName,
 				config.MicroK8sBootstrapProviderName,
-				config.OracleCloudNativeBootstrapProviderName,
 				config.TalosBootstrapProviderName,
 				config.KubeadmControlPlaneProviderName,
 				config.KubeKeyK3sControlPlaneProviderName,
 				config.MicroK8sControlPlaneProviderName,
 				config.NestedControlPlaneProviderName,
-				config.OracleCloudNativeControlPlaneProviderName,
 				config.TalosControlPlaneProviderName,
 				config.AWSProviderName,
 				config.AzureProviderName,
@@ -109,13 +107,11 @@ func Test_clusterctlClient_GetProvidersConfig(t *testing.T) {
 				config.KubeadmBootstrapProviderName,
 				config.KubeKeyK3sBootstrapProviderName,
 				config.MicroK8sBootstrapProviderName,
-				config.OracleCloudNativeBootstrapProviderName,
 				config.TalosBootstrapProviderName,
 				config.KubeadmControlPlaneProviderName,
 				config.KubeKeyK3sControlPlaneProviderName,
 				config.MicroK8sControlPlaneProviderName,
 				config.NestedControlPlaneProviderName,
-				config.OracleCloudNativeControlPlaneProviderName,
 				config.TalosControlPlaneProviderName,
 				config.AWSProviderName,
 				config.AzureProviderName,
diff --git a/cmd/clusterctl/cmd/config_repositories_test.go b/cmd/clusterctl/cmd/config_repositories_test.go
index 6b014f13b..c5867edbe 100644
--- a/cmd/clusterctl/cmd/config_repositories_test.go
+++ b/cmd/clusterctl/cmd/config_repositories_test.go
@@ -108,13 +108,11 @@ another-provider    BootstrapProvider        ./
 kubeadm             BootstrapProvider        https://github.com/kubernetes-sigs/cluster-api/releases/latest/                             bootstrap-components.yaml
 kubekey-k3s         BootstrapProvider        https://github.com/kubesphere/kubekey/releases/latest/                                      bootstrap-components.yaml
 microk8s            BootstrapProvider        https://github.com/canonical/cluster-api-bootstrap-provider-microk8s/releases/latest/       bootstrap-components.yaml
-ocne                BootstrapProvider        https://github.com/verrazzano/cluster-api-provider-ocne/releases/latest/                    bootstrap-components.yaml
 talos               BootstrapProvider        https://github.com/siderolabs/cluster-api-bootstrap-provider-talos/releases/latest/         bootstrap-components.yaml
 kubeadm             ControlPlaneProvider     https://github.com/kubernetes-sigs/cluster-api/releases/latest/                             control-plane-components.yaml
 kubekey-k3s         ControlPlaneProvider     https://github.com/kubesphere/kubekey/releases/latest/                                      control-plane-components.yaml
 microk8s            ControlPlaneProvider     https://github.com/canonical/cluster-api-control-plane-provider-microk8s/releases/latest/   control-plane-components.yaml
 nested              ControlPlaneProvider     https://github.com/kubernetes-sigs/cluster-api-provider-nested/releases/latest/             control-plane-components.yaml
-ocne                ControlPlaneProvider     https://github.com/verrazzano/cluster-api-provider-ocne/releases/latest/                    control-plane-components.yaml
 talos               ControlPlaneProvider     https://github.com/siderolabs/cluster-api-control-plane-provider-talos/releases/latest/     control-plane-components.yaml
 aws                 InfrastructureProvider                                                                                               my-aws-infrastructure-components.yaml
 azure               InfrastructureProvider   https://github.com/kubernetes-sigs/cluster-api-provider-azure/releases/latest/              infrastructure-components.yaml
@@ -166,10 +164,6 @@ var expectedOutputYaml = `- File: core_components.yaml
   Name: microk8s
   ProviderType: BootstrapProvider
   URL: https://github.com/canonical/cluster-api-bootstrap-provider-microk8s/releases/latest/
-- File: bootstrap-components.yaml
-  Name: ocne
-  ProviderType: BootstrapProvider
-  URL: https://github.com/verrazzano/cluster-api-provider-ocne/releases/latest/
 - File: bootstrap-components.yaml
   Name: talos
   ProviderType: BootstrapProvider
@@ -190,10 +184,6 @@ var expectedOutputYaml = `- File: core_components.yaml
   Name: nested
   ProviderType: ControlPlaneProvider
   URL: https://github.com/kubernetes-sigs/cluster-api-provider-nested/releases/latest/
-- File: control-plane-components.yaml
-  Name: ocne
-  ProviderType: ControlPlaneProvider
-  URL: https://github.com/verrazzano/cluster-api-provider-ocne/releases/latest/
 - File: control-plane-components.yaml
   Name: talos
   ProviderType: ControlPlaneProvider
diff --git a/controlplane/kubeadm/api/v1beta1/kubeadm_control_plane_webhook_test.go b/controlplane/kubeadm/api/v1beta1/kubeadm_control_plane_webhook_test.go
index cd230eb61..b593798ee 100644
--- a/controlplane/kubeadm/api/v1beta1/kubeadm_control_plane_webhook_test.go
+++ b/controlplane/kubeadm/api/v1beta1/kubeadm_control_plane_webhook_test.go
@@ -17,7 +17,6 @@ limitations under the License.
 package v1beta1
 
 import (
-	"strings"
 	"testing"
 	"time"
 
@@ -153,16 +152,6 @@ func TestKubeadmControlPlaneValidateCreate(t *testing.T) {
 	validIgnitionConfiguration.Spec.KubeadmConfigSpec.Format = bootstrapv1.Ignition
 	validIgnitionConfiguration.Spec.KubeadmConfigSpec.Ignition = &bootstrapv1.IgnitionSpec{}
 
-	invalidMetadata := valid.DeepCopy()
-	invalidMetadata.Spec.MachineTemplate.ObjectMeta.Labels = map[string]string{
-		"foo":          "$invalid-key",
-		"bar":          strings.Repeat("a", 64) + "too-long-value",
-		"/invalid-key": "foo",
-	}
-	invalidMetadata.Spec.MachineTemplate.ObjectMeta.Annotations = map[string]string{
-		"/invalid-key": "foo",
-	}
-
 	tests := []struct {
 		name                  string
 		enableIgnitionFeature bool
@@ -247,12 +236,6 @@ func TestKubeadmControlPlaneValidateCreate(t *testing.T) {
 			expectErr:             false,
 			kcp:                   validIgnitionConfiguration,
 		},
-		{
-			name:                  "should return error for invalid metadata",
-			enableIgnitionFeature: true,
-			expectErr:             true,
-			kcp:                   invalidMetadata,
-		},
 	}
 
 	for _, tt := range tests {
@@ -688,16 +671,6 @@ func TestKubeadmControlPlaneValidateUpdate(t *testing.T) {
 		{"/var/lib/testdir", "/var/lib/etcd/data"},
 	}
 
-	invalidMetadata := before.DeepCopy()
-	invalidMetadata.Spec.MachineTemplate.ObjectMeta.Labels = map[string]string{
-		"foo":          "$invalid-key",
-		"bar":          strings.Repeat("a", 64) + "too-long-value",
-		"/invalid-key": "foo",
-	}
-	invalidMetadata.Spec.MachineTemplate.ObjectMeta.Annotations = map[string]string{
-		"/invalid-key": "foo",
-	}
-
 	tests := []struct {
 		name                  string
 		enableIgnitionFeature bool
@@ -1043,13 +1016,6 @@ func TestKubeadmControlPlaneValidateUpdate(t *testing.T) {
 			before:                before,
 			kcp:                   switchFromCloudInitToIgnition,
 		},
-		{
-			name:                  "should return error for invalid metadata",
-			enableIgnitionFeature: true,
-			expectErr:             true,
-			before:                before,
-			kcp:                   invalidMetadata,
-		},
 	}
 
 	for _, tt := range tests {
diff --git a/controlplane/kubeadm/api/v1beta1/kubeadmcontrolplanetemplate_webhook_test.go b/controlplane/kubeadm/api/v1beta1/kubeadmcontrolplanetemplate_webhook_test.go
index d02e46360..7702184a1 100644
--- a/controlplane/kubeadm/api/v1beta1/kubeadmcontrolplanetemplate_webhook_test.go
+++ b/controlplane/kubeadm/api/v1beta1/kubeadmcontrolplanetemplate_webhook_test.go
@@ -17,7 +17,6 @@ limitations under the License.
 package v1beta1
 
 import (
-	"strings"
 	"testing"
 	"time"
 
@@ -25,7 +24,6 @@ import (
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	utilfeature "k8s.io/component-base/featuregate/testing"
 
-	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
 	bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1beta1"
 	"sigs.k8s.io/cluster-api/feature"
 	utildefaulting "sigs.k8s.io/cluster-api/util/defaulting"
@@ -112,42 +110,3 @@ func TestKubeadmControlPlaneTemplateValidationFeatureGateDisabled(t *testing.T)
 		g.Expect(warnings).To(BeEmpty())
 	})
 }
-
-func TestKubeadmControlPlaneTemplateValidationMetadata(t *testing.T) {
-	t.Run("create kubeadmcontrolplanetemplate should not pass if metadata is invalid", func(t *testing.T) {
-		g := NewWithT(t)
-		kcpTemplate := &KubeadmControlPlaneTemplate{
-			Spec: KubeadmControlPlaneTemplateSpec{
-				Template: KubeadmControlPlaneTemplateResource{
-					ObjectMeta: clusterv1.ObjectMeta{
-						Labels: map[string]string{
-							"foo":          "$invalid-key",
-							"bar":          strings.Repeat("a", 64) + "too-long-value",
-							"/invalid-key": "foo",
-						},
-						Annotations: map[string]string{
-							"/invalid-key": "foo",
-						},
-					},
-					Spec: KubeadmControlPlaneTemplateResourceSpec{
-						MachineTemplate: &KubeadmControlPlaneTemplateMachineTemplate{
-							ObjectMeta: clusterv1.ObjectMeta{
-								Labels: map[string]string{
-									"foo":          "$invalid-key",
-									"bar":          strings.Repeat("a", 64) + "too-long-value",
-									"/invalid-key": "foo",
-								},
-								Annotations: map[string]string{
-									"/invalid-key": "foo",
-								},
-							},
-						},
-					},
-				},
-			},
-		}
-		warnings, err := kcpTemplate.ValidateCreate()
-		g.Expect(err).To(HaveOccurred())
-		g.Expect(warnings).To(BeEmpty())
-	})
-}
diff --git a/exp/api/v1beta1/machinepool_webhook_test.go b/exp/api/v1beta1/machinepool_webhook_test.go
index 3b115bd2d..de3e1f4a1 100644
--- a/exp/api/v1beta1/machinepool_webhook_test.go
+++ b/exp/api/v1beta1/machinepool_webhook_test.go
@@ -17,7 +17,6 @@ limitations under the License.
 package v1beta1
 
 import (
-	"strings"
 	"testing"
 
 	. "github.com/onsi/gomega"
@@ -321,56 +320,3 @@ func TestMachinePoolVersionValidation(t *testing.T) {
 		})
 	}
 }
-
-func TestMachinePoolMetadataValidation(t *testing.T) {
-	tests := []struct {
-		name        string
-		labels      map[string]string
-		annotations map[string]string
-		expectErr   bool
-	}{
-		{
-			name: "should return error for invalid labels and annotations",
-			labels: map[string]string{
-				"foo":          "$invalid-key",
-				"bar":          strings.Repeat("a", 64) + "too-long-value",
-				"/invalid-key": "foo",
-			},
-			annotations: map[string]string{
-				"/invalid-key": "foo",
-			},
-			expectErr: true,
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			g := NewWithT(t)
-			mp := &MachinePool{
-				Spec: MachinePoolSpec{
-					Template: clusterv1.MachineTemplateSpec{
-						ObjectMeta: clusterv1.ObjectMeta{
-							Labels:      tt.labels,
-							Annotations: tt.annotations,
-						},
-					},
-				},
-			}
-			if tt.expectErr {
-				warnings, err := mp.ValidateCreate()
-				g.Expect(err).To(HaveOccurred())
-				g.Expect(warnings).To(BeEmpty())
-				warnings, err = mp.ValidateUpdate(mp)
-				g.Expect(err).To(HaveOccurred())
-				g.Expect(warnings).To(BeEmpty())
-			} else {
-				warnings, err := mp.ValidateCreate()
-				g.Expect(err).ToNot(HaveOccurred())
-				g.Expect(warnings).To(BeEmpty())
-				warnings, err = mp.ValidateUpdate(mp)
-				g.Expect(err).ToNot(HaveOccurred())
-				g.Expect(warnings).To(BeEmpty())
-			}
-		})
-	}
-}
diff --git a/internal/test/builder/builders.go b/internal/test/builder/builders.go
index 49f1b3ea3..25629561c 100644
--- a/internal/test/builder/builders.go
+++ b/internal/test/builder/builders.go
@@ -25,6 +25,7 @@ import (
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/util/intstr"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
@@ -1268,8 +1269,8 @@ func (m *MachineDeploymentBuilder) Build() *clusterv1.MachineDeployment {
 		}
 	}
 	if m.defaulter {
-		scheme, err := clusterv1.SchemeBuilder.Build()
-		if err != nil {
+		scheme := runtime.NewScheme()
+		if err := clusterv1.AddToScheme(scheme); err != nil {
 			panic(err)
 		}
 		ctx := admission.NewContextWithRequest(context.Background(), admission.Request{
diff --git a/internal/webhooks/clusterclass_test.go b/internal/webhooks/clusterclass_test.go
index 702d453f2..444e9268a 100644
--- a/internal/webhooks/clusterclass_test.go
+++ b/internal/webhooks/clusterclass_test.go
@@ -17,7 +17,6 @@ limitations under the License.
 package webhooks
 
 import (
-	"strings"
 	"testing"
 	"time"
 
@@ -1141,30 +1140,6 @@ func TestClusterClassValidation(t *testing.T) {
 				Build(),
 			expectErr: true,
 		},
-		{
-			name: "should return error for invalid labels and annotations",
-			in: builder.ClusterClass(metav1.NamespaceDefault, "class1").
-				WithInfrastructureClusterTemplate(
-					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "infra1").Build()).
-				WithControlPlaneTemplate(
-					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
-						Build()).
-				WithControlPlaneInfrastructureMachineTemplate(
-					builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "cpInfra1").
-						Build()).
-				WithWorkerMachineDeploymentClasses(
-					*builder.MachineDeploymentClass("aa").
-						WithInfrastructureTemplate(
-							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
-						WithBootstrapTemplate(
-							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
-						WithLabels(invalidLabels()).
-						WithAnnotations(invalidAnnotations()).
-						Build()).
-				WithControlPlaneMetadata(invalidLabels(), invalidAnnotations()).
-				Build(),
-			expectErr: true,
-		},
 	}
 
 	for _, tt := range tests {
@@ -1666,17 +1641,3 @@ func TestClusterClassValidationWithClusterAwareChecks(t *testing.T) {
 		})
 	}
 }
-
-func invalidLabels() map[string]string {
-	return map[string]string{
-		"foo":          "$invalid-key",
-		"bar":          strings.Repeat("a", 64) + "too-long-value",
-		"/invalid-key": "foo",
-	}
-}
-
-func invalidAnnotations() map[string]string {
-	return map[string]string{
-		"/invalid-key": "foo",
-	}
-}
diff --git a/test/go.mod b/test/go.mod
index 9bb2d61dd..902b2634d 100644
--- a/test/go.mod
+++ b/test/go.mod
@@ -6,15 +6,15 @@ replace sigs.k8s.io/cluster-api => ../
 
 require (
 	github.com/blang/semver v3.5.1+incompatible
-	github.com/docker/docker v24.0.5+incompatible
+	github.com/docker/docker v20.10.24+incompatible
 	github.com/docker/go-connections v0.4.0
-	github.com/emicklei/go-restful/v3 v3.10.2
+	github.com/emicklei/go-restful/v3 v3.9.0
 	github.com/evanphx/json-patch/v5 v5.6.0
 	github.com/flatcar/ignition v0.36.2
 	github.com/go-logr/logr v1.2.4
 	github.com/google/go-cmp v0.5.9
 	github.com/onsi/ginkgo/v2 v2.11.0
-	github.com/onsi/gomega v1.27.10
+	github.com/onsi/gomega v1.27.8
 	github.com/pkg/errors v0.9.1
 	github.com/prometheus/client_golang v1.16.0
 	github.com/spf13/pflag v1.0.5
@@ -113,6 +113,7 @@ require (
 	github.com/prometheus/common v0.42.0 // indirect
 	github.com/prometheus/procfs v0.10.1 // indirect
 	github.com/shopspring/decimal v1.3.1 // indirect
+	github.com/sirupsen/logrus v1.9.0 // indirect
 	github.com/spf13/afero v1.9.5 // indirect
 	github.com/spf13/cast v1.5.1 // indirect
 	github.com/spf13/cobra v1.7.0 // indirect
diff --git a/test/go.sum b/test/go.sum
index 5c165e3a4..1633926ef 100644
--- a/test/go.sum
+++ b/test/go.sum
@@ -140,8 +140,8 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm
 github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
 github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
 github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
-github.com/docker/docker v24.0.5+incompatible h1:WmgcE4fxyI6EEXxBRxsHnZXrO1pQ3smi0k/jho4HLeY=
-github.com/docker/docker v24.0.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v20.10.24+incompatible h1:Ugvxm7a8+Gz6vqQYQQ2W7GYq5EUPaAiuPgIfVyI3dYE=
+github.com/docker/docker v20.10.24+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
 github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
 github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
 github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
@@ -150,8 +150,8 @@ github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3
 github.com/drone/envsubst/v2 v2.0.0-20210730161058-179042472c46 h1:7QPwrLT79GlD5sizHf27aoY2RTvw62mO6x7mxkScNk0=
 github.com/drone/envsubst/v2 v2.0.0-20210730161058-179042472c46/go.mod h1:esf2rsHFNlZlxsqsZDojNBcnNs5REqIvRrWRHqX0vEU=
 github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
-github.com/emicklei/go-restful/v3 v3.10.2 h1:hIovbnmBTLjHXkqEBUz3HGpXZdM7ZrE9fJIZIqlJLqE=
-github.com/emicklei/go-restful/v3 v3.10.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
+github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE=
+github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
 github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
@@ -414,8 +414,8 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA
 github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
 github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU=
 github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM=
-github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
-github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M=
+github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc=
+github.com/onsi/gomega v1.27.8/go.mod h1:2J8vzI/s+2shY9XHRApDkdgPo1TKT7P2u6fXeJKFnNQ=
 github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
 github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
@@ -472,6 +472,7 @@ github.com/sigma/vmw-guestinfo v0.0.0-20160204083807-95dd4126d6e8/go.mod h1:JrRF
 github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
 github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
+github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
 github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
 github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
 github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
@@ -757,6 +758,7 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
diff --git a/test/infrastructure/docker/api/v1alpha3/conversion.go b/test/infrastructure/docker/api/v1alpha3/conversion.go
index ba133d350..6bdb64825 100644
--- a/test/infrastructure/docker/api/v1alpha3/conversion.go
+++ b/test/infrastructure/docker/api/v1alpha3/conversion.go
@@ -45,10 +45,6 @@ func (src *DockerCluster) ConvertTo(dstRaw conversion.Hub) error {
 		dst.Spec.LoadBalancer.ImageTag = restored.Spec.LoadBalancer.ImageTag
 	}
 
-	if restored.Spec.LoadBalancer.CustomHAProxyConfigTemplateRef != nil {
-		dst.Spec.LoadBalancer.CustomHAProxyConfigTemplateRef = restored.Spec.LoadBalancer.CustomHAProxyConfigTemplateRef
-	}
-
 	return nil
 }
 
diff --git a/test/infrastructure/docker/api/v1alpha4/conversion.go b/test/infrastructure/docker/api/v1alpha4/conversion.go
index e8c9ae8d8..a1b42d8f4 100644
--- a/test/infrastructure/docker/api/v1alpha4/conversion.go
+++ b/test/infrastructure/docker/api/v1alpha4/conversion.go
@@ -27,35 +27,13 @@ import (
 func (src *DockerCluster) ConvertTo(dstRaw conversion.Hub) error {
 	dst := dstRaw.(*infrav1.DockerCluster)
 
-	if err := Convert_v1alpha4_DockerCluster_To_v1beta1_DockerCluster(src, dst, nil); err != nil {
-		return err
-	}
-
-	// Manually restore data.
-	restored := &infrav1.DockerCluster{}
-	if ok, err := utilconversion.UnmarshalData(src, restored); err != nil || !ok {
-		return err
-	}
-
-	if restored.Spec.LoadBalancer.CustomHAProxyConfigTemplateRef != nil {
-		dst.Spec.LoadBalancer.CustomHAProxyConfigTemplateRef = restored.Spec.LoadBalancer.CustomHAProxyConfigTemplateRef
-	}
-
-	return nil
+	return Convert_v1alpha4_DockerCluster_To_v1beta1_DockerCluster(src, dst, nil)
 }
 
 func (dst *DockerCluster) ConvertFrom(srcRaw conversion.Hub) error {
 	src := srcRaw.(*infrav1.DockerCluster)
 
-	if err := Convert_v1beta1_DockerCluster_To_v1alpha4_DockerCluster(src, dst, nil); err != nil {
-		return err
-	}
-
-	if err := utilconversion.MarshalData(src, dst); err != nil {
-		return err
-	}
-
-	return nil
+	return Convert_v1beta1_DockerCluster_To_v1alpha4_DockerCluster(src, dst, nil)
 }
 
 func (src *DockerClusterList) ConvertTo(dstRaw conversion.Hub) error {
@@ -85,10 +63,6 @@ func (src *DockerClusterTemplate) ConvertTo(dstRaw conversion.Hub) error {
 
 	dst.Spec.Template.ObjectMeta = restored.Spec.Template.ObjectMeta
 
-	if restored.Spec.Template.Spec.LoadBalancer.CustomHAProxyConfigTemplateRef != nil {
-		dst.Spec.Template.Spec.LoadBalancer.CustomHAProxyConfigTemplateRef = restored.Spec.Template.Spec.LoadBalancer.CustomHAProxyConfigTemplateRef
-	}
-
 	return nil
 }
 
@@ -197,7 +171,3 @@ func Convert_v1beta1_DockerMachineTemplateResource_To_v1alpha4_DockerMachineTemp
 	// NOTE: custom conversion func is required because spec.template.metadata has been added in v1beta1.
 	return autoConvert_v1beta1_DockerMachineTemplateResource_To_v1alpha4_DockerMachineTemplateResource(in, out, s)
 }
-
-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)
-}
diff --git a/test/infrastructure/docker/api/v1alpha4/zz_generated.conversion.go b/test/infrastructure/docker/api/v1alpha4/zz_generated.conversion.go
index 71075f02d..9594641da 100644
--- a/test/infrastructure/docker/api/v1alpha4/zz_generated.conversion.go
+++ b/test/infrastructure/docker/api/v1alpha4/zz_generated.conversion.go
@@ -128,6 +128,11 @@ func RegisterConversions(s *runtime.Scheme) error {
 	}); err != nil {
 		return err
 	}
+	if err := s.AddGeneratedConversionFunc((*v1beta1.DockerLoadBalancer)(nil), (*DockerLoadBalancer)(nil), func(a, b interface{}, scope conversion.Scope) error {
+		return Convert_v1beta1_DockerLoadBalancer_To_v1alpha4_DockerLoadBalancer(a.(*v1beta1.DockerLoadBalancer), b.(*DockerLoadBalancer), scope)
+	}); err != nil {
+		return err
+	}
 	if err := s.AddGeneratedConversionFunc((*DockerMachine)(nil), (*v1beta1.DockerMachine)(nil), func(a, b interface{}, scope conversion.Scope) error {
 		return Convert_v1alpha4_DockerMachine_To_v1beta1_DockerMachine(a.(*DockerMachine), b.(*v1beta1.DockerMachine), scope)
 	}); err != nil {
@@ -228,11 +233,6 @@ func RegisterConversions(s *runtime.Scheme) error {
 	}); err != nil {
 		return err
 	}
-	if err := s.AddConversionFunc((*v1beta1.DockerLoadBalancer)(nil), (*DockerLoadBalancer)(nil), func(a, b interface{}, scope conversion.Scope) error {
-		return Convert_v1beta1_DockerLoadBalancer_To_v1alpha4_DockerLoadBalancer(a.(*v1beta1.DockerLoadBalancer), b.(*DockerLoadBalancer), 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 {
@@ -589,10 +589,14 @@ func autoConvert_v1beta1_DockerLoadBalancer_To_v1alpha4_DockerLoadBalancer(in *v
 	if err := Convert_v1beta1_ImageMeta_To_v1alpha4_ImageMeta(&in.ImageMeta, &out.ImageMeta, s); err != nil {
 		return err
 	}
-	// WARNING: in.CustomHAProxyConfigTemplateRef requires manual conversion: does not exist in peer-type
 	return nil
 }
 
+// Convert_v1beta1_DockerLoadBalancer_To_v1alpha4_DockerLoadBalancer is an autogenerated conversion function.
+func Convert_v1beta1_DockerLoadBalancer_To_v1alpha4_DockerLoadBalancer(in *v1beta1.DockerLoadBalancer, out *DockerLoadBalancer, s conversion.Scope) error {
+	return autoConvert_v1beta1_DockerLoadBalancer_To_v1alpha4_DockerLoadBalancer(in, out, s)
+}
+
 func autoConvert_v1alpha4_DockerMachine_To_v1beta1_DockerMachine(in *DockerMachine, out *v1beta1.DockerMachine, s conversion.Scope) error {
 	out.ObjectMeta = in.ObjectMeta
 	if err := Convert_v1alpha4_DockerMachineSpec_To_v1beta1_DockerMachineSpec(&in.Spec, &out.Spec, s); err != nil {
diff --git a/test/infrastructure/docker/api/v1beta1/dockercluster_types.go b/test/infrastructure/docker/api/v1beta1/dockercluster_types.go
index 617938a6e..b4c138bb6 100644
--- a/test/infrastructure/docker/api/v1beta1/dockercluster_types.go
+++ b/test/infrastructure/docker/api/v1beta1/dockercluster_types.go
@@ -17,7 +17,6 @@ limitations under the License.
 package v1beta1
 
 import (
-	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 
 	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
@@ -54,17 +53,6 @@ type DockerClusterSpec struct {
 type DockerLoadBalancer struct {
 	// ImageMeta allows customizing the image used for the cluster load balancer.
 	ImageMeta `json:",inline"`
-
-	// CustomHAProxyConfigTemplateRef allows you to replace the default HAProxy config file.
-	// This field is a reference to a config map that contains the configuration template. The key of the config map should be equal to 'value'.
-	// The content of the config map will be processed and will replace the default HAProxy config file. Please use it with caution, as there are
-	// no checks to ensure the validity of the configuration. This template will support the following variables that will be passed by the controller:
-	// $IPv6 (bool) indicates if the cluster is IPv6, $FrontendControlPlanePort (string) indicates the frontend control plane port,
-	// $BackendControlPlanePort (string) indicates the backend control plane port, $BackendServers (map[string]string) indicates the backend server
-	// where the key is the server name and the value is the address. This map is dynamic and is updated every time a new control plane
-	// node is added or removed. The template will also support the JoinHostPort function to join the host and port of the backend server.
-	// +optional
-	CustomHAProxyConfigTemplateRef *corev1.LocalObjectReference `json:"customHAProxyConfigTemplateRef,omitempty"`
 }
 
 // ImageMeta allows customizing the image used for components that are not
diff --git a/test/infrastructure/docker/api/v1beta1/dockerclustertemplate_webhook.go b/test/infrastructure/docker/api/v1beta1/dockerclustertemplate_webhook.go
index 5492ec952..46451cb02 100644
--- a/test/infrastructure/docker/api/v1beta1/dockerclustertemplate_webhook.go
+++ b/test/infrastructure/docker/api/v1beta1/dockerclustertemplate_webhook.go
@@ -63,10 +63,6 @@ func (r *DockerClusterTemplate) ValidateCreate() (admission.Warnings, error) {
 	}
 
 	allErrs := validateDockerClusterSpec(r.Spec.Template.Spec)
-
-	// Validate the metadata of the template.
-	allErrs = append(allErrs, r.Spec.Template.ObjectMeta.Validate(field.NewPath("spec", "template", "metadata"))...)
-
 	if len(allErrs) > 0 {
 		return nil, apierrors.NewInvalid(GroupVersion.WithKind("DockerClusterTemplate").GroupKind(), r.Name, allErrs)
 	}
diff --git a/test/infrastructure/docker/api/v1beta1/dockerclustertemplate_webhook_test.go b/test/infrastructure/docker/api/v1beta1/dockerclustertemplate_webhook_test.go
index 4244684d4..9284e4cb6 100644
--- a/test/infrastructure/docker/api/v1beta1/dockerclustertemplate_webhook_test.go
+++ b/test/infrastructure/docker/api/v1beta1/dockerclustertemplate_webhook_test.go
@@ -17,14 +17,12 @@ limitations under the License.
 package v1beta1
 
 import (
-	"strings"
 	"testing"
 
 	. "github.com/onsi/gomega"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	utilfeature "k8s.io/component-base/featuregate/testing"
 
-	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
 	"sigs.k8s.io/cluster-api/feature"
 )
 
@@ -70,62 +68,3 @@ func TestDockerClusterTemplateValidationFeatureGateDisabled(t *testing.T) {
 		g.Expect(warnings).To(BeEmpty())
 	})
 }
-
-func TestDockerClusterTemplateValidationMetadata(t *testing.T) {
-	defer utilfeature.SetFeatureGateDuringTest(t, feature.Gates, feature.ClusterTopology, true)()
-
-	tests := []struct {
-		name        string
-		labels      map[string]string
-		annotations map[string]string
-		expectErr   bool
-	}{
-		{
-			name: "should return error for invalid labels and annotations",
-			labels: map[string]string{
-				"foo":          "$invalid-key",
-				"bar":          strings.Repeat("a", 64) + "too-long-value",
-				"/invalid-key": "foo",
-			},
-			annotations: map[string]string{
-				"/invalid-key": "foo",
-			},
-			expectErr: true,
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			g := NewWithT(t)
-			dct := &DockerClusterTemplate{
-				ObjectMeta: metav1.ObjectMeta{
-					Name:      "dockerclustertemplate-test",
-					Namespace: "test-namespace",
-				},
-				Spec: DockerClusterTemplateSpec{
-					Template: DockerClusterTemplateResource{
-						ObjectMeta: clusterv1.ObjectMeta{
-							Labels: map[string]string{
-								"foo":          "$invalid-key",
-								"bar":          strings.Repeat("a", 64) + "too-long-value",
-								"/invalid-key": "foo",
-							},
-							Annotations: map[string]string{
-								"/invalid-key": "foo",
-							},
-						},
-						Spec: DockerClusterSpec{},
-					},
-				},
-			}
-			warnings, err := dct.ValidateCreate()
-			if tt.expectErr {
-				g.Expect(err).To(HaveOccurred())
-				g.Expect(warnings).To(BeEmpty())
-			} else {
-				g.Expect(err).NotTo(HaveOccurred())
-				g.Expect(warnings).To(BeEmpty())
-			}
-		})
-	}
-}
diff --git a/test/infrastructure/docker/api/v1beta1/dockermachinetemplate_webhook.go b/test/infrastructure/docker/api/v1beta1/dockermachinetemplate_webhook.go
index 52d1e1f50..d7df886f8 100644
--- a/test/infrastructure/docker/api/v1beta1/dockermachinetemplate_webhook.go
+++ b/test/infrastructure/docker/api/v1beta1/dockermachinetemplate_webhook.go
@@ -49,16 +49,7 @@ type DockerMachineTemplateWebhook struct{}
 var _ webhook.CustomValidator = &DockerMachineTemplateWebhook{}
 
 // ValidateCreate implements webhook.Validator so a webhook will be registered for the type.
-func (*DockerMachineTemplateWebhook) ValidateCreate(_ context.Context, raw runtime.Object) (admission.Warnings, error) {
-	obj, ok := raw.(*DockerMachineTemplate)
-	if !ok {
-		return nil, apierrors.NewBadRequest(fmt.Sprintf("expected a DockerMachineTemplate but got a %T", raw))
-	}
-	// Validate the metadata of the template.
-	allErrs := obj.Spec.Template.ObjectMeta.Validate(field.NewPath("spec", "template", "metadata"))
-	if len(allErrs) > 0 {
-		return nil, apierrors.NewInvalid(GroupVersion.WithKind("DockerClusterTemplate").GroupKind(), obj.Name, allErrs)
-	}
+func (*DockerMachineTemplateWebhook) ValidateCreate(_ context.Context, _ runtime.Object) (admission.Warnings, error) {
 	return nil, nil
 }
 
@@ -83,9 +74,6 @@ func (*DockerMachineTemplateWebhook) ValidateUpdate(ctx context.Context, oldRaw
 		!reflect.DeepEqual(newObj.Spec.Template.Spec, oldObj.Spec.Template.Spec) {
 		allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "template", "spec"), newObj, dockerMachineTemplateImmutableMsg))
 	}
-	// Validate the metadata of the template.
-	allErrs = append(allErrs, newObj.Spec.Template.ObjectMeta.Validate(field.NewPath("spec", "template", "metadata"))...)
-
 	if len(allErrs) == 0 {
 		return nil, nil
 	}
diff --git a/test/infrastructure/docker/api/v1beta1/dockermachinetemplate_webhook_test.go b/test/infrastructure/docker/api/v1beta1/dockermachinetemplate_webhook_test.go
index 0d11343c9..b70fbfbd6 100644
--- a/test/infrastructure/docker/api/v1beta1/dockermachinetemplate_webhook_test.go
+++ b/test/infrastructure/docker/api/v1beta1/dockermachinetemplate_webhook_test.go
@@ -18,7 +18,6 @@ package v1beta1
 
 import (
 	"context"
-	"strings"
 	"testing"
 
 	. "github.com/onsi/gomega"
@@ -43,18 +42,6 @@ func TestDockerMachineTemplateInvalid(t *testing.T) {
 	newTemplateSkipImmutabilityAnnotationSet := newTemplate.DeepCopy()
 	newTemplateSkipImmutabilityAnnotationSet.SetAnnotations(map[string]string{clusterv1.TopologyDryRunAnnotation: ""})
 
-	newTemplateWithInvalidMetadata := newTemplate.DeepCopy()
-	newTemplateWithInvalidMetadata.Spec.Template.ObjectMeta = clusterv1.ObjectMeta{
-		Labels: map[string]string{
-			"foo":          "$invalid-key",
-			"bar":          strings.Repeat("a", 64) + "too-long-value",
-			"/invalid-key": "foo",
-		},
-		Annotations: map[string]string{
-			"/invalid-key": "foo",
-		},
-	}
-
 	tests := []struct {
 		name        string
 		newTemplate *DockerMachineTemplate
@@ -97,13 +84,6 @@ func TestDockerMachineTemplateInvalid(t *testing.T) {
 			req:         &admission.Request{AdmissionRequest: admissionv1.AdmissionRequest{DryRun: pointer.Bool(true)}},
 			wantError:   false,
 		},
-		{
-			name:        "don't allow invalid metadata",
-			newTemplate: newTemplateWithInvalidMetadata,
-			oldTemplate: newTemplate,
-			req:         &admission.Request{AdmissionRequest: admissionv1.AdmissionRequest{DryRun: pointer.Bool(true)}},
-			wantError:   true,
-		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
diff --git a/test/infrastructure/docker/api/v1beta1/zz_generated.deepcopy.go b/test/infrastructure/docker/api/v1beta1/zz_generated.deepcopy.go
index b73abe209..a409b01f3 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 @@ limitations under the License.
 package v1beta1
 
 import (
-	"k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/runtime"
 	apiv1beta1 "sigs.k8s.io/cluster-api/api/v1beta1"
 )
@@ -112,7 +111,7 @@ func (in *DockerClusterSpec) DeepCopyInto(out *DockerClusterSpec) {
 			(*out)[key] = *val.DeepCopy()
 		}
 	}
-	in.LoadBalancer.DeepCopyInto(&out.LoadBalancer)
+	out.LoadBalancer = in.LoadBalancer
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DockerClusterSpec.
@@ -249,11 +248,6 @@ func (in *DockerClusterTemplateSpec) DeepCopy() *DockerClusterTemplateSpec {
 func (in *DockerLoadBalancer) DeepCopyInto(out *DockerLoadBalancer) {
 	*out = *in
 	out.ImageMeta = in.ImageMeta
-	if in.CustomHAProxyConfigTemplateRef != nil {
-		in, out := &in.CustomHAProxyConfigTemplateRef, &out.CustomHAProxyConfigTemplateRef
-		*out = new(v1.LocalObjectReference)
-		**out = **in
-	}
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DockerLoadBalancer.
diff --git a/test/infrastructure/docker/config/crd/bases/infrastructure.cluster.x-k8s.io_dockerclusters.yaml b/test/infrastructure/docker/config/crd/bases/infrastructure.cluster.x-k8s.io_dockerclusters.yaml
index 6bf6baa38..fb3c07828 100644
--- a/test/infrastructure/docker/config/crd/bases/infrastructure.cluster.x-k8s.io_dockerclusters.yaml
+++ b/test/infrastructure/docker/config/crd/bases/infrastructure.cluster.x-k8s.io_dockerclusters.yaml
@@ -385,31 +385,6 @@ spec:
                 description: LoadBalancer allows defining configurations for the cluster
                   load balancer.
                 properties:
-                  customHAProxyConfigTemplateRef:
-                    description: 'CustomHAProxyConfigTemplateRef allows you to replace
-                      the default HAProxy config file. This field is a reference to
-                      a config map that contains the configuration template. The key
-                      of the config map should be equal to ''value''. The content
-                      of the config map will be processed and will replace the default
-                      HAProxy config file. Please use it with caution, as there are
-                      no checks to ensure the validity of the configuration. This
-                      template will support the following variables that will be passed
-                      by the controller: $IPv6 (bool) indicates if the cluster is
-                      IPv6, $FrontendControlPlanePort (string) indicates the frontend
-                      control plane port, $BackendControlPlanePort (string) indicates
-                      the backend control plane port, $BackendServers (map[string]string)
-                      indicates the backend server where the key is the server name
-                      and the value is the address. This map is dynamic and is updated
-                      every time a new control plane node is added or removed. The
-                      template will also support the JoinHostPort function to join
-                      the host and port of the backend server.'
-                    properties:
-                      name:
-                        description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
-                          TODO: Add other useful fields. apiVersion, kind, uid?'
-                        type: string
-                    type: object
-                    x-kubernetes-map-type: atomic
                   imageRepository:
                     description: ImageRepository sets the container registry to pull
                       the haproxy image from. if not set, "kindest" will be used instead.
diff --git a/test/infrastructure/docker/config/crd/bases/infrastructure.cluster.x-k8s.io_dockerclustertemplates.yaml b/test/infrastructure/docker/config/crd/bases/infrastructure.cluster.x-k8s.io_dockerclustertemplates.yaml
index 5d293e445..2e87c6507 100644
--- a/test/infrastructure/docker/config/crd/bases/infrastructure.cluster.x-k8s.io_dockerclustertemplates.yaml
+++ b/test/infrastructure/docker/config/crd/bases/infrastructure.cluster.x-k8s.io_dockerclustertemplates.yaml
@@ -216,34 +216,6 @@ spec:
                         description: LoadBalancer allows defining configurations for
                           the cluster load balancer.
                         properties:
-                          customHAProxyConfigTemplateRef:
-                            description: 'CustomHAProxyConfigTemplateRef allows you
-                              to replace the default HAProxy config file. This field
-                              is a reference to a config map that contains the configuration
-                              template. The key of the config map should be equal
-                              to ''value''. The content of the config map will be
-                              processed and will replace the default HAProxy config
-                              file. Please use it with caution, as there are no checks
-                              to ensure the validity of the configuration. This template
-                              will support the following variables that will be passed
-                              by the controller: $IPv6 (bool) indicates if the cluster
-                              is IPv6, $FrontendControlPlanePort (string) indicates
-                              the frontend control plane port, $BackendControlPlanePort
-                              (string) indicates the backend control plane port, $BackendServers
-                              (map[string]string) indicates the backend server where
-                              the key is the server name and the value is the address.
-                              This map is dynamic and is updated every time a new
-                              control plane node is added or removed. The template
-                              will also support the JoinHostPort function to join
-                              the host and port of the backend server.'
-                            properties:
-                              name:
-                                description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
-                                  TODO: Add other useful fields. apiVersion, kind,
-                                  uid?'
-                                type: string
-                            type: object
-                            x-kubernetes-map-type: atomic
                           imageRepository:
                             description: ImageRepository sets the container registry
                               to pull the haproxy image from. if not set, "kindest"
diff --git a/test/infrastructure/docker/internal/controllers/dockermachine_controller.go b/test/infrastructure/docker/internal/controllers/dockermachine_controller.go
index 4d3a2cf35..8936020eb 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"
-	"k8s.io/apimachinery/pkg/types"
 	"k8s.io/klog/v2"
 	ctrl "sigs.k8s.io/controller-runtime"
 	"sigs.k8s.io/controller-runtime/pkg/builder"
@@ -168,11 +167,11 @@ func (r *DockerMachineReconciler) Reconcile(ctx context.Context, req ctrl.Reques
 
 	// Handle deleted machines
 	if !dockerMachine.ObjectMeta.DeletionTimestamp.IsZero() {
-		return ctrl.Result{}, r.reconcileDelete(ctx, dockerCluster, machine, dockerMachine, externalMachine, externalLoadBalancer)
+		return ctrl.Result{}, r.reconcileDelete(ctx, machine, dockerMachine, externalMachine, externalLoadBalancer)
 	}
 
 	// Handle non-deleted machines
-	res, err := r.reconcileNormal(ctx, cluster, dockerCluster, machine, dockerMachine, externalMachine, externalLoadBalancer)
+	res, err := r.reconcileNormal(ctx, cluster, machine, dockerMachine, externalMachine, externalLoadBalancer)
 	// Requeue if the reconcile failed because the ClusterCacheTracker was locked for
 	// the current cluster because of concurrent access.
 	if errors.Is(err, remote.ErrClusterLocked) {
@@ -205,7 +204,7 @@ func patchDockerMachine(ctx context.Context, patchHelper *patch.Helper, dockerMa
 	)
 }
 
-func (r *DockerMachineReconciler) reconcileNormal(ctx context.Context, cluster *clusterv1.Cluster, dockerCluster *infrav1.DockerCluster, machine *clusterv1.Machine, dockerMachine *infrav1.DockerMachine, externalMachine *docker.Machine, externalLoadBalancer *docker.LoadBalancer) (res ctrl.Result, retErr error) {
+func (r *DockerMachineReconciler) reconcileNormal(ctx context.Context, cluster *clusterv1.Cluster, machine *clusterv1.Machine, dockerMachine *infrav1.DockerMachine, externalMachine *docker.Machine, externalLoadBalancer *docker.LoadBalancer) (res ctrl.Result, retErr error) {
 	log := ctrl.LoggerFrom(ctx)
 
 	// Check if the infrastructure is ready, otherwise return and wait for the cluster object to be updated
@@ -272,11 +271,7 @@ func (r *DockerMachineReconciler) reconcileNormal(ctx context.Context, cluster *
 	// we should only do this once, as reconfiguration more or less ensures
 	// node ref setting fails
 	if util.IsControlPlaneMachine(machine) && !dockerMachine.Status.LoadBalancerConfigured {
-		unsafeLoadBalancerConfigTemplate, err := r.getUnsafeLoadBalancerConfigTemplate(ctx, dockerCluster)
-		if err != nil {
-			return ctrl.Result{}, errors.Wrap(err, "failed to retrieve HAProxy configuration from CustomHAProxyConfigTemplateRef")
-		}
-		if err := externalLoadBalancer.UpdateConfiguration(ctx, unsafeLoadBalancerConfigTemplate); err != nil {
+		if err := externalLoadBalancer.UpdateConfiguration(ctx); err != nil {
 			return ctrl.Result{}, errors.Wrap(err, "failed to update DockerCluster.loadbalancer configuration")
 		}
 		dockerMachine.Status.LoadBalancerConfigured = true
@@ -395,7 +390,7 @@ func (r *DockerMachineReconciler) reconcileNormal(ctx context.Context, cluster *
 	return ctrl.Result{}, nil
 }
 
-func (r *DockerMachineReconciler) reconcileDelete(ctx context.Context, dockerCluster *infrav1.DockerCluster, machine *clusterv1.Machine, dockerMachine *infrav1.DockerMachine, externalMachine *docker.Machine, externalLoadBalancer *docker.LoadBalancer) error {
+func (r *DockerMachineReconciler) reconcileDelete(ctx context.Context, machine *clusterv1.Machine, dockerMachine *infrav1.DockerMachine, externalMachine *docker.Machine, externalLoadBalancer *docker.LoadBalancer) error {
 	// Set the ContainerProvisionedCondition reporting delete is started, and issue a patch in order to make
 	// this visible to the users.
 	// NB. The operation in docker is fast, so there is the chance the user will not notice the status change;
@@ -416,11 +411,7 @@ func (r *DockerMachineReconciler) reconcileDelete(ctx context.Context, dockerClu
 
 	// if the deleted machine is a control-plane node, remove it from the load balancer configuration;
 	if util.IsControlPlaneMachine(machine) {
-		unsafeLoadBalancerConfigTemplate, err := r.getUnsafeLoadBalancerConfigTemplate(ctx, dockerCluster)
-		if err != nil {
-			return errors.Wrap(err, "failed to retrieve HAProxy configuration from CustomHAProxyConfigTemplateRef")
-		}
-		if err := externalLoadBalancer.UpdateConfiguration(ctx, unsafeLoadBalancerConfigTemplate); err != nil {
+		if err := externalLoadBalancer.UpdateConfiguration(ctx); err != nil {
 			return errors.Wrap(err, "failed to update DockerCluster.loadbalancer configuration")
 		}
 	}
@@ -519,25 +510,6 @@ func (r *DockerMachineReconciler) getBootstrapData(ctx context.Context, machine
 	return base64.StdEncoding.EncodeToString(value), bootstrapv1.Format(format), nil
 }
 
-func (r *DockerMachineReconciler) getUnsafeLoadBalancerConfigTemplate(ctx context.Context, dockerCluster *infrav1.DockerCluster) (string, error) {
-	if dockerCluster.Spec.LoadBalancer.CustomHAProxyConfigTemplateRef == nil {
-		return "", nil
-	}
-	var cm *corev1.ConfigMap
-	key := types.NamespacedName{
-		Name:      dockerCluster.Spec.LoadBalancer.CustomHAProxyConfigTemplateRef.Name,
-		Namespace: dockerCluster.Namespace,
-	}
-	if err := r.Get(ctx, key, cm); err != nil {
-		return "", errors.Wrapf(err, "failed to retrieve custom HAProxy configuration ConfigMap %s", key)
-	}
-	template, ok := cm.Data["value"]
-	if !ok {
-		return "", fmt.Errorf("expected key \"value\" to exist in ConfigMap %s", key)
-	}
-	return template, nil
-}
-
 // setMachineAddress gets the address from the container corresponding to a docker node and sets it on the Machine object.
 func setMachineAddress(ctx context.Context, dockerMachine *infrav1.DockerMachine, externalMachine *docker.Machine) error {
 	machineAddresses, err := externalMachine.Address(ctx)
diff --git a/test/infrastructure/docker/internal/docker/loadbalancer.go b/test/infrastructure/docker/internal/docker/loadbalancer.go
index b590b1b6c..2126165b1 100644
--- a/test/infrastructure/docker/internal/docker/loadbalancer.go
+++ b/test/infrastructure/docker/internal/docker/loadbalancer.go
@@ -19,7 +19,7 @@ package docker
 import (
 	"context"
 	"fmt"
-	"strconv"
+	"net"
 
 	"github.com/pkg/errors"
 	ctrl "sigs.k8s.io/controller-runtime"
@@ -38,13 +38,12 @@ type lbCreator interface {
 
 // LoadBalancer manages the load balancer for a specific docker cluster.
 type LoadBalancer struct {
-	name                     string
-	image                    string
-	container                *types.Node
-	ipFamily                 clusterv1.ClusterIPFamily
-	lbCreator                lbCreator
-	backendControlPlanePort  string
-	frontendControlPlanePort string
+	name             string
+	image            string
+	container        *types.Node
+	ipFamily         clusterv1.ClusterIPFamily
+	lbCreator        lbCreator
+	controlPlanePort int
 }
 
 // NewLoadBalancer returns a new helper for managing a docker loadbalancer with a given name.
@@ -73,13 +72,12 @@ func NewLoadBalancer(ctx context.Context, cluster *clusterv1.Cluster, dockerClus
 	image := getLoadBalancerImage(dockerCluster)
 
 	return &LoadBalancer{
-		name:                     cluster.Name,
-		image:                    image,
-		container:                container,
-		ipFamily:                 ipFamily,
-		lbCreator:                &Manager{},
-		frontendControlPlanePort: strconv.Itoa(dockerCluster.Spec.ControlPlaneEndpoint.Port),
-		backendControlPlanePort:  "6443",
+		name:             cluster.Name,
+		image:            image,
+		container:        container,
+		ipFamily:         ipFamily,
+		lbCreator:        &Manager{},
+		controlPlanePort: dockerCluster.Spec.ControlPlaneEndpoint.Port,
 	}, nil
 }
 
@@ -139,7 +137,7 @@ func (s *LoadBalancer) Create(ctx context.Context) error {
 }
 
 // UpdateConfiguration updates the external load balancer configuration with new control plane nodes.
-func (s *LoadBalancer) UpdateConfiguration(ctx context.Context, unsafeLoadBalancerConfig string) error {
+func (s *LoadBalancer) UpdateConfiguration(ctx context.Context) error {
 	log := ctrl.LoggerFrom(ctx)
 
 	if s.container == nil {
@@ -163,25 +161,17 @@ func (s *LoadBalancer) UpdateConfiguration(ctx context.Context, unsafeLoadBalanc
 			return errors.Wrapf(err, "failed to get IP for container %s", n.String())
 		}
 		if s.ipFamily == clusterv1.IPv6IPFamily {
-			backendServers[n.String()] = controlPlaneIPv6
+			backendServers[n.String()] = net.JoinHostPort(controlPlaneIPv6, "6443")
 		} else {
-			backendServers[n.String()] = controlPlaneIPv4
+			backendServers[n.String()] = net.JoinHostPort(controlPlaneIPv4, "6443")
 		}
 	}
 
-	loadBalancerConfigTemplate := loadbalancer.DefaultTemplate
-	if unsafeLoadBalancerConfig != "" {
-		loadBalancerConfigTemplate = unsafeLoadBalancerConfig
-	}
-
 	loadBalancerConfig, err := loadbalancer.Config(&loadbalancer.ConfigData{
-		FrontendControlPlanePort: s.frontendControlPlanePort,
-		BackendControlPlanePort:  s.backendControlPlanePort,
-		BackendServers:           backendServers,
-		IPv6:                     s.ipFamily == clusterv1.IPv6IPFamily,
-	},
-		loadBalancerConfigTemplate,
-	)
+		ControlPlanePort: s.controlPlanePort,
+		BackendServers:   backendServers,
+		IPv6:             s.ipFamily == clusterv1.IPv6IPFamily,
+	})
 	if err != nil {
 		return errors.WithStack(err)
 	}
diff --git a/test/infrastructure/docker/internal/loadbalancer/config.go b/test/infrastructure/docker/internal/loadbalancer/config.go
index 0e31f2a02..8d2f70a02 100644
--- a/test/infrastructure/docker/internal/loadbalancer/config.go
+++ b/test/infrastructure/docker/internal/loadbalancer/config.go
@@ -18,7 +18,6 @@ package loadbalancer
 
 import (
 	"bytes"
-	"net"
 	"text/template"
 
 	"sigs.k8s.io/kind/pkg/errors"
@@ -26,14 +25,13 @@ import (
 
 // ConfigData is supplied to the loadbalancer config template.
 type ConfigData struct {
-	FrontendControlPlanePort string
-	BackendControlPlanePort  string
-	BackendServers           map[string]string
-	IPv6                     bool
+	ControlPlanePort int
+	BackendServers   map[string]string
+	IPv6             bool
 }
 
-// DefaultTemplate is the loadbalancer config template.
-const DefaultTemplate = `# generated by kind
+// ConfigTemplate is the loadbalancer config template.
+const ConfigTemplate = `# generated by kind
 global
   log /dev/log local0
   log /dev/log local1 notice
@@ -57,9 +55,9 @@ defaults
   default-server init-addr none
 
 frontend control-plane
-  bind *:{{ .FrontendControlPlanePort }}
+  bind *:{{ .ControlPlanePort }}
   {{ if .IPv6 -}}
-  bind :::{{ .FrontendControlPlanePort }};
+  bind :::{{ .ControlPlanePort }};
   {{- end }}
   default_backend kube-apiservers
 
@@ -67,15 +65,13 @@ backend kube-apiservers
   option httpchk GET /healthz
   # TODO: we should be verifying (!)
   {{range $server, $address := .BackendServers}}
-  server {{ $server }} {{ JoinHostPort $address $.BackendControlPlanePort }} check check-ssl verify none resolvers docker resolve-prefer {{ if $.IPv6 -}} ipv6 {{- else -}} ipv4 {{- end }}
+  server {{ $server }} {{ $address }} check check-ssl verify none resolvers docker resolve-prefer {{ if $.IPv6 -}} ipv6 {{- else -}} ipv4 {{- end }}
   {{- end}}
 `
 
 // Config generates the loadbalancer config from the ConfigTemplate and ConfigData.
-func Config(data *ConfigData, configTemplate string) (config string, err error) {
-	t, err := template.New("loadbalancer-config").Funcs(template.FuncMap{
-		"JoinHostPort": net.JoinHostPort,
-	}).Parse(configTemplate)
+func Config(data *ConfigData) (config string, err error) {
+	t, err := template.New("loadbalancer-config").Parse(ConfigTemplate)
 	if err != nil {
 		return "", errors.Wrap(err, "failed to parse config template")
 	}
diff --git a/test/infrastructure/docker/internal/loadbalancer/config_test.go b/test/infrastructure/docker/internal/loadbalancer/config_test.go
deleted file mode 100644
index 460b6d915..000000000
--- a/test/infrastructure/docker/internal/loadbalancer/config_test.go
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
-Copyright 2023 The Kubernetes Authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package loadbalancer
-
-import (
-	"testing"
-
-	. "github.com/onsi/gomega"
-)
-
-func TestConfig(t *testing.T) {
-	g := NewWithT(t)
-
-	testCases := []struct {
-		name           string
-		data           *ConfigData
-		configTemplate string
-		expectedConfig string
-	}{
-		{
-			name: "should return default HA proxy config",
-			data: &ConfigData{
-				BackendControlPlanePort:  "6443",
-				FrontendControlPlanePort: "7777",
-				BackendServers: map[string]string{
-					"control-plane-0": "1.1.1.1",
-				},
-			},
-			configTemplate: DefaultTemplate,
-			expectedConfig: `# generated by kind
-global
-  log /dev/log local0
-  log /dev/log local1 notice
-  daemon
-  # limit memory usage to approximately 18 MB
-  # (see https://github.com/kubernetes-sigs/kind/pull/3115)
-  maxconn 100000
-
-resolvers docker
-  nameserver dns 127.0.0.11:53
-
-defaults
-  log global
-  mode tcp
-  option dontlognull
-  # TODO: tune these
-  timeout connect 5000
-  timeout client 50000
-  timeout server 50000
-  # allow to boot despite dns don't resolve backends
-  default-server init-addr none
-
-frontend control-plane
-  bind *:7777
-  
-  default_backend kube-apiservers
-
-backend kube-apiservers
-  option httpchk GET /healthz
-  # TODO: we should be verifying (!)
-  
-  server control-plane-0 1.1.1.1:6443 check check-ssl verify none resolvers docker resolve-prefer ipv4
-`,
-		},
-		{
-			name: "should return a custom HA config",
-			data: &ConfigData{
-				FrontendControlPlanePort: "7777",
-				BackendControlPlanePort:  "6443",
-				BackendServers: map[string]string{
-					"control-plane-0": "1.1.1.1",
-				},
-			},
-			configTemplate: `# generated by kind
-global
-  log /dev/log local0
-  log /dev/log local1 notice
-  daemon
-  # limit memory usage to approximately 18 MB
-  # (see https://github.com/kubernetes-sigs/kind/pull/3115)
-  maxconn 100000
-
-resolvers docker
-  nameserver dns 127.0.0.11:53
-
-defaults
-  log global
-  mode tcp
-  option dontlognull
-  # TODO: tune these
-  timeout connect 5000
-  timeout client 50000
-  timeout server 50000
-  # allow to boot despite dns don't resolve backends
-  default-server init-addr none
-
-frontend control-plane
-  bind *:{{ .FrontendControlPlanePort }}
-  {{ if .IPv6 -}}
-  bind :::{{ .FrontendControlPlanePort }};
-  {{- end }}
-  default_backend kube-apiservers
-
-backend kube-apiservers
-  option httpchk GET /healthz
-  # TODO: we should be verifying (!)
-  {{range $server, $address := .BackendServers}}
-  server {{ $server }} {{ JoinHostPort $address $.BackendControlPlanePort }} check check-ssl verify none resolvers docker resolve-prefer {{ if $.IPv6 -}} ipv6 {{- else -}} ipv4 {{- end }}
-  {{- end}}
-
-frontend rke2-join
-  bind *:9345
-  {{ if .IPv6 -}}
-  bind :::9345;
-  {{- end }}
-  default_backend rke2-servers
-backend rke2-servers
-  option httpchk GET /v1-rke2/readyz
-  http-check expect status 403
-  {{range $server, $address := .BackendServers}}
-  server {{ $server }} {{ $address }}:9345 check check-ssl verify none resolvers docker resolve-prefer {{ if $.IPv6 -}} ipv6 {{- else -}} ipv4 {{- end }}
-  {{- end}}
-`,
-			expectedConfig: `# generated by kind
-global
-  log /dev/log local0
-  log /dev/log local1 notice
-  daemon
-  # limit memory usage to approximately 18 MB
-  # (see https://github.com/kubernetes-sigs/kind/pull/3115)
-  maxconn 100000
-
-resolvers docker
-  nameserver dns 127.0.0.11:53
-
-defaults
-  log global
-  mode tcp
-  option dontlognull
-  # TODO: tune these
-  timeout connect 5000
-  timeout client 50000
-  timeout server 50000
-  # allow to boot despite dns don't resolve backends
-  default-server init-addr none
-
-frontend control-plane
-  bind *:7777
-  
-  default_backend kube-apiservers
-
-backend kube-apiservers
-  option httpchk GET /healthz
-  # TODO: we should be verifying (!)
-  
-  server control-plane-0 1.1.1.1:6443 check check-ssl verify none resolvers docker resolve-prefer ipv4
-
-frontend rke2-join
-  bind *:9345
-  
-  default_backend rke2-servers
-backend rke2-servers
-  option httpchk GET /v1-rke2/readyz
-  http-check expect status 403
-  
-  server control-plane-0 1.1.1.1:9345 check check-ssl verify none resolvers docker resolve-prefer ipv4
-`,
-		},
-	}
-
-	for _, tc := range testCases {
-		t.Run(tc.name, func(t *testing.T) {
-			config, err := Config(tc.data, tc.configTemplate)
-			g.Expect(err).NotTo(HaveOccurred())
-			g.Expect(config).To(Equal(tc.expectedConfig))
-		})
-	}
-}