Repository: kubernetes-sigs/cluster-api
Pull request #6693 (2022-06-21 14:46:59)

🌱 RuntimeSDK: improve error wrapping and godoc
<!-- please add a icon to the title of this PR (see https://sigs.k8s.io/cluster-api/VERSIONING.md), 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**:

WIP => WIP (only done with some of the packages)

**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:
To complete the [pull request process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process), please ask for approval from **sbueringer** after the PR has been reviewed.

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

Patch:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 3fcf1408c..0c53ea0cd 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -134,7 +134,6 @@ Cluster API maintains the most recent release branch for all supported API and c
         - 🐛 (`:bug:`, patch and bugfixes)
         - 📖 (`:book:`, documentation or proposals)
         - 🌱 (`:seedling:`, minor or other)
-1. If your PR has multiple commits, you must [squash them into a single commit](https://kubernetes.io/docs/contribute/new-content/open-a-pr/#squashing-commits) before merging your PR.
 
 Individual commits should not be tagged separately, but will generally be
 assumed to match the PR. For instance, if you have a bugfix in with
diff --git a/api/v1beta1/clusterclass_types.go b/api/v1beta1/clusterclass_types.go
index 8a5a53f3c..500d2fb86 100644
--- a/api/v1beta1/clusterclass_types.go
+++ b/api/v1beta1/clusterclass_types.go
@@ -336,12 +336,14 @@ type ClusterClassPatch struct {
 	// +optional
 	EnabledIf *string `json:"enabledIf,omitempty"`
 
-	// Definitions define the patches inline.
+	// Definitions define inline patches.
 	// Note: Patches will be applied in the order of the array.
+	// Note: Exactly one of Definitions or External must be set.
 	// +optional
 	Definitions []PatchDefinition `json:"definitions,omitempty"`
 
 	// External defines an external patch.
+	// Note: Exactly one of Definitions or External must be set.
 	// +optional
 	External *ExternalPatchDefinition `json:"external,omitempty"`
 }
@@ -447,6 +449,7 @@ type JSONPatchValue struct {
 }
 
 // ExternalPatchDefinition defines an external patch.
+// Note: At least one of GenerateExtension or ValidateExtension must be set.
 type ExternalPatchDefinition struct {
 	// GenerateExtension references an extension which is called to generate patches.
 	// +optional
diff --git a/api/v1beta1/zz_generated.openapi.go b/api/v1beta1/zz_generated.openapi.go
index 885c378af..01c418b08 100644
--- a/api/v1beta1/zz_generated.openapi.go
+++ b/api/v1beta1/zz_generated.openapi.go
@@ -317,7 +317,7 @@ func schema_sigsk8sio_cluster_api_api_v1beta1_ClusterClassPatch(ref common.Refer
 					},
 					"definitions": {
 						SchemaProps: spec.SchemaProps{
-							Description: "Definitions define the patches inline. Note: Patches will be applied in the order of the array.",
+							Description: "Definitions define inline patches. Note: Patches will be applied in the order of the array. Note: Exactly one of Definitions or External must be set.",
 							Type:        []string{"array"},
 							Items: &spec.SchemaOrArray{
 								Schema: &spec.Schema{
@@ -331,7 +331,7 @@ func schema_sigsk8sio_cluster_api_api_v1beta1_ClusterClassPatch(ref common.Refer
 					},
 					"external": {
 						SchemaProps: spec.SchemaProps{
-							Description: "External defines an external patch.",
+							Description: "External defines an external patch. Note: Exactly one of Definitions or External must be set.",
 							Ref:         ref("sigs.k8s.io/cluster-api/api/v1beta1.ExternalPatchDefinition"),
 						},
 					},
@@ -849,7 +849,7 @@ func schema_sigsk8sio_cluster_api_api_v1beta1_ExternalPatchDefinition(ref common
 	return common.OpenAPIDefinition{
 		Schema: spec.Schema{
 			SchemaProps: spec.SchemaProps{
-				Description: "ExternalPatchDefinition defines an external patch.",
+				Description: "ExternalPatchDefinition defines an external patch. Note: At least one of GenerateExtension or ValidateExtension must be set.",
 				Type:        []string{"object"},
 				Properties: map[string]spec.Schema{
 					"generateExtension": {
diff --git a/cmd/clusterctl/client/config/cert_manager_client.go b/cmd/clusterctl/client/config/cert_manager_client.go
index ae71a50d4..65ecd0e9d 100644
--- a/cmd/clusterctl/client/config/cert_manager_client.go
+++ b/cmd/clusterctl/client/config/cert_manager_client.go
@@ -27,7 +27,7 @@ const (
 	CertManagerConfigKey = "cert-manager"
 
 	// CertManagerDefaultVersion defines the default cert-manager version to be used by clusterctl.
-	CertManagerDefaultVersion = "v1.8.2"
+	CertManagerDefaultVersion = "v1.7.2"
 
 	// CertManagerDefaultURL defines the default cert-manager repository url to be used by clusterctl.
 	// NOTE: At runtime /latest will be replaced with the CertManagerDefaultVersion or with the
diff --git a/config/crd/bases/cluster.x-k8s.io_clusterclasses.yaml b/config/crd/bases/cluster.x-k8s.io_clusterclasses.yaml
index 9a8657777..e00aaa0f1 100644
--- a/config/crd/bases/cluster.x-k8s.io_clusterclasses.yaml
+++ b/config/crd/bases/cluster.x-k8s.io_clusterclasses.yaml
@@ -672,8 +672,9 @@ spec:
                     to customize the referenced templates.
                   properties:
                     definitions:
-                      description: 'Definitions define the patches inline. Note: Patches
-                        will be applied in the order of the array.'
+                      description: 'Definitions define inline patches. Note: Patches
+                        will be applied in the order of the array. Note: Exactly one
+                        of Definitions or External must be set.'
                       items:
                         description: PatchDefinition defines a patch which is applied
                           to customize the referenced templates.
@@ -795,7 +796,8 @@ spec:
                         be enabled per default.
                       type: string
                     external:
-                      description: External defines an external patch.
+                      description: 'External defines an external patch. Note: Exactly
+                        one of Definitions or External must be set.'
                       properties:
                         generateExtension:
                           description: GenerateExtension references an extension which
diff --git a/config/crd/bases/runtime.cluster.x-k8s.io_extensionconfigs.yaml b/config/crd/bases/runtime.cluster.x-k8s.io_extensionconfigs.yaml
index 4a063b488..56c1c83a0 100644
--- a/config/crd/bases/runtime.cluster.x-k8s.io_extensionconfigs.yaml
+++ b/config/crd/bases/runtime.cluster.x-k8s.io_extensionconfigs.yaml
@@ -45,18 +45,19 @@ spec:
             description: ExtensionConfigSpec is the desired state of the ExtensionConfig
             properties:
               clientConfig:
-                description: ClientConfig defines how to communicate with ExtensionHandlers.
+                description: ClientConfig defines how to communicate with the Extension
+                  server.
                 properties:
                   caBundle:
                     description: CABundle is a PEM encoded CA bundle which will be
-                      used to validate the ExtensionHandler's server certificate.
+                      used to validate the Extension server's server certificate.
                     format: byte
                     type: string
                   service:
                     description: "Service is a reference to the Kubernetes service
-                      for the ExtensionHandler. Either `service` or `url` must be
-                      specified. \n If the ExtensionHandler is running within a cluster,
-                      then you should use `service`."
+                      for the Extension server. Note: Exactly one of `url` or `service`
+                      must be specified. \n If the Extension server is running within
+                      a cluster, then you should use `service`."
                     properties:
                       name:
                         description: Name is the name of the service.
@@ -65,13 +66,13 @@ spec:
                         description: Namespace is the namespace of the service.
                         type: string
                       path:
-                        description: Path is an optional URL path which will be sent
-                          in any request to this service. If a path is set it will
-                          be used as prefix and the hook-specific path will be appended.
+                        description: Path is an optional URL path and if present may
+                          be any string permissible in a URL. If a path is set it
+                          will be used as prefix to the hook-specific path.
                         type: string
                       port:
                         description: Port is the port on the service that's hosting
-                          the ExtensionHandler. Default to 443. `port` should be a
+                          the Extension server. Defaults to 443. Port should be a
                           valid port number (1-65535, inclusive).
                         format: int32
                         type: integer
@@ -80,15 +81,14 @@ spec:
                     - namespace
                     type: object
                   url:
-                    description: "URL gives the location of the ExtensionHandler,
-                      in standard URL form (`scheme://host:port/path`). Exactly one
-                      of `url` or `service` must be specified. \n The `host` should
-                      not refer to a service running in the cluster; use the `service`
-                      field instead. \n The scheme should be \"https\"; the URL should
-                      begin with \"https://\". \n A path is optional, and if present
-                      may be any string permissible in a URL. If a path is set it
-                      will be used as prefix and the hook-specific path will be appended.
-                      \n Attempting to use a user or basic auth e.g. \"user:password@\"
+                    description: "URL gives the location of the Extension server,
+                      in standard URL form (`scheme://host:port/path`). Note: Exactly
+                      one of `url` or `service` must be specified. \n The scheme must
+                      be \"https\". \n The `host` should not refer to a service running
+                      in the cluster; use the `service` field instead. \n A path is
+                      optional, and if present may be any string permissible in a
+                      URL. If a path is set it will be used as prefix to the hook-specific
+                      path. \n Attempting to use a user or basic auth e.g. \"user:password@\"
                       is not allowed. Fragments (\"#...\") and query parameters (\"?...\")
                       are not allowed either."
                     type: string
@@ -96,8 +96,8 @@ spec:
               namespaceSelector:
                 description: NamespaceSelector decides whether to call the hook for
                   an object based on whether the namespace for that object matches
-                  the selector. Default to the empty LabelSelector, which matches
-                  everything.
+                  the selector. Defaults to the empty LabelSelector, which matches
+                  all objects.
                 properties:
                   matchExpressions:
                     description: matchExpressions is a list of label selector requirements.
@@ -211,7 +211,8 @@ spec:
                         which this ExtensionHandler serves.
                       properties:
                         apiVersion:
-                          description: APIVersion is the Version of the Hook.
+                          description: APIVersion is the group and version of the
+                            Hook.
                           type: string
                         hook:
                           description: Hook is the name of the hook.
@@ -222,7 +223,8 @@ spec:
                       type: object
                     timeoutSeconds:
                       description: TimeoutSeconds defines the timeout duration for
-                        client calls to the ExtensionHandler.
+                        client calls to the ExtensionHandler. Defaults to 10 is not
+                        set.
                       format: int32
                       type: integer
                   required:
diff --git a/docs/book/src/SUMMARY.md b/docs/book/src/SUMMARY.md
index 55558523e..d333814ba 100644
--- a/docs/book/src/SUMMARY.md
+++ b/docs/book/src/SUMMARY.md
@@ -68,7 +68,6 @@
           - [v0.4 to v1.0](./developer/providers/v0.4-to-v1.0.md)
           - [v1.0 to v1.1](./developer/providers/v1.0-to-v1.1.md)
           - [v1.1 to v1.2](./developer/providers/v1.1-to-v1.2.md)
-          - [v1.2 to v1.3](./developer/providers/v1.2-to-v1.3.md)
         - [Provider contracts](./developer/providers/contracts.md)
           - [Cluster Infrastructure](./developer/providers/cluster-infrastructure.md)
           - [Machine Infrastructure](./developer/providers/machine-infrastructure.md)
diff --git a/docs/book/src/clusterctl/commands/init.md b/docs/book/src/clusterctl/commands/init.md
index 8acb136ea..3ac988db3 100644
--- a/docs/book/src/clusterctl/commands/init.md
+++ b/docs/book/src/clusterctl/commands/init.md
@@ -182,7 +182,7 @@ If this happens, there are no guarantees about the proper functioning of `cluste
 Cluster API providers require a cert-manager version supporting the `cert-manager.io/v1` API to be installed in the cluster.
 
 While doing init, clusterctl checks if there is a version of cert-manager already installed. If not, clusterctl will 
-install a default version (currently cert-manager v1.8.2). See [clusterctl configuration](../configuration.md) for
+install a default version (currently cert-manager v1.7.2). See [clusterctl configuration](../configuration.md) for
 available options to customize this operation.
 
 <aside class="note warning">
diff --git a/docs/book/src/clusterctl/configuration.md b/docs/book/src/clusterctl/configuration.md
index e60fcacdb..5f37d053c 100644
--- a/docs/book/src/clusterctl/configuration.md
+++ b/docs/book/src/clusterctl/configuration.md
@@ -97,14 +97,6 @@ Please note that the configuration above will be considered also when doing `clu
 
 ## Overrides Layer
 
-<aside class="note warning">
-
-<h1> Warning! </h1>
-
-Overrides only provide file replacements; instead, provider version resolution is based only on the actual repository structure.
-
-</aside>
-
 `clusterctl` uses an overrides layer to read in injected provider components,
 cluster templates and metadata. By default, it reads the files from
 `$HOME/.cluster-api/overrides`.
diff --git a/docs/book/src/developer/guide.md b/docs/book/src/developer/guide.md
index e8e00a320..4bbdab866 100644
--- a/docs/book/src/developer/guide.md
+++ b/docs/book/src/developer/guide.md
@@ -81,7 +81,7 @@ The generated binary can be found at ./hack/tools/bin/envsubst
 You'll need to deploy [cert-manager] components on your [management cluster][mcluster], using `kubectl`
 
 ```bash
-kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.8.2/cert-manager.yaml
+kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.7.2/cert-manager.yaml
 ```
 
 Ensure the cert-manager webhook service is ready before creating the Cluster API components.
diff --git a/docs/book/src/developer/providers/v1.2-to-v1.3.md b/docs/book/src/developer/providers/v1.2-to-v1.3.md
deleted file mode 100644
index f94d30ae7..000000000
--- a/docs/book/src/developer/providers/v1.2-to-v1.3.md
+++ /dev/null
@@ -1,33 +0,0 @@
-# Cluster API v1.2 compared to v1.3
-
-This document provides an overview over relevant changes between Cluster API v1.2 and v1.3 for
-maintainers of providers and consumers of our Go API.
-
-## Minimum Go version
-
-* The Go version used by Cluster API is still Go 1.18.x
-
-## Dependencies
-
-**Note**: Only the most relevant dependencies are listed, `k8s.io/` and `ginkgo`/`gomega` dependencies
-in Cluster API are kept in sync with the versions used by `sigs.k8s.io/controller-runtime`. 
-
--
-
-## Changes by Kind
-
-### Deprecation
-
--
-
-### Removals
-
--
-
-### API Changes
-
--
-
-### Other
-
--
\ No newline at end of file
diff --git a/docs/book/src/developer/providers/webhooks.md b/docs/book/src/developer/providers/webhooks.md
index 264cac1ef..deedcd53f 100644
--- a/docs/book/src/developer/providers/webhooks.md
+++ b/docs/book/src/developer/providers/webhooks.md
@@ -11,7 +11,7 @@ Defaulting webhooks are an implementation of a [Kubernetes mutating webhook](htt
 ## Conversion webhooks
 Conversion webhooks are what allow Cluster API to work with multiple API types without requiring different versions. It does this by converting the incoming version to a `Hub` version which is used internally by the controllers. To read more about conversion see the [Kubebuilder documentation](https://book.kubebuilder.io/multiversion-tutorial/conversion.html)
 
-For a walkthrough on implementing conversion webhooks see the video in the [Developer Guide](../guide.md#videos-explaining-capi-architecture-and-code-walkthroughs)
+For a walkthrough on implementing conversion webhooks see the video in the [Developer Guide](https://cluster-api.sigs.k8s.io/developer/guide.html?highlight=video#videos-explaining-capi-architecture-and-code-walkthroughs)
 
 ## Implementing webhooks with Controller Runtime and Kubebuilder
 The webhooks in Cluster API are offered through tools in Controller Runtime and Kubebuilder. The webhooks implement interfaces defined in Controller Runtime, while generation of manifests can be done using Kubebuilder.
diff --git a/docs/book/src/introduction.md b/docs/book/src/introduction.md
index ef85a7041..d0a2aa0b9 100644
--- a/docs/book/src/introduction.md
+++ b/docs/book/src/introduction.md
@@ -10,7 +10,6 @@ Started by the Kubernetes Special Interest Group (SIG) [Cluster Lifecycle](https
 * [Concepts](./user/concepts.md)
 * [Developer guide](./developer/guide.md)
 * [Contributing](./CONTRIBUTING.md)
-* [Videos explaining Cluster API architecture](./developer/guide.md#videos-explaining-capi-architecture-and-code-walkthroughs)
 
 <aside class="note">
 
diff --git a/docs/book/src/reference/versions.md b/docs/book/src/reference/versions.md
index f1dd8294e..863dd154a 100644
--- a/docs/book/src/reference/versions.md
+++ b/docs/book/src/reference/versions.md
@@ -63,17 +63,17 @@ These diagrams show the relationships between components in a Cluster API releas
 
 #### Core Provider (`cluster-api-controller`)
 
-|                   | CAPI v0.3 (v1alpha3) | CAPI v0.4 (v1alpha4) | CAPI v1.0 (v1beta1) | CAPI v1.1+v1.2 (v1beta1) |
-|-------------------|----------------------|----------------------|---------------------|--------------------------|
-| Kubernetes v1.16  | ✓                    |                      |                     |                          |
-| Kubernetes v1.17  | ✓                    |                      |                     |                          |
-| Kubernetes v1.18  | ✓                    | ✓ (only workload)    | ✓ (only workload)   | ✓ (only workload)        |
-| Kubernetes v1.19  | ✓                    | ✓                    | ✓                   | ✓ (only workload v1.2)   |
-| Kubernetes v1.20  | ✓                    | ✓                    | ✓                   | ✓                        |
-| Kubernetes v1.21  | ✓                    | ✓                    | ✓                   | ✓                        |
-| Kubernetes v1.22  | ✓ (only workload)    | ✓                    | ✓                   | ✓                        |
-| Kubernetes v1.23* |                      | ✓                    | ✓                   | ✓                        |
-| Kubernetes v1.24  |                      |                      |                     | ✓                        |
+|                  |  CAPI v0.3 (v1alpha3) |  CAPI v0.4 (v1alpha4)  |  CAPI v1.0 (v1beta1) | CAPI v1.1+v1.2 (v1beta1) |
+| ---------------- | --------------------- | ---------------------- | -------------------- | ------------------------ |
+| Kubernetes v1.16 | ✓                     |                        |                      |                          |
+| Kubernetes v1.17 | ✓                     |                        |                      |                          |
+| Kubernetes v1.18 | ✓                     | ✓ (only workload)      | ✓ (only workload)    | ✓ (only workload)        |
+| Kubernetes v1.19 | ✓                     | ✓                      | ✓                    | ✓ (only workload v1.2)   |
+| Kubernetes v1.20 | ✓                     | ✓                      | ✓                    | ✓                        |
+| Kubernetes v1.21 | ✓                     | ✓                      | ✓                    | ✓                        |
+| Kubernetes v1.22 | ✓ (only workload)     | ✓                      | ✓                    | ✓                        |
+| Kubernetes v1.23* |                      | ✓                      | ✓                    | ✓                        |
+| Kubernetes v1.24 |                       |                        |                      | ✓                        |
 
 \* There is an issue with CRDs in Kubernetes v1.23.{0-2}. ClusterClass with patches is affected by that (for more details please see [this issue](https://github.com/kubernetes-sigs/cluster-api/issues/5990)). Therefore we recommend to use Kubernetes v1.23.3+ with ClusterClass.
    Previous Kubernetes **minor** versions are not affected.
@@ -83,24 +83,24 @@ The Core Provider also talks to API server of every Workload Cluster. Therefore,
 
 #### Kubeadm Bootstrap Provider (`kubeadm-bootstrap-controller`)
 
-|                                                                   | CAPI v0.3 (v1alpha3) | CAPI v0.4 (v1alpha4) | CAPI v1.0 (v1beta1) | CAPI v1.1+v1.2 (v1beta1) |
-|-------------------------------------------------------------------|----------------------|----------------------|---------------------|--------------------------|
-| Kubernetes v1.16 + kubeadm/v1beta2                                | ✓                    |                      |                     |                          |
-| Kubernetes v1.17 + kubeadm/v1beta2                                | ✓                    |                      |                     |                          |
-| Kubernetes v1.18 + kubeadm/v1beta2                                | ✓                    | ✓ (only workload)    | ✓ (only workload)   | ✓ (only workload)        |
-| Kubernetes v1.19 + kubeadm/v1beta2                                | ✓                    | ✓                    | ✓                   | ✓ (only workload v1.2)   |
-| Kubernetes v1.20 + kubeadm/v1beta2                                | ✓                    | ✓                    | ✓                   | ✓                        |
-| Kubernetes v1.21 + kubeadm/v1beta2                                | ✓                    | ✓                    | ✓                   | ✓                        |
-| Kubernetes v1.22 + kubeadm/v1beta2 (v0.3) kubeadm/v1beta3 (v0.4+) | ✓ (only workload)    | ✓                    | ✓                   | ✓                        |
-| Kubernetes v1.23 + kubeadm/v1beta3                                |                      | ✓                    | ✓                   | ✓                        |
-| Kubernetes v1.24 + kubeadm/v1beta3                                |                      |                      |                     | ✓                        |
+|                                    |  CAPI v0.3 (v1alpha3)            | CAPI v0.4 (v1alpha4) | CAPI v1.0 (v1beta1) | CAPI v1.1+v1.2 (v1beta1) |
+| ---------------------------------- | -------------------------------- | -------------------- | ------------------- | ------------------------ |
+| Kubernetes v1.16 + kubeadm/v1beta2 | ✓                                |                      |                     |                          |
+| Kubernetes v1.17 + kubeadm/v1beta2 | ✓                                |                      |                     |                          |
+| Kubernetes v1.18 + kubeadm/v1beta2 | ✓                                | ✓ (only workload)    | ✓ (only workload)   | ✓ (only workload)        |
+| Kubernetes v1.19 + kubeadm/v1beta2 | ✓                                | ✓                    | ✓                   | ✓ (only workload v1.2)   |
+| Kubernetes v1.20 + kubeadm/v1beta2 | ✓                                | ✓                    | ✓                   | ✓                        |
+| Kubernetes v1.21 + kubeadm/v1beta2 | ✓                                | ✓                    | ✓                   | ✓                        |
+| Kubernetes v1.22 + kubeadm/v1beta2 (v0.3) kubeadm/v1beta3 (v0.4+) | ✓ (only workload) |  ✓   | ✓                   | ✓                        |
+| Kubernetes v1.23 + kubeadm/v1beta3 |                                  | ✓                    | ✓                   | ✓                        |
+| Kubernetes v1.24 + kubeadm/v1beta3 |                                  |                      |                     | ✓                        |
 
 The Kubeadm Bootstrap Provider generates kubeadm configuration using the API version recommended for the target Kubernetes version.
 
 #### Kubeadm Control Plane Provider (`kubeadm-control-plane-controller`)
 
 |                            | CAPI v0.3 (v1alpha3) | CAPI v0.4 (v1alpha4) | CAPI v1.0 (v1beta1) | CAPI v1.1+v1.2 (v1beta1) |
-|----------------------------|----------------------|----------------------|---------------------|--------------------------|
+| -------------------------- | -------------------- |----------------------| ------------------- | ------------------------ |
 | Kubernetes v1.16 + etcd/v3 | ✓                    |                      |                     |                          |
 | Kubernetes v1.17 + etcd/v3 | ✓                    |                      |                     |                          |
 | Kubernetes v1.18 + etcd/v3 | ✓                    | ✓ (only workload)    | ✓ (only workload)   | ✓ (only workload)        |
@@ -122,7 +122,7 @@ The Kubeadm Control Plane requires the Kubeadm Bootstrap Provider.
 | v0.3 (v1alpha3) | v1.8.4                          |
 | v0.4 (v1alpha4) | v1.8.4                          |
 | v1.0 (v1beta1)  | v1.8.5                          | 
-| v1.1 (v1beta1)  | v1.9.3                          |
+| v1.1 (v1beta1)  | v1.8.6                          |
 | v1.2 (v1beta1)  | v1.9.3                          |
 
 #### Kubernetes version specific notes
diff --git a/docs/book/src/tasks/experimental-features/runtime-sdk.md b/docs/book/src/tasks/experimental-features/runtime-sdk.md
index f27a2b5cd..d831dc9e3 100644
--- a/docs/book/src/tasks/experimental-features/runtime-sdk.md
+++ b/docs/book/src/tasks/experimental-features/runtime-sdk.md
@@ -9,4 +9,4 @@ The Runtime SDK feature provides an extensibility mechanism that allows systems,
 
 
 More details on the Runtime SDK can be found at:
-[RuntimeSDK CAEP](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/proposals/20220221-runtime-SDK.md)
+[RuntimeSDK CAEP](./../../../../proposals/20220221-runtime-SDK.md)
\ No newline at end of file
diff --git a/docs/book/src/user/quick-start.md b/docs/book/src/user/quick-start.md
index d4fe8022b..fe3f14150 100644
--- a/docs/book/src/user/quick-start.md
+++ b/docs/book/src/user/quick-start.md
@@ -485,7 +485,7 @@ The output of `clusterctl init` is similar to this:
 
 ```bash
 Fetching providers
-Installing cert-manager Version="v1.8.2"
+Installing cert-manager Version="v1.7.2"
 Waiting for cert-manager to be available...
 Installing Provider="cluster-api" Version="v1.0.0" TargetNamespace="capi-system"
 Installing Provider="bootstrap-kubeadm" Version="v1.0.0" TargetNamespace="capi-kubeadm-bootstrap-system"
diff --git a/docs/book/src/user/troubleshooting.md b/docs/book/src/user/troubleshooting.md
index 40ec44ef3..1765834a4 100644
--- a/docs/book/src/user/troubleshooting.md
+++ b/docs/book/src/user/troubleshooting.md
@@ -96,11 +96,11 @@ exit
 When using older versions of Cluster API 0.4 and 1.0 releases - 0.4.6, 1.0.3 and older respectively - Cert Manager may not be downloadable due to a change in the repository location. This will cause `clusterctl init` to fail with the error:
 
 ```bash
-clusterctl init --infrastructure docker
+cluster-api % clusterctl init --infrastructure docker
 ```
 ```bash
 Fetching providers
-Installing cert-manager Version="v1.8.2"
+Installing cert-manager Version="v1.7.2"
 Error: action failed after 10 attempts: failed to get cert-manager object /, Kind=, /: Object 'Kind' is missing in 'unstructured object has no kind'
 ```
 
@@ -112,35 +112,10 @@ cert-manager:
   url: "https://github.com/cert-manager/cert-manager/releases/latest/cert-manager.yaml"
 ```
 
-Alternatively a Cert Manager yaml file can be placed in the [clusterctl overrides layer](../clusterctl/configuration.md#overrides-layer) which is by default in `$HOME/.cluster-api/overrides`. A Cert Manager yaml file can be placed at `$(HOME)/.cluster-api/overrides/cert-manager/v1.8.2/cert-manager.yaml`
+Alternatively a Cert Manager yaml file can be placed in the [clusterctl overrides layer](../clusterctl/configuration.md#overrides-layer) which is by default in `$HOME/.cluster-api/overrides`. A Cert Manager yaml file can be placed at `$(HOME)/.cluster-api/overrides/cert-manager/v1.7.2/cert-manager.yaml`
 
 More information on the clusterctl config file can be found at [its page in the book](../clusterctl/configuration.md#clusterctl-configuration-file)
 
-## Failed clusterctl upgrade apply - 'failed to update cert-manager component'
-
-Upgrading Cert Manager may fail due to a breaking change introduced in Cert Manager release v1.6.
-An upgrade using `clusterctl` is affected when:
-
-* using `clusterctl` in version `v1.1.4` or a more recent version.
-* Cert Manager lower than version `v1.0.0` did run in the management cluster (which was shipped in Cluster API until including `v0.3.14`).
-
-This will cause `clusterctl upgrade apply` to fail with the error:
-
-```bash
-clusterctl upgrade apply
-```
-
-```bash
-Checking cert-manager version...
-Deleting cert-manager Version="v1.5.3"
-Installing cert-manager Version="v1.7.2"
-Error: action failed after 10 attempts: failed to update cert-manager component apiextensions.k8s.io/v1, Kind=CustomResourceDefinition, /certificaterequests.cert-manager.io: CustomResourceDefinition.apiextensions.k8s.io "certificaterequests.cert-manager.io" is invalid: status.storedVersions[0]: Invalid value: "v1alpha2": must appear in spec.versions
-```
-
-The Cert Manager maintainers provide documentation to [migrate the deprecated API Resources](https://cert-manager.io/docs/installation/upgrading/remove-deprecated-apis/#upgrading-existing-cert-manager-resources) to the new storage versions to mitigate the issue.
-
-More information about the change in Cert Manager can be found at [their upgrade notes from v1.5 to v1.6](https://cert-manager.io/docs/installation/upgrading/upgrading-1.5-1.6).
-
 ## Clusterctl failing to start providers due to outdated image overrides
 
 clusterctl allows users to configure [image overrides](../clusterctl/configuration.md#image-overrides) via the clusterctl config file.
diff --git a/exp/runtime/api/v1alpha1/extensionconfig_types.go b/exp/runtime/api/v1alpha1/extensionconfig_types.go
index 60f276175..ca2a7a5e7 100644
--- a/exp/runtime/api/v1alpha1/extensionconfig_types.go
+++ b/exp/runtime/api/v1alpha1/extensionconfig_types.go
@@ -26,31 +26,30 @@ import (
 
 // ExtensionConfigSpec defines the desired state of ExtensionConfig.
 type ExtensionConfigSpec struct {
-	// ClientConfig defines how to communicate with ExtensionHandlers.
+	// ClientConfig defines how to communicate with the Extension server.
 	ClientConfig ClientConfig `json:"clientConfig"`
 
 	// NamespaceSelector decides whether to call the hook for an object based
 	// on whether the namespace for that object matches the selector.
-	// Default to the empty LabelSelector, which matches everything.
+	// Defaults to the empty LabelSelector, which matches all objects.
 	// +optional
 	NamespaceSelector *metav1.LabelSelector `json:"namespaceSelector,omitempty"`
 }
 
 // ClientConfig contains the information to make a client
-// connection with an ExtensionHandler.
+// connection with an Extension server.
 type ClientConfig struct {
-	// URL gives the location of the ExtensionHandler, in standard URL form
-	// (`scheme://host:port/path`). Exactly one of `url` or `service`
-	// must be specified.
+	// URL gives the location of the Extension server, in standard URL form
+	// (`scheme://host:port/path`).
+	// Note: Exactly one of `url` or `service` must be specified.
+	//
+	// The scheme must be "https".
 	//
 	// The `host` should not refer to a service running in the cluster; use
 	// the `service` field instead.
 	//
-	// The scheme should be "https"; the URL should begin with "https://".
-	//
 	// A path is optional, and if present may be any string permissible in
-	// a URL. If a path is set it will be used as prefix and the hook-specific
-	// path will be appended.
+	// a URL. If a path is set it will be used as prefix to the hook-specific path.
 	//
 	// Attempting to use a user or basic auth e.g. "user:password@" is not
 	// allowed. Fragments ("#...") and query parameters ("?...") are not
@@ -59,20 +58,20 @@ type ClientConfig struct {
 	// +optional
 	URL *string `json:"url,omitempty"`
 
-	// Service is a reference to the Kubernetes service for the ExtensionHandler.
-	// Either `service` or `url` must be specified.
+	// Service is a reference to the Kubernetes service for the Extension server.
+	// Note: Exactly one of `url` or `service` must be specified.
 	//
-	// If the ExtensionHandler is running within a cluster, then you should use `service`.
+	// If the Extension server is running within a cluster, then you should use `service`.
 	//
 	// +optional
 	Service *ServiceReference `json:"service,omitempty"`
 
-	// CABundle is a PEM encoded CA bundle which will be used to validate the ExtensionHandler's server certificate.
+	// CABundle is a PEM encoded CA bundle which will be used to validate the Extension server's server certificate.
 	// +optional
 	CABundle []byte `json:"caBundle,omitempty"`
 }
 
-// ServiceReference holds a reference to a Kubernetes Service.
+// ServiceReference holds a reference to a Kubernetes Service of an Extension server.
 type ServiceReference struct {
 	// Namespace is the namespace of the service.
 	Namespace string `json:"namespace"`
@@ -80,15 +79,14 @@ type ServiceReference struct {
 	// Name is the name of the service.
 	Name string `json:"name"`
 
-	// Path is an optional URL path which will be sent in any request to
-	// this service. If a path is set it will be used as prefix and the hook-specific
-	// path will be appended.
+	// Path is an optional URL path and if present may be any string permissible in
+	// a URL. If a path is set it will be used as prefix to the hook-specific path.
 	// +optional
 	Path *string `json:"path,omitempty"`
 
-	// Port is the port on the service that's hosting the ExtensionHandler.
-	// Default to 443.
-	// `port` should be a valid port number (1-65535, inclusive).
+	// Port is the port on the service that's hosting the Extension server.
+	// Defaults to 443.
+	// Port should be a valid port number (1-65535, inclusive).
 	// +optional
 	Port *int32 `json:"port,omitempty"`
 }
@@ -119,6 +117,7 @@ type ExtensionHandler struct {
 	RequestHook GroupVersionHook `json:"requestHook"`
 
 	// TimeoutSeconds defines the timeout duration for client calls to the ExtensionHandler.
+	// Defaults to 10 is not set.
 	// +optional
 	TimeoutSeconds *int32 `json:"timeoutSeconds,omitempty"`
 
@@ -130,25 +129,25 @@ type ExtensionHandler struct {
 
 // GroupVersionHook defines the runtime hook when the ExtensionHandler is called.
 type GroupVersionHook struct {
-	// APIVersion is the Version of the Hook.
+	// APIVersion is the group and version of the Hook.
 	APIVersion string `json:"apiVersion"`
 
 	// Hook is the name of the hook.
 	Hook string `json:"hook"`
 }
 
-// FailurePolicy specifies how unrecognized errors from the admission endpoint are handled.
+// FailurePolicy specifies how unrecognized errors when calling the ExtensionHandler are handled.
 // FailurePolicy helps with extensions not working consistently, e.g. due to an intermittent network issue.
-// The following type of errors are always considered blocking Failures:
+// The following type of errors are never ignored by FailurePolicy Ignore:
 // - Misconfigurations (e.g. incompatible types)
-// - Extension explicitly reports a Status Failure.
+// - Extension explicitly returns a Status Failure.
 type FailurePolicy string
 
 const (
-	// FailurePolicyIgnore means that an error calling the extension is ignored.
+	// FailurePolicyIgnore means that an error when calling the extension is ignored.
 	FailurePolicyIgnore FailurePolicy = "Ignore"
 
-	// FailurePolicyFail means that an error calling the extension is propagated as an error.
+	// FailurePolicyFail means that an error when calling the extension is propagated as an error.
 	FailurePolicyFail FailurePolicy = "Fail"
 )
 
@@ -202,12 +201,12 @@ const (
 	// DiscoveryFailedReason documents failure of a Discovery call.
 	DiscoveryFailedReason string = "DiscoveryFailed"
 
-	// InjectCAFromSecretAnnotation is the annotation that specifies that a particular
-	// object wants injection of CAs. It takes the form of a reference to a Secret
-	// as namespace/name.
+	// InjectCAFromSecretAnnotation is the annotation that specifies that an ExtensionConfig
+	// object wants injection of CAs. The value is a reference to a Secret
+	// as <namespace>/<name>.
 	InjectCAFromSecretAnnotation string = "runtime.cluster.x-k8s.io/inject-ca-from-secret"
 
-	// PendingHooksAnnotation is the annotation used to keep a track of pending runtime hooks.
+	// PendingHooksAnnotation is the annotation used to keep track of pending runtime hooks.
 	// The annotation will be used to track the intent to call a hook as soon as an operation completes;
 	// the intent will be removed as soon as the hook call completes successfully.
 	PendingHooksAnnotation string = "runtime.cluster.x-k8s.io/pending-hooks"
diff --git a/exp/runtime/hooks/api/v1alpha1/common_types.go b/exp/runtime/hooks/api/v1alpha1/common_types.go
index c7ace37dc..8ab7a9341 100644
--- a/exp/runtime/hooks/api/v1alpha1/common_types.go
+++ b/exp/runtime/hooks/api/v1alpha1/common_types.go
@@ -20,7 +20,7 @@ import (
 	"k8s.io/apimachinery/pkg/runtime"
 )
 
-// ResponseObject is a runtime object extended with methods to handle response-specific fields.
+// ResponseObject is a runtime.Object extended with methods to handle response-specific fields.
 // +kubebuilder:object:generate=false
 type ResponseObject interface {
 	runtime.Object
@@ -40,6 +40,8 @@ type RetryResponseObject interface {
 }
 
 // CommonResponse is the data structure common to all response types.
+// Note: By embedding CommonResponse in a runtime.Object the ResponseObject
+// interface is satisfied.
 type CommonResponse struct {
 	// Status of the call. One of "Success" or "Failure".
 	Status ResponseStatus `json:"status"`
@@ -48,12 +50,12 @@ type CommonResponse struct {
 	Message string `json:"message"`
 }
 
-// SetMessage sets the message field for the CommonResponse.
+// SetMessage sets the Message field for the CommonResponse.
 func (r *CommonResponse) SetMessage(message string) {
 	r.Message = message
 }
 
-// SetStatus sets the status field for the CommonResponse.
+// SetStatus sets the Status field for the CommonResponse.
 func (r *CommonResponse) SetStatus(status ResponseStatus) {
 	r.Status = status
 }
@@ -73,7 +75,7 @@ func (r *CommonResponse) GetStatus() ResponseStatus {
 type ResponseStatus string
 
 const (
-	// ResponseStatusSuccess represents the success response.
+	// ResponseStatusSuccess represents a success response.
 	ResponseStatusSuccess ResponseStatus = "Success"
 
 	// ResponseStatusFailure represents a failure response.
@@ -81,7 +83,9 @@ const (
 )
 
 // CommonRetryResponse is the data structure which contains all
-// common retry fields.
+// common and retry fields.
+// Note: By embedding CommonRetryResponse in a runtime.Object the RetryResponseObject
+// interface is satisfied.
 type CommonRetryResponse struct {
 	// CommonResponse contains Status and Message fields common to all response types.
 	CommonResponse `json:",inline"`
@@ -91,12 +95,12 @@ type CommonRetryResponse struct {
 	RetryAfterSeconds int32 `json:"retryAfterSeconds"`
 }
 
-// GetRetryAfterSeconds sets the RetryAfterSeconds value.
+// GetRetryAfterSeconds returns the RetryAfterSeconds field for the CommonRetryResponse.
 func (r *CommonRetryResponse) GetRetryAfterSeconds() int32 {
 	return r.RetryAfterSeconds
 }
 
-// SetRetryAfterSeconds returns the RetryAfterSeconds value.
+// SetRetryAfterSeconds sets the RetryAfterSeconds field for the CommonRetryResponse.
 func (r *CommonRetryResponse) SetRetryAfterSeconds(retryAfterSeconds int32) {
 	r.RetryAfterSeconds = retryAfterSeconds
 }
diff --git a/exp/runtime/hooks/api/v1alpha1/discovery_types.go b/exp/runtime/hooks/api/v1alpha1/discovery_types.go
index 6766efe45..8a6feeb64 100644
--- a/exp/runtime/hooks/api/v1alpha1/discovery_types.go
+++ b/exp/runtime/hooks/api/v1alpha1/discovery_types.go
@@ -22,7 +22,7 @@ import (
 	runtimecatalog "sigs.k8s.io/cluster-api/internal/runtime/catalog"
 )
 
-// DiscoveryRequest represents the object of a discovery request.
+// DiscoveryRequest is the request of the Discovery hook.
 // +kubebuilder:object:root=true
 type DiscoveryRequest struct {
 	metav1.TypeMeta `json:",inline"`
@@ -30,7 +30,7 @@ type DiscoveryRequest struct {
 
 var _ ResponseObject = &DiscoveryResponse{}
 
-// DiscoveryResponse represents the object received as a discovery response.
+// DiscoveryResponse is the response of the Discovery hook.
 // +kubebuilder:object:root=true
 type DiscoveryResponse struct {
 	metav1.TypeMeta `json:",inline"`
@@ -44,7 +44,7 @@ type DiscoveryResponse struct {
 	Handlers []ExtensionHandler `json:"handlers"`
 }
 
-// ExtensionHandler represents the discovery information of the extension which includes
+// ExtensionHandler represents the discovery information for an extension handler which includes
 // the hook it supports.
 type ExtensionHandler struct {
 	// Name is the name of the ExtensionHandler.
@@ -64,25 +64,25 @@ type ExtensionHandler struct {
 
 // GroupVersionHook defines the runtime hook when the ExtensionHandler is called.
 type GroupVersionHook struct {
-	// APIVersion is the Version of the Hook
+	// APIVersion is the group and version of the Hook
 	APIVersion string `json:"apiVersion"`
 
 	// Hook is the name of the hook
 	Hook string `json:"hook"`
 }
 
-// FailurePolicy specifies how unrecognized errors from the admission endpoint are handled.
+// FailurePolicy specifies how unrecognized errors when calling the ExtensionHandler are handled.
 // FailurePolicy helps with extensions not working consistently, e.g. due to an intermittent network issue.
-// The following type of errors are always considered blocking Failures:
+// The following type of errors are never ignored by FailurePolicy Ignore:
 // - Misconfigurations (e.g. incompatible types)
-// - Extension explicitly reports a Status Failure.
+// - Extension explicitly returns a Status Failure.
 type FailurePolicy string
 
 const (
-	// FailurePolicyIgnore means that an error calling the extension is ignored.
+	// FailurePolicyIgnore means that an error when calling the extension is ignored.
 	FailurePolicyIgnore FailurePolicy = "Ignore"
 
-	// FailurePolicyFail means that an error calling the extension is not ignored.
+	// FailurePolicyFail means that an error when calling the extension is not ignored.
 	FailurePolicyFail FailurePolicy = "Fail"
 )
 
@@ -92,8 +92,8 @@ func Discovery(*DiscoveryRequest, *DiscoveryResponse) {}
 func init() {
 	catalogBuilder.RegisterHook(Discovery, &runtimecatalog.HookMeta{
 		Tags:        []string{"Discovery"},
-		Summary:     "Discovery endpoint",
-		Description: "Discovery endpoint discovers the supported hooks of a RuntimeExtension",
+		Summary:     "Discovery hook",
+		Description: "Discovery hook discovers the supported hooks of a RuntimeExtension",
 		Singleton:   true,
 	})
 }
diff --git a/exp/runtime/hooks/api/v1alpha1/groupversion_info.go b/exp/runtime/hooks/api/v1alpha1/groupversion_info.go
index 7fdb0a73a..537987288 100644
--- a/exp/runtime/hooks/api/v1alpha1/groupversion_info.go
+++ b/exp/runtime/hooks/api/v1alpha1/groupversion_info.go
@@ -23,7 +23,7 @@ import (
 )
 
 var (
-	// GroupVersion is group version identifying RuntimeHooks defined in this package
+	// GroupVersion is the group version identifying RuntimeHooks defined in this package
 	// and their request and response types.
 	GroupVersion = schema.GroupVersion{Group: "hooks.runtime.cluster.x-k8s.io", Version: "v1alpha1"}
 
diff --git a/exp/runtime/hooks/api/v1alpha1/lifecyclehooks_types.go b/exp/runtime/hooks/api/v1alpha1/lifecyclehooks_types.go
index 467a49897..de25bcd41 100644
--- a/exp/runtime/hooks/api/v1alpha1/lifecyclehooks_types.go
+++ b/exp/runtime/hooks/api/v1alpha1/lifecyclehooks_types.go
@@ -28,7 +28,7 @@ import (
 type BeforeClusterCreateRequest struct {
 	metav1.TypeMeta `json:",inline"`
 
-	// The cluster object the lifecycle hook corresponds to.
+	// Cluster is the cluster object the lifecycle hook corresponds to.
 	Cluster clusterv1.Cluster `json:"cluster"`
 }
 
@@ -39,11 +39,11 @@ var _ RetryResponseObject = &BeforeClusterCreateResponse{}
 type BeforeClusterCreateResponse struct {
 	metav1.TypeMeta `json:",inline"`
 
-	// CommonRetryResponse contains RetryAfterSeconds field common to all retry response types.
+	// CommonRetryResponse contains Status, Message and RetryAfterSeconds fields.
 	CommonRetryResponse `json:",inline"`
 }
 
-// BeforeClusterCreate is the runtime hook that will be called right before a Cluster is created.
+// BeforeClusterCreate is the hook that will be called right before the topology of the Cluster is created.
 func BeforeClusterCreate(*BeforeClusterCreateRequest, *BeforeClusterCreateResponse) {}
 
 // AfterControlPlaneInitializedRequest is the request of the AfterControlPlaneInitialized hook.
@@ -51,7 +51,7 @@ func BeforeClusterCreate(*BeforeClusterCreateRequest, *BeforeClusterCreateRespon
 type AfterControlPlaneInitializedRequest struct {
 	metav1.TypeMeta `json:",inline"`
 
-	// The cluster object the lifecycle hook corresponds to.
+	// Cluster is the cluster object the lifecycle hook corresponds to.
 	Cluster clusterv1.Cluster `json:"cluster"`
 }
 
@@ -66,7 +66,7 @@ type AfterControlPlaneInitializedResponse struct {
 	CommonResponse `json:",inline"`
 }
 
-// AfterControlPlaneInitialized is the runtime hook that will be called after the control plane is available for the first time.
+// AfterControlPlaneInitialized is the hook that will be called after the control plane is initialized for the first time.
 func AfterControlPlaneInitialized(*AfterControlPlaneInitializedRequest, *AfterControlPlaneInitializedResponse) {
 }
 
@@ -75,13 +75,13 @@ func AfterControlPlaneInitialized(*AfterControlPlaneInitializedRequest, *AfterCo
 type BeforeClusterUpgradeRequest struct {
 	metav1.TypeMeta `json:",inline"`
 
-	// The cluster object the lifecycle hook corresponds to.
+	// Cluster is the cluster object the lifecycle hook corresponds to.
 	Cluster clusterv1.Cluster `json:"cluster"`
 
-	// The current Kubernetes version of the cluster.
+	// FromKubernetesVersion is the current Kubernetes version of the cluster.
 	FromKubernetesVersion string `json:"fromKubernetesVersion"`
 
-	// The target Kubernetes version of upgrade.
+	// ToKubernetesVersion is the target Kubernetes version of the upgrade.
 	ToKubernetesVersion string `json:"toKubernetesVersion"`
 }
 
@@ -92,11 +92,11 @@ var _ RetryResponseObject = &BeforeClusterUpgradeResponse{}
 type BeforeClusterUpgradeResponse struct {
 	metav1.TypeMeta `json:",inline"`
 
-	// CommonRetryResponse contains RetryAfterSeconds field common to all retry response types.
+	// CommonRetryResponse contains Status, Message and RetryAfterSeconds fields.
 	CommonRetryResponse `json:",inline"`
 }
 
-// BeforeClusterUpgrade is the runtime hook that will be called after a cluster.spec.version is upgraded and
+// BeforeClusterUpgrade is the hook that will be called after a Cluster.spec.version is upgraded and
 // before the updated version is propagated to the underlying objects.
 func BeforeClusterUpgrade(*BeforeClusterUpgradeRequest, *BeforeClusterUpgradeResponse) {}
 
@@ -105,10 +105,10 @@ func BeforeClusterUpgrade(*BeforeClusterUpgradeRequest, *BeforeClusterUpgradeRes
 type AfterControlPlaneUpgradeRequest struct {
 	metav1.TypeMeta `json:",inline"`
 
-	// The cluster object the lifecycle hook corresponds to.
+	// Cluster is the cluster object the lifecycle hook corresponds to.
 	Cluster clusterv1.Cluster `json:"cluster"`
 
-	// The Kubernetes version after upgrade.
+	// KubernetesVersion is the Kubernetes version of the Control Plane after the upgrade.
 	KubernetesVersion string `json:"kubernetesVersion"`
 }
 
@@ -119,11 +119,11 @@ var _ RetryResponseObject = &AfterControlPlaneUpgradeResponse{}
 type AfterControlPlaneUpgradeResponse struct {
 	metav1.TypeMeta `json:",inline"`
 
-	// CommonRetryResponse contains RetryAfterSeconds field common to all retry response types.
+	// CommonRetryResponse contains Status, Message and RetryAfterSeconds fields.
 	CommonRetryResponse `json:",inline"`
 }
 
-// AfterControlPlaneUpgrade is the runtime hook called after the control plane is successfully upgraded to the target
+// AfterControlPlaneUpgrade is the hook called after the control plane is successfully upgraded to the target
 // Kubernetes version and before the target version is propagated to the workload machines.
 func AfterControlPlaneUpgrade(*AfterControlPlaneUpgradeRequest, *AfterControlPlaneUpgradeResponse) {}
 
@@ -132,10 +132,10 @@ func AfterControlPlaneUpgrade(*AfterControlPlaneUpgradeRequest, *AfterControlPla
 type AfterClusterUpgradeRequest struct {
 	metav1.TypeMeta `json:",inline"`
 
-	// The cluster object the lifecycle hook corresponds to.
+	// Cluster is the cluster object the lifecycle hook corresponds to.
 	Cluster clusterv1.Cluster `json:"cluster"`
 
-	// The Kubernetes version after upgrade.
+	// KubernetesVersion is the Kubernetes version after upgrade.
 	KubernetesVersion string `json:"kubernetesVersion"`
 }
 
@@ -150,8 +150,8 @@ type AfterClusterUpgradeResponse struct {
 	CommonResponse `json:",inline"`
 }
 
-// AfterClusterUpgrade is the runtime hook that is called after all of the cluster is updated
-// to the target kubernetes version.
+// AfterClusterUpgrade is the hook that is called after the entire cluster is updated
+// to the target Kubernetes version.
 func AfterClusterUpgrade(*AfterClusterUpgradeRequest, *AfterClusterUpgradeResponse) {}
 
 // BeforeClusterDeleteRequest is the request of the BeforeClusterDelete hook.
@@ -159,7 +159,7 @@ func AfterClusterUpgrade(*AfterClusterUpgradeRequest, *AfterClusterUpgradeRespon
 type BeforeClusterDeleteRequest struct {
 	metav1.TypeMeta `json:",inline"`
 
-	// The cluster object the lifecycle hook corresponds to.
+	// Cluster is the cluster object the lifecycle hook corresponds to.
 	Cluster clusterv1.Cluster `json:"cluster"`
 }
 
@@ -170,48 +170,48 @@ var _ RetryResponseObject = &BeforeClusterDeleteResponse{}
 type BeforeClusterDeleteResponse struct {
 	metav1.TypeMeta `json:",inline"`
 
-	// CommonRetryResponse contains RetryAfterSeconds field common to all retry response types.
+	// CommonRetryResponse contains Status, Message and RetryAfterSeconds fields.
 	CommonRetryResponse `json:",inline"`
 }
 
-// BeforeClusterDelete is the runtime hook that is called after a delete is issued on a cluster
+// BeforeClusterDelete is the hook that is called after delete is issued on a cluster
 // and before the cluster and its underlying objects are deleted.
 func BeforeClusterDelete(*BeforeClusterDeleteRequest, *BeforeClusterDeleteResponse) {}
 
 func init() {
 	catalogBuilder.RegisterHook(BeforeClusterCreate, &runtimecatalog.HookMeta{
 		Tags:        []string{"Lifecycle Hooks"},
-		Summary:     "Called before Cluster topology is created",
-		Description: "This blocking hook is called after the Cluster is created by the user and immediately before all the objects which are part of a Cluster topology are going to be created",
+		Summary:     "Called before Cluster topology is created.",
+		Description: "This blocking hook is called after the Cluster is created by the user and immediately before all the objects which are part of a Cluster topology are going to be created.",
 	})
 
 	catalogBuilder.RegisterHook(AfterControlPlaneInitialized, &runtimecatalog.HookMeta{
 		Tags:        []string{"Lifecycle Hooks"},
-		Summary:     "Called after the Control Plane is available for the first time",
-		Description: "This non-blocking hook is called after the ControlPlane for the Cluster reachable for the first time",
+		Summary:     "Called after the Control Plane is initialized for the first time.",
+		Description: "This non-blocking hook is called after the Control Plane for the Cluster is reachable for the first time.",
 	})
 
 	catalogBuilder.RegisterHook(BeforeClusterUpgrade, &runtimecatalog.HookMeta{
 		Tags:        []string{"Lifecycle Hooks"},
-		Summary:     "Called before the Cluster begins upgrade",
-		Description: "This blocking hook is called after the Cluster object has been updated with a new spec.topology.version by the user, and immediately before the new version is propagated to the Control Plane",
+		Summary:     "Called before the Cluster is upgraded.",
+		Description: "This blocking hook is called after the Cluster object has been updated with a new spec.topology.version by the user, and immediately before the new version is propagated to the Control Plane.",
 	})
 
 	catalogBuilder.RegisterHook(AfterControlPlaneUpgrade, &runtimecatalog.HookMeta{
 		Tags:        []string{"Lifecycle Hooks"},
-		Summary:     "Called after the Control Plane finished upgrade",
-		Description: "This blocking hook is called after the Control Plane has been upgraded to the version specified in spec.topology.version, and immediately before the new version is propagated to the MachineDeployments of the Cluster",
+		Summary:     "Called after the Control Plane is upgraded.",
+		Description: "This blocking hook is called after the Control Plane has been upgraded to the version specified in spec.topology.version, and immediately before the new version is propagated to the MachineDeployments.",
 	})
 
 	catalogBuilder.RegisterHook(AfterClusterUpgrade, &runtimecatalog.HookMeta{
 		Tags:        []string{"Lifecycle Hooks"},
-		Summary:     "Called after the Cluster finished upgrade",
-		Description: "This non-blocking hook is called after the Cluster, Control Plane and Workers have been upgraded to the version specified in spec.topology.version",
+		Summary:     "Called after the Cluster is upgraded.",
+		Description: "This non-blocking hook is called after the Cluster, Control Plane and MachineDeployments have been upgraded to the version specified in spec.topology.version.",
 	})
 
 	catalogBuilder.RegisterHook(BeforeClusterDelete, &runtimecatalog.HookMeta{
 		Tags:        []string{"Lifecycle Hooks"},
-		Summary:     "Called before the Cluster is deleted",
-		Description: "This blocking hook is called after the Cluster has been deleted by the user, and immediately before objects of the Cluster are going to be deleted",
+		Summary:     "Called before the Cluster is deleted.",
+		Description: "This blocking hook is called after the Cluster deletion has been triggered by the user, and immediately before objects of the Cluster are going to be deleted.",
 	})
 }
diff --git a/exp/runtime/hooks/api/v1alpha1/topologymutation_types.go b/exp/runtime/hooks/api/v1alpha1/topologymutation_types.go
index ea90e6eab..44fff9fa0 100644
--- a/exp/runtime/hooks/api/v1alpha1/topologymutation_types.go
+++ b/exp/runtime/hooks/api/v1alpha1/topologymutation_types.go
@@ -40,7 +40,7 @@ type GeneratePatchesRequest struct {
 // GeneratePatchesRequestItem represents a template to generate patches for.
 type GeneratePatchesRequestItem struct {
 	// UID is an identifier for this template. It allows us to correlate the template in the request
-	// with the corresponding generates patches in the response.
+	// with the corresponding generated patches in the response.
 	UID types.UID `json:"uid"`
 
 	// HolderReference is a reference to the object where the template is used.
diff --git a/exp/runtime/hooks/api/v1alpha1/zz_generated.openapi.go b/exp/runtime/hooks/api/v1alpha1/zz_generated.openapi.go
index 46e9a40de..94eb3747b 100644
--- a/exp/runtime/hooks/api/v1alpha1/zz_generated.openapi.go
+++ b/exp/runtime/hooks/api/v1alpha1/zz_generated.openapi.go
@@ -83,14 +83,14 @@ func schema_runtime_hooks_api_v1alpha1_AfterClusterUpgradeRequest(ref common.Ref
 					},
 					"cluster": {
 						SchemaProps: spec.SchemaProps{
-							Description: "The cluster object the lifecycle hook corresponds to.",
+							Description: "Cluster is the cluster object the lifecycle hook corresponds to.",
 							Default:     map[string]interface{}{},
 							Ref:         ref("sigs.k8s.io/cluster-api/api/v1beta1.Cluster"),
 						},
 					},
 					"kubernetesVersion": {
 						SchemaProps: spec.SchemaProps{
-							Description: "The Kubernetes version after upgrade.",
+							Description: "KubernetesVersion is the Kubernetes version after upgrade.",
 							Default:     "",
 							Type:        []string{"string"},
 							Format:      "",
@@ -128,7 +128,7 @@ func schema_runtime_hooks_api_v1alpha1_AfterClusterUpgradeResponse(ref common.Re
 					},
 					"status": {
 						SchemaProps: spec.SchemaProps{
-							Description: "Status of the call. One of \"Success\" or \"Failure\".\n\nPossible enum values:\n - `\"Failure\"` represents a failure response.\n - `\"Success\"` represents the success response.",
+							Description: "Status of the call. One of \"Success\" or \"Failure\".\n\nPossible enum values:\n - `\"Failure\"` represents a failure response.\n - `\"Success\"` represents a success response.",
 							Default:     "",
 							Type:        []string{"string"},
 							Format:      "",
@@ -172,7 +172,7 @@ func schema_runtime_hooks_api_v1alpha1_AfterControlPlaneInitializedRequest(ref c
 					},
 					"cluster": {
 						SchemaProps: spec.SchemaProps{
-							Description: "The cluster object the lifecycle hook corresponds to.",
+							Description: "Cluster is the cluster object the lifecycle hook corresponds to.",
 							Default:     map[string]interface{}{},
 							Ref:         ref("sigs.k8s.io/cluster-api/api/v1beta1.Cluster"),
 						},
@@ -209,7 +209,7 @@ func schema_runtime_hooks_api_v1alpha1_AfterControlPlaneInitializedResponse(ref
 					},
 					"status": {
 						SchemaProps: spec.SchemaProps{
-							Description: "Status of the call. One of \"Success\" or \"Failure\".\n\nPossible enum values:\n - `\"Failure\"` represents a failure response.\n - `\"Success\"` represents the success response.",
+							Description: "Status of the call. One of \"Success\" or \"Failure\".\n\nPossible enum values:\n - `\"Failure\"` represents a failure response.\n - `\"Success\"` represents a success response.",
 							Default:     "",
 							Type:        []string{"string"},
 							Format:      "",
@@ -253,14 +253,14 @@ func schema_runtime_hooks_api_v1alpha1_AfterControlPlaneUpgradeRequest(ref commo
 					},
 					"cluster": {
 						SchemaProps: spec.SchemaProps{
-							Description: "The cluster object the lifecycle hook corresponds to.",
+							Description: "Cluster is the cluster object the lifecycle hook corresponds to.",
 							Default:     map[string]interface{}{},
 							Ref:         ref("sigs.k8s.io/cluster-api/api/v1beta1.Cluster"),
 						},
 					},
 					"kubernetesVersion": {
 						SchemaProps: spec.SchemaProps{
-							Description: "The Kubernetes version after upgrade.",
+							Description: "KubernetesVersion is the Kubernetes version of the Control Plane after the upgrade.",
 							Default:     "",
 							Type:        []string{"string"},
 							Format:      "",
@@ -298,7 +298,7 @@ func schema_runtime_hooks_api_v1alpha1_AfterControlPlaneUpgradeResponse(ref comm
 					},
 					"status": {
 						SchemaProps: spec.SchemaProps{
-							Description: "Status of the call. One of \"Success\" or \"Failure\".\n\nPossible enum values:\n - `\"Failure\"` represents a failure response.\n - `\"Success\"` represents the success response.",
+							Description: "Status of the call. One of \"Success\" or \"Failure\".\n\nPossible enum values:\n - `\"Failure\"` represents a failure response.\n - `\"Success\"` represents a success response.",
 							Default:     "",
 							Type:        []string{"string"},
 							Format:      "",
@@ -350,7 +350,7 @@ func schema_runtime_hooks_api_v1alpha1_BeforeClusterCreateRequest(ref common.Ref
 					},
 					"cluster": {
 						SchemaProps: spec.SchemaProps{
-							Description: "The cluster object the lifecycle hook corresponds to.",
+							Description: "Cluster is the cluster object the lifecycle hook corresponds to.",
 							Default:     map[string]interface{}{},
 							Ref:         ref("sigs.k8s.io/cluster-api/api/v1beta1.Cluster"),
 						},
@@ -387,7 +387,7 @@ func schema_runtime_hooks_api_v1alpha1_BeforeClusterCreateResponse(ref common.Re
 					},
 					"status": {
 						SchemaProps: spec.SchemaProps{
-							Description: "Status of the call. One of \"Success\" or \"Failure\".\n\nPossible enum values:\n - `\"Failure\"` represents a failure response.\n - `\"Success\"` represents the success response.",
+							Description: "Status of the call. One of \"Success\" or \"Failure\".\n\nPossible enum values:\n - `\"Failure\"` represents a failure response.\n - `\"Success\"` represents a success response.",
 							Default:     "",
 							Type:        []string{"string"},
 							Format:      "",
@@ -439,7 +439,7 @@ func schema_runtime_hooks_api_v1alpha1_BeforeClusterDeleteRequest(ref common.Ref
 					},
 					"cluster": {
 						SchemaProps: spec.SchemaProps{
-							Description: "The cluster object the lifecycle hook corresponds to.",
+							Description: "Cluster is the cluster object the lifecycle hook corresponds to.",
 							Default:     map[string]interface{}{},
 							Ref:         ref("sigs.k8s.io/cluster-api/api/v1beta1.Cluster"),
 						},
@@ -476,7 +476,7 @@ func schema_runtime_hooks_api_v1alpha1_BeforeClusterDeleteResponse(ref common.Re
 					},
 					"status": {
 						SchemaProps: spec.SchemaProps{
-							Description: "Status of the call. One of \"Success\" or \"Failure\".\n\nPossible enum values:\n - `\"Failure\"` represents a failure response.\n - `\"Success\"` represents the success response.",
+							Description: "Status of the call. One of \"Success\" or \"Failure\".\n\nPossible enum values:\n - `\"Failure\"` represents a failure response.\n - `\"Success\"` represents a success response.",
 							Default:     "",
 							Type:        []string{"string"},
 							Format:      "",
@@ -528,14 +528,14 @@ func schema_runtime_hooks_api_v1alpha1_BeforeClusterUpgradeRequest(ref common.Re
 					},
 					"cluster": {
 						SchemaProps: spec.SchemaProps{
-							Description: "The cluster object the lifecycle hook corresponds to.",
+							Description: "Cluster is the cluster object the lifecycle hook corresponds to.",
 							Default:     map[string]interface{}{},
 							Ref:         ref("sigs.k8s.io/cluster-api/api/v1beta1.Cluster"),
 						},
 					},
 					"fromKubernetesVersion": {
 						SchemaProps: spec.SchemaProps{
-							Description: "The current Kubernetes version of the cluster.",
+							Description: "FromKubernetesVersion is the current Kubernetes version of the cluster.",
 							Default:     "",
 							Type:        []string{"string"},
 							Format:      "",
@@ -543,7 +543,7 @@ func schema_runtime_hooks_api_v1alpha1_BeforeClusterUpgradeRequest(ref common.Re
 					},
 					"toKubernetesVersion": {
 						SchemaProps: spec.SchemaProps{
-							Description: "The target Kubernetes version of upgrade.",
+							Description: "ToKubernetesVersion is the target Kubernetes version of the upgrade.",
 							Default:     "",
 							Type:        []string{"string"},
 							Format:      "",
@@ -581,7 +581,7 @@ func schema_runtime_hooks_api_v1alpha1_BeforeClusterUpgradeResponse(ref common.R
 					},
 					"status": {
 						SchemaProps: spec.SchemaProps{
-							Description: "Status of the call. One of \"Success\" or \"Failure\".\n\nPossible enum values:\n - `\"Failure\"` represents a failure response.\n - `\"Success\"` represents the success response.",
+							Description: "Status of the call. One of \"Success\" or \"Failure\".\n\nPossible enum values:\n - `\"Failure\"` represents a failure response.\n - `\"Success\"` represents a success response.",
 							Default:     "",
 							Type:        []string{"string"},
 							Format:      "",
@@ -614,12 +614,12 @@ func schema_runtime_hooks_api_v1alpha1_CommonResponse(ref common.ReferenceCallba
 	return common.OpenAPIDefinition{
 		Schema: spec.Schema{
 			SchemaProps: spec.SchemaProps{
-				Description: "CommonResponse is the data structure common to all response types.",
+				Description: "CommonResponse is the data structure common to all response types. Note: By embedding CommonResponse in a runtime.Object the ResponseObject interface is satisfied.",
 				Type:        []string{"object"},
 				Properties: map[string]spec.Schema{
 					"status": {
 						SchemaProps: spec.SchemaProps{
-							Description: "Status of the call. One of \"Success\" or \"Failure\".\n\nPossible enum values:\n - `\"Failure\"` represents a failure response.\n - `\"Success\"` represents the success response.",
+							Description: "Status of the call. One of \"Success\" or \"Failure\".\n\nPossible enum values:\n - `\"Failure\"` represents a failure response.\n - `\"Success\"` represents a success response.",
 							Default:     "",
 							Type:        []string{"string"},
 							Format:      "",
@@ -644,12 +644,12 @@ func schema_runtime_hooks_api_v1alpha1_CommonRetryResponse(ref common.ReferenceC
 	return common.OpenAPIDefinition{
 		Schema: spec.Schema{
 			SchemaProps: spec.SchemaProps{
-				Description: "CommonRetryResponse is the data structure which contains all common retry fields.",
+				Description: "CommonRetryResponse is the data structure which contains all common and retry fields. Note: By embedding CommonRetryResponse in a runtime.Object the RetryResponseObject interface is satisfied.",
 				Type:        []string{"object"},
 				Properties: map[string]spec.Schema{
 					"status": {
 						SchemaProps: spec.SchemaProps{
-							Description: "Status of the call. One of \"Success\" or \"Failure\".\n\nPossible enum values:\n - `\"Failure\"` represents a failure response.\n - `\"Success\"` represents the success response.",
+							Description: "Status of the call. One of \"Success\" or \"Failure\".\n\nPossible enum values:\n - `\"Failure\"` represents a failure response.\n - `\"Success\"` represents a success response.",
 							Default:     "",
 							Type:        []string{"string"},
 							Format:      "",
@@ -682,7 +682,7 @@ func schema_runtime_hooks_api_v1alpha1_DiscoveryRequest(ref common.ReferenceCall
 	return common.OpenAPIDefinition{
 		Schema: spec.Schema{
 			SchemaProps: spec.SchemaProps{
-				Description: "DiscoveryRequest represents the object of a discovery request.",
+				Description: "DiscoveryRequest is the request of the Discovery hook.",
 				Type:        []string{"object"},
 				Properties: map[string]spec.Schema{
 					"kind": {
@@ -709,7 +709,7 @@ func schema_runtime_hooks_api_v1alpha1_DiscoveryResponse(ref common.ReferenceCal
 	return common.OpenAPIDefinition{
 		Schema: spec.Schema{
 			SchemaProps: spec.SchemaProps{
-				Description: "DiscoveryResponse represents the object received as a discovery response.",
+				Description: "DiscoveryResponse is the response of the Discovery hook.",
 				Type:        []string{"object"},
 				Properties: map[string]spec.Schema{
 					"kind": {
@@ -728,7 +728,7 @@ func schema_runtime_hooks_api_v1alpha1_DiscoveryResponse(ref common.ReferenceCal
 					},
 					"status": {
 						SchemaProps: spec.SchemaProps{
-							Description: "Status of the call. One of \"Success\" or \"Failure\".\n\nPossible enum values:\n - `\"Failure\"` represents a failure response.\n - `\"Success\"` represents the success response.",
+							Description: "Status of the call. One of \"Success\" or \"Failure\".\n\nPossible enum values:\n - `\"Failure\"` represents a failure response.\n - `\"Success\"` represents a success response.",
 							Default:     "",
 							Type:        []string{"string"},
 							Format:      "",
@@ -777,7 +777,7 @@ func schema_runtime_hooks_api_v1alpha1_ExtensionHandler(ref common.ReferenceCall
 	return common.OpenAPIDefinition{
 		Schema: spec.Schema{
 			SchemaProps: spec.SchemaProps{
-				Description: "ExtensionHandler represents the discovery information of the extension which includes the hook it supports.",
+				Description: "ExtensionHandler represents the discovery information for an extension handler which includes the hook it supports.",
 				Type:        []string{"object"},
 				Properties: map[string]spec.Schema{
 					"name": {
@@ -885,7 +885,7 @@ func schema_runtime_hooks_api_v1alpha1_GeneratePatchesRequestItem(ref common.Ref
 				Properties: map[string]spec.Schema{
 					"uid": {
 						SchemaProps: spec.SchemaProps{
-							Description: "UID is an identifier for this template. It allows us to correlate the template in the request with the corresponding generates patches in the response.",
+							Description: "UID is an identifier for this template. It allows us to correlate the template in the request with the corresponding generated patches in the response.",
 							Default:     "",
 							Type:        []string{"string"},
 							Format:      "",
@@ -951,7 +951,7 @@ func schema_runtime_hooks_api_v1alpha1_GeneratePatchesResponse(ref common.Refere
 					},
 					"status": {
 						SchemaProps: spec.SchemaProps{
-							Description: "Status of the call. One of \"Success\" or \"Failure\".\n\nPossible enum values:\n - `\"Failure\"` represents a failure response.\n - `\"Success\"` represents the success response.",
+							Description: "Status of the call. One of \"Success\" or \"Failure\".\n\nPossible enum values:\n - `\"Failure\"` represents a failure response.\n - `\"Success\"` represents a success response.",
 							Default:     "",
 							Type:        []string{"string"},
 							Format:      "",
@@ -1034,7 +1034,7 @@ func schema_runtime_hooks_api_v1alpha1_GroupVersionHook(ref common.ReferenceCall
 				Properties: map[string]spec.Schema{
 					"apiVersion": {
 						SchemaProps: spec.SchemaProps{
-							Description: "APIVersion is the Version of the Hook",
+							Description: "APIVersion is the group and version of the Hook",
 							Default:     "",
 							Type:        []string{"string"},
 							Format:      "",
@@ -1233,7 +1233,7 @@ func schema_runtime_hooks_api_v1alpha1_ValidateTopologyResponse(ref common.Refer
 					},
 					"status": {
 						SchemaProps: spec.SchemaProps{
-							Description: "Status of the call. One of \"Success\" or \"Failure\".\n\nPossible enum values:\n - `\"Failure\"` represents a failure response.\n - `\"Success\"` represents the success response.",
+							Description: "Status of the call. One of \"Success\" or \"Failure\".\n\nPossible enum values:\n - `\"Failure\"` represents a failure response.\n - `\"Success\"` represents a success response.",
 							Default:     "",
 							Type:        []string{"string"},
 							Format:      "",
diff --git a/exp/runtime/internal/controllers/extensionconfig_controller.go b/exp/runtime/internal/controllers/extensionconfig_controller.go
index 2982fe8fc..d6746b0e1 100644
--- a/exp/runtime/internal/controllers/extensionconfig_controller.go
+++ b/exp/runtime/internal/controllers/extensionconfig_controller.go
@@ -35,6 +35,7 @@ import (
 
 	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
 	runtimev1 "sigs.k8s.io/cluster-api/exp/runtime/api/v1alpha1"
+	tlog "sigs.k8s.io/cluster-api/internal/log"
 	runtimeclient "sigs.k8s.io/cluster-api/internal/runtime/client"
 	"sigs.k8s.io/cluster-api/util/annotations"
 	"sigs.k8s.io/cluster-api/util/conditions"
@@ -75,7 +76,7 @@ func (r *Reconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, opt
 	}
 
 	if err := indexByExtensionInjectCAFromSecretName(ctx, mgr); err != nil {
-		return err
+		return errors.Wrap(err, "failed setting up with a controller manager")
 	}
 
 	// warmupRunnable will attempt to sync the RuntimeSDK registry with existing ExtensionConfig objects to ensure extensions
@@ -94,7 +95,9 @@ func (r *Reconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, opt
 func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
 	var errs []error
 	log := ctrl.LoggerFrom(ctx)
+
 	// Requeue events when the registry is not ready.
+	// The registry will become ready after it is 'warmed up' by warmupRunnable.
 	if !r.RuntimeClient.IsReady() {
 		return ctrl.Result{Requeue: true}, nil
 	}
@@ -104,7 +107,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
 	if err != nil {
 		if apierrors.IsNotFound(err) {
 			// ExtensionConfig not found. Remove from registry.
-			// First need to add Namespace/Name to empty ExtensionConfig object.
+			// First we need to add Namespace/Name to empty ExtensionConfig object.
 			extensionConfig.Name = req.Name
 			extensionConfig.Namespace = req.Namespace
 			return r.reconcileDelete(ctx, extensionConfig)
@@ -157,7 +160,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
 func patchExtensionConfig(ctx context.Context, client client.Client, original, modified *runtimev1.ExtensionConfig, options ...patch.Option) error {
 	patchHelper, err := patch.NewHelper(original, client)
 	if err != nil {
-		return errors.Wrapf(err, "failed to create PatchHelper for ExtensionConfig %s/%s", modified.Namespace, modified.Name)
+		return errors.Wrapf(err, "failed to create patch helper for %s", tlog.KObj{Obj: modified})
 	}
 
 	options = append(options, patch.WithOwnedConditions{Conditions: []clusterv1.ConditionType{
@@ -165,7 +168,7 @@ func patchExtensionConfig(ctx context.Context, client client.Client, original, m
 	}})
 	err = patchHelper.Patch(ctx, modified, options...)
 	if err != nil {
-		return errors.Wrapf(err, "failed to patch ExtensionConfig %s/%s", modified.Namespace, modified.Name)
+		return errors.Wrapf(err, "failed to patch %s", tlog.KObj{Obj: modified})
 	}
 	return nil
 }
@@ -174,12 +177,13 @@ func patchExtensionConfig(ctx context.Context, client client.Client, original, m
 // effort deletion that may not catch all cases.
 func (r *Reconciler) reconcileDelete(_ context.Context, extensionConfig *runtimev1.ExtensionConfig) (ctrl.Result, error) {
 	if err := r.RuntimeClient.Unregister(extensionConfig); err != nil {
-		return ctrl.Result{}, errors.Wrapf(err, "failed to unregister ExtensionConfig %s/%s", extensionConfig.Namespace, extensionConfig.Name)
+		return ctrl.Result{}, errors.Wrapf(err, "failed to unregister %s", tlog.KObj{Obj: extensionConfig})
 	}
 	return ctrl.Result{}, nil
 }
 
-// secretToExtensionConfig maps a secret to ExtensionConfigs to reconcile them on updates of the secrets.
+// secretToExtensionConfig maps a secret to ExtensionConfigs with the corresponding InjectCAFromSecretAnnotation
+// to reconcile them on updates of the secrets.
 func (r *Reconciler) secretToExtensionConfig(secret client.Object) []reconcile.Request {
 	result := []ctrl.Request{}
 
@@ -187,7 +191,7 @@ func (r *Reconciler) secretToExtensionConfig(secret client.Object) []reconcile.R
 	indexKey := secret.GetNamespace() + "/" + secret.GetName()
 
 	if err := r.Client.List(
-		context.Background(),
+		context.TODO(),
 		&extensionConfigs,
 		client.MatchingFields{injectCAFromSecretAnnotationField: indexKey},
 	); err != nil {
@@ -209,7 +213,7 @@ func discoverExtensionConfig(ctx context.Context, runtimeClient runtimeclient.Cl
 	if err != nil {
 		modifiedExtensionConfig := extensionConfig.DeepCopy()
 		conditions.MarkFalse(modifiedExtensionConfig, runtimev1.RuntimeExtensionDiscoveredCondition, runtimev1.DiscoveryFailedReason, clusterv1.ConditionSeverityError, "error in discovery: %v", err)
-		return modifiedExtensionConfig, errors.Wrapf(err, "failed to discover ExtensionConfig %s/%s", extensionConfig.Namespace, extensionConfig.Name)
+		return modifiedExtensionConfig, errors.Wrapf(err, "failed to discover %s", tlog.KObj{Obj: extensionConfig})
 	}
 
 	conditions.MarkTrue(discoveredExtension, runtimev1.RuntimeExtensionDiscoveredCondition)
@@ -217,7 +221,8 @@ func discoverExtensionConfig(ctx context.Context, runtimeClient runtimeclient.Cl
 }
 
 // reconcileCABundle reconciles the CA bundle for the ExtensionConfig.
-// cert-manager code: pkg/controller/cainjector/sources.go certificateDataSource.
+// Note: This was implemented to behave similar to the cert-manager cainjector.
+// We couldn't use the cert-manager cainjector because it doesn't work with CustomResources.
 func reconcileCABundle(ctx context.Context, client client.Client, config *runtimev1.ExtensionConfig) error {
 	secretNameRaw, ok := config.Annotations[runtimev1.InjectCAFromSecretAnnotation]
 	if !ok {
@@ -226,18 +231,18 @@ func reconcileCABundle(ctx context.Context, client client.Client, config *runtim
 	secretName := splitNamespacedName(secretNameRaw)
 
 	if secretName.Namespace == "" || secretName.Name == "" {
-		return errors.Errorf("secret name %q must be in the form namespace/name", secretNameRaw)
+		return errors.Errorf("failed to reconcile caBundle: secret name %q must be in the form <namespace>/<name>", secretNameRaw)
 	}
 
 	var secret corev1.Secret
 	// Note: this is an expensive API call because secrets are explicitly not cached.
 	if err := client.Get(ctx, secretName, &secret); err != nil {
-		return errors.Wrapf(err, "failed to get secret %s", secretNameRaw)
+		return errors.Wrapf(err, "failed to reconcile caBundle: failed to get secret %q", secretNameRaw)
 	}
 
 	caData, hasCAData := secret.Data[tlsCAKey]
 	if !hasCAData {
-		return errors.Errorf("secret %s does not contain a %s", secretNameRaw, tlsCAKey)
+		return errors.Errorf("failed to reconcile caBundle: secret %s does not contain a %q entry", secretNameRaw, tlsCAKey)
 	}
 
 	config.Spec.ClientConfig.CABundle = caData
@@ -245,7 +250,7 @@ func reconcileCABundle(ctx context.Context, client client.Client, config *runtim
 }
 
 // splitNamespacedName turns the string form of a namespaced name
-// (<namespace>/<name>) back into a types.NamespacedName.
+// (<namespace>/<name>) into a types.NamespacedName.
 func splitNamespacedName(nameStr string) types.NamespacedName {
 	splitPoint := strings.IndexRune(nameStr, types.Separator)
 	if splitPoint == -1 {
diff --git a/exp/runtime/internal/controllers/warmup.go b/exp/runtime/internal/controllers/warmup.go
index b17852890..1204ac6ec 100644
--- a/exp/runtime/internal/controllers/warmup.go
+++ b/exp/runtime/internal/controllers/warmup.go
@@ -48,7 +48,9 @@ type warmupRunnable struct {
 }
 
 // NeedLeaderElection satisfies the controller runtime LeaderElectionRunnable interface.
-// This helps ensure we warm up the RuntimeSDK registry before controllers begin reconciling.
+// This ensures we warm up the RuntimeSDK registry only after the controller became leader.
+// Note: Only after the warmupRunnable is completed the registry becomes ready and thus
+// all controllers using the runtime client or registry will wait until warmup is completed.
 func (r *warmupRunnable) NeedLeaderElection() bool {
 	return true
 }
@@ -57,7 +59,7 @@ func (r *warmupRunnable) NeedLeaderElection() bool {
 // cause the CAPI controller manager to fail.
 // We are retrying for 60 seconds to mitigate failures when the CAPI controller manager and RuntimeExtensions
 // are started at the same time. After 60 seconds we crash the entire controller to surface the
-// issue which block reconciliation of all Clusters to users in a timely fashion.
+// issue to users in a timely fashion as it would block reconciliation of all Clusters.
 func (r *warmupRunnable) Start(ctx context.Context) error {
 	log := ctrl.LoggerFrom(ctx)
 	if r.warmupInterval == 0 {
@@ -78,20 +80,20 @@ func (r *warmupRunnable) Start(ctx context.Context) error {
 	})
 
 	if err != nil {
-		return errors.Wrapf(err, "ExtensionConfig registry warmup timed out after  %s", r.warmupTimeout.String())
+		return errors.Wrapf(err, "ExtensionConfig registry warmup timed out after %s", r.warmupTimeout.String())
 	}
 
 	return nil
 }
 
-// warmupRegistry attempts to discover all existing ExtensionConfigs and patch their Status with discovered Handlers.
+// warmupRegistry attempts to discover all existing ExtensionConfigs and patch their status with discovered Handlers.
 // It warms up the registry by passing it the up-to-date list of ExtensionConfigs.
 func warmupRegistry(ctx context.Context, client client.Client, reader client.Reader, runtimeClient runtimeclient.Client) error {
 	var errs []error
 
 	extensionConfigList := runtimev1.ExtensionConfigList{}
 	if err := reader.List(ctx, &extensionConfigList); err != nil {
-		return err
+		return errors.Wrapf(err, "failed to list ExtensionConfigs")
 	}
 
 	for i := range extensionConfigList.Items {
@@ -101,6 +103,8 @@ func warmupRegistry(ctx context.Context, client client.Client, reader client.Rea
 		// Inject CABundle from secret if annotation is set. Otherwise https calls may fail.
 		if err := reconcileCABundle(ctx, client, extensionConfig); err != nil {
 			errs = append(errs, err)
+			// Note: we continue here because if reconcileCABundle doesn't work discovery will fail as well.
+			continue
 		}
 
 		extensionConfig, err := discoverExtensionConfig(ctx, runtimeClient, extensionConfig)
@@ -108,7 +112,7 @@ func warmupRegistry(ctx context.Context, client client.Client, reader client.Rea
 			errs = append(errs, err)
 		}
 
-		// Patch the ExtensionConfig with the updated condition and Handlers if discovered.
+		// Always patch the ExtensionConfig as it may contain updates in conditions or clientConfig.caBundle.
 		if err = patchExtensionConfig(ctx, client, original, extensionConfig); err != nil {
 			errs = append(errs, err)
 		}
diff --git a/go.mod b/go.mod
index 5cb029353..b9b7903c1 100644
--- a/go.mod
+++ b/go.mod
@@ -32,17 +32,17 @@ require (
 	golang.org/x/net v0.0.0-20220617184016-355a448f1bc9
 	golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb
 	google.golang.org/grpc v1.47.0
-	k8s.io/api v0.24.2
-	k8s.io/apiextensions-apiserver v0.24.2
-	k8s.io/apimachinery v0.24.2
-	k8s.io/apiserver v0.24.2
-	k8s.io/client-go v0.24.2
+	k8s.io/api v0.24.0
+	k8s.io/apiextensions-apiserver v0.24.0
+	k8s.io/apimachinery v0.24.0
+	k8s.io/apiserver v0.24.0
+	k8s.io/client-go v0.24.0
 	k8s.io/cluster-bootstrap v0.24.0
-	k8s.io/component-base v0.24.2
+	k8s.io/component-base v0.24.0
 	k8s.io/klog/v2 v2.60.1
 	k8s.io/kubectl v0.24.0
 	k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9
-	sigs.k8s.io/controller-runtime v0.12.2
+	sigs.k8s.io/controller-runtime v0.12.1
 	sigs.k8s.io/yaml v1.3.0
 )
 
diff --git a/go.sum b/go.sum
index 16e73522e..42525dc7d 100644
--- a/go.sum
+++ b/go.sum
@@ -1339,28 +1339,23 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
 honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
 honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
+k8s.io/api v0.24.0 h1:J0hann2hfxWr1hinZIDefw7Q96wmCBx6SSB8IY0MdDg=
 k8s.io/api v0.24.0/go.mod h1:5Jl90IUrJHUJYEMANRURMiVvJ0g7Ax7r3R1bqO8zx8I=
-k8s.io/api v0.24.2 h1:g518dPU/L7VRLxWfcadQn2OnsiGWVOadTLpdnqgY2OI=
-k8s.io/api v0.24.2/go.mod h1:AHqbSkTm6YrQ0ObxjO3Pmp/ubFF/KuM7jU+3khoBsOg=
-k8s.io/apiextensions-apiserver v0.24.2 h1:/4NEQHKlEz1MlaK/wHT5KMKC9UKYz6NZz6JE6ov4G6k=
-k8s.io/apiextensions-apiserver v0.24.2/go.mod h1:e5t2GMFVngUEHUd0wuCJzw8YDwZoqZfJiGOW6mm2hLQ=
+k8s.io/apiextensions-apiserver v0.24.0 h1:JfgFqbA8gKJ/uDT++feAqk9jBIwNnL9YGdQvaI9DLtY=
+k8s.io/apiextensions-apiserver v0.24.0/go.mod h1:iuVe4aEpe6827lvO6yWQVxiPSpPoSKVjkq+MIdg84cM=
+k8s.io/apimachinery v0.24.0 h1:ydFCyC/DjCvFCHK5OPMKBlxayQytB8pxy8YQInd5UyQ=
 k8s.io/apimachinery v0.24.0/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM=
-k8s.io/apimachinery v0.24.2 h1:5QlH9SL2C8KMcrNJPor+LbXVTaZRReml7svPEh4OKDM=
-k8s.io/apimachinery v0.24.2/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM=
-k8s.io/apiserver v0.24.2 h1:orxipm5elPJSkkFNlwH9ClqaKEDJJA3yR2cAAlCnyj4=
-k8s.io/apiserver v0.24.2/go.mod h1:pSuKzr3zV+L+MWqsEo0kHHYwCo77AT5qXbFXP2jbvFI=
+k8s.io/apiserver v0.24.0 h1:GR7kGsjOMfilRvlG3Stxv/3uz/ryvJ/aZXc5pqdsNV0=
+k8s.io/apiserver v0.24.0/go.mod h1:WFx2yiOMawnogNToVvUYT9nn1jaIkMKj41ZYCVycsBA=
 k8s.io/cli-runtime v0.24.0 h1:ot3Qf49T852uEyNApABO1UHHpFIckKK/NqpheZYN2gM=
 k8s.io/cli-runtime v0.24.0/go.mod h1:9XxoZDsEkRFUThnwqNviqzljtT/LdHtNWvcNFrAXl0A=
+k8s.io/client-go v0.24.0 h1:lbE4aB1gTHvYFSwm6eD3OF14NhFDKCejlnsGYlSJe5U=
 k8s.io/client-go v0.24.0/go.mod h1:VFPQET+cAFpYxh6Bq6f4xyMY80G6jKKktU6G0m00VDw=
-k8s.io/client-go v0.24.2 h1:CoXFSf8if+bLEbinDqN9ePIDGzcLtqhfd6jpfnwGOFA=
-k8s.io/client-go v0.24.2/go.mod h1:zg4Xaoo+umDsfCWr4fCnmLEtQXyCNXCvJuSsglNcV30=
 k8s.io/cluster-bootstrap v0.24.0 h1:MTs2x3Vfcl/PWvB5bfX7gzTFRyi4ZSbNSQgGJTCb6Sw=
 k8s.io/cluster-bootstrap v0.24.0/go.mod h1:xw+IfoaUweMCAoi+VYhmqkcjii2G7gNg59dmGn7hi0g=
 k8s.io/code-generator v0.24.0/go.mod h1:dpVhs00hTuTdTY6jvVxvTFCk6gSMrtfRydbhZwHI15w=
-k8s.io/code-generator v0.24.2/go.mod h1:dpVhs00hTuTdTY6jvVxvTFCk6gSMrtfRydbhZwHI15w=
+k8s.io/component-base v0.24.0 h1:h5jieHZQoHrY/lHG+HyrSbJeyfuitheBvqvKwKHVC0g=
 k8s.io/component-base v0.24.0/go.mod h1:Dgazgon0i7KYUsS8krG8muGiMVtUZxG037l1MKyXgrA=
-k8s.io/component-base v0.24.2 h1:kwpQdoSfbcH+8MPN4tALtajLDfSfYxBDYlXobNWI6OU=
-k8s.io/component-base v0.24.2/go.mod h1:ucHwW76dajvQ9B7+zecZAP3BVqvrHoOxm8olHEg0nmM=
 k8s.io/component-helpers v0.24.0/go.mod h1:Q2SlLm4h6g6lPTC9GMMfzdywfLSvJT2f1hOnnjaWD8c=
 k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
 k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
@@ -1382,8 +1377,8 @@ 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/apiserver-network-proxy/konnectivity-client v0.0.30/go.mod h1:fEO7lRTdivWO2qYVCVG7dEADOMo/MLDCVr8So2g88Uw=
-sigs.k8s.io/controller-runtime v0.12.2 h1:nqV02cvhbAj7tbt21bpPpTByrXGn2INHRsi39lXy9sE=
-sigs.k8s.io/controller-runtime v0.12.2/go.mod h1:qKsk4WE6zW2Hfj0G4v10EnNB2jMG1C+NTb8h+DwCoU0=
+sigs.k8s.io/controller-runtime v0.12.1 h1:4BJY01xe9zKQti8oRjj/NeHKRXthf1YkYJAgLONFFoI=
+sigs.k8s.io/controller-runtime v0.12.1/go.mod h1:BKhxlA4l7FPK4AQcsuL4X6vZeWnKDXez/vp1Y8dxTU0=
 sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 h1:kDi4JBNAsJWfz1aEXhO8Jg87JJaPNLh5tIzYHgStQ9Y=
 sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY=
 sigs.k8s.io/kustomize/api v0.11.4 h1:/0Mr3kfBBNcNPOW5Qwk/3eb8zkswCwnqQxxKtmrTkRo=
diff --git a/hack/tools/go.mod b/hack/tools/go.mod
index e9d5e1e80..fcec21b8b 100644
--- a/hack/tools/go.mod
+++ b/hack/tools/go.mod
@@ -16,16 +16,16 @@ require (
 	golang.org/x/net v0.0.0-20220617184016-355a448f1bc9
 	golang.org/x/tools v0.1.10-0.20220218145154-897bd77cd717
 	google.golang.org/api v0.81.0
-	k8s.io/api v0.24.2
-	k8s.io/apiextensions-apiserver v0.24.2
-	k8s.io/apimachinery v0.24.2
-	k8s.io/client-go v0.24.2
+	k8s.io/api v0.24.0
+	k8s.io/apiextensions-apiserver v0.24.0
+	k8s.io/apimachinery v0.24.0
+	k8s.io/client-go v0.24.0
 	k8s.io/klog/v2 v2.60.1
 	k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42
 	k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9
 	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.12.2
+	sigs.k8s.io/controller-runtime v0.12.1
 	sigs.k8s.io/controller-tools v0.8.0
 	sigs.k8s.io/kubebuilder/docs/book/utils v0.0.0-20211028165026-57688c578b5d
 	sigs.k8s.io/yaml v1.3.0
@@ -124,9 +124,9 @@ require (
 	gopkg.in/ini.v1 v1.66.4 // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect
 	gopkg.in/yaml.v3 v3.0.0 // indirect
-	k8s.io/apiserver v0.24.2 // indirect
+	k8s.io/apiserver v0.24.0 // indirect
 	k8s.io/cluster-bootstrap v0.24.0 // indirect
-	k8s.io/component-base v0.24.2 // indirect
+	k8s.io/component-base v0.24.0 // indirect
 	sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 // indirect
 	sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect
 )
diff --git a/hack/tools/go.sum b/hack/tools/go.sum
index f9477bf1a..eb6b18679 100644
--- a/hack/tools/go.sum
+++ b/hack/tools/go.sum
@@ -1346,33 +1346,28 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt
 honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
 k8s.io/api v0.23.0/go.mod h1:8wmDdLBHBNxtOIytwLstXt5E9PddnZb0GaMcqsvDBpg=
+k8s.io/api v0.24.0 h1:J0hann2hfxWr1hinZIDefw7Q96wmCBx6SSB8IY0MdDg=
 k8s.io/api v0.24.0/go.mod h1:5Jl90IUrJHUJYEMANRURMiVvJ0g7Ax7r3R1bqO8zx8I=
-k8s.io/api v0.24.2 h1:g518dPU/L7VRLxWfcadQn2OnsiGWVOadTLpdnqgY2OI=
-k8s.io/api v0.24.2/go.mod h1:AHqbSkTm6YrQ0ObxjO3Pmp/ubFF/KuM7jU+3khoBsOg=
 k8s.io/apiextensions-apiserver v0.23.0/go.mod h1:xIFAEEDlAZgpVBl/1VSjGDmLoXAWRG40+GsWhKhAxY4=
-k8s.io/apiextensions-apiserver v0.24.2 h1:/4NEQHKlEz1MlaK/wHT5KMKC9UKYz6NZz6JE6ov4G6k=
-k8s.io/apiextensions-apiserver v0.24.2/go.mod h1:e5t2GMFVngUEHUd0wuCJzw8YDwZoqZfJiGOW6mm2hLQ=
+k8s.io/apiextensions-apiserver v0.24.0 h1:JfgFqbA8gKJ/uDT++feAqk9jBIwNnL9YGdQvaI9DLtY=
+k8s.io/apiextensions-apiserver v0.24.0/go.mod h1:iuVe4aEpe6827lvO6yWQVxiPSpPoSKVjkq+MIdg84cM=
 k8s.io/apimachinery v0.23.0/go.mod h1:fFCTTBKvKcwTPFzjlcxp91uPFZr+JA0FubU4fLzzFYc=
+k8s.io/apimachinery v0.24.0 h1:ydFCyC/DjCvFCHK5OPMKBlxayQytB8pxy8YQInd5UyQ=
 k8s.io/apimachinery v0.24.0/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM=
-k8s.io/apimachinery v0.24.2 h1:5QlH9SL2C8KMcrNJPor+LbXVTaZRReml7svPEh4OKDM=
-k8s.io/apimachinery v0.24.2/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM=
 k8s.io/apiserver v0.23.0/go.mod h1:Cec35u/9zAepDPPFyT+UMrgqOCjgJ5qtfVJDxjZYmt4=
-k8s.io/apiserver v0.24.2 h1:orxipm5elPJSkkFNlwH9ClqaKEDJJA3yR2cAAlCnyj4=
-k8s.io/apiserver v0.24.2/go.mod h1:pSuKzr3zV+L+MWqsEo0kHHYwCo77AT5qXbFXP2jbvFI=
+k8s.io/apiserver v0.24.0 h1:GR7kGsjOMfilRvlG3Stxv/3uz/ryvJ/aZXc5pqdsNV0=
+k8s.io/apiserver v0.24.0/go.mod h1:WFx2yiOMawnogNToVvUYT9nn1jaIkMKj41ZYCVycsBA=
 k8s.io/cli-runtime v0.24.0/go.mod h1:9XxoZDsEkRFUThnwqNviqzljtT/LdHtNWvcNFrAXl0A=
 k8s.io/client-go v0.23.0/go.mod h1:hrDnpnK1mSr65lHHcUuIZIXDgEbzc7/683c6hyG4jTA=
+k8s.io/client-go v0.24.0 h1:lbE4aB1gTHvYFSwm6eD3OF14NhFDKCejlnsGYlSJe5U=
 k8s.io/client-go v0.24.0/go.mod h1:VFPQET+cAFpYxh6Bq6f4xyMY80G6jKKktU6G0m00VDw=
-k8s.io/client-go v0.24.2 h1:CoXFSf8if+bLEbinDqN9ePIDGzcLtqhfd6jpfnwGOFA=
-k8s.io/client-go v0.24.2/go.mod h1:zg4Xaoo+umDsfCWr4fCnmLEtQXyCNXCvJuSsglNcV30=
 k8s.io/cluster-bootstrap v0.24.0 h1:MTs2x3Vfcl/PWvB5bfX7gzTFRyi4ZSbNSQgGJTCb6Sw=
 k8s.io/cluster-bootstrap v0.24.0/go.mod h1:xw+IfoaUweMCAoi+VYhmqkcjii2G7gNg59dmGn7hi0g=
 k8s.io/code-generator v0.23.0/go.mod h1:vQvOhDXhuzqiVfM/YHp+dmg10WDZCchJVObc9MvowsE=
 k8s.io/code-generator v0.24.0/go.mod h1:dpVhs00hTuTdTY6jvVxvTFCk6gSMrtfRydbhZwHI15w=
-k8s.io/code-generator v0.24.2/go.mod h1:dpVhs00hTuTdTY6jvVxvTFCk6gSMrtfRydbhZwHI15w=
 k8s.io/component-base v0.23.0/go.mod h1:DHH5uiFvLC1edCpvcTDV++NKULdYYU6pR9Tt3HIKMKI=
+k8s.io/component-base v0.24.0 h1:h5jieHZQoHrY/lHG+HyrSbJeyfuitheBvqvKwKHVC0g=
 k8s.io/component-base v0.24.0/go.mod h1:Dgazgon0i7KYUsS8krG8muGiMVtUZxG037l1MKyXgrA=
-k8s.io/component-base v0.24.2 h1:kwpQdoSfbcH+8MPN4tALtajLDfSfYxBDYlXobNWI6OU=
-k8s.io/component-base v0.24.2/go.mod h1:ucHwW76dajvQ9B7+zecZAP3BVqvrHoOxm8olHEg0nmM=
 k8s.io/component-helpers v0.24.0/go.mod h1:Q2SlLm4h6g6lPTC9GMMfzdywfLSvJT2f1hOnnjaWD8c=
 k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
 k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
@@ -1397,8 +1392,8 @@ 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/apiserver-network-proxy/konnectivity-client v0.0.25/go.mod h1:Mlj9PNLmG9bZ6BHFwFKDo5afkpWyUISkb9Me0GnK66I=
 sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.30/go.mod h1:fEO7lRTdivWO2qYVCVG7dEADOMo/MLDCVr8So2g88Uw=
-sigs.k8s.io/controller-runtime v0.12.2 h1:nqV02cvhbAj7tbt21bpPpTByrXGn2INHRsi39lXy9sE=
-sigs.k8s.io/controller-runtime v0.12.2/go.mod h1:qKsk4WE6zW2Hfj0G4v10EnNB2jMG1C+NTb8h+DwCoU0=
+sigs.k8s.io/controller-runtime v0.12.1 h1:4BJY01xe9zKQti8oRjj/NeHKRXthf1YkYJAgLONFFoI=
+sigs.k8s.io/controller-runtime v0.12.1/go.mod h1:BKhxlA4l7FPK4AQcsuL4X6vZeWnKDXez/vp1Y8dxTU0=
 sigs.k8s.io/controller-tools v0.8.0 h1:uUkfTGEwrguqYYfcI2RRGUnC8mYdCFDqfwPKUcNJh1o=
 sigs.k8s.io/controller-tools v0.8.0/go.mod h1:qE2DXhVOiEq5ijmINcFbqi9GZrrUjzB1TuJU0xa6eoY=
 sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6/go.mod h1:p4QtZmO4uMYipTQNzagwnNoseA6OxSUutVw05NhYDRs=
diff --git a/internal/controllers/machine/machine_controller.go b/internal/controllers/machine/machine_controller.go
index 0ed6d12c5..0fc79a564 100644
--- a/internal/controllers/machine/machine_controller.go
+++ b/internal/controllers/machine/machine_controller.go
@@ -525,7 +525,7 @@ func (r *Reconciler) drainNode(ctx context.Context, cluster *clusterv1.Cluster,
 			log.Error(err, "Could not find node from noderef, it may have already been deleted")
 			return ctrl.Result{}, nil
 		}
-		return ctrl.Result{}, errors.Wrapf(err, "unable to get node %v", nodeName)
+		return ctrl.Result{}, errors.Errorf("unable to get node %q: %v", nodeName, err)
 	}
 
 	drainer := &kubedrain.Helper{
@@ -560,7 +560,7 @@ func (r *Reconciler) drainNode(ctx context.Context, cluster *clusterv1.Cluster,
 	if err := kubedrain.RunCordonOrUncordon(drainer, node, true); err != nil {
 		// Machine will be re-reconciled after a cordon failure.
 		log.Error(err, "Cordon failed")
-		return ctrl.Result{}, errors.Wrapf(err, "unable to cordon node %v", node.Name)
+		return ctrl.Result{}, errors.Errorf("unable to cordon node %s: %v", node.Name, err)
 	}
 
 	if err := kubedrain.RunNodeDrain(drainer, node.Name); err != nil {
diff --git a/internal/controllers/topology/cluster/cluster_controller.go b/internal/controllers/topology/cluster/cluster_controller.go
index a40862b12..7d76b0f46 100644
--- a/internal/controllers/topology/cluster/cluster_controller.go
+++ b/internal/controllers/topology/cluster/cluster_controller.go
@@ -42,7 +42,6 @@ import (
 	"sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/patches"
 	"sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/scope"
 	"sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/structuredmerge"
-	runtimecatalog "sigs.k8s.io/cluster-api/internal/runtime/catalog"
 	runtimeclient "sigs.k8s.io/cluster-api/internal/runtime/client"
 	"sigs.k8s.io/cluster-api/util"
 	"sigs.k8s.io/cluster-api/util/annotations"
@@ -281,7 +280,7 @@ func (r *Reconciler) callBeforeClusterCreateHook(ctx context.Context, s *scope.S
 		}
 		hookResponse := &runtimehooksv1.BeforeClusterCreateResponse{}
 		if err := r.RuntimeClient.CallAllExtensions(ctx, runtimehooksv1.BeforeClusterCreate, s.Current.Cluster, hookRequest, hookResponse); err != nil {
-			return ctrl.Result{}, errors.Wrapf(err, "error calling the %s hook", runtimecatalog.HookName(runtimehooksv1.BeforeClusterCreate))
+			return ctrl.Result{}, err
 		}
 		s.HookResponseTracker.Add(runtimehooksv1.BeforeClusterCreate, hookResponse)
 		if hookResponse.RetryAfterSeconds != 0 {
diff --git a/internal/controllers/topology/cluster/desired_state.go b/internal/controllers/topology/cluster/desired_state.go
index 0fdbc3aa7..694c041fe 100644
--- a/internal/controllers/topology/cluster/desired_state.go
+++ b/internal/controllers/topology/cluster/desired_state.go
@@ -326,7 +326,7 @@ func (r *Reconciler) computeControlPlaneVersion(ctx context.Context, s *scope.Sc
 				}
 				hookResponse := &runtimehooksv1.AfterControlPlaneUpgradeResponse{}
 				if err := r.RuntimeClient.CallAllExtensions(ctx, runtimehooksv1.AfterControlPlaneUpgrade, s.Current.Cluster, hookRequest, hookResponse); err != nil {
-					return "", errors.Wrapf(err, "error calling the %s hook", runtimecatalog.HookName(runtimehooksv1.AfterControlPlaneUpgrade))
+					return "", err
 				}
 				// Add the response to the tracker so we can later update condition or requeue when required.
 				s.HookResponseTracker.Add(runtimehooksv1.AfterControlPlaneUpgrade, hookResponse)
@@ -378,7 +378,7 @@ func (r *Reconciler) computeControlPlaneVersion(ctx context.Context, s *scope.Sc
 		}
 		hookResponse := &runtimehooksv1.BeforeClusterUpgradeResponse{}
 		if err := r.RuntimeClient.CallAllExtensions(ctx, runtimehooksv1.BeforeClusterUpgrade, s.Current.Cluster, hookRequest, hookResponse); err != nil {
-			return "", errors.Wrapf(err, "failed to call %s hook", runtimecatalog.HookName(runtimehooksv1.BeforeClusterUpgrade))
+			return "", err
 		}
 		// Add the response to the tracker so we can later update condition or requeue when required.
 		s.HookResponseTracker.Add(runtimehooksv1.BeforeClusterUpgrade, hookResponse)
diff --git a/internal/controllers/topology/cluster/reconcile_state.go b/internal/controllers/topology/cluster/reconcile_state.go
index 2e93a13e0..2a1b1ed3b 100644
--- a/internal/controllers/topology/cluster/reconcile_state.go
+++ b/internal/controllers/topology/cluster/reconcile_state.go
@@ -218,11 +218,11 @@ func (r *Reconciler) callAfterControlPlaneInitialized(ctx context.Context, s *sc
 			}
 			hookResponse := &runtimehooksv1.AfterControlPlaneInitializedResponse{}
 			if err := r.RuntimeClient.CallAllExtensions(ctx, runtimehooksv1.AfterControlPlaneInitialized, s.Current.Cluster, hookRequest, hookResponse); err != nil {
-				return errors.Wrapf(err, "failed to call %s hook", runtimecatalog.HookName(runtimehooksv1.AfterControlPlaneInitialized))
+				return err
 			}
 			s.HookResponseTracker.Add(runtimehooksv1.AfterControlPlaneInitialized, hookResponse)
 			if err := hooks.MarkAsDone(ctx, r.Client, s.Current.Cluster, runtimehooksv1.AfterControlPlaneInitialized); err != nil {
-				return errors.Wrapf(err, "failed to unmark %s hook", runtimecatalog.HookName(runtimehooksv1.AfterControlPlaneInitialized))
+				return err
 			}
 		}
 	}
@@ -280,12 +280,12 @@ func (r *Reconciler) callAfterClusterUpgrade(ctx context.Context, s *scope.Scope
 			}
 			hookResponse := &runtimehooksv1.AfterClusterUpgradeResponse{}
 			if err := r.RuntimeClient.CallAllExtensions(ctx, runtimehooksv1.AfterClusterUpgrade, s.Current.Cluster, hookRequest, hookResponse); err != nil {
-				return errors.Wrapf(err, "failed to call %s hook", runtimecatalog.HookName(runtimehooksv1.AfterClusterUpgrade))
+				return err
 			}
 			s.HookResponseTracker.Add(runtimehooksv1.AfterClusterUpgrade, hookResponse)
 			// The hook is successfully called; we can remove this hook from the list of pending-hooks.
 			if err := hooks.MarkAsDone(ctx, r.Client, s.Current.Cluster, runtimehooksv1.AfterClusterUpgrade); err != nil {
-				return errors.Wrapf(err, "failed to remove the %s hook from pending hooks tracker", runtimecatalog.HookName(runtimehooksv1.AfterClusterUpgrade))
+				return err
 			}
 		}
 	}
diff --git a/internal/hooks/tracking.go b/internal/hooks/tracking.go
index 55a0d1c9c..62493e734 100644
--- a/internal/hooks/tracking.go
+++ b/internal/hooks/tracking.go
@@ -26,6 +26,7 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/client"
 
 	runtimev1 "sigs.k8s.io/cluster-api/exp/runtime/api/v1alpha1"
+	tlog "sigs.k8s.io/cluster-api/internal/log"
 	runtimecatalog "sigs.k8s.io/cluster-api/internal/runtime/catalog"
 	"sigs.k8s.io/cluster-api/util/patch"
 )
@@ -35,10 +36,10 @@ import (
 func MarkAsPending(ctx context.Context, c client.Client, obj client.Object, hooks ...runtimecatalog.Hook) error {
 	patchHelper, err := patch.NewHelper(obj, c)
 	if err != nil {
-		return errors.Wrap(err, "failed to create patch helper")
+		return errors.Wrapf(err, "failed to create patch helper for %s", tlog.KObj{Obj: obj})
 	}
 
-	// read the annotation of the objects and add the hook to the comma separated list
+	// Read the annotation of the objects and add the hook to the comma separated list
 	hookNames := []string{}
 	for _, hook := range hooks {
 		hookNames = append(hookNames, runtimecatalog.HookName(hook))
@@ -51,7 +52,7 @@ func MarkAsPending(ctx context.Context, c client.Client, obj client.Object, hook
 	obj.SetAnnotations(annotations)
 
 	if err := patchHelper.Patch(ctx, obj); err != nil {
-		return errors.Wrap(err, "failed to apply patch")
+		return errors.Wrapf(err, "failed to patch %s", tlog.KObj{Obj: obj})
 	}
 
 	return nil
@@ -73,10 +74,10 @@ func IsPending(hook runtimecatalog.Hook, obj client.Object) bool {
 func MarkAsDone(ctx context.Context, c client.Client, obj client.Object, hooks ...runtimecatalog.Hook) error {
 	patchHelper, err := patch.NewHelper(obj, c)
 	if err != nil {
-		return errors.Wrap(err, "failed to create patch helper")
+		return errors.Wrapf(err, "failed to create patch helper for %s", tlog.KObj{Obj: obj})
 	}
 
-	// read the annotation of the objects and add the hook to the comma separated list
+	// Read the annotation of the objects and add the hook to the comma separated list
 	hookNames := []string{}
 	for _, hook := range hooks {
 		hookNames = append(hookNames, runtimecatalog.HookName(hook))
@@ -92,7 +93,7 @@ func MarkAsDone(ctx context.Context, c client.Client, obj client.Object, hooks .
 	obj.SetAnnotations(annotations)
 
 	if err := patchHelper.Patch(ctx, obj); err != nil {
-		return errors.Wrap(err, "failed to apply patch")
+		return errors.Wrapf(err, "failed to patch %s", tlog.KObj{Obj: obj})
 	}
 
 	return nil
diff --git a/internal/runtime/catalog/builder.go b/internal/runtime/catalog/builder.go
index 2bfe52469..1b163dca2 100644
--- a/internal/runtime/catalog/builder.go
+++ b/internal/runtime/catalog/builder.go
@@ -38,7 +38,7 @@ type Builder struct {
 	schemeBuilder runtime.SchemeBuilder
 }
 
-// RegisterHook registers a Hook and its request and response types.
+// RegisterHook registers a Hook with its request and response types and metadata.
 // The passed in hookFunc must have the following type: func(*RequestType,*ResponseType)
 // The name of the func becomes the "hook" field of the GroupVersionHook.
 func (bld *Builder) RegisterHook(hookFunc Hook, hookMeta *HookMeta) *Builder {
@@ -59,8 +59,8 @@ func (bld *Builder) RegisterOpenAPIDefinitions(getter OpenAPIDefinitionsGetter)
 	return bld
 }
 
-// AddToCatalog adds all registered Hooks their request and response types and the
-// OpenAPIDefinitions to the catalog.
+// AddToCatalog adds all registered Hooks with their request and response types and metadata
+// and the OpenAPIDefinitions to the catalog.
 func (bld *Builder) AddToCatalog(catalog *Catalog) error {
 	for _, addTo := range bld.catalogBuilder {
 		addTo(catalog)
@@ -72,13 +72,13 @@ func (bld *Builder) AddToCatalog(catalog *Catalog) error {
 // Note: This function is used by generated conversion code.
 // If we would just expose the func from the embedded schemeBuilder
 // directly it would not work because it is nil at that time and
-// appending to a nil schemeBuilder doesn't propagate to our builder.
+// appending to a nil schemeBuilder doesn't propagate to our Builder.
 func (bld *Builder) Register(f func(*runtime.Scheme) error) {
 	bld.schemeBuilder.Register(f)
 }
 
-// Build returns a new Catalog containing all registered Hooks their request and response types
-// and the OpenAPIDefinitions.
+// Build returns a new Catalog containing all registered Hooks with their request and response types
+// and metadata and the OpenAPIDefinitions.
 func (bld *Builder) Build() (*Catalog, error) {
 	s := New()
 	return s, bld.AddToCatalog(s)
diff --git a/internal/runtime/catalog/catalog.go b/internal/runtime/catalog/catalog.go
index 431aa5ffb..57f236acc 100644
--- a/internal/runtime/catalog/catalog.go
+++ b/internal/runtime/catalog/catalog.go
@@ -40,15 +40,14 @@ type Catalog struct {
 	// gvhToType maps a GroupVersionHook to the corresponding RuntimeHook function.
 	gvhToType map[GroupVersionHook]reflect.Type
 
-	// gvhToType maps a RuntimeHook to the corresponding GroupVersionHook.
+	// typeToGVH maps a RuntimeHook function to the corresponding GroupVersionHook.
 	typeToGVH map[reflect.Type]GroupVersionHook
 
 	// gvhToHookDescriptor maps a GroupVersionHook to the corresponding hook descriptor.
 	gvhToHookDescriptor map[GroupVersionHook]hookDescriptor
 
-	// openAPIDefinitions maps a GroupVersion to a func
-	// which returns OpenAPI definitions for all request and response types with that
-	// GroupVersion.
+	// openAPIDefinitions is a list of OpenAPIDefinitionsGetter, which return OpenAPI definitions
+	// for all request and response types of a GroupVersion.
 	openAPIDefinitions []OpenAPIDefinitionsGetter
 
 	// catalogName is the name of this catalog. It is set based on the stack of the New caller.
@@ -56,8 +55,9 @@ type Catalog struct {
 	catalogName string
 }
 
-// Hook is a marker interface for a hook.
-// Hooks should be defined as a: func(*RequestType, *ResponseType).
+// Hook is a marker interface for a RuntimeHook function.
+// RuntimeHook functions should be defined as a: func(*RequestType, *ResponseType).
+// The name of the func should be the name of the hook.
 type Hook interface{}
 
 // hookDescriptor is a data structure which holds
@@ -87,7 +87,7 @@ type HookMeta struct {
 	Deprecated bool
 
 	// Singleton signals if the hook can only be implemented once on a
-	// Runtime Extension, e.g. like the DiscoveryHook.
+	// Runtime Extension, e.g. like the Discovery hook.
 	Singleton bool
 }
 
@@ -104,44 +104,44 @@ func New() *Catalog {
 		typeToGVH:           map[reflect.Type]GroupVersionHook{},
 		gvhToHookDescriptor: map[GroupVersionHook]hookDescriptor{},
 		openAPIDefinitions:  []OpenAPIDefinitionsGetter{},
-		// Note: We have to ignore the current file so that GetNameFromCallsite retrieves the name of the caller (the parent).
+		// Note: We have to ignore the current file so that GetNameFromCallsite retrieves the name of the caller of New (the parent).
 		catalogName: naming.GetNameFromCallsite("sigs.k8s.io/cluster-api/internal/runtime/catalog/catalog.go"),
 	}
 }
 
-// AddHook adds a HookFunc and its request and response types with the gv GroupVersion.
+// AddHook adds a RuntimeHook function and its request and response types with the gv GroupVersion.
 // The passed in hookFunc must have the following type: func(*RequestType,*ResponseType)
-// The name of the func becomes the "hook" in GroupVersionHook.
+// The name of the func becomes the "Hook" in GroupVersionHook.
 // GroupVersion must not have empty fields.
 func (c *Catalog) AddHook(gv schema.GroupVersion, hookFunc Hook, hookMeta *HookMeta) {
 	// Validate gv.Group and gv.Version are not empty.
 	if gv.Group == "" {
-		panic("group must not be empty")
+		panic("Group must not be empty")
 	}
 	if gv.Version == "" {
-		panic("version must not be empty")
+		panic("Version must not be empty")
 	}
 
 	// Validate that hookFunc is a func.
 	t := reflect.TypeOf(hookFunc)
 	if t.Kind() != reflect.Func {
-		panic("hook must be a func")
+		panic("Hook must be a func")
 	}
 	if t.NumIn() != 2 {
-		panic("hook must have two input parameter: *RequestType, *ResponseType")
+		panic("Hook must have two input parameter: *RequestType, *ResponseType")
 	}
 	if t.NumOut() != 0 {
-		panic("hook must have no output parameter")
+		panic("Hook must have no output parameter")
 	}
 
 	// Create request and response objects based on the input types.
 	request, ok := reflect.New(t.In(0).Elem()).Interface().(runtime.Object)
 	if !ok {
-		panic("hook request (first parameter) must be a runtime.Object")
+		panic("Hook request (first parameter) must be a runtime.Object")
 	}
 	response, ok := reflect.New(t.In(1).Elem()).Interface().(runtime.Object)
 	if !ok {
-		panic("hook response (second parameter) must be a runtime.Object")
+		panic("Hook response (second parameter) must be a runtime.Object")
 	}
 
 	// Calculate the hook name based on the func name.
@@ -155,10 +155,10 @@ func (c *Catalog) AddHook(gv schema.GroupVersion, hookFunc Hook, hookMeta *HookM
 
 	// Validate that the GVH is not already registered with another type.
 	if oldT, found := c.gvhToType[gvh]; found && oldT != t {
-		panic(fmt.Sprintf("double registration of different type for %v: old=%v.%v, new=%v.%v in catalog %q", gvh, oldT.PkgPath(), oldT.Name(), t.PkgPath(), t.Name(), c.catalogName))
+		panic(fmt.Sprintf("Double registration of different type for %v: old=%v.%v, new=%v.%v in catalog %q", gvh, oldT.PkgPath(), oldT.Name(), t.PkgPath(), t.Name(), c.catalogName))
 	}
 
-	// Add GVH <=> RuntimeHook mappings.
+	// Add GVH <=> RuntimeHook function mappings.
 	c.gvhToType[gvh] = t
 	c.typeToGVH[t] = gvh
 
@@ -169,11 +169,11 @@ func (c *Catalog) AddHook(gv schema.GroupVersion, hookFunc Hook, hookMeta *HookM
 	// Create a hook descriptor and store it in the GVH => Descriptor map.
 	requestGVK, err := c.GroupVersionKind(request)
 	if err != nil {
-		panic(fmt.Sprintf("failed to get GVK for request %T: %v", request, err))
+		panic(fmt.Sprintf("Failed to get GVK for request %T: %v", request, err))
 	}
 	responseGVK, err := c.GroupVersionKind(response)
 	if err != nil {
-		panic(fmt.Sprintf("failed to get GVK for response %T: %v", request, err))
+		panic(fmt.Sprintf("Failed to get GVK for response %T: %v", request, err))
 	}
 	if hookMeta == nil {
 		panic("hookMeta cannot be nil")
@@ -185,7 +185,7 @@ func (c *Catalog) AddHook(gv schema.GroupVersion, hookFunc Hook, hookMeta *HookM
 	}
 }
 
-// AddOpenAPIDefinitions adds an OpenAPIDefinitionsGetter with the gv GroupVersion.
+// AddOpenAPIDefinitions adds an OpenAPIDefinitionsGetter.
 func (c *Catalog) AddOpenAPIDefinitions(getter OpenAPIDefinitionsGetter) {
 	c.openAPIDefinitions = append(c.openAPIDefinitions, getter)
 }
@@ -196,18 +196,18 @@ func (c *Catalog) Convert(in, out interface{}, context interface{}) error {
 	return c.scheme.Convert(in, out, context)
 }
 
-// GroupVersionHook returns the GVH of the Hook or an error if hook is not a function
+// GroupVersionHook returns the GVH of the hookFunc or an error if hook is not a function
 // or not registered.
 func (c *Catalog) GroupVersionHook(hookFunc Hook) (GroupVersionHook, error) {
 	// Validate that hookFunc is a func.
 	t := reflect.TypeOf(hookFunc)
 	if t.Kind() != reflect.Func {
-		return emptyGroupVersionHook, errors.Errorf("hook %T is not a func", hookFunc)
+		return emptyGroupVersionHook, errors.Errorf("hook %s is not a func", HookName(hookFunc))
 	}
 
 	gvh, ok := c.typeToGVH[t]
 	if !ok {
-		return emptyGroupVersionHook, errors.Errorf("hook %T is not registered in catalog %q", hookFunc, c.catalogName)
+		return emptyGroupVersionHook, errors.Errorf("hook %s is not registered in catalog %q", HookName(hookFunc), c.catalogName)
 	}
 	return gvh, nil
 }
@@ -230,7 +230,7 @@ func (c *Catalog) GroupVersionKind(obj runtime.Object) (schema.GroupVersionKind,
 func (c *Catalog) Request(hook GroupVersionHook) (schema.GroupVersionKind, error) {
 	descriptor, ok := c.gvhToHookDescriptor[hook]
 	if !ok {
-		return emptyGroupVersionKind, errors.Errorf("hook %T is not registered in catalog %q", hook, c.catalogName)
+		return emptyGroupVersionKind, errors.Errorf("failed to get request GVK for hook %s: hook is not registered in catalog %q", hook, c.catalogName)
 	}
 
 	return descriptor.request, nil
@@ -240,7 +240,7 @@ func (c *Catalog) Request(hook GroupVersionHook) (schema.GroupVersionKind, error
 func (c *Catalog) Response(hook GroupVersionHook) (schema.GroupVersionKind, error) {
 	descriptor, ok := c.gvhToHookDescriptor[hook]
 	if !ok {
-		return emptyGroupVersionKind, errors.Errorf("hook %T is not registered in catalog %q", hook, c.catalogName)
+		return emptyGroupVersionKind, errors.Errorf("failed to get response GVK for hook %s: hook is not registered in catalog %q", hook, c.catalogName)
 	}
 
 	return descriptor.response, nil
@@ -250,11 +250,11 @@ func (c *Catalog) Response(hook GroupVersionHook) (schema.GroupVersionKind, erro
 func (c *Catalog) NewRequest(hook GroupVersionHook) (runtime.Object, error) {
 	descriptor, ok := c.gvhToHookDescriptor[hook]
 	if !ok {
-		return nil, errors.Errorf("hook %T is not registered in catalog %q", hook, c.catalogName)
+		return nil, errors.Errorf("failed to create request object for hook %s: hook is not registered in catalog %q", hook, c.catalogName)
 	}
 	obj, err := c.scheme.New(descriptor.request)
 	if err != nil {
-		return nil, errors.Wrap(err, "failed to create request object")
+		return nil, errors.Wrapf(err, "failed to create request object for hook %s", hook)
 	}
 	return obj, nil
 }
@@ -263,11 +263,11 @@ func (c *Catalog) NewRequest(hook GroupVersionHook) (runtime.Object, error) {
 func (c *Catalog) NewResponse(hook GroupVersionHook) (runtime.Object, error) {
 	descriptor, ok := c.gvhToHookDescriptor[hook]
 	if !ok {
-		return nil, errors.Errorf("hook %T is not registered in catalog %q", hook, c.catalogName)
+		return nil, errors.Errorf("failed to create response object for hook %s: hook is not registered in catalog %q", hook, c.catalogName)
 	}
 	obj, err := c.scheme.New(descriptor.response)
 	if err != nil {
-		return nil, errors.Wrap(err, "failed to create response object")
+		return nil, errors.Wrapf(err, "failed to create response object for hook %s", hook)
 	}
 	return obj, nil
 }
@@ -278,17 +278,17 @@ func (c *Catalog) ValidateRequest(hook GroupVersionHook, obj runtime.Object) err
 	// Get GVK of obj.
 	objGVK, err := c.GroupVersionKind(obj)
 	if err != nil {
-		return err
+		return errors.Wrapf(err, "failed to validate request for hook %s", hook)
 	}
 
 	// Get request GVK from hook.
 	hookGVK, err := c.Request(hook)
 	if err != nil {
-		return err
+		return errors.Wrapf(err, "failed to validate request for hook %s", hook)
 	}
 
 	if objGVK != hookGVK {
-		return errors.Errorf("request object has invalid GVK %q, expected %q", objGVK, hookGVK)
+		return errors.Errorf("request object of hook %s has invalid GVK %q, expected %q", hook, objGVK, hookGVK)
 	}
 	return nil
 }
@@ -299,17 +299,17 @@ func (c *Catalog) ValidateResponse(hook GroupVersionHook, obj runtime.Object) er
 	// Get GVK of obj.
 	objGVK, err := c.GroupVersionKind(obj)
 	if err != nil {
-		return err
+		return errors.Wrapf(err, "failed to validate response for hook %s", hook)
 	}
 
 	// Get response GVK from hook.
 	hookGVK, err := c.Response(hook)
 	if err != nil {
-		return err
+		return errors.Wrapf(err, "failed to validate response for hook %s", hook)
 	}
 
 	if objGVK != hookGVK {
-		return errors.Errorf("response object has invalid GVK %q, expected %q", objGVK, hookGVK)
+		return errors.Errorf("response object of hook %s has invalid GVK %q, expected %q", hook, objGVK, hookGVK)
 	}
 	return nil
 }
@@ -327,7 +327,7 @@ type GroupVersionHook struct {
 	Hook    string
 }
 
-// Empty returns true if group, version, and hook are empty.
+// Empty returns true if group, version and hook are empty.
 func (gvh GroupVersionHook) Empty() bool {
 	return gvh.Group == "" && gvh.Version == "" && gvh.Hook == ""
 }
@@ -342,13 +342,15 @@ func (gvh GroupVersionHook) GroupHook() GroupHook {
 	return GroupHook{Group: gvh.Group, Hook: gvh.Hook}
 }
 
+// String returns a string representation of a GroupVersionHook.
 func (gvh GroupVersionHook) String() string {
 	return strings.Join([]string{gvh.Group, "/", gvh.Version, ", Hook=", gvh.Hook}, "")
 }
 
 // HookName returns the name of the runtime hook.
-func HookName(hook Hook) string {
-	hookFuncName := goruntime.FuncForPC(reflect.ValueOf(hook).Pointer()).Name()
+// Note: The name of the hook is the name of the hookFunc.
+func HookName(hookFunc Hook) string {
+	hookFuncName := goruntime.FuncForPC(reflect.ValueOf(hookFunc).Pointer()).Name()
 	hookName := hookFuncName[strings.LastIndex(hookFuncName, ".")+1:]
 	return hookName
 }
@@ -365,6 +367,7 @@ type GroupHook struct {
 	Hook  string
 }
 
+// String returns a string representation of a GroupHook.
 func (gh GroupHook) String() string {
 	if gh.Group == "" {
 		return gh.Hook
@@ -375,7 +378,7 @@ func (gh GroupHook) String() string {
 // GVHToPath calculates the path for a given GroupVersionHook.
 // This func is aligned with Kubernetes paths for cluster-wide resources, e.g.:
 // /apis/storage.k8s.io/v1/storageclasses/standard.
-// Note: name is only appended if set, e.g. the Discovery Hook does not have a name.
+// Note: name is only appended if set, e.g. the Discovery hook does not have a name.
 func GVHToPath(gvh GroupVersionHook, name string) string {
 	if name == "" {
 		return fmt.Sprintf("/%s/%s/%s", gvh.Group, gvh.Version, strings.ToLower(gvh.Hook))
diff --git a/internal/runtime/catalog/openapi.go b/internal/runtime/catalog/openapi.go
index 6bd77994a..eb8b2279d 100644
--- a/internal/runtime/catalog/openapi.go
+++ b/internal/runtime/catalog/openapi.go
@@ -184,7 +184,7 @@ func addTypeToOpenAPI(openAPI *spec3.OpenAPI, c *Catalog, typeName string) error
 	}
 
 	if openAPIDefinition == nil {
-		return errors.Errorf("failed to get definition for %v. If you added a new type, you may need to add +k8s:openapi-gen=true to the package or type and run code-gen again", typeName)
+		return errors.Errorf("failed to get definition for %v. If you added a new type, you may need to add +k8s:openapi-gen=true to the package or type and run openapi-gen again", typeName)
 	}
 
 	// Add schema for component to components.
diff --git a/internal/runtime/client/client.go b/internal/runtime/client/client.go
index cc5d7333b..fe2d7b041 100644
--- a/internal/runtime/client/client.go
+++ b/internal/runtime/client/client.go
@@ -65,11 +65,11 @@ func New(options Options) Client {
 	return &client{
 		catalog:  options.Catalog,
 		registry: options.Registry,
-		Client:   options.Client,
+		client:   options.Client,
 	}
 }
 
-// Client is the runtime client to interact with hooks and extensions.
+// Client is the runtime client to interact with extensions.
 type Client interface {
 	// WarmUp can be used to initialize a "cold" RuntimeClient with all
 	// known runtimev1.ExtensionConfigs at a given time.
@@ -80,7 +80,7 @@ type Client interface {
 	IsReady() bool
 
 	// Discover makes the discovery call on the extension and returns an updated ExtensionConfig
-	// with extension handlers information in the extension status.
+	// with extension handlers information in the ExtensionConfig status.
 	Discover(context.Context, *runtimev1.ExtensionConfig) (*runtimev1.ExtensionConfig, error)
 
 	// Register registers the ExtensionConfig.
@@ -92,7 +92,7 @@ type Client interface {
 	// CallAllExtensions calls all the ExtensionHandler registered for the hook.
 	CallAllExtensions(ctx context.Context, hook runtimecatalog.Hook, forObject metav1.Object, request runtime.Object, response runtimehooksv1.ResponseObject) error
 
-	// CallExtension calls only the ExtensionHandler with the given name.
+	// CallExtension calls the ExtensionHandler with the given name.
 	CallExtension(ctx context.Context, hook runtimecatalog.Hook, forObject metav1.Object, name string, request runtime.Object, response runtimehooksv1.ResponseObject) error
 }
 
@@ -101,12 +101,12 @@ var _ Client = &client{}
 type client struct {
 	catalog  *runtimecatalog.Catalog
 	registry runtimeregistry.ExtensionRegistry
-	Client   ctrlclient.Client
+	client   ctrlclient.Client
 }
 
 func (c *client) WarmUp(extensionConfigList *runtimev1.ExtensionConfigList) error {
 	if err := c.registry.WarmUp(extensionConfigList); err != nil {
-		return errors.Wrap(err, "failed to warm up")
+		return err
 	}
 	return nil
 }
@@ -118,7 +118,7 @@ func (c *client) IsReady() bool {
 func (c *client) Discover(ctx context.Context, extensionConfig *runtimev1.ExtensionConfig) (*runtimev1.ExtensionConfig, error) {
 	hookGVH, err := c.catalog.GroupVersionHook(runtimehooksv1.Discovery)
 	if err != nil {
-		return nil, errors.Wrap(err, "failed to compute GVH of hook")
+		return nil, errors.Wrapf(err, "failed to discover extension %q: failed to compute GVH of hook", extensionConfig.Name)
 	}
 
 	request := &runtimehooksv1.DiscoveryRequest{}
@@ -131,16 +131,17 @@ func (c *client) Discover(ctx context.Context, extensionConfig *runtimev1.Extens
 		timeout:         defaultDiscoveryTimeout,
 	}
 	if err := httpCall(ctx, request, response, opts); err != nil {
-		return nil, errors.Wrap(err, "failed to call the Discovery endpoint")
+		return nil, errors.Wrapf(err, "failed to discover extension %q", extensionConfig.Name)
 	}
+
 	// Check to see if the response is a failure and handle the failure accordingly.
 	if response.GetStatus() == runtimehooksv1.ResponseStatusFailure {
-		return nil, errors.Errorf("discovery failed with %v", response.GetMessage())
+		return nil, errors.Errorf("failed to discover extension %q: got failure response with message %q", extensionConfig.Name, response.GetMessage())
 	}
 
 	// Check to see if the response is valid.
 	if err = defaultAndValidateDiscoveryResponse(c.catalog, response); err != nil {
-		return nil, errors.Wrapf(err, "discovery response validation failed")
+		return nil, errors.Wrapf(err, "failed to discover extension %q", extensionConfig.Name)
 	}
 
 	modifiedExtensionConfig := extensionConfig.DeepCopy()
@@ -167,40 +168,40 @@ func (c *client) Discover(ctx context.Context, extensionConfig *runtimev1.Extens
 
 func (c *client) Register(extensionConfig *runtimev1.ExtensionConfig) error {
 	if err := c.registry.Add(extensionConfig); err != nil {
-		return errors.Wrap(err, "failed to register ExtensionConfig")
+		return errors.Wrapf(err, "failed to register ExtensionConfig %q", extensionConfig.Name)
 	}
 	return nil
 }
 
 func (c *client) Unregister(extensionConfig *runtimev1.ExtensionConfig) error {
 	if err := c.registry.Remove(extensionConfig); err != nil {
-		return errors.Wrap(err, "failed to unregister ExtensionConfig")
+		return errors.Wrapf(err, "failed to unregister ExtensionConfig %q", extensionConfig.Name)
 	}
 	return nil
 }
 
 // CallAllExtensions calls all the ExtensionHandlers registered for the hook.
-// The ExtensionHandler are called sequentially. The function exits immediately after any of the ExtensionHandlers return an error.
+// The ExtensionHandlers are called sequentially. The function exits immediately after any of the ExtensionHandlers return an error.
 // This ensures we don't end up waiting for timeout from multiple unreachable Extensions.
 // See CallExtension for more details on when an ExtensionHandler returns an error.
-// The aggregate result of the ExtensionHandlers is updated into the response object passed to the function.
+// The aggregated result of the ExtensionHandlers is updated into the response object passed to the function.
 func (c *client) CallAllExtensions(ctx context.Context, hook runtimecatalog.Hook, forObject metav1.Object, request runtime.Object, response runtimehooksv1.ResponseObject) error {
 	gvh, err := c.catalog.GroupVersionHook(hook)
 	if err != nil {
-		return errors.Wrap(err, "failed to compute GroupVersionHook")
+		return errors.Wrapf(err, "failed to call extension handlers for hook %q: failed to compute GroupVersionHook", runtimecatalog.HookName(hook))
 	}
-	// Make sure the request is compatible with the version of the hook expected by the ExtensionHandler.
+	// Make sure the request is compatible with the hook.
 	if err := c.catalog.ValidateRequest(gvh, request); err != nil {
-		return errors.Wrapf(err, "request object is invalid for hook %s", gvh)
+		return errors.Wrapf(err, "failed to call extension handlers for hook %q: request object is invalid for hook", gvh.GroupHook())
 	}
-	// Make sure the response is compatible with the version of the hook expected by the ExtensionHandler.
+	// Make sure the response is compatible with the hook.
 	if err := c.catalog.ValidateResponse(gvh, response); err != nil {
-		return errors.Wrapf(err, "response object is invalid for hook %s", gvh)
+		return errors.Wrapf(err, "failed to call extension handlers for hook %q: response object is invalid for hook", gvh.GroupHook())
 	}
 
 	registrations, err := c.registry.List(gvh.GroupHook())
 	if err != nil {
-		return errors.Wrapf(err, "failed to retrieve ExtensionHandlers for %s", gvh.GroupHook())
+		return errors.Wrapf(err, "failed to call extension handlers for hook %q", gvh.GroupHook())
 	}
 
 	responses := []runtimehooksv1.ResponseObject{}
@@ -208,23 +209,24 @@ func (c *client) CallAllExtensions(ctx context.Context, hook runtimecatalog.Hook
 		// Creates a new instance of the response parameter.
 		responseObject, err := c.catalog.NewResponse(gvh)
 		if err != nil {
-			return errors.Wrapf(err, "ExtensionHandler %s failed", registration.Name)
+			return errors.Wrapf(err, "failed to call extension handlers for hook %q: failed to call extension handler %q", gvh.GroupHook(), registration.Name)
 		}
 		tmpResponse := responseObject.(runtimehooksv1.ResponseObject)
 
 		// Compute whether the object the call is being made for matches the namespaceSelector
 		namespaceMatches, err := c.matchNamespace(ctx, registration.NamespaceSelector, forObject.GetNamespace())
 		if err != nil {
-			return errors.Errorf("ExtensionHandler %q namespaceSelector could not be resolved", registration.Name)
+			return errors.Wrapf(err, "failed to call extension handlers for hook %q: failed to call extension handler %q", gvh.GroupHook(), registration.Name)
 		}
 		// If the object namespace isn't matched by the registration NamespaceSelector skip the call.
 		if !namespaceMatches {
 			continue
 		}
+
 		err = c.CallExtension(ctx, hook, forObject, registration.Name, request, tmpResponse)
 		// If one of the extension handlers fails lets short-circuit here and return early.
 		if err != nil {
-			return errors.Wrapf(err, "ExtensionHandler %s failed", registration.Name)
+			return errors.Wrapf(err, "failed to call extension handlers for hook %q", gvh.GroupHook())
 		}
 		responses = append(responses, tmpResponse)
 	}
@@ -248,7 +250,7 @@ func aggregateSuccessfulResponses(aggregatedResponse runtimehooksv1.ResponseObje
 		// If the aggregated response is not a RetryResponseObject then we're done.
 		return
 	}
-	// Note: as all responses have the same type we can assume now that
+	// Note: As all responses have the same type we can assume now that
 	// they all implement the RetryResponseObject interface.
 
 	for _, resp := range responses {
@@ -273,7 +275,7 @@ func lowestNonZeroRetryAfterSeconds(i, j int32) int32 {
 	return j
 }
 
-// CallExtension make the call to the extension with the given name.
+// CallExtension makes the call to the extension with the given name.
 // The response object passed will be updated with the response of the call.
 // An error is returned if the extension is not compatible with the hook.
 // If the ExtensionHandler returns a response with `Status` set to `Failure` the function returns an error
@@ -288,33 +290,33 @@ func lowestNonZeroRetryAfterSeconds(i, j int32) int32 {
 func (c *client) CallExtension(ctx context.Context, hook runtimecatalog.Hook, forObject metav1.Object, name string, request runtime.Object, response runtimehooksv1.ResponseObject) error {
 	hookGVH, err := c.catalog.GroupVersionHook(hook)
 	if err != nil {
-		return errors.Wrap(err, "failed to compute GroupVersionHook")
+		return errors.Wrapf(err, "failed to call extension handler %q: failed to compute GroupVersionHook", name)
 	}
-	// Make sure the request is compatible with the version of the hook expected by the ExtensionHandler.
+	// Make sure the request is compatible with the hook.
 	if err := c.catalog.ValidateRequest(hookGVH, request); err != nil {
-		return errors.Wrapf(err, "request object is invalid for hook %s", hookGVH)
+		return errors.Wrapf(err, "failed to call extension handler %q: request object is invalid for hook %q", name, hookGVH)
 	}
-	// Make sure the response is compatible with the version of the hook expected by the ExtensionHandler.
+	// Make sure the response is compatible with the hook.
 	if err := c.catalog.ValidateResponse(hookGVH, response); err != nil {
-		return errors.Wrapf(err, "response object is invalid for hook %s", hookGVH)
+		return errors.Wrapf(err, "failed to call extension handler %q: response object is invalid for hook %q", name, hookGVH)
 	}
 
 	registration, err := c.registry.Get(name)
 	if err != nil {
-		return errors.Wrapf(err, "failed to retrieve ExtensionHandler with name %q", name)
+		return errors.Wrapf(err, "failed to call extension handler %q", name)
 	}
 	if hookGVH.GroupHook() != registration.GroupVersionHook.GroupHook() {
-		return errors.Errorf("ExtensionHandler %q does not match group %s, hook %s", name, hookGVH.Group, hookGVH.Hook)
+		return errors.Errorf("failed to call extension handler %q: handler does not match GroupHook %q", name, hookGVH.GroupHook())
 	}
 
 	// Compute whether the object the call is being made for matches the namespaceSelector
 	namespaceMatches, err := c.matchNamespace(ctx, registration.NamespaceSelector, forObject.GetNamespace())
 	if err != nil {
-		return errors.Errorf("ExtensionHandler %q namespaceSelector could not be resolved", name)
+		return errors.Errorf("failed to call extension handler %q", name)
 	}
 	// If the object namespace isn't matched by the registration NamespaceSelector return an error.
 	if !namespaceMatches {
-		return errors.Errorf("ExtensionHandler %q namespaceSelector did not match object %s", name, util.ObjectKey(forObject))
+		return errors.Errorf("failed to call extension handler %q: namespaceSelector did not match object %s", name, util.ObjectKey(forObject))
 	}
 
 	var timeoutDuration time.Duration
@@ -340,16 +342,16 @@ func (c *client) CallExtension(ctx context.Context, hook runtimecatalog.Hook, fo
 			response.SetMessage("")
 			return nil
 		}
-		return errors.Wrap(err, "failed to call ExtensionHandler")
+		return errors.Wrapf(err, "failed to call extension handler %q", name)
 	}
 
 	// If the received response is a failure then return an error.
 	if response.GetStatus() == runtimehooksv1.ResponseStatusFailure {
-		return errors.Errorf("ExtensionHandler %s failed with message %s", name, response.GetMessage())
+		return errors.Errorf("failed to call extension handler %q: got failure response with message %q", name, response.GetMessage())
 	}
 
 	// Received a successful response from the extension handler. The `response` object
-	// is populated with the result. Return no error.
+	// has been populated with the result. Return no error.
 	return nil
 }
 
@@ -364,15 +366,15 @@ type httpCallOptions struct {
 
 func httpCall(ctx context.Context, request, response runtime.Object, opts *httpCallOptions) error {
 	if opts == nil || request == nil || response == nil {
-		return errors.New("opts, request and response cannot be nil")
+		return errors.New("http call failed: opts, request and response cannot be nil")
 	}
 	if opts.catalog == nil {
-		return errors.New("opts.Catalog cannot be nil")
+		return errors.New("http call failed: opts.Catalog cannot be nil")
 	}
 
 	extensionURL, err := urlForExtension(opts.config, opts.registrationGVH, opts.name)
 	if err != nil {
-		return errors.Wrapf(err, "failed to compute URL of the extension handler %q", opts.name)
+		return errors.Wrap(err, "http call failed")
 	}
 
 	requireConversion := opts.registrationGVH.Version != opts.hookGVH.Version
@@ -388,35 +390,35 @@ func httpCall(ctx context.Context, request, response runtime.Object, opts *httpC
 		// Create a new hook request object that is compatible with the version of ExtensionHandler.
 		requestLocal, err = opts.catalog.NewRequest(opts.registrationGVH)
 		if err != nil {
-			return errors.Wrapf(err, "failed to create new request for hook %s", opts.registrationGVH)
+			return errors.Wrap(err, "http call failed")
 		}
 
+		// Convert the request to the version supported by the ExtensionHandler.
 		if err := opts.catalog.Convert(request, requestLocal, ctx); err != nil {
-			return errors.Wrapf(err, "failed to convert request from %T to %T", request, requestLocal)
+			return errors.Wrapf(err, "http call failed: failed to convert request from %T to %T", request, requestLocal)
 		}
 
 		// Create a new hook response object that is compatible with the version of the ExtensionHandler.
 		responseLocal, err = opts.catalog.NewResponse(opts.registrationGVH)
 		if err != nil {
-			return errors.Wrapf(err, "failed to create new response for hook %s", opts.registrationGVH)
+			return errors.Wrap(err, "http call failed")
 		}
 	}
 
-	// Ensure the correct GroupVersionKind is set to the request.
+	// Ensure the GroupVersionKind is set to the request.
 	requestGVH, err := opts.catalog.Request(opts.registrationGVH)
 	if err != nil {
-		return errors.Wrap(err, "failed to create request object")
+		return errors.Wrap(err, "http call failed")
 	}
-
 	requestLocal.GetObjectKind().SetGroupVersionKind(requestGVH)
 
 	postBody, err := json.Marshal(requestLocal)
 	if err != nil {
-		return errors.Wrap(err, "failed to marshall request object")
+		return errors.Wrap(err, "http call failed: failed to marshall request object")
 	}
 
 	if opts.timeout != 0 {
-		// Make the call timebound if timeout is non-zero value.
+		// Make the call time-bound if timeout is non-zero value.
 		values := extensionURL.Query()
 		values.Add("timeout", opts.timeout.String())
 		extensionURL.RawQuery = values.Encode()
@@ -428,10 +430,10 @@ func httpCall(ctx context.Context, request, response runtime.Object, opts *httpC
 
 	httpRequest, err := http.NewRequestWithContext(ctx, http.MethodPost, extensionURL.String(), bytes.NewBuffer(postBody))
 	if err != nil {
-		return errors.Wrap(err, "failed to create http request")
+		return errors.Wrap(err, "http call failed: failed to create http request")
 	}
 
-	// use client-go's transport.TLSConfigureFor to ensure good defaults for tls
+	// Use client-go's transport.TLSConfigureFor to ensure good defaults for tls
 	client := http.DefaultClient
 	tlsConfig, err := transport.TLSConfigFor(&transport.Config{
 		TLS: transport.TLSConfig{
@@ -440,9 +442,9 @@ func httpCall(ctx context.Context, request, response runtime.Object, opts *httpC
 		},
 	})
 	if err != nil {
-		return errors.Wrap(err, "failed to create tls config")
+		return errors.Wrap(err, "http call failed: failed to create tls config")
 	}
-	// this also adds http2
+	// This also adds http2
 	client.Transport = utilnet.SetTransportDefaults(&http.Transport{
 		TLSClientConfig: tlsConfig,
 	})
@@ -450,7 +452,7 @@ func httpCall(ctx context.Context, request, response runtime.Object, opts *httpC
 	resp, err := client.Do(httpRequest)
 	if err != nil {
 		return errCallingExtensionHandler(
-			errors.Wrapf(err, "failed to call ExtensionHandler: %q", opts.name),
+			errors.Wrapf(err, "http call failed"),
 		)
 	}
 	defer resp.Body.Close()
@@ -459,25 +461,25 @@ func httpCall(ctx context.Context, request, response runtime.Object, opts *httpC
 		respBody, err := io.ReadAll(resp.Body)
 		if err != nil {
 			return errCallingExtensionHandler(
-				errors.Errorf("failed to call ExtensionHandler: failed to read response body (status code: %d)", resp.StatusCode),
+				errors.Errorf("http call failed: got response with status code %d != 200: failed to read response body", resp.StatusCode),
 			)
 		}
 
 		return errCallingExtensionHandler(
-			errors.Errorf("failed to call ExtensionHandler: response: %q (status code: %d)", string(respBody), resp.StatusCode),
+			errors.Errorf("http call failed: got response with status code %d != 200: response: %q", resp.StatusCode, string(respBody)),
 		)
 	}
 
 	if err := json.NewDecoder(resp.Body).Decode(responseLocal); err != nil {
 		return errCallingExtensionHandler(
-			errors.Wrap(err, "failed to decode message"),
+			errors.Wrap(err, "http call failed: failed to decode response"),
 		)
 	}
 
 	if requireConversion {
 		// Convert the received response to the original version of the response object.
 		if err := opts.catalog.Convert(responseLocal, response, ctx); err != nil {
-			return errors.Wrapf(err, "failed to convert response from %T to %T", requestLocal, response)
+			return errors.Wrapf(err, "http call failed: failed to convert response from %T to %T", requestLocal, response)
 		}
 	}
 
@@ -502,18 +504,21 @@ func urlForExtension(config runtimev1.ClientConfig, gvh runtimecatalog.GroupVers
 		}
 	} else {
 		if config.URL == nil {
-			return nil, errors.New("at least one of Service and URL should be defined in config")
+			return nil, errors.New("failed to compute URL: at least one of service and url should be defined in config")
 		}
+
 		var err error
 		u, err = url.Parse(*config.URL)
 		if err != nil {
-			return nil, errors.Wrap(err, "URL in ClientConfig is invalid")
+			return nil, errors.Wrap(err, "failed to compute URL: failed to parse url from clientConfig")
 		}
+
 		if u.Scheme != "https" {
-			return nil, errors.Errorf("expected https scheme, got %s", u.Scheme)
+			return nil, errors.Errorf("failed to compute URL: expected https scheme, got %s", u.Scheme)
 		}
 	}
-	// Add the subpatch to the ExtensionHandler for the given hook.
+
+	// Append the ExtensionHandler path.
 	u.Path = path.Join(u.Path, runtimecatalog.GVHToPath(gvh, name))
 	return u, nil
 }
@@ -521,13 +526,14 @@ func urlForExtension(config runtimev1.ClientConfig, gvh runtimecatalog.GroupVers
 // defaultAndValidateDiscoveryResponse defaults unset values and runs a set of validations on the Discovery Response.
 // If any of these checks fails the response is invalid and an error is returned.
 func defaultAndValidateDiscoveryResponse(cat *runtimecatalog.Catalog, discovery *runtimehooksv1.DiscoveryResponse) error {
-	names := make(map[string]bool)
-	var errs []error
-
 	if discovery == nil {
-		return errors.New("error validating discovery: response is empty")
+		return errors.New("failed to validate discovery response: response is nil")
 	}
+
 	discovery = defaultDiscoveryResponse(discovery)
+
+	var errs []error
+	names := make(map[string]bool)
 	for _, handler := range discovery.Handlers {
 		// Names should be unique.
 		if _, ok := names[handler.Name]; ok {
@@ -561,18 +567,20 @@ func defaultAndValidateDiscoveryResponse(cat *runtimecatalog.Catalog, discovery
 			errs = append(errs, errors.Errorf("handler %s requestHook %s/%s is not in the Runtime SDK catalog", handler.Name, handler.RequestHook.APIVersion, handler.RequestHook.Hook))
 		}
 	}
-	return kerrors.NewAggregate(errs)
+
+	return errors.Wrapf(kerrors.NewAggregate(errs), "failed to validate discovery response")
 }
 
 // defaultDiscoveryResponse defaults FailurePolicy and TimeoutSeconds for all discovered handlers.
 func defaultDiscoveryResponse(discovery *runtimehooksv1.DiscoveryResponse) *runtimehooksv1.DiscoveryResponse {
 	for i, handler := range discovery.Handlers {
-		// If FailurePolicy is not defined set to "Fail"
+		// If FailurePolicy is not defined set to "Fail".
 		if handler.FailurePolicy == nil {
 			defaultFailPolicy := runtimehooksv1.FailurePolicyFail
 			handler.FailurePolicy = &defaultFailPolicy
 		}
-		// If TimeoutSeconds is not defined set to ten.
+
+		// If TimeoutSeconds is not defined set to 10.
 		if handler.TimeoutSeconds == nil {
 			handler.TimeoutSeconds = pointer.Int32(10)
 		}
@@ -585,18 +593,20 @@ func defaultDiscoveryResponse(discovery *runtimehooksv1.DiscoveryResponse) *runt
 // matchNamespace returns true if the passed namespace matches the selector. It returns an error if the namespace does
 // not exist in the API server.
 func (c *client) matchNamespace(ctx context.Context, selector labels.Selector, namespace string) (bool, error) {
-	// return early if the selector is empty.
+	// Return early if the selector is empty.
 	if selector.Empty() {
 		return true, nil
 	}
+
 	ns := &metav1.PartialObjectMetadata{}
 	ns.SetGroupVersionKind(schema.GroupVersionKind{
 		Group:   "",
 		Version: "v1",
 		Kind:    "Namespace",
 	})
-	if err := c.Client.Get(ctx, ctrlclient.ObjectKey{Name: namespace}, ns); err != nil {
-		return false, errors.Wrapf(err, "failed to find namespace %s for extension namespaceSelector", namespace)
+	if err := c.client.Get(ctx, ctrlclient.ObjectKey{Name: namespace}, ns); err != nil {
+		return false, errors.Wrapf(err, "failed to match namespace: failed to get namespace %s", namespace)
 	}
+
 	return selector.Matches(labels.Set(ns.GetLabels())), nil
 }
diff --git a/internal/runtime/client/fake/fake_client.go b/internal/runtime/client/fake/fake_client.go
index 2c0aec5ca..84742a125 100644
--- a/internal/runtime/client/fake/fake_client.go
+++ b/internal/runtime/client/fake/fake_client.go
@@ -50,13 +50,13 @@ func (f *RuntimeClientBuilder) WithCatalog(catalog *runtimecatalog.Catalog) *Run
 	return f
 }
 
-// WithCallAllExtensionResponses can be used to dictate the responses for the runtime hooks.
+// WithCallAllExtensionResponses can be used to dictate the responses for CallAllExtensions.
 func (f *RuntimeClientBuilder) WithCallAllExtensionResponses(responses map[runtimecatalog.GroupVersionHook]runtimehooksv1.ResponseObject) *RuntimeClientBuilder {
 	f.callAllResponses = responses
 	return f
 }
 
-// WithCallExtensionResponses can be used to dictate the responses for the runtime extension handlers.
+// WithCallExtensionResponses can be used to dictate the responses for CallExtension.
 func (f *RuntimeClientBuilder) WithCallExtensionResponses(responses map[string]runtimehooksv1.ResponseObject) *RuntimeClientBuilder {
 	f.callResponses = responses
 	return f
@@ -101,15 +101,18 @@ func (fc *RuntimeClient) CallAllExtensions(ctx context.Context, hook runtimecata
 	if err != nil {
 		return errors.Wrap(err, "failed to compute GVH")
 	}
+
 	expectedResponse, ok := fc.callAllResponses[gvh]
 	if !ok {
 		// This should actually panic because an error here would mean a mistake in the test setup.
 		panic(fmt.Sprintf("test response not available hook for %q", gvh))
 	}
+
 	if err := fc.catalog.Convert(expectedResponse, response, ctx); err != nil {
 		// This should actually panic because an error here would mean a mistake in the test setup.
 		panic("cannot update response")
 	}
+
 	if response.GetStatus() == runtimehooksv1.ResponseStatusFailure {
 		return errors.Errorf("runtime hook %q failed", gvh)
 	}
@@ -123,10 +126,12 @@ func (fc *RuntimeClient) CallExtension(ctx context.Context, _ runtimecatalog.Hoo
 		// This should actually panic because an error here would mean a mistake in the test setup.
 		panic(fmt.Sprintf("test response not available for extension %q", name))
 	}
+
 	if err := fc.catalog.Convert(expectedResponse, response, ctx); err != nil {
 		// This should actually panic because an error here would mean a mistake in the test setup.
 		panic("cannot update response")
 	}
+
 	// If the received response is a failure then return an error.
 	if response.GetStatus() == runtimehooksv1.ResponseStatusFailure {
 		return errors.Errorf("ExtensionHandler %s failed with message %s", name, response.GetMessage())
@@ -159,7 +164,7 @@ func (fc *RuntimeClient) WarmUp(extensionConfigList *runtimev1.ExtensionConfigLi
 	panic("unimplemented")
 }
 
-// CallAllCount return the number of times a hooks was called.
+// CallAllCount return the number of times a hook was called.
 func (fc *RuntimeClient) CallAllCount(hook runtimecatalog.Hook) int {
 	return fc.callAllTracker[runtimecatalog.HookName(hook)]
 }
diff --git a/internal/runtime/registry/registry.go b/internal/runtime/registry/registry.go
index 12e008bf8..3d8b1bcdf 100644
--- a/internal/runtime/registry/registry.go
+++ b/internal/runtime/registry/registry.go
@@ -49,10 +49,10 @@ type ExtensionRegistry interface {
 	// Remove removes all RuntimeExtensions corresponding to the provided ExtensionConfig.
 	Remove(extensionConfig *runtimev1.ExtensionConfig) error
 
-	// List all registered RuntimeExtensions for a given catalog.GroupHook.
+	// List lists all registered RuntimeExtensions for a given catalog.GroupHook.
 	List(gh runtimecatalog.GroupHook) ([]*ExtensionRegistration, error)
 
-	// Get the RuntimeExtensions with the given name.
+	// Get gets the RuntimeExtensions with the given name.
 	Get(name string) (*ExtensionRegistration, error)
 }
 
@@ -67,18 +67,20 @@ type ExtensionRegistration struct {
 	// GroupVersionHook is the GroupVersionHook that the RuntimeExtension implements.
 	GroupVersionHook runtimecatalog.GroupVersionHook
 
-	// NamespaceSelector limits the namespace in which a Runtime Extension is called.
+	// NamespaceSelector limits the objects by namespace for which a Runtime Extension is called.
 	NamespaceSelector labels.Selector
 
 	// ClientConfig is the ClientConfig to communicate with the RuntimeExtension.
 	ClientConfig runtimev1.ClientConfig
+
 	// TimeoutSeconds is the timeout duration used for calls to the RuntimeExtension.
 	TimeoutSeconds *int32
+
 	// FailurePolicy defines how failures in calls to the RuntimeExtension should be handled by a client.
 	FailurePolicy *runtimev1.FailurePolicy
 }
 
-// extensionRegistry is a implementation of ExtensionRegistry.
+// extensionRegistry is an implementation of ExtensionRegistry.
 type extensionRegistry struct {
 	// ready represents if the registry has been warmed up.
 	ready bool
@@ -100,14 +102,14 @@ func New() ExtensionRegistry {
 // After WarmUp completes the RuntimeExtension registry is considered ready.
 func (r *extensionRegistry) WarmUp(extensionConfigList *runtimev1.ExtensionConfigList) error {
 	if extensionConfigList == nil {
-		return errors.New("invalid argument: when calling WarmUp extensionConfigList must not be nil")
+		return errors.New("failed to warm up registry: invalid argument: when calling WarmUp ExtensionConfigList must not be nil")
 	}
 
 	r.lock.Lock()
 	defer r.lock.Unlock()
 
 	if r.ready {
-		return errors.New("invalid operation: WarmUp cannot be called on a registry which has already been warmed up")
+		return errors.New("failed to warm up registry: invalid operation: WarmUp cannot be called on a registry which has already been warmed up")
 	}
 
 	var allErrs []error
@@ -120,7 +122,7 @@ func (r *extensionRegistry) WarmUp(extensionConfigList *runtimev1.ExtensionConfi
 		// Reset the map, so that the next WarmUp can start with an empty map
 		// and doesn't inherit entries from this failed WarmUp.
 		r.items = map[string]*ExtensionRegistration{}
-		return kerrors.NewAggregate(allErrs)
+		return errors.Wrapf(kerrors.NewAggregate(allErrs), "failed to warm up registry")
 	}
 
 	r.ready = true
@@ -142,14 +144,14 @@ func (r *extensionRegistry) IsReady() bool {
 // one from the newly provided ExtensionConfig.
 func (r *extensionRegistry) Add(extensionConfig *runtimev1.ExtensionConfig) error {
 	if extensionConfig == nil {
-		return errors.New("invalid argument: when calling Add extensionConfig must not be nil")
+		return errors.New("failed to add ExtensionConfig to registry: invalid argument: when calling Add extensionConfig must not be nil")
 	}
 
 	r.lock.Lock()
 	defer r.lock.Unlock()
 
 	if !r.ready {
-		return errors.New("invalid operation: Add cannot be called on a registry which has not been warmed up")
+		return errors.Errorf("failed to add ExtensionConfig %q to registry: invalid operation: Add cannot be called on a registry which has not been warmed up", extensionConfig.Name)
 	}
 
 	return r.add(extensionConfig)
@@ -158,14 +160,14 @@ func (r *extensionRegistry) Add(extensionConfig *runtimev1.ExtensionConfig) erro
 // Remove removes all RuntimeExtensions corresponding to the provided ExtensionConfig.
 func (r *extensionRegistry) Remove(extensionConfig *runtimev1.ExtensionConfig) error {
 	if extensionConfig == nil {
-		return errors.New("invalid argument: when calling Remove extensionConfig must not be nil")
+		return errors.New("failed to remove ExtensionConfig from registry: invalid argument: when calling Remove ExtensionConfig must not be nil")
 	}
 
 	r.lock.Lock()
 	defer r.lock.Unlock()
 
 	if !r.ready {
-		return errors.New("invalid operation: Remove cannot be called on a registry which has not been warmed up")
+		return errors.Errorf("failed to remove ExtensionConfig %q from registry: invalid operation: Remove cannot be called on a registry which has not been warmed up", extensionConfig.Name)
 	}
 
 	r.remove(extensionConfig)
@@ -180,20 +182,20 @@ func (r *extensionRegistry) remove(extensionConfig *runtimev1.ExtensionConfig) {
 	}
 }
 
-// List all registered RuntimeExtensions for a given catalog.GroupHook.
+// List lists all registered RuntimeExtensions for a given catalog.GroupHook.
 func (r *extensionRegistry) List(gh runtimecatalog.GroupHook) ([]*ExtensionRegistration, error) {
 	if gh.Group == "" {
-		return nil, errors.New("invalid argument: when calling List gh.Group must not be empty")
+		return nil, errors.New("failed to list extension handlers: invalid argument: when calling List gh.Group must not be empty")
 	}
 	if gh.Hook == "" {
-		return nil, errors.New("invalid argument: when calling List gh.Hook must not be empty")
+		return nil, errors.New("failed to list extension handlers: invalid argument: when calling List gh.Hook must not be empty")
 	}
 
 	r.lock.RLock()
 	defer r.lock.RUnlock()
 
 	if !r.ready {
-		return nil, errors.New("invalid operation: List cannot be called on a registry which has not been warmed up")
+		return nil, errors.Errorf("failed to list extension handlers for GroupHook %q: invalid operation: List cannot be called on a registry which has not been warmed up", gh.String())
 	}
 
 	l := []*ExtensionRegistration{}
@@ -205,18 +207,18 @@ func (r *extensionRegistry) List(gh runtimecatalog.GroupHook) ([]*ExtensionRegis
 	return l, nil
 }
 
-// Get the RuntimeExtensions with the given name.
+// Get gets the RuntimeExtensions with the given name.
 func (r *extensionRegistry) Get(name string) (*ExtensionRegistration, error) {
 	r.lock.RLock()
 	defer r.lock.RUnlock()
 
 	if !r.ready {
-		return nil, errors.New("invalid operation: Get cannot be called on a registry not yet ready")
+		return nil, errors.Errorf("failed to get extension handler %q from registry: invalid operation: Get cannot be called on a registry not yet ready", name)
 	}
 
 	registration, ok := r.items[name]
 	if !ok {
-		return nil, errors.Errorf("RuntimeExtension with name %q has not been registered", name)
+		return nil, errors.Errorf("failed to get extension handler %q from registry: handler with name %q has not been registered", name, name)
 	}
 
 	return registration, nil
@@ -228,7 +230,7 @@ func (r *extensionRegistry) add(extensionConfig *runtimev1.ExtensionConfig) erro
 	// Create a selector from the NamespaceSelector defined in the extensionConfig spec.
 	selector, err := metav1.LabelSelectorAsSelector(extensionConfig.Spec.NamespaceSelector)
 	if err != nil {
-		return errors.Wrapf(err, "failed to create namespaceSelector")
+		return errors.Wrapf(err, "failed to add ExtensionConfig %q to registry: failed to create namespaceSelector", extensionConfig.Name)
 	}
 
 	var allErrs []error
@@ -236,7 +238,7 @@ func (r *extensionRegistry) add(extensionConfig *runtimev1.ExtensionConfig) erro
 	for _, e := range extensionConfig.Status.Handlers {
 		gv, err := schema.ParseGroupVersion(e.RequestHook.APIVersion)
 		if err != nil {
-			allErrs = append(allErrs, errors.Wrapf(err, "failed to parse GroupVersion %q", e.RequestHook.APIVersion))
+			allErrs = append(allErrs, errors.Wrapf(err, "failed to add extension handler %q to registry: failed to parse GroupVersion %q of handler %q", e.Name, e.RequestHook.APIVersion, e.Name))
 			continue
 		}
 
@@ -257,7 +259,7 @@ func (r *extensionRegistry) add(extensionConfig *runtimev1.ExtensionConfig) erro
 	}
 
 	if len(allErrs) > 0 {
-		return kerrors.NewAggregate(allErrs)
+		return errors.Wrapf(kerrors.NewAggregate(allErrs), "failed to add ExtensionConfig %q to registry", extensionConfig.Name)
 	}
 
 	for _, registration := range registrations {
diff --git a/internal/webhooks/runtime/extensionconfig_webhook.go b/internal/webhooks/runtime/extensionconfig_webhook.go
index 3e2a65e85..2112404ae 100644
--- a/internal/webhooks/runtime/extensionconfig_webhook.go
+++ b/internal/webhooks/runtime/extensionconfig_webhook.go
@@ -92,7 +92,7 @@ func (webhook *ExtensionConfig) ValidateUpdate(ctx context.Context, old, updated
 	return webhook.validate(ctx, oldExtensionConfig, newExtensionConfig)
 }
 
-// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type.
+// validate validates an ExtensionConfig create or update.
 func (webhook *ExtensionConfig) validate(_ context.Context, _, newExtensionConfig *runtimev1.ExtensionConfig) error {
 	// NOTE: ExtensionConfig is behind the RuntimeSDK feature gate flag; the web hook
 	// must prevent creating and updating objects in case the feature flag is disabled.
@@ -125,13 +125,13 @@ func validateExtensionConfigSpec(e *runtimev1.ExtensionConfig) field.ErrorList {
 	if e.Spec.ClientConfig.URL == nil && e.Spec.ClientConfig.Service == nil {
 		allErrs = append(allErrs, field.Required(
 			specPath.Child("clientConfig"),
-			"either URL or Service must be defined",
+			"either url or service must be defined",
 		))
 	}
 	if e.Spec.ClientConfig.URL != nil && e.Spec.ClientConfig.Service != nil {
 		allErrs = append(allErrs, field.Forbidden(
 			specPath.Child("clientConfig"),
-			"only one of URL or Service can be defined",
+			"only one of url or service can be defined",
 		))
 	}
 
@@ -180,7 +180,7 @@ func validateExtensionConfigSpec(e *runtimev1.ExtensionConfig) field.ErrorList {
 		for _, msg := range validation.IsDNS1123Label(e.Spec.ClientConfig.Service.Namespace) {
 			allErrs = append(allErrs, field.Invalid(
 				specPath.Child("clientConfig", "service", "namespace"),
-				e.Spec.ClientConfig.Service.Name,
+				e.Spec.ClientConfig.Service.Namespace,
 				msg,
 			))
 		}


Test patch:
diff --git a/internal/runtime/client/client_test.go b/internal/runtime/client/client_test.go
index a7c1e650f..c72a45934 100644
--- a/internal/runtime/client/client_test.go
+++ b/internal/runtime/client/client_test.go
@@ -1069,7 +1069,7 @@ func Test_client_matchNamespace(t *testing.T) {
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
 			c := client{
-				Client: fake.NewClientBuilder().
+				client: fake.NewClientBuilder().
 					WithObjects(tt.existingNamespaces...).
 					Build(),
 			}
diff --git a/internal/runtime/test/v1alpha1/fake_types.go b/internal/runtime/test/v1alpha1/fake_types.go
index 5d9d5e090..76b3b903c 100644
--- a/internal/runtime/test/v1alpha1/fake_types.go
+++ b/internal/runtime/test/v1alpha1/fake_types.go
@@ -14,9 +14,6 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-// Package v1alpha1 contains types for catalog tests
-// Note: they have to be in a separate package because otherwise it wouldn't
-// be possible to register different versions of the same hook.
 package v1alpha1
 
 import (
diff --git a/internal/runtime/test/v1alpha1/groupversion_info.go b/internal/runtime/test/v1alpha1/groupversion_info.go
index 7fcc026bc..3509baca8 100644
--- a/internal/runtime/test/v1alpha1/groupversion_info.go
+++ b/internal/runtime/test/v1alpha1/groupversion_info.go
@@ -23,21 +23,21 @@ import (
 )
 
 var (
-	// GroupVersion is group version identifying rpc services defined in this package
+	// GroupVersion is the group version identifying test RuntimeHooks defined in this package
 	// and their request and response types.
 	GroupVersion = schema.GroupVersion{Group: "test.runtime.cluster.x-k8s.io", Version: "v1alpha1"}
 
-	// catalogBuilder is used to add rpc services and their request and response types
+	// catalogBuilder is used to add RuntimeHooks and their request and response types
 	// to a Catalog.
 	catalogBuilder = &runtimecatalog.Builder{GroupVersion: GroupVersion}
 
-	// AddToCatalog adds rpc services defined in this package and their request and
+	// AddToCatalog adds test RuntimeHooks defined in this package and their request and
 	// response types to a catalog.
 	AddToCatalog = catalogBuilder.AddToCatalog
 
-	// localSchemeBuilder provide access to the SchemeBuilder used for managing rpc
-	// method's request and response types defined in this package.
-	// NOTE: this object is required to allow registration of automatically generated
-	// conversions func.
+	// localSchemeBuilder provide access to the SchemeBuilder used for managing RuntimeHooks
+	// and their request and response types defined in this package.
+	// NOTE: The localSchemeBuilder is required to allow registration of automatically generated
+	// conversion funcs.
 	localSchemeBuilder = catalogBuilder
 )
diff --git a/internal/runtime/test/v1alpha2/fake_types.go b/internal/runtime/test/v1alpha2/fake_types.go
index 6d772393f..70592d5f9 100644
--- a/internal/runtime/test/v1alpha2/fake_types.go
+++ b/internal/runtime/test/v1alpha2/fake_types.go
@@ -14,9 +14,6 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-// Package v1alpha2 contains types for catalog tests
-// Note: they have to be in a separate package because otherwise it wouldn't
-// be possible to register different versions of the same hook.
 package v1alpha2
 
 import (
diff --git a/internal/runtime/test/v1alpha2/groupversion_info.go b/internal/runtime/test/v1alpha2/groupversion_info.go
index 69d668a86..b47f4376e 100644
--- a/internal/runtime/test/v1alpha2/groupversion_info.go
+++ b/internal/runtime/test/v1alpha2/groupversion_info.go
@@ -23,15 +23,15 @@ import (
 )
 
 var (
-	// GroupVersion is group version identifying rpc services defined in this package
+	// GroupVersion is the group version identifying test RuntimeHooks defined in this package
 	// and their request and response types.
 	GroupVersion = schema.GroupVersion{Group: "test.runtime.cluster.x-k8s.io", Version: "v1alpha2"}
 
-	// catalogBuilder is used to add rpc services and their request and response types
+	// catalogBuilder is used to add RuntimeHooks and their request and response types
 	// to a Catalog.
 	catalogBuilder = &runtimecatalog.Builder{GroupVersion: GroupVersion}
 
-	// AddToCatalog adds rpc services defined in this package and their request and
+	// AddToCatalog adds test RuntimeHooks defined in this package and their request and
 	// response types to a catalog.
 	AddToCatalog = catalogBuilder.AddToCatalog
 )
diff --git a/internal/test/builder/builders.go b/internal/test/builder/builders.go
index a3c8cc6f4..cbcbcd1f2 100644
--- a/internal/test/builder/builders.go
+++ b/internal/test/builder/builders.go
@@ -716,26 +716,19 @@ func (c *ControlPlaneTemplateBuilder) Build() *unstructured.Unstructured {
 }
 
 // TestControlPlaneTemplateBuilder holds the variables and objects needed to build a generic ControlPlane template.
+// +kubebuilder:object:generate=false
 type TestControlPlaneTemplateBuilder struct {
-	obj *unstructured.Unstructured
+	namespace                     string
+	name                          string
+	infrastructureMachineTemplate *unstructured.Unstructured
+	specFields                    map[string]interface{}
 }
 
 // TestControlPlaneTemplate creates a NewControlPlaneTemplate builder with the given name and namespace.
 func TestControlPlaneTemplate(namespace, name string) *TestControlPlaneTemplateBuilder {
-	obj := &unstructured.Unstructured{}
-	obj.SetName(name)
-	obj.SetNamespace(namespace)
-	obj.SetAPIVersion(ControlPlaneGroupVersion.String())
-	obj.SetKind(TestControlPlaneTemplateKind)
-	// Set the mandatory spec fields for the object.
-	if err := unstructured.SetNestedField(obj.Object, map[string]interface{}{}, "spec"); err != nil {
-		panic(err)
-	}
-	if err := unstructured.SetNestedField(obj.Object, map[string]interface{}{}, "spec", "template", "spec"); err != nil {
-		panic(err)
-	}
 	return &TestControlPlaneTemplateBuilder{
-		obj,
+		namespace: namespace,
+		name:      name,
 	}
 }
 
@@ -748,21 +741,35 @@ func TestControlPlaneTemplate(namespace, name string) *TestControlPlaneTemplateB
 //     "spec.version": "v1.2.3",
 // }.
 func (c *TestControlPlaneTemplateBuilder) WithSpecFields(fields map[string]interface{}) *TestControlPlaneTemplateBuilder {
-	setSpecFields(c.obj, fields)
+	c.specFields = fields
 	return c
 }
 
 // WithInfrastructureMachineTemplate adds the given Unstructured object to the ControlPlaneTemplateBuilder as its InfrastructureMachineTemplate.
 func (c *TestControlPlaneTemplateBuilder) WithInfrastructureMachineTemplate(t *unstructured.Unstructured) *TestControlPlaneTemplateBuilder {
-	if err := setNestedRef(c.obj, t, "spec", "template", "spec", "machineTemplate", "infrastructureRef"); err != nil {
-		panic(err)
-	}
+	c.infrastructureMachineTemplate = t
 	return c
 }
 
 // Build creates an Unstructured object from the variables passed to the ControlPlaneTemplateBuilder.
 func (c *TestControlPlaneTemplateBuilder) Build() *unstructured.Unstructured {
-	return c.obj
+	obj := &unstructured.Unstructured{}
+	obj.SetAPIVersion(ControlPlaneGroupVersion.String())
+	obj.SetKind(TestControlPlaneTemplateKind)
+	obj.SetNamespace(c.namespace)
+	obj.SetName(c.name)
+
+	// Initialize the spec.template.spec to make the object valid in reconciliation.
+	setSpecFields(obj, map[string]interface{}{"spec.template.spec": map[string]interface{}{}})
+
+	setSpecFields(obj, c.specFields)
+
+	if c.infrastructureMachineTemplate != nil {
+		if err := setNestedRef(obj, c.infrastructureMachineTemplate, "spec", "template", "spec", "machineTemplate", "infrastructureRef"); err != nil {
+			panic(err)
+		}
+	}
+	return obj
 }
 
 // InfrastructureClusterBuilder holds the variables and objects needed to build a generic InfrastructureCluster.
diff --git a/internal/test/builder/zz_generated.deepcopy.go b/internal/test/builder/zz_generated.deepcopy.go
index 75fb38b60..42e773866 100644
--- a/internal/test/builder/zz_generated.deepcopy.go
+++ b/internal/test/builder/zz_generated.deepcopy.go
@@ -417,22 +417,3 @@ func (in *MachineSetBuilder) DeepCopy() *MachineSetBuilder {
 	in.DeepCopyInto(out)
 	return out
 }
-
-// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *TestControlPlaneTemplateBuilder) DeepCopyInto(out *TestControlPlaneTemplateBuilder) {
-	*out = *in
-	if in.obj != nil {
-		in, out := &in.obj, &out.obj
-		*out = (*in).DeepCopy()
-	}
-}
-
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TestControlPlaneTemplateBuilder.
-func (in *TestControlPlaneTemplateBuilder) DeepCopy() *TestControlPlaneTemplateBuilder {
-	if in == nil {
-		return nil
-	}
-	out := new(TestControlPlaneTemplateBuilder)
-	in.DeepCopyInto(out)
-	return out
-}
diff --git a/scripts/ci-e2e-lib.sh b/scripts/ci-e2e-lib.sh
index a73b72bbb..5f3abf9a2 100644
--- a/scripts/ci-e2e-lib.sh
+++ b/scripts/ci-e2e-lib.sh
@@ -215,9 +215,9 @@ EOL
 # the actual test run less sensible to the network speed.
 kind:prepullAdditionalImages () {
   # Pulling cert manager images so we can pre-load in kind nodes
-  kind::prepullImage "quay.io/jetstack/cert-manager-cainjector:v1.8.2"
-  kind::prepullImage "quay.io/jetstack/cert-manager-webhook:v1.8.2"
-  kind::prepullImage "quay.io/jetstack/cert-manager-controller:v1.8.2"
+  kind::prepullImage "quay.io/jetstack/cert-manager-cainjector:v1.7.2"
+  kind::prepullImage "quay.io/jetstack/cert-manager-webhook:v1.7.2"
+  kind::prepullImage "quay.io/jetstack/cert-manager-controller:v1.7.2"
 }
 
 # kind:prepullImage pre-pull a docker image if no already present locally.
diff --git a/test/e2e/config/docker.yaml b/test/e2e/config/docker.yaml
index c9ec0b0e3..fa98c30dc 100644
--- a/test/e2e/config/docker.yaml
+++ b/test/e2e/config/docker.yaml
@@ -19,11 +19,11 @@ images:
   loadBehavior: tryLoad
 - name: gcr.io/k8s-staging-cluster-api/test-extension-{ARCH}:dev
   loadBehavior: tryLoad
-- name: quay.io/jetstack/cert-manager-cainjector:v1.8.2
+- name: quay.io/jetstack/cert-manager-cainjector:v1.7.2
   loadBehavior: tryLoad
-- name: quay.io/jetstack/cert-manager-webhook:v1.8.2
+- name: quay.io/jetstack/cert-manager-webhook:v1.7.2
   loadBehavior: tryLoad
-- name: quay.io/jetstack/cert-manager-controller:v1.8.2
+- name: quay.io/jetstack/cert-manager-controller:v1.7.2
   loadBehavior: tryLoad
 
 providers:
diff --git a/test/framework/kubernetesversions/data/debian_injection_script.envsubst.sh.tpl b/test/framework/kubernetesversions/data/debian_injection_script.envsubst.sh.tpl
index 481221229..66c7e0e59 100644
--- a/test/framework/kubernetesversions/data/debian_injection_script.envsubst.sh.tpl
+++ b/test/framework/kubernetesversions/data/debian_injection_script.envsubst.sh.tpl
@@ -65,12 +65,6 @@ if [[ "$${KUBERNETES_VERSION}" != "" ]]; then
   CI_DIR=/tmp/k8s-ci
   mkdir -p "$${CI_DIR}"
   declare -a PACKAGES_TO_TEST=("kubectl" "kubelet" "kubeadm")
-  {{- if .IsControlPlaneMachine }}
-  declare -a CONTAINERS_TO_TEST=("kube-apiserver" "kube-controller-manager" "kube-proxy" "kube-scheduler")
-  {{- else }}
-  declare -a CONTAINERS_TO_TEST=("kube-proxy")
-  {{- end }}
-  CONTAINER_EXT="tar"
   echo "* testing CI version $${KUBERNETES_VERSION}"
   # Check for semver
   if [[ "$${KUBERNETES_VERSION}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
@@ -102,18 +96,6 @@ if [[ "$${KUBERNETES_VERSION}" != "" ]]; then
     done
     systemctl restart kubelet
   fi
-  IMAGE_REGISTRY_PREFIX=registry.k8s.io
-  # Kubernetes builds from 1.20 through 1.24 are tagged with k8s.gcr.io
-  if [[ "$${CI_VERSION}" =~ ^v1\.(1[0-9]|2[0-4])[\.[0-9]+ ]]; then
-    IMAGE_REGISTRY_PREFIX=k8s.gcr.io
-  fi
-  for CI_CONTAINER in "$${CONTAINERS_TO_TEST[@]}"; do
-    echo "* downloading package: $${CI_URL}/$${CI_CONTAINER}.$${CONTAINER_EXT}"
-    wget "$${CI_URL}/$${CI_CONTAINER}.$${CONTAINER_EXT}" -O "$${CI_DIR}/$${CI_CONTAINER}.$${CONTAINER_EXT}"
-    $${SUDO} ctr -n k8s.io images import "$${CI_DIR}/$${CI_CONTAINER}.$${CONTAINER_EXT}" || echo "* ignoring expected 'ctr images import' result"
-    $${SUDO} ctr -n k8s.io images tag "$${IMAGE_REGISTRY_PREFIX}/$${CI_CONTAINER}-amd64:$${KUBERNETES_VERSION//+/_}" "$${IMAGE_REGISTRY_PREFIX}/$${CI_CONTAINER}:$${KUBERNETES_VERSION//+/_}"
-    $${SUDO} ctr -n k8s.io images tag "$${IMAGE_REGISTRY_PREFIX}/$${CI_CONTAINER}-amd64:$${KUBERNETES_VERSION//+/_}" "gcr.io/k8s-staging-ci-images/$${CI_CONTAINER}:$${KUBERNETES_VERSION//+/_}"
-  done
 fi
 echo "* checking binary versions"
 echo "ctr version: " "$(ctr version)"
diff --git a/test/go.mod b/test/go.mod
index 1bca77c61..aa6c1ce3e 100644
--- a/test/go.mod
+++ b/test/go.mod
@@ -16,15 +16,15 @@ require (
 	github.com/spf13/pflag v1.0.5
 	github.com/vincent-petithory/dataurl v1.0.0
 	gomodules.xyz/jsonpatch/v2 v2.2.0
-	k8s.io/api v0.24.2
-	k8s.io/apiextensions-apiserver v0.24.2
-	k8s.io/apimachinery v0.24.2
-	k8s.io/client-go v0.24.2
-	k8s.io/component-base v0.24.2
+	k8s.io/api v0.24.0
+	k8s.io/apiextensions-apiserver v0.24.0
+	k8s.io/apimachinery v0.24.0
+	k8s.io/client-go v0.24.0
+	k8s.io/component-base v0.24.0
 	k8s.io/klog/v2 v2.60.1
 	k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9
 	sigs.k8s.io/cluster-api v0.0.0-00010101000000-000000000000
-	sigs.k8s.io/controller-runtime v0.12.2
+	sigs.k8s.io/controller-runtime v0.12.1
 	sigs.k8s.io/kind v0.14.0
 	sigs.k8s.io/yaml v1.3.0
 )
@@ -125,7 +125,7 @@ require (
 	gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect
 	gopkg.in/yaml.v3 v3.0.0 // indirect
-	k8s.io/apiserver v0.24.2 // indirect
+	k8s.io/apiserver v0.24.0 // indirect
 	k8s.io/cluster-bootstrap v0.24.0 // indirect
 	k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42 // indirect
 	sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 // indirect
diff --git a/test/go.sum b/test/go.sum
index b7a4af00a..7ed511558 100644
--- a/test/go.sum
+++ b/test/go.sum
@@ -1317,27 +1317,22 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
 honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
 honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
+k8s.io/api v0.24.0 h1:J0hann2hfxWr1hinZIDefw7Q96wmCBx6SSB8IY0MdDg=
 k8s.io/api v0.24.0/go.mod h1:5Jl90IUrJHUJYEMANRURMiVvJ0g7Ax7r3R1bqO8zx8I=
-k8s.io/api v0.24.2 h1:g518dPU/L7VRLxWfcadQn2OnsiGWVOadTLpdnqgY2OI=
-k8s.io/api v0.24.2/go.mod h1:AHqbSkTm6YrQ0ObxjO3Pmp/ubFF/KuM7jU+3khoBsOg=
-k8s.io/apiextensions-apiserver v0.24.2 h1:/4NEQHKlEz1MlaK/wHT5KMKC9UKYz6NZz6JE6ov4G6k=
-k8s.io/apiextensions-apiserver v0.24.2/go.mod h1:e5t2GMFVngUEHUd0wuCJzw8YDwZoqZfJiGOW6mm2hLQ=
+k8s.io/apiextensions-apiserver v0.24.0 h1:JfgFqbA8gKJ/uDT++feAqk9jBIwNnL9YGdQvaI9DLtY=
+k8s.io/apiextensions-apiserver v0.24.0/go.mod h1:iuVe4aEpe6827lvO6yWQVxiPSpPoSKVjkq+MIdg84cM=
+k8s.io/apimachinery v0.24.0 h1:ydFCyC/DjCvFCHK5OPMKBlxayQytB8pxy8YQInd5UyQ=
 k8s.io/apimachinery v0.24.0/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM=
-k8s.io/apimachinery v0.24.2 h1:5QlH9SL2C8KMcrNJPor+LbXVTaZRReml7svPEh4OKDM=
-k8s.io/apimachinery v0.24.2/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM=
-k8s.io/apiserver v0.24.2 h1:orxipm5elPJSkkFNlwH9ClqaKEDJJA3yR2cAAlCnyj4=
-k8s.io/apiserver v0.24.2/go.mod h1:pSuKzr3zV+L+MWqsEo0kHHYwCo77AT5qXbFXP2jbvFI=
+k8s.io/apiserver v0.24.0 h1:GR7kGsjOMfilRvlG3Stxv/3uz/ryvJ/aZXc5pqdsNV0=
+k8s.io/apiserver v0.24.0/go.mod h1:WFx2yiOMawnogNToVvUYT9nn1jaIkMKj41ZYCVycsBA=
 k8s.io/cli-runtime v0.24.0/go.mod h1:9XxoZDsEkRFUThnwqNviqzljtT/LdHtNWvcNFrAXl0A=
+k8s.io/client-go v0.24.0 h1:lbE4aB1gTHvYFSwm6eD3OF14NhFDKCejlnsGYlSJe5U=
 k8s.io/client-go v0.24.0/go.mod h1:VFPQET+cAFpYxh6Bq6f4xyMY80G6jKKktU6G0m00VDw=
-k8s.io/client-go v0.24.2 h1:CoXFSf8if+bLEbinDqN9ePIDGzcLtqhfd6jpfnwGOFA=
-k8s.io/client-go v0.24.2/go.mod h1:zg4Xaoo+umDsfCWr4fCnmLEtQXyCNXCvJuSsglNcV30=
 k8s.io/cluster-bootstrap v0.24.0 h1:MTs2x3Vfcl/PWvB5bfX7gzTFRyi4ZSbNSQgGJTCb6Sw=
 k8s.io/cluster-bootstrap v0.24.0/go.mod h1:xw+IfoaUweMCAoi+VYhmqkcjii2G7gNg59dmGn7hi0g=
 k8s.io/code-generator v0.24.0/go.mod h1:dpVhs00hTuTdTY6jvVxvTFCk6gSMrtfRydbhZwHI15w=
-k8s.io/code-generator v0.24.2/go.mod h1:dpVhs00hTuTdTY6jvVxvTFCk6gSMrtfRydbhZwHI15w=
+k8s.io/component-base v0.24.0 h1:h5jieHZQoHrY/lHG+HyrSbJeyfuitheBvqvKwKHVC0g=
 k8s.io/component-base v0.24.0/go.mod h1:Dgazgon0i7KYUsS8krG8muGiMVtUZxG037l1MKyXgrA=
-k8s.io/component-base v0.24.2 h1:kwpQdoSfbcH+8MPN4tALtajLDfSfYxBDYlXobNWI6OU=
-k8s.io/component-base v0.24.2/go.mod h1:ucHwW76dajvQ9B7+zecZAP3BVqvrHoOxm8olHEg0nmM=
 k8s.io/component-helpers v0.24.0/go.mod h1:Q2SlLm4h6g6lPTC9GMMfzdywfLSvJT2f1hOnnjaWD8c=
 k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
 k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
@@ -1358,8 +1353,8 @@ 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/apiserver-network-proxy/konnectivity-client v0.0.30/go.mod h1:fEO7lRTdivWO2qYVCVG7dEADOMo/MLDCVr8So2g88Uw=
-sigs.k8s.io/controller-runtime v0.12.2 h1:nqV02cvhbAj7tbt21bpPpTByrXGn2INHRsi39lXy9sE=
-sigs.k8s.io/controller-runtime v0.12.2/go.mod h1:qKsk4WE6zW2Hfj0G4v10EnNB2jMG1C+NTb8h+DwCoU0=
+sigs.k8s.io/controller-runtime v0.12.1 h1:4BJY01xe9zKQti8oRjj/NeHKRXthf1YkYJAgLONFFoI=
+sigs.k8s.io/controller-runtime v0.12.1/go.mod h1:BKhxlA4l7FPK4AQcsuL4X6vZeWnKDXez/vp1Y8dxTU0=
 sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 h1:kDi4JBNAsJWfz1aEXhO8Jg87JJaPNLh5tIzYHgStQ9Y=
 sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY=
 sigs.k8s.io/kind v0.14.0 h1:cNmI3jGBvp7UegEGbC5we8plDtCUmaNRL+bod7JoSCE=