Repository: syself/cluster-api-provider-hetzner
Pull request #1035 (2023-11-01 11:03:06)

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

| Package | Type | Update | Change |
|---|---|---|---|
| [github.com/go-logr/zapr](https://togithub.com/go-logr/zapr) | require | minor | `v1.2.4` -> `v1.3.0` |
| golang.org/x/mod | require | minor | `v0.13.0` -> `v0.14.0` |
| [k8s.io/klog/v2](https://togithub.com/kubernetes/klog) | require | minor | `v2.100.1` -> `v2.110.1` |

---

### Release Notes

<details>
<summary>go-logr/zapr (github.com/go-logr/zapr)</summary>

### [`v1.3.0`](https://togithub.com/go-logr/zapr/releases/tag/v1.3.0)

[Compare Source](https://togithub.com/go-logr/zapr/compare/v1.2.4...v1.3.0)

This release adds [support for slog](https://togithub.com/go-logr/logr#slog-interoperability). zapr implements `slogr.SlogSink` and therefore can be used through [`slogr.NewSlogHandler`](https://pkg.go.dev/github.com/go-logr/logr@v1.3.0/slogr#NewSlogHandler) as backend for slog.

#### What's Changed

-   Added dependabot by [@&#8203;Neo2308](https://togithub.com/Neo2308) in [https://github.com/go-logr/zapr/pull/63](https://togithub.com/go-logr/zapr/pull/63)
-   Updated min supported version to go 1.18 by [@&#8203;Neo2308](https://togithub.com/Neo2308) in [https://github.com/go-logr/zapr/pull/62](https://togithub.com/go-logr/zapr/pull/62)
-   update linter config and fix issues by [@&#8203;pohly](https://togithub.com/pohly) in [https://github.com/go-logr/zapr/pull/61](https://togithub.com/go-logr/zapr/pull/61)
-   Bump github.com/go-logr/logr from 1.2.4 to 1.3.0 by [@&#8203;dependabot](https://togithub.com/dependabot) in [https://github.com/go-logr/zapr/pull/71](https://togithub.com/go-logr/zapr/pull/71)
-   support slog by [@&#8203;pohly](https://togithub.com/pohly) in [https://github.com/go-logr/zapr/pull/60](https://togithub.com/go-logr/zapr/pull/60)

***

-   Bump github.com/stretchr/testify from 1.8.0 to 1.8.4 by [@&#8203;dependabot](https://togithub.com/dependabot) in [https://github.com/go-logr/zapr/pull/65](https://togithub.com/go-logr/zapr/pull/65)
-   Bump actions/checkout from 2 to 3 by [@&#8203;dependabot](https://togithub.com/dependabot) in [https://github.com/go-logr/zapr/pull/66](https://togithub.com/go-logr/zapr/pull/66)
-   Bump actions/setup-go from 2 to 4 by [@&#8203;dependabot](https://togithub.com/dependabot) in [https://github.com/go-logr/zapr/pull/67](https://togithub.com/go-logr/zapr/pull/67)
-   Bump golangci/golangci-lint-action from 2 to 3 by [@&#8203;dependabot](https://togithub.com/dependabot) in [https://github.com/go-logr/zapr/pull/68](https://togithub.com/go-logr/zapr/pull/68)
-   Bump actions/checkout from 3 to 4 by [@&#8203;dependabot](https://togithub.com/dependabot) in [https://github.com/go-logr/zapr/pull/69](https://togithub.com/go-logr/zapr/pull/69)
-   Bump go.uber.org/zap from 1.24.0 to 1.25.0 by [@&#8203;dependabot](https://togithub.com/dependabot) in [https://github.com/go-logr/zapr/pull/64](https://togithub.com/go-logr/zapr/pull/64)
-   Bump go.uber.org/zap from 1.25.0 to 1.26.0 by [@&#8203;dependabot](https://togithub.com/dependabot) in [https://github.com/go-logr/zapr/pull/70](https://togithub.com/go-logr/zapr/pull/70)

#### New Contributors

-   [@&#8203;Neo2308](https://togithub.com/Neo2308) made their first contribution in [https://github.com/go-logr/zapr/pull/63](https://togithub.com/go-logr/zapr/pull/63)
-   [@&#8203;dependabot](https://togithub.com/dependabot) made their first contribution in [https://github.com/go-logr/zapr/pull/65](https://togithub.com/go-logr/zapr/pull/65)

**Full Changelog**: https://github.com/go-logr/zapr/compare/v1.2.4...v1.3.0

</details>

<details>
<summary>kubernetes/klog (k8s.io/klog/v2)</summary>

### [`v2.110.1`](https://togithub.com/kubernetes/klog/releases/tag/v2.110.1): Prepare klog release for Kubernetes v1.29 (Take 1)

[Compare Source](https://togithub.com/kubernetes/klog/compare/v2.100.1...v2.110.1)

#### What's Changed

-   fix: SetLogger via klog.SetLogger will output an unexpected newline by [@&#8203;aimuz](https://togithub.com/aimuz) in [https://github.com/kubernetes/klog/pull/378](https://togithub.com/kubernetes/klog/pull/378)
-   resolve comments warning by [@&#8203;lowang-bh](https://togithub.com/lowang-bh) in [https://github.com/kubernetes/klog/pull/379](https://togithub.com/kubernetes/klog/pull/379)
-   stderrthreshold: fix flag comment by [@&#8203;pohly](https://togithub.com/pohly) in [https://github.com/kubernetes/klog/pull/376](https://togithub.com/kubernetes/klog/pull/376)
-   enable "go vet" checks for parameters by [@&#8203;pohly](https://togithub.com/pohly) in [https://github.com/kubernetes/klog/pull/390](https://togithub.com/kubernetes/klog/pull/390)
-   promote experimental code to stable by [@&#8203;pohly](https://togithub.com/pohly) in [https://github.com/kubernetes/klog/pull/392](https://togithub.com/kubernetes/klog/pull/392)
-   golangci-lint action by [@&#8203;pohly](https://togithub.com/pohly) in [https://github.com/kubernetes/klog/pull/380](https://togithub.com/kubernetes/klog/pull/380)
-   output: handle WithName like zapr does by [@&#8203;pohly](https://togithub.com/pohly) in [https://github.com/kubernetes/klog/pull/391](https://togithub.com/kubernetes/klog/pull/391)
-   slog support + logr 1.3.0 update by [@&#8203;pohly](https://togithub.com/pohly) in [https://github.com/kubernetes/klog/pull/384](https://togithub.com/kubernetes/klog/pull/384)

#### New Contributors

-   [@&#8203;aimuz](https://togithub.com/aimuz) made their first contribution in [https://github.com/kubernetes/klog/pull/378](https://togithub.com/kubernetes/klog/pull/378)
-   [@&#8203;lowang-bh](https://togithub.com/lowang-bh) made their first contribution in [https://github.com/kubernetes/klog/pull/379](https://togithub.com/kubernetes/klog/pull/379)

**Full Changelog**: https://github.com/kubernetes/klog/compare/v2.100.1...v2.110.1

</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:eyJjcmVhdGVkSW5WZXIiOiIzNy4zNy4yIiwidXBkYXRlZEluVmVyIjoiMzcuNDUuMCIsInRhcmdldEJyYW5jaCI6Im1haW4ifQ==-->



Patch:
diff --git a/.github/workflows/pr-verify.yml b/.github/workflows/pr-verify.yml
index 4094ac26..ff9e0db0 100644
--- a/.github/workflows/pr-verify.yml
+++ b/.github/workflows/pr-verify.yml
@@ -11,7 +11,7 @@ jobs:
     steps:
       - name: Verifier action
         id: verifier
-        uses: kubernetes-sigs/kubebuilder-release-tools@3c3411345eedc489d1022288aa844691e92a9c29 # v0.4.2
+        uses: kubernetes-sigs/kubebuilder-release-tools@d8367c29de8af903319d3a76de2436672515729b # v0.4.0
         with:
           github_token: ${{ secrets.GITHUB_TOKEN }}
 
diff --git a/.github/workflows/schedule-update-bot.yaml b/.github/workflows/schedule-update-bot.yaml
index 7dccfead..726f3beb 100644
--- a/.github/workflows/schedule-update-bot.yaml
+++ b/.github/workflows/schedule-update-bot.yaml
@@ -44,7 +44,7 @@ jobs:
           echo "LOG_LEVEL=${{ github.event.inputs.logLevel || env.LOG_LEVEL }}" >> "$GITHUB_ENV"
 
       - name: Renovate
-        uses: renovatebot/github-action@5c6c06aa0e3d7a6a9b6cba05c078c51631b5f11a # v39.1.1
+        uses: renovatebot/github-action@89c8a9b60f5bc8395658abab8f40f96e4b490ebf # v39.1.0
         env:
           RENOVATE_HOST_RULES: '[{"hostType": "docker", "matchHost": "ghcr.io", "username": "${{ github.actor }}", "password": "${{ secrets.GITHUB_TOKEN }}" }]'
           RENOVATE_ALLOWED_POST_UPGRADE_COMMANDS: '[".*"]'
diff --git a/go.mod b/go.mod
index 1ec60c4c..5a10c030 100644
--- a/go.mod
+++ b/go.mod
@@ -7,7 +7,7 @@ replace sigs.k8s.io/cluster-api => sigs.k8s.io/cluster-api v1.5.0
 require (
 	github.com/blang/semver/v4 v4.0.0
 	github.com/go-logr/logr v1.3.0
-	github.com/go-logr/zapr v1.2.4
+	github.com/go-logr/zapr v1.3.0
 	github.com/hetznercloud/hcloud-go/v2 v2.4.0
 	github.com/onsi/ginkgo/v2 v2.13.0
 	github.com/onsi/gomega v1.29.0
@@ -17,12 +17,12 @@ require (
 	github.com/syself/hrobot-go v0.2.5
 	go.uber.org/zap v1.26.0
 	golang.org/x/crypto v0.14.0
-	golang.org/x/mod v0.13.0
+	golang.org/x/mod v0.14.0
 	k8s.io/api v0.27.7
 	k8s.io/apimachinery v0.27.7
 	k8s.io/apiserver v0.27.7
 	k8s.io/client-go v0.27.7
-	k8s.io/klog/v2 v2.100.1
+	k8s.io/klog/v2 v2.110.1
 	k8s.io/kubectl v0.27.2
 	k8s.io/utils v0.0.0-20230726121419-3b25d923346b
 	sigs.k8s.io/cluster-api v1.5.1
diff --git a/go.sum b/go.sum
index 801e03ce..e24efb34 100644
--- a/go.sum
+++ b/go.sum
@@ -68,7 +68,6 @@ github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmV
 github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
 github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA=
 github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
-github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
 github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
 github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
@@ -149,12 +148,10 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2
 github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
-github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
-github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
 github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY=
 github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
-github.com/go-logr/zapr v1.2.4 h1:QHVo+6stLbfJmYGkQ7uGHUCu5hnAFAj6mDe6Ea0SeOo=
-github.com/go-logr/zapr v1.2.4/go.mod h1:FyHWQIzQORZ0QVE1BtVHv3cKtNLuXsbNLtpuhNapBOA=
+github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ=
+github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg=
 github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
 github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
 github.com/go-openapi/jsonreference v0.20.1 h1:FBLnyygC4/IZZr893oiomc9XaghoveYTrLC1F86HID8=
@@ -478,7 +475,6 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
 go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
@@ -489,16 +485,12 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
 go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
 go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
 go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
-go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
-go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
 go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
 go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
 go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
-go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
 go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
 go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
 go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
-go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
 go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
 go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
 golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@@ -547,10 +539,9 @@ 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.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.4.2/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.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY=
-golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
+golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
+golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -614,7 +605,6 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -743,7 +733,6 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f
 golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
-golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
 golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
 golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
@@ -905,8 +894,8 @@ k8s.io/cluster-bootstrap v0.27.2 h1:OL3onrOwrUD7NQxBUqQwTl1Uu2GQKCkw9BMHpc4PbiA=
 k8s.io/cluster-bootstrap v0.27.2/go.mod h1:b++PF0mjUOiTKdPQFlDw7p4V2VquANZ8SfhAwzxZJFM=
 k8s.io/component-base v0.27.7 h1:kngM58HR9W9Nqpv7e4rpdRyWnKl/ABpUhLAZ+HoliMs=
 k8s.io/component-base v0.27.7/go.mod h1:YGjlCVL1oeKvG3HSciyPHFh+LCjIEqsxz4BDR3cfHRs=
-k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg=
-k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
+k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0=
+k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo=
 k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f h1:2kWPakN3i/k81b0gvD5C5FJ2kxm1WrQFanWchyKuqGg=
 k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f/go.mod h1:byini6yhqGC14c3ebc/QwanvYwhuMWF6yz2F8uwW8eg=
 k8s.io/kubectl v0.27.2 h1:sSBM2j94MHBFRWfHIWtEXWCicViQzZsb177rNsKBhZg=
diff --git a/pkg/scope/hcloudmachinetemplate.go b/pkg/scope/hcloudmachinetemplate.go
index 1420cee6..6cd99eef 100644
--- a/pkg/scope/hcloudmachinetemplate.go
+++ b/pkg/scope/hcloudmachinetemplate.go
@@ -23,7 +23,7 @@ import (
 	"fmt"
 
 	"github.com/go-logr/logr"
-	"k8s.io/klog/v2/klogr"
+	"k8s.io/klog/v2/textlogger"
 	"sigs.k8s.io/cluster-api/util/conditions"
 	"sigs.k8s.io/cluster-api/util/patch"
 	"sigs.k8s.io/controller-runtime/pkg/client"
@@ -48,7 +48,7 @@ func NewHCloudMachineTemplateScope(params HCloudMachineTemplateScopeParams) (*HC
 	}
 
 	if params.Logger == nil {
-		logger := klogr.New()
+		logger := textlogger.NewLogger(textlogger.NewConfig())
 		params.Logger = &logger
 	}
 
diff --git a/vendor/github.com/go-logr/logr/slogr/sloghandler.go b/vendor/github.com/go-logr/logr/slogr/sloghandler.go
new file mode 100644
index 00000000..ec6725ce
--- /dev/null
+++ b/vendor/github.com/go-logr/logr/slogr/sloghandler.go
@@ -0,0 +1,168 @@
+//go:build go1.21
+// +build go1.21
+
+/*
+Copyright 2023 The logr 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 slogr
+
+import (
+	"context"
+	"log/slog"
+
+	"github.com/go-logr/logr"
+)
+
+type slogHandler struct {
+	// May be nil, in which case all logs get discarded.
+	sink logr.LogSink
+	// Non-nil if sink is non-nil and implements SlogSink.
+	slogSink SlogSink
+
+	// groupPrefix collects values from WithGroup calls. It gets added as
+	// prefix to value keys when handling a log record.
+	groupPrefix string
+
+	// levelBias can be set when constructing the handler to influence the
+	// slog.Level of log records. A positive levelBias reduces the
+	// slog.Level value. slog has no API to influence this value after the
+	// handler got created, so it can only be set indirectly through
+	// Logger.V.
+	levelBias slog.Level
+}
+
+var _ slog.Handler = &slogHandler{}
+
+// groupSeparator is used to concatenate WithGroup names and attribute keys.
+const groupSeparator = "."
+
+// GetLevel is used for black box unit testing.
+func (l *slogHandler) GetLevel() slog.Level {
+	return l.levelBias
+}
+
+func (l *slogHandler) Enabled(ctx context.Context, level slog.Level) bool {
+	return l.sink != nil && (level >= slog.LevelError || l.sink.Enabled(l.levelFromSlog(level)))
+}
+
+func (l *slogHandler) Handle(ctx context.Context, record slog.Record) error {
+	if l.slogSink != nil {
+		// Only adjust verbosity level of log entries < slog.LevelError.
+		if record.Level < slog.LevelError {
+			record.Level -= l.levelBias
+		}
+		return l.slogSink.Handle(ctx, record)
+	}
+
+	// No need to check for nil sink here because Handle will only be called
+	// when Enabled returned true.
+
+	kvList := make([]any, 0, 2*record.NumAttrs())
+	record.Attrs(func(attr slog.Attr) bool {
+		if attr.Key != "" {
+			kvList = append(kvList, l.addGroupPrefix(attr.Key), attr.Value.Resolve().Any())
+		}
+		return true
+	})
+	if record.Level >= slog.LevelError {
+		l.sinkWithCallDepth().Error(nil, record.Message, kvList...)
+	} else {
+		level := l.levelFromSlog(record.Level)
+		l.sinkWithCallDepth().Info(level, record.Message, kvList...)
+	}
+	return nil
+}
+
+// sinkWithCallDepth adjusts the stack unwinding so that when Error or Info
+// are called by Handle, code in slog gets skipped.
+//
+// This offset currently (Go 1.21.0) works for calls through
+// slog.New(NewSlogHandler(...)).  There's no guarantee that the call
+// chain won't change. Wrapping the handler will also break unwinding. It's
+// still better than not adjusting at all....
+//
+// This cannot be done when constructing the handler because NewLogr needs
+// access to the original sink without this adjustment. A second copy would
+// work, but then WithAttrs would have to be called for both of them.
+func (l *slogHandler) sinkWithCallDepth() logr.LogSink {
+	if sink, ok := l.sink.(logr.CallDepthLogSink); ok {
+		return sink.WithCallDepth(2)
+	}
+	return l.sink
+}
+
+func (l *slogHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
+	if l.sink == nil || len(attrs) == 0 {
+		return l
+	}
+
+	copy := *l
+	if l.slogSink != nil {
+		copy.slogSink = l.slogSink.WithAttrs(attrs)
+		copy.sink = copy.slogSink
+	} else {
+		kvList := make([]any, 0, 2*len(attrs))
+		for _, attr := range attrs {
+			if attr.Key != "" {
+				kvList = append(kvList, l.addGroupPrefix(attr.Key), attr.Value.Resolve().Any())
+			}
+		}
+		copy.sink = l.sink.WithValues(kvList...)
+	}
+	return &copy
+}
+
+func (l *slogHandler) WithGroup(name string) slog.Handler {
+	if l.sink == nil {
+		return l
+	}
+	copy := *l
+	if l.slogSink != nil {
+		copy.slogSink = l.slogSink.WithGroup(name)
+		copy.sink = l.slogSink
+	} else {
+		copy.groupPrefix = copy.addGroupPrefix(name)
+	}
+	return &copy
+}
+
+func (l *slogHandler) addGroupPrefix(name string) string {
+	if l.groupPrefix == "" {
+		return name
+	}
+	return l.groupPrefix + groupSeparator + name
+}
+
+// levelFromSlog adjusts the level by the logger's verbosity and negates it.
+// It ensures that the result is >= 0. This is necessary because the result is
+// passed to a logr.LogSink and that API did not historically document whether
+// levels could be negative or what that meant.
+//
+// Some example usage:
+//     logrV0 := getMyLogger()
+//     logrV2 := logrV0.V(2)
+//     slogV2 := slog.New(slogr.NewSlogHandler(logrV2))
+//     slogV2.Debug("msg") // =~ logrV2.V(4) =~ logrV0.V(6)
+//     slogV2.Info("msg")  // =~  logrV2.V(0) =~ logrV0.V(2)
+//     slogv2.Warn("msg")  // =~ logrV2.V(-4) =~ logrV0.V(0)
+func (l *slogHandler) levelFromSlog(level slog.Level) int {
+	result := -level
+	result += l.levelBias // in case the original logr.Logger had a V level
+	if result < 0 {
+		result = 0 // because logr.LogSink doesn't expect negative V levels
+	}
+	return int(result)
+}
diff --git a/vendor/github.com/go-logr/logr/slogr/slogr.go b/vendor/github.com/go-logr/logr/slogr/slogr.go
new file mode 100644
index 00000000..eb519ae2
--- /dev/null
+++ b/vendor/github.com/go-logr/logr/slogr/slogr.go
@@ -0,0 +1,108 @@
+//go:build go1.21
+// +build go1.21
+
+/*
+Copyright 2023 The logr 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 slogr enables usage of a slog.Handler with logr.Logger as front-end
+// API and of a logr.LogSink through the slog.Handler and thus slog.Logger
+// APIs.
+//
+// See the README in the top-level [./logr] package for a discussion of
+// interoperability.
+package slogr
+
+import (
+	"context"
+	"log/slog"
+
+	"github.com/go-logr/logr"
+)
+
+// NewLogr returns a logr.Logger which writes to the slog.Handler.
+//
+// The logr verbosity level is mapped to slog levels such that V(0) becomes
+// slog.LevelInfo and V(4) becomes slog.LevelDebug.
+func NewLogr(handler slog.Handler) logr.Logger {
+	if handler, ok := handler.(*slogHandler); ok {
+		if handler.sink == nil {
+			return logr.Discard()
+		}
+		return logr.New(handler.sink).V(int(handler.levelBias))
+	}
+	return logr.New(&slogSink{handler: handler})
+}
+
+// NewSlogHandler returns a slog.Handler which writes to the same sink as the logr.Logger.
+//
+// The returned logger writes all records with level >= slog.LevelError as
+// error log entries with LogSink.Error, regardless of the verbosity level of
+// the logr.Logger:
+//
+//	logger := <some logr.Logger with 0 as verbosity level>
+//	slog.New(NewSlogHandler(logger.V(10))).Error(...) -> logSink.Error(...)
+//
+// The level of all other records gets reduced by the verbosity
+// level of the logr.Logger and the result is negated. If it happens
+// to be negative, then it gets replaced by zero because a LogSink
+// is not expected to handled negative levels:
+//
+//	slog.New(NewSlogHandler(logger)).Debug(...) -> logger.GetSink().Info(level=4, ...)
+//	slog.New(NewSlogHandler(logger)).Warning(...) -> logger.GetSink().Info(level=0, ...)
+//	slog.New(NewSlogHandler(logger)).Info(...) -> logger.GetSink().Info(level=0, ...)
+//	slog.New(NewSlogHandler(logger.V(4))).Info(...) -> logger.GetSink().Info(level=4, ...)
+func NewSlogHandler(logger logr.Logger) slog.Handler {
+	if sink, ok := logger.GetSink().(*slogSink); ok && logger.GetV() == 0 {
+		return sink.handler
+	}
+
+	handler := &slogHandler{sink: logger.GetSink(), levelBias: slog.Level(logger.GetV())}
+	if slogSink, ok := handler.sink.(SlogSink); ok {
+		handler.slogSink = slogSink
+	}
+	return handler
+}
+
+// SlogSink is an optional interface that a LogSink can implement to support
+// logging through the slog.Logger or slog.Handler APIs better. It then should
+// also support special slog values like slog.Group. When used as a
+// slog.Handler, the advantages are:
+//
+//   - stack unwinding gets avoided in favor of logging the pre-recorded PC,
+//     as intended by slog
+//   - proper grouping of key/value pairs via WithGroup
+//   - verbosity levels > slog.LevelInfo can be recorded
+//   - less overhead
+//
+// Both APIs (logr.Logger and slog.Logger/Handler) then are supported equally
+// well. Developers can pick whatever API suits them better and/or mix
+// packages which use either API in the same binary with a common logging
+// implementation.
+//
+// This interface is necessary because the type implementing the LogSink
+// interface cannot also implement the slog.Handler interface due to the
+// different prototype of the common Enabled method.
+//
+// An implementation could support both interfaces in two different types, but then
+// additional interfaces would be needed to convert between those types in NewLogr
+// and NewSlogHandler.
+type SlogSink interface {
+	logr.LogSink
+
+	Handle(ctx context.Context, record slog.Record) error
+	WithAttrs(attrs []slog.Attr) SlogSink
+	WithGroup(name string) SlogSink
+}
diff --git a/vendor/github.com/go-logr/logr/slogr/slogsink.go b/vendor/github.com/go-logr/logr/slogr/slogsink.go
new file mode 100644
index 00000000..6fbac561
--- /dev/null
+++ b/vendor/github.com/go-logr/logr/slogr/slogsink.go
@@ -0,0 +1,122 @@
+//go:build go1.21
+// +build go1.21
+
+/*
+Copyright 2023 The logr 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 slogr
+
+import (
+	"context"
+	"log/slog"
+	"runtime"
+	"time"
+
+	"github.com/go-logr/logr"
+)
+
+var (
+	_ logr.LogSink          = &slogSink{}
+	_ logr.CallDepthLogSink = &slogSink{}
+	_ Underlier             = &slogSink{}
+)
+
+// Underlier is implemented by the LogSink returned by NewLogr.
+type Underlier interface {
+	// GetUnderlying returns the Handler used by the LogSink.
+	GetUnderlying() slog.Handler
+}
+
+const (
+	// nameKey is used to log the `WithName` values as an additional attribute.
+	nameKey = "logger"
+
+	// errKey is used to log the error parameter of Error as an additional attribute.
+	errKey = "err"
+)
+
+type slogSink struct {
+	callDepth int
+	name      string
+	handler   slog.Handler
+}
+
+func (l *slogSink) Init(info logr.RuntimeInfo) {
+	l.callDepth = info.CallDepth
+}
+
+func (l *slogSink) GetUnderlying() slog.Handler {
+	return l.handler
+}
+
+func (l *slogSink) WithCallDepth(depth int) logr.LogSink {
+	newLogger := *l
+	newLogger.callDepth += depth
+	return &newLogger
+}
+
+func (l *slogSink) Enabled(level int) bool {
+	return l.handler.Enabled(context.Background(), slog.Level(-level))
+}
+
+func (l *slogSink) Info(level int, msg string, kvList ...interface{}) {
+	l.log(nil, msg, slog.Level(-level), kvList...)
+}
+
+func (l *slogSink) Error(err error, msg string, kvList ...interface{}) {
+	l.log(err, msg, slog.LevelError, kvList...)
+}
+
+func (l *slogSink) log(err error, msg string, level slog.Level, kvList ...interface{}) {
+	var pcs [1]uintptr
+	// skip runtime.Callers, this function, Info/Error, and all helper functions above that.
+	runtime.Callers(3+l.callDepth, pcs[:])
+
+	record := slog.NewRecord(time.Now(), level, msg, pcs[0])
+	if l.name != "" {
+		record.AddAttrs(slog.String(nameKey, l.name))
+	}
+	if err != nil {
+		record.AddAttrs(slog.Any(errKey, err))
+	}
+	record.Add(kvList...)
+	l.handler.Handle(context.Background(), record)
+}
+
+func (l slogSink) WithName(name string) logr.LogSink {
+	if l.name != "" {
+		l.name = l.name + "/"
+	}
+	l.name += name
+	return &l
+}
+
+func (l slogSink) WithValues(kvList ...interface{}) logr.LogSink {
+	l.handler = l.handler.WithAttrs(kvListToAttrs(kvList...))
+	return &l
+}
+
+func kvListToAttrs(kvList ...interface{}) []slog.Attr {
+	// We don't need the record itself, only its Add method.
+	record := slog.NewRecord(time.Time{}, 0, "", 0)
+	record.Add(kvList...)
+	attrs := make([]slog.Attr, 0, record.NumAttrs())
+	record.Attrs(func(attr slog.Attr) bool {
+		attrs = append(attrs, attr)
+		return true
+	})
+	return attrs
+}
diff --git a/vendor/github.com/go-logr/zapr/.golangci.yaml b/vendor/github.com/go-logr/zapr/.golangci.yaml
new file mode 100644
index 00000000..64246c50
--- /dev/null
+++ b/vendor/github.com/go-logr/zapr/.golangci.yaml
@@ -0,0 +1,20 @@
+issues:
+  exclude-use-default: false
+
+linters:
+  disable-all: true
+  enable:
+  - asciicheck
+  - errcheck
+  - forcetypeassert
+  - gocritic
+  - gofmt
+  - goimports
+  - gosimple
+  - govet
+  - ineffassign
+  - misspell
+  - revive
+  - staticcheck
+  - typecheck
+  - unused
diff --git a/vendor/github.com/go-logr/zapr/README.md b/vendor/github.com/go-logr/zapr/README.md
index 78f5f765..ff332da3 100644
--- a/vendor/github.com/go-logr/zapr/README.md
+++ b/vendor/github.com/go-logr/zapr/README.md
@@ -2,12 +2,17 @@ Zapr :zap:
 ==========
 
 A [logr](https://github.com/go-logr/logr) implementation using
-[Zap](https://github.com/uber-go/zap).
+[Zap](https://github.com/uber-go/zap). Can also be used as
+[slog](https://pkg.go.dev/log/slog) handler.
 
 Usage
 -----
 
+Via logr:
+
 ```go
+package main
+
 import (
     "fmt"
 
@@ -29,6 +34,33 @@ func main() {
 }
 ```
 
+Via slog:
+
+```
+package main
+
+import (
+	"fmt"
+	"log/slog"
+
+	"github.com/go-logr/logr/slogr"
+	"github.com/go-logr/zapr"
+	"go.uber.org/zap"
+)
+
+func main() {
+	var log *slog.Logger
+
+	zapLog, err := zap.NewDevelopment()
+	if err != nil {
+		panic(fmt.Sprintf("who watches the watchmen (%v)?", err))
+	}
+	log = slog.New(slogr.NewSlogHandler(zapr.NewLogger(zapLog)))
+
+	log.Info("Logr in action!", "the answer", 42)
+}
+```
+
 Increasing Verbosity
 --------------------
 
@@ -68,3 +100,8 @@ For the most part, concepts in Zap correspond directly with those in logr.
 Unlike Zap, all fields *must* be in the form of sugared fields --
 it's illegal to pass a strongly-typed Zap field in a key position to any
 of the logging methods (`Log`, `Error`).
+
+The zapr `logr.LogSink` implementation also implements `logr.SlogHandler`. That
+enables `slogr.NewSlogHandler` to provide a `slog.Handler` which just passes
+parameters through to zapr. zapr handles special slog values (Group,
+LogValuer), regardless of which front-end API is used.
diff --git a/vendor/github.com/go-logr/zapr/slogzapr.go b/vendor/github.com/go-logr/zapr/slogzapr.go
new file mode 100644
index 00000000..84f56e43
--- /dev/null
+++ b/vendor/github.com/go-logr/zapr/slogzapr.go
@@ -0,0 +1,183 @@
+//go:build go1.21
+// +build go1.21
+
+/*
+Copyright 2023 The logr 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 zapr
+
+import (
+	"context"
+	"log/slog"
+	"runtime"
+
+	"github.com/go-logr/logr/slogr"
+	"go.uber.org/zap"
+	"go.uber.org/zap/zapcore"
+)
+
+var _ slogr.SlogSink = &zapLogger{}
+
+func (zl *zapLogger) Handle(_ context.Context, record slog.Record) error {
+	zapLevel := zap.InfoLevel
+	intLevel := 0
+	isError := false
+	switch {
+	case record.Level >= slog.LevelError:
+		zapLevel = zap.ErrorLevel
+		isError = true
+	case record.Level >= slog.LevelWarn:
+		zapLevel = zap.WarnLevel
+	case record.Level >= 0:
+		// Already set above -> info.
+	default:
+		zapLevel = zapcore.Level(record.Level)
+		intLevel = int(-zapLevel)
+	}
+
+	if checkedEntry := zl.l.Check(zapLevel, record.Message); checkedEntry != nil {
+		checkedEntry.Time = record.Time
+		checkedEntry.Caller = pcToCallerEntry(record.PC)
+		var fieldsBuffer [2]zap.Field
+		fields := fieldsBuffer[:0]
+		if !isError && zl.numericLevelKey != "" {
+			// Record verbosity for info entries.
+			fields = append(fields, zap.Int(zl.numericLevelKey, intLevel))
+		}
+		// Inline all attributes.
+		fields = append(fields, zap.Inline(zapcore.ObjectMarshalerFunc(func(enc zapcore.ObjectEncoder) error {
+			record.Attrs(func(attr slog.Attr) bool {
+				encodeSlog(enc, attr)
+				return true
+			})
+			return nil
+		})))
+		checkedEntry.Write(fields...)
+	}
+	return nil
+}
+
+func encodeSlog(enc zapcore.ObjectEncoder, attr slog.Attr) {
+	if attr.Equal(slog.Attr{}) {
+		// Ignore empty attribute.
+		return
+	}
+
+	// Check in order of expected frequency, most common ones first.
+	//
+	// Usage statistics for parameters from Kubernetes 152876a3e,
+	// calculated with k/k/test/integration/logs/benchmark:
+	//
+	// kube-controller-manager -v10:
+	// strings: 10043 (85%)
+	// with API objects: 2 (0% of all arguments)
+	//   types and their number of usage: NodeStatus:2
+	// numbers: 792 (6%)
+	// ObjectRef: 292 (2%)
+	// others: 595 (5%)
+	//
+	// kube-scheduler -v10:
+	// strings: 1325 (40%)
+	// with API objects: 109 (3% of all arguments)
+	//   types and their number of usage: PersistentVolume:50 PersistentVolumeClaim:59
+	// numbers: 473 (14%)
+	// ObjectRef: 1305 (39%)
+	// others: 176 (5%)
+
+	kind := attr.Value.Kind()
+	switch kind {
+	case slog.KindString:
+		enc.AddString(attr.Key, attr.Value.String())
+	case slog.KindLogValuer:
+		// This includes klog.KObj.
+		encodeSlog(enc, slog.Attr{
+			Key:   attr.Key,
+			Value: attr.Value.Resolve(),
+		})
+	case slog.KindInt64:
+		enc.AddInt64(attr.Key, attr.Value.Int64())
+	case slog.KindUint64:
+		enc.AddUint64(attr.Key, attr.Value.Uint64())
+	case slog.KindFloat64:
+		enc.AddFloat64(attr.Key, attr.Value.Float64())
+	case slog.KindBool:
+		enc.AddBool(attr.Key, attr.Value.Bool())
+	case slog.KindDuration:
+		enc.AddDuration(attr.Key, attr.Value.Duration())
+	case slog.KindTime:
+		enc.AddTime(attr.Key, attr.Value.Time())
+	case slog.KindGroup:
+		attrs := attr.Value.Group()
+		if attr.Key == "" {
+			// Inline group.
+			for _, attr := range attrs {
+				encodeSlog(enc, attr)
+			}
+			return
+		}
+		if len(attrs) == 0 {
+			// Ignore empty group.
+			return
+		}
+		_ = enc.AddObject(attr.Key, marshalAttrs(attrs))
+	default:
+		// We have to go through reflection in zap.Any to get support
+		// for e.g. fmt.Stringer.
+		zap.Any(attr.Key, attr.Value.Any()).AddTo(enc)
+	}
+}
+
+type marshalAttrs []slog.Attr
+
+func (attrs marshalAttrs) MarshalLogObject(enc zapcore.ObjectEncoder) error {
+	for _, attr := range attrs {
+		encodeSlog(enc, attr)
+	}
+	return nil
+}
+
+var _ zapcore.ObjectMarshaler = marshalAttrs(nil)
+
+func pcToCallerEntry(pc uintptr) zapcore.EntryCaller {
+	if pc == 0 {
+		return zapcore.EntryCaller{}
+	}
+	// Same as https://cs.opensource.google/go/x/exp/+/642cacee:slog/record.go;drc=642cacee5cc05231f45555a333d07f1005ffc287;l=70
+	fs := runtime.CallersFrames([]uintptr{pc})
+	f, _ := fs.Next()
+	if f.File == "" {
+		return zapcore.EntryCaller{}
+	}
+	return zapcore.EntryCaller{
+		Defined:  true,
+		PC:       pc,
+		File:     f.File,
+		Line:     f.Line,
+		Function: f.Function,
+	}
+}
+
+func (zl *zapLogger) WithAttrs(attrs []slog.Attr) slogr.SlogSink {
+	newLogger := *zl
+	newLogger.l = newLogger.l.With(zap.Inline(marshalAttrs(attrs)))
+	return &newLogger
+}
+
+func (zl *zapLogger) WithGroup(name string) slogr.SlogSink {
+	newLogger := *zl
+	newLogger.l = newLogger.l.With(zap.Namespace(name))
+	return &newLogger
+}
diff --git a/vendor/github.com/go-logr/zapr/zapr.go b/vendor/github.com/go-logr/zapr/zapr.go
index 8bb7fceb..c8503ab9 100644
--- a/vendor/github.com/go-logr/zapr/zapr.go
+++ b/vendor/github.com/go-logr/zapr/zapr.go
@@ -31,14 +31,14 @@ limitations under the License.
 // Package zapr defines an implementation of the github.com/go-logr/logr
 // interfaces built on top of Zap (go.uber.org/zap).
 //
-// Usage
+// # Usage
 //
 // A new logr.Logger can be constructed from an existing zap.Logger using
 // the NewLogger function:
 //
-//  log := zapr.NewLogger(someZapLogger)
+//	log := zapr.NewLogger(someZapLogger)
 //
-// Implementation Details
+// # Implementation Details
 //
 // For the most part, concepts in Zap correspond directly with those in
 // logr.
@@ -168,15 +168,6 @@ func (zl *zapLogger) handleFields(lvl int, args []interface{}, additional ...zap
 	return append(fields, additional...)
 }
 
-func zapIt(field string, val interface{}) zap.Field {
-	// Handle types that implement logr.Marshaler: log the replacement
-	// object instead of the original one.
-	if marshaler, ok := val.(logr.Marshaler); ok {
-		field, val = invokeMarshaler(field, marshaler)
-	}
-	return zap.Any(field, val)
-}
-
 func invokeMarshaler(field string, m logr.Marshaler) (f string, ret interface{}) {
 	defer func() {
 		if r := recover(); r != nil {
diff --git a/vendor/github.com/go-logr/zapr/zapr_noslog.go b/vendor/github.com/go-logr/zapr/zapr_noslog.go
new file mode 100644
index 00000000..ec8517b7
--- /dev/null
+++ b/vendor/github.com/go-logr/zapr/zapr_noslog.go
@@ -0,0 +1,34 @@
+//go:build !go1.21
+// +build !go1.21
+
+/*
+Copyright 2023 The logr 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 zapr
+
+import (
+	"github.com/go-logr/logr"
+	"go.uber.org/zap"
+)
+
+func zapIt(field string, val interface{}) zap.Field {
+	// Handle types that implement logr.Marshaler: log the replacement
+	// object instead of the original one.
+	if marshaler, ok := val.(logr.Marshaler); ok {
+		field, val = invokeMarshaler(field, marshaler)
+	}
+	return zap.Any(field, val)
+}
diff --git a/vendor/github.com/go-logr/zapr/zapr_slog.go b/vendor/github.com/go-logr/zapr/zapr_slog.go
new file mode 100644
index 00000000..f0720360
--- /dev/null
+++ b/vendor/github.com/go-logr/zapr/zapr_slog.go
@@ -0,0 +1,48 @@
+//go:build go1.21
+// +build go1.21
+
+/*
+Copyright 2023 The logr 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 zapr
+
+import (
+	"log/slog"
+
+	"github.com/go-logr/logr"
+	"go.uber.org/zap"
+	"go.uber.org/zap/zapcore"
+)
+
+func zapIt(field string, val interface{}) zap.Field {
+	switch valTyped := val.(type) {
+	case logr.Marshaler:
+		// Handle types that implement logr.Marshaler: log the replacement
+		// object instead of the original one.
+		field, val = invokeMarshaler(field, valTyped)
+	case slog.LogValuer:
+		// The same for slog.LogValuer. We let slog.Value handle
+		// potential panics and recursion.
+		val = slog.AnyValue(val).Resolve()
+	}
+	if slogValue, ok := val.(slog.Value); ok {
+		return zap.Inline(zapcore.ObjectMarshalerFunc(func(enc zapcore.ObjectEncoder) error {
+			encodeSlog(enc, slog.Attr{Key: field, Value: slogValue})
+			return nil
+		}))
+	}
+	return zap.Any(field, val)
+}
diff --git a/vendor/golang.org/x/mod/modfile/rule.go b/vendor/golang.org/x/mod/modfile/rule.go
index e0869fa3..35fd1f53 100644
--- a/vendor/golang.org/x/mod/modfile/rule.go
+++ b/vendor/golang.org/x/mod/modfile/rule.go
@@ -542,7 +542,7 @@ func parseReplace(filename string, line *Line, verb string, args []string, fix V
 			if strings.Contains(ns, "@") {
 				return nil, errorf("replacement module must match format 'path version', not 'path@version'")
 			}
-			return nil, errorf("replacement module without version must be directory path (rooted or starting with ./ or ../)")
+			return nil, errorf("replacement module without version must be directory path (rooted or starting with . or ..)")
 		}
 		if filepath.Separator == '/' && strings.Contains(ns, `\`) {
 			return nil, errorf("replacement directory appears to be Windows path (on a non-windows system)")
@@ -555,7 +555,6 @@ func parseReplace(filename string, line *Line, verb string, args []string, fix V
 		}
 		if IsDirectoryPath(ns) {
 			return nil, errorf("replacement module directory path %q cannot have version", ns)
-
 		}
 	}
 	return &Replace{
@@ -679,14 +678,15 @@ func (f *WorkFile) add(errs *ErrorList, line *Line, verb string, args []string,
 	}
 }
 
-// IsDirectoryPath reports whether the given path should be interpreted
-// as a directory path. Just like on the go command line, relative paths
+// IsDirectoryPath reports whether the given path should be interpreted as a directory path.
+// Just like on the go command line, relative paths starting with a '.' or '..' path component
 // and rooted paths are directory paths; the rest are module paths.
 func IsDirectoryPath(ns string) bool {
 	// Because go.mod files can move from one system to another,
 	// we check all known path syntaxes, both Unix and Windows.
-	return strings.HasPrefix(ns, "./") || strings.HasPrefix(ns, "../") || strings.HasPrefix(ns, "/") ||
-		strings.HasPrefix(ns, `.\`) || strings.HasPrefix(ns, `..\`) || strings.HasPrefix(ns, `\`) ||
+	return ns == "." || strings.HasPrefix(ns, "./") || strings.HasPrefix(ns, `.\`) ||
+		ns == ".." || strings.HasPrefix(ns, "../") || strings.HasPrefix(ns, `..\`) ||
+		strings.HasPrefix(ns, "/") || strings.HasPrefix(ns, `\`) ||
 		len(ns) >= 2 && ('A' <= ns[0] && ns[0] <= 'Z' || 'a' <= ns[0] && ns[0] <= 'z') && ns[1] == ':'
 }
 
diff --git a/vendor/k8s.io/klog/v2/.golangci.yaml b/vendor/k8s.io/klog/v2/.golangci.yaml
new file mode 100644
index 00000000..0d77d65f
--- /dev/null
+++ b/vendor/k8s.io/klog/v2/.golangci.yaml
@@ -0,0 +1,6 @@
+linters:
+  disable-all: true
+  enable: # sorted alphabetical
+    - gofmt
+    - misspell
+    - revive
diff --git a/vendor/k8s.io/klog/v2/internal/buffer/buffer.go b/vendor/k8s.io/klog/v2/internal/buffer/buffer.go
index f325ded5..46de00fb 100644
--- a/vendor/k8s.io/klog/v2/internal/buffer/buffer.go
+++ b/vendor/k8s.io/klog/v2/internal/buffer/buffer.go
@@ -30,14 +30,16 @@ import (
 var (
 	// Pid is inserted into log headers. Can be overridden for tests.
 	Pid = os.Getpid()
+
+	// Time, if set, will be used instead of the actual current time.
+	Time *time.Time
 )
 
 // Buffer holds a single byte.Buffer for reuse. The zero value is ready for
 // use. It also provides some helper methods for output formatting.
 type Buffer struct {
 	bytes.Buffer
-	Tmp  [64]byte // temporary byte array for creating headers.
-	next *Buffer
+	Tmp [64]byte // temporary byte array for creating headers.
 }
 
 var buffers = sync.Pool{
@@ -122,6 +124,9 @@ func (buf *Buffer) FormatHeader(s severity.Severity, file string, line int, now
 
 	// Avoid Fprintf, for speed. The format is so simple that we can do it quickly by hand.
 	// It's worth about 3X. Fprintf is hard.
+	if Time != nil {
+		now = *Time
+	}
 	_, month, day := now.Date()
 	hour, minute, second := now.Clock()
 	// Lmmdd hh:mm:ss.uuuuuu threadid file:line]
@@ -157,6 +162,9 @@ func (buf *Buffer) SprintHeader(s severity.Severity, now time.Time) string {
 
 	// Avoid Fprintf, for speed. The format is so simple that we can do it quickly by hand.
 	// It's worth about 3X. Fprintf is hard.
+	if Time != nil {
+		now = *Time
+	}
 	_, month, day := now.Date()
 	hour, minute, second := now.Clock()
 	// Lmmdd hh:mm:ss.uuuuuu threadid file:line]
diff --git a/vendor/k8s.io/klog/v2/internal/clock/clock.go b/vendor/k8s.io/klog/v2/internal/clock/clock.go
index b8b6af5c..cc11bb48 100644
--- a/vendor/k8s.io/klog/v2/internal/clock/clock.go
+++ b/vendor/k8s.io/klog/v2/internal/clock/clock.go
@@ -39,16 +39,6 @@ type Clock interface {
 	// Sleep sleeps for the provided duration d.
 	// Consider making the sleep interruptible by using 'select' on a context channel and a timer channel.
 	Sleep(d time.Duration)
-	// Tick returns the channel of a new Ticker.
-	// This method does not allow to free/GC the backing ticker. Use
-	// NewTicker from WithTicker instead.
-	Tick(d time.Duration) <-chan time.Time
-}
-
-// WithTicker allows for injecting fake or real clocks into code that
-// needs to do arbitrary things based on time.
-type WithTicker interface {
-	Clock
 	// NewTicker returns a new Ticker.
 	NewTicker(time.Duration) Ticker
 }
@@ -66,7 +56,7 @@ type WithDelayedExecution interface {
 // WithTickerAndDelayedExecution allows for injecting fake or real clocks
 // into code that needs Ticker and AfterFunc functionality
 type WithTickerAndDelayedExecution interface {
-	WithTicker
+	Clock
 	// AfterFunc executes f in its own goroutine after waiting
 	// for d duration and returns a Timer whose channel can be
 	// closed by calling Stop() on the Timer.
@@ -79,7 +69,7 @@ type Ticker interface {
 	Stop()
 }
 
-var _ = WithTicker(RealClock{})
+var _ Clock = RealClock{}
 
 // RealClock really calls time.Now()
 type RealClock struct{}
@@ -115,13 +105,6 @@ func (RealClock) AfterFunc(d time.Duration, f func()) Timer {
 	}
 }
 
-// Tick is the same as time.Tick(d)
-// This method does not allow to free/GC the backing ticker. Use
-// NewTicker instead.
-func (RealClock) Tick(d time.Duration) <-chan time.Time {
-	return time.Tick(d)
-}
-
 // NewTicker returns a new Ticker.
 func (RealClock) NewTicker(d time.Duration) Ticker {
 	return &realTicker{
diff --git a/vendor/k8s.io/klog/v2/internal/serialize/keyvalues.go b/vendor/k8s.io/klog/v2/internal/serialize/keyvalues.go
index bcdf5f8e..d1a4751c 100644
--- a/vendor/k8s.io/klog/v2/internal/serialize/keyvalues.go
+++ b/vendor/k8s.io/klog/v2/internal/serialize/keyvalues.go
@@ -172,73 +172,6 @@ func KVListFormat(b *bytes.Buffer, keysAndValues ...interface{}) {
 	Formatter{}.KVListFormat(b, keysAndValues...)
 }
 
-// KVFormat serializes one key/value pair into the provided buffer.
-// A space gets inserted before the pair.
-func (f Formatter) KVFormat(b *bytes.Buffer, k, v interface{}) {
-	b.WriteByte(' ')
-	// Keys are assumed to be well-formed according to
-	// https://github.com/kubernetes/community/blob/master/contributors/devel/sig-instrumentation/migration-to-structured-logging.md#name-arguments
-	// for the sake of performance. Keys with spaces,
-	// special characters, etc. will break parsing.
-	if sK, ok := k.(string); ok {
-		// Avoid one allocation when the key is a string, which
-		// normally it should be.
-		b.WriteString(sK)
-	} else {
-		b.WriteString(fmt.Sprintf("%s", k))
-	}
-
-	// The type checks are sorted so that more frequently used ones
-	// come first because that is then faster in the common
-	// cases. In Kubernetes, ObjectRef (a Stringer) is more common
-	// than plain strings
-	// (https://github.com/kubernetes/kubernetes/pull/106594#issuecomment-975526235).
-	switch v := v.(type) {
-	case textWriter:
-		writeTextWriterValue(b, v)
-	case fmt.Stringer:
-		writeStringValue(b, StringerToString(v))
-	case string:
-		writeStringValue(b, v)
-	case error:
-		writeStringValue(b, ErrorToString(v))
-	case logr.Marshaler:
-		value := MarshalerToValue(v)
-		// A marshaler that returns a string is useful for
-		// delayed formatting of complex values. We treat this
-		// case like a normal string. This is useful for
-		// multi-line support.
-		//
-		// We could do this by recursively formatting a value,
-		// but that comes with the risk of infinite recursion
-		// if a marshaler returns itself. Instead we call it
-		// only once and rely on it returning the intended
-		// value directly.
-		switch value := value.(type) {
-		case string:
-			writeStringValue(b, value)
-		default:
-			f.formatAny(b, value)
-		}
-	case []byte:
-		// In https://github.com/kubernetes/klog/pull/237 it was decided
-		// to format byte slices with "%+q". The advantages of that are:
-		// - readable output if the bytes happen to be printable
-		// - non-printable bytes get represented as unicode escape
-		//   sequences (\uxxxx)
-		//
-		// The downsides are that we cannot use the faster
-		// strconv.Quote here and that multi-line output is not
-		// supported. If developers know that a byte array is
-		// printable and they want multi-line output, they can
-		// convert the value to string before logging it.
-		b.WriteByte('=')
-		b.WriteString(fmt.Sprintf("%+q", v))
-	default:
-		f.formatAny(b, v)
-	}
-}
-
 func KVFormat(b *bytes.Buffer, k, v interface{}) {
 	Formatter{}.KVFormat(b, k, v)
 }
@@ -251,6 +184,10 @@ func (f Formatter) formatAny(b *bytes.Buffer, v interface{}) {
 		b.WriteString(f.AnyToStringHook(v))
 		return
 	}
+	formatAsJSON(b, v)
+}
+
+func formatAsJSON(b *bytes.Buffer, v interface{}) {
 	encoder := json.NewEncoder(b)
 	l := b.Len()
 	if err := encoder.Encode(v); err != nil {
diff --git a/vendor/k8s.io/klog/v2/internal/serialize/keyvalues_no_slog.go b/vendor/k8s.io/klog/v2/internal/serialize/keyvalues_no_slog.go
new file mode 100644
index 00000000..d9c7d154
--- /dev/null
+++ b/vendor/k8s.io/klog/v2/internal/serialize/keyvalues_no_slog.go
@@ -0,0 +1,97 @@
+//go:build !go1.21
+// +build !go1.21
+
+/*
+Copyright 2023 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package serialize
+
+import (
+	"bytes"
+	"fmt"
+
+	"github.com/go-logr/logr"
+)
+
+// KVFormat serializes one key/value pair into the provided buffer.
+// A space gets inserted before the pair.
+func (f Formatter) KVFormat(b *bytes.Buffer, k, v interface{}) {
+	// This is the version without slog support. Must be kept in sync with
+	// the version in keyvalues_slog.go.
+
+	b.WriteByte(' ')
+	// Keys are assumed to be well-formed according to
+	// https://github.com/kubernetes/community/blob/master/contributors/devel/sig-instrumentation/migration-to-structured-logging.md#name-arguments
+	// for the sake of performance. Keys with spaces,
+	// special characters, etc. will break parsing.
+	if sK, ok := k.(string); ok {
+		// Avoid one allocation when the key is a string, which
+		// normally it should be.
+		b.WriteString(sK)
+	} else {
+		b.WriteString(fmt.Sprintf("%s", k))
+	}
+
+	// The type checks are sorted so that more frequently used ones
+	// come first because that is then faster in the common
+	// cases. In Kubernetes, ObjectRef (a Stringer) is more common
+	// than plain strings
+	// (https://github.com/kubernetes/kubernetes/pull/106594#issuecomment-975526235).
+	switch v := v.(type) {
+	case textWriter:
+		writeTextWriterValue(b, v)
+	case fmt.Stringer:
+		writeStringValue(b, StringerToString(v))
+	case string:
+		writeStringValue(b, v)
+	case error:
+		writeStringValue(b, ErrorToString(v))
+	case logr.Marshaler:
+		value := MarshalerToValue(v)
+		// A marshaler that returns a string is useful for
+		// delayed formatting of complex values. We treat this
+		// case like a normal string. This is useful for
+		// multi-line support.
+		//
+		// We could do this by recursively formatting a value,
+		// but that comes with the risk of infinite recursion
+		// if a marshaler returns itself. Instead we call it
+		// only once and rely on it returning the intended
+		// value directly.
+		switch value := value.(type) {
+		case string:
+			writeStringValue(b, value)
+		default:
+			f.formatAny(b, value)
+		}
+	case []byte:
+		// In https://github.com/kubernetes/klog/pull/237 it was decided
+		// to format byte slices with "%+q". The advantages of that are:
+		// - readable output if the bytes happen to be printable
+		// - non-printable bytes get represented as unicode escape
+		//   sequences (\uxxxx)
+		//
+		// The downsides are that we cannot use the faster
+		// strconv.Quote here and that multi-line output is not
+		// supported. If developers know that a byte array is
+		// printable and they want multi-line output, they can
+		// convert the value to string before logging it.
+		b.WriteByte('=')
+		b.WriteString(fmt.Sprintf("%+q", v))
+	default:
+		f.formatAny(b, v)
+	}
+}
diff --git a/vendor/k8s.io/klog/v2/internal/serialize/keyvalues_slog.go b/vendor/k8s.io/klog/v2/internal/serialize/keyvalues_slog.go
new file mode 100644
index 00000000..89acf977
--- /dev/null
+++ b/vendor/k8s.io/klog/v2/internal/serialize/keyvalues_slog.go
@@ -0,0 +1,155 @@
+//go:build go1.21
+// +build go1.21
+
+/*
+Copyright 2023 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package serialize
+
+import (
+	"bytes"
+	"fmt"
+	"log/slog"
+	"strconv"
+
+	"github.com/go-logr/logr"
+)
+
+// KVFormat serializes one key/value pair into the provided buffer.
+// A space gets inserted before the pair.
+func (f Formatter) KVFormat(b *bytes.Buffer, k, v interface{}) {
+	// This is the version without slog support. Must be kept in sync with
+	// the version in keyvalues_slog.go.
+
+	b.WriteByte(' ')
+	// Keys are assumed to be well-formed according to
+	// https://github.com/kubernetes/community/blob/master/contributors/devel/sig-instrumentation/migration-to-structured-logging.md#name-arguments
+	// for the sake of performance. Keys with spaces,
+	// special characters, etc. will break parsing.
+	if sK, ok := k.(string); ok {
+		// Avoid one allocation when the key is a string, which
+		// normally it should be.
+		b.WriteString(sK)
+	} else {
+		b.WriteString(fmt.Sprintf("%s", k))
+	}
+
+	// The type checks are sorted so that more frequently used ones
+	// come first because that is then faster in the common
+	// cases. In Kubernetes, ObjectRef (a Stringer) is more common
+	// than plain strings
+	// (https://github.com/kubernetes/kubernetes/pull/106594#issuecomment-975526235).
+	//
+	// slog.LogValuer does not need to be handled here because the handler will
+	// already have resolved such special values to the final value for logging.
+	switch v := v.(type) {
+	case textWriter:
+		writeTextWriterValue(b, v)
+	case slog.Value:
+		// This must come before fmt.Stringer because slog.Value implements
+		// fmt.Stringer, but does not produce the output that we want.
+		b.WriteByte('=')
+		generateJSON(b, v)
+	case fmt.Stringer:
+		writeStringValue(b, StringerToString(v))
+	case string:
+		writeStringValue(b, v)
+	case error:
+		writeStringValue(b, ErrorToString(v))
+	case logr.Marshaler:
+		value := MarshalerToValue(v)
+		// A marshaler that returns a string is useful for
+		// delayed formatting of complex values. We treat this
+		// case like a normal string. This is useful for
+		// multi-line support.
+		//
+		// We could do this by recursively formatting a value,
+		// but that comes with the risk of infinite recursion
+		// if a marshaler returns itself. Instead we call it
+		// only once and rely on it returning the intended
+		// value directly.
+		switch value := value.(type) {
+		case string:
+			writeStringValue(b, value)
+		default:
+			f.formatAny(b, value)
+		}
+	case slog.LogValuer:
+		value := slog.AnyValue(v).Resolve()
+		if value.Kind() == slog.KindString {
+			writeStringValue(b, value.String())
+		} else {
+			b.WriteByte('=')
+			generateJSON(b, value)
+		}
+	case []byte:
+		// In https://github.com/kubernetes/klog/pull/237 it was decided
+		// to format byte slices with "%+q". The advantages of that are:
+		// - readable output if the bytes happen to be printable
+		// - non-printable bytes get represented as unicode escape
+		//   sequences (\uxxxx)
+		//
+		// The downsides are that we cannot use the faster
+		// strconv.Quote here and that multi-line output is not
+		// supported. If developers know that a byte array is
+		// printable and they want multi-line output, they can
+		// convert the value to string before logging it.
+		b.WriteByte('=')
+		b.WriteString(fmt.Sprintf("%+q", v))
+	default:
+		f.formatAny(b, v)
+	}
+}
+
+// generateJSON has the same preference for plain strings as KVFormat.
+// In contrast to KVFormat it always produces valid JSON with no line breaks.
+func generateJSON(b *bytes.Buffer, v interface{}) {
+	switch v := v.(type) {
+	case slog.Value:
+		switch v.Kind() {
+		case slog.KindGroup:
+			// Format as a JSON group. We must not involve f.AnyToStringHook (if there is any),
+			// because there is no guarantee that it produces valid JSON.
+			b.WriteByte('{')
+			for i, attr := range v.Group() {
+				if i > 0 {
+					b.WriteByte(',')
+				}
+				b.WriteString(strconv.Quote(attr.Key))
+				b.WriteByte(':')
+				generateJSON(b, attr.Value)
+			}
+			b.WriteByte('}')
+		case slog.KindLogValuer:
+			generateJSON(b, v.Resolve())
+		default:
+			// Peel off the slog.Value wrapper and format the actual value.
+			generateJSON(b, v.Any())
+		}
+	case fmt.Stringer:
+		b.WriteString(strconv.Quote(StringerToString(v)))
+	case logr.Marshaler:
+		generateJSON(b, MarshalerToValue(v))
+	case slog.LogValuer:
+		generateJSON(b, slog.AnyValue(v).Resolve().Any())
+	case string:
+		b.WriteString(strconv.Quote(v))
+	case error:
+		b.WriteString(strconv.Quote(v.Error()))
+	default:
+		formatAsJSON(b, v)
+	}
+}
diff --git a/vendor/k8s.io/klog/v2/internal/sloghandler/sloghandler_slog.go b/vendor/k8s.io/klog/v2/internal/sloghandler/sloghandler_slog.go
new file mode 100644
index 00000000..21f1697d
--- /dev/null
+++ b/vendor/k8s.io/klog/v2/internal/sloghandler/sloghandler_slog.go
@@ -0,0 +1,96 @@
+//go:build go1.21
+// +build go1.21
+
+/*
+Copyright 2023 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package sloghandler
+
+import (
+	"context"
+	"log/slog"
+	"runtime"
+	"strings"
+	"time"
+
+	"k8s.io/klog/v2/internal/severity"
+)
+
+func Handle(_ context.Context, record slog.Record, groups string, printWithInfos func(file string, line int, now time.Time, err error, s severity.Severity, msg string, kvList []interface{})) error {
+	now := record.Time
+	if now.IsZero() {
+		// This format doesn't support printing entries without a time.
+		now = time.Now()
+	}
+
+	// slog has numeric severity levels, with 0 as default "info", negative for debugging, and
+	// positive with some pre-defined levels for more important. Those ranges get mapped to
+	// the corresponding klog levels where possible, with "info" the default that is used
+	// also for negative debug levels.
+	level := record.Level
+	s := severity.InfoLog
+	switch {
+	case level >= slog.LevelError:
+		s = severity.ErrorLog
+	case level >= slog.LevelWarn:
+		s = severity.WarningLog
+	}
+
+	var file string
+	var line int
+	if record.PC != 0 {
+		// Same as https://cs.opensource.google/go/x/exp/+/642cacee:slog/record.go;drc=642cacee5cc05231f45555a333d07f1005ffc287;l=70
+		fs := runtime.CallersFrames([]uintptr{record.PC})
+		f, _ := fs.Next()
+		if f.File != "" {
+			file = f.File
+			if slash := strings.LastIndex(file, "/"); slash >= 0 {
+				file = file[slash+1:]
+			}
+			line = f.Line
+		}
+	} else {
+		file = "???"
+		line = 1
+	}
+
+	kvList := make([]interface{}, 0, 2*record.NumAttrs())
+	record.Attrs(func(attr slog.Attr) bool {
+		kvList = appendAttr(groups, kvList, attr)
+		return true
+	})
+
+	printWithInfos(file, line, now, nil, s, record.Message, kvList)
+	return nil
+}
+
+func Attrs2KVList(groups string, attrs []slog.Attr) []interface{} {
+	kvList := make([]interface{}, 0, 2*len(attrs))
+	for _, attr := range attrs {
+		kvList = appendAttr(groups, kvList, attr)
+	}
+	return kvList
+}
+
+func appendAttr(groups string, kvList []interface{}, attr slog.Attr) []interface{} {
+	var key string
+	if groups != "" {
+		key = groups + "." + attr.Key
+	} else {
+		key = attr.Key
+	}
+	return append(kvList, key, attr.Value)
+}
diff --git a/vendor/k8s.io/klog/v2/internal/verbosity/verbosity.go b/vendor/k8s.io/klog/v2/internal/verbosity/verbosity.go
new file mode 100644
index 00000000..40ec27d8
--- /dev/null
+++ b/vendor/k8s.io/klog/v2/internal/verbosity/verbosity.go
@@ -0,0 +1,303 @@
+/*
+Copyright 2013 Google Inc. All Rights Reserved.
+Copyright 2022 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 verbosity
+
+import (
+	"bytes"
+	"errors"
+	"flag"
+	"fmt"
+	"path/filepath"
+	"runtime"
+	"strconv"
+	"strings"
+	"sync"
+	"sync/atomic"
+)
+
+// New returns a struct that implements -v and -vmodule support. Changing and
+// checking these settings is thread-safe, with all concurrency issues handled
+// internally.
+func New() *VState {
+	vs := new(VState)
+
+	// The two fields must have a pointer to the overal struct for their
+	// implementation of Set.
+	vs.vmodule.vs = vs
+	vs.verbosity.vs = vs
+
+	return vs
+}
+
+// Value is an extension that makes it possible to use the values in pflag.
+type Value interface {
+	flag.Value
+	Type() string
+}
+
+func (vs *VState) V() Value {
+	return &vs.verbosity
+}
+
+func (vs *VState) VModule() Value {
+	return &vs.vmodule
+}
+
+// VState contains settings and state. Some of its fields can be accessed
+// through atomic read/writes, in other cases a mutex must be held.
+type VState struct {
+	mu sync.Mutex
+
+	// These flags are modified only under lock, although verbosity may be fetched
+	// safely using atomic.LoadInt32.
+	vmodule   moduleSpec // The state of the -vmodule flag.
+	verbosity levelSpec  // V logging level, the value of the -v flag/
+
+	// pcs is used in V to avoid an allocation when computing the caller's PC.
+	pcs [1]uintptr
+	// vmap is a cache of the V Level for each V() call site, identified by PC.
+	// It is wiped whenever the vmodule flag changes state.
+	vmap map[uintptr]Level
+	// filterLength stores the length of the vmodule filter chain. If greater
+	// than zero, it means vmodule is enabled. It may be read safely
+	// using sync.LoadInt32, but is only modified under mu.
+	filterLength int32
+}
+
+// Level must be an int32 to support atomic read/writes.
+type Level int32
+
+type levelSpec struct {
+	vs *VState
+	l  Level
+}
+
+// get returns the value of the level.
+func (l *levelSpec) get() Level {
+	return Level(atomic.LoadInt32((*int32)(&l.l)))
+}
+
+// set sets the value of the level.
+func (l *levelSpec) set(val Level) {
+	atomic.StoreInt32((*int32)(&l.l), int32(val))
+}
+
+// String is part of the flag.Value interface.
+func (l *levelSpec) String() string {
+	return strconv.FormatInt(int64(l.l), 10)
+}
+
+// Get is part of the flag.Getter interface. It returns the
+// verbosity level as int32.
+func (l *levelSpec) Get() interface{} {
+	return int32(l.l)
+}
+
+// Type is part of pflag.Value.
+func (l *levelSpec) Type() string {
+	return "Level"
+}
+
+// Set is part of the flag.Value interface.
+func (l *levelSpec) Set(value string) error {
+	v, err := strconv.ParseInt(value, 10, 32)
+	if err != nil {
+		return err
+	}
+	l.vs.mu.Lock()
+	defer l.vs.mu.Unlock()
+	l.vs.set(Level(v), l.vs.vmodule.filter, false)
+	return nil
+}
+
+// moduleSpec represents the setting of the -vmodule flag.
+type moduleSpec struct {
+	vs     *VState
+	filter []modulePat
+}
+
+// modulePat contains a filter for the -vmodule flag.
+// It holds a verbosity level and a file pattern to match.
+type modulePat struct {
+	pattern string
+	literal bool // The pattern is a literal string
+	level   Level
+}
+
+// match reports whether the file matches the pattern. It uses a string
+// comparison if the pattern contains no metacharacters.
+func (m *modulePat) match(file string) bool {
+	if m.literal {
+		return file == m.pattern
+	}
+	match, _ := filepath.Match(m.pattern, file)
+	return match
+}
+
+func (m *moduleSpec) String() string {
+	// Lock because the type is not atomic. TODO: clean this up.
+	// Empty instances don't have and don't need a lock (can
+	// happen when flag uses introspection).
+	if m.vs != nil {
+		m.vs.mu.Lock()
+		defer m.vs.mu.Unlock()
+	}
+	var b bytes.Buffer
+	for i, f := range m.filter {
+		if i > 0 {
+			b.WriteRune(',')
+		}
+		fmt.Fprintf(&b, "%s=%d", f.pattern, f.level)
+	}
+	return b.String()
+}
+
+// Get is part of the (Go 1.2)  flag.Getter interface. It always returns nil for this flag type since the
+// struct is not exported.
+func (m *moduleSpec) Get() interface{} {
+	return nil
+}
+
+// Type is part of pflag.Value
+func (m *moduleSpec) Type() string {
+	return "pattern=N,..."
+}
+
+var errVmoduleSyntax = errors.New("syntax error: expect comma-separated list of filename=N")
+
+// Set will sets module value
+// Syntax: -vmodule=recordio=2,file=1,gfs*=3
+func (m *moduleSpec) Set(value string) error {
+	var filter []modulePat
+	for _, pat := range strings.Split(value, ",") {
+		if len(pat) == 0 {
+			// Empty strings such as from a trailing comma can be ignored.
+			continue
+		}
+		patLev := strings.Split(pat, "=")
+		if len(patLev) != 2 || len(patLev[0]) == 0 || len(patLev[1]) == 0 {
+			return errVmoduleSyntax
+		}
+		pattern := patLev[0]
+		v, err := strconv.ParseInt(patLev[1], 10, 32)
+		if err != nil {
+			return errors.New("syntax error: expect comma-separated list of filename=N")
+		}
+		if v < 0 {
+			return errors.New("negative value for vmodule level")
+		}
+		if v == 0 {
+			continue // Ignore. It's harmless but no point in paying the overhead.
+		}
+		// TODO: check syntax of filter?
+		filter = append(filter, modulePat{pattern, isLiteral(pattern), Level(v)})
+	}
+	m.vs.mu.Lock()
+	defer m.vs.mu.Unlock()
+	m.vs.set(m.vs.verbosity.l, filter, true)
+	return nil
+}
+
+// isLiteral reports whether the pattern is a literal string, that is, has no metacharacters
+// that require filepath.Match to be called to match the pattern.
+func isLiteral(pattern string) bool {
+	return !strings.ContainsAny(pattern, `\*?[]`)
+}
+
+// set sets a consistent state for V logging.
+// The mutex must be held.
+func (vs *VState) set(l Level, filter []modulePat, setFilter bool) {
+	// Turn verbosity off so V will not fire while we are in transition.
+	vs.verbosity.set(0)
+	// Ditto for filter length.
+	atomic.StoreInt32(&vs.filterLength, 0)
+
+	// Set the new filters and wipe the pc->Level map if the filter has changed.
+	if setFilter {
+		vs.vmodule.filter = filter
+		vs.vmap = make(map[uintptr]Level)
+	}
+
+	// Things are consistent now, so enable filtering and verbosity.
+	// They are enabled in order opposite to that in V.
+	atomic.StoreInt32(&vs.filterLength, int32(len(filter)))
+	vs.verbosity.set(l)
+}
+
+// Enabled checks whether logging is enabled at the given level. This must be
+// called with depth=0 when the caller of enabled will do the logging and
+// higher values when more stack levels need to be skipped.
+//
+// The mutex will be locked only if needed.
+func (vs *VState) Enabled(level Level, depth int) bool {
+	// This function tries hard to be cheap unless there's work to do.
+	// The fast path is two atomic loads and compares.
+
+	// Here is a cheap but safe test to see if V logging is enabled globally.
+	if vs.verbosity.get() >= level {
+		return true
+	}
+
+	// It's off globally but vmodule may still be set.
+	// Here is another cheap but safe test to see if vmodule is enabled.
+	if atomic.LoadInt32(&vs.filterLength) > 0 {
+		// Now we need a proper lock to use the logging structure. The pcs field
+		// is shared so we must lock before accessing it. This is fairly expensive,
+		// but if V logging is enabled we're slow anyway.
+		vs.mu.Lock()
+		defer vs.mu.Unlock()
+		if runtime.Callers(depth+2, vs.pcs[:]) == 0 {
+			return false
+		}
+		// runtime.Callers returns "return PCs", but we want
+		// to look up the symbolic information for the call,
+		// so subtract 1 from the PC. runtime.CallersFrames
+		// would be cleaner, but allocates.
+		pc := vs.pcs[0] - 1
+		v, ok := vs.vmap[pc]
+		if !ok {
+			v = vs.setV(pc)
+		}
+		return v >= level
+	}
+	return false
+}
+
+// setV computes and remembers the V level for a given PC
+// when vmodule is enabled.
+// File pattern matching takes the basename of the file, stripped
+// of its .go suffix, and uses filepath.Match, which is a little more
+// general than the *? matching used in C++.
+// Mutex is held.
+func (vs *VState) setV(pc uintptr) Level {
+	fn := runtime.FuncForPC(pc)
+	file, _ := fn.FileLine(pc)
+	// The file is something like /a/b/c/d.go. We want just the d.
+	file = strings.TrimSuffix(file, ".go")
+	if slash := strings.LastIndex(file, "/"); slash >= 0 {
+		file = file[slash+1:]
+	}
+	for _, filter := range vs.vmodule.filter {
+		if filter.match(file) {
+			vs.vmap[pc] = filter.level
+			return filter.level
+		}
+	}
+	vs.vmap[pc] = 0
+	return 0
+}
diff --git a/vendor/k8s.io/klog/v2/k8s_references_slog.go b/vendor/k8s.io/klog/v2/k8s_references_slog.go
new file mode 100644
index 00000000..5522c84c
--- /dev/null
+++ b/vendor/k8s.io/klog/v2/k8s_references_slog.go
@@ -0,0 +1,39 @@
+//go:build go1.21
+// +build go1.21
+
+/*
+Copyright 2021 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 klog
+
+import (
+	"log/slog"
+)
+
+func (ref ObjectRef) LogValue() slog.Value {
+	if ref.Namespace != "" {
+		return slog.GroupValue(slog.String("name", ref.Name), slog.String("namespace", ref.Namespace))
+	}
+	return slog.GroupValue(slog.String("name", ref.Name))
+}
+
+var _ slog.LogValuer = ObjectRef{}
+
+func (ks kobjSlice) LogValue() slog.Value {
+	return slog.AnyValue(ks.MarshalLog())
+}
+
+var _ slog.LogValuer = kobjSlice{}
diff --git a/vendor/k8s.io/klog/v2/klog.go b/vendor/k8s.io/klog/v2/klog.go
index 152f8a6b..72502db3 100644
--- a/vendor/k8s.io/klog/v2/klog.go
+++ b/vendor/k8s.io/klog/v2/klog.go
@@ -415,7 +415,7 @@ func init() {
 	logging.stderrThreshold = severityValue{
 		Severity: severity.ErrorLog, // Default stderrThreshold is ERROR.
 	}
-	commandLine.Var(&logging.stderrThreshold, "stderrthreshold", "logs at or above this threshold go to stderr when writing to files and stderr (no effect when -logtostderr=true or -alsologtostderr=false)")
+	commandLine.Var(&logging.stderrThreshold, "stderrthreshold", "logs at or above this threshold go to stderr when writing to files and stderr (no effect when -logtostderr=true or -alsologtostderr=true)")
 	commandLine.Var(&logging.vmodule, "vmodule", "comma-separated list of pattern=N settings for file-filtered logging")
 	commandLine.Var(&logging.traceLocation, "log_backtrace_at", "when logging hits line file:N, emit a stack trace")
 
@@ -518,9 +518,7 @@ type settings struct {
 func (s settings) deepCopy() settings {
 	// vmodule is a slice and would be shared, so we have copy it.
 	filter := make([]modulePat, len(s.vmodule.filter))
-	for i := range s.vmodule.filter {
-		filter[i] = s.vmodule.filter[i]
-	}
+	copy(filter, s.vmodule.filter)
 	s.vmodule.filter = filter
 
 	if s.logger != nil {
@@ -657,16 +655,15 @@ func (l *loggingT) header(s severity.Severity, depth int) (*buffer.Buffer, strin
 			}
 		}
 	}
-	return l.formatHeader(s, file, line), file, line
+	return l.formatHeader(s, file, line, timeNow()), file, line
 }
 
 // formatHeader formats a log header using the provided file name and line number.
-func (l *loggingT) formatHeader(s severity.Severity, file string, line int) *buffer.Buffer {
+func (l *loggingT) formatHeader(s severity.Severity, file string, line int, now time.Time) *buffer.Buffer {
 	buf := buffer.GetBuffer()
 	if l.skipHeaders {
 		return buf
 	}
-	now := timeNow()
 	buf.FormatHeader(s, file, line, now)
 	return buf
 }
@@ -676,6 +673,10 @@ func (l *loggingT) println(s severity.Severity, logger *logWriter, filter LogFil
 }
 
 func (l *loggingT) printlnDepth(s severity.Severity, logger *logWriter, filter LogFilter, depth int, args ...interface{}) {
+	if false {
+		_ = fmt.Sprintln(args...) // cause vet to treat this function like fmt.Println
+	}
+
 	buf, file, line := l.header(s, depth)
 	// If a logger is set and doesn't support writing a formatted buffer,
 	// we clear the generated header as we rely on the backing
@@ -696,7 +697,15 @@ func (l *loggingT) print(s severity.Severity, logger *logWriter, filter LogFilte
 }
 
 func (l *loggingT) printDepth(s severity.Severity, logger *logWriter, filter LogFilter, depth int, args ...interface{}) {
+	if false {
+		_ = fmt.Sprint(args...) //  // cause vet to treat this function like fmt.Print
+	}
+
 	buf, file, line := l.header(s, depth)
+	l.printWithInfos(buf, file, line, s, logger, filter, depth+1, args...)
+}
+
+func (l *loggingT) printWithInfos(buf *buffer.Buffer, file string, line int, s severity.Severity, logger *logWriter, filter LogFilter, depth int, args ...interface{}) {
 	// If a logger is set and doesn't support writing a formatted buffer,
 	// we clear the generated header as we rely on the backing
 	// logger implementation to print headers.
@@ -719,6 +728,10 @@ func (l *loggingT) printf(s severity.Severity, logger *logWriter, filter LogFilt
 }
 
 func (l *loggingT) printfDepth(s severity.Severity, logger *logWriter, filter LogFilter, depth int, format string, args ...interface{}) {
+	if false {
+		_ = fmt.Sprintf(format, args...) // cause vet to treat this function like fmt.Printf
+	}
+
 	buf, file, line := l.header(s, depth)
 	// If a logger is set and doesn't support writing a formatted buffer,
 	// we clear the generated header as we rely on the backing
@@ -741,7 +754,7 @@ func (l *loggingT) printfDepth(s severity.Severity, logger *logWriter, filter Lo
 // alsoLogToStderr is true, the log message always appears on standard error; it
 // will also appear in the log file unless --logtostderr is set.
 func (l *loggingT) printWithFileLine(s severity.Severity, logger *logWriter, filter LogFilter, file string, line int, alsoToStderr bool, args ...interface{}) {
-	buf := l.formatHeader(s, file, line)
+	buf := l.formatHeader(s, file, line, timeNow())
 	// If a logger is set and doesn't support writing a formatted buffer,
 	// we clear the generated header as we rely on the backing
 	// logger implementation to print headers.
@@ -759,7 +772,7 @@ func (l *loggingT) printWithFileLine(s severity.Severity, logger *logWriter, fil
 	l.output(s, logger, buf, 2 /* depth */, file, line, alsoToStderr)
 }
 
-// if loggr is specified, will call loggr.Error, otherwise output with logging module.
+// if logger is specified, will call logger.Error, otherwise output with logging module.
 func (l *loggingT) errorS(err error, logger *logWriter, filter LogFilter, depth int, msg string, keysAndValues ...interface{}) {
 	if filter != nil {
 		msg, keysAndValues = filter.FilterS(msg, keysAndValues)
@@ -771,7 +784,7 @@ func (l *loggingT) errorS(err error, logger *logWriter, filter LogFilter, depth
 	l.printS(err, severity.ErrorLog, depth+1, msg, keysAndValues...)
 }
 
-// if loggr is specified, will call loggr.Info, otherwise output with logging module.
+// if logger is specified, will call logger.Info, otherwise output with logging module.
 func (l *loggingT) infoS(logger *logWriter, filter LogFilter, depth int, msg string, keysAndValues ...interface{}) {
 	if filter != nil {
 		msg, keysAndValues = filter.FilterS(msg, keysAndValues)
@@ -783,7 +796,7 @@ func (l *loggingT) infoS(logger *logWriter, filter LogFilter, depth int, msg str
 	l.printS(nil, severity.InfoLog, depth+1, msg, keysAndValues...)
 }
 
-// printS is called from infoS and errorS if loggr is not specified.
+// printS is called from infoS and errorS if logger is not specified.
 // set log severity by s
 func (l *loggingT) printS(err error, s severity.Severity, depth int, msg string, keysAndValues ...interface{}) {
 	// Only create a new buffer if we don't have one cached.
@@ -796,7 +809,7 @@ func (l *loggingT) printS(err error, s severity.Severity, depth int, msg string,
 		serialize.KVListFormat(&b.Buffer, "err", err)
 	}
 	serialize.KVListFormat(&b.Buffer, keysAndValues...)
-	l.printDepth(s, logging.logger, nil, depth+1, &b.Buffer)
+	l.printDepth(s, nil, nil, depth+1, &b.Buffer)
 	// Make the buffer available for reuse.
 	buffer.PutBuffer(b)
 }
@@ -873,6 +886,9 @@ func (l *loggingT) output(s severity.Severity, logger *logWriter, buf *buffer.Bu
 		if logger.writeKlogBuffer != nil {
 			logger.writeKlogBuffer(data)
 		} else {
+			if len(data) > 0 && data[len(data)-1] == '\n' {
+				data = data[:len(data)-1]
+			}
 			// TODO: set 'severity' and caller information as structured log info
 			// keysAndValues := []interface{}{"severity", severityName[s], "file", file, "line", line}
 			if s == severity.ErrorLog {
@@ -897,7 +913,7 @@ func (l *loggingT) output(s severity.Severity, logger *logWriter, buf *buffer.Bu
 					l.exit(err)
 				}
 			}
-			l.file[severity.InfoLog].Write(data)
+			_, _ = l.file[severity.InfoLog].Write(data)
 		} else {
 			if l.file[s] == nil {
 				if err := l.createFiles(s); err != nil {
@@ -907,20 +923,20 @@ func (l *loggingT) output(s severity.Severity, logger *logWriter, buf *buffer.Bu
 			}
 
 			if l.oneOutput {
-				l.file[s].Write(data)
+				_, _ = l.file[s].Write(data)
 			} else {
 				switch s {
 				case severity.FatalLog:
-					l.file[severity.FatalLog].Write(data)
+					_, _ = l.file[severity.FatalLog].Write(data)
 					fallthrough
 				case severity.ErrorLog:
-					l.file[severity.ErrorLog].Write(data)
+					_, _ = l.file[severity.ErrorLog].Write(data)
 					fallthrough
 				case severity.WarningLog:
-					l.file[severity.WarningLog].Write(data)
+					_, _ = l.file[severity.WarningLog].Write(data)
 					fallthrough
 				case severity.InfoLog:
-					l.file[severity.InfoLog].Write(data)
+					_, _ = l.file[severity.InfoLog].Write(data)
 				}
 			}
 		}
@@ -946,7 +962,7 @@ func (l *loggingT) output(s severity.Severity, logger *logWriter, buf *buffer.Bu
 		logExitFunc = func(error) {} // If we get a write error, we'll still exit below.
 		for log := severity.FatalLog; log >= severity.InfoLog; log-- {
 			if f := l.file[log]; f != nil { // Can be nil if -logtostderr is set.
-				f.Write(trace)
+				_, _ = f.Write(trace)
 			}
 		}
 		l.mu.Unlock()
@@ -1102,7 +1118,7 @@ const flushInterval = 5 * time.Second
 // flushDaemon periodically flushes the log file buffers.
 type flushDaemon struct {
 	mu       sync.Mutex
-	clock    clock.WithTicker
+	clock    clock.Clock
 	flush    func()
 	stopC    chan struct{}
 	stopDone chan struct{}
@@ -1110,7 +1126,7 @@ type flushDaemon struct {
 
 // newFlushDaemon returns a new flushDaemon. If the passed clock is nil, a
 // clock.RealClock is used.
-func newFlushDaemon(flush func(), tickClock clock.WithTicker) *flushDaemon {
+func newFlushDaemon(flush func(), tickClock clock.Clock) *flushDaemon {
 	if tickClock == nil {
 		tickClock = clock.RealClock{}
 	}
@@ -1201,8 +1217,8 @@ func (l *loggingT) flushAll() {
 	for s := severity.FatalLog; s >= severity.InfoLog; s-- {
 		file := l.file[s]
 		if file != nil {
-			file.Flush() // ignore error
-			file.Sync()  // ignore error
+			_ = file.Flush() // ignore error
+			_ = file.Sync()  // ignore error
 		}
 	}
 	if logging.loggerOptions.flush != nil {
@@ -1281,9 +1297,7 @@ func (l *loggingT) setV(pc uintptr) Level {
 	fn := runtime.FuncForPC(pc)
 	file, _ := fn.FileLine(pc)
 	// The file is something like /a/b/c/d.go. We want just the d.
-	if strings.HasSuffix(file, ".go") {
-		file = file[:len(file)-3]
-	}
+	file = strings.TrimSuffix(file, ".go")
 	if slash := strings.LastIndex(file, "/"); slash >= 0 {
 		file = file[slash+1:]
 	}
diff --git a/vendor/k8s.io/klog/v2/klog_file.go b/vendor/k8s.io/klog/v2/klog_file.go
index 1025d644..8bee1620 100644
--- a/vendor/k8s.io/klog/v2/klog_file.go
+++ b/vendor/k8s.io/klog/v2/klog_file.go
@@ -109,8 +109,8 @@ func create(tag string, t time.Time, startup bool) (f *os.File, filename string,
 		f, err := openOrCreate(fname, startup)
 		if err == nil {
 			symlink := filepath.Join(dir, link)
-			os.Remove(symlink)        // ignore err
-			os.Symlink(name, symlink) // ignore err
+			_ = os.Remove(symlink)        // ignore err
+			_ = os.Symlink(name, symlink) // ignore err
 			return f, fname, nil
 		}
 		lastErr = err
diff --git a/vendor/k8s.io/klog/v2/klogr.go b/vendor/k8s.io/klog/v2/klogr.go
index 15de00e2..efec96fd 100644
--- a/vendor/k8s.io/klog/v2/klogr.go
+++ b/vendor/k8s.io/klog/v2/klogr.go
@@ -22,6 +22,11 @@ import (
 	"k8s.io/klog/v2/internal/serialize"
 )
 
+const (
+	// nameKey is used to log the `WithName` values as an additional attribute.
+	nameKey = "logger"
+)
+
 // NewKlogr returns a logger that is functionally identical to
 // klogr.NewWithOptions(klogr.FormatKlog), i.e. it passes through to klog. The
 // difference is that it uses a simpler implementation.
@@ -32,10 +37,15 @@ func NewKlogr() Logger {
 // klogger is a subset of klogr/klogr.go. It had to be copied to break an
 // import cycle (klogr wants to use klog, and klog wants to use klogr).
 type klogger struct {
-	level     int
 	callDepth int
-	prefix    string
-	values    []interface{}
+
+	// hasPrefix is true if the first entry in values is the special
+	// nameKey key/value. Such an entry gets added and later updated in
+	// WithName.
+	hasPrefix bool
+
+	values []interface{}
+	groups string
 }
 
 func (l *klogger) Init(info logr.RuntimeInfo) {
@@ -44,34 +54,40 @@ func (l *klogger) Init(info logr.RuntimeInfo) {
 
 func (l *klogger) Info(level int, msg string, kvList ...interface{}) {
 	merged := serialize.MergeKVs(l.values, kvList)
-	if l.prefix != "" {
-		msg = l.prefix + ": " + msg
-	}
 	// Skip this function.
 	VDepth(l.callDepth+1, Level(level)).InfoSDepth(l.callDepth+1, msg, merged...)
 }
 
 func (l *klogger) Enabled(level int) bool {
-	// Skip this function and logr.Logger.Info where Enabled is called.
-	return VDepth(l.callDepth+2, Level(level)).Enabled()
+	return VDepth(l.callDepth+1, Level(level)).Enabled()
 }
 
 func (l *klogger) Error(err error, msg string, kvList ...interface{}) {
 	merged := serialize.MergeKVs(l.values, kvList)
-	if l.prefix != "" {
-		msg = l.prefix + ": " + msg
-	}
 	ErrorSDepth(l.callDepth+1, err, msg, merged...)
 }
 
 // WithName returns a new logr.Logger with the specified name appended.  klogr
-// uses '/' characters to separate name elements.  Callers should not pass '/'
+// uses '.' characters to separate name elements.  Callers should not pass '.'
 // in the provided name string, but this library does not actually enforce that.
 func (l klogger) WithName(name string) logr.LogSink {
-	if len(l.prefix) > 0 {
-		l.prefix = l.prefix + "/"
+	if l.hasPrefix {
+		// Copy slice and modify value. No length checks and type
+		// assertions are needed because hasPrefix is only true if the
+		// first two elements exist and are key/value strings.
+		v := make([]interface{}, 0, len(l.values))
+		v = append(v, l.values...)
+		prefix, _ := v[1].(string)
+		v[1] = prefix + "." + name
+		l.values = v
+	} else {
+		// Preprend new key/value pair.
+		v := make([]interface{}, 0, 2+len(l.values))
+		v = append(v, nameKey, name)
+		v = append(v, l.values...)
+		l.values = v
+		l.hasPrefix = true
 	}
-	l.prefix += name
 	return &l
 }
 
diff --git a/vendor/k8s.io/klog/v2/klogr/klogr.go b/vendor/k8s.io/klog/v2/klogr/klogr.go
index db5b5c78..a154960c 100644
--- a/vendor/k8s.io/klog/v2/klogr/klogr.go
+++ b/vendor/k8s.io/klog/v2/klogr/klogr.go
@@ -15,6 +15,11 @@ import (
 	"k8s.io/klog/v2/internal/serialize"
 )
 
+const (
+	// nameKey is used to log the `WithName` values as an additional attribute.
+	nameKey = "logger"
+)
+
 // Option is a functional option that reconfigures the logger created with New.
 type Option func(*klogger)
 
@@ -23,7 +28,7 @@ type Format string
 
 const (
 	// FormatSerialize tells klogr to turn key/value pairs into text itself
-	// before invoking klog.
+	// before invoking klog. Key/value pairs are sorted by key.
 	FormatSerialize Format = "Serialize"
 
 	// FormatKlog tells klogr to pass all text messages and key/value pairs
@@ -41,6 +46,8 @@ func WithFormat(format Format) Option {
 
 // New returns a logr.Logger which serializes output itself
 // and writes it via klog.
+//
+// Deprecated: this uses a custom, out-dated output format. Use textlogger.NewLogger instead.
 func New() logr.Logger {
 	return NewWithOptions(WithFormat(FormatSerialize))
 }
@@ -48,10 +55,11 @@ func New() logr.Logger {
 // NewWithOptions returns a logr.Logger which serializes as determined
 // by the WithFormat option and writes via klog. The default is
 // FormatKlog.
+//
+// Deprecated: FormatSerialize is out-dated. For FormatKlog, use textlogger.NewLogger instead.
 func NewWithOptions(options ...Option) logr.Logger {
 	l := klogger{
 		level:  0,
-		prefix: "",
 		values: nil,
 		format: FormatKlog,
 	}
@@ -64,9 +72,14 @@ func NewWithOptions(options ...Option) logr.Logger {
 type klogger struct {
 	level     int
 	callDepth int
-	prefix    string
-	values    []interface{}
-	format    Format
+
+	// hasPrefix is true if the first entry in values is the special
+	// nameKey key/value. Such an entry gets added and later updated in
+	// WithName.
+	hasPrefix bool
+
+	values []interface{}
+	format Format
 }
 
 func (l *klogger) Init(info logr.RuntimeInfo) {
@@ -115,8 +128,10 @@ func pretty(value interface{}) string {
 	buffer := &bytes.Buffer{}
 	encoder := json.NewEncoder(buffer)
 	encoder.SetEscapeHTML(false)
-	encoder.Encode(value)
-	return strings.TrimSpace(string(buffer.Bytes()))
+	if err := encoder.Encode(value); err != nil {
+		return fmt.Sprintf("<<error: %v>>", err)
+	}
+	return strings.TrimSpace(buffer.String())
 }
 
 func (l *klogger) Info(level int, msg string, kvList ...interface{}) {
@@ -125,19 +140,15 @@ func (l *klogger) Info(level int, msg string, kvList ...interface{}) {
 		msgStr := flatten("msg", msg)
 		merged := serialize.MergeKVs(l.values, kvList)
 		kvStr := flatten(merged...)
-		klog.VDepth(l.callDepth+1, klog.Level(level)).InfoDepth(l.callDepth+1, l.prefix, " ", msgStr, " ", kvStr)
+		klog.VDepth(l.callDepth+1, klog.Level(level)).InfoDepth(l.callDepth+1, msgStr, " ", kvStr)
 	case FormatKlog:
 		merged := serialize.MergeKVs(l.values, kvList)
-		if l.prefix != "" {
-			msg = l.prefix + ": " + msg
-		}
 		klog.VDepth(l.callDepth+1, klog.Level(level)).InfoSDepth(l.callDepth+1, msg, merged...)
 	}
 }
 
 func (l *klogger) Enabled(level int) bool {
-	// Skip this function and logr.Logger.Info where Enabled is called.
-	return klog.VDepth(l.callDepth+2, klog.Level(level)).Enabled()
+	return klog.VDepth(l.callDepth+1, klog.Level(level)).Enabled()
 }
 
 func (l *klogger) Error(err error, msg string, kvList ...interface{}) {
@@ -151,24 +162,35 @@ func (l *klogger) Error(err error, msg string, kvList ...interface{}) {
 		errStr := flatten("error", loggableErr)
 		merged := serialize.MergeKVs(l.values, kvList)
 		kvStr := flatten(merged...)
-		klog.ErrorDepth(l.callDepth+1, l.prefix, " ", msgStr, " ", errStr, " ", kvStr)
+		klog.ErrorDepth(l.callDepth+1, msgStr, " ", errStr, " ", kvStr)
 	case FormatKlog:
 		merged := serialize.MergeKVs(l.values, kvList)
-		if l.prefix != "" {
-			msg = l.prefix + ": " + msg
-		}
 		klog.ErrorSDepth(l.callDepth+1, err, msg, merged...)
 	}
 }
 
 // WithName returns a new logr.Logger with the specified name appended.  klogr
-// uses '/' characters to separate name elements.  Callers should not pass '/'
+// uses '.' characters to separate name elements.  Callers should not pass '.'
 // in the provided name string, but this library does not actually enforce that.
 func (l klogger) WithName(name string) logr.LogSink {
-	if len(l.prefix) > 0 {
-		l.prefix = l.prefix + "/"
+	if l.hasPrefix {
+		// Copy slice and modify value. No length checks and type
+		// assertions are needed because hasPrefix is only true if the
+		// first two elements exist and are key/value strings.
+		v := make([]interface{}, 0, len(l.values))
+		v = append(v, l.values...)
+		prefix, _ := v[1].(string)
+		prefix = prefix + "." + name
+		v[1] = prefix
+		l.values = v
+	} else {
+		// Preprend new key/value pair.
+		v := make([]interface{}, 0, 2+len(l.values))
+		v = append(v, nameKey, name)
+		v = append(v, l.values...)
+		l.values = v
+		l.hasPrefix = true
 	}
-	l.prefix += name
 	return &l
 }
 
diff --git a/vendor/k8s.io/klog/v2/klogr_slog.go b/vendor/k8s.io/klog/v2/klogr_slog.go
new file mode 100644
index 00000000..f7bf7403
--- /dev/null
+++ b/vendor/k8s.io/klog/v2/klogr_slog.go
@@ -0,0 +1,96 @@
+//go:build go1.21
+// +build go1.21
+
+/*
+Copyright 2023 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package klog
+
+import (
+	"context"
+	"log/slog"
+	"strconv"
+	"time"
+
+	"github.com/go-logr/logr/slogr"
+
+	"k8s.io/klog/v2/internal/buffer"
+	"k8s.io/klog/v2/internal/serialize"
+	"k8s.io/klog/v2/internal/severity"
+	"k8s.io/klog/v2/internal/sloghandler"
+)
+
+func (l *klogger) Handle(ctx context.Context, record slog.Record) error {
+	if logging.logger != nil {
+		if slogSink, ok := logging.logger.GetSink().(slogr.SlogSink); ok {
+			// Let that logger do the work.
+			return slogSink.Handle(ctx, record)
+		}
+	}
+
+	return sloghandler.Handle(ctx, record, l.groups, slogOutput)
+}
+
+// slogOutput corresponds to several different functions in klog.go.
+// It goes through some of the same checks and formatting steps before
+// it ultimately converges by calling logging.printWithInfos.
+func slogOutput(file string, line int, now time.Time, err error, s severity.Severity, msg string, kvList []interface{}) {
+	// See infoS.
+	if logging.logger != nil {
+		// Taking this path happens when klog has a logger installed
+		// as backend which doesn't support slog. Not good, we have to
+		// guess about the call depth and drop the actual location.
+		logger := logging.logger.WithCallDepth(2)
+		if s > severity.ErrorLog {
+			logger.Error(err, msg, kvList...)
+		} else {
+			logger.Info(msg, kvList...)
+		}
+		return
+	}
+
+	// See printS.
+	b := buffer.GetBuffer()
+	b.WriteString(strconv.Quote(msg))
+	if err != nil {
+		serialize.KVListFormat(&b.Buffer, "err", err)
+	}
+	serialize.KVListFormat(&b.Buffer, kvList...)
+
+	// See print + header.
+	buf := logging.formatHeader(s, file, line, now)
+	logging.printWithInfos(buf, file, line, s, nil, nil, 0, &b.Buffer)
+
+	buffer.PutBuffer(b)
+}
+
+func (l *klogger) WithAttrs(attrs []slog.Attr) slogr.SlogSink {
+	clone := *l
+	clone.values = serialize.WithValues(l.values, sloghandler.Attrs2KVList(l.groups, attrs))
+	return &clone
+}
+
+func (l *klogger) WithGroup(name string) slogr.SlogSink {
+	clone := *l
+	if clone.groups != "" {
+		clone.groups += "." + name
+	} else {
+		clone.groups = name
+	}
+	return &clone
+}
+
+var _ slogr.SlogSink = &klogger{}
diff --git a/vendor/k8s.io/klog/v2/textlogger/options.go b/vendor/k8s.io/klog/v2/textlogger/options.go
new file mode 100644
index 00000000..a5c25300
--- /dev/null
+++ b/vendor/k8s.io/klog/v2/textlogger/options.go
@@ -0,0 +1,133 @@
+/*
+Copyright 2021 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 textlogger
+
+import (
+	"flag"
+	"io"
+	"os"
+	"strconv"
+	"time"
+
+	"k8s.io/klog/v2/internal/verbosity"
+)
+
+// Config influences logging in a text logger. To make this configurable via
+// command line flags, instantiate this once per program and use AddFlags to
+// bind command line flags to the instance before passing it to NewTestContext.
+//
+// Must be constructed with NewConfig.
+type Config struct {
+	vstate *verbosity.VState
+	co     configOptions
+}
+
+// Verbosity returns a value instance that can be used to query (via String) or
+// modify (via Set) the verbosity threshold. This is thread-safe and can be
+// done at runtime.
+func (c *Config) Verbosity() flag.Value {
+	return c.vstate.V()
+}
+
+// VModule returns a value instance that can be used to query (via String) or
+// modify (via Set) the vmodule settings. This is thread-safe and can be done
+// at runtime.
+func (c *Config) VModule() flag.Value {
+	return c.vstate.VModule()
+}
+
+// ConfigOption implements functional parameters for NewConfig.
+type ConfigOption func(co *configOptions)
+
+type configOptions struct {
+	verbosityFlagName string
+	vmoduleFlagName   string
+	verbosityDefault  int
+	fixedTime         *time.Time
+	output            io.Writer
+}
+
+// VerbosityFlagName overrides the default -v for the verbosity level.
+func VerbosityFlagName(name string) ConfigOption {
+	return func(co *configOptions) {
+
+		co.verbosityFlagName = name
+	}
+}
+
+// VModulFlagName overrides the default -vmodule for the per-module
+// verbosity levels.
+func VModuleFlagName(name string) ConfigOption {
+	return func(co *configOptions) {
+		co.vmoduleFlagName = name
+	}
+}
+
+// Verbosity overrides the default verbosity level of 0.
+// See https://github.com/kubernetes/community/blob/9406b4352fe2d5810cb21cc3cb059ce5886de157/contributors/devel/sig-instrumentation/logging.md#logging-conventions
+// for log level conventions in Kubernetes.
+func Verbosity(level int) ConfigOption {
+	return func(co *configOptions) {
+		co.verbosityDefault = level
+	}
+}
+
+// Output overrides stderr as the output stream.
+func Output(output io.Writer) ConfigOption {
+	return func(co *configOptions) {
+		co.output = output
+	}
+}
+
+// FixedTime overrides the actual time with a fixed time. Useful only for testing.
+//
+// # Experimental
+//
+// Notice: This function is EXPERIMENTAL and may be changed or removed in a
+// later release.
+func FixedTime(ts time.Time) ConfigOption {
+	return func(co *configOptions) {
+		co.fixedTime = &ts
+	}
+}
+
+// NewConfig returns a configuration with recommended defaults and optional
+// modifications. Command line flags are not bound to any FlagSet yet.
+func NewConfig(opts ...ConfigOption) *Config {
+	c := &Config{
+		vstate: verbosity.New(),
+		co: configOptions{
+			verbosityFlagName: "v",
+			vmoduleFlagName:   "vmodule",
+			verbosityDefault:  0,
+			output:            os.Stderr,
+		},
+	}
+	for _, opt := range opts {
+		opt(&c.co)
+	}
+
+	// Cannot fail for this input.
+	_ = c.Verbosity().Set(strconv.FormatInt(int64(c.co.verbosityDefault), 10))
+	return c
+}
+
+// AddFlags registers the command line flags that control the configuration.
+func (c *Config) AddFlags(fs *flag.FlagSet) {
+	fs.Var(c.Verbosity(), c.co.verbosityFlagName, "number for the log level verbosity of the testing logger")
+	fs.Var(c.VModule(), c.co.vmoduleFlagName, "comma-separated list of pattern=N log level settings for files matching the patterns")
+}
diff --git a/vendor/k8s.io/klog/v2/textlogger/textlogger.go b/vendor/k8s.io/klog/v2/textlogger/textlogger.go
new file mode 100644
index 00000000..235ecff5
--- /dev/null
+++ b/vendor/k8s.io/klog/v2/textlogger/textlogger.go
@@ -0,0 +1,179 @@
+/*
+Copyright 2019 The Kubernetes Authors.
+Copyright 2020 Intel Coporation.
+
+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 textlogger contains an implementation of the logr interface
+// which is producing the exact same output as klog.
+package textlogger
+
+import (
+	"runtime"
+	"strconv"
+	"strings"
+	"time"
+
+	"github.com/go-logr/logr"
+
+	"k8s.io/klog/v2/internal/buffer"
+	"k8s.io/klog/v2/internal/serialize"
+	"k8s.io/klog/v2/internal/severity"
+	"k8s.io/klog/v2/internal/verbosity"
+)
+
+var (
+	// TimeNow is used to retrieve the current time. May be changed for testing.
+	TimeNow = time.Now
+)
+
+const (
+	// nameKey is used to log the `WithName` values as an additional attribute.
+	nameKey = "logger"
+)
+
+// NewLogger constructs a new logger.
+//
+// Verbosity can be modified at any time through the Config.V and
+// Config.VModule API.
+func NewLogger(c *Config) logr.Logger {
+	return logr.New(&tlogger{
+		values: nil,
+		config: c,
+	})
+}
+
+type tlogger struct {
+	callDepth int
+
+	// hasPrefix is true if the first entry in values is the special
+	// nameKey key/value. Such an entry gets added and later updated in
+	// WithName.
+	hasPrefix bool
+
+	values []interface{}
+	groups string
+	config *Config
+}
+
+func (l *tlogger) Init(info logr.RuntimeInfo) {
+	l.callDepth = info.CallDepth
+}
+
+func (l *tlogger) WithCallDepth(depth int) logr.LogSink {
+	newLogger := *l
+	newLogger.callDepth += depth
+	return &newLogger
+}
+
+func (l *tlogger) Enabled(level int) bool {
+	return l.config.vstate.Enabled(verbosity.Level(level), 1+l.callDepth)
+}
+
+func (l *tlogger) Info(_ int, msg string, kvList ...interface{}) {
+	l.print(nil, severity.InfoLog, msg, kvList)
+}
+
+func (l *tlogger) Error(err error, msg string, kvList ...interface{}) {
+	l.print(err, severity.ErrorLog, msg, kvList)
+}
+
+func (l *tlogger) print(err error, s severity.Severity, msg string, kvList []interface{}) {
+	// Determine caller.
+	// +1 for this frame, +1 for Info/Error.
+	_, file, line, ok := runtime.Caller(l.callDepth + 2)
+	if !ok {
+		file = "???"
+		line = 1
+	} else {
+		if slash := strings.LastIndex(file, "/"); slash >= 0 {
+			file = file[slash+1:]
+		}
+	}
+
+	l.printWithInfos(file, line, time.Now(), err, s, msg, kvList)
+}
+
+func (l *tlogger) printWithInfos(file string, line int, now time.Time, err error, s severity.Severity, msg string, kvList []interface{}) {
+	// Only create a new buffer if we don't have one cached.
+	b := buffer.GetBuffer()
+	defer buffer.PutBuffer(b)
+
+	// Format header.
+	if l.config.co.fixedTime != nil {
+		now = *l.config.co.fixedTime
+	}
+	b.FormatHeader(s, file, line, now)
+
+	// The message is always quoted, even if it contains line breaks.
+	// If developers want multi-line output, they should use a small, fixed
+	// message and put the multi-line output into a value.
+	b.WriteString(strconv.Quote(msg))
+	if err != nil {
+		serialize.KVFormat(&b.Buffer, "err", err)
+	}
+	serialize.MergeAndFormatKVs(&b.Buffer, l.values, kvList)
+	if b.Len() == 0 || b.Bytes()[b.Len()-1] != '\n' {
+		b.WriteByte('\n')
+	}
+	_, _ = l.config.co.output.Write(b.Bytes())
+}
+
+func (l *tlogger) WriteKlogBuffer(data []byte) {
+	_, _ = l.config.co.output.Write(data)
+}
+
+// WithName returns a new logr.Logger with the specified name appended.  klogr
+// uses '/' characters to separate name elements.  Callers should not pass '/'
+// in the provided name string, but this library does not actually enforce that.
+func (l *tlogger) WithName(name string) logr.LogSink {
+	clone := *l
+	if l.hasPrefix {
+		// Copy slice and modify value. No length checks and type
+		// assertions are needed because hasPrefix is only true if the
+		// first two elements exist and are key/value strings.
+		v := make([]interface{}, 0, len(l.values))
+		v = append(v, l.values...)
+		prefix, _ := v[1].(string)
+		v[1] = prefix + "." + name
+		clone.values = v
+	} else {
+		// Preprend new key/value pair.
+		v := make([]interface{}, 0, 2+len(l.values))
+		v = append(v, nameKey, name)
+		v = append(v, l.values...)
+		clone.values = v
+		clone.hasPrefix = true
+	}
+	return &clone
+}
+
+func (l *tlogger) WithValues(kvList ...interface{}) logr.LogSink {
+	clone := *l
+	clone.values = serialize.WithValues(l.values, kvList)
+	return &clone
+}
+
+// KlogBufferWriter is implemented by the textlogger LogSink.
+type KlogBufferWriter interface {
+	// WriteKlogBuffer takes a pre-formatted buffer prepared by klog and
+	// writes it unchanged to the output stream. Can be used with
+	// klog.WriteKlogBuffer when setting a logger through
+	// klog.SetLoggerWithOptions.
+	WriteKlogBuffer([]byte)
+}
+
+var _ logr.LogSink = &tlogger{}
+var _ logr.CallDepthLogSink = &tlogger{}
+var _ KlogBufferWriter = &tlogger{}
diff --git a/vendor/k8s.io/klog/v2/textlogger/textlogger_slog.go b/vendor/k8s.io/klog/v2/textlogger/textlogger_slog.go
new file mode 100644
index 00000000..af0fab00
--- /dev/null
+++ b/vendor/k8s.io/klog/v2/textlogger/textlogger_slog.go
@@ -0,0 +1,52 @@
+//go:build go1.21
+// +build go1.21
+
+/*
+Copyright 2023 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package textlogger
+
+import (
+	"context"
+	"log/slog"
+
+	"github.com/go-logr/logr/slogr"
+
+	"k8s.io/klog/v2/internal/serialize"
+	"k8s.io/klog/v2/internal/sloghandler"
+)
+
+func (l *tlogger) Handle(ctx context.Context, record slog.Record) error {
+	return sloghandler.Handle(ctx, record, l.groups, l.printWithInfos)
+}
+
+func (l *tlogger) WithAttrs(attrs []slog.Attr) slogr.SlogSink {
+	clone := *l
+	clone.values = serialize.WithValues(l.values, sloghandler.Attrs2KVList(l.groups, attrs))
+	return &clone
+}
+
+func (l *tlogger) WithGroup(name string) slogr.SlogSink {
+	clone := *l
+	if clone.groups != "" {
+		clone.groups += "." + name
+	} else {
+		clone.groups = name
+	}
+	return &clone
+}
+
+var _ slogr.SlogSink = &tlogger{}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 4e48ebf1..52a9fed9 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -108,8 +108,9 @@ github.com/fsnotify/fsnotify
 ## explicit; go 1.18
 github.com/go-logr/logr
 github.com/go-logr/logr/funcr
-# github.com/go-logr/zapr v1.2.4
-## explicit; go 1.16
+github.com/go-logr/logr/slogr
+# github.com/go-logr/zapr v1.3.0
+## explicit; go 1.18
 github.com/go-logr/zapr
 # github.com/go-openapi/jsonpointer v0.19.6
 ## explicit; go 1.13
@@ -421,7 +422,7 @@ golang.org/x/crypto/pbkdf2
 golang.org/x/crypto/scrypt
 golang.org/x/crypto/ssh
 golang.org/x/crypto/ssh/internal/bcrypt_pbkdf
-# golang.org/x/mod v0.13.0
+# golang.org/x/mod v0.14.0
 ## explicit; go 1.18
 golang.org/x/mod/internal/lazyregexp
 golang.org/x/mod/modfile
@@ -962,7 +963,7 @@ k8s.io/component-base/metrics/legacyregistry
 k8s.io/component-base/metrics/prometheus/feature
 k8s.io/component-base/metrics/prometheusextension
 k8s.io/component-base/version
-# k8s.io/klog/v2 v2.100.1
+# k8s.io/klog/v2 v2.110.1
 ## explicit; go 1.13
 k8s.io/klog/v2
 k8s.io/klog/v2/internal/buffer
@@ -970,7 +971,10 @@ k8s.io/klog/v2/internal/clock
 k8s.io/klog/v2/internal/dbg
 k8s.io/klog/v2/internal/serialize
 k8s.io/klog/v2/internal/severity
+k8s.io/klog/v2/internal/sloghandler
+k8s.io/klog/v2/internal/verbosity
 k8s.io/klog/v2/klogr
+k8s.io/klog/v2/textlogger
 # k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f
 ## explicit; go 1.19
 k8s.io/kube-openapi/pkg/builder3/util


Test patch:
diff --git a/pkg/services/baremetal/baremetal/baremetal_suite_test.go b/pkg/services/baremetal/baremetal/baremetal_suite_test.go
index cda749e7..633b0d9c 100644
--- a/pkg/services/baremetal/baremetal/baremetal_suite_test.go
+++ b/pkg/services/baremetal/baremetal/baremetal_suite_test.go
@@ -21,14 +21,14 @@ import (
 
 	. "github.com/onsi/ginkgo/v2"
 	. "github.com/onsi/gomega"
-	"k8s.io/klog/v2/klogr"
+	"k8s.io/klog/v2/textlogger"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 
 	infrav1 "github.com/syself/cluster-api-provider-hetzner/api/v1beta1"
 	"github.com/syself/cluster-api-provider-hetzner/pkg/scope"
 )
 
-var log = klogr.New()
+var log = textlogger.NewLogger(textlogger.NewConfig())
 
 func TestBaremetal(t *testing.T) {
 	RegisterFailHandler(Fail)
diff --git a/pkg/services/baremetal/host/host_suite_test.go b/pkg/services/baremetal/host/host_suite_test.go
index db1e9dce..4aa1a6d3 100644
--- a/pkg/services/baremetal/host/host_suite_test.go
+++ b/pkg/services/baremetal/host/host_suite_test.go
@@ -25,7 +25,7 @@ import (
 	corev1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/runtime"
 	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
-	"k8s.io/klog/v2/klogr"
+	"k8s.io/klog/v2/textlogger"
 	fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake"
 
 	infrav1 "github.com/syself/cluster-api-provider-hetzner/api/v1beta1"
@@ -59,7 +59,7 @@ func TestHost(t *testing.T) {
 }
 
 var (
-	log        = klogr.New()
+	log        = textlogger.NewLogger(textlogger.NewConfig())
 	errTimeout = fmt.Errorf("timeout")
 	timeout    = timeoutError{errTimeout}
 )