Repository: syself/cluster-api-provider-hetzner
Pull request #1308 (2024-05-14 11:02:33)

:seedling: Update Update Golang Dependencies group (minor)
This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence | Type | Update |
|---|---|---|---|---|---|---|---|
| [github.com/hetznercloud/hcloud-go/v2](https://togithub.com/hetznercloud/hcloud-go) | `v2.8.0` -> `v2.9.0` | [![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fhetznercloud%2fhcloud-go%2fv2/v2.9.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/go/github.com%2fhetznercloud%2fhcloud-go%2fv2/v2.9.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/go/github.com%2fhetznercloud%2fhcloud-go%2fv2/v2.8.0/v2.9.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fhetznercloud%2fhcloud-go%2fv2/v2.8.0/v2.9.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | require | minor |
| [github.com/onsi/ginkgo/v2](https://togithub.com/onsi/ginkgo) | `v2.17.2` -> `v2.19.0` | [![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fonsi%2fginkgo%2fv2/v2.19.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/go/github.com%2fonsi%2fginkgo%2fv2/v2.19.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/go/github.com%2fonsi%2fginkgo%2fv2/v2.17.2/v2.19.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fonsi%2fginkgo%2fv2/v2.17.2/v2.19.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | require | minor |
| [github.com/stoewer/go-strcase](https://togithub.com/stoewer/go-strcase) | `v1.2.0` -> `v1.3.0` | [![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fstoewer%2fgo-strcase/v1.3.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/go/github.com%2fstoewer%2fgo-strcase/v1.3.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/go/github.com%2fstoewer%2fgo-strcase/v1.2.0/v1.3.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fstoewer%2fgo-strcase/v1.2.0/v1.3.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | require | minor |
| golang.org/x/crypto | `v0.23.0` -> `v0.24.0` | [![age](https://developer.mend.io/api/mc/badges/age/go/golang.org%2fx%2fcrypto/v0.24.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/go/golang.org%2fx%2fcrypto/v0.24.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/go/golang.org%2fx%2fcrypto/v0.23.0/v0.24.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/go/golang.org%2fx%2fcrypto/v0.23.0/v0.24.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | require | minor |
| golang.org/x/exp | `fe59bbe` -> `fc45aab` | [![age](https://developer.mend.io/api/mc/badges/age/go/golang.org%2fx%2fexp/v0.0.0-20240604190554-fc45aab8b7f8?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/go/golang.org%2fx%2fexp/v0.0.0-20240604190554-fc45aab8b7f8?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/go/golang.org%2fx%2fexp/v0.0.0-20240416160154-fe59bbe5cc7f/v0.0.0-20240604190554-fc45aab8b7f8?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/go/golang.org%2fx%2fexp/v0.0.0-20240416160154-fe59bbe5cc7f/v0.0.0-20240604190554-fc45aab8b7f8?slim=true)](https://docs.renovatebot.com/merge-confidence/) | require | digest |
| golang.org/x/mod | `v0.17.0` -> `v0.18.0` | [![age](https://developer.mend.io/api/mc/badges/age/go/golang.org%2fx%2fmod/v0.18.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/go/golang.org%2fx%2fmod/v0.18.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/go/golang.org%2fx%2fmod/v0.17.0/v0.18.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/go/golang.org%2fx%2fmod/v0.17.0/v0.18.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | require | minor |
| [k8s.io/utils](https://togithub.com/kubernetes/utils) | `4693a02` -> `fe8a2dd` | [![age](https://developer.mend.io/api/mc/badges/age/go/k8s.io%2futils/v0.0.0-20240502163921-fe8a2dddb1d0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/go/k8s.io%2futils/v0.0.0-20240502163921-fe8a2dddb1d0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/go/k8s.io%2futils/v0.0.0-20240310230437-4693a0247e57/v0.0.0-20240502163921-fe8a2dddb1d0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/go/k8s.io%2futils/v0.0.0-20240310230437-4693a0247e57/v0.0.0-20240502163921-fe8a2dddb1d0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | require | digest |
| [sigs.k8s.io/kind](https://togithub.com/kubernetes-sigs/kind) | `v0.22.0` -> `v0.23.0` | [![age](https://developer.mend.io/api/mc/badges/age/go/sigs.k8s.io%2fkind/v0.23.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/go/sigs.k8s.io%2fkind/v0.23.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/go/sigs.k8s.io%2fkind/v0.22.0/v0.23.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/go/sigs.k8s.io%2fkind/v0.22.0/v0.23.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | require | minor |

---

### Release Notes

<details>
<summary>hetznercloud/hcloud-go (github.com/hetznercloud/hcloud-go/v2)</summary>

### [`v2.9.0`](https://togithub.com/hetznercloud/hcloud-go/releases/tag/v2.9.0)

[Compare Source](https://togithub.com/hetznercloud/hcloud-go/compare/v2.8.0...v2.9.0)

##### Features

-   **exp:** add `AppendNextActions` function ([#&#8203;440](https://togithub.com/hetznercloud/hcloud-go/issues/440)) ([b07d7ad](https://togithub.com/hetznercloud/hcloud-go/commit/b07d7adf0bb08dd372f1f6ff630d16af8cb1265c))
-   **exp:** add ssh key functions ([#&#8203;441](https://togithub.com/hetznercloud/hcloud-go/issues/441)) ([d766e96](https://togithub.com/hetznercloud/hcloud-go/commit/d766e96e2220b069a0a8067a3249afe48cf68b2e))

##### Bug Fixes

-   **exp:** rename to `sshutils` package name ([#&#8203;450](https://togithub.com/hetznercloud/hcloud-go/issues/450)) ([6d4100d](https://togithub.com/hetznercloud/hcloud-go/commit/6d4100dbceb9a43a66bad3ce91cee039198130c3))

</details>

<details>
<summary>onsi/ginkgo (github.com/onsi/ginkgo/v2)</summary>

### [`v2.19.0`](https://togithub.com/onsi/ginkgo/releases/tag/v2.19.0)

[Compare Source](https://togithub.com/onsi/ginkgo/compare/v2.18.0...v2.19.0)

#### 2.19.0

##### Features

[Label Sets](https://onsi.github.io/ginkgo/#label-sets) allow for more expressive and flexible label filtering.

### [`v2.18.0`](https://togithub.com/onsi/ginkgo/releases/tag/v2.18.0)

[Compare Source](https://togithub.com/onsi/ginkgo/compare/v2.17.3...v2.18.0)

#### 2.18.0

##### Features

-   Add --slience-skips and --force-newlines \[[`f010b65`](https://togithub.com/onsi/ginkgo/commit/f010b65)]
-   fail when no tests were run and --fail-on-empty was set \[[`d80eebe`](https://togithub.com/onsi/ginkgo/commit/d80eebe)]

##### Fixes

-   Fix table entry context edge case \[[`42013d6`](https://togithub.com/onsi/ginkgo/commit/42013d6)]

##### Maintenance

-   Bump golang.org/x/tools from 0.20.0 to 0.21.0 ([#&#8203;1406](https://togithub.com/onsi/ginkgo/issues/1406)) \[[`fcf1fd7`](https://togithub.com/onsi/ginkgo/commit/fcf1fd7)]
-   Bump github.com/onsi/gomega from 1.33.0 to 1.33.1 ([#&#8203;1399](https://togithub.com/onsi/ginkgo/issues/1399)) \[[`8bb14fd`](https://togithub.com/onsi/ginkgo/commit/8bb14fd)]
-   Bump golang.org/x/net from 0.24.0 to 0.25.0 ([#&#8203;1407](https://togithub.com/onsi/ginkgo/issues/1407)) \[[`04bfad7`](https://togithub.com/onsi/ginkgo/commit/04bfad7)]

### [`v2.17.3`](https://togithub.com/onsi/ginkgo/releases/tag/v2.17.3)

[Compare Source](https://togithub.com/onsi/ginkgo/compare/v2.17.2...v2.17.3)

#### 2.17.3

##### Fixes

`ginkgo watch` now ignores hidden files \[[`bde6e00`](https://togithub.com/onsi/ginkgo/commit/bde6e00)]

</details>

<details>
<summary>stoewer/go-strcase (github.com/stoewer/go-strcase)</summary>

### [`v1.3.0`](https://togithub.com/stoewer/go-strcase/releases/tag/v1.3.0): Release 1.3.0

[Compare Source](https://togithub.com/stoewer/go-strcase/compare/v1.2.1...v1.3.0)

-   Fix bug in camel case conversion [#&#8203;9](https://togithub.com/stoewer/go-strcase/issues/9) (PR [#&#8203;11](https://togithub.com/stoewer/go-strcase/issues/11)). Please notice that this changes existing string conversion behavior and might break software that relies on the previous conversion results.

### [`v1.2.1`](https://togithub.com/stoewer/go-strcase/compare/v1.2.0...v1.2.1)

[Compare Source](https://togithub.com/stoewer/go-strcase/compare/v1.2.0...v1.2.1)

</details>

<details>
<summary>kubernetes-sigs/kind (sigs.k8s.io/kind)</summary>

### [`v0.23.0`](https://togithub.com/kubernetes-sigs/kind/releases/tag/v0.23.0)

[Compare Source](https://togithub.com/kubernetes-sigs/kind/compare/v0.22.0...v0.23.0)

This release introduces initial limited support for `nerdctl` and kube-proxy nftables mode.

<h1 id="breaking-changes">Breaking Changes</h1>

-   The default node image is now Kubernetes 1.30.0: `kindest/node:v1.30.0@&#8203;sha256:047357ac0cfea04663786a612ba1eaba9702bef25227a794b52890dd8bcd692e`
-   `ipFamily` config field is now validated
    -   While technically a breaking change, if the value you set here is now caught as invalid it was being silently ignored and creating an ipv4 cluster previously and you should correct your config
-   Go 1.17+ is required for `go install sigs.k8s.io/kind` / building the `kind` binary
    -   Prebuilt binaries are available as an alternative to `go install`

    -   For local development `make` will automatically setup the correct go version

    -   Note that the go team only supports 1.21+ and major linux distros have 1.19+

    -   Future releases may increase this to a more current Go release

    -   Future release may adopt `toolchain` in go.mod to make this seamless if you have go 1.21+ installed even without our makefile. We highly recommend installing go 1.21+

<h1 id="new-features">New Features</h1>

-   Initial support for nodes created with [nerdctl](https://togithub.com/containerd/nerdctl)
-   Initial support for `kubeProxyMode: nftables` (ahead of Kubernetes 1.31+, see https://kind.sigs.k8s.io/docs/user/configuration/#kube-proxy-mode)
-   Sweeping dependency updates, see commits for full details. https://github.com/kubernetes-sigs/kind/compare/v0.22.0...v0.23.0

Images pre-built for this release:

-   v1.30.0: `kindest/node:v1.30.0@&#8203;sha256:047357ac0cfea04663786a612ba1eaba9702bef25227a794b52890dd8bcd692e`
-   v1.29.4: `kindest/node:v1.29.4@&#8203;sha256:3abb816a5b1061fb15c6e9e60856ec40d56b7b52bcea5f5f1350bc6e2320b6f8`
-   v1.28.9: `kindest/node:v1.28.9@&#8203;sha256:dca54bc6a6079dd34699d53d7d4ffa2e853e46a20cd12d619a09207e35300bd0`
-   v1.27.13: `kindest/node:v1.27.13@&#8203;sha256:17439fa5b32290e3ead39ead1250dca1d822d94a10d26f1981756cd51b24b9d8`
-   v1.26.15: `kindest/node:v1.26.15@&#8203;sha256:84333e26cae1d70361bb7339efb568df1871419f2019c80f9a12b7e2d485fe19`
-   v1.25.16: `kindest/node:v1.25.16@&#8203;sha256:5da57dfc290ac3599e775e63b8b6c49c0c85d3fec771cd7d55b45fae14b38d3b`

**NOTE**: You *must* use the `@sha256` digest to guarantee an image built for this release, until such a time as we switch to a different tagging scheme. Even then we will highly encourage digest pinning for security and reproducibility reasons.

See also:

-   https://kind.sigs.k8s.io/docs/user/quick-start/#creating-a-cluster
-   https://kind.sigs.k8s.io/docs/user/quick-start/#building-images

NOTE: These node images support amd64 and arm64, both of our supported platforms. **You must use the same platform as your host,** for more context see [https://github.com/kubernetes-sigs/kind/issues/2718](https://togithub.com/kubernetes-sigs/kind/issues/2718)

<h1 id="fixes">Fixes</h1>

-   Correctly case `kubeProxyMode: "none"`

<h1 id="contributors">Contributors</h1>

**Thank you to everyone who contributed to this release! ❤️**

Users whose commits are in this release (alphabetically by user name)

-   [@&#8203;aojea](https://togithub.com/aojea)
-   [@&#8203;AkihiroSuda](https://togithub.com/AkihiroSuda)
-   [@&#8203;BenTheElder](https://togithub.com/BenTheElder)
-   [@&#8203;dependabot](https://togithub.com/dependabot)\[bot]
-   [@&#8203;estesp](https://togithub.com/estesp)
-   [@&#8203;hp685](https://togithub.com/hp685)
-   [@&#8203;jizusun](https://togithub.com/jizusun)
-   [@&#8203;k8s-ci-robot](https://togithub.com/k8s-ci-robot)
-   [@&#8203;kevin85421](https://togithub.com/kevin85421)
-   [@&#8203;stmcginnis](https://togithub.com/stmcginnis)
-   [@&#8203;tnqn](https://togithub.com/tnqn)
-   [@&#8203;yankay](https://togithub.com/yankay)

Thank you as well to everyone else not listed here who contributed in other ways like filing issues, giving feedback, testing fixes, and helping users in slack!

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://togithub.com/renovatebot/renovate/discussions) if that's undesired.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy4zNjEuMCIsInVwZGF0ZWRJblZlciI6IjM3LjM5MS4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJ0eXBlL21pbm9yIiwidXBkYXRlL2dvIl19-->


syself-bot[bot]: ### ℹ Artifact update notice

##### File name: go.mod

In order to perform the update(s) described in the table above, Renovate ran the `go get` command, which resulted in the following additional change(s):


- 2 additional dependency was updated


Details:
| **Package**          | **Change**             |
| :------------------- | :--------------------- |
| `golang.org/x/net`   | `v0.24.0` -> `v0.25.0` |
| `golang.org/x/tools` | `v0.20.0` -> `v0.21.0` |

Patch:
diff --git a/.github/workflows/main-promote-builder-image.yml b/.github/workflows/main-promote-builder-image.yml
index 47d54af5..3a17da9e 100644
--- a/.github/workflows/main-promote-builder-image.yml
+++ b/.github/workflows/main-promote-builder-image.yml
@@ -16,7 +16,7 @@ jobs:
         password: ${{ secrets.github_token }}
     steps:
       - name: Checkout repository
-        uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
+        uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
       - name: Fixup git permissions
         # https://github.com/actions/checkout/issues/766
         shell: bash
diff --git a/.github/workflows/pr-lint.yml b/.github/workflows/pr-lint.yml
index 7f218e03..241d6115 100644
--- a/.github/workflows/pr-lint.yml
+++ b/.github/workflows/pr-lint.yml
@@ -29,7 +29,7 @@ jobs:
         password: ${{ secrets.github_token }}
     steps:
       - name: Checkout repository
-        uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
+        uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
         with:
           ref: ${{ github.event.pull_request.head.sha }}
 
diff --git a/.github/workflows/pr-verify.yml b/.github/workflows/pr-verify.yml
index e3ff4d2a..40b9fbe3 100644
--- a/.github/workflows/pr-verify.yml
+++ b/.github/workflows/pr-verify.yml
@@ -16,7 +16,7 @@ jobs:
           github_token: ${{ secrets.GITHUB_TOKEN }}
 
       - name: Checkout repository
-        uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
+        uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
         with:
           ref: ${{ github.event.pull_request.head.sha }}
 
diff --git a/.github/workflows/report-bin-size.yml b/.github/workflows/report-bin-size.yml
index 97751b28..3f649901 100644
--- a/.github/workflows/report-bin-size.yml
+++ b/.github/workflows/report-bin-size.yml
@@ -9,7 +9,7 @@ jobs:
     timeout-minutes: 10
     steps:
       - name: Checkout repository
-        uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
+        uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
       - name: Fixup git permissions
         # https://github.com/actions/checkout/issues/766
         shell: bash
diff --git a/.github/workflows/schedule-scan-image.yml b/.github/workflows/schedule-scan-image.yml
index 13bd7a18..4b0e6d42 100644
--- a/.github/workflows/schedule-scan-image.yml
+++ b/.github/workflows/schedule-scan-image.yml
@@ -16,7 +16,7 @@ jobs:
         password: ${{ secrets.github_token }}
     steps:
       - name: Checkout repository
-        uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
+        uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
       - name: Fixup git permissions
         # https://github.com/actions/checkout/issues/766
         shell: bash
diff --git a/.github/workflows/schedule-update-bot.yaml b/.github/workflows/schedule-update-bot.yaml
index 84decad1..97e8884b 100644
--- a/.github/workflows/schedule-update-bot.yaml
+++ b/.github/workflows/schedule-update-bot.yaml
@@ -30,7 +30,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - name: Checkout
-        uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
+        uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
 
       - name: Generate Token
         uses: actions/create-github-app-token@a0de6af83968303c8c955486bf9739a57d23c7f1 # v1
@@ -44,7 +44,7 @@ jobs:
           echo "LOG_LEVEL=${{ github.event.inputs.logLevel || env.LOG_LEVEL }}" >> "$GITHUB_ENV"
 
       - name: Renovate
-        uses: renovatebot/github-action@063e0c946b9c1af35ef3450efc44114925d6e8e6 # v40.1.11
+        uses: renovatebot/github-action@21d88b0bf0183abcee15f990011cca090dfc47dd # v40.1.12
         env:
           RENOVATE_HOST_RULES: '[{"hostType": "docker", "matchHost": "ghcr.io", "username": "${{ github.actor }}", "password": "${{ secrets.GITHUB_TOKEN }}" }]'
           RENOVATE_ALLOWED_POST_UPGRADE_COMMANDS: '[".*"]'
diff --git a/api/v1beta1/hcloudmachine_types.go b/api/v1beta1/hcloudmachine_types.go
index 5a26f55b..19f5c489 100644
--- a/api/v1beta1/hcloudmachine_types.go
+++ b/api/v1beta1/hcloudmachine_types.go
@@ -37,7 +37,7 @@ type HCloudMachineSpec struct {
 	ProviderID *string `json:"providerID,omitempty"`
 
 	// Type is the HCloud Machine Type for this machine. It defines the desired server type of server in Hetzner's Cloud API. Example: cpx11.
-	// +kubebuilder:validation:Enum=cpx11;cx21;cpx21;cx31;cpx31;cx41;cpx41;cx51;cpx51;ccx11;ccx12;ccx13;ccx21;ccx22;ccx23;ccx31;ccx32;ccx33;ccx41;ccx42;ccx43;ccx51;ccx52;ccx53;ccx62;ccx63;cax11;cax21;cax31;cax41
+	// +kubebuilder:validation:Enum=cpx11;cx21;cpx21;cx31;cpx31;cx41;cpx41;cx51;cpx51;ccx11;ccx12;ccx13;ccx21;ccx22;ccx23;ccx31;ccx32;ccx33;ccx41;ccx42;ccx43;ccx51;ccx52;ccx53;ccx62;ccx63;cax11;cax21;cax31;cax41;cx22;cx32;cx42;cx52
 	Type HCloudMachineType `json:"type"`
 
 	// ImageName is the reference to the Machine Image from which to create the machine instance.
diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_hcloudmachines.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_hcloudmachines.yaml
index 094a7c7a..d5427343 100644
--- a/config/crd/bases/infrastructure.cluster.x-k8s.io_hcloudmachines.yaml
+++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_hcloudmachines.yaml
@@ -151,6 +151,10 @@ spec:
                 - cax21
                 - cax31
                 - cax41
+                - cx22
+                - cx32
+                - cx42
+                - cx52
                 type: string
             required:
             - imageName
diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_hcloudmachinetemplates.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_hcloudmachinetemplates.yaml
index 81034649..9ee936e9 100644
--- a/config/crd/bases/infrastructure.cluster.x-k8s.io_hcloudmachinetemplates.yaml
+++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_hcloudmachinetemplates.yaml
@@ -178,6 +178,10 @@ spec:
                         - cax21
                         - cax31
                         - cax41
+                        - cx22
+                        - cx32
+                        - cx42
+                        - cx52
                         type: string
                     required:
                     - imageName
diff --git a/go.mod b/go.mod
index 7fb6108c..62d08f20 100644
--- a/go.mod
+++ b/go.mod
@@ -6,28 +6,28 @@ require (
 	github.com/blang/semver/v4 v4.0.0
 	github.com/go-logr/logr v1.4.1
 	github.com/go-logr/zapr v1.3.0
-	github.com/hetznercloud/hcloud-go/v2 v2.8.0
-	github.com/onsi/ginkgo/v2 v2.17.2
+	github.com/hetznercloud/hcloud-go/v2 v2.9.0
+	github.com/onsi/ginkgo/v2 v2.19.0
 	github.com/onsi/gomega v1.33.1
 	github.com/spf13/pflag v1.0.5
-	github.com/stoewer/go-strcase v1.2.0
+	github.com/stoewer/go-strcase v1.3.0
 	github.com/stretchr/testify v1.9.0
 	github.com/syself/hrobot-go v0.2.5
 	go.uber.org/zap v1.27.0
-	golang.org/x/crypto v0.23.0
-	golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f
-	golang.org/x/mod v0.17.0
+	golang.org/x/crypto v0.24.0
+	golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8
+	golang.org/x/mod v0.18.0
 	k8s.io/api v0.29.3
 	k8s.io/apimachinery v0.29.3
 	k8s.io/apiserver v0.29.3
 	k8s.io/client-go v0.29.3
 	k8s.io/klog/v2 v2.120.1
 	k8s.io/kubectl v0.29.3
-	k8s.io/utils v0.0.0-20240310230437-4693a0247e57
+	k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0
 	sigs.k8s.io/cluster-api v1.7.2
 	sigs.k8s.io/cluster-api/test v1.7.2
 	sigs.k8s.io/controller-runtime v0.17.3
-	sigs.k8s.io/kind v0.22.0
+	sigs.k8s.io/kind v0.23.0
 )
 
 require (
@@ -95,7 +95,7 @@ require (
 	github.com/pelletier/go-toml/v2 v2.1.0 // indirect
 	github.com/pkg/errors v0.9.1 // indirect
 	github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
-	github.com/prometheus/client_golang v1.19.0 // indirect
+	github.com/prometheus/client_golang v1.19.1 // indirect
 	github.com/prometheus/client_model v0.5.0 // indirect
 	github.com/prometheus/common v0.48.0 // indirect
 	github.com/prometheus/procfs v0.12.0 // indirect
@@ -115,14 +115,14 @@ require (
 	go.opentelemetry.io/otel/metric v1.22.0 // indirect
 	go.opentelemetry.io/otel/trace v1.22.0 // indirect
 	go.uber.org/multierr v1.11.0 // indirect
-	golang.org/x/net v0.24.0 // indirect
+	golang.org/x/net v0.26.0 // indirect
 	golang.org/x/oauth2 v0.18.0 // indirect
 	golang.org/x/sync v0.7.0 // indirect
-	golang.org/x/sys v0.20.0 // indirect
-	golang.org/x/term v0.20.0 // indirect
-	golang.org/x/text v0.15.0 // indirect
+	golang.org/x/sys v0.21.0 // indirect
+	golang.org/x/term v0.21.0 // indirect
+	golang.org/x/text v0.16.0 // indirect
 	golang.org/x/time v0.5.0 // indirect
-	golang.org/x/tools v0.20.0 // indirect
+	golang.org/x/tools v0.22.0 // indirect
 	gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
 	google.golang.org/appengine v1.6.8 // indirect
 	google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 // indirect
diff --git a/go.sum b/go.sum
index 92278329..98ba56cd 100644
--- a/go.sum
+++ b/go.sum
@@ -45,7 +45,6 @@ github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03V
 github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU=
 github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
 github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
-github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
 github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -66,7 +65,6 @@ github.com/emicklei/go-restful/v3 v3.12.0 h1:y2DdzBAURM29NFF94q6RaY4vjIH1rtwDapw
 github.com/emicklei/go-restful/v3 v3.12.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
 github.com/evanphx/json-patch v5.7.0+incompatible h1:vgGkfT/9f8zE6tvSCe74nfpAVDQ2tG6yudJd8LBksgI=
 github.com/evanphx/json-patch v5.7.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
-github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4=
 github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg=
 github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ=
 github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
@@ -131,17 +129,15 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rH
 github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg=
 github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
 github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
-github.com/hetznercloud/hcloud-go/v2 v2.8.0 h1:vfbfL/JfV8dIZUX7ANHWEbKNqgFWsETqvt/EctvoFJ0=
-github.com/hetznercloud/hcloud-go/v2 v2.8.0/go.mod h1:jvpP3qAWMIZ3WQwQLYa97ia6t98iPCgsJNwRts+Jnrk=
+github.com/hetznercloud/hcloud-go/v2 v2.9.0 h1:s0N6R7Zoi2DPfMtUF5o9VeUBzTtHVY6MIkHOQnfu/AY=
+github.com/hetznercloud/hcloud-go/v2 v2.9.0/go.mod h1:qtW/TuU7Bs16ibXl/ktJarWqU2LwHr7eGlwoilHxtgg=
 github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4=
 github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
 github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
 github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
 github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
-github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
 github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
-github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
 github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
 github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
 github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
@@ -159,7 +155,6 @@ github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0V
 github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
 github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
 github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
-github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
 github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
 github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
 github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
@@ -181,28 +176,25 @@ github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
 github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
-github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
-github.com/onsi/ginkgo/v2 v2.17.2 h1:7eMhcy3GimbsA3hEnVKdw/PQM9XN9krpKVXsZdph0/g=
-github.com/onsi/ginkgo/v2 v2.17.2/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc=
+github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA=
+github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To=
 github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk=
 github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0=
 github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
 github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
 github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
-github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
 github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
 github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
 github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
-github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU=
-github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
+github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
+github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
 github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
 github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
 github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
@@ -229,15 +221,14 @@ github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNo
 github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
 github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
 github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
-github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g=
 github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
 github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
 github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
 github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ=
 github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk=
-github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU=
-github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
+github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs=
+github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
 github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
@@ -299,15 +290,15 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
-golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
-golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
-golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2nTQVIEhZIptnrVb1XY=
-golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
+golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
+golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
+golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8 h1:LoYXNGAShUG3m/ehNk4iFctuhGX/+R1ZpfJ4/ia80JM=
+golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI=
 golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
-golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
-golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
+golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
+golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -315,8 +306,8 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
-golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
-golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
+golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
+golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
 golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI=
 golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -332,27 +323,26 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
-golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
+golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
-golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
-golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
+golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA=
+golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
 golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
-golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
+golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
 golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
 golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -360,8 +350,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
 golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
-golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
-golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=
+golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
+golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -383,7 +373,6 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ
 google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
 google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
 gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
@@ -421,8 +410,8 @@ k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/A
 k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA=
 k8s.io/kubectl v0.29.3 h1:RuwyyIU42MAISRIePaa8Q7A3U74Q9P4MoJbDFz9o3us=
 k8s.io/kubectl v0.29.3/go.mod h1:yCxfY1dbwgVdEt2zkJ6d5NNLOhhWgTyrqACIoFhpdd4=
-k8s.io/utils v0.0.0-20240310230437-4693a0247e57 h1:gbqbevonBh57eILzModw6mrkbwM0gQBEuevE/AaBsHY=
-k8s.io/utils v0.0.0-20240310230437-4693a0247e57/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
+k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0 h1:jgGTlFYnhF1PM1Ax/lAlxUPE+KfCIXHaathvJg1C3ak=
+k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
 sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.28.0 h1:TgtAeesdhpm2SGwkQasmbeqDo8th5wOBA5h/AjTKA4I=
 sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.28.0/go.mod h1:VHVDI/KrK4fjnV61bE2g3sA7tiETLn8sooImelsCx3Y=
 sigs.k8s.io/cluster-api v1.7.2 h1:bRE8zoao7ajuLC0HijqfZVcubKQCPlZ04HMgcA53FGE=
@@ -433,10 +422,9 @@ sigs.k8s.io/controller-runtime v0.17.3 h1:65QmN7r3FWgTxDMz9fvGnO1kbf2nu+acg9p2R9
 sigs.k8s.io/controller-runtime v0.17.3/go.mod h1:N0jpP5Lo7lMTF9aL56Z/B2oWBJjey6StQM0jRbKQXtY=
 sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
 sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
-sigs.k8s.io/kind v0.22.0 h1:z/+yr/azoOfzsfooqRsPw1wjJlqT/ukXP0ShkHwNlsI=
-sigs.k8s.io/kind v0.22.0/go.mod h1:aBlbxg08cauDgZ612shr017/rZwqd7AS563FvpWKPVs=
+sigs.k8s.io/kind v0.23.0 h1:8fyDGWbWTeCcCTwA04v4Nfr45KKxbSPH1WO9K+jVrBg=
+sigs.k8s.io/kind v0.23.0/go.mod h1:ZQ1iZuJLh3T+O8fzhdi3VWcFTzsdXtNv2ppsHc8JQ7s=
 sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
 sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
-sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
 sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
 sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
diff --git a/templates/cluster-templates/bases/hcloud-kcp-ubuntu.yaml b/templates/cluster-templates/bases/hcloud-kcp-ubuntu.yaml
index 20b1846d..b45414f5 100644
--- a/templates/cluster-templates/bases/hcloud-kcp-ubuntu.yaml
+++ b/templates/cluster-templates/bases/hcloud-kcp-ubuntu.yaml
@@ -166,7 +166,7 @@ spec:
           nameserver 2606:4700:4700::1111
     preKubeadmCommands:
       - set -x
-      - export CONTAINERD=1.7.17 # update: datasource=github-tags depName=containerd/containerd extractVersion=^v(?<version>.*)$ versioning=semver
+      - export CONTAINERD=1.7.18 # update: datasource=github-tags depName=containerd/containerd extractVersion=^v(?<version>.*)$ versioning=semver
       - export KUBERNETES_VERSION=$(echo ${KUBERNETES_VERSION} | sed 's/^v//')
       - export TRIMMED_KUBERNETES_VERSION=$(echo ${KUBERNETES_VERSION} | sed 's/^v//' | awk -F . '{print $1 "." $2}')
       - ARCH=amd64
diff --git a/templates/cluster-templates/bases/hetznerbaremetal-kcp-ubuntu.yaml b/templates/cluster-templates/bases/hetznerbaremetal-kcp-ubuntu.yaml
index 00ca468f..41959210 100644
--- a/templates/cluster-templates/bases/hetznerbaremetal-kcp-ubuntu.yaml
+++ b/templates/cluster-templates/bases/hetznerbaremetal-kcp-ubuntu.yaml
@@ -185,7 +185,7 @@ spec:
           nameserver 2606:4700:4700::1111
     preKubeadmCommands:
       - set -x
-      - export CONTAINERD=1.7.17 # update: datasource=github-tags depName=containerd/containerd extractVersion=^v(?<version>.*)$ versioning=semver
+      - export CONTAINERD=1.7.18 # update: datasource=github-tags depName=containerd/containerd extractVersion=^v(?<version>.*)$ versioning=semver
       - export KUBERNETES_VERSION=$(echo ${KUBERNETES_VERSION} | sed 's/^v//')
       - export TRIMMED_KUBERNETES_VERSION=$(echo ${KUBERNETES_VERSION} | sed 's/^v//' | awk -F . '{print $1 "." $2}')
       - localectl set-locale LANG=en_US.UTF-8
diff --git a/templates/cluster-templates/bases/kct-md-0-ubuntu.yaml b/templates/cluster-templates/bases/kct-md-0-ubuntu.yaml
index 14dd383c..abcecbc5 100644
--- a/templates/cluster-templates/bases/kct-md-0-ubuntu.yaml
+++ b/templates/cluster-templates/bases/kct-md-0-ubuntu.yaml
@@ -55,7 +55,7 @@ spec:
       preKubeadmCommands:
         - set -x
         - grep VERSION= /etc/os-release; uname -a
-        - export CONTAINERD=1.7.17 # update: datasource=github-tags depName=containerd/containerd extractVersion=^v(?<version>.*)$ versioning=semver
+        - export CONTAINERD=1.7.18 # update: datasource=github-tags depName=containerd/containerd extractVersion=^v(?<version>.*)$ versioning=semver
         - export KUBERNETES_VERSION=$(echo ${KUBERNETES_VERSION} | sed 's/^v//')
         - export TRIMMED_KUBERNETES_VERSION=$(echo ${KUBERNETES_VERSION} | sed 's/^v//' | awk -F . '{print $1 "." $2}')
         - ARCH=amd64
diff --git a/vendor/github.com/hetznercloud/hcloud-go/v2/hcloud/hcloud.go b/vendor/github.com/hetznercloud/hcloud-go/v2/hcloud/hcloud.go
index 2c6745c2..4f10fdd0 100644
--- a/vendor/github.com/hetznercloud/hcloud-go/v2/hcloud/hcloud.go
+++ b/vendor/github.com/hetznercloud/hcloud-go/v2/hcloud/hcloud.go
@@ -2,4 +2,4 @@
 package hcloud
 
 // Version is the library's version following Semantic Versioning.
-const Version = "2.8.0" // x-release-please-version
+const Version = "2.9.0" // x-release-please-version
diff --git a/vendor/github.com/onsi/ginkgo/v2/CHANGELOG.md b/vendor/github.com/onsi/ginkgo/v2/CHANGELOG.md
index 0a894979..76577dc7 100644
--- a/vendor/github.com/onsi/ginkgo/v2/CHANGELOG.md
+++ b/vendor/github.com/onsi/ginkgo/v2/CHANGELOG.md
@@ -1,3 +1,28 @@
+## 2.19.0
+
+### Features
+
+[Label Sets](https://onsi.github.io/ginkgo/#label-sets) allow for more expressive and flexible label filtering.
+
+## 2.18.0
+
+### Features
+- Add --slience-skips and --force-newlines [f010b65]
+- fail when no tests were run and --fail-on-empty was set [d80eebe]
+
+### Fixes
+- Fix table entry context edge case [42013d6]
+
+### Maintenance
+- Bump golang.org/x/tools from 0.20.0 to 0.21.0 (#1406) [fcf1fd7]
+- Bump github.com/onsi/gomega from 1.33.0 to 1.33.1 (#1399) [8bb14fd]
+- Bump golang.org/x/net from 0.24.0 to 0.25.0 (#1407) [04bfad7]
+
+## 2.17.3
+
+### Fixes
+`ginkgo watch` now ignores hidden files [bde6e00]
+
 ## 2.17.2
 
 ### Fixes
diff --git a/vendor/github.com/onsi/ginkgo/v2/CONTRIBUTING.md b/vendor/github.com/onsi/ginkgo/v2/CONTRIBUTING.md
index 1da92fe7..80de566a 100644
--- a/vendor/github.com/onsi/ginkgo/v2/CONTRIBUTING.md
+++ b/vendor/github.com/onsi/ginkgo/v2/CONTRIBUTING.md
@@ -6,8 +6,10 @@ Your contributions to Ginkgo are essential for its long-term maintenance and imp
 - Ensure adequate test coverage:
     - When adding to the Ginkgo library, add unit and/or integration tests (under the `integration` folder).
     - When adding to the Ginkgo CLI, note that there are very few unit tests.  Please add an integration test.
-- Make sure all the tests succeed via `ginkgo -r -p`
-- Vet your changes via `go vet ./...`
-- Update the documentation. Ginkgo uses `godoc` comments and documentation in `docs/index.md`.  You can run `bundle exec jekyll serve` in the `docs` directory to preview your changes.
+- Run `make` or:
+  - Install ginkgo locally via `go install ./...`
+  - Make sure all the tests succeed via `ginkgo -r -p`
+  - Vet your changes via `go vet ./...`
+- Update the documentation. Ginkgo uses `godoc` comments and documentation in `docs/index.md`.  You can run `bundle && bundle exec jekyll serve` in the `docs` directory to preview your changes.
 
-Thanks for supporting Ginkgo!
\ No newline at end of file
+Thanks for supporting Ginkgo!
diff --git a/vendor/github.com/onsi/ginkgo/v2/Makefile b/vendor/github.com/onsi/ginkgo/v2/Makefile
new file mode 100644
index 00000000..cb099aff
--- /dev/null
+++ b/vendor/github.com/onsi/ginkgo/v2/Makefile
@@ -0,0 +1,11 @@
+# default task since it's first
+.PHONY: all
+all:  vet test
+
+.PHONY: test
+test:
+	go run github.com/onsi/ginkgo/v2/ginkgo -r -p
+
+.PHONY: vet
+vet:
+	go vet ./...
diff --git a/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/package_hash.go b/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/package_hash.go
index 17d052bd..0e6ae1f2 100644
--- a/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/package_hash.go
+++ b/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/package_hash.go
@@ -4,6 +4,7 @@ import (
 	"fmt"
 	"os"
 	"regexp"
+	"strings"
 	"time"
 )
 
@@ -79,6 +80,10 @@ func (p *PackageHash) computeHashes() (codeHash string, codeModifiedTime time.Ti
 			continue
 		}
 
+		if isHiddenFile(info) {
+			continue
+		}
+
 		if goTestRegExp.MatchString(info.Name()) {
 			testHash += p.hashForFileInfo(info)
 			if info.ModTime().After(testModifiedTime) {
@@ -103,6 +108,10 @@ func (p *PackageHash) computeHashes() (codeHash string, codeModifiedTime time.Ti
 	return
 }
 
+func isHiddenFile(info os.FileInfo) bool {
+	return strings.HasPrefix(info.Name(), ".") || strings.HasPrefix(info.Name(), "_")
+}
+
 func (p *PackageHash) hashForFileInfo(info os.FileInfo) string {
 	return fmt.Sprintf("%s_%d_%d", info.Name(), info.Size(), info.ModTime().UnixNano())
 }
diff --git a/vendor/github.com/onsi/ginkgo/v2/internal/suite.go b/vendor/github.com/onsi/ginkgo/v2/internal/suite.go
index a994ee3d..a3c9e6bf 100644
--- a/vendor/github.com/onsi/ginkgo/v2/internal/suite.go
+++ b/vendor/github.com/onsi/ginkgo/v2/internal/suite.go
@@ -489,10 +489,15 @@ func (suite *Suite) runSpecs(description string, suiteLabels Labels, suitePath s
 			newGroup(suite).run(specs.AtIndices(groupedSpecIndices[groupedSpecIdx]))
 		}
 
-		if specs.HasAnySpecsMarkedPending() && suite.config.FailOnPending {
+		if suite.config.FailOnPending && specs.HasAnySpecsMarkedPending() {
 			suite.report.SpecialSuiteFailureReasons = append(suite.report.SpecialSuiteFailureReasons, "Detected pending specs and --fail-on-pending is set")
 			suite.report.SuiteSucceeded = false
 		}
+
+		if suite.config.FailOnEmpty && specs.CountWithoutSkip() == 0 {
+			suite.report.SpecialSuiteFailureReasons = append(suite.report.SpecialSuiteFailureReasons, "Detected no specs ran and --fail-on-empty is set")
+			suite.report.SuiteSucceeded = false
+		}
 	}
 
 	if ranBeforeSuite {
diff --git a/vendor/github.com/onsi/ginkgo/v2/reporters/default_reporter.go b/vendor/github.com/onsi/ginkgo/v2/reporters/default_reporter.go
index 98097337..48073048 100644
--- a/vendor/github.com/onsi/ginkgo/v2/reporters/default_reporter.go
+++ b/vendor/github.com/onsi/ginkgo/v2/reporters/default_reporter.go
@@ -202,6 +202,11 @@ func (r *DefaultReporter) DidRun(report types.SpecReport) {
 	v := r.conf.Verbosity()
 	inParallel := report.RunningInParallel
 
+	//should we completely omit this spec?
+	if report.State.Is(types.SpecStateSkipped) && r.conf.SilenceSkips {
+		return
+	}
+
 	header := r.specDenoter
 	if report.LeafNodeType.Is(types.NodeTypesForSuiteLevelNodes) {
 		header = fmt.Sprintf("[%s]", report.LeafNodeType)
@@ -278,9 +283,12 @@ func (r *DefaultReporter) DidRun(report types.SpecReport) {
 		}
 	}
 
-	// If we have no content to show, jsut emit the header and return
+	// If we have no content to show, just emit the header and return
 	if !reportHasContent {
 		r.emit(r.f(highlightColor + header + "{{/}}"))
+		if r.conf.ForceNewlines {
+			r.emit("\n")
+		}
 		return
 	}
 
diff --git a/vendor/github.com/onsi/ginkgo/v2/reporters/junit_report.go b/vendor/github.com/onsi/ginkgo/v2/reporters/junit_report.go
index 2a3215b5..562e0f62 100644
--- a/vendor/github.com/onsi/ginkgo/v2/reporters/junit_report.go
+++ b/vendor/github.com/onsi/ginkgo/v2/reporters/junit_report.go
@@ -177,6 +177,7 @@ func GenerateJUnitReportWithConfig(report types.Report, dst string, config Junit
 				{"FocusFiles", strings.Join(report.SuiteConfig.FocusFiles, ";")},
 				{"SkipFiles", strings.Join(report.SuiteConfig.SkipFiles, ";")},
 				{"FailOnPending", fmt.Sprintf("%t", report.SuiteConfig.FailOnPending)},
+				{"FailOnEmpty", fmt.Sprintf("%t", report.SuiteConfig.FailOnEmpty)},
 				{"FailFast", fmt.Sprintf("%t", report.SuiteConfig.FailFast)},
 				{"FlakeAttempts", fmt.Sprintf("%d", report.SuiteConfig.FlakeAttempts)},
 				{"DryRun", fmt.Sprintf("%t", report.SuiteConfig.DryRun)},
diff --git a/vendor/github.com/onsi/ginkgo/v2/table_dsl.go b/vendor/github.com/onsi/ginkgo/v2/table_dsl.go
index a3aef821..c7de7a8b 100644
--- a/vendor/github.com/onsi/ginkgo/v2/table_dsl.go
+++ b/vendor/github.com/onsi/ginkgo/v2/table_dsl.go
@@ -269,11 +269,15 @@ func generateTable(description string, isSubtree bool, args ...interface{}) {
 			internalNodeArgs = append(internalNodeArgs, entry.decorations...)
 
 			hasContext := false
-			if internalBodyType.NumIn() > 0. {
+			if internalBodyType.NumIn() > 0 {
 				if internalBodyType.In(0).Implements(specContextType) {
 					hasContext = true
-				} else if internalBodyType.In(0).Implements(contextType) && (len(entry.parameters) == 0 || !reflect.TypeOf(entry.parameters[0]).Implements(contextType)) {
+				} else if internalBodyType.In(0).Implements(contextType) {
 					hasContext = true
+					if len(entry.parameters) > 0 && reflect.TypeOf(entry.parameters[0]) != nil && reflect.TypeOf(entry.parameters[0]).Implements(contextType) {
+						// we allow you to pass in a non-nil context
+						hasContext = false
+					}
 				}
 			}
 
diff --git a/vendor/github.com/onsi/ginkgo/v2/types/config.go b/vendor/github.com/onsi/ginkgo/v2/types/config.go
index cef273ee..66463cf5 100644
--- a/vendor/github.com/onsi/ginkgo/v2/types/config.go
+++ b/vendor/github.com/onsi/ginkgo/v2/types/config.go
@@ -25,6 +25,7 @@ type SuiteConfig struct {
 	SkipFiles             []string
 	LabelFilter           string
 	FailOnPending         bool
+	FailOnEmpty           bool
 	FailFast              bool
 	FlakeAttempts         int
 	MustPassRepeatedly    int
@@ -90,6 +91,8 @@ type ReporterConfig struct {
 	FullTrace      bool
 	ShowNodeEvents bool
 	GithubOutput   bool
+	SilenceSkips   bool
+	ForceNewlines  bool
 
 	JSONReport     string
 	JUnitReport    string
@@ -275,6 +278,8 @@ var SuiteConfigFlags = GinkgoFlags{
 		Usage: "If set, ginkgo will stop running a test suite after a failure occurs."},
 	{KeyPath: "S.FlakeAttempts", Name: "flake-attempts", SectionKey: "failure", UsageDefaultValue: "0 - failed tests are not retried", DeprecatedName: "flakeAttempts", DeprecatedDocLink: "changed-command-line-flags",
 		Usage: "Make up to this many attempts to run each spec. If any of the attempts succeed, the suite will not be failed."},
+	{KeyPath: "S.FailOnEmpty", Name: "fail-on-empty", SectionKey: "failure",
+		Usage: "If set, ginkgo will mark the test suite as failed if no specs are run."},
 
 	{KeyPath: "S.DryRun", Name: "dry-run", SectionKey: "debug", DeprecatedName: "dryRun", DeprecatedDocLink: "changed-command-line-flags",
 		Usage: "If set, ginkgo will walk the test hierarchy without actually running anything.  Best paired with -v."},
@@ -334,6 +339,10 @@ var ReporterConfigFlags = GinkgoFlags{
 		Usage: "If set, default reporter prints node > Enter and < Exit events when specs fail"},
 	{KeyPath: "R.GithubOutput", Name: "github-output", SectionKey: "output",
 		Usage: "If set, default reporter prints easier to manage output in Github Actions."},
+	{KeyPath: "R.SilenceSkips", Name: "silence-skips", SectionKey: "output",
+		Usage: "If set, default reporter will not print out skipped tests."},
+	{KeyPath: "R.ForceNewlines", Name: "force-newlines", SectionKey: "output",
+		Usage: "If set, default reporter will ensure a newline appears after each test."},
 
 	{KeyPath: "R.JSONReport", Name: "json-report", UsageArgument: "filename.json", SectionKey: "output",
 		Usage: "If set, Ginkgo will generate a JSON-formatted test report at the specified location."},
diff --git a/vendor/github.com/onsi/ginkgo/v2/types/label_filter.go b/vendor/github.com/onsi/ginkgo/v2/types/label_filter.go
index b0d3b651..7fdc8aa2 100644
--- a/vendor/github.com/onsi/ginkgo/v2/types/label_filter.go
+++ b/vendor/github.com/onsi/ginkgo/v2/types/label_filter.go
@@ -45,6 +45,83 @@ func orAction(a, b LabelFilter) LabelFilter {
 	return func(labels []string) bool { return a(labels) || b(labels) }
 }
 
+func labelSetFor(key string, labels []string) map[string]bool {
+	key = strings.ToLower(strings.TrimSpace(key))
+	out := map[string]bool{}
+	for _, label := range labels {
+		components := strings.SplitN(label, ":", 2)
+		if len(components) < 2 {
+			continue
+		}
+		if key == strings.ToLower(strings.TrimSpace(components[0])) {
+			out[strings.ToLower(strings.TrimSpace(components[1]))] = true
+		}
+	}
+
+	return out
+}
+
+func isEmptyLabelSetAction(key string) LabelFilter {
+	return func(labels []string) bool {
+		return len(labelSetFor(key, labels)) == 0
+	}
+}
+
+func containsAnyLabelSetAction(key string, expectedValues []string) LabelFilter {
+	return func(labels []string) bool {
+		set := labelSetFor(key, labels)
+		for _, value := range expectedValues {
+			if set[value] {
+				return true
+			}
+		}
+		return false
+	}
+}
+
+func containsAllLabelSetAction(key string, expectedValues []string) LabelFilter {
+	return func(labels []string) bool {
+		set := labelSetFor(key, labels)
+		for _, value := range expectedValues {
+			if !set[value] {
+				return false
+			}
+		}
+		return true
+	}
+}
+
+func consistsOfLabelSetAction(key string, expectedValues []string) LabelFilter {
+	return func(labels []string) bool {
+		set := labelSetFor(key, labels)
+		if len(set) != len(expectedValues) {
+			return false
+		}
+		for _, value := range expectedValues {
+			if !set[value] {
+				return false
+			}
+		}
+		return true
+	}
+}
+
+func isSubsetOfLabelSetAction(key string, expectedValues []string) LabelFilter {
+	expectedSet := map[string]bool{}
+	for _, value := range expectedValues {
+		expectedSet[value] = true
+	}
+	return func(labels []string) bool {
+		set := labelSetFor(key, labels)
+		for value := range set {
+			if !expectedSet[value] {
+				return false
+			}
+		}
+		return true
+	}
+}
+
 type lfToken uint
 
 const (
@@ -58,6 +135,9 @@ const (
 	lfTokenOr
 	lfTokenRegexp
 	lfTokenLabel
+	lfTokenSetKey
+	lfTokenSetOperation
+	lfTokenSetArgument
 	lfTokenEOF
 )
 
@@ -71,6 +151,8 @@ func (l lfToken) Precedence() int {
 		return 2
 	case lfTokenNot:
 		return 3
+	case lfTokenSetOperation:
+		return 4
 	}
 	return -1
 }
@@ -93,6 +175,12 @@ func (l lfToken) String() string {
 		return "/regexp/"
 	case lfTokenLabel:
 		return "label"
+	case lfTokenSetKey:
+		return "set_key"
+	case lfTokenSetOperation:
+		return "set_operation"
+	case lfTokenSetArgument:
+		return "set_argument"
 	case lfTokenEOF:
 		return "EOF"
 	}
@@ -148,6 +236,35 @@ func (tn *treeNode) constructLabelFilter(input string) (LabelFilter, error) {
 			return nil, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, tn.location, fmt.Sprintf("RegExp compilation error: %s", err))
 		}
 		return matchLabelRegexAction(re), nil
+	case lfTokenSetOperation:
+		tokenSetOperation := strings.ToLower(tn.value)
+		if tokenSetOperation == "isempty" {
+			return isEmptyLabelSetAction(tn.leftNode.value), nil
+		}
+		if tn.rightNode == nil {
+			return nil, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, tn.location, fmt.Sprintf("Set operation '%s' is missing an argument.", tn.value))
+		}
+
+		rawValues := strings.Split(tn.rightNode.value, ",")
+		values := make([]string, len(rawValues))
+		for i := range rawValues {
+			values[i] = strings.ToLower(strings.TrimSpace(rawValues[i]))
+			if strings.ContainsAny(values[i], "&|!,()/") {
+				return nil, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, tn.rightNode.location, fmt.Sprintf("Invalid label value '%s' in set operation argument.", values[i]))
+			} else if values[i] == "" {
+				return nil, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, tn.rightNode.location, "Empty label value in set operation argument.")
+			}
+		}
+		switch tokenSetOperation {
+		case "containsany":
+			return containsAnyLabelSetAction(tn.leftNode.value, values), nil
+		case "containsall":
+			return containsAllLabelSetAction(tn.leftNode.value, values), nil
+		case "consistsof":
+			return consistsOfLabelSetAction(tn.leftNode.value, values), nil
+		case "issubsetof":
+			return isSubsetOfLabelSetAction(tn.leftNode.value, values), nil
+		}
 	}
 
 	if tn.rightNode == nil {
@@ -203,7 +320,17 @@ func (tn *treeNode) toString(indent int) string {
 	return out
 }
 
+var validSetOperations = map[string]string{
+	"containsany": "containsAny",
+	"containsall": "containsAll",
+	"consistsof":  "consistsOf",
+	"issubsetof":  "isSubsetOf",
+	"isempty":     "isEmpty",
+}
+
 func tokenize(input string) func() (*treeNode, error) {
+	lastToken := lfTokenInvalid
+	lastValue := ""
 	runes, i := []rune(input), 0
 
 	peekIs := func(r rune) bool {
@@ -233,6 +360,53 @@ func tokenize(input string) func() (*treeNode, error) {
 		}
 
 		node := &treeNode{location: i}
+		defer func() {
+			lastToken = node.token
+			lastValue = node.value
+		}()
+
+		if lastToken == lfTokenSetKey {
+			//we should get a valid set operation next
+			value, n := consumeUntil(" )")
+			if validSetOperations[strings.ToLower(value)] == "" {
+				return &treeNode{}, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, i, fmt.Sprintf("Invalid set operation '%s'.", value))
+			}
+			i += n
+			node.token, node.value = lfTokenSetOperation, value
+			return node, nil
+		}
+		if lastToken == lfTokenSetOperation {
+			//we should get an argument next, if we aren't isempty
+			var arg = ""
+			origI := i
+			if runes[i] == '{' {
+				i += 1
+				value, n := consumeUntil("}")
+				if i+n >= len(runes) {
+					return &treeNode{}, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, i-1, "Missing closing '}' in set operation argument?")
+				}
+				i += n + 1
+				arg = value
+			} else {
+				value, n := consumeUntil("&|!,()/")
+				i += n
+				arg = strings.TrimSpace(value)
+			}
+			if strings.ToLower(lastValue) == "isempty" && arg != "" {
+				return &treeNode{}, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, origI, fmt.Sprintf("isEmpty does not take arguments, was passed '%s'.", arg))
+			}
+			if arg == "" && strings.ToLower(lastValue) != "isempty" {
+				if i < len(runes) && runes[i] == '/' {
+					return &treeNode{}, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, origI, "Set operations do not support regular expressions.")
+				} else {
+					return &treeNode{}, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, origI, fmt.Sprintf("Set operation '%s' requires an argument.", lastValue))
+				}
+			}
+			// note that we sent an empty SetArgument token if we are isempty
+			node.token, node.value = lfTokenSetArgument, arg
+			return node, nil
+		}
+
 		switch runes[i] {
 		case '&':
 			if !peekIs('&') {
@@ -264,8 +438,38 @@ func tokenize(input string) func() (*treeNode, error) {
 			i += n + 1
 			node.token, node.value = lfTokenRegexp, value
 		default:
-			value, n := consumeUntil("&|!,()/")
+			value, n := consumeUntil("&|!,()/:")
 			i += n
+			value = strings.TrimSpace(value)
+
+			//are we the beginning of a set operation?
+			if i < len(runes) && runes[i] == ':' {
+				if peekIs(' ') {
+					if value == "" {
+						return &treeNode{}, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, i, "Missing set key.")
+					}
+					i += 1
+					//we are the beginning of a set operation
+					node.token, node.value = lfTokenSetKey, value
+					return node, nil
+				}
+				additionalValue, n := consumeUntil("&|!,()/")
+				additionalValue = strings.TrimSpace(additionalValue)
+				if additionalValue == ":" {
+					return &treeNode{}, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, i, "Missing set operation.")
+				}
+				i += n
+				value += additionalValue
+			}
+
+			valueToCheckForSetOperation := strings.ToLower(value)
+			for setOperation := range validSetOperations {
+				idx := strings.Index(valueToCheckForSetOperation, " "+setOperation)
+				if idx > 0 {
+					return &treeNode{}, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, i-n+idx+1, fmt.Sprintf("Looks like you are using the set operator '%s' but did not provide a set key.  Did you forget the ':'?", validSetOperations[setOperation]))
+				}
+			}
+
 			node.token, node.value = lfTokenLabel, strings.TrimSpace(value)
 		}
 		return node, nil
@@ -307,7 +511,7 @@ LOOP:
 		switch node.token {
 		case lfTokenEOF:
 			break LOOP
-		case lfTokenLabel, lfTokenRegexp:
+		case lfTokenLabel, lfTokenRegexp, lfTokenSetKey:
 			if current.rightNode != nil {
 				return nil, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, node.location, "Found two adjacent labels.  You need an operator between them.")
 			}
@@ -326,6 +530,18 @@ LOOP:
 			node.setLeftNode(nodeToStealFrom.rightNode)
 			nodeToStealFrom.setRightNode(node)
 			current = node
+		case lfTokenSetOperation:
+			if current.rightNode == nil {
+				return nil, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, node.location, fmt.Sprintf("Set operation '%s' missing left hand operand.", node.value))
+			}
+			node.setLeftNode(current.rightNode)
+			current.setRightNode(node)
+			current = node
+		case lfTokenSetArgument:
+			if current.rightNode != nil {
+				return nil, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, node.location, fmt.Sprintf("Unexpected set argument '%s'.", node.token))
+			}
+			current.setRightNode(node)
 		case lfTokenCloseGroup:
 			firstUnmatchedOpenNode := current.firstUnmatchedOpenNode()
 			if firstUnmatchedOpenNode == nil {
@@ -354,5 +570,14 @@ func ValidateAndCleanupLabel(label string, cl CodeLocation) (string, error) {
 	if strings.ContainsAny(out, "&|!,()/") {
 		return "", GinkgoErrors.InvalidLabel(label, cl)
 	}
+	if out[0] == ':' {
+		return "", GinkgoErrors.InvalidLabel(label, cl)
+	}
+	if strings.Contains(out, ":") {
+		components := strings.SplitN(out, ":", 2)
+		if len(components) < 2 || components[1] == "" {
+			return "", GinkgoErrors.InvalidLabel(label, cl)
+		}
+	}
 	return out, nil
 }
diff --git a/vendor/github.com/onsi/ginkgo/v2/types/version.go b/vendor/github.com/onsi/ginkgo/v2/types/version.go
index 5dd0140c..acab0349 100644
--- a/vendor/github.com/onsi/ginkgo/v2/types/version.go
+++ b/vendor/github.com/onsi/ginkgo/v2/types/version.go
@@ -1,3 +1,3 @@
 package types
 
-const VERSION = "2.17.2"
+const VERSION = "2.19.0"
diff --git a/vendor/github.com/stoewer/go-strcase/README.md b/vendor/github.com/stoewer/go-strcase/README.md
index 0e8635d8..84a640e7 100644
--- a/vendor/github.com/stoewer/go-strcase/README.md
+++ b/vendor/github.com/stoewer/go-strcase/README.md
@@ -1,5 +1,5 @@
-[![CircleCI](https://circleci.com/gh/stoewer/go-strcase/tree/master.svg?style=svg)](https://circleci.com/gh/stoewer/go-strcase/tree/master)
-[![codecov](https://codecov.io/gh/stoewer/go-strcase/branch/master/graph/badge.svg)](https://codecov.io/gh/stoewer/go-strcase)
+[![GH Actions](https://github.com/stoewer/go-strcase/actions/workflows/lint-test.yml/badge.svg?branch=master)](https://github.com/stoewer/go-strcase/actions)
+[![codecov](https://codecov.io/github/stoewer/go-strcase/branch/master/graph/badge.svg?token=c0UokYnop5)](https://codecov.io/github/stoewer/go-strcase)
 [![GoDoc](https://godoc.org/github.com/stoewer/go-strcase?status.svg)](https://pkg.go.dev/github.com/stoewer/go-strcase)
 ---
 
diff --git a/vendor/github.com/stoewer/go-strcase/camel.go b/vendor/github.com/stoewer/go-strcase/camel.go
index 5c233cc8..ff9e66e0 100644
--- a/vendor/github.com/stoewer/go-strcase/camel.go
+++ b/vendor/github.com/stoewer/go-strcase/camel.go
@@ -27,6 +27,9 @@ func camelCase(s string, upper bool) string {
 				buffer = append(buffer, toUpper(curr))
 			} else if isLower(prev) {
 				buffer = append(buffer, curr)
+			} else if isUpper(prev) && isUpper(curr) && isLower(next) {
+				// Assume a case like "R" for "XRequestId"
+				buffer = append(buffer, curr)
 			} else {
 				buffer = append(buffer, toLower(curr))
 			}
diff --git a/vendor/golang.org/x/crypto/sha3/hashes.go b/vendor/golang.org/x/crypto/sha3/hashes.go
index 0d8043fd..5eae6cb9 100644
--- a/vendor/golang.org/x/crypto/sha3/hashes.go
+++ b/vendor/golang.org/x/crypto/sha3/hashes.go
@@ -16,39 +16,43 @@ import (
 // Its generic security strength is 224 bits against preimage attacks,
 // and 112 bits against collision attacks.
 func New224() hash.Hash {
-	if h := new224Asm(); h != nil {
-		return h
-	}
-	return &state{rate: 144, outputLen: 28, dsbyte: 0x06}
+	return new224()
 }
 
 // New256 creates a new SHA3-256 hash.
 // Its generic security strength is 256 bits against preimage attacks,
 // and 128 bits against collision attacks.
 func New256() hash.Hash {
-	if h := new256Asm(); h != nil {
-		return h
-	}
-	return &state{rate: 136, outputLen: 32, dsbyte: 0x06}
+	return new256()
 }
 
 // New384 creates a new SHA3-384 hash.
 // Its generic security strength is 384 bits against preimage attacks,
 // and 192 bits against collision attacks.
 func New384() hash.Hash {
-	if h := new384Asm(); h != nil {
-		return h
-	}
-	return &state{rate: 104, outputLen: 48, dsbyte: 0x06}
+	return new384()
 }
 
 // New512 creates a new SHA3-512 hash.
 // Its generic security strength is 512 bits against preimage attacks,
 // and 256 bits against collision attacks.
 func New512() hash.Hash {
-	if h := new512Asm(); h != nil {
-		return h
-	}
+	return new512()
+}
+
+func new224Generic() *state {
+	return &state{rate: 144, outputLen: 28, dsbyte: 0x06}
+}
+
+func new256Generic() *state {
+	return &state{rate: 136, outputLen: 32, dsbyte: 0x06}
+}
+
+func new384Generic() *state {
+	return &state{rate: 104, outputLen: 48, dsbyte: 0x06}
+}
+
+func new512Generic() *state {
 	return &state{rate: 72, outputLen: 64, dsbyte: 0x06}
 }
 
diff --git a/vendor/golang.org/x/crypto/sha3/hashes_generic.go b/vendor/golang.org/x/crypto/sha3/hashes_generic.go
deleted file mode 100644
index fe8c8479..00000000
--- a/vendor/golang.org/x/crypto/sha3/hashes_generic.go
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2017 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-//go:build !gc || purego || !s390x
-
-package sha3
-
-import (
-	"hash"
-)
-
-// new224Asm returns an assembly implementation of SHA3-224 if available,
-// otherwise it returns nil.
-func new224Asm() hash.Hash { return nil }
-
-// new256Asm returns an assembly implementation of SHA3-256 if available,
-// otherwise it returns nil.
-func new256Asm() hash.Hash { return nil }
-
-// new384Asm returns an assembly implementation of SHA3-384 if available,
-// otherwise it returns nil.
-func new384Asm() hash.Hash { return nil }
-
-// new512Asm returns an assembly implementation of SHA3-512 if available,
-// otherwise it returns nil.
-func new512Asm() hash.Hash { return nil }
diff --git a/vendor/golang.org/x/crypto/sha3/hashes_noasm.go b/vendor/golang.org/x/crypto/sha3/hashes_noasm.go
new file mode 100644
index 00000000..9d85fb62
--- /dev/null
+++ b/vendor/golang.org/x/crypto/sha3/hashes_noasm.go
@@ -0,0 +1,23 @@
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build !gc || purego || !s390x
+
+package sha3
+
+func new224() *state {
+	return new224Generic()
+}
+
+func new256() *state {
+	return new256Generic()
+}
+
+func new384() *state {
+	return new384Generic()
+}
+
+func new512() *state {
+	return new512Generic()
+}
diff --git a/vendor/golang.org/x/crypto/sha3/sha3.go b/vendor/golang.org/x/crypto/sha3/sha3.go
index 4884d172..afedde5a 100644
--- a/vendor/golang.org/x/crypto/sha3/sha3.go
+++ b/vendor/golang.org/x/crypto/sha3/sha3.go
@@ -23,7 +23,6 @@ const (
 type state struct {
 	// Generic sponge components.
 	a    [25]uint64 // main state of the hash
-	buf  []byte     // points into storage
 	rate int        // the number of bytes of state to use
 
 	// dsbyte contains the "domain separation" bits and the first bit of
@@ -40,7 +39,8 @@ type state struct {
 	//      Extendable-Output Functions (May 2014)"
 	dsbyte byte
 
-	storage storageBuf
+	i, n    int // storage[i:n] is the buffer, i is only used while squeezing
+	storage [maxRate]byte
 
 	// Specific to SHA-3 and SHAKE.
 	outputLen int             // the default output size in bytes
@@ -54,24 +54,18 @@ func (d *state) BlockSize() int { return d.rate }
 func (d *state) Size() int { return d.outputLen }
 
 // Reset clears the internal state by zeroing the sponge state and
-// the byte buffer, and setting Sponge.state to absorbing.
+// the buffer indexes, and setting Sponge.state to absorbing.
 func (d *state) Reset() {
 	// Zero the permutation's state.
 	for i := range d.a {
 		d.a[i] = 0
 	}
 	d.state = spongeAbsorbing
-	d.buf = d.storage.asBytes()[:0]
+	d.i, d.n = 0, 0
 }
 
 func (d *state) clone() *state {
 	ret := *d
-	if ret.state == spongeAbsorbing {
-		ret.buf = ret.storage.asBytes()[:len(ret.buf)]
-	} else {
-		ret.buf = ret.storage.asBytes()[d.rate-cap(d.buf) : d.rate]
-	}
-
 	return &ret
 }
 
@@ -82,43 +76,40 @@ func (d *state) permute() {
 	case spongeAbsorbing:
 		// If we're absorbing, we need to xor the input into the state
 		// before applying the permutation.
-		xorIn(d, d.buf)
-		d.buf = d.storage.asBytes()[:0]
+		xorIn(d, d.storage[:d.rate])
+		d.n = 0
 		keccakF1600(&d.a)
 	case spongeSqueezing:
 		// If we're squeezing, we need to apply the permutation before
 		// copying more output.
 		keccakF1600(&d.a)
-		d.buf = d.storage.asBytes()[:d.rate]
-		copyOut(d, d.buf)
+		d.i = 0
+		copyOut(d, d.storage[:d.rate])
 	}
 }
 
 // pads appends the domain separation bits in dsbyte, applies
 // the multi-bitrate 10..1 padding rule, and permutes the state.
-func (d *state) padAndPermute(dsbyte byte) {
-	if d.buf == nil {
-		d.buf = d.storage.asBytes()[:0]
-	}
+func (d *state) padAndPermute() {
 	// Pad with this instance's domain-separator bits. We know that there's
 	// at least one byte of space in d.buf because, if it were full,
 	// permute would have been called to empty it. dsbyte also contains the
 	// first one bit for the padding. See the comment in the state struct.
-	d.buf = append(d.buf, dsbyte)
-	zerosStart := len(d.buf)
-	d.buf = d.storage.asBytes()[:d.rate]
-	for i := zerosStart; i < d.rate; i++ {
-		d.buf[i] = 0
+	d.storage[d.n] = d.dsbyte
+	d.n++
+	for d.n < d.rate {
+		d.storage[d.n] = 0
+		d.n++
 	}
 	// This adds the final one bit for the padding. Because of the way that
 	// bits are numbered from the LSB upwards, the final bit is the MSB of
 	// the last byte.
-	d.buf[d.rate-1] ^= 0x80
+	d.storage[d.rate-1] ^= 0x80
 	// Apply the permutation
 	d.permute()
 	d.state = spongeSqueezing
-	d.buf = d.storage.asBytes()[:d.rate]
-	copyOut(d, d.buf)
+	d.n = d.rate
+	copyOut(d, d.storage[:d.rate])
 }
 
 // Write absorbs more data into the hash's state. It panics if any
@@ -127,28 +118,25 @@ func (d *state) Write(p []byte) (written int, err error) {
 	if d.state != spongeAbsorbing {
 		panic("sha3: Write after Read")
 	}
-	if d.buf == nil {
-		d.buf = d.storage.asBytes()[:0]
-	}
 	written = len(p)
 
 	for len(p) > 0 {
-		if len(d.buf) == 0 && len(p) >= d.rate {
+		if d.n == 0 && len(p) >= d.rate {
 			// The fast path; absorb a full "rate" bytes of input and apply the permutation.
 			xorIn(d, p[:d.rate])
 			p = p[d.rate:]
 			keccakF1600(&d.a)
 		} else {
 			// The slow path; buffer the input until we can fill the sponge, and then xor it in.
-			todo := d.rate - len(d.buf)
+			todo := d.rate - d.n
 			if todo > len(p) {
 				todo = len(p)
 			}
-			d.buf = append(d.buf, p[:todo]...)
+			d.n += copy(d.storage[d.n:], p[:todo])
 			p = p[todo:]
 
 			// If the sponge is full, apply the permutation.
-			if len(d.buf) == d.rate {
+			if d.n == d.rate {
 				d.permute()
 			}
 		}
@@ -161,19 +149,19 @@ func (d *state) Write(p []byte) (written int, err error) {
 func (d *state) Read(out []byte) (n int, err error) {
 	// If we're still absorbing, pad and apply the permutation.
 	if d.state == spongeAbsorbing {
-		d.padAndPermute(d.dsbyte)
+		d.padAndPermute()
 	}
 
 	n = len(out)
 
 	// Now, do the squeezing.
 	for len(out) > 0 {
-		n := copy(out, d.buf)
-		d.buf = d.buf[n:]
+		n := copy(out, d.storage[d.i:d.n])
+		d.i += n
 		out = out[n:]
 
 		// Apply the permutation if we've squeezed the sponge dry.
-		if len(d.buf) == 0 {
+		if d.i == d.rate {
 			d.permute()
 		}
 	}
diff --git a/vendor/golang.org/x/crypto/sha3/sha3_s390x.go b/vendor/golang.org/x/crypto/sha3/sha3_s390x.go
index b4fbbf86..00d8034a 100644
--- a/vendor/golang.org/x/crypto/sha3/sha3_s390x.go
+++ b/vendor/golang.org/x/crypto/sha3/sha3_s390x.go
@@ -248,56 +248,56 @@ func (s *asmState) Clone() ShakeHash {
 	return s.clone()
 }
 
-// new224Asm returns an assembly implementation of SHA3-224 if available,
-// otherwise it returns nil.
-func new224Asm() hash.Hash {
+// new224 returns an assembly implementation of SHA3-224 if available,
+// otherwise it returns a generic implementation.
+func new224() hash.Hash {
 	if cpu.S390X.HasSHA3 {
 		return newAsmState(sha3_224)
 	}
-	return nil
+	return new224Generic()
 }
 
-// new256Asm returns an assembly implementation of SHA3-256 if available,
-// otherwise it returns nil.
-func new256Asm() hash.Hash {
+// new256 returns an assembly implementation of SHA3-256 if available,
+// otherwise it returns a generic implementation.
+func new256() hash.Hash {
 	if cpu.S390X.HasSHA3 {
 		return newAsmState(sha3_256)
 	}
-	return nil
+	return new256Generic()
 }
 
-// new384Asm returns an assembly implementation of SHA3-384 if available,
-// otherwise it returns nil.
-func new384Asm() hash.Hash {
+// new384 returns an assembly implementation of SHA3-384 if available,
+// otherwise it returns a generic implementation.
+func new384() hash.Hash {
 	if cpu.S390X.HasSHA3 {
 		return newAsmState(sha3_384)
 	}
-	return nil
+	return new384Generic()
 }
 
-// new512Asm returns an assembly implementation of SHA3-512 if available,
-// otherwise it returns nil.
-func new512Asm() hash.Hash {
+// new512 returns an assembly implementation of SHA3-512 if available,
+// otherwise it returns a generic implementation.
+func new512() hash.Hash {
 	if cpu.S390X.HasSHA3 {
 		return newAsmState(sha3_512)
 	}
-	return nil
+	return new512Generic()
 }
 
-// newShake128Asm returns an assembly implementation of SHAKE-128 if available,
-// otherwise it returns nil.
-func newShake128Asm() ShakeHash {
+// newShake128 returns an assembly implementation of SHAKE-128 if available,
+// otherwise it returns a generic implementation.
+func newShake128() ShakeHash {
 	if cpu.S390X.HasSHA3 {
 		return newAsmState(shake_128)
 	}
-	return nil
+	return newShake128Generic()
 }
 
-// newShake256Asm returns an assembly implementation of SHAKE-256 if available,
-// otherwise it returns nil.
-func newShake256Asm() ShakeHash {
+// newShake256 returns an assembly implementation of SHAKE-256 if available,
+// otherwise it returns a generic implementation.
+func newShake256() ShakeHash {
 	if cpu.S390X.HasSHA3 {
 		return newAsmState(shake_256)
 	}
-	return nil
+	return newShake256Generic()
 }
diff --git a/vendor/golang.org/x/crypto/sha3/shake.go b/vendor/golang.org/x/crypto/sha3/shake.go
index bb699840..1ea9275b 100644
--- a/vendor/golang.org/x/crypto/sha3/shake.go
+++ b/vendor/golang.org/x/crypto/sha3/shake.go
@@ -115,19 +115,21 @@ func (c *state) Clone() ShakeHash {
 // Its generic security strength is 128 bits against all attacks if at
 // least 32 bytes of its output are used.
 func NewShake128() ShakeHash {
-	if h := newShake128Asm(); h != nil {
-		return h
-	}
-	return &state{rate: rate128, outputLen: 32, dsbyte: dsbyteShake}
+	return newShake128()
 }
 
 // NewShake256 creates a new SHAKE256 variable-output-length ShakeHash.
 // Its generic security strength is 256 bits against all attacks if
 // at least 64 bytes of its output are used.
 func NewShake256() ShakeHash {
-	if h := newShake256Asm(); h != nil {
-		return h
-	}
+	return newShake256()
+}
+
+func newShake128Generic() *state {
+	return &state{rate: rate128, outputLen: 32, dsbyte: dsbyteShake}
+}
+
+func newShake256Generic() *state {
 	return &state{rate: rate256, outputLen: 64, dsbyte: dsbyteShake}
 }
 
diff --git a/vendor/golang.org/x/crypto/sha3/shake_generic.go b/vendor/golang.org/x/crypto/sha3/shake_generic.go
deleted file mode 100644
index 8d31cf5b..00000000
--- a/vendor/golang.org/x/crypto/sha3/shake_generic.go
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2017 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-//go:build !gc || purego || !s390x
-
-package sha3
-
-// newShake128Asm returns an assembly implementation of SHAKE-128 if available,
-// otherwise it returns nil.
-func newShake128Asm() ShakeHash {
-	return nil
-}
-
-// newShake256Asm returns an assembly implementation of SHAKE-256 if available,
-// otherwise it returns nil.
-func newShake256Asm() ShakeHash {
-	return nil
-}
diff --git a/vendor/golang.org/x/crypto/sha3/shake_noasm.go b/vendor/golang.org/x/crypto/sha3/shake_noasm.go
new file mode 100644
index 00000000..4276ba4a
--- /dev/null
+++ b/vendor/golang.org/x/crypto/sha3/shake_noasm.go
@@ -0,0 +1,15 @@
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build !gc || purego || !s390x
+
+package sha3
+
+func newShake128() *state {
+	return newShake128Generic()
+}
+
+func newShake256() *state {
+	return newShake256Generic()
+}
diff --git a/vendor/golang.org/x/crypto/sha3/xor.go b/vendor/golang.org/x/crypto/sha3/xor.go
index 7337cca8..6ada5c95 100644
--- a/vendor/golang.org/x/crypto/sha3/xor.go
+++ b/vendor/golang.org/x/crypto/sha3/xor.go
@@ -2,22 +2,39 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-//go:build (!amd64 && !386 && !ppc64le) || purego
-
 package sha3
 
-// A storageBuf is an aligned array of maxRate bytes.
-type storageBuf [maxRate]byte
-
-func (b *storageBuf) asBytes() *[maxRate]byte {
-	return (*[maxRate]byte)(b)
-}
+import (
+	"crypto/subtle"
+	"encoding/binary"
+	"unsafe"
 
-var (
-	xorIn            = xorInGeneric
-	copyOut          = copyOutGeneric
-	xorInUnaligned   = xorInGeneric
-	copyOutUnaligned = copyOutGeneric
+	"golang.org/x/sys/cpu"
 )
 
-const xorImplementationUnaligned = "generic"
+// xorIn xors the bytes in buf into the state.
+func xorIn(d *state, buf []byte) {
+	if cpu.IsBigEndian {
+		for i := 0; len(buf) >= 8; i++ {
+			a := binary.LittleEndian.Uint64(buf)
+			d.a[i] ^= a
+			buf = buf[8:]
+		}
+	} else {
+		ab := (*[25 * 64 / 8]byte)(unsafe.Pointer(&d.a))
+		subtle.XORBytes(ab[:], ab[:], buf)
+	}
+}
+
+// copyOut copies uint64s to a byte buffer.
+func copyOut(d *state, b []byte) {
+	if cpu.IsBigEndian {
+		for i := 0; len(b) >= 8; i++ {
+			binary.LittleEndian.PutUint64(b, d.a[i])
+			b = b[8:]
+		}
+	} else {
+		ab := (*[25 * 64 / 8]byte)(unsafe.Pointer(&d.a))
+		copy(b, ab[:])
+	}
+}
diff --git a/vendor/golang.org/x/crypto/sha3/xor_generic.go b/vendor/golang.org/x/crypto/sha3/xor_generic.go
deleted file mode 100644
index 8d947711..00000000
--- a/vendor/golang.org/x/crypto/sha3/xor_generic.go
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package sha3
-
-import "encoding/binary"
-
-// xorInGeneric xors the bytes in buf into the state; it
-// makes no non-portable assumptions about memory layout
-// or alignment.
-func xorInGeneric(d *state, buf []byte) {
-	n := len(buf) / 8
-
-	for i := 0; i < n; i++ {
-		a := binary.LittleEndian.Uint64(buf)
-		d.a[i] ^= a
-		buf = buf[8:]
-	}
-}
-
-// copyOutGeneric copies uint64s to a byte buffer.
-func copyOutGeneric(d *state, b []byte) {
-	for i := 0; len(b) >= 8; i++ {
-		binary.LittleEndian.PutUint64(b, d.a[i])
-		b = b[8:]
-	}
-}
diff --git a/vendor/golang.org/x/crypto/sha3/xor_unaligned.go b/vendor/golang.org/x/crypto/sha3/xor_unaligned.go
deleted file mode 100644
index 870e2d16..00000000
--- a/vendor/golang.org/x/crypto/sha3/xor_unaligned.go
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-//go:build (amd64 || 386 || ppc64le) && !purego
-
-package sha3
-
-import "unsafe"
-
-// A storageBuf is an aligned array of maxRate bytes.
-type storageBuf [maxRate / 8]uint64
-
-func (b *storageBuf) asBytes() *[maxRate]byte {
-	return (*[maxRate]byte)(unsafe.Pointer(b))
-}
-
-// xorInUnaligned uses unaligned reads and writes to update d.a to contain d.a
-// XOR buf.
-func xorInUnaligned(d *state, buf []byte) {
-	n := len(buf)
-	bw := (*[maxRate / 8]uint64)(unsafe.Pointer(&buf[0]))[: n/8 : n/8]
-	if n >= 72 {
-		d.a[0] ^= bw[0]
-		d.a[1] ^= bw[1]
-		d.a[2] ^= bw[2]
-		d.a[3] ^= bw[3]
-		d.a[4] ^= bw[4]
-		d.a[5] ^= bw[5]
-		d.a[6] ^= bw[6]
-		d.a[7] ^= bw[7]
-		d.a[8] ^= bw[8]
-	}
-	if n >= 104 {
-		d.a[9] ^= bw[9]
-		d.a[10] ^= bw[10]
-		d.a[11] ^= bw[11]
-		d.a[12] ^= bw[12]
-	}
-	if n >= 136 {
-		d.a[13] ^= bw[13]
-		d.a[14] ^= bw[14]
-		d.a[15] ^= bw[15]
-		d.a[16] ^= bw[16]
-	}
-	if n >= 144 {
-		d.a[17] ^= bw[17]
-	}
-	if n >= 168 {
-		d.a[18] ^= bw[18]
-		d.a[19] ^= bw[19]
-		d.a[20] ^= bw[20]
-	}
-}
-
-func copyOutUnaligned(d *state, buf []byte) {
-	ab := (*[maxRate]uint8)(unsafe.Pointer(&d.a[0]))
-	copy(buf, ab[:])
-}
-
-var (
-	xorIn   = xorInUnaligned
-	copyOut = copyOutUnaligned
-)
-
-const xorImplementationUnaligned = "unaligned"
diff --git a/vendor/golang.org/x/crypto/ssh/keys.go b/vendor/golang.org/x/crypto/ssh/keys.go
index df4ebdad..7967665f 100644
--- a/vendor/golang.org/x/crypto/ssh/keys.go
+++ b/vendor/golang.org/x/crypto/ssh/keys.go
@@ -904,6 +904,10 @@ func (k *skECDSAPublicKey) Verify(data []byte, sig *Signature) error {
 	return errors.New("ssh: signature did not verify")
 }
 
+func (k *skECDSAPublicKey) CryptoPublicKey() crypto.PublicKey {
+	return &k.PublicKey
+}
+
 type skEd25519PublicKey struct {
 	// application is a URL-like string, typically "ssh:" for SSH.
 	// see openssh/PROTOCOL.u2f for details.
@@ -1000,6 +1004,10 @@ func (k *skEd25519PublicKey) Verify(data []byte, sig *Signature) error {
 	return nil
 }
 
+func (k *skEd25519PublicKey) CryptoPublicKey() crypto.PublicKey {
+	return k.PublicKey
+}
+
 // NewSignerFromKey takes an *rsa.PrivateKey, *dsa.PrivateKey,
 // *ecdsa.PrivateKey or any other crypto.Signer and returns a
 // corresponding Signer instance. ECDSA keys must use P-256, P-384 or
diff --git a/vendor/golang.org/x/crypto/ssh/server.go b/vendor/golang.org/x/crypto/ssh/server.go
index e2ae4f89..3ca9e89e 100644
--- a/vendor/golang.org/x/crypto/ssh/server.go
+++ b/vendor/golang.org/x/crypto/ssh/server.go
@@ -462,6 +462,24 @@ func (p *PartialSuccessError) Error() string {
 // It is returned in ServerAuthError.Errors from NewServerConn.
 var ErrNoAuth = errors.New("ssh: no auth passed yet")
 
+// BannerError is an error that can be returned by authentication handlers in
+// ServerConfig to send a banner message to the client.
+type BannerError struct {
+	Err     error
+	Message string
+}
+
+func (b *BannerError) Unwrap() error {
+	return b.Err
+}
+
+func (b *BannerError) Error() string {
+	if b.Err == nil {
+		return b.Message
+	}
+	return b.Err.Error()
+}
+
 func (s *connection) serverAuthenticate(config *ServerConfig) (*Permissions, error) {
 	sessionID := s.transport.getSessionID()
 	var cache pubKeyCache
@@ -734,6 +752,18 @@ userAuthLoop:
 			config.AuthLogCallback(s, userAuthReq.Method, authErr)
 		}
 
+		var bannerErr *BannerError
+		if errors.As(authErr, &bannerErr) {
+			if bannerErr.Message != "" {
+				bannerMsg := &userAuthBannerMsg{
+					Message: bannerErr.Message,
+				}
+				if err := s.transport.writePacket(Marshal(bannerMsg)); err != nil {
+					return nil, err
+				}
+			}
+		}
+
 		if authErr == nil {
 			break userAuthLoop
 		}
diff --git a/vendor/golang.org/x/mod/modfile/rule.go b/vendor/golang.org/x/mod/modfile/rule.go
index 0e7b7e26..66dcaf98 100644
--- a/vendor/golang.org/x/mod/modfile/rule.go
+++ b/vendor/golang.org/x/mod/modfile/rule.go
@@ -38,6 +38,7 @@ type File struct {
 	Module    *Module
 	Go        *Go
 	Toolchain *Toolchain
+	Godebug   []*Godebug
 	Require   []*Require
 	Exclude   []*Exclude
 	Replace   []*Replace
@@ -65,6 +66,13 @@ type Toolchain struct {
 	Syntax *Line
 }
 
+// A Godebug is a single godebug key=value statement.
+type Godebug struct {
+	Key    string
+	Value  string
+	Syntax *Line
+}
+
 // An Exclude is a single exclude statement.
 type Exclude struct {
 	Mod    module.Version
@@ -289,7 +297,7 @@ func parseToFile(file string, data []byte, fix VersionFixer, strict bool) (parse
 					})
 				}
 				continue
-			case "module", "require", "exclude", "replace", "retract":
+			case "module", "godebug", "require", "exclude", "replace", "retract":
 				for _, l := range x.Line {
 					f.add(&errs, x, l, x.Token[0], l.Token, fix, strict)
 				}
@@ -308,7 +316,9 @@ var laxGoVersionRE = lazyregexp.New(`^v?(([1-9][0-9]*)\.(0|[1-9][0-9]*))([^0-9].
 
 // Toolchains must be named beginning with `go1`,
 // like "go1.20.3" or "go1.20.3-gccgo". As a special case, "default" is also permitted.
-// TODO(samthanawalla): Replace regex with https://pkg.go.dev/go/version#IsValid in 1.23+
+// Note that this regexp is a much looser condition than go/version.IsValid,
+// for forward compatibility.
+// (This code has to be work to identify new toolchains even if we tweak the syntax in the future.)
 var ToolchainRE = lazyregexp.New(`^default$|^go1($|\.)`)
 
 func (f *File) add(errs *ErrorList, block *LineBlock, line *Line, verb string, args []string, fix VersionFixer, strict bool) {
@@ -384,7 +394,7 @@ func (f *File) add(errs *ErrorList, block *LineBlock, line *Line, verb string, a
 		if len(args) != 1 {
 			errorf("toolchain directive expects exactly one argument")
 			return
-		} else if strict && !ToolchainRE.MatchString(args[0]) {
+		} else if !ToolchainRE.MatchString(args[0]) {
 			errorf("invalid toolchain version '%s': must match format go1.23.0 or default", args[0])
 			return
 		}
@@ -412,6 +422,22 @@ func (f *File) add(errs *ErrorList, block *LineBlock, line *Line, verb string, a
 		}
 		f.Module.Mod = module.Version{Path: s}
 
+	case "godebug":
+		if len(args) != 1 || strings.ContainsAny(args[0], "\"`',") {
+			errorf("usage: godebug key=value")
+			return
+		}
+		key, value, ok := strings.Cut(args[0], "=")
+		if !ok {
+			errorf("usage: godebug key=value")
+			return
+		}
+		f.Godebug = append(f.Godebug, &Godebug{
+			Key:    key,
+			Value:  value,
+			Syntax: line,
+		})
+
 	case "require", "exclude":
 		if len(args) != 2 {
 			errorf("usage: %s module/path v1.2.3", verb)
@@ -654,6 +680,22 @@ func (f *WorkFile) add(errs *ErrorList, line *Line, verb string, args []string,
 		f.Toolchain = &Toolchain{Syntax: line}
 		f.Toolchain.Name = args[0]
 
+	case "godebug":
+		if len(args) != 1 || strings.ContainsAny(args[0], "\"`',") {
+			errorf("usage: godebug key=value")
+			return
+		}
+		key, value, ok := strings.Cut(args[0], "=")
+		if !ok {
+			errorf("usage: godebug key=value")
+			return
+		}
+		f.Godebug = append(f.Godebug, &Godebug{
+			Key:    key,
+			Value:  value,
+			Syntax: line,
+		})
+
 	case "use":
 		if len(args) != 1 {
 			errorf("usage: %s local/dir", verb)
@@ -929,6 +971,15 @@ func (f *File) Format() ([]byte, error) {
 // Cleanup cleans out all the cleared entries.
 func (f *File) Cleanup() {
 	w := 0
+	for _, g := range f.Godebug {
+		if g.Key != "" {
+			f.Godebug[w] = g
+			w++
+		}
+	}
+	f.Godebug = f.Godebug[:w]
+
+	w = 0
 	for _, r := range f.Require {
 		if r.Mod.Path != "" {
 			f.Require[w] = r
@@ -1027,6 +1078,45 @@ func (f *File) AddToolchainStmt(name string) error {
 	return nil
 }
 
+// AddGodebug sets the first godebug line for key to value,
+// preserving any existing comments for that line and removing all
+// other godebug lines for key.
+//
+// If no line currently exists for key, AddGodebug adds a new line
+// at the end of the last godebug block.
+func (f *File) AddGodebug(key, value string) error {
+	need := true
+	for _, g := range f.Godebug {
+		if g.Key == key {
+			if need {
+				g.Value = value
+				f.Syntax.updateLine(g.Syntax, "godebug", key+"="+value)
+				need = false
+			} else {
+				g.Syntax.markRemoved()
+				*g = Godebug{}
+			}
+		}
+	}
+
+	if need {
+		f.addNewGodebug(key, value)
+	}
+	return nil
+}
+
+// addNewGodebug adds a new godebug key=value line at the end
+// of the last godebug block, regardless of any existing godebug lines for key.
+func (f *File) addNewGodebug(key, value string) {
+	line := f.Syntax.addLine(nil, "godebug", key+"="+value)
+	g := &Godebug{
+		Key:    key,
+		Value:  value,
+		Syntax: line,
+	}
+	f.Godebug = append(f.Godebug, g)
+}
+
 // AddRequire sets the first require line for path to version vers,
 // preserving any existing comments for that line and removing all
 // other lines for path.
@@ -1334,6 +1424,16 @@ func (f *File) SetRequireSeparateIndirect(req []*Require) {
 	f.SortBlocks()
 }
 
+func (f *File) DropGodebug(key string) error {
+	for _, g := range f.Godebug {
+		if g.Key == key {
+			g.Syntax.markRemoved()
+			*g = Godebug{}
+		}
+	}
+	return nil
+}
+
 func (f *File) DropRequire(path string) error {
 	for _, r := range f.Require {
 		if r.Mod.Path == path {
diff --git a/vendor/golang.org/x/mod/modfile/work.go b/vendor/golang.org/x/mod/modfile/work.go
index d7b99376..8f54897c 100644
--- a/vendor/golang.org/x/mod/modfile/work.go
+++ b/vendor/golang.org/x/mod/modfile/work.go
@@ -14,6 +14,7 @@ import (
 type WorkFile struct {
 	Go        *Go
 	Toolchain *Toolchain
+	Godebug   []*Godebug
 	Use       []*Use
 	Replace   []*Replace
 
@@ -68,7 +69,7 @@ func ParseWork(file string, data []byte, fix VersionFixer) (*WorkFile, error) {
 					Err:      fmt.Errorf("unknown block type: %s", strings.Join(x.Token, " ")),
 				})
 				continue
-			case "use", "replace":
+			case "godebug", "use", "replace":
 				for _, l := range x.Line {
 					f.add(&errs, l, x.Token[0], l.Token, fix)
 				}
@@ -184,6 +185,55 @@ func (f *WorkFile) DropToolchainStmt() {
 	}
 }
 
+// AddGodebug sets the first godebug line for key to value,
+// preserving any existing comments for that line and removing all
+// other godebug lines for key.
+//
+// If no line currently exists for key, AddGodebug adds a new line
+// at the end of the last godebug block.
+func (f *WorkFile) AddGodebug(key, value string) error {
+	need := true
+	for _, g := range f.Godebug {
+		if g.Key == key {
+			if need {
+				g.Value = value
+				f.Syntax.updateLine(g.Syntax, "godebug", key+"="+value)
+				need = false
+			} else {
+				g.Syntax.markRemoved()
+				*g = Godebug{}
+			}
+		}
+	}
+
+	if need {
+		f.addNewGodebug(key, value)
+	}
+	return nil
+}
+
+// addNewGodebug adds a new godebug key=value line at the end
+// of the last godebug block, regardless of any existing godebug lines for key.
+func (f *WorkFile) addNewGodebug(key, value string) {
+	line := f.Syntax.addLine(nil, "godebug", key+"="+value)
+	g := &Godebug{
+		Key:    key,
+		Value:  value,
+		Syntax: line,
+	}
+	f.Godebug = append(f.Godebug, g)
+}
+
+func (f *WorkFile) DropGodebug(key string) error {
+	for _, g := range f.Godebug {
+		if g.Key == key {
+			g.Syntax.markRemoved()
+			*g = Godebug{}
+		}
+	}
+	return nil
+}
+
 func (f *WorkFile) AddUse(diskPath, modulePath string) error {
 	need := true
 	for _, d := range f.Use {
diff --git a/vendor/golang.org/x/mod/module/module.go b/vendor/golang.org/x/mod/module/module.go
index 2a364b22..cac1a899 100644
--- a/vendor/golang.org/x/mod/module/module.go
+++ b/vendor/golang.org/x/mod/module/module.go
@@ -506,6 +506,7 @@ var badWindowsNames = []string{
 	"PRN",
 	"AUX",
 	"NUL",
+	"COM0",
 	"COM1",
 	"COM2",
 	"COM3",
@@ -515,6 +516,7 @@ var badWindowsNames = []string{
 	"COM7",
 	"COM8",
 	"COM9",
+	"LPT0",
 	"LPT1",
 	"LPT2",
 	"LPT3",
diff --git a/vendor/golang.org/x/net/html/doc.go b/vendor/golang.org/x/net/html/doc.go
index 2466ae3d..3a7e5ab1 100644
--- a/vendor/golang.org/x/net/html/doc.go
+++ b/vendor/golang.org/x/net/html/doc.go
@@ -104,7 +104,7 @@ tokenization, and tokenization and tree construction stages of the WHATWG HTML
 parsing specification respectively. While the tokenizer parses and normalizes
 individual HTML tokens, only the parser constructs the DOM tree from the
 tokenized HTML, as described in the tree construction stage of the
-specification, dynamically modifying or extending the docuemnt's DOM tree.
+specification, dynamically modifying or extending the document's DOM tree.
 
 If your use case requires semantically well-formed HTML documents, as defined by
 the WHATWG specification, the parser should be used rather than the tokenizer.
diff --git a/vendor/golang.org/x/net/http/httpguts/httplex.go b/vendor/golang.org/x/net/http/httpguts/httplex.go
index 6e071e85..9b4de940 100644
--- a/vendor/golang.org/x/net/http/httpguts/httplex.go
+++ b/vendor/golang.org/x/net/http/httpguts/httplex.go
@@ -12,7 +12,7 @@ import (
 	"golang.org/x/net/idna"
 )
 
-var isTokenTable = [127]bool{
+var isTokenTable = [256]bool{
 	'!':  true,
 	'#':  true,
 	'$':  true,
@@ -93,12 +93,7 @@ var isTokenTable = [127]bool{
 }
 
 func IsTokenRune(r rune) bool {
-	i := int(r)
-	return i < len(isTokenTable) && isTokenTable[i]
-}
-
-func isNotToken(r rune) bool {
-	return !IsTokenRune(r)
+	return r < utf8.RuneSelf && isTokenTable[byte(r)]
 }
 
 // HeaderValuesContainsToken reports whether any string in values
@@ -202,8 +197,8 @@ func ValidHeaderFieldName(v string) bool {
 	if len(v) == 0 {
 		return false
 	}
-	for _, r := range v {
-		if !IsTokenRune(r) {
+	for i := 0; i < len(v); i++ {
+		if !isTokenTable[v[i]] {
 			return false
 		}
 	}
diff --git a/vendor/golang.org/x/net/http2/frame.go b/vendor/golang.org/x/net/http2/frame.go
index 43557ab7..105c3b27 100644
--- a/vendor/golang.org/x/net/http2/frame.go
+++ b/vendor/golang.org/x/net/http2/frame.go
@@ -490,6 +490,9 @@ func terminalReadFrameError(err error) bool {
 // returned error is ErrFrameTooLarge. Other errors may be of type
 // ConnectionError, StreamError, or anything else from the underlying
 // reader.
+//
+// If ReadFrame returns an error and a non-nil Frame, the Frame's StreamID
+// indicates the stream responsible for the error.
 func (fr *Framer) ReadFrame() (Frame, error) {
 	fr.errDetail = nil
 	if fr.lastFrame != nil {
@@ -1521,7 +1524,7 @@ func (fr *Framer) maxHeaderStringLen() int {
 // readMetaFrame returns 0 or more CONTINUATION frames from fr and
 // merge them into the provided hf and returns a MetaHeadersFrame
 // with the decoded hpack values.
-func (fr *Framer) readMetaFrame(hf *HeadersFrame) (*MetaHeadersFrame, error) {
+func (fr *Framer) readMetaFrame(hf *HeadersFrame) (Frame, error) {
 	if fr.AllowIllegalReads {
 		return nil, errors.New("illegal use of AllowIllegalReads with ReadMetaHeaders")
 	}
@@ -1592,7 +1595,7 @@ func (fr *Framer) readMetaFrame(hf *HeadersFrame) (*MetaHeadersFrame, error) {
 			}
 			// It would be nice to send a RST_STREAM before sending the GOAWAY,
 			// but the structure of the server's frame writer makes this difficult.
-			return nil, ConnectionError(ErrCodeProtocol)
+			return mh, ConnectionError(ErrCodeProtocol)
 		}
 
 		// Also close the connection after any CONTINUATION frame following an
@@ -1604,11 +1607,11 @@ func (fr *Framer) readMetaFrame(hf *HeadersFrame) (*MetaHeadersFrame, error) {
 			}
 			// It would be nice to send a RST_STREAM before sending the GOAWAY,
 			// but the structure of the server's frame writer makes this difficult.
-			return nil, ConnectionError(ErrCodeProtocol)
+			return mh, ConnectionError(ErrCodeProtocol)
 		}
 
 		if _, err := hdec.Write(frag); err != nil {
-			return nil, ConnectionError(ErrCodeCompression)
+			return mh, ConnectionError(ErrCodeCompression)
 		}
 
 		if hc.HeadersEnded() {
@@ -1625,7 +1628,7 @@ func (fr *Framer) readMetaFrame(hf *HeadersFrame) (*MetaHeadersFrame, error) {
 	mh.HeadersFrame.invalidate()
 
 	if err := hdec.Close(); err != nil {
-		return nil, ConnectionError(ErrCodeCompression)
+		return mh, ConnectionError(ErrCodeCompression)
 	}
 	if invalid != nil {
 		fr.errDetail = invalid
diff --git a/vendor/golang.org/x/net/http2/http2.go b/vendor/golang.org/x/net/http2/http2.go
index 6f2df281..003e649f 100644
--- a/vendor/golang.org/x/net/http2/http2.go
+++ b/vendor/golang.org/x/net/http2/http2.go
@@ -17,6 +17,7 @@ package http2 // import "golang.org/x/net/http2"
 
 import (
 	"bufio"
+	"context"
 	"crypto/tls"
 	"fmt"
 	"io"
@@ -26,6 +27,7 @@ import (
 	"strconv"
 	"strings"
 	"sync"
+	"time"
 
 	"golang.org/x/net/http/httpguts"
 )
@@ -210,12 +212,6 @@ type stringWriter interface {
 	WriteString(s string) (n int, err error)
 }
 
-// A gate lets two goroutines coordinate their activities.
-type gate chan struct{}
-
-func (g gate) Done() { g <- struct{}{} }
-func (g gate) Wait() { <-g }
-
 // A closeWaiter is like a sync.WaitGroup but only goes 1 to 0 (open to closed).
 type closeWaiter chan struct{}
 
@@ -383,3 +379,14 @@ func validPseudoPath(v string) bool {
 // makes that struct also non-comparable, and generally doesn't add
 // any size (as long as it's first).
 type incomparable [0]func()
+
+// synctestGroupInterface is the methods of synctestGroup used by Server and Transport.
+// It's defined as an interface here to let us keep synctestGroup entirely test-only
+// and not a part of non-test builds.
+type synctestGroupInterface interface {
+	Join()
+	Now() time.Time
+	NewTimer(d time.Duration) timer
+	AfterFunc(d time.Duration, f func()) timer
+	ContextWithTimeout(ctx context.Context, d time.Duration) (context.Context, context.CancelFunc)
+}
diff --git a/vendor/golang.org/x/net/http2/server.go b/vendor/golang.org/x/net/http2/server.go
index ce2e8b40..6c349f3e 100644
--- a/vendor/golang.org/x/net/http2/server.go
+++ b/vendor/golang.org/x/net/http2/server.go
@@ -154,6 +154,39 @@ type Server struct {
 	// so that we don't embed a Mutex in this struct, which will make the
 	// struct non-copyable, which might break some callers.
 	state *serverInternalState
+
+	// Synchronization group used for testing.
+	// Outside of tests, this is nil.
+	group synctestGroupInterface
+}
+
+func (s *Server) markNewGoroutine() {
+	if s.group != nil {
+		s.group.Join()
+	}
+}
+
+func (s *Server) now() time.Time {
+	if s.group != nil {
+		return s.group.Now()
+	}
+	return time.Now()
+}
+
+// newTimer creates a new time.Timer, or a synthetic timer in tests.
+func (s *Server) newTimer(d time.Duration) timer {
+	if s.group != nil {
+		return s.group.NewTimer(d)
+	}
+	return timeTimer{time.NewTimer(d)}
+}
+
+// afterFunc creates a new time.AfterFunc timer, or a synthetic timer in tests.
+func (s *Server) afterFunc(d time.Duration, f func()) timer {
+	if s.group != nil {
+		return s.group.AfterFunc(d, f)
+	}
+	return timeTimer{time.AfterFunc(d, f)}
 }
 
 func (s *Server) initialConnRecvWindowSize() int32 {
@@ -400,6 +433,10 @@ func (o *ServeConnOpts) handler() http.Handler {
 //
 // The opts parameter is optional. If nil, default values are used.
 func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {
+	s.serveConn(c, opts, nil)
+}
+
+func (s *Server) serveConn(c net.Conn, opts *ServeConnOpts, newf func(*serverConn)) {
 	baseCtx, cancel := serverConnBaseContext(c, opts)
 	defer cancel()
 
@@ -426,6 +463,9 @@ func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {
 		pushEnabled:                 true,
 		sawClientPreface:            opts.SawClientPreface,
 	}
+	if newf != nil {
+		newf(sc)
+	}
 
 	s.state.registerConn(sc)
 	defer s.state.unregisterConn(sc)
@@ -599,8 +639,8 @@ type serverConn struct {
 	inFrameScheduleLoop         bool              // whether we're in the scheduleFrameWrite loop
 	needToSendGoAway            bool              // we need to schedule a GOAWAY frame write
 	goAwayCode                  ErrCode
-	shutdownTimer               *time.Timer // nil until used
-	idleTimer                   *time.Timer // nil if unused
+	shutdownTimer               timer // nil until used
+	idleTimer                   timer // nil if unused
 
 	// Owned by the writeFrameAsync goroutine:
 	headerWriteBuf bytes.Buffer
@@ -649,12 +689,12 @@ type stream struct {
 	flow             outflow // limits writing from Handler to client
 	inflow           inflow  // what the client is allowed to POST/etc to us
 	state            streamState
-	resetQueued      bool        // RST_STREAM queued for write; set by sc.resetStream
-	gotTrailerHeader bool        // HEADER frame for trailers was seen
-	wroteHeaders     bool        // whether we wrote headers (not status 100)
-	readDeadline     *time.Timer // nil if unused
-	writeDeadline    *time.Timer // nil if unused
-	closeErr         error       // set before cw is closed
+	resetQueued      bool  // RST_STREAM queued for write; set by sc.resetStream
+	gotTrailerHeader bool  // HEADER frame for trailers was seen
+	wroteHeaders     bool  // whether we wrote headers (not status 100)
+	readDeadline     timer // nil if unused
+	writeDeadline    timer // nil if unused
+	closeErr         error // set before cw is closed
 
 	trailer    http.Header // accumulated trailers
 	reqTrailer http.Header // handler's Request.Trailer
@@ -732,11 +772,7 @@ func isClosedConnError(err error) bool {
 		return false
 	}
 
-	// TODO: remove this string search and be more like the Windows
-	// case below. That might involve modifying the standard library
-	// to return better error types.
-	str := err.Error()
-	if strings.Contains(str, "use of closed network connection") {
+	if errors.Is(err, net.ErrClosed) {
 		return true
 	}
 
@@ -815,8 +851,9 @@ type readFrameResult struct {
 // consumer is done with the frame.
 // It's run on its own goroutine.
 func (sc *serverConn) readFrames() {
-	gate := make(gate)
-	gateDone := gate.Done
+	sc.srv.markNewGoroutine()
+	gate := make(chan struct{})
+	gateDone := func() { gate <- struct{}{} }
 	for {
 		f, err := sc.framer.ReadFrame()
 		select {
@@ -847,6 +884,7 @@ type frameWriteResult struct {
 // At most one goroutine can be running writeFrameAsync at a time per
 // serverConn.
 func (sc *serverConn) writeFrameAsync(wr FrameWriteRequest, wd *writeData) {
+	sc.srv.markNewGoroutine()
 	var err error
 	if wd == nil {
 		err = wr.write.writeFrame(sc)
@@ -926,13 +964,13 @@ func (sc *serverConn) serve() {
 	sc.setConnState(http.StateIdle)
 
 	if sc.srv.IdleTimeout > 0 {
-		sc.idleTimer = time.AfterFunc(sc.srv.IdleTimeout, sc.onIdleTimer)
+		sc.idleTimer = sc.srv.afterFunc(sc.srv.IdleTimeout, sc.onIdleTimer)
 		defer sc.idleTimer.Stop()
 	}
 
 	go sc.readFrames() // closed by defer sc.conn.Close above
 
-	settingsTimer := time.AfterFunc(firstSettingsTimeout, sc.onSettingsTimer)
+	settingsTimer := sc.srv.afterFunc(firstSettingsTimeout, sc.onSettingsTimer)
 	defer settingsTimer.Stop()
 
 	loopNum := 0
@@ -1061,10 +1099,10 @@ func (sc *serverConn) readPreface() error {
 			errc <- nil
 		}
 	}()
-	timer := time.NewTimer(prefaceTimeout) // TODO: configurable on *Server?
+	timer := sc.srv.newTimer(prefaceTimeout) // TODO: configurable on *Server?
 	defer timer.Stop()
 	select {
-	case <-timer.C:
+	case <-timer.C():
 		return errPrefaceTimeout
 	case err := <-errc:
 		if err == nil {
@@ -1429,7 +1467,7 @@ func (sc *serverConn) goAway(code ErrCode) {
 
 func (sc *serverConn) shutDownIn(d time.Duration) {
 	sc.serveG.check()
-	sc.shutdownTimer = time.AfterFunc(d, sc.onShutdownTimer)
+	sc.shutdownTimer = sc.srv.afterFunc(d, sc.onShutdownTimer)
 }
 
 func (sc *serverConn) resetStream(se StreamError) {
@@ -1482,6 +1520,11 @@ func (sc *serverConn) processFrameFromReader(res readFrameResult) bool {
 		sc.goAway(ErrCodeFlowControl)
 		return true
 	case ConnectionError:
+		if res.f != nil {
+			if id := res.f.Header().StreamID; id > sc.maxClientStreamID {
+				sc.maxClientStreamID = id
+			}
+		}
 		sc.logf("http2: server connection error from %v: %v", sc.conn.RemoteAddr(), ev)
 		sc.goAway(ErrCode(ev))
 		return true // goAway will handle shutdown
@@ -1638,7 +1681,7 @@ func (sc *serverConn) closeStream(st *stream, err error) {
 	delete(sc.streams, st.id)
 	if len(sc.streams) == 0 {
 		sc.setConnState(http.StateIdle)
-		if sc.srv.IdleTimeout > 0 {
+		if sc.srv.IdleTimeout > 0 && sc.idleTimer != nil {
 			sc.idleTimer.Reset(sc.srv.IdleTimeout)
 		}
 		if h1ServerKeepAlivesDisabled(sc.hs) {
@@ -1660,6 +1703,7 @@ func (sc *serverConn) closeStream(st *stream, err error) {
 		}
 	}
 	st.closeErr = err
+	st.cancelCtx()
 	st.cw.Close() // signals Handler's CloseNotifier, unblocks writes, etc
 	sc.writeSched.CloseStream(st.id)
 }
@@ -2020,7 +2064,7 @@ func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error {
 	// (in Go 1.8), though. That's a more sane option anyway.
 	if sc.hs.ReadTimeout > 0 {
 		sc.conn.SetReadDeadline(time.Time{})
-		st.readDeadline = time.AfterFunc(sc.hs.ReadTimeout, st.onReadTimeout)
+		st.readDeadline = sc.srv.afterFunc(sc.hs.ReadTimeout, st.onReadTimeout)
 	}
 
 	return sc.scheduleHandler(id, rw, req, handler)
@@ -2118,7 +2162,7 @@ func (sc *serverConn) newStream(id, pusherID uint32, state streamState) *stream
 	st.flow.add(sc.initialStreamSendWindowSize)
 	st.inflow.init(sc.srv.initialStreamRecvWindowSize())
 	if sc.hs.WriteTimeout > 0 {
-		st.writeDeadline = time.AfterFunc(sc.hs.WriteTimeout, st.onWriteTimeout)
+		st.writeDeadline = sc.srv.afterFunc(sc.hs.WriteTimeout, st.onWriteTimeout)
 	}
 
 	sc.streams[id] = st
@@ -2342,6 +2386,7 @@ func (sc *serverConn) handlerDone() {
 
 // Run on its own goroutine.
 func (sc *serverConn) runHandler(rw *responseWriter, req *http.Request, handler func(http.ResponseWriter, *http.Request)) {
+	sc.srv.markNewGoroutine()
 	defer sc.sendServeMsg(handlerDoneMsg)
 	didPanic := true
 	defer func() {
@@ -2638,7 +2683,7 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) {
 		var date string
 		if _, ok := rws.snapHeader["Date"]; !ok {
 			// TODO(bradfitz): be faster here, like net/http? measure.
-			date = time.Now().UTC().Format(http.TimeFormat)
+			date = rws.conn.srv.now().UTC().Format(http.TimeFormat)
 		}
 
 		for _, v := range rws.snapHeader["Trailer"] {
@@ -2760,7 +2805,7 @@ func (rws *responseWriterState) promoteUndeclaredTrailers() {
 
 func (w *responseWriter) SetReadDeadline(deadline time.Time) error {
 	st := w.rws.stream
-	if !deadline.IsZero() && deadline.Before(time.Now()) {
+	if !deadline.IsZero() && deadline.Before(w.rws.conn.srv.now()) {
 		// If we're setting a deadline in the past, reset the stream immediately
 		// so writes after SetWriteDeadline returns will fail.
 		st.onReadTimeout()
@@ -2776,9 +2821,9 @@ func (w *responseWriter) SetReadDeadline(deadline time.Time) error {
 		if deadline.IsZero() {
 			st.readDeadline = nil
 		} else if st.readDeadline == nil {
-			st.readDeadline = time.AfterFunc(deadline.Sub(time.Now()), st.onReadTimeout)
+			st.readDeadline = sc.srv.afterFunc(deadline.Sub(sc.srv.now()), st.onReadTimeout)
 		} else {
-			st.readDeadline.Reset(deadline.Sub(time.Now()))
+			st.readDeadline.Reset(deadline.Sub(sc.srv.now()))
 		}
 	})
 	return nil
@@ -2786,7 +2831,7 @@ func (w *responseWriter) SetReadDeadline(deadline time.Time) error {
 
 func (w *responseWriter) SetWriteDeadline(deadline time.Time) error {
 	st := w.rws.stream
-	if !deadline.IsZero() && deadline.Before(time.Now()) {
+	if !deadline.IsZero() && deadline.Before(w.rws.conn.srv.now()) {
 		// If we're setting a deadline in the past, reset the stream immediately
 		// so writes after SetWriteDeadline returns will fail.
 		st.onWriteTimeout()
@@ -2802,9 +2847,9 @@ func (w *responseWriter) SetWriteDeadline(deadline time.Time) error {
 		if deadline.IsZero() {
 			st.writeDeadline = nil
 		} else if st.writeDeadline == nil {
-			st.writeDeadline = time.AfterFunc(deadline.Sub(time.Now()), st.onWriteTimeout)
+			st.writeDeadline = sc.srv.afterFunc(deadline.Sub(sc.srv.now()), st.onWriteTimeout)
 		} else {
-			st.writeDeadline.Reset(deadline.Sub(time.Now()))
+			st.writeDeadline.Reset(deadline.Sub(sc.srv.now()))
 		}
 	})
 	return nil
diff --git a/vendor/golang.org/x/net/http2/timer.go b/vendor/golang.org/x/net/http2/timer.go
new file mode 100644
index 00000000..0b1c17b8
--- /dev/null
+++ b/vendor/golang.org/x/net/http2/timer.go
@@ -0,0 +1,20 @@
+// Copyright 2024 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+package http2
+
+import "time"
+
+// A timer is a time.Timer, as an interface which can be replaced in tests.
+type timer = interface {
+	C() <-chan time.Time
+	Reset(d time.Duration) bool
+	Stop() bool
+}
+
+// timeTimer adapts a time.Timer to the timer interface.
+type timeTimer struct {
+	*time.Timer
+}
+
+func (t timeTimer) C() <-chan time.Time { return t.Timer.C }
diff --git a/vendor/golang.org/x/net/http2/transport.go b/vendor/golang.org/x/net/http2/transport.go
index ce375c8c..98a49c6b 100644
--- a/vendor/golang.org/x/net/http2/transport.go
+++ b/vendor/golang.org/x/net/http2/transport.go
@@ -185,7 +185,45 @@ type Transport struct {
 	connPoolOnce  sync.Once
 	connPoolOrDef ClientConnPool // non-nil version of ConnPool
 
-	syncHooks *testSyncHooks
+	*transportTestHooks
+}
+
+// Hook points used for testing.
+// Outside of tests, t.transportTestHooks is nil and these all have minimal implementations.
+// Inside tests, see the testSyncHooks function docs.
+
+type transportTestHooks struct {
+	newclientconn func(*ClientConn)
+	group         synctestGroupInterface
+}
+
+func (t *Transport) markNewGoroutine() {
+	if t != nil && t.transportTestHooks != nil {
+		t.transportTestHooks.group.Join()
+	}
+}
+
+// newTimer creates a new time.Timer, or a synthetic timer in tests.
+func (t *Transport) newTimer(d time.Duration) timer {
+	if t.transportTestHooks != nil {
+		return t.transportTestHooks.group.NewTimer(d)
+	}
+	return timeTimer{time.NewTimer(d)}
+}
+
+// afterFunc creates a new time.AfterFunc timer, or a synthetic timer in tests.
+func (t *Transport) afterFunc(d time.Duration, f func()) timer {
+	if t.transportTestHooks != nil {
+		return t.transportTestHooks.group.AfterFunc(d, f)
+	}
+	return timeTimer{time.AfterFunc(d, f)}
+}
+
+func (t *Transport) contextWithTimeout(ctx context.Context, d time.Duration) (context.Context, context.CancelFunc) {
+	if t.transportTestHooks != nil {
+		return t.transportTestHooks.group.ContextWithTimeout(ctx, d)
+	}
+	return context.WithTimeout(ctx, d)
 }
 
 func (t *Transport) maxHeaderListSize() uint32 {
@@ -352,60 +390,6 @@ type ClientConn struct {
 	werr error        // first write error that has occurred
 	hbuf bytes.Buffer // HPACK encoder writes into this
 	henc *hpack.Encoder
-
-	syncHooks *testSyncHooks // can be nil
-}
-
-// Hook points used for testing.
-// Outside of tests, cc.syncHooks is nil and these all have minimal implementations.
-// Inside tests, see the testSyncHooks function docs.
-
-// goRun starts a new goroutine.
-func (cc *ClientConn) goRun(f func()) {
-	if cc.syncHooks != nil {
-		cc.syncHooks.goRun(f)
-		return
-	}
-	go f()
-}
-
-// condBroadcast is cc.cond.Broadcast.
-func (cc *ClientConn) condBroadcast() {
-	if cc.syncHooks != nil {
-		cc.syncHooks.condBroadcast(cc.cond)
-	}
-	cc.cond.Broadcast()
-}
-
-// condWait is cc.cond.Wait.
-func (cc *ClientConn) condWait() {
-	if cc.syncHooks != nil {
-		cc.syncHooks.condWait(cc.cond)
-	}
-	cc.cond.Wait()
-}
-
-// newTimer creates a new time.Timer, or a synthetic timer in tests.
-func (cc *ClientConn) newTimer(d time.Duration) timer {
-	if cc.syncHooks != nil {
-		return cc.syncHooks.newTimer(d)
-	}
-	return newTimeTimer(d)
-}
-
-// afterFunc creates a new time.AfterFunc timer, or a synthetic timer in tests.
-func (cc *ClientConn) afterFunc(d time.Duration, f func()) timer {
-	if cc.syncHooks != nil {
-		return cc.syncHooks.afterFunc(d, f)
-	}
-	return newTimeAfterFunc(d, f)
-}
-
-func (cc *ClientConn) contextWithTimeout(ctx context.Context, d time.Duration) (context.Context, context.CancelFunc) {
-	if cc.syncHooks != nil {
-		return cc.syncHooks.contextWithTimeout(ctx, d)
-	}
-	return context.WithTimeout(ctx, d)
 }
 
 // clientStream is the state for a single HTTP/2 stream. One of these
@@ -487,7 +471,7 @@ func (cs *clientStream) abortStreamLocked(err error) {
 	// TODO(dneil): Clean up tests where cs.cc.cond is nil.
 	if cs.cc.cond != nil {
 		// Wake up writeRequestBody if it is waiting on flow control.
-		cs.cc.condBroadcast()
+		cs.cc.cond.Broadcast()
 	}
 }
 
@@ -497,7 +481,7 @@ func (cs *clientStream) abortRequestBodyWrite() {
 	defer cc.mu.Unlock()
 	if cs.reqBody != nil && cs.reqBodyClosed == nil {
 		cs.closeReqBodyLocked()
-		cc.condBroadcast()
+		cc.cond.Broadcast()
 	}
 }
 
@@ -507,10 +491,11 @@ func (cs *clientStream) closeReqBodyLocked() {
 	}
 	cs.reqBodyClosed = make(chan struct{})
 	reqBodyClosed := cs.reqBodyClosed
-	cs.cc.goRun(func() {
+	go func() {
+		cs.cc.t.markNewGoroutine()
 		cs.reqBody.Close()
 		close(reqBodyClosed)
-	})
+	}()
 }
 
 type stickyErrWriter struct {
@@ -626,21 +611,7 @@ func (t *Transport) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Res
 				backoff := float64(uint(1) << (uint(retry) - 1))
 				backoff += backoff * (0.1 * mathrand.Float64())
 				d := time.Second * time.Duration(backoff)
-				var tm timer
-				if t.syncHooks != nil {
-					tm = t.syncHooks.newTimer(d)
-					t.syncHooks.blockUntil(func() bool {
-						select {
-						case <-tm.C():
-						case <-req.Context().Done():
-						default:
-							return false
-						}
-						return true
-					})
-				} else {
-					tm = newTimeTimer(d)
-				}
+				tm := t.newTimer(d)
 				select {
 				case <-tm.C():
 					t.vlogf("RoundTrip retrying after failure: %v", roundTripErr)
@@ -725,8 +696,8 @@ func canRetryError(err error) bool {
 }
 
 func (t *Transport) dialClientConn(ctx context.Context, addr string, singleUse bool) (*ClientConn, error) {
-	if t.syncHooks != nil {
-		return t.newClientConn(nil, singleUse, t.syncHooks)
+	if t.transportTestHooks != nil {
+		return t.newClientConn(nil, singleUse)
 	}
 	host, _, err := net.SplitHostPort(addr)
 	if err != nil {
@@ -736,7 +707,7 @@ func (t *Transport) dialClientConn(ctx context.Context, addr string, singleUse b
 	if err != nil {
 		return nil, err
 	}
-	return t.newClientConn(tconn, singleUse, nil)
+	return t.newClientConn(tconn, singleUse)
 }
 
 func (t *Transport) newTLSConfig(host string) *tls.Config {
@@ -802,10 +773,10 @@ func (t *Transport) maxEncoderHeaderTableSize() uint32 {
 }
 
 func (t *Transport) NewClientConn(c net.Conn) (*ClientConn, error) {
-	return t.newClientConn(c, t.disableKeepAlives(), nil)
+	return t.newClientConn(c, t.disableKeepAlives())
 }
 
-func (t *Transport) newClientConn(c net.Conn, singleUse bool, hooks *testSyncHooks) (*ClientConn, error) {
+func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, error) {
 	cc := &ClientConn{
 		t:                     t,
 		tconn:                 c,
@@ -820,16 +791,12 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool, hooks *testSyncHoo
 		wantSettingsAck:       true,
 		pings:                 make(map[[8]byte]chan struct{}),
 		reqHeaderMu:           make(chan struct{}, 1),
-		syncHooks:             hooks,
 	}
-	if hooks != nil {
-		hooks.newclientconn(cc)
+	if t.transportTestHooks != nil {
+		t.markNewGoroutine()
+		t.transportTestHooks.newclientconn(cc)
 		c = cc.tconn
 	}
-	if d := t.idleConnTimeout(); d != 0 {
-		cc.idleTimeout = d
-		cc.idleTimer = cc.afterFunc(d, cc.onIdleTimeout)
-	}
 	if VerboseLogs {
 		t.vlogf("http2: Transport creating client conn %p to %v", cc, c.RemoteAddr())
 	}
@@ -893,7 +860,13 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool, hooks *testSyncHoo
 		return nil, cc.werr
 	}
 
-	cc.goRun(cc.readLoop)
+	// Start the idle timer after the connection is fully initialized.
+	if d := t.idleConnTimeout(); d != 0 {
+		cc.idleTimeout = d
+		cc.idleTimer = t.afterFunc(d, cc.onIdleTimeout)
+	}
+
+	go cc.readLoop()
 	return cc, nil
 }
 
@@ -901,7 +874,7 @@ func (cc *ClientConn) healthCheck() {
 	pingTimeout := cc.t.pingTimeout()
 	// We don't need to periodically ping in the health check, because the readLoop of ClientConn will
 	// trigger the healthCheck again if there is no frame received.
-	ctx, cancel := cc.contextWithTimeout(context.Background(), pingTimeout)
+	ctx, cancel := cc.t.contextWithTimeout(context.Background(), pingTimeout)
 	defer cancel()
 	cc.vlogf("http2: Transport sending health check")
 	err := cc.Ping(ctx)
@@ -936,7 +909,20 @@ func (cc *ClientConn) setGoAway(f *GoAwayFrame) {
 	}
 	last := f.LastStreamID
 	for streamID, cs := range cc.streams {
-		if streamID > last {
+		if streamID <= last {
+			// The server's GOAWAY indicates that it received this stream.
+			// It will either finish processing it, or close the connection
+			// without doing so. Either way, leave the stream alone for now.
+			continue
+		}
+		if streamID == 1 && cc.goAway.ErrCode != ErrCodeNo {
+			// Don't retry the first stream on a connection if we get a non-NO error.
+			// If the server is sending an error on a new connection,
+			// retrying the request on a new one probably isn't going to work.
+			cs.abortStreamLocked(fmt.Errorf("http2: Transport received GOAWAY from server ErrCode:%v", cc.goAway.ErrCode))
+		} else {
+			// Aborting the stream with errClentConnGotGoAway indicates that
+			// the request should be retried on a new connection.
 			cs.abortStreamLocked(errClientConnGotGoAway)
 		}
 	}
@@ -1131,7 +1117,8 @@ func (cc *ClientConn) Shutdown(ctx context.Context) error {
 	// Wait for all in-flight streams to complete or connection to close
 	done := make(chan struct{})
 	cancelled := false // guarded by cc.mu
-	cc.goRun(func() {
+	go func() {
+		cc.t.markNewGoroutine()
 		cc.mu.Lock()
 		defer cc.mu.Unlock()
 		for {
@@ -1143,9 +1130,9 @@ func (cc *ClientConn) Shutdown(ctx context.Context) error {
 			if cancelled {
 				break
 			}
-			cc.condWait()
+			cc.cond.Wait()
 		}
-	})
+	}()
 	shutdownEnterWaitStateHook()
 	select {
 	case <-done:
@@ -1155,7 +1142,7 @@ func (cc *ClientConn) Shutdown(ctx context.Context) error {
 		cc.mu.Lock()
 		// Free the goroutine above
 		cancelled = true
-		cc.condBroadcast()
+		cc.cond.Broadcast()
 		cc.mu.Unlock()
 		return ctx.Err()
 	}
@@ -1193,7 +1180,7 @@ func (cc *ClientConn) closeForError(err error) {
 	for _, cs := range cc.streams {
 		cs.abortStreamLocked(err)
 	}
-	cc.condBroadcast()
+	cc.cond.Broadcast()
 	cc.mu.Unlock()
 	cc.closeConn()
 }
@@ -1308,23 +1295,30 @@ func (cc *ClientConn) roundTrip(req *http.Request, streamf func(*clientStream))
 		respHeaderRecv:       make(chan struct{}),
 		donec:                make(chan struct{}),
 	}
-	cc.goRun(func() {
-		cs.doRequest(req)
-	})
+
+	// TODO(bradfitz): this is a copy of the logic in net/http. Unify somewhere?
+	if !cc.t.disableCompression() &&
+		req.Header.Get("Accept-Encoding") == "" &&
+		req.Header.Get("Range") == "" &&
+		!cs.isHead {
+		// Request gzip only, not deflate. Deflate is ambiguous and
+		// not as universally supported anyway.
+		// See: https://zlib.net/zlib_faq.html#faq39
+		//
+		// Note that we don't request this for HEAD requests,
+		// due to a bug in nginx:
+		//   http://trac.nginx.org/nginx/ticket/358
+		//   https://golang.org/issue/5522
+		//
+		// We don't request gzip if the request is for a range, since
+		// auto-decoding a portion of a gzipped document will just fail
+		// anyway. See https://golang.org/issue/8923
+		cs.requestedGzip = true
+	}
+
+	go cs.doRequest(req, streamf)
 
 	waitDone := func() error {
-		if cc.syncHooks != nil {
-			cc.syncHooks.blockUntil(func() bool {
-				select {
-				case <-cs.donec:
-				case <-ctx.Done():
-				case <-cs.reqCancel:
-				default:
-					return false
-				}
-				return true
-			})
-		}
 		select {
 		case <-cs.donec:
 			return nil
@@ -1385,24 +1379,7 @@ func (cc *ClientConn) roundTrip(req *http.Request, streamf func(*clientStream))
 		return err
 	}
 
-	if streamf != nil {
-		streamf(cs)
-	}
-
 	for {
-		if cc.syncHooks != nil {
-			cc.syncHooks.blockUntil(func() bool {
-				select {
-				case <-cs.respHeaderRecv:
-				case <-cs.abort:
-				case <-ctx.Done():
-				case <-cs.reqCancel:
-				default:
-					return false
-				}
-				return true
-			})
-		}
 		select {
 		case <-cs.respHeaderRecv:
 			return handleResponseHeaders()
@@ -1432,8 +1409,9 @@ func (cc *ClientConn) roundTrip(req *http.Request, streamf func(*clientStream))
 // doRequest runs for the duration of the request lifetime.
 //
 // It sends the request and performs post-request cleanup (closing Request.Body, etc.).
-func (cs *clientStream) doRequest(req *http.Request) {
-	err := cs.writeRequest(req)
+func (cs *clientStream) doRequest(req *http.Request, streamf func(*clientStream)) {
+	cs.cc.t.markNewGoroutine()
+	err := cs.writeRequest(req, streamf)
 	cs.cleanupWriteRequest(err)
 }
 
@@ -1444,7 +1422,7 @@ func (cs *clientStream) doRequest(req *http.Request) {
 //
 // It returns non-nil if the request ends otherwise.
 // If the returned error is StreamError, the error Code may be used in resetting the stream.
-func (cs *clientStream) writeRequest(req *http.Request) (err error) {
+func (cs *clientStream) writeRequest(req *http.Request, streamf func(*clientStream)) (err error) {
 	cc := cs.cc
 	ctx := cs.ctx
 
@@ -1458,21 +1436,6 @@ func (cs *clientStream) writeRequest(req *http.Request) (err error) {
 	if cc.reqHeaderMu == nil {
 		panic("RoundTrip on uninitialized ClientConn") // for tests
 	}
-	var newStreamHook func(*clientStream)
-	if cc.syncHooks != nil {
-		newStreamHook = cc.syncHooks.newstream
-		cc.syncHooks.blockUntil(func() bool {
-			select {
-			case cc.reqHeaderMu <- struct{}{}:
-				<-cc.reqHeaderMu
-			case <-cs.reqCancel:
-			case <-ctx.Done():
-			default:
-				return false
-			}
-			return true
-		})
-	}
 	select {
 	case cc.reqHeaderMu <- struct{}{}:
 	case <-cs.reqCancel:
@@ -1497,28 +1460,8 @@ func (cs *clientStream) writeRequest(req *http.Request) (err error) {
 	}
 	cc.mu.Unlock()
 
-	if newStreamHook != nil {
-		newStreamHook(cs)
-	}
-
-	// TODO(bradfitz): this is a copy of the logic in net/http. Unify somewhere?
-	if !cc.t.disableCompression() &&
-		req.Header.Get("Accept-Encoding") == "" &&
-		req.Header.Get("Range") == "" &&
-		!cs.isHead {
-		// Request gzip only, not deflate. Deflate is ambiguous and
-		// not as universally supported anyway.
-		// See: https://zlib.net/zlib_faq.html#faq39
-		//
-		// Note that we don't request this for HEAD requests,
-		// due to a bug in nginx:
-		//   http://trac.nginx.org/nginx/ticket/358
-		//   https://golang.org/issue/5522
-		//
-		// We don't request gzip if the request is for a range, since
-		// auto-decoding a portion of a gzipped document will just fail
-		// anyway. See https://golang.org/issue/8923
-		cs.requestedGzip = true
+	if streamf != nil {
+		streamf(cs)
 	}
 
 	continueTimeout := cc.t.expectContinueTimeout()
@@ -1581,7 +1524,7 @@ func (cs *clientStream) writeRequest(req *http.Request) (err error) {
 	var respHeaderTimer <-chan time.Time
 	var respHeaderRecv chan struct{}
 	if d := cc.responseHeaderTimeout(); d != 0 {
-		timer := cc.newTimer(d)
+		timer := cc.t.newTimer(d)
 		defer timer.Stop()
 		respHeaderTimer = timer.C()
 		respHeaderRecv = cs.respHeaderRecv
@@ -1590,21 +1533,6 @@ func (cs *clientStream) writeRequest(req *http.Request) (err error) {
 	// or until the request is aborted (via context, error, or otherwise),
 	// whichever comes first.
 	for {
-		if cc.syncHooks != nil {
-			cc.syncHooks.blockUntil(func() bool {
-				select {
-				case <-cs.peerClosed:
-				case <-respHeaderTimer:
-				case <-respHeaderRecv:
-				case <-cs.abort:
-				case <-ctx.Done():
-				case <-cs.reqCancel:
-				default:
-					return false
-				}
-				return true
-			})
-		}
 		select {
 		case <-cs.peerClosed:
 			return nil
@@ -1753,7 +1681,7 @@ func (cc *ClientConn) awaitOpenSlotForStreamLocked(cs *clientStream) error {
 			return nil
 		}
 		cc.pendingRequests++
-		cc.condWait()
+		cc.cond.Wait()
 		cc.pendingRequests--
 		select {
 		case <-cs.abort:
@@ -2015,7 +1943,7 @@ func (cs *clientStream) awaitFlowControl(maxBytes int) (taken int32, err error)
 			cs.flow.take(take)
 			return take, nil
 		}
-		cc.condWait()
+		cc.cond.Wait()
 	}
 }
 
@@ -2298,7 +2226,7 @@ func (cc *ClientConn) forgetStreamID(id uint32) {
 	}
 	// Wake up writeRequestBody via clientStream.awaitFlowControl and
 	// wake up RoundTrip if there is a pending request.
-	cc.condBroadcast()
+	cc.cond.Broadcast()
 
 	closeOnIdle := cc.singleUse || cc.doNotReuse || cc.t.disableKeepAlives() || cc.goAway != nil
 	if closeOnIdle && cc.streamsReserved == 0 && len(cc.streams) == 0 {
@@ -2320,6 +2248,7 @@ type clientConnReadLoop struct {
 
 // readLoop runs in its own goroutine and reads and dispatches frames.
 func (cc *ClientConn) readLoop() {
+	cc.t.markNewGoroutine()
 	rl := &clientConnReadLoop{cc: cc}
 	defer rl.cleanup()
 	cc.readerErr = rl.run()
@@ -2386,7 +2315,7 @@ func (rl *clientConnReadLoop) cleanup() {
 			cs.abortStreamLocked(err)
 		}
 	}
-	cc.condBroadcast()
+	cc.cond.Broadcast()
 	cc.mu.Unlock()
 }
 
@@ -2423,7 +2352,7 @@ func (rl *clientConnReadLoop) run() error {
 	readIdleTimeout := cc.t.ReadIdleTimeout
 	var t timer
 	if readIdleTimeout != 0 {
-		t = cc.afterFunc(readIdleTimeout, cc.healthCheck)
+		t = cc.t.afterFunc(readIdleTimeout, cc.healthCheck)
 	}
 	for {
 		f, err := cc.fr.ReadFrame()
@@ -3021,7 +2950,7 @@ func (rl *clientConnReadLoop) processSettingsNoWrite(f *SettingsFrame) error {
 			for _, cs := range cc.streams {
 				cs.flow.add(delta)
 			}
-			cc.condBroadcast()
+			cc.cond.Broadcast()
 
 			cc.initialWindowSize = s.Val
 		case SettingHeaderTableSize:
@@ -3076,7 +3005,7 @@ func (rl *clientConnReadLoop) processWindowUpdate(f *WindowUpdateFrame) error {
 
 		return ConnectionError(ErrCodeFlowControl)
 	}
-	cc.condBroadcast()
+	cc.cond.Broadcast()
 	return nil
 }
 
@@ -3120,7 +3049,8 @@ func (cc *ClientConn) Ping(ctx context.Context) error {
 	}
 	var pingError error
 	errc := make(chan struct{})
-	cc.goRun(func() {
+	go func() {
+		cc.t.markNewGoroutine()
 		cc.wmu.Lock()
 		defer cc.wmu.Unlock()
 		if pingError = cc.fr.WritePing(false, p); pingError != nil {
@@ -3131,20 +3061,7 @@ func (cc *ClientConn) Ping(ctx context.Context) error {
 			close(errc)
 			return
 		}
-	})
-	if cc.syncHooks != nil {
-		cc.syncHooks.blockUntil(func() bool {
-			select {
-			case <-c:
-			case <-errc:
-			case <-ctx.Done():
-			case <-cc.readerDone:
-			default:
-				return false
-			}
-			return true
-		})
-	}
+	}()
 	select {
 	case <-c:
 		return nil
diff --git a/vendor/golang.org/x/net/http2/writesched_priority.go b/vendor/golang.org/x/net/http2/writesched_priority.go
index 0a242c66..f6783339 100644
--- a/vendor/golang.org/x/net/http2/writesched_priority.go
+++ b/vendor/golang.org/x/net/http2/writesched_priority.go
@@ -443,8 +443,8 @@ func (ws *priorityWriteScheduler) addClosedOrIdleNode(list *[]*priorityNode, max
 }
 
 func (ws *priorityWriteScheduler) removeNode(n *priorityNode) {
-	for k := n.kids; k != nil; k = k.next {
-		k.setParent(n.parent)
+	for n.kids != nil {
+		n.kids.setParent(n.parent)
 	}
 	n.setParent(nil)
 	delete(ws.nodes, n.id)
diff --git a/vendor/golang.org/x/sys/unix/mkerrors.sh b/vendor/golang.org/x/sys/unix/mkerrors.sh
index fdcaa974..4ed2e488 100644
--- a/vendor/golang.org/x/sys/unix/mkerrors.sh
+++ b/vendor/golang.org/x/sys/unix/mkerrors.sh
@@ -263,6 +263,7 @@ struct ltchars {
 #include <linux/sched.h>
 #include <linux/seccomp.h>
 #include <linux/serial.h>
+#include <linux/sock_diag.h>
 #include <linux/sockios.h>
 #include <linux/taskstats.h>
 #include <linux/tipc.h>
@@ -549,6 +550,7 @@ ccflags="$@"
 		$2 !~ "NLA_TYPE_MASK" &&
 		$2 !~ /^RTC_VL_(ACCURACY|BACKUP|DATA)/ &&
 		$2 ~ /^(NETLINK|NLM|NLMSG|NLA|IFA|IFAN|RT|RTC|RTCF|RTN|RTPROT|RTNH|ARPHRD|ETH_P|NETNSA)_/ ||
+		$2 ~ /^SOCK_|SK_DIAG_|SKNLGRP_$/ ||
 		$2 ~ /^FIORDCHK$/ ||
 		$2 ~ /^SIOC/ ||
 		$2 ~ /^TIOC/ ||
diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux.go b/vendor/golang.org/x/sys/unix/zerrors_linux.go
index 93a38a97..877a62b4 100644
--- a/vendor/golang.org/x/sys/unix/zerrors_linux.go
+++ b/vendor/golang.org/x/sys/unix/zerrors_linux.go
@@ -502,6 +502,7 @@ const (
 	BPF_IMM                                     = 0x0
 	BPF_IND                                     = 0x40
 	BPF_JA                                      = 0x0
+	BPF_JCOND                                   = 0xe0
 	BPF_JEQ                                     = 0x10
 	BPF_JGE                                     = 0x30
 	BPF_JGT                                     = 0x20
@@ -657,6 +658,9 @@ const (
 	CAN_NPROTO                                  = 0x8
 	CAN_RAW                                     = 0x1
 	CAN_RAW_FILTER_MAX                          = 0x200
+	CAN_RAW_XL_VCID_RX_FILTER                   = 0x4
+	CAN_RAW_XL_VCID_TX_PASS                     = 0x2
+	CAN_RAW_XL_VCID_TX_SET                      = 0x1
 	CAN_RTR_FLAG                                = 0x40000000
 	CAN_SFF_ID_BITS                             = 0xb
 	CAN_SFF_MASK                                = 0x7ff
@@ -1339,6 +1343,7 @@ const (
 	F_OFD_SETLK                                 = 0x25
 	F_OFD_SETLKW                                = 0x26
 	F_OK                                        = 0x0
+	F_SEAL_EXEC                                 = 0x20
 	F_SEAL_FUTURE_WRITE                         = 0x10
 	F_SEAL_GROW                                 = 0x4
 	F_SEAL_SEAL                                 = 0x1
@@ -1627,6 +1632,7 @@ const (
 	IP_FREEBIND                                 = 0xf
 	IP_HDRINCL                                  = 0x3
 	IP_IPSEC_POLICY                             = 0x10
+	IP_LOCAL_PORT_RANGE                         = 0x33
 	IP_MAXPACKET                                = 0xffff
 	IP_MAX_MEMBERSHIPS                          = 0x14
 	IP_MF                                       = 0x2000
@@ -1653,6 +1659,7 @@ const (
 	IP_PMTUDISC_OMIT                            = 0x5
 	IP_PMTUDISC_PROBE                           = 0x3
 	IP_PMTUDISC_WANT                            = 0x1
+	IP_PROTOCOL                                 = 0x34
 	IP_RECVERR                                  = 0xb
 	IP_RECVERR_RFC4884                          = 0x1a
 	IP_RECVFRAGSIZE                             = 0x19
@@ -2169,7 +2176,7 @@ const (
 	NFT_SECMARK_CTX_MAXLEN                      = 0x100
 	NFT_SET_MAXNAMELEN                          = 0x100
 	NFT_SOCKET_MAX                              = 0x3
-	NFT_TABLE_F_MASK                            = 0x3
+	NFT_TABLE_F_MASK                            = 0x7
 	NFT_TABLE_MAXNAMELEN                        = 0x100
 	NFT_TRACETYPE_MAX                           = 0x3
 	NFT_TUNNEL_F_MASK                           = 0x7
@@ -2403,6 +2410,7 @@ const (
 	PERF_RECORD_MISC_USER                       = 0x2
 	PERF_SAMPLE_BRANCH_PLM_ALL                  = 0x7
 	PERF_SAMPLE_WEIGHT_TYPE                     = 0x1004000
+	PID_FS_MAGIC                                = 0x50494446
 	PIPEFS_MAGIC                                = 0x50495045
 	PPPIOCGNPMODE                               = 0xc008744c
 	PPPIOCNEWUNIT                               = 0xc004743e
@@ -2896,8 +2904,9 @@ const (
 	RWF_APPEND                                  = 0x10
 	RWF_DSYNC                                   = 0x2
 	RWF_HIPRI                                   = 0x1
+	RWF_NOAPPEND                                = 0x20
 	RWF_NOWAIT                                  = 0x8
-	RWF_SUPPORTED                               = 0x1f
+	RWF_SUPPORTED                               = 0x3f
 	RWF_SYNC                                    = 0x4
 	RWF_WRITE_LIFE_NOT_SET                      = 0x0
 	SCHED_BATCH                                 = 0x3
@@ -2918,7 +2927,9 @@ const (
 	SCHED_RESET_ON_FORK                         = 0x40000000
 	SCHED_RR                                    = 0x2
 	SCM_CREDENTIALS                             = 0x2
+	SCM_PIDFD                                   = 0x4
 	SCM_RIGHTS                                  = 0x1
+	SCM_SECURITY                                = 0x3
 	SCM_TIMESTAMP                               = 0x1d
 	SC_LOG_FLUSH                                = 0x100000
 	SECCOMP_ADDFD_FLAG_SEND                     = 0x2
@@ -3051,6 +3062,8 @@ const (
 	SIOCSMIIREG                                 = 0x8949
 	SIOCSRARP                                   = 0x8962
 	SIOCWANDEV                                  = 0x894a
+	SK_DIAG_BPF_STORAGE_MAX                     = 0x3
+	SK_DIAG_BPF_STORAGE_REQ_MAX                 = 0x1
 	SMACK_MAGIC                                 = 0x43415d53
 	SMART_AUTOSAVE                              = 0xd2
 	SMART_AUTO_OFFLINE                          = 0xdb
@@ -3071,6 +3084,8 @@ const (
 	SOCKFS_MAGIC                                = 0x534f434b
 	SOCK_BUF_LOCK_MASK                          = 0x3
 	SOCK_DCCP                                   = 0x6
+	SOCK_DESTROY                                = 0x15
+	SOCK_DIAG_BY_FAMILY                         = 0x14
 	SOCK_IOC_TYPE                               = 0x89
 	SOCK_PACKET                                 = 0xa
 	SOCK_RAW                                    = 0x3
@@ -3260,6 +3275,7 @@ const (
 	TCP_MAX_WINSHIFT                            = 0xe
 	TCP_MD5SIG                                  = 0xe
 	TCP_MD5SIG_EXT                              = 0x20
+	TCP_MD5SIG_FLAG_IFINDEX                     = 0x2
 	TCP_MD5SIG_FLAG_PREFIX                      = 0x1
 	TCP_MD5SIG_MAXKEYLEN                        = 0x50
 	TCP_MSS                                     = 0x200
diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_386.go b/vendor/golang.org/x/sys/unix/zerrors_linux_386.go
index 42ff8c3c..e4bc0bd5 100644
--- a/vendor/golang.org/x/sys/unix/zerrors_linux_386.go
+++ b/vendor/golang.org/x/sys/unix/zerrors_linux_386.go
@@ -118,6 +118,7 @@ const (
 	IXOFF                            = 0x1000
 	IXON                             = 0x400
 	MAP_32BIT                        = 0x40
+	MAP_ABOVE4G                      = 0x80
 	MAP_ANON                         = 0x20
 	MAP_ANONYMOUS                    = 0x20
 	MAP_DENYWRITE                    = 0x800
diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go
index dca43600..689317af 100644
--- a/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go
+++ b/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go
@@ -118,6 +118,7 @@ const (
 	IXOFF                            = 0x1000
 	IXON                             = 0x400
 	MAP_32BIT                        = 0x40
+	MAP_ABOVE4G                      = 0x80
 	MAP_ANON                         = 0x20
 	MAP_ANONYMOUS                    = 0x20
 	MAP_DENYWRITE                    = 0x800
diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go
index d8cae6d1..14270508 100644
--- a/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go
+++ b/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go
@@ -87,6 +87,7 @@ const (
 	FICLONE                          = 0x40049409
 	FICLONERANGE                     = 0x4020940d
 	FLUSHO                           = 0x1000
+	FPMR_MAGIC                       = 0x46504d52
 	FPSIMD_MAGIC                     = 0x46508001
 	FS_IOC_ENABLE_VERITY             = 0x40806685
 	FS_IOC_GETFLAGS                  = 0x80086601
diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux.go b/vendor/golang.org/x/sys/unix/ztypes_linux.go
index 0036746e..4740b834 100644
--- a/vendor/golang.org/x/sys/unix/ztypes_linux.go
+++ b/vendor/golang.org/x/sys/unix/ztypes_linux.go
@@ -4605,7 +4605,7 @@ const (
 	NL80211_ATTR_MAC_HINT                                   = 0xc8
 	NL80211_ATTR_MAC_MASK                                   = 0xd7
 	NL80211_ATTR_MAX_AP_ASSOC_STA                           = 0xca
-	NL80211_ATTR_MAX                                        = 0x149
+	NL80211_ATTR_MAX                                        = 0x14a
 	NL80211_ATTR_MAX_CRIT_PROT_DURATION                     = 0xb4
 	NL80211_ATTR_MAX_CSA_COUNTERS                           = 0xce
 	NL80211_ATTR_MAX_MATCH_SETS                             = 0x85
@@ -5209,7 +5209,7 @@ const (
 	NL80211_FREQUENCY_ATTR_GO_CONCURRENT                    = 0xf
 	NL80211_FREQUENCY_ATTR_INDOOR_ONLY                      = 0xe
 	NL80211_FREQUENCY_ATTR_IR_CONCURRENT                    = 0xf
-	NL80211_FREQUENCY_ATTR_MAX                              = 0x1f
+	NL80211_FREQUENCY_ATTR_MAX                              = 0x20
 	NL80211_FREQUENCY_ATTR_MAX_TX_POWER                     = 0x6
 	NL80211_FREQUENCY_ATTR_NO_10MHZ                         = 0x11
 	NL80211_FREQUENCY_ATTR_NO_160MHZ                        = 0xc
@@ -5703,7 +5703,7 @@ const (
 	NL80211_STA_FLAG_ASSOCIATED                             = 0x7
 	NL80211_STA_FLAG_AUTHENTICATED                          = 0x5
 	NL80211_STA_FLAG_AUTHORIZED                             = 0x1
-	NL80211_STA_FLAG_MAX                                    = 0x7
+	NL80211_STA_FLAG_MAX                                    = 0x8
 	NL80211_STA_FLAG_MAX_OLD_API                            = 0x6
 	NL80211_STA_FLAG_MFP                                    = 0x4
 	NL80211_STA_FLAG_SHORT_PREAMBLE                         = 0x2
@@ -6001,3 +6001,34 @@ type CachestatRange struct {
 	Off uint64
 	Len uint64
 }
+
+const (
+	SK_MEMINFO_RMEM_ALLOC          = 0x0
+	SK_MEMINFO_RCVBUF              = 0x1
+	SK_MEMINFO_WMEM_ALLOC          = 0x2
+	SK_MEMINFO_SNDBUF              = 0x3
+	SK_MEMINFO_FWD_ALLOC           = 0x4
+	SK_MEMINFO_WMEM_QUEUED         = 0x5
+	SK_MEMINFO_OPTMEM              = 0x6
+	SK_MEMINFO_BACKLOG             = 0x7
+	SK_MEMINFO_DROPS               = 0x8
+	SK_MEMINFO_VARS                = 0x9
+	SKNLGRP_NONE                   = 0x0
+	SKNLGRP_INET_TCP_DESTROY       = 0x1
+	SKNLGRP_INET_UDP_DESTROY       = 0x2
+	SKNLGRP_INET6_TCP_DESTROY      = 0x3
+	SKNLGRP_INET6_UDP_DESTROY      = 0x4
+	SK_DIAG_BPF_STORAGE_REQ_NONE   = 0x0
+	SK_DIAG_BPF_STORAGE_REQ_MAP_FD = 0x1
+	SK_DIAG_BPF_STORAGE_REP_NONE   = 0x0
+	SK_DIAG_BPF_STORAGE            = 0x1
+	SK_DIAG_BPF_STORAGE_NONE       = 0x0
+	SK_DIAG_BPF_STORAGE_PAD        = 0x1
+	SK_DIAG_BPF_STORAGE_MAP_ID     = 0x2
+	SK_DIAG_BPF_STORAGE_MAP_VALUE  = 0x3
+)
+
+type SockDiagReq struct {
+	Family   uint8
+	Protocol uint8
+}
diff --git a/vendor/golang.org/x/sys/windows/security_windows.go b/vendor/golang.org/x/sys/windows/security_windows.go
index 26be94a8..6f7d2ac7 100644
--- a/vendor/golang.org/x/sys/windows/security_windows.go
+++ b/vendor/golang.org/x/sys/windows/security_windows.go
@@ -68,6 +68,7 @@ type UserInfo10 struct {
 //sys	NetUserGetInfo(serverName *uint16, userName *uint16, level uint32, buf **byte) (neterr error) = netapi32.NetUserGetInfo
 //sys	NetGetJoinInformation(server *uint16, name **uint16, bufType *uint32) (neterr error) = netapi32.NetGetJoinInformation
 //sys	NetApiBufferFree(buf *byte) (neterr error) = netapi32.NetApiBufferFree
+//sys   NetUserEnum(serverName *uint16, level uint32, filter uint32, buf **byte, prefMaxLen uint32, entriesRead *uint32, totalEntries *uint32, resumeHandle *uint32) (neterr error) = netapi32.NetUserEnum
 
 const (
 	// do not reorder
diff --git a/vendor/golang.org/x/sys/windows/zsyscall_windows.go b/vendor/golang.org/x/sys/windows/zsyscall_windows.go
index 5c6035dd..9f73df75 100644
--- a/vendor/golang.org/x/sys/windows/zsyscall_windows.go
+++ b/vendor/golang.org/x/sys/windows/zsyscall_windows.go
@@ -401,6 +401,7 @@ var (
 	procTransmitFile                                         = modmswsock.NewProc("TransmitFile")
 	procNetApiBufferFree                                     = modnetapi32.NewProc("NetApiBufferFree")
 	procNetGetJoinInformation                                = modnetapi32.NewProc("NetGetJoinInformation")
+	procNetUserEnum                                          = modnetapi32.NewProc("NetUserEnum")
 	procNetUserGetInfo                                       = modnetapi32.NewProc("NetUserGetInfo")
 	procNtCreateFile                                         = modntdll.NewProc("NtCreateFile")
 	procNtCreateNamedPipeFile                                = modntdll.NewProc("NtCreateNamedPipeFile")
@@ -3486,6 +3487,14 @@ func NetGetJoinInformation(server *uint16, name **uint16, bufType *uint32) (nete
 	return
 }
 
+func NetUserEnum(serverName *uint16, level uint32, filter uint32, buf **byte, prefMaxLen uint32, entriesRead *uint32, totalEntries *uint32, resumeHandle *uint32) (neterr error) {
+	r0, _, _ := syscall.Syscall9(procNetUserEnum.Addr(), 8, uintptr(unsafe.Pointer(serverName)), uintptr(level), uintptr(filter), uintptr(unsafe.Pointer(buf)), uintptr(prefMaxLen), uintptr(unsafe.Pointer(entriesRead)), uintptr(unsafe.Pointer(totalEntries)), uintptr(unsafe.Pointer(resumeHandle)), 0)
+	if r0 != 0 {
+		neterr = syscall.Errno(r0)
+	}
+	return
+}
+
 func NetUserGetInfo(serverName *uint16, userName *uint16, level uint32, buf **byte) (neterr error) {
 	r0, _, _ := syscall.Syscall6(procNetUserGetInfo.Addr(), 4, uintptr(unsafe.Pointer(serverName)), uintptr(unsafe.Pointer(userName)), uintptr(level), uintptr(unsafe.Pointer(buf)), 0, 0)
 	if r0 != 0 {
diff --git a/vendor/golang.org/x/text/message/message.go b/vendor/golang.org/x/text/message/message.go
index 48d76630..91a97264 100644
--- a/vendor/golang.org/x/text/message/message.go
+++ b/vendor/golang.org/x/text/message/message.go
@@ -138,21 +138,20 @@ func (p *Printer) Printf(key Reference, a ...interface{}) (n int, err error) {
 
 func lookupAndFormat(p *printer, r Reference, a []interface{}) {
 	p.fmt.Reset(a)
-	var id, msg string
 	switch v := r.(type) {
 	case string:
-		id, msg = v, v
+		if p.catContext.Execute(v) == catalog.ErrNotFound {
+			p.Render(v)
+			return
+		}
 	case key:
-		id, msg = v.id, v.fallback
-	default:
-		panic("key argument is not a Reference")
-	}
-
-	if p.catContext.Execute(id) == catalog.ErrNotFound {
-		if p.catContext.Execute(msg) == catalog.ErrNotFound {
-			p.Render(msg)
+		if p.catContext.Execute(v.id) == catalog.ErrNotFound &&
+			p.catContext.Execute(v.fallback) == catalog.ErrNotFound {
+			p.Render(v.fallback)
 			return
 		}
+	default:
+		panic("key argument is not a Reference")
 	}
 }
 
diff --git a/vendor/k8s.io/utils/trace/trace.go b/vendor/k8s.io/utils/trace/trace.go
index 187eb5d8..559aebb5 100644
--- a/vendor/k8s.io/utils/trace/trace.go
+++ b/vendor/k8s.io/utils/trace/trace.go
@@ -192,7 +192,7 @@ func (t *Trace) Log() {
 	t.endTime = &endTime
 	t.lock.Unlock()
 	// an explicit logging request should dump all the steps out at the higher level
-	if t.parentTrace == nil { // We don't start logging until Log or LogIfLong is called on the root trace
+	if t.parentTrace == nil && klogV(2) { // We don't start logging until Log or LogIfLong is called on the root trace
 		t.logTrace()
 	}
 }
diff --git a/vendor/modules.txt b/vendor/modules.txt
index daaec55f..481acd3e 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -246,7 +246,7 @@ github.com/hashicorp/hcl/hcl/token
 github.com/hashicorp/hcl/json/parser
 github.com/hashicorp/hcl/json/scanner
 github.com/hashicorp/hcl/json/token
-# github.com/hetznercloud/hcloud-go/v2 v2.8.0
+# github.com/hetznercloud/hcloud-go/v2 v2.9.0
 ## explicit; go 1.21
 github.com/hetznercloud/hcloud-go/v2/hcloud
 github.com/hetznercloud/hcloud-go/v2/hcloud/internal/instrumentation
@@ -295,7 +295,7 @@ github.com/modern-go/reflect2
 # github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822
 ## explicit
 github.com/munnerz/goautoneg
-# github.com/onsi/ginkgo/v2 v2.17.2
+# github.com/onsi/ginkgo/v2 v2.19.0
 ## explicit; go 1.20
 github.com/onsi/ginkgo/v2
 github.com/onsi/ginkgo/v2/config
@@ -352,7 +352,7 @@ github.com/pkg/errors
 # github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2
 ## explicit
 github.com/pmezard/go-difflib/difflib
-# github.com/prometheus/client_golang v1.19.0
+# github.com/prometheus/client_golang v1.19.1
 ## explicit; go 1.20
 github.com/prometheus/client_golang/prometheus
 github.com/prometheus/client_golang/prometheus/collectors
@@ -412,7 +412,7 @@ github.com/spf13/viper/internal/encoding/json
 github.com/spf13/viper/internal/encoding/toml
 github.com/spf13/viper/internal/encoding/yaml
 github.com/spf13/viper/internal/features
-# github.com/stoewer/go-strcase v1.2.0
+# github.com/stoewer/go-strcase v1.3.0
 ## explicit; go 1.11
 github.com/stoewer/go-strcase
 # github.com/stretchr/objx v0.5.2
@@ -472,7 +472,7 @@ go.uber.org/zap/internal/exit
 go.uber.org/zap/internal/pool
 go.uber.org/zap/internal/stacktrace
 go.uber.org/zap/zapcore
-# golang.org/x/crypto v0.23.0
+# golang.org/x/crypto v0.24.0
 ## explicit; go 1.18
 golang.org/x/crypto/bcrypt
 golang.org/x/crypto/blowfish
@@ -488,7 +488,7 @@ golang.org/x/crypto/scrypt
 golang.org/x/crypto/sha3
 golang.org/x/crypto/ssh
 golang.org/x/crypto/ssh/internal/bcrypt_pbkdf
-# golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f
+# golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8
 ## explicit; go 1.20
 golang.org/x/exp/constraints
 golang.org/x/exp/maps
@@ -496,13 +496,13 @@ golang.org/x/exp/slices
 golang.org/x/exp/slog
 golang.org/x/exp/slog/internal
 golang.org/x/exp/slog/internal/buffer
-# golang.org/x/mod v0.17.0
+# golang.org/x/mod v0.18.0
 ## explicit; go 1.18
 golang.org/x/mod/internal/lazyregexp
 golang.org/x/mod/modfile
 golang.org/x/mod/module
 golang.org/x/mod/semver
-# golang.org/x/net v0.24.0
+# golang.org/x/net v0.26.0
 ## explicit; go 1.18
 golang.org/x/net/context
 golang.org/x/net/html
@@ -519,16 +519,16 @@ golang.org/x/oauth2/internal
 # golang.org/x/sync v0.7.0
 ## explicit; go 1.18
 golang.org/x/sync/singleflight
-# golang.org/x/sys v0.20.0
+# golang.org/x/sys v0.21.0
 ## explicit; go 1.18
 golang.org/x/sys/cpu
 golang.org/x/sys/plan9
 golang.org/x/sys/unix
 golang.org/x/sys/windows
-# golang.org/x/term v0.20.0
+# golang.org/x/term v0.21.0
 ## explicit; go 1.18
 golang.org/x/term
-# golang.org/x/text v0.15.0
+# golang.org/x/text v0.16.0
 ## explicit; go 1.18
 golang.org/x/text/cases
 golang.org/x/text/encoding
@@ -563,7 +563,7 @@ golang.org/x/text/width
 # golang.org/x/time v0.5.0
 ## explicit; go 1.18
 golang.org/x/time/rate
-# golang.org/x/tools v0.20.0
+# golang.org/x/tools v0.22.0
 ## explicit; go 1.19
 golang.org/x/tools/cover
 golang.org/x/tools/go/ast/inspector
@@ -1086,7 +1086,7 @@ k8s.io/kube-openapi/pkg/validation/validate
 # k8s.io/kubectl v0.29.3
 ## explicit; go 1.21
 k8s.io/kubectl/pkg/scheme
-# k8s.io/utils v0.0.0-20240310230437-4693a0247e57
+# k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0
 ## explicit; go 1.18
 k8s.io/utils/buffer
 k8s.io/utils/clock
@@ -1249,8 +1249,8 @@ sigs.k8s.io/controller-runtime/pkg/webhook/internal/metrics
 ## explicit; go 1.18
 sigs.k8s.io/json
 sigs.k8s.io/json/internal/golang/encoding/json
-# sigs.k8s.io/kind v0.22.0
-## explicit; go 1.16
+# sigs.k8s.io/kind v0.23.0
+## explicit; go 1.17
 sigs.k8s.io/kind/pkg/apis/config/defaults
 sigs.k8s.io/kind/pkg/apis/config/v1alpha4
 sigs.k8s.io/kind/pkg/cluster
@@ -1273,6 +1273,7 @@ sigs.k8s.io/kind/pkg/cluster/internal/logs
 sigs.k8s.io/kind/pkg/cluster/internal/providers
 sigs.k8s.io/kind/pkg/cluster/internal/providers/common
 sigs.k8s.io/kind/pkg/cluster/internal/providers/docker
+sigs.k8s.io/kind/pkg/cluster/internal/providers/nerdctl
 sigs.k8s.io/kind/pkg/cluster/internal/providers/podman
 sigs.k8s.io/kind/pkg/cluster/nodes
 sigs.k8s.io/kind/pkg/cluster/nodeutils
diff --git a/vendor/sigs.k8s.io/kind/pkg/apis/config/defaults/image.go b/vendor/sigs.k8s.io/kind/pkg/apis/config/defaults/image.go
index da3864fa..9d31a9dd 100644
--- a/vendor/sigs.k8s.io/kind/pkg/apis/config/defaults/image.go
+++ b/vendor/sigs.k8s.io/kind/pkg/apis/config/defaults/image.go
@@ -18,4 +18,4 @@ limitations under the License.
 package defaults
 
 // Image is the default for the Config.Image field, aka the default node image.
-const Image = "kindest/node:v1.29.2@sha256:51a1434a5397193442f0be2a297b488b6c919ce8a3931be0ce822606ea5ca245"
+const Image = "kindest/node:v1.30.0@sha256:047357ac0cfea04663786a612ba1eaba9702bef25227a794b52890dd8bcd692e"
diff --git a/vendor/sigs.k8s.io/kind/pkg/apis/config/v1alpha4/types.go b/vendor/sigs.k8s.io/kind/pkg/apis/config/v1alpha4/types.go
index 308a6853..33acf503 100644
--- a/vendor/sigs.k8s.io/kind/pkg/apis/config/v1alpha4/types.go
+++ b/vendor/sigs.k8s.io/kind/pkg/apis/config/v1alpha4/types.go
@@ -186,7 +186,7 @@ type Networking struct {
 	// If DisableDefaultCNI is true, kind will not install the default CNI setup.
 	// Instead the user should install their own CNI after creating the cluster.
 	DisableDefaultCNI bool `yaml:"disableDefaultCNI,omitempty" json:"disableDefaultCNI,omitempty"`
-	// KubeProxyMode defines if kube-proxy should operate in iptables or ipvs mode
+	// KubeProxyMode defines if kube-proxy should operate in iptables, ipvs or nftables mode
 	// Defaults to 'iptables' mode
 	KubeProxyMode ProxyMode `yaml:"kubeProxyMode,omitempty" json:"kubeProxyMode,omitempty"`
 	// DNSSearch defines the DNS search domain to use for nodes. If not set, this will be inherited from the host.
@@ -213,6 +213,8 @@ const (
 	IPTablesProxyMode ProxyMode = "iptables"
 	// IPVSProxyMode sets ProxyMode to ipvs
 	IPVSProxyMode ProxyMode = "ipvs"
+	// NFTablesProxyMode sets ProxyMode to nftables
+	NFTablesProxyMode ProxyMode = "nftables"
 )
 
 // PatchJSON6902 represents an inline kustomize json 6902 patch
diff --git a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/kubeadm/config.go b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/kubeadm/config.go
index 6aa17581..bd431c52 100644
--- a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/kubeadm/config.go
+++ b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/kubeadm/config.go
@@ -57,7 +57,7 @@ type ConfigData struct {
 	// The Token for TLS bootstrap
 	Token string
 
-	// KubeProxyMode defines the kube-proxy mode between iptables or ipvs
+	// KubeProxyMode defines the kube-proxy mode between iptables, ipvs or nftables
 	KubeProxyMode string
 	// The subnet used for pods
 	PodSubnet string
@@ -285,7 +285,7 @@ evictionHard:
 {{ range $index, $gate := .SortedFeatureGates }}
   "{{ (StructuralData $gate.Name) }}": {{ $gate.Value }}
 {{end}}{{end}}
-{{if ne .KubeProxyMode "None"}}
+{{if ne .KubeProxyMode "none"}}
 ---
 apiVersion: kubeproxy.config.k8s.io/v1alpha1
 kind: KubeProxyConfiguration
@@ -302,6 +302,12 @@ conntrack:
 # Skip setting sysctl value "net.netfilter.nf_conntrack_max"
 # It is a global variable that affects other namespaces
   maxPerCore: 0
+# Set sysctl value "net.netfilter.nf_conntrack_tcp_be_liberal"
+# for nftables proxy (theoretically for kernels older than 6.1)
+# xref: https://github.com/kubernetes/kubernetes/issues/117924
+{{if and (eq .KubeProxyMode "nftables") (not .RootlessProvider)}}
+  tcpBeLiberal: true
+{{end}}
 {{if .RootlessProvider}}
 # Skip setting "net.netfilter.nf_conntrack_tcp_timeout_established"
   tcpEstablishedTimeout: 0s
@@ -423,7 +429,7 @@ evictionHard:
   "{{ (StructuralData $gate.Name) }}": {{ $gate.Value }}
 {{end}}{{end}}
 {{if .DisableLocalStorageCapacityIsolation}}localStorageCapacityIsolation: false{{end}}
-{{if ne .KubeProxyMode "None"}}
+{{if ne .KubeProxyMode "none"}}
 ---
 apiVersion: kubeproxy.config.k8s.io/v1alpha1
 kind: KubeProxyConfiguration
@@ -440,6 +446,12 @@ conntrack:
 # Skip setting sysctl value "net.netfilter.nf_conntrack_max"
 # It is a global variable that affects other namespaces
   maxPerCore: 0
+# Set sysctl value "net.netfilter.nf_conntrack_tcp_be_liberal"
+# for nftables proxy (theoretically for kernels older than 6.1)
+# xref: https://github.com/kubernetes/kubernetes/issues/117924
+{{if and (eq .KubeProxyMode "nftables") (not .RootlessProvider)}}
+  tcpBeLiberal: true
+{{end}}
 {{if .RootlessProvider}}
 # Skip setting "net.netfilter.nf_conntrack_tcp_timeout_established"
   tcpEstablishedTimeout: 0s
diff --git a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/docker/provision.go b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/docker/provision.go
index 3a6e1a70..b2d6bbea 100644
--- a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/docker/provision.go
+++ b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/docker/provision.go
@@ -403,10 +403,7 @@ func generatePortMappings(clusterIPFamily config.ClusterIPFamily, portMappings .
 }
 
 func createContainer(name string, args []string) error {
-	if err := exec.Command("docker", append([]string{"run", "--name", name}, args...)...).Run(); err != nil {
-		return err
-	}
-	return nil
+	return exec.Command("docker", append([]string{"run", "--name", name}, args...)...).Run()
 }
 
 func createContainerWithWaitUntilSystemdReachesMultiUserSystem(name string, args []string) error {
diff --git a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/nerdctl/OWNERS b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/nerdctl/OWNERS
new file mode 100644
index 00000000..71a8b4d7
--- /dev/null
+++ b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/nerdctl/OWNERS
@@ -0,0 +1,2 @@
+labels:
+- area/provider/nerdctl
diff --git a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/nerdctl/constants.go b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/nerdctl/constants.go
new file mode 100644
index 00000000..6b77abb0
--- /dev/null
+++ b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/nerdctl/constants.go
@@ -0,0 +1,24 @@
+/*
+Copyright 2019 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impliep.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package nerdctl
+
+// clusterLabelKey is applied to each "node" container for identification
+const clusterLabelKey = "io.x-k8s.kind.cluster"
+
+// nodeRoleLabelKey is applied to each "node" container for categorization
+// of nodes by role
+const nodeRoleLabelKey = "io.x-k8s.kind.role"
diff --git a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/nerdctl/images.go b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/nerdctl/images.go
new file mode 100644
index 00000000..c090138f
--- /dev/null
+++ b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/nerdctl/images.go
@@ -0,0 +1,91 @@
+/*
+Copyright 2019 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package nerdctl
+
+import (
+	"fmt"
+	"strings"
+	"time"
+
+	"sigs.k8s.io/kind/pkg/errors"
+	"sigs.k8s.io/kind/pkg/exec"
+	"sigs.k8s.io/kind/pkg/log"
+
+	"sigs.k8s.io/kind/pkg/cluster/internal/providers/common"
+	"sigs.k8s.io/kind/pkg/internal/apis/config"
+	"sigs.k8s.io/kind/pkg/internal/cli"
+)
+
+// ensureNodeImages ensures that the node images used by the create
+// configuration are present
+func ensureNodeImages(logger log.Logger, status *cli.Status, cfg *config.Cluster, binaryName string) error {
+	// pull each required image
+	for _, image := range common.RequiredNodeImages(cfg).List() {
+		// prints user friendly message
+		friendlyImageName, image := sanitizeImage(image)
+		status.Start(fmt.Sprintf("Ensuring node image (%s) 🖼", friendlyImageName))
+		if _, err := pullIfNotPresent(logger, image, 4, binaryName); err != nil {
+			status.End(false)
+			return err
+		}
+	}
+	return nil
+}
+
+// pullIfNotPresent will pull an image if it is not present locally
+// retrying up to retries times
+// it returns true if it attempted to pull, and any errors from pulling
+func pullIfNotPresent(logger log.Logger, image string, retries int, binaryName string) (pulled bool, err error) {
+	// TODO(bentheelder): switch most (all) of the logging here to debug level
+	// once we have configurable log levels
+	// if this did not return an error, then the image exists locally
+	cmd := exec.Command(binaryName, "inspect", "--type=image", image)
+	if err := cmd.Run(); err == nil {
+		logger.V(1).Infof("Image: %s present locally", image)
+		return false, nil
+	}
+	// otherwise try to pull it
+	return true, pull(logger, image, retries, binaryName)
+}
+
+// pull pulls an image, retrying up to retries times
+func pull(logger log.Logger, image string, retries int, binaryName string) error {
+	logger.V(1).Infof("Pulling image: %s ...", image)
+	err := exec.Command(binaryName, "pull", image).Run()
+	// retry pulling up to retries times if necessary
+	if err != nil {
+		for i := 0; i < retries; i++ {
+			time.Sleep(time.Second * time.Duration(i+1))
+			logger.V(1).Infof("Trying again to pull image: %q ... %v", image, err)
+			// TODO(bentheelder): add some backoff / sleep?
+			err = exec.Command(binaryName, "pull", image).Run()
+			if err == nil {
+				break
+			}
+		}
+	}
+	return errors.Wrapf(err, "failed to pull image %q", image)
+}
+
+// sanitizeImage is a helper to return human readable image name and
+// the docker pullable image name from the provided image
+func sanitizeImage(image string) (string, string) {
+	if strings.Contains(image, "@sha256:") {
+		return strings.Split(image, "@sha256:")[0], image
+	}
+	return image, image
+}
diff --git a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/nerdctl/network.go b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/nerdctl/network.go
new file mode 100644
index 00000000..e9a1d21e
--- /dev/null
+++ b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/nerdctl/network.go
@@ -0,0 +1,187 @@
+/*
+Copyright 2020 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package nerdctl
+
+import (
+	"crypto/sha1"
+	"encoding/binary"
+	"fmt"
+	"net"
+	"strconv"
+	"strings"
+
+	"sigs.k8s.io/kind/pkg/errors"
+	"sigs.k8s.io/kind/pkg/exec"
+)
+
+// This may be overridden by KIND_EXPERIMENTAL_DOCKER_NETWORK env,
+// experimentally...
+//
+// By default currently picking a single network is equivalent to the previous
+// behavior *except* that we moved from the default bridge to a user defined
+// network because the default bridge is actually special versus any other
+// docker network and lacks the embedded DNS
+//
+// For now this also makes it easier for apps to join the same network, and
+// leaves users with complex networking desires to create and manage their own
+// networks.
+const fixedNetworkName = "kind"
+
+// ensureNetwork checks if docker network by name exists, if not it creates it
+func ensureNetwork(name, binaryName string) error {
+	// check if network exists already and remove any duplicate networks
+	exists, err := checkIfNetworkExists(name, binaryName)
+	if err != nil {
+		return err
+	}
+
+	// network already exists, we're good
+	// TODO: the network might already exist and not have ipv6 ... :|
+	// discussion: https://github.com/kubernetes-sigs/kind/pull/1508#discussion_r414594198
+	if exists {
+		return nil
+	}
+
+	subnet := generateULASubnetFromName(name, 0)
+	mtu := getDefaultNetworkMTU(binaryName)
+	err = createNetwork(name, subnet, mtu, binaryName)
+	if err == nil {
+		// Success!
+		return nil
+	}
+
+	// On the first try check if ipv6 fails entirely on this machine
+	// https://github.com/kubernetes-sigs/kind/issues/1544
+	// Otherwise if it's not a pool overlap error, fail
+	// If it is, make more attempts below
+	if isIPv6UnavailableError(err) {
+		// only one attempt, IPAM is automatic in ipv4 only
+		return createNetwork(name, "", mtu, binaryName)
+	}
+	if isPoolOverlapError(err) {
+		// pool overlap suggests perhaps another process created the network
+		// check if network exists already and remove any duplicate networks
+		exists, err := checkIfNetworkExists(name, binaryName)
+		if err != nil {
+			return err
+		}
+		if exists {
+			return nil
+		}
+		// otherwise we'll start trying with different subnets
+	} else {
+		// unknown error ...
+		return err
+	}
+
+	// keep trying for ipv6 subnets
+	const maxAttempts = 5
+	for attempt := int32(1); attempt < maxAttempts; attempt++ {
+		subnet := generateULASubnetFromName(name, attempt)
+		err = createNetwork(name, subnet, mtu, binaryName)
+		if err == nil {
+			// success!
+			return nil
+		}
+		if isPoolOverlapError(err) {
+			// pool overlap suggests perhaps another process created the network
+			// check if network exists already and remove any duplicate networks
+			exists, err := checkIfNetworkExists(name, binaryName)
+			if err != nil {
+				return err
+			}
+			if exists {
+				return nil
+			}
+			// otherwise we'll try again
+			continue
+		}
+		// unknown error ...
+		return err
+	}
+	return errors.New("exhausted attempts trying to find a non-overlapping subnet")
+}
+
+func createNetwork(name, ipv6Subnet string, mtu int, binaryName string) error {
+	args := []string{"network", "create", "-d=bridge"}
+	// TODO: Not supported in nerdctl yet
+	//	"-o", "com.docker.network.bridge.enable_ip_masquerade=true",
+	if mtu > 0 {
+		args = append(args, "-o", fmt.Sprintf("com.docker.network.driver.mtu=%d", mtu))
+	}
+	if ipv6Subnet != "" {
+		args = append(args, "--ipv6", "--subnet", ipv6Subnet)
+	}
+	args = append(args, name)
+	return exec.Command(binaryName, args...).Run()
+}
+
+// getDefaultNetworkMTU obtains the MTU from the docker default network
+func getDefaultNetworkMTU(binaryName string) int {
+	cmd := exec.Command(binaryName, "network", "inspect", "bridge",
+		"-f", `{{ index .Options "com.docker.network.driver.mtu" }}`)
+	lines, err := exec.OutputLines(cmd)
+	if err != nil || len(lines) != 1 {
+		return 0
+	}
+	mtu, err := strconv.Atoi(lines[0])
+	if err != nil {
+		return 0
+	}
+	return mtu
+}
+
+func checkIfNetworkExists(name, binaryName string) (bool, error) {
+	out, err := exec.Output(exec.Command(
+		binaryName, "network", "inspect",
+		name, "--format={{.Name}}",
+	))
+	if err != nil {
+		return false, nil
+	}
+	return strings.HasPrefix(string(out), name), err
+}
+
+func isIPv6UnavailableError(err error) bool {
+	rerr := exec.RunErrorForError(err)
+	return rerr != nil && strings.HasPrefix(string(rerr.Output), "Error response from daemon: Cannot read IPv6 setup for bridge")
+}
+
+func isPoolOverlapError(err error) bool {
+	rerr := exec.RunErrorForError(err)
+	return rerr != nil && strings.HasPrefix(string(rerr.Output), "Error response from daemon: Pool overlaps with other one on this address space") || strings.Contains(string(rerr.Output), "networks have overlapping")
+}
+
+// generateULASubnetFromName generate an IPv6 subnet based on the
+// name and Nth probing attempt
+func generateULASubnetFromName(name string, attempt int32) string {
+	ip := make([]byte, 16)
+	ip[0] = 0xfc
+	ip[1] = 0x00
+	h := sha1.New()
+	_, _ = h.Write([]byte(name))
+	_ = binary.Write(h, binary.LittleEndian, attempt)
+	bs := h.Sum(nil)
+	for i := 2; i < 8; i++ {
+		ip[i] = bs[i]
+	}
+	subnet := &net.IPNet{
+		IP:   net.IP(ip),
+		Mask: net.CIDRMask(64, 128),
+	}
+	return subnet.String()
+}
diff --git a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/nerdctl/node.go b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/nerdctl/node.go
new file mode 100644
index 00000000..a6a64e5a
--- /dev/null
+++ b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/nerdctl/node.go
@@ -0,0 +1,175 @@
+/*
+Copyright 2019 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impliep.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package nerdctl
+
+import (
+	"context"
+	"fmt"
+	"io"
+	"strings"
+
+	"sigs.k8s.io/kind/pkg/errors"
+	"sigs.k8s.io/kind/pkg/exec"
+)
+
+// nodes.Node implementation for the docker provider
+type node struct {
+	name       string
+	binaryName string
+}
+
+func (n *node) String() string {
+	return n.name
+}
+
+func (n *node) Role() (string, error) {
+	cmd := exec.Command(n.binaryName, "inspect",
+		"--format", fmt.Sprintf(`{{ index .Config.Labels "%s"}}`, nodeRoleLabelKey),
+		n.name,
+	)
+	lines, err := exec.OutputLines(cmd)
+	if err != nil {
+		return "", errors.Wrap(err, "failed to get role for node")
+	}
+	if len(lines) != 1 {
+		return "", errors.Errorf("failed to get role for node: output lines %d != 1", len(lines))
+	}
+	return lines[0], nil
+}
+
+func (n *node) IP() (ipv4 string, ipv6 string, err error) {
+	// retrieve the IP address of the node using docker inspect
+	cmd := exec.Command(n.binaryName, "inspect",
+		"-f", "{{range .NetworkSettings.Networks}}{{.IPAddress}},{{.GlobalIPv6Address}}{{end}}",
+		n.name, // ... against the "node" container
+	)
+	lines, err := exec.OutputLines(cmd)
+	if err != nil {
+		return "", "", errors.Wrap(err, "failed to get container details")
+	}
+	if len(lines) != 1 {
+		return "", "", errors.Errorf("file should only be one line, got %d lines", len(lines))
+	}
+	ips := strings.Split(lines[0], ",")
+	if len(ips) != 2 {
+		return "", "", errors.Errorf("container addresses should have 2 values, got %d values", len(ips))
+	}
+	return ips[0], ips[1], nil
+}
+
+func (n *node) Command(command string, args ...string) exec.Cmd {
+	return &nodeCmd{
+		binaryName: n.binaryName,
+		nameOrID:   n.name,
+		command:    command,
+		args:       args,
+	}
+}
+
+func (n *node) CommandContext(ctx context.Context, command string, args ...string) exec.Cmd {
+	return &nodeCmd{
+		binaryName: n.binaryName,
+		nameOrID:   n.name,
+		command:    command,
+		args:       args,
+		ctx:        ctx,
+	}
+}
+
+// nodeCmd implements exec.Cmd for docker nodes
+type nodeCmd struct {
+	binaryName string
+	nameOrID   string // the container name or ID
+	command    string
+	args       []string
+	env        []string
+	stdin      io.Reader
+	stdout     io.Writer
+	stderr     io.Writer
+	ctx        context.Context
+}
+
+func (c *nodeCmd) Run() error {
+	args := []string{
+		"exec",
+		// run with privileges so we can remount etc..
+		// this might not make sense in the most general sense, but it is
+		// important to many kind commands
+		"--privileged",
+	}
+	if c.stdin != nil {
+		args = append(args,
+			"-i", // interactive so we can supply input
+		)
+	}
+	// set env
+	for _, env := range c.env {
+		args = append(args, "-e", env)
+	}
+	// specify the container and command, after this everything will be
+	// args the command in the container rather than to docker
+	args = append(
+		args,
+		c.nameOrID, // ... against the container
+		c.command,  // with the command specified
+	)
+	args = append(
+		args,
+		// finally, with the caller args
+		c.args...,
+	)
+	var cmd exec.Cmd
+	if c.ctx != nil {
+		cmd = exec.CommandContext(c.ctx, c.binaryName, args...)
+	} else {
+		cmd = exec.Command(c.binaryName, args...)
+	}
+	if c.stdin != nil {
+		cmd.SetStdin(c.stdin)
+	}
+	if c.stderr != nil {
+		cmd.SetStderr(c.stderr)
+	}
+	if c.stdout != nil {
+		cmd.SetStdout(c.stdout)
+	}
+	return cmd.Run()
+}
+
+func (c *nodeCmd) SetEnv(env ...string) exec.Cmd {
+	c.env = env
+	return c
+}
+
+func (c *nodeCmd) SetStdin(r io.Reader) exec.Cmd {
+	c.stdin = r
+	return c
+}
+
+func (c *nodeCmd) SetStdout(w io.Writer) exec.Cmd {
+	c.stdout = w
+	return c
+}
+
+func (c *nodeCmd) SetStderr(w io.Writer) exec.Cmd {
+	c.stderr = w
+	return c
+}
+
+func (n *node) SerialLogs(w io.Writer) error {
+	return exec.Command(n.binaryName, "logs", n.name).SetStdout(w).SetStderr(w).Run()
+}
diff --git a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/nerdctl/provider.go b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/nerdctl/provider.go
new file mode 100644
index 00000000..05bba6b1
--- /dev/null
+++ b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/nerdctl/provider.go
@@ -0,0 +1,392 @@
+/*
+Copyright 2019 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impliep.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package nerdctl
+
+import (
+	"encoding/csv"
+	"encoding/json"
+	"fmt"
+	"net"
+	osexec "os/exec"
+	"path/filepath"
+	"strings"
+
+	"sigs.k8s.io/kind/pkg/cluster/nodes"
+	"sigs.k8s.io/kind/pkg/errors"
+	"sigs.k8s.io/kind/pkg/exec"
+	"sigs.k8s.io/kind/pkg/log"
+
+	internallogs "sigs.k8s.io/kind/pkg/cluster/internal/logs"
+	"sigs.k8s.io/kind/pkg/cluster/internal/providers"
+	"sigs.k8s.io/kind/pkg/cluster/internal/providers/common"
+	"sigs.k8s.io/kind/pkg/cluster/nodeutils"
+	"sigs.k8s.io/kind/pkg/internal/apis/config"
+	"sigs.k8s.io/kind/pkg/internal/cli"
+	"sigs.k8s.io/kind/pkg/internal/sets"
+)
+
+// NewProvider returns a new provider based on executing `nerdctl ...`
+func NewProvider(logger log.Logger, binaryName string) providers.Provider {
+	// if binaryName is unset, do a lookup; we may be here via a
+	// library call to provider.DetectNodeProvider(), which returns
+	// true from nerdctl.IsAvailable() by checking for both finch
+	// and nerdctl. If we don't redo the lookup here, then a finch
+	// install that triggered IsAvailable() to be true would fail
+	// to be used if we default to nerdctl when unset.
+	if binaryName == "" {
+		// default to "nerdctl"; but look for "finch" if
+		// nerctl binary lookup fails
+		binaryName = "nerdctl"
+		if _, err := osexec.LookPath("nerdctl"); err != nil {
+			if _, err := osexec.LookPath("finch"); err == nil {
+				binaryName = "finch"
+			}
+		}
+	}
+	return &provider{
+		logger:     logger,
+		binaryName: binaryName,
+	}
+}
+
+// Provider implements provider.Provider
+// see NewProvider
+type provider struct {
+	logger     log.Logger
+	binaryName string
+	info       *providers.ProviderInfo
+}
+
+// String implements fmt.Stringer
+// NOTE: the value of this should not currently be relied upon for anything!
+// This is only used for setting the Node's providerID
+func (p *provider) String() string {
+	return "nerdctl"
+}
+
+func (p *provider) Binary() string {
+	return p.binaryName
+}
+
+// Provision is part of the providers.Provider interface
+func (p *provider) Provision(status *cli.Status, cfg *config.Cluster) (err error) {
+	// TODO: validate cfg
+	// ensure node images are pulled before actually provisioning
+	if err := ensureNodeImages(p.logger, status, cfg, p.Binary()); err != nil {
+		return err
+	}
+
+	// ensure the pre-requisite network exists
+	if err := ensureNetwork(fixedNetworkName, p.Binary()); err != nil {
+		return errors.Wrap(err, "failed to ensure nerdctl network")
+	}
+
+	// actually provision the cluster
+	icons := strings.Repeat("📦 ", len(cfg.Nodes))
+	status.Start(fmt.Sprintf("Preparing nodes %s", icons))
+	defer func() { status.End(err == nil) }()
+
+	// plan creating the containers
+	createContainerFuncs, err := planCreation(cfg, fixedNetworkName, p.Binary())
+	if err != nil {
+		return err
+	}
+
+	// actually create nodes
+	// TODO: remove once nerdctl handles concurrency better
+	// xref: https://github.com/containerd/nerdctl/issues/2908
+	for _, f := range createContainerFuncs {
+		if err := f(); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+// ListClusters is part of the providers.Provider interface
+func (p *provider) ListClusters() ([]string, error) {
+	cmd := exec.Command(p.Binary(),
+		"ps",
+		"-a", // show stopped nodes
+		// filter for nodes with the cluster label
+		"--filter", "label="+clusterLabelKey,
+		// format to include the cluster name
+		"--format", fmt.Sprintf(`{{index .Labels "%s"}}`, clusterLabelKey),
+	)
+	lines, err := exec.OutputLines(cmd)
+	if err != nil {
+		return nil, errors.Wrap(err, "failed to list clusters")
+	}
+	return sets.NewString(lines...).List(), nil
+}
+
+// ListNodes is part of the providers.Provider interface
+func (p *provider) ListNodes(cluster string) ([]nodes.Node, error) {
+	cmd := exec.Command(p.Binary(),
+		"ps",
+		"-a", // show stopped nodes
+		// filter for nodes with the cluster label
+		"--filter", fmt.Sprintf("label=%s=%s", clusterLabelKey, cluster),
+		// format to include the cluster name
+		"--format", `{{.Names}}`,
+	)
+	lines, err := exec.OutputLines(cmd)
+	if err != nil {
+		return nil, errors.Wrap(err, "failed to list nodes")
+	}
+	length := len(lines)
+	// convert names to node handles
+	ret := make([]nodes.Node, 0, length)
+	for _, name := range lines {
+		if name != "" {
+			ret = append(ret, p.node(name))
+		}
+	}
+	return ret, nil
+}
+
+// DeleteNodes is part of the providers.Provider interface
+func (p *provider) DeleteNodes(n []nodes.Node) error {
+	if len(n) == 0 {
+		return nil
+	}
+	argsNoRestart := make([]string, 0, len(n)+2)
+	argsNoRestart = append(argsNoRestart,
+		"update",
+		"--restart=no",
+	)
+	argsStop := make([]string, 0, len(n)+1)
+	argsStop = append(argsStop, "stop")
+	argsWait := make([]string, 0, len(n)+1)
+	argsWait = append(argsWait, "wait")
+
+	argsRm := make([]string, 0, len(n)+3) // allocate once
+	argsRm = append(argsRm,
+		"rm",
+		"-f",
+		"-v", // delete volumes
+	)
+	for _, node := range n {
+		argsRm = append(argsRm, node.String())
+		argsStop = append(argsStop, node.String())
+		argsWait = append(argsWait, node.String())
+		argsNoRestart = append(argsNoRestart, node.String())
+	}
+	if err := exec.Command(p.Binary(), argsNoRestart...).Run(); err != nil {
+		return errors.Wrap(err, "failed to update restart policy to 'no'")
+	}
+	if err := exec.Command(p.Binary(), argsStop...).Run(); err != nil {
+		return errors.Wrap(err, "failed to stop nodes")
+	}
+	if err := exec.Command(p.Binary(), argsWait...).Run(); err != nil {
+		return errors.Wrap(err, "failed to wait for node exit")
+	}
+	if err := exec.Command(p.Binary(), argsRm...).Run(); err != nil {
+		return errors.Wrap(err, "failed to delete nodes")
+	}
+	return nil
+}
+
+// GetAPIServerEndpoint is part of the providers.Provider interface
+func (p *provider) GetAPIServerEndpoint(cluster string) (string, error) {
+	// locate the node that hosts this
+	allNodes, err := p.ListNodes(cluster)
+	if err != nil {
+		return "", errors.Wrap(err, "failed to list nodes")
+	}
+	n, err := nodeutils.APIServerEndpointNode(allNodes)
+	if err != nil {
+		return "", errors.Wrap(err, "failed to get api server endpoint")
+	}
+
+	// if the 'desktop.docker.io/ports/<PORT>/tcp' label is present,
+	// defer to its value for the api server endpoint
+	//
+	// For example:
+	// "Labels": {
+	// 	"desktop.docker.io/ports/6443/tcp": "10.0.1.7:6443",
+	// }
+	cmd := exec.Command(
+		p.Binary(), "inspect",
+		"--format", fmt.Sprintf(
+			"{{ index .Config.Labels \"desktop.docker.io/ports/%d/tcp\" }}", common.APIServerInternalPort,
+		),
+		n.String(),
+	)
+	lines, err := exec.OutputLines(cmd)
+	if err != nil {
+		return "", errors.Wrap(err, "failed to get api server port")
+	}
+	if len(lines) == 1 && lines[0] != "" {
+		return lines[0], nil
+	}
+
+	// else, retrieve the specific port mapping via NetworkSettings.Ports
+	cmd = exec.Command(
+		p.Binary(), "inspect",
+		"--format", fmt.Sprintf(
+			"{{ with (index (index .NetworkSettings.Ports \"%d/tcp\") 0) }}{{ printf \"%%s\t%%s\" .HostIp .HostPort }}{{ end }}", common.APIServerInternalPort,
+		),
+		n.String(),
+	)
+	lines, err = exec.OutputLines(cmd)
+	if err != nil {
+		return "", errors.Wrap(err, "failed to get api server port")
+	}
+	if len(lines) != 1 {
+		return "", errors.Errorf("network details should only be one line, got %d lines", len(lines))
+	}
+	parts := strings.Split(lines[0], "\t")
+	if len(parts) != 2 {
+		return "", errors.Errorf("network details should only be two parts, got %d", len(parts))
+	}
+
+	// join host and port
+	return net.JoinHostPort(parts[0], parts[1]), nil
+}
+
+// GetAPIServerInternalEndpoint is part of the providers.Provider interface
+func (p *provider) GetAPIServerInternalEndpoint(cluster string) (string, error) {
+	// locate the node that hosts this
+	allNodes, err := p.ListNodes(cluster)
+	if err != nil {
+		return "", errors.Wrap(err, "failed to list nodes")
+	}
+	n, err := nodeutils.APIServerEndpointNode(allNodes)
+	if err != nil {
+		return "", errors.Wrap(err, "failed to get api server endpoint")
+	}
+	// NOTE: we're using the nodes's hostnames which are their names
+	return net.JoinHostPort(n.String(), fmt.Sprintf("%d", common.APIServerInternalPort)), nil
+}
+
+// node returns a new node handle for this provider
+func (p *provider) node(name string) nodes.Node {
+	return &node{
+		binaryName: p.binaryName,
+		name:       name,
+	}
+}
+
+// CollectLogs will populate dir with cluster logs and other debug files
+func (p *provider) CollectLogs(dir string, nodes []nodes.Node) error {
+	execToPathFn := func(cmd exec.Cmd, path string) func() error {
+		return func() error {
+			f, err := common.FileOnHost(path)
+			if err != nil {
+				return err
+			}
+			defer f.Close()
+			return cmd.SetStdout(f).SetStderr(f).Run()
+		}
+	}
+	// construct a slice of methods to collect logs
+	fns := []func() error{
+		// record info about the host nerdctl
+		execToPathFn(
+			exec.Command(p.Binary(), "info"),
+			filepath.Join(dir, "docker-info.txt"),
+		),
+	}
+
+	// collect /var/log for each node and plan collecting more logs
+	var errs []error
+	for _, n := range nodes {
+		node := n // https://golang.org/doc/faq#closures_and_goroutines
+		name := node.String()
+		path := filepath.Join(dir, name)
+		if err := internallogs.DumpDir(p.logger, node, "/var/log", path); err != nil {
+			errs = append(errs, err)
+		}
+
+		fns = append(fns,
+			func() error { return common.CollectLogs(node, path) },
+			execToPathFn(exec.Command(p.Binary(), "inspect", name), filepath.Join(path, "inspect.json")),
+			func() error {
+				f, err := common.FileOnHost(filepath.Join(path, "serial.log"))
+				if err != nil {
+					return err
+				}
+				defer f.Close()
+				return node.SerialLogs(f)
+			},
+		)
+	}
+
+	// run and collect up all errors
+	errs = append(errs, errors.AggregateConcurrent(fns))
+	return errors.NewAggregate(errs)
+}
+
+// Info returns the provider info.
+// The info is cached on the first time of the execution.
+func (p *provider) Info() (*providers.ProviderInfo, error) {
+	var err error
+	if p.info == nil {
+		p.info, err = info(p.Binary())
+	}
+	return p.info, err
+}
+
+// dockerInfo corresponds to `docker info --format '{{json .}}'`
+type dockerInfo struct {
+	CgroupDriver    string   `json:"CgroupDriver"`  // "systemd", "cgroupfs", "none"
+	CgroupVersion   string   `json:"CgroupVersion"` // e.g. "2"
+	MemoryLimit     bool     `json:"MemoryLimit"`
+	PidsLimit       bool     `json:"PidsLimit"`
+	CPUShares       bool     `json:"CPUShares"`
+	SecurityOptions []string `json:"SecurityOptions"`
+}
+
+func info(binaryName string) (*providers.ProviderInfo, error) {
+	cmd := exec.Command(binaryName, "info", "--format", "{{json .}}")
+	out, err := exec.Output(cmd)
+	if err != nil {
+		return nil, errors.Wrap(err, "failed to get nerdctl info")
+	}
+	var dInfo dockerInfo
+	if err := json.Unmarshal(out, &dInfo); err != nil {
+		return nil, err
+	}
+	info := providers.ProviderInfo{
+		Cgroup2: dInfo.CgroupVersion == "2",
+	}
+	// When CgroupDriver == "none", the MemoryLimit/PidsLimit/CPUShares
+	// values are meaningless and need to be considered false.
+	// https://github.com/moby/moby/issues/42151
+	if dInfo.CgroupDriver != "none" {
+		info.SupportsMemoryLimit = dInfo.MemoryLimit
+		info.SupportsPidsLimit = dInfo.PidsLimit
+		info.SupportsCPUShares = dInfo.CPUShares
+	}
+	for _, o := range dInfo.SecurityOptions {
+		// o is like "name=seccomp,profile=default", or "name=rootless",
+		csvReader := csv.NewReader(strings.NewReader(o))
+		sliceSlice, err := csvReader.ReadAll()
+		if err != nil {
+			return nil, err
+		}
+		for _, f := range sliceSlice {
+			for _, ff := range f {
+				if ff == "name=rootless" {
+					info.Rootless = true
+				}
+			}
+		}
+	}
+	return &info, nil
+}
diff --git a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/nerdctl/provision.go b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/nerdctl/provision.go
new file mode 100644
index 00000000..d754b38d
--- /dev/null
+++ b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/nerdctl/provision.go
@@ -0,0 +1,388 @@
+/*
+Copyright 2019 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package nerdctl
+
+import (
+	"context"
+	"fmt"
+	"net"
+	"path/filepath"
+	"strings"
+	"time"
+
+	"sigs.k8s.io/kind/pkg/cluster/constants"
+	"sigs.k8s.io/kind/pkg/errors"
+	"sigs.k8s.io/kind/pkg/exec"
+	"sigs.k8s.io/kind/pkg/fs"
+
+	"sigs.k8s.io/kind/pkg/cluster/internal/loadbalancer"
+	"sigs.k8s.io/kind/pkg/cluster/internal/providers/common"
+	"sigs.k8s.io/kind/pkg/internal/apis/config"
+)
+
+// planCreation creates a slice of funcs that will create the containers
+func planCreation(cfg *config.Cluster, networkName, binaryName string) (createContainerFuncs []func() error, err error) {
+	// we need to know all the names for NO_PROXY
+	// compute the names first before any actual node details
+	nodeNamer := common.MakeNodeNamer(cfg.Name)
+	names := make([]string, len(cfg.Nodes))
+	for i, node := range cfg.Nodes {
+		name := nodeNamer(string(node.Role)) // name the node
+		names[i] = name
+	}
+	haveLoadbalancer := config.ClusterHasImplicitLoadBalancer(cfg)
+	if haveLoadbalancer {
+		names = append(names, nodeNamer(constants.ExternalLoadBalancerNodeRoleValue))
+	}
+
+	// these apply to all container creation
+	genericArgs, err := commonArgs(cfg.Name, cfg, networkName, names, binaryName)
+	if err != nil {
+		return nil, err
+	}
+
+	// only the external LB should reflect the port if we have multiple control planes
+	apiServerPort := cfg.Networking.APIServerPort
+	apiServerAddress := cfg.Networking.APIServerAddress
+	if haveLoadbalancer {
+		// TODO: picking ports locally is less than ideal with remote docker
+		// but this is supposed to be an implementation detail and NOT picking
+		// them breaks host reboot ...
+		// For now remote docker + multi control plane is not supported
+		apiServerPort = 0              // replaced with random ports
+		apiServerAddress = "127.0.0.1" // only the LB needs to be non-local
+		// only for IPv6 only clusters
+		if cfg.Networking.IPFamily == config.IPv6Family {
+			apiServerAddress = "::1" // only the LB needs to be non-local
+		}
+		// plan loadbalancer node
+		name := names[len(names)-1]
+		createContainerFuncs = append(createContainerFuncs, func() error {
+			args, err := runArgsForLoadBalancer(cfg, name, genericArgs)
+			if err != nil {
+				return err
+			}
+			return createContainer(name, args, binaryName)
+		})
+	}
+
+	// plan normal nodes
+	for i, node := range cfg.Nodes {
+		node := node.DeepCopy() // copy so we can modify
+		name := names[i]
+
+		// fixup relative paths, docker can only handle absolute paths
+		for m := range node.ExtraMounts {
+			hostPath := node.ExtraMounts[m].HostPath
+			if !fs.IsAbs(hostPath) {
+				absHostPath, err := filepath.Abs(hostPath)
+				if err != nil {
+					return nil, errors.Wrapf(err, "unable to resolve absolute path for hostPath: %q", hostPath)
+				}
+				node.ExtraMounts[m].HostPath = absHostPath
+			}
+		}
+
+		// plan actual creation based on role
+		switch node.Role {
+		case config.ControlPlaneRole:
+			createContainerFuncs = append(createContainerFuncs, func() error {
+				node.ExtraPortMappings = append(node.ExtraPortMappings,
+					config.PortMapping{
+						ListenAddress: apiServerAddress,
+						HostPort:      apiServerPort,
+						ContainerPort: common.APIServerInternalPort,
+					},
+				)
+				args, err := runArgsForNode(node, cfg.Networking.IPFamily, name, genericArgs)
+				if err != nil {
+					return err
+				}
+				return createContainerWithWaitUntilSystemdReachesMultiUserSystem(name, args, binaryName)
+			})
+		case config.WorkerRole:
+			createContainerFuncs = append(createContainerFuncs, func() error {
+				args, err := runArgsForNode(node, cfg.Networking.IPFamily, name, genericArgs)
+				if err != nil {
+					return err
+				}
+				return createContainerWithWaitUntilSystemdReachesMultiUserSystem(name, args, binaryName)
+			})
+		default:
+			return nil, errors.Errorf("unknown node role: %q", node.Role)
+		}
+	}
+	return createContainerFuncs, nil
+}
+
+// commonArgs computes static arguments that apply to all containers
+func commonArgs(cluster string, cfg *config.Cluster, networkName string, nodeNames []string, binaryName string) ([]string, error) {
+	// standard arguments all nodes containers need, computed once
+	args := []string{
+		"--detach", // run the container detached
+		"--tty",    // allocate a tty for entrypoint logs
+		// label the node with the cluster ID
+		"--label", fmt.Sprintf("%s=%s", clusterLabelKey, cluster),
+		// user a user defined network so we get embedded DNS
+		"--net", networkName,
+		// containerd supports the following restart modes:
+		// - no
+		// - on-failure[:max-retries]
+		// - unless-stopped
+		// - always
+		//
+		// What we desire is:
+		// - restart on host / container runtime reboot
+		// - don't restart for any other reason
+		//
+		"--restart=on-failure:1",
+		// this can be enabled by default in docker daemon.json, so we explicitly
+		// disable it, we want our entrypoint to be PID1, not docker-init / tini
+		"--init=false",
+	}
+
+	// enable IPv6 if necessary
+	if config.ClusterHasIPv6(cfg) {
+		args = append(args, "--sysctl=net.ipv6.conf.all.disable_ipv6=0", "--sysctl=net.ipv6.conf.all.forwarding=1")
+	}
+
+	// pass proxy environment variables
+	proxyEnv, err := getProxyEnv(cfg, networkName, nodeNames, binaryName)
+	if err != nil {
+		return nil, errors.Wrap(err, "proxy setup error")
+	}
+	for key, val := range proxyEnv {
+		args = append(args, "-e", fmt.Sprintf("%s=%s", key, val))
+	}
+
+	// enable /dev/fuse explicitly for fuse-overlayfs
+	// (Rootless Docker does not automatically mount /dev/fuse with --privileged)
+	if mountFuse(binaryName) {
+		args = append(args, "--device", "/dev/fuse")
+	}
+
+	if cfg.Networking.DNSSearch != nil {
+		args = append(args, "-e", "KIND_DNS_SEARCH="+strings.Join(*cfg.Networking.DNSSearch, " "))
+	}
+
+	return args, nil
+}
+
+func runArgsForNode(node *config.Node, clusterIPFamily config.ClusterIPFamily, name string, args []string) ([]string, error) {
+	args = append([]string{
+		"--hostname", name, // make hostname match container name
+		// label the node with the role ID
+		"--label", fmt.Sprintf("%s=%s", nodeRoleLabelKey, node.Role),
+		// running containers in a container requires privileged
+		// NOTE: we could try to replicate this with --cap-add, and use less
+		// privileges, but this flag also changes some mounts that are necessary
+		// including some ones docker would otherwise do by default.
+		// for now this is what we want. in the future we may revisit this.
+		"--privileged",
+		"--security-opt", "seccomp=unconfined", // also ignore seccomp
+		"--security-opt", "apparmor=unconfined", // also ignore apparmor
+		// runtime temporary storage
+		"--tmpfs", "/tmp", // various things depend on working /tmp
+		"--tmpfs", "/run", // systemd wants a writable /run
+		// runtime persistent storage
+		// this ensures that E.G. pods, logs etc. are not on the container
+		// filesystem, which is not only better for performance, but allows
+		// running kind in kind for "party tricks"
+		// (please don't depend on doing this though!)
+		"--volume", "/var",
+		// some k8s things want to read /lib/modules
+		"--volume", "/lib/modules:/lib/modules:ro",
+		// propagate KIND_EXPERIMENTAL_CONTAINERD_SNAPSHOTTER to the entrypoint script
+		"-e", "KIND_EXPERIMENTAL_CONTAINERD_SNAPSHOTTER",
+	},
+		args...,
+	)
+
+	// convert mounts and port mappings to container run args
+	args = append(args, generateMountBindings(node.ExtraMounts...)...)
+	mappingArgs, err := generatePortMappings(clusterIPFamily, node.ExtraPortMappings...)
+	if err != nil {
+		return nil, err
+	}
+	args = append(args, mappingArgs...)
+
+	switch node.Role {
+	case config.ControlPlaneRole:
+		args = append(args, "-e", "KUBECONFIG=/etc/kubernetes/admin.conf")
+	}
+
+	// finally, specify the image to run
+	return append(args, node.Image), nil
+}
+
+func runArgsForLoadBalancer(cfg *config.Cluster, name string, args []string) ([]string, error) {
+	args = append([]string{
+		"--hostname", name, // make hostname match container name
+		// label the node with the role ID
+		"--label", fmt.Sprintf("%s=%s", nodeRoleLabelKey, constants.ExternalLoadBalancerNodeRoleValue),
+	},
+		args...,
+	)
+
+	// load balancer port mapping
+	mappingArgs, err := generatePortMappings(cfg.Networking.IPFamily,
+		config.PortMapping{
+			ListenAddress: cfg.Networking.APIServerAddress,
+			HostPort:      cfg.Networking.APIServerPort,
+			ContainerPort: common.APIServerInternalPort,
+		},
+	)
+	if err != nil {
+		return nil, err
+	}
+	args = append(args, mappingArgs...)
+
+	// finally, specify the image to run
+	return append(args, loadbalancer.Image), nil
+}
+
+func getProxyEnv(cfg *config.Cluster, networkName string, nodeNames []string, binaryName string) (map[string]string, error) {
+	envs := common.GetProxyEnvs(cfg)
+	// Specifically add the docker network subnets to NO_PROXY if we are using a proxy
+	if len(envs) > 0 {
+		subnets, err := getSubnets(networkName, binaryName)
+		if err != nil {
+			return nil, err
+		}
+
+		noProxyList := append(subnets, envs[common.NOProxy])
+		noProxyList = append(noProxyList, nodeNames...)
+		// Add pod and service dns names to no_proxy to allow in cluster
+		// Note: this is best effort based on the default CoreDNS spec
+		// https://github.com/kubernetes/dns/blob/master/docs/specification.md
+		// Any user created pod/service hostnames, namespaces, custom DNS services
+		// are expected to be no-proxied by the user explicitly.
+		noProxyList = append(noProxyList, ".svc", ".svc.cluster", ".svc.cluster.local")
+		noProxyJoined := strings.Join(noProxyList, ",")
+		envs[common.NOProxy] = noProxyJoined
+		envs[strings.ToLower(common.NOProxy)] = noProxyJoined
+	}
+	return envs, nil
+}
+
+func getSubnets(networkName, binaryName string) ([]string, error) {
+	format := `{{range (index (index . "IPAM") "Config")}}{{index . "Subnet"}} {{end}}`
+	cmd := exec.Command(binaryName, "network", "inspect", "-f", format, networkName)
+	lines, err := exec.OutputLines(cmd)
+	if err != nil {
+		return nil, errors.Wrap(err, "failed to get subnets")
+	}
+	return strings.Split(strings.TrimSpace(lines[0]), " "), nil
+}
+
+// generateMountBindings converts the mount list to a list of args for docker
+// '<HostPath>:<ContainerPath>[:options]', where 'options'
+// is a comma-separated list of the following strings:
+// 'ro', if the path is read only
+// 'Z', if the volume requires SELinux relabeling
+func generateMountBindings(mounts ...config.Mount) []string {
+	args := make([]string, 0, len(mounts))
+	for _, m := range mounts {
+		bind := fmt.Sprintf("%s:%s", m.HostPath, m.ContainerPath)
+		var attrs []string
+		if m.Readonly {
+			attrs = append(attrs, "ro")
+		}
+		// Only request relabeling if the pod provides an SELinux context. If the pod
+		// does not provide an SELinux context relabeling will label the volume with
+		// the container's randomly allocated MCS label. This would restrict access
+		// to the volume to the container which mounts it first.
+		if m.SelinuxRelabel {
+			attrs = append(attrs, "Z")
+		}
+		switch m.Propagation {
+		case config.MountPropagationNone:
+			// noop, private is default
+		case config.MountPropagationBidirectional:
+			attrs = append(attrs, "rshared")
+		case config.MountPropagationHostToContainer:
+			attrs = append(attrs, "rslave")
+		default: // Falls back to "private"
+		}
+		if len(attrs) > 0 {
+			bind = fmt.Sprintf("%s:%s", bind, strings.Join(attrs, ","))
+		}
+		args = append(args, fmt.Sprintf("--volume=%s", bind))
+	}
+	return args
+}
+
+// generatePortMappings converts the portMappings list to a list of args for docker
+func generatePortMappings(clusterIPFamily config.ClusterIPFamily, portMappings ...config.PortMapping) ([]string, error) {
+	args := make([]string, 0, len(portMappings))
+	for _, pm := range portMappings {
+		// do provider internal defaulting
+		// in a future API revision we will handle this at the API level and remove this
+		if pm.ListenAddress == "" {
+			switch clusterIPFamily {
+			case config.IPv4Family, config.DualStackFamily:
+				pm.ListenAddress = "0.0.0.0" // this is the docker default anyhow
+			case config.IPv6Family:
+				pm.ListenAddress = "::"
+			default:
+				return nil, errors.Errorf("unknown cluster IP family: %v", clusterIPFamily)
+			}
+		}
+		if string(pm.Protocol) == "" {
+			pm.Protocol = config.PortMappingProtocolTCP // TCP is the default
+		}
+
+		// validate that the provider can handle this binding
+		switch pm.Protocol {
+		case config.PortMappingProtocolTCP:
+		case config.PortMappingProtocolUDP:
+		case config.PortMappingProtocolSCTP:
+		default:
+			return nil, errors.Errorf("unknown port mapping protocol: %v", pm.Protocol)
+		}
+
+		// get a random port if necessary (port = 0)
+		hostPort, releaseHostPortFn, err := common.PortOrGetFreePort(pm.HostPort, pm.ListenAddress)
+		if err != nil {
+			return nil, errors.Wrap(err, "failed to get random host port for port mapping")
+		}
+		if releaseHostPortFn != nil {
+			defer releaseHostPortFn()
+		}
+
+		// generate the actual mapping arg
+		protocol := string(pm.Protocol)
+		hostPortBinding := net.JoinHostPort(pm.ListenAddress, fmt.Sprintf("%d", hostPort))
+		args = append(args, fmt.Sprintf("--publish=%s:%d/%s", hostPortBinding, pm.ContainerPort, protocol))
+	}
+	return args, nil
+}
+
+func createContainer(name string, args []string, binaryName string) error {
+	return exec.Command(binaryName, append([]string{"run", "--name", name}, args...)...).Run()
+}
+
+func createContainerWithWaitUntilSystemdReachesMultiUserSystem(name string, args []string, binaryName string) error {
+	if err := exec.Command(binaryName, append([]string{"run", "--name", name}, args...)...).Run(); err != nil {
+		return err
+	}
+
+	logCtx, logCancel := context.WithTimeout(context.Background(), 30*time.Second)
+	logCmd := exec.CommandContext(logCtx, binaryName, "logs", "-f", name)
+	defer logCancel()
+	return common.WaitUntilLogRegexpMatches(logCtx, logCmd, common.NodeReachedCgroupsReadyRegexp())
+}
diff --git a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/nerdctl/util.go b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/nerdctl/util.go
new file mode 100644
index 00000000..6281bbf1
--- /dev/null
+++ b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/nerdctl/util.go
@@ -0,0 +1,52 @@
+/*
+Copyright 2019 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package nerdctl
+
+import (
+	"strings"
+
+	"sigs.k8s.io/kind/pkg/exec"
+)
+
+// IsAvailable checks if nerdctl (or finch) is available in the system
+func IsAvailable() bool {
+	cmd := exec.Command("nerdctl", "-v")
+	lines, err := exec.OutputLines(cmd)
+	if err != nil || len(lines) != 1 {
+		// check finch
+		cmd = exec.Command("finch", "-v")
+		lines, err = exec.OutputLines(cmd)
+		if err != nil || len(lines) != 1 {
+			return false
+		}
+		return strings.HasPrefix(lines[0], "finch version")
+	}
+	return strings.HasPrefix(lines[0], "nerdctl version")
+}
+
+// rootless: use fuse-overlayfs by default
+// https://github.com/kubernetes-sigs/kind/issues/2275
+func mountFuse(binaryName string) bool {
+	i, err := info(binaryName)
+	if err != nil {
+		return false
+	}
+	if i != nil && i.Rootless {
+		return true
+	}
+	return false
+}
diff --git a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/podman/provision.go b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/podman/provision.go
index 5ed1c6b2..5e15707d 100644
--- a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/podman/provision.go
+++ b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/podman/provision.go
@@ -421,10 +421,7 @@ func generatePortMappings(clusterIPFamily config.ClusterIPFamily, portMappings .
 }
 
 func createContainer(name string, args []string) error {
-	if err := exec.Command("podman", append([]string{"run", "--name", name}, args...)...).Run(); err != nil {
-		return err
-	}
-	return nil
+	return exec.Command("podman", append([]string{"run", "--name", name}, args...)...).Run()
 }
 
 func createContainerWithWaitUntilSystemdReachesMultiUserSystem(name string, args []string) error {
diff --git a/vendor/sigs.k8s.io/kind/pkg/cluster/provider.go b/vendor/sigs.k8s.io/kind/pkg/cluster/provider.go
index 3cff1747..f5c68e42 100644
--- a/vendor/sigs.k8s.io/kind/pkg/cluster/provider.go
+++ b/vendor/sigs.k8s.io/kind/pkg/cluster/provider.go
@@ -34,6 +34,7 @@ import (
 	"sigs.k8s.io/kind/pkg/cluster/internal/kubeconfig"
 	internalproviders "sigs.k8s.io/kind/pkg/cluster/internal/providers"
 	"sigs.k8s.io/kind/pkg/cluster/internal/providers/docker"
+	"sigs.k8s.io/kind/pkg/cluster/internal/providers/nerdctl"
 	"sigs.k8s.io/kind/pkg/cluster/internal/providers/podman"
 )
 
@@ -102,8 +103,8 @@ var NoNodeProviderDetectedError = errors.NewWithoutStack("failed to detect any s
 // Pass the returned ProviderOption to NewProvider to pass the auto-detect Docker
 // or Podman option explicitly (in the future there will be more options)
 //
-// NOTE: The kind *cli* also checks `KIND_EXPERIMENTAL_PROVIDER` for "podman" or
-// "docker" currently and does not auto-detect / respects this if set.
+// NOTE: The kind *cli* also checks `KIND_EXPERIMENTAL_PROVIDER` for "podman",
+// "nerctl" or "docker" currently and does not auto-detect / respects this if set.
 //
 // This will be replaced with some other mechanism in the future (likely when
 // podman support is GA), in the meantime though your tool may wish to match this.
@@ -115,6 +116,9 @@ func DetectNodeProvider() (ProviderOption, error) {
 	if docker.IsAvailable() {
 		return ProviderWithDocker(), nil
 	}
+	if nerdctl.IsAvailable() {
+		return ProviderWithNerdctl(""), nil
+	}
 	if podman.IsAvailable() {
 		return ProviderWithPodman(), nil
 	}
@@ -167,6 +171,13 @@ func ProviderWithPodman() ProviderOption {
 	})
 }
 
+// ProviderWithNerdctl configures the provider to use the nerdctl runtime
+func ProviderWithNerdctl(binaryName string) ProviderOption {
+	return providerRuntimeOption(func(p *Provider) {
+		p.provider = nerdctl.NewProvider(p.logger, binaryName)
+	})
+}
+
 // Create provisions and starts a kubernetes-in-docker cluster
 func (p *Provider) Create(name string, options ...CreateOption) error {
 	// apply options
diff --git a/vendor/sigs.k8s.io/kind/pkg/cmd/kind/version/version.go b/vendor/sigs.k8s.io/kind/pkg/cmd/kind/version/version.go
index d1e4b921..85f49fac 100644
--- a/vendor/sigs.k8s.io/kind/pkg/cmd/kind/version/version.go
+++ b/vendor/sigs.k8s.io/kind/pkg/cmd/kind/version/version.go
@@ -54,11 +54,11 @@ func DisplayVersion() string {
 }
 
 // versionCore is the core portion of the kind CLI version per Semantic Versioning 2.0.0
-const versionCore = "0.22.0"
+const versionCore = "0.23.0"
 
 // versionPreRelease is the base pre-release portion of the kind CLI version per
 // Semantic Versioning 2.0.0
-const versionPreRelease = ""
+var versionPreRelease = ""
 
 // gitCommitCount count the commits since the last release.
 // It is injected at build time.
diff --git a/vendor/sigs.k8s.io/kind/pkg/fs/fs.go b/vendor/sigs.k8s.io/kind/pkg/fs/fs.go
index 7fb4eae3..94764aa5 100644
--- a/vendor/sigs.k8s.io/kind/pkg/fs/fs.go
+++ b/vendor/sigs.k8s.io/kind/pkg/fs/fs.go
@@ -64,10 +64,10 @@ func Copy(src, dst string) error {
 		return err
 	}
 	// do real copy work
-	return copy(src, dst, info)
+	return copyWithSrcInfo(src, dst, info)
 }
 
-func copy(src, dst string, info os.FileInfo) error {
+func copyWithSrcInfo(src, dst string, info os.FileInfo) error {
 	if info.Mode()&os.ModeSymlink != 0 {
 		return copySymlink(src, dst)
 	}
@@ -128,7 +128,7 @@ func copySymlink(src, dst string) error {
 		return err
 	}
 	// copy the underlying contents
-	return copy(realSrc, dst, info)
+	return copyWithSrcInfo(realSrc, dst, info)
 }
 
 func copyDir(src, dst string, info os.FileInfo) error {
@@ -148,7 +148,7 @@ func copyDir(src, dst string, info os.FileInfo) error {
 		if err != nil {
 			return err
 		}
-		if err := copy(entrySrc, entryDst, fileInfo); err != nil {
+		if err := copyWithSrcInfo(entrySrc, entryDst, fileInfo); err != nil {
 			return err
 		}
 	}
diff --git a/vendor/sigs.k8s.io/kind/pkg/internal/apis/config/types.go b/vendor/sigs.k8s.io/kind/pkg/internal/apis/config/types.go
index fed30007..f93c0e7b 100644
--- a/vendor/sigs.k8s.io/kind/pkg/internal/apis/config/types.go
+++ b/vendor/sigs.k8s.io/kind/pkg/internal/apis/config/types.go
@@ -148,7 +148,7 @@ type Networking struct {
 	// If DisableDefaultCNI is true, kind will not install the default CNI setup.
 	// Instead the user should install their own CNI after creating the cluster.
 	DisableDefaultCNI bool
-	// KubeProxyMode defines if kube-proxy should operate in iptables or ipvs mode
+	// KubeProxyMode defines if kube-proxy should operate in iptables, ipvs or nftables mode
 	KubeProxyMode ProxyMode
 	// DNSSearch defines the DNS search domain to use for nodes. If not set, this will be inherited from the host.
 	DNSSearch *[]string
@@ -174,6 +174,8 @@ const (
 	IPTablesProxyMode ProxyMode = "iptables"
 	// IPVSProxyMode sets ProxyMode to ipvs
 	IPVSProxyMode ProxyMode = "ipvs"
+	// NFTablesProxyMode sets ProxyMode to nftables
+	NFTablesProxyMode ProxyMode = "nftables"
 	// NoneProxyMode disables kube-proxy
 	NoneProxyMode ProxyMode = "none"
 )
diff --git a/vendor/sigs.k8s.io/kind/pkg/internal/apis/config/validate.go b/vendor/sigs.k8s.io/kind/pkg/internal/apis/config/validate.go
index 2eabaac6..884e5a47 100644
--- a/vendor/sigs.k8s.io/kind/pkg/internal/apis/config/validate.go
+++ b/vendor/sigs.k8s.io/kind/pkg/internal/apis/config/validate.go
@@ -52,6 +52,11 @@ func (c *Cluster) Validate() error {
 		}
 	}
 
+	// ipFamily should be ipv4, ipv6, or dual
+	if c.Networking.IPFamily != IPv4Family && c.Networking.IPFamily != IPv6Family && c.Networking.IPFamily != DualStackFamily {
+		errs = append(errs, errors.Errorf("invalid ipFamily: %s", c.Networking.IPFamily))
+	}
+
 	// podSubnet should be a valid CIDR
 	if err := validateSubnets(c.Networking.PodSubnet, c.Networking.IPFamily); err != nil {
 		errs = append(errs, errors.Errorf("invalid pod subnet %v", err))
@@ -64,7 +69,7 @@ func (c *Cluster) Validate() error {
 
 	// KubeProxyMode should be iptables or ipvs
 	if c.Networking.KubeProxyMode != IPTablesProxyMode && c.Networking.KubeProxyMode != IPVSProxyMode &&
-		c.Networking.KubeProxyMode != NoneProxyMode {
+		c.Networking.KubeProxyMode != NoneProxyMode && c.Networking.KubeProxyMode != NFTablesProxyMode {
 		errs = append(errs, errors.Errorf("invalid kubeProxyMode: %s", c.Networking.KubeProxyMode))
 	}
 


Test patch:
diff --git a/.github/workflows/pr-e2e.yaml b/.github/workflows/pr-e2e.yaml
index 36402243..766992d1 100644
--- a/.github/workflows/pr-e2e.yaml
+++ b/.github/workflows/pr-e2e.yaml
@@ -29,7 +29,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - name: Checkout repository
-        uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
+        uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
         with:
           ref: ${{ github.event.pull_request.head.sha }}
           fetch-depth: 0
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 0771f788..720fa2d6 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -28,7 +28,7 @@ jobs:
     timeout-minutes: 10
     steps:
       - name: Checkout repository
-        uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
+        uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
       - name: Coverage result name
         id: name
         run: |
diff --git a/vendor/golang.org/x/net/http2/testsync.go b/vendor/golang.org/x/net/http2/testsync.go
deleted file mode 100644
index 61075bd1..00000000
--- a/vendor/golang.org/x/net/http2/testsync.go
+++ /dev/null
@@ -1,331 +0,0 @@
-// Copyright 2024 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-package http2
-
-import (
-	"context"
-	"sync"
-	"time"
-)
-
-// testSyncHooks coordinates goroutines in tests.
-//
-// For example, a call to ClientConn.RoundTrip involves several goroutines, including:
-//   - the goroutine running RoundTrip;
-//   - the clientStream.doRequest goroutine, which writes the request; and
-//   - the clientStream.readLoop goroutine, which reads the response.
-//
-// Using testSyncHooks, a test can start a RoundTrip and identify when all these goroutines
-// are blocked waiting for some condition such as reading the Request.Body or waiting for
-// flow control to become available.
-//
-// The testSyncHooks also manage timers and synthetic time in tests.
-// This permits us to, for example, start a request and cause it to time out waiting for
-// response headers without resorting to time.Sleep calls.
-type testSyncHooks struct {
-	// active/inactive act as a mutex and condition variable.
-	//
-	//  - neither chan contains a value: testSyncHooks is locked.
-	//  - active contains a value: unlocked, and at least one goroutine is not blocked
-	//  - inactive contains a value: unlocked, and all goroutines are blocked
-	active   chan struct{}
-	inactive chan struct{}
-
-	// goroutine counts
-	total    int                     // total goroutines
-	condwait map[*sync.Cond]int      // blocked in sync.Cond.Wait
-	blocked  []*testBlockedGoroutine // otherwise blocked
-
-	// fake time
-	now    time.Time
-	timers []*fakeTimer
-
-	// Transport testing: Report various events.
-	newclientconn func(*ClientConn)
-	newstream     func(*clientStream)
-}
-
-// testBlockedGoroutine is a blocked goroutine.
-type testBlockedGoroutine struct {
-	f  func() bool   // blocked until f returns true
-	ch chan struct{} // closed when unblocked
-}
-
-func newTestSyncHooks() *testSyncHooks {
-	h := &testSyncHooks{
-		active:   make(chan struct{}, 1),
-		inactive: make(chan struct{}, 1),
-		condwait: map[*sync.Cond]int{},
-	}
-	h.inactive <- struct{}{}
-	h.now = time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)
-	return h
-}
-
-// lock acquires the testSyncHooks mutex.
-func (h *testSyncHooks) lock() {
-	select {
-	case <-h.active:
-	case <-h.inactive:
-	}
-}
-
-// waitInactive waits for all goroutines to become inactive.
-func (h *testSyncHooks) waitInactive() {
-	for {
-		<-h.inactive
-		if !h.unlock() {
-			break
-		}
-	}
-}
-
-// unlock releases the testSyncHooks mutex.
-// It reports whether any goroutines are active.
-func (h *testSyncHooks) unlock() (active bool) {
-	// Look for a blocked goroutine which can be unblocked.
-	blocked := h.blocked[:0]
-	unblocked := false
-	for _, b := range h.blocked {
-		if !unblocked && b.f() {
-			unblocked = true
-			close(b.ch)
-		} else {
-			blocked = append(blocked, b)
-		}
-	}
-	h.blocked = blocked
-
-	// Count goroutines blocked on condition variables.
-	condwait := 0
-	for _, count := range h.condwait {
-		condwait += count
-	}
-
-	if h.total > condwait+len(blocked) {
-		h.active <- struct{}{}
-		return true
-	} else {
-		h.inactive <- struct{}{}
-		return false
-	}
-}
-
-// goRun starts a new goroutine.
-func (h *testSyncHooks) goRun(f func()) {
-	h.lock()
-	h.total++
-	h.unlock()
-	go func() {
-		defer func() {
-			h.lock()
-			h.total--
-			h.unlock()
-		}()
-		f()
-	}()
-}
-
-// blockUntil indicates that a goroutine is blocked waiting for some condition to become true.
-// It waits until f returns true before proceeding.
-//
-// Example usage:
-//
-//	h.blockUntil(func() bool {
-//		// Is the context done yet?
-//		select {
-//		case <-ctx.Done():
-//		default:
-//			return false
-//		}
-//		return true
-//	})
-//	// Wait for the context to become done.
-//	<-ctx.Done()
-//
-// The function f passed to blockUntil must be non-blocking and idempotent.
-func (h *testSyncHooks) blockUntil(f func() bool) {
-	if f() {
-		return
-	}
-	ch := make(chan struct{})
-	h.lock()
-	h.blocked = append(h.blocked, &testBlockedGoroutine{
-		f:  f,
-		ch: ch,
-	})
-	h.unlock()
-	<-ch
-}
-
-// broadcast is sync.Cond.Broadcast.
-func (h *testSyncHooks) condBroadcast(cond *sync.Cond) {
-	h.lock()
-	delete(h.condwait, cond)
-	h.unlock()
-	cond.Broadcast()
-}
-
-// broadcast is sync.Cond.Wait.
-func (h *testSyncHooks) condWait(cond *sync.Cond) {
-	h.lock()
-	h.condwait[cond]++
-	h.unlock()
-}
-
-// newTimer creates a new fake timer.
-func (h *testSyncHooks) newTimer(d time.Duration) timer {
-	h.lock()
-	defer h.unlock()
-	t := &fakeTimer{
-		hooks: h,
-		when:  h.now.Add(d),
-		c:     make(chan time.Time),
-	}
-	h.timers = append(h.timers, t)
-	return t
-}
-
-// afterFunc creates a new fake AfterFunc timer.
-func (h *testSyncHooks) afterFunc(d time.Duration, f func()) timer {
-	h.lock()
-	defer h.unlock()
-	t := &fakeTimer{
-		hooks: h,
-		when:  h.now.Add(d),
-		f:     f,
-	}
-	h.timers = append(h.timers, t)
-	return t
-}
-
-func (h *testSyncHooks) contextWithTimeout(ctx context.Context, d time.Duration) (context.Context, context.CancelFunc) {
-	ctx, cancel := context.WithCancel(ctx)
-	t := h.afterFunc(d, cancel)
-	return ctx, func() {
-		t.Stop()
-		cancel()
-	}
-}
-
-func (h *testSyncHooks) timeUntilEvent() time.Duration {
-	h.lock()
-	defer h.unlock()
-	var next time.Time
-	for _, t := range h.timers {
-		if next.IsZero() || t.when.Before(next) {
-			next = t.when
-		}
-	}
-	if d := next.Sub(h.now); d > 0 {
-		return d
-	}
-	return 0
-}
-
-// advance advances time and causes synthetic timers to fire.
-func (h *testSyncHooks) advance(d time.Duration) {
-	h.lock()
-	defer h.unlock()
-	h.now = h.now.Add(d)
-	timers := h.timers[:0]
-	for _, t := range h.timers {
-		t := t // remove after go.mod depends on go1.22
-		t.mu.Lock()
-		switch {
-		case t.when.After(h.now):
-			timers = append(timers, t)
-		case t.when.IsZero():
-			// stopped timer
-		default:
-			t.when = time.Time{}
-			if t.c != nil {
-				close(t.c)
-			}
-			if t.f != nil {
-				h.total++
-				go func() {
-					defer func() {
-						h.lock()
-						h.total--
-						h.unlock()
-					}()
-					t.f()
-				}()
-			}
-		}
-		t.mu.Unlock()
-	}
-	h.timers = timers
-}
-
-// A timer wraps a time.Timer, or a synthetic equivalent in tests.
-// Unlike time.Timer, timer is single-use: The timer channel is closed when the timer expires.
-type timer interface {
-	C() <-chan time.Time
-	Stop() bool
-	Reset(d time.Duration) bool
-}
-
-// timeTimer implements timer using real time.
-type timeTimer struct {
-	t *time.Timer
-	c chan time.Time
-}
-
-// newTimeTimer creates a new timer using real time.
-func newTimeTimer(d time.Duration) timer {
-	ch := make(chan time.Time)
-	t := time.AfterFunc(d, func() {
-		close(ch)
-	})
-	return &timeTimer{t, ch}
-}
-
-// newTimeAfterFunc creates an AfterFunc timer using real time.
-func newTimeAfterFunc(d time.Duration, f func()) timer {
-	return &timeTimer{
-		t: time.AfterFunc(d, f),
-	}
-}
-
-func (t timeTimer) C() <-chan time.Time        { return t.c }
-func (t timeTimer) Stop() bool                 { return t.t.Stop() }
-func (t timeTimer) Reset(d time.Duration) bool { return t.t.Reset(d) }
-
-// fakeTimer implements timer using fake time.
-type fakeTimer struct {
-	hooks *testSyncHooks
-
-	mu   sync.Mutex
-	when time.Time      // when the timer will fire
-	c    chan time.Time // closed when the timer fires; mutually exclusive with f
-	f    func()         // called when the timer fires; mutually exclusive with c
-}
-
-func (t *fakeTimer) C() <-chan time.Time { return t.c }
-
-func (t *fakeTimer) Stop() bool {
-	t.mu.Lock()
-	defer t.mu.Unlock()
-	stopped := t.when.IsZero()
-	t.when = time.Time{}
-	return stopped
-}
-
-func (t *fakeTimer) Reset(d time.Duration) bool {
-	if t.c != nil || t.f == nil {
-		panic("fakeTimer only supports Reset on AfterFunc timers")
-	}
-	t.mu.Lock()
-	defer t.mu.Unlock()
-	t.hooks.lock()
-	defer t.hooks.unlock()
-	active := !t.when.IsZero()
-	t.when = t.hooks.now.Add(d)
-	if !active {
-		t.hooks.timers = append(t.hooks.timers, t)
-	}
-	return active
-}