Repository: kubernetes-sigs/security-profiles-operator
Pull request #836 (2022-03-15 23:07:32)

Bump k8s.io/klog/v2 from 2.40.1 to 2.50.0
Bumps [k8s.io/klog/v2](https://github.com/kubernetes/klog) from 2.40.1 to 2.50.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/kubernetes/klog/releases">k8s.io/klog/v2's releases</a>.</em></p>
<blockquote>
<h2>Prepare klog release for Kubernetes v1.24 (Take 3)</h2>
<h2>What's Changed</h2>
<ul>
<li>Panic on empty info with custom logr by <a href="https://github.com/jklaw90"><code>@​jklaw90</code></a> in <a href="https://github-redirect.dependabot.com/kubernetes/klog/pull/283">kubernetes/klog#283</a></li>
<li>Add missing Depth logging functions. by <a href="https://github.com/s3rj1k"><code>@​s3rj1k</code></a> in <a href="https://github-redirect.dependabot.com/kubernetes/klog/pull/280">kubernetes/klog#280</a></li>
<li>fix typo in klog.go by <a href="https://github.com/cocaccola"><code>@​cocaccola</code></a> in <a href="https://github-redirect.dependabot.com/kubernetes/klog/pull/270">kubernetes/klog#270</a></li>
<li>Update README.md by <a href="https://github.com/noaabarki"><code>@​noaabarki</code></a> in <a href="https://github-redirect.dependabot.com/kubernetes/klog/pull/281">kubernetes/klog#281</a></li>
<li>log filter: ignored by V, used during log call by <a href="https://github.com/pohly"><code>@​pohly</code></a> in <a href="https://github-redirect.dependabot.com/kubernetes/klog/pull/290">kubernetes/klog#290</a></li>
<li>SetLogger/ClearLogger/SetLogFilter cleanup by <a href="https://github.com/pohly"><code>@​pohly</code></a> in <a href="https://github-redirect.dependabot.com/kubernetes/klog/pull/289">kubernetes/klog#289</a></li>
<li>fixes for PR <a href="https://github-redirect.dependabot.com/kubernetes/klog/issues/280">#280</a>, refactoring, textlogger, unit test by <a href="https://github.com/pohly"><code>@​pohly</code></a> in <a href="https://github-redirect.dependabot.com/kubernetes/klog/pull/287">kubernetes/klog#287</a></li>
<li>klogr verbosity by <a href="https://github.com/pohly"><code>@​pohly</code></a> in <a href="https://github-redirect.dependabot.com/kubernetes/klog/pull/295">kubernetes/klog#295</a></li>
<li>test: fix Go version matrix by <a href="https://github.com/pohly"><code>@​pohly</code></a> in <a href="https://github-redirect.dependabot.com/kubernetes/klog/pull/298">kubernetes/klog#298</a></li>
<li>handle panics in MarshalLog, Error, String by <a href="https://github.com/pohly"><code>@​pohly</code></a> in <a href="https://github-redirect.dependabot.com/kubernetes/klog/pull/299">kubernetes/klog#299</a></li>
<li>Fix goroutine leak: make flushDaemon stoppable by <a href="https://github.com/katexochen"><code>@​katexochen</code></a> in <a href="https://github-redirect.dependabot.com/kubernetes/klog/pull/293">kubernetes/klog#293</a></li>
<li>structured logging: replacing Fatal/Exit/etc. without loss of flushing by <a href="https://github.com/pohly"><code>@​pohly</code></a> in <a href="https://github-redirect.dependabot.com/kubernetes/klog/pull/303">kubernetes/klog#303</a></li>
<li>contextual logging by <a href="https://github.com/pohly"><code>@​pohly</code></a> in <a href="https://github-redirect.dependabot.com/kubernetes/klog/pull/296">kubernetes/klog#296</a></li>
<li>remove side effects of tests by <a href="https://github.com/pohly"><code>@​pohly</code></a> in <a href="https://github-redirect.dependabot.com/kubernetes/klog/pull/305">kubernetes/klog#305</a></li>
<li>tests: stop testing with Go 1.14 by <a href="https://github.com/pohly"><code>@​pohly</code></a> in <a href="https://github-redirect.dependabot.com/kubernetes/klog/pull/304">kubernetes/klog#304</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/jklaw90"><code>@​jklaw90</code></a> made their first contribution in <a href="https://github-redirect.dependabot.com/kubernetes/klog/pull/283">kubernetes/klog#283</a></li>
<li><a href="https://github.com/s3rj1k"><code>@​s3rj1k</code></a> made their first contribution in <a href="https://github-redirect.dependabot.com/kubernetes/klog/pull/280">kubernetes/klog#280</a></li>
<li><a href="https://github.com/cocaccola"><code>@​cocaccola</code></a> made their first contribution in <a href="https://github-redirect.dependabot.com/kubernetes/klog/pull/270">kubernetes/klog#270</a></li>
<li><a href="https://github.com/noaabarki"><code>@​noaabarki</code></a> made their first contribution in <a href="https://github-redirect.dependabot.com/kubernetes/klog/pull/281">kubernetes/klog#281</a></li>
<li><a href="https://github.com/katexochen"><code>@​katexochen</code></a> made their first contribution in <a href="https://github-redirect.dependabot.com/kubernetes/klog/pull/293">kubernetes/klog#293</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a href="https://github.com/kubernetes/klog/compare/v2.40.1...v2.50.0">https://github.com/kubernetes/klog/compare/v2.40.1...v2.50.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/kubernetes/klog/commit/a77ab7d9236af04727cc470c24e4ebc23b482a2a"><code>a77ab7d</code></a> Merge pull request <a href="https://github-redirect.dependabot.com/kubernetes/klog/issues/304">#304</a> from pohly/go-1.14-testing</li>
<li><a href="https://github.com/kubernetes/klog/commit/1e923c616a2c74ef58de840f0cf7b966e0a184d2"><code>1e923c6</code></a> tests: stop testing with Go 1.14</li>
<li><a href="https://github.com/kubernetes/klog/commit/ae0c078513b487e9902dfa7804c3e8a999c2df29"><code>ae0c078</code></a> Merge pull request <a href="https://github-redirect.dependabot.com/kubernetes/klog/issues/305">#305</a> from pohly/flush-and-exit</li>
<li><a href="https://github.com/kubernetes/klog/commit/cd54fdc3e0faffd626dfa7c182837ebec9f0da0e"><code>cd54fdc</code></a> ExampleSetLogger: reset state after test</li>
<li><a href="https://github.com/kubernetes/klog/commit/0013f225ca16fd06d5876420a2e33f4df7f0ff97"><code>0013f22</code></a> exit_test.go: avoid depending on side effect of other tests</li>
<li><a href="https://github.com/kubernetes/klog/commit/6631b8cc3c1bdd283444c9ce6927439a00867ec2"><code>6631b8c</code></a> Merge pull request <a href="https://github-redirect.dependabot.com/kubernetes/klog/issues/296">#296</a> from pohly/contextual-logging</li>
<li><a href="https://github.com/kubernetes/klog/commit/f676d4a7b854110beaf02079d6a83223d5d93b5e"><code>f676d4a</code></a> Merge pull request <a href="https://github-redirect.dependabot.com/kubernetes/klog/issues/303">#303</a> from pohly/flush-and-exit</li>
<li><a href="https://github.com/kubernetes/klog/commit/56b8a4ea35ba0fc3be9fc82182e71c2f0116adc1"><code>56b8a4e</code></a> structured logging: replacing Fatal/Exit/etc. without loss of flushing</li>
<li><a href="https://github.com/kubernetes/klog/commit/260ac219dda6b7427653d082b1265b95303166ec"><code>260ac21</code></a> contextual logging</li>
<li><a href="https://github.com/kubernetes/klog/commit/d8885505366606dbacabd9c781a7aba7ff458e12"><code>d888550</code></a> make klog sufficient for working with logr</li>
<li>Additional commits viewable in <a href="https://github.com/kubernetes/klog/compare/v2.40.1...v2.50.0">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=k8s.io/klog/v2&package-manager=go_modules&previous-version=2.40.1&new-version=2.50.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>

k8s-ci-robot: @dependabot[bot]: Adding the "do-not-merge/release-note-label-needed" label because no release-note block was detected, please follow our [release note process](https://git.k8s.io/community/contributors/guide/release-notes.md) to remove it.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>
k8s-ci-robot: Hi @dependabot[bot]. Thanks for your PR.

I'm waiting for a [kubernetes-sigs](https://github.com/orgs/kubernetes-sigs/people) member to verify that this patch is reasonable to test. If it is, they should reply with `/ok-to-test` on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should [join the org](https://git.k8s.io/community/community-membership.md#member) to skip this step.

Once the patch is verified, the new status will be reflected by the `ok-to-test` label.

I understand the commands that are listed [here](https://go.k8s.io/bot-commands?repo=kubernetes-sigs%2Fsecurity-profiles-operator).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>

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

This pull-request has been approved by: *<a href="https://github.com/kubernetes-sigs/security-profiles-operator/pull/836#" title="Author self-approved">dependabot[bot]</a>*
To complete the [pull request process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process), please assign **jaormx** after the PR has been reviewed.
You can assign the PR to them by writing `/assign @jaormx` in a comment when ready.

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

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

- **[OWNERS](https://github.com/kubernetes-sigs/security-profiles-operator/blob/main/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["jaormx"]} -->
codecov-commenter: # [Codecov](https://codecov.io/gh/kubernetes-sigs/security-profiles-operator/pull/836?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) Report
> Merging [#836](https://codecov.io/gh/kubernetes-sigs/security-profiles-operator/pull/836?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) (7e52e24) into [main](https://codecov.io/gh/kubernetes-sigs/security-profiles-operator/commit/21726dc95fa5556c3d9a80381c1c6ece1a8b20f6?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) (21726dc) will **not change** coverage.
> The diff coverage is `n/a`.


```diff
@@           Coverage Diff           @@
##             main     #836   +/-   ##
=======================================
  Coverage   52.60%   52.60%           
=======================================
  Files          42       42           
  Lines        4268     4268           
=======================================
  Hits         2245     2245           
  Misses       1952     1952           
  Partials       71       71           
```

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

This pull-request has been approved by: *<a href="https://github.com/kubernetes-sigs/security-profiles-operator/pull/836#" title="Author self-approved">dependabot[bot]</a>*, *<a href="https://github.com/kubernetes-sigs/security-profiles-operator/pull/836#pullrequestreview-911253781" title="Approved">saschagrunert</a>*

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

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

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

- ~~[OWNERS](https://github.com/kubernetes-sigs/security-profiles-operator/blob/main/OWNERS)~~ [saschagrunert]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->

Patch:
diff --git a/go.mod b/go.mod
index 37dfffff6..0ed843ce2 100644
--- a/go.mod
+++ b/go.mod
@@ -27,7 +27,7 @@ require (
 	k8s.io/api v0.23.4
 	k8s.io/apimachinery v0.23.4
 	k8s.io/client-go v0.23.4
-	k8s.io/klog/v2 v2.40.1
+	k8s.io/klog/v2 v2.50.0
 	sigs.k8s.io/controller-runtime v0.11.1
 	sigs.k8s.io/controller-tools v0.8.0
 	sigs.k8s.io/mdtoc v1.1.0
diff --git a/go.sum b/go.sum
index af68aa997..c54cd34e2 100644
--- a/go.sum
+++ b/go.sum
@@ -2197,8 +2197,8 @@ k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
 k8s.io/klog/v2 v2.8.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=
 k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=
 k8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
-k8s.io/klog/v2 v2.40.1 h1:P4RRucWk/lFOlDdkAr3mc7iWFkgKrZY9qZMAgek06S4=
-k8s.io/klog/v2 v2.40.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
+k8s.io/klog/v2 v2.50.0 h1:2QWjlAHYTUqUM5XMSobX4mc//mXrf194+hiaZ4jy57Q=
+k8s.io/klog/v2 v2.50.0/go.mod h1:N3kgBtsFxMb4nQ0eBDgbHEt/dtxBuTkSFQ+7K5OUoz4=
 k8s.io/kube-aggregator v0.23.1/go.mod h1:1SPZXYD/je2gKxxLBkYyG3yFxSCUWI5QTyjqP2ZxRDI=
 k8s.io/kube-openapi v0.0.0-20190816220812-743ec37842bf/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
 k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
@@ -2223,8 +2223,9 @@ k8s.io/utils v0.0.0-20210707171843-4b05e18ac7d9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/
 k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
 k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
 k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
-k8s.io/utils v0.0.0-20211116205334-6203023598ed h1:ck1fRPWPJWsMd8ZRFsWc6mh/zHp5fZ/shhbrgPUxDAE=
 k8s.io/utils v0.0.0-20211116205334-6203023598ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
+k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 h1:HNSDgDCrr/6Ly3WEGKZftiE7IY19Vz2GdbOCyI4qqhc=
+k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
 modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw=
 modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
 modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
diff --git a/vendor/k8s.io/klog/v2/README.md b/vendor/k8s.io/klog/v2/README.md
index 64d29622e..a9c945e1d 100644
--- a/vendor/k8s.io/klog/v2/README.md
+++ b/vendor/k8s.io/klog/v2/README.md
@@ -23,6 +23,18 @@ Historical context is available here:
  * https://groups.google.com/forum/#!msg/kubernetes-sig-architecture/wCWiWf3Juzs/hXRVBH90CgAJ
  * https://groups.google.com/forum/#!msg/kubernetes-dev/7vnijOMhLS0/1oRiNtigBgAJ
 
+## Release versioning
+
+Semantic versioning is used in this repository. It contains several Go modules
+with different levels of stability:
+- `k8s.io/klog/v2` - stable API, `vX.Y.Z` tags
+- `k8s.io/tools` - no stable API yet (may change eventually), `tools/v0.Y.Z` tags
+- `examples` - no stable API, no tags, no intention to ever stabilize
+
+Exempt from the API stability guarantee are items (packages, functions, etc.)
+which are marked explicitly as `EXPERIMENTAL` in their docs comment. Those
+may still change in incompatible ways or get removed entirely.
+
 ----
 
 How to use klog
@@ -32,6 +44,7 @@ How to use klog
 - You can now use `log_file` instead of `log_dir` for logging to a single file (See `examples/log_file/usage_log_file.go`)
 - If you want to redirect everything logged using klog somewhere else (say syslog!), you can use `klog.SetOutput()` method and supply a `io.Writer`. (See `examples/set_output/usage_set_output.go`)
 - For more logging conventions (See [Logging Conventions](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-instrumentation/logging.md))
+- See our documentation on [pkg.go.dev/k8s.io](https://pkg.go.dev/k8s.io/klog).
 
 **NOTE**: please use the newer go versions that support semantic import versioning in modules, ideally go 1.11.4 or greater.
 
@@ -85,7 +98,7 @@ The comment from glog.go introduces the ideas:
 
 		glog.Fatalf("Initialization failed: %s", err)
 
-	See the documentation for the V function for an explanation
+	See the documentation of the V function for an explanation
 	of these examples:
 
 		if glog.V(2) {
diff --git a/vendor/k8s.io/klog/v2/contextual.go b/vendor/k8s.io/klog/v2/contextual.go
new file mode 100644
index 000000000..67492670d
--- /dev/null
+++ b/vendor/k8s.io/klog/v2/contextual.go
@@ -0,0 +1,246 @@
+/*
+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 (
+	"context"
+
+	"github.com/go-logr/logr"
+)
+
+// This file provides the implementation of
+// https://github.com/kubernetes/enhancements/tree/master/keps/sig-instrumentation/1602-structured-logging
+//
+// SetLogger and ClearLogger were originally added to klog.go and got moved
+// here. Contextual logging adds a way to retrieve a Logger for direct logging
+// without the logging calls in klog.go.
+//
+// The global variables are expected to be modified only during sequential
+// parts of a program (init, serial tests) and therefore are not protected by
+// mutex locking.
+
+var (
+	// contextualLoggingEnabled controls whether contextual logging is
+	// active. Disabling it may have some small performance benefit.
+	contextualLoggingEnabled = true
+
+	// globalLogger is the global Logger chosen by users of klog, nil if
+	// none is available.
+	globalLogger *Logger
+
+	// contextualLogger defines whether globalLogger may get called
+	// directly.
+	contextualLogger bool
+
+	// klogLogger is used as fallback for logging through the normal klog code
+	// when no Logger is set.
+	klogLogger logr.Logger = logr.New(&klogger{})
+)
+
+// SetLogger sets a Logger implementation that will be used as backing
+// implementation of the traditional klog log calls. klog will do its own
+// verbosity checks before calling logger.V().Info. logger.Error is always
+// called, regardless of the klog verbosity settings.
+//
+// If set, all log lines will be suppressed from the regular output, and
+// redirected to the logr implementation.
+// Use as:
+//   ...
+//   klog.SetLogger(zapr.NewLogger(zapLog))
+//
+// To remove a backing logr implemention, use ClearLogger. Setting an
+// empty logger with SetLogger(logr.Logger{}) does not work.
+//
+// Modifying the logger is not thread-safe and should be done while no other
+// goroutines invoke log calls, usually during program initialization.
+func SetLogger(logger logr.Logger) {
+	SetLoggerWithOptions(logger)
+}
+
+// SetLoggerWithOptions is a more flexible version of SetLogger. Without
+// additional options, it behaves exactly like SetLogger. By passing
+// ContextualLogger(true) as option, it can be used to set a logger that then
+// will also get called directly by applications which retrieve it via
+// FromContext, Background, or TODO.
+//
+// Supporting direct calls is recommended because it avoids the overhead of
+// routing log entries through klogr into klog and then into the actual Logger
+// backend.
+//
+// Experimental
+//
+// Notice: This function is EXPERIMENTAL and may be changed or removed in a
+// later release.
+func SetLoggerWithOptions(logger logr.Logger, opts ...LoggerOption) {
+	globalLogger = &logger
+	var o loggerOptions
+	for _, opt := range opts {
+		opt(&o)
+	}
+	contextualLogger = o.contextualLogger
+}
+
+// ContextualLogger determines whether the logger passed to
+// SetLoggerWithOptions may also get called directly. Such a logger cannot rely
+// on verbosity checking in klog.
+//
+// Experimental
+//
+// Notice: This function is EXPERIMENTAL and may be changed or removed in a
+// later release.
+func ContextualLogger(enabled bool) LoggerOption {
+	return func(o *loggerOptions) {
+		o.contextualLogger = enabled
+	}
+}
+
+// LoggerOption implements the functional parameter paradigm for
+// SetLoggerWithOptions.
+//
+// Experimental
+//
+// Notice: This type is EXPERIMENTAL and may be changed or removed in a
+// later release.
+type LoggerOption func(o *loggerOptions)
+
+type loggerOptions struct {
+	contextualLogger bool
+}
+
+// SetContextualLogger does the same as SetLogger, but in addition the
+// logger may also get called directly by code that retrieves it
+// with FromContext, TODO or Background. The logger therefore must
+// implements its own verbosity checking.
+func SetContextualLogger(logger logr.Logger) {
+	globalLogger = &logger
+	contextualLogger = true
+}
+
+// ClearLogger removes a backing Logger implementation if one was set earlier
+// with SetLogger.
+//
+// Modifying the logger is not thread-safe and should be done while no other
+// goroutines invoke log calls, usually during program initialization.
+func ClearLogger() {
+	globalLogger = nil
+}
+
+// EnableContextualLogging controls whether contextual logging is enabled.
+// By default it is enabled. When disabled, FromContext avoids looking up
+// the logger in the context and always returns the global logger.
+// LoggerWithValues, LoggerWithName, and NewContext become no-ops
+// and return their input logger respectively context. This may be useful
+// to avoid the additional overhead for contextual logging.
+//
+// This must be called during initialization before goroutines are started.
+//
+// Experimental
+//
+// Notice: This function is EXPERIMENTAL and may be changed or removed in a
+// later release.
+func EnableContextualLogging(enabled bool) {
+	contextualLoggingEnabled = enabled
+}
+
+// FromContext retrieves a logger set by the caller or, if not set,
+// falls back to the program's global logger (a Logger instance or klog
+// itself).
+//
+// Experimental
+//
+// Notice: This function is EXPERIMENTAL and may be changed or removed in a
+// later release.
+func FromContext(ctx context.Context) Logger {
+	if contextualLoggingEnabled {
+		if logger, err := logr.FromContext(ctx); err == nil {
+			return logger
+		}
+	}
+
+	return Background()
+}
+
+// TODO can be used as a last resort by code that has no means of
+// receiving a logger from its caller. FromContext or an explicit logger
+// parameter should be used instead.
+//
+// Experimental
+//
+// Notice: This function is EXPERIMENTAL and may be changed or removed in a
+// later release.
+func TODO() Logger {
+	return Background()
+}
+
+// Background retrieves the fallback logger. It should not be called before
+// that logger was initialized by the program and not by code that should
+// better receive a logger via its parameters. TODO can be used as a temporary
+// solution for such code.
+//
+// Experimental
+//
+// Notice: This function is EXPERIMENTAL and may be changed or removed in a
+// later release.
+func Background() Logger {
+	if globalLogger != nil && contextualLogger {
+		return *globalLogger
+	}
+
+	return klogLogger
+}
+
+// LoggerWithValues returns logger.WithValues(...kv) when
+// contextual logging is enabled, otherwise the logger.
+//
+// Experimental
+//
+// Notice: This function is EXPERIMENTAL and may be changed or removed in a
+// later release.
+func LoggerWithValues(logger Logger, kv ...interface{}) Logger {
+	if contextualLoggingEnabled {
+		return logger.WithValues(kv...)
+	}
+	return logger
+}
+
+// LoggerWithName returns logger.WithName(name) when contextual logging is
+// enabled, otherwise the logger.
+//
+// Experimental
+//
+// Notice: This function is EXPERIMENTAL and may be changed or removed in a
+// later release.
+func LoggerWithName(logger Logger, name string) Logger {
+	if contextualLoggingEnabled {
+		return logger.WithName(name)
+	}
+	return logger
+}
+
+// NewContext returns logr.NewContext(ctx, logger) when
+// contextual logging is enabled, otherwise ctx.
+//
+// Experimental
+//
+// Notice: This function is EXPERIMENTAL and may be changed or removed in a
+// later release.
+func NewContext(ctx context.Context, logger Logger) context.Context {
+	if contextualLoggingEnabled {
+		return logr.NewContext(ctx, logger)
+	}
+	return ctx
+}
diff --git a/vendor/k8s.io/klog/v2/exit.go b/vendor/k8s.io/klog/v2/exit.go
new file mode 100644
index 000000000..320a14772
--- /dev/null
+++ b/vendor/k8s.io/klog/v2/exit.go
@@ -0,0 +1,69 @@
+// Go support for leveled logs, analogous to https://code.google.com/p/google-glog/
+//
+// 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 klog
+
+import (
+	"fmt"
+	"os"
+	"time"
+)
+
+var (
+
+	// ExitFlushTimeout is the timeout that klog has traditionally used during
+	// calls like Fatal or Exit when flushing log data right before exiting.
+	// Applications that replace those calls and do not have some specific
+	// requirements like "exit immediately" can use this value as parameter
+	// for FlushAndExit.
+	//
+	// Can be set for testing purpose or to change the application's
+	// default.
+	ExitFlushTimeout = 10 * time.Second
+
+	// OsExit is the function called by FlushAndExit to terminate the program.
+	//
+	// Can be set for testing purpose or to change the application's
+	// default behavior. Note that the function should not simply return
+	// because callers of functions like Fatal will not expect that.
+	OsExit = os.Exit
+)
+
+// FlushAndExit flushes log data for a certain amount of time and then calls
+// os.Exit. Combined with some logging call it provides a replacement for
+// traditional calls like Fatal or Exit.
+func FlushAndExit(flushTimeout time.Duration, exitCode int) {
+	timeoutFlush(flushTimeout)
+	OsExit(exitCode)
+}
+
+// timeoutFlush calls Flush and returns when it completes or after timeout
+// elapses, whichever happens first.  This is needed because the hooks invoked
+// by Flush may deadlock when klog.Fatal is called from a hook that holds
+// a lock. Flushing also might take too long.
+func timeoutFlush(timeout time.Duration) {
+	done := make(chan bool, 1)
+	go func() {
+		Flush() // calls logging.lockAndFlushAll()
+		done <- true
+	}()
+	select {
+	case <-done:
+	case <-time.After(timeout):
+		fmt.Fprintln(os.Stderr, "klog: Flush took longer than", timeout)
+	}
+}
diff --git a/vendor/k8s.io/klog/v2/go.mod b/vendor/k8s.io/klog/v2/go.mod
index 31aefba74..6385c6cde 100644
--- a/vendor/k8s.io/klog/v2/go.mod
+++ b/vendor/k8s.io/klog/v2/go.mod
@@ -2,4 +2,7 @@ module k8s.io/klog/v2
 
 go 1.13
 
-require github.com/go-logr/logr v1.2.0
+require (
+	github.com/go-logr/logr v1.2.0
+	k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9
+)
diff --git a/vendor/k8s.io/klog/v2/go.sum b/vendor/k8s.io/klog/v2/go.sum
index 919fbadbc..ef731394a 100644
--- a/vendor/k8s.io/klog/v2/go.sum
+++ b/vendor/k8s.io/klog/v2/go.sum
@@ -1,2 +1,13 @@
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
 github.com/go-logr/logr v1.2.0 h1:QK40JKJyMdUDz+h+xvCsru/bJhvG0UxvePV0ufL/AcE=
 github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
+k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 h1:HNSDgDCrr/6Ly3WEGKZftiE7IY19Vz2GdbOCyI4qqhc=
+k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
diff --git a/vendor/k8s.io/klog/v2/imports.go b/vendor/k8s.io/klog/v2/imports.go
new file mode 100644
index 000000000..43cd08190
--- /dev/null
+++ b/vendor/k8s.io/klog/v2/imports.go
@@ -0,0 +1,58 @@
+/*
+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 (
+	"github.com/go-logr/logr"
+)
+
+// The reason for providing these aliases is to allow code to work with logr
+// without directly importing it.
+
+// Logger in this package is exactly the same as logr.Logger.
+//
+// Experimental
+//
+// Notice: This type is EXPERIMENTAL and may be changed or removed in a
+// later release.
+type Logger = logr.Logger
+
+// LogSink in this package is exactly the same as logr.LogSink.
+//
+// Experimental
+//
+// Notice: This type is EXPERIMENTAL and may be changed or removed in a
+// later release.
+type LogSink = logr.LogSink
+
+// Runtimeinfo in this package is exactly the same as logr.RuntimeInfo.
+//
+// Experimental
+//
+// Notice: This type is EXPERIMENTAL and may be changed or removed in a
+// later release.
+type RuntimeInfo = logr.RuntimeInfo
+
+var (
+	// New is an alias for logr.New.
+	//
+	// Experimental
+	//
+	// Notice: This variable is EXPERIMENTAL and may be changed or removed in a
+	// later release.
+	New = logr.New
+)
diff --git a/vendor/k8s.io/klog/v2/internal/buffer/buffer.go b/vendor/k8s.io/klog/v2/internal/buffer/buffer.go
new file mode 100644
index 000000000..ac88682a2
--- /dev/null
+++ b/vendor/k8s.io/klog/v2/internal/buffer/buffer.go
@@ -0,0 +1,159 @@
+// 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 buffer provides a cache for byte.Buffer instances that can be reused
+// to avoid frequent allocation and deallocation. It also has utility code
+// for log header formatting that use these buffers.
+package buffer
+
+import (
+	"bytes"
+	"os"
+	"sync"
+	"time"
+
+	"k8s.io/klog/v2/internal/severity"
+)
+
+var (
+	// Pid is inserted into log headers. Can be overridden for tests.
+	Pid = os.Getpid()
+)
+
+// 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
+}
+
+// Buffers manages the reuse of individual buffer instances. It is thread-safe.
+type Buffers struct {
+	// mu protects the free list. It is separate from the main mutex
+	// so buffers can be grabbed and printed to without holding the main lock,
+	// for better parallelization.
+	mu sync.Mutex
+
+	// freeList is a list of byte buffers, maintained under mu.
+	freeList *Buffer
+}
+
+// GetBuffer returns a new, ready-to-use buffer.
+func (bl *Buffers) GetBuffer() *Buffer {
+	bl.mu.Lock()
+	b := bl.freeList
+	if b != nil {
+		bl.freeList = b.next
+	}
+	bl.mu.Unlock()
+	if b == nil {
+		b = new(Buffer)
+	} else {
+		b.next = nil
+		b.Reset()
+	}
+	return b
+}
+
+// PutBuffer returns a buffer to the free list.
+func (bl *Buffers) PutBuffer(b *Buffer) {
+	if b.Len() >= 256 {
+		// Let big buffers die a natural death.
+		return
+	}
+	bl.mu.Lock()
+	b.next = bl.freeList
+	bl.freeList = b
+	bl.mu.Unlock()
+}
+
+// Some custom tiny helper functions to print the log header efficiently.
+
+const digits = "0123456789"
+
+// twoDigits formats a zero-prefixed two-digit integer at buf.Tmp[i].
+func (buf *Buffer) twoDigits(i, d int) {
+	buf.Tmp[i+1] = digits[d%10]
+	d /= 10
+	buf.Tmp[i] = digits[d%10]
+}
+
+// nDigits formats an n-digit integer at buf.Tmp[i],
+// padding with pad on the left.
+// It assumes d >= 0.
+func (buf *Buffer) nDigits(n, i, d int, pad byte) {
+	j := n - 1
+	for ; j >= 0 && d > 0; j-- {
+		buf.Tmp[i+j] = digits[d%10]
+		d /= 10
+	}
+	for ; j >= 0; j-- {
+		buf.Tmp[i+j] = pad
+	}
+}
+
+// someDigits formats a zero-prefixed variable-width integer at buf.Tmp[i].
+func (buf *Buffer) someDigits(i, d int) int {
+	// Print into the top, then copy down. We know there's space for at least
+	// a 10-digit number.
+	j := len(buf.Tmp)
+	for {
+		j--
+		buf.Tmp[j] = digits[d%10]
+		d /= 10
+		if d == 0 {
+			break
+		}
+	}
+	return copy(buf.Tmp[i:], buf.Tmp[j:])
+}
+
+// FormatHeader formats a log header using the provided file name and line number.
+func (buf *Buffer) FormatHeader(s severity.Severity, file string, line int, now time.Time) {
+	if line < 0 {
+		line = 0 // not a real line number, but acceptable to someDigits
+	}
+	if s > severity.FatalLog {
+		s = severity.InfoLog // for safety.
+	}
+
+	// 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.
+	_, month, day := now.Date()
+	hour, minute, second := now.Clock()
+	// Lmmdd hh:mm:ss.uuuuuu threadid file:line]
+	buf.Tmp[0] = severity.Char[s]
+	buf.twoDigits(1, int(month))
+	buf.twoDigits(3, day)
+	buf.Tmp[5] = ' '
+	buf.twoDigits(6, hour)
+	buf.Tmp[8] = ':'
+	buf.twoDigits(9, minute)
+	buf.Tmp[11] = ':'
+	buf.twoDigits(12, second)
+	buf.Tmp[14] = '.'
+	buf.nDigits(6, 15, now.Nanosecond()/1000, '0')
+	buf.Tmp[21] = ' '
+	buf.nDigits(7, 22, Pid, ' ') // TODO: should be TID
+	buf.Tmp[29] = ' '
+	buf.Write(buf.Tmp[:30])
+	buf.WriteString(file)
+	buf.Tmp[0] = ':'
+	n := buf.someDigits(1, line)
+	buf.Tmp[n+1] = ']'
+	buf.Tmp[n+2] = ' '
+	buf.Write(buf.Tmp[:n+3])
+}
diff --git a/vendor/k8s.io/klog/v2/internal/serialize/keyvalues.go b/vendor/k8s.io/klog/v2/internal/serialize/keyvalues.go
new file mode 100644
index 000000000..d89731368
--- /dev/null
+++ b/vendor/k8s.io/klog/v2/internal/serialize/keyvalues.go
@@ -0,0 +1,225 @@
+/*
+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 serialize
+
+import (
+	"bytes"
+	"fmt"
+	"strconv"
+)
+
+// WithValues implements LogSink.WithValues. The old key/value pairs are
+// assumed to be well-formed, the new ones are checked and padded if
+// necessary. It returns a new slice.
+func WithValues(oldKV, newKV []interface{}) []interface{} {
+	if len(newKV) == 0 {
+		return oldKV
+	}
+	newLen := len(oldKV) + len(newKV)
+	hasMissingValue := newLen%2 != 0
+	if hasMissingValue {
+		newLen++
+	}
+	// The new LogSink must have its own slice.
+	kv := make([]interface{}, 0, newLen)
+	kv = append(kv, oldKV...)
+	kv = append(kv, newKV...)
+	if hasMissingValue {
+		kv = append(kv, missingValue)
+	}
+	return kv
+}
+
+// TrimDuplicates deduplicates elements provided in multiple key/value tuple
+// slices, whilst maintaining the distinction between where the items are
+// contained.
+func TrimDuplicates(kvLists ...[]interface{}) [][]interface{} {
+	// maintain a map of all seen keys
+	seenKeys := map[interface{}]struct{}{}
+	// build the same number of output slices as inputs
+	outs := make([][]interface{}, len(kvLists))
+	// iterate over the input slices backwards, as 'later' kv specifications
+	// of the same key will take precedence over earlier ones
+	for i := len(kvLists) - 1; i >= 0; i-- {
+		// initialise this output slice
+		outs[i] = []interface{}{}
+		// obtain a reference to the kvList we are processing
+		// and make sure it has an even number of entries
+		kvList := kvLists[i]
+		if len(kvList)%2 != 0 {
+			kvList = append(kvList, missingValue)
+		}
+
+		// start iterating at len(kvList) - 2 (i.e. the 2nd last item) for
+		// slices that have an even number of elements.
+		// We add (len(kvList) % 2) here to handle the case where there is an
+		// odd number of elements in a kvList.
+		// If there is an odd number, then the last element in the slice will
+		// have the value 'null'.
+		for i2 := len(kvList) - 2 + (len(kvList) % 2); i2 >= 0; i2 -= 2 {
+			k := kvList[i2]
+			// if we have already seen this key, do not include it again
+			if _, ok := seenKeys[k]; ok {
+				continue
+			}
+			// make a note that we've observed a new key
+			seenKeys[k] = struct{}{}
+			// attempt to obtain the value of the key
+			var v interface{}
+			// i2+1 should only ever be out of bounds if we handling the first
+			// iteration over a slice with an odd number of elements
+			if i2+1 < len(kvList) {
+				v = kvList[i2+1]
+			}
+			// add this KV tuple to the *start* of the output list to maintain
+			// the original order as we are iterating over the slice backwards
+			outs[i] = append([]interface{}{k, v}, outs[i]...)
+		}
+	}
+	return outs
+}
+
+const missingValue = "(MISSING)"
+
+// KVListFormat serializes all key/value pairs into the provided buffer.
+// A space gets inserted before the first pair and between each pair.
+func KVListFormat(b *bytes.Buffer, keysAndValues ...interface{}) {
+	for i := 0; i < len(keysAndValues); i += 2 {
+		var v interface{}
+		k := keysAndValues[i]
+		if i+1 < len(keysAndValues) {
+			v = keysAndValues[i+1]
+		} else {
+			v = missingValue
+		}
+		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 k, ok := k.(string); ok {
+			// Avoid one allocation when the key is a string, which
+			// normally it should be.
+			b.WriteString(k)
+		} 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 fmt.Stringer:
+			writeStringValue(b, true, StringerToString(v))
+		case string:
+			writeStringValue(b, true, v)
+		case error:
+			writeStringValue(b, true, ErrorToString(v))
+		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:
+			writeStringValue(b, false, fmt.Sprintf("%+v", v))
+		}
+	}
+}
+
+// StringerToString converts a Stringer to a string,
+// handling panics if they occur.
+func StringerToString(s fmt.Stringer) (ret string) {
+	defer func() {
+		if err := recover(); err != nil {
+			ret = fmt.Sprintf("<panic: %s>", err)
+		}
+	}()
+	ret = s.String()
+	return
+}
+
+// ErrorToString converts an error to a string,
+// handling panics if they occur.
+func ErrorToString(err error) (ret string) {
+	defer func() {
+		if err := recover(); err != nil {
+			ret = fmt.Sprintf("<panic: %s>", err)
+		}
+	}()
+	ret = err.Error()
+	return
+}
+
+func writeStringValue(b *bytes.Buffer, quote bool, v string) {
+	data := []byte(v)
+	index := bytes.IndexByte(data, '\n')
+	if index == -1 {
+		b.WriteByte('=')
+		if quote {
+			// Simple string, quote quotation marks and non-printable characters.
+			b.WriteString(strconv.Quote(v))
+			return
+		}
+		// Non-string with no line breaks.
+		b.WriteString(v)
+		return
+	}
+
+	// Complex multi-line string, show as-is with indention like this:
+	// I... "hello world" key=<
+	// <tab>line 1
+	// <tab>line 2
+	//  >
+	//
+	// Tabs indent the lines of the value while the end of string delimiter
+	// is indented with a space. That has two purposes:
+	// - visual difference between the two for a human reader because indention
+	//   will be different
+	// - no ambiguity when some value line starts with the end delimiter
+	//
+	// One downside is that the output cannot distinguish between strings that
+	// end with a line break and those that don't because the end delimiter
+	// will always be on the next line.
+	b.WriteString("=<\n")
+	for index != -1 {
+		b.WriteByte('\t')
+		b.Write(data[0 : index+1])
+		data = data[index+1:]
+		index = bytes.IndexByte(data, '\n')
+	}
+	if len(data) == 0 {
+		// String ended with line break, don't add another.
+		b.WriteString(" >")
+	} else {
+		// No line break at end of last line, write rest of string and
+		// add one.
+		b.WriteByte('\t')
+		b.Write(data)
+		b.WriteString("\n >")
+	}
+}
diff --git a/vendor/k8s.io/klog/v2/internal/severity/severity.go b/vendor/k8s.io/klog/v2/internal/severity/severity.go
new file mode 100644
index 000000000..30fa1834f
--- /dev/null
+++ b/vendor/k8s.io/klog/v2/internal/severity/severity.go
@@ -0,0 +1,58 @@
+// 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 severity provides definitions for klog severity (info, warning, ...)
+package severity
+
+import (
+	"strings"
+)
+
+// severity identifies the sort of log: info, warning etc. The binding to flag.Value
+// is handled in klog.go
+type Severity int32 // sync/atomic int32
+
+// These constants identify the log levels in order of increasing severity.
+// A message written to a high-severity log file is also written to each
+// lower-severity log file.
+const (
+	InfoLog Severity = iota
+	WarningLog
+	ErrorLog
+	FatalLog
+	NumSeverity = 4
+)
+
+// Char contains one shortcut letter per severity level.
+const Char = "IWEF"
+
+// Name contains one name per severity level.
+var Name = []string{
+	InfoLog:    "INFO",
+	WarningLog: "WARNING",
+	ErrorLog:   "ERROR",
+	FatalLog:   "FATAL",
+}
+
+// ByName looks up a severity level by name.
+func ByName(s string) (Severity, bool) {
+	s = strings.ToUpper(s)
+	for i, name := range Name {
+		if name == s {
+			return Severity(i), true
+		}
+	}
+	return 0, false
+}
diff --git a/vendor/k8s.io/klog/v2/k8s_references.go b/vendor/k8s.io/klog/v2/k8s_references.go
new file mode 100644
index 000000000..db58f8baa
--- /dev/null
+++ b/vendor/k8s.io/klog/v2/k8s_references.go
@@ -0,0 +1,94 @@
+/*
+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 (
+	"fmt"
+	"reflect"
+
+	"github.com/go-logr/logr"
+)
+
+// ObjectRef references a kubernetes object
+type ObjectRef struct {
+	Name      string `json:"name"`
+	Namespace string `json:"namespace,omitempty"`
+}
+
+func (ref ObjectRef) String() string {
+	if ref.Namespace != "" {
+		return fmt.Sprintf("%s/%s", ref.Namespace, ref.Name)
+	}
+	return ref.Name
+}
+
+// MarshalLog ensures that loggers with support for structured output will log
+// as a struct by removing the String method via a custom type.
+func (ref ObjectRef) MarshalLog() interface{} {
+	type or ObjectRef
+	return or(ref)
+}
+
+var _ logr.Marshaler = ObjectRef{}
+
+// KMetadata is a subset of the kubernetes k8s.io/apimachinery/pkg/apis/meta/v1.Object interface
+// this interface may expand in the future, but will always be a subset of the
+// kubernetes k8s.io/apimachinery/pkg/apis/meta/v1.Object interface
+type KMetadata interface {
+	GetName() string
+	GetNamespace() string
+}
+
+// KObj returns ObjectRef from ObjectMeta
+func KObj(obj KMetadata) ObjectRef {
+	if obj == nil {
+		return ObjectRef{}
+	}
+	if val := reflect.ValueOf(obj); val.Kind() == reflect.Ptr && val.IsNil() {
+		return ObjectRef{}
+	}
+
+	return ObjectRef{
+		Name:      obj.GetName(),
+		Namespace: obj.GetNamespace(),
+	}
+}
+
+// KRef returns ObjectRef from name and namespace
+func KRef(namespace, name string) ObjectRef {
+	return ObjectRef{
+		Name:      name,
+		Namespace: namespace,
+	}
+}
+
+// KObjs returns slice of ObjectRef from an slice of ObjectMeta
+func KObjs(arg interface{}) []ObjectRef {
+	s := reflect.ValueOf(arg)
+	if s.Kind() != reflect.Slice {
+		return nil
+	}
+	objectRefs := make([]ObjectRef, 0, s.Len())
+	for i := 0; i < s.Len(); i++ {
+		if v, ok := s.Index(i).Interface().(KMetadata); ok {
+			objectRefs = append(objectRefs, KObj(v))
+		} else {
+			return nil
+		}
+	}
+	return objectRefs
+}
diff --git a/vendor/k8s.io/klog/v2/klog.go b/vendor/k8s.io/klog/v2/klog.go
index dacdf9112..7eae51924 100644
--- a/vendor/k8s.io/klog/v2/klog.go
+++ b/vendor/k8s.io/klog/v2/klog.go
@@ -81,7 +81,6 @@ import (
 	"math"
 	"os"
 	"path/filepath"
-	"reflect"
 	"runtime"
 	"strconv"
 	"strings"
@@ -90,81 +89,58 @@ import (
 	"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/utils/clock"
 )
 
-// severity identifies the sort of log: info, warning etc. It also implements
+// severityValue identifies the sort of log: info, warning etc. It also implements
 // the flag.Value interface. The -stderrthreshold flag is of type severity and
 // should be modified only through the flag.Value interface. The values match
 // the corresponding constants in C++.
-type severity int32 // sync/atomic int32
-
-// These constants identify the log levels in order of increasing severity.
-// A message written to a high-severity log file is also written to each
-// lower-severity log file.
-const (
-	infoLog severity = iota
-	warningLog
-	errorLog
-	fatalLog
-	numSeverity = 4
-)
-
-const severityChar = "IWEF"
-
-var severityName = []string{
-	infoLog:    "INFO",
-	warningLog: "WARNING",
-	errorLog:   "ERROR",
-	fatalLog:   "FATAL",
+type severityValue struct {
+	severity.Severity
 }
 
 // get returns the value of the severity.
-func (s *severity) get() severity {
-	return severity(atomic.LoadInt32((*int32)(s)))
+func (s *severityValue) get() severity.Severity {
+	return severity.Severity(atomic.LoadInt32((*int32)(&s.Severity)))
 }
 
 // set sets the value of the severity.
-func (s *severity) set(val severity) {
-	atomic.StoreInt32((*int32)(s), int32(val))
+func (s *severityValue) set(val severity.Severity) {
+	atomic.StoreInt32((*int32)(&s.Severity), int32(val))
 }
 
 // String is part of the flag.Value interface.
-func (s *severity) String() string {
-	return strconv.FormatInt(int64(*s), 10)
+func (s *severityValue) String() string {
+	return strconv.FormatInt(int64(s.Severity), 10)
 }
 
 // Get is part of the flag.Getter interface.
-func (s *severity) Get() interface{} {
-	return *s
+func (s *severityValue) Get() interface{} {
+	return s.Severity
 }
 
 // Set is part of the flag.Value interface.
-func (s *severity) Set(value string) error {
-	var threshold severity
+func (s *severityValue) Set(value string) error {
+	var threshold severity.Severity
 	// Is it a known name?
-	if v, ok := severityByName(value); ok {
+	if v, ok := severity.ByName(value); ok {
 		threshold = v
 	} else {
 		v, err := strconv.ParseInt(value, 10, 32)
 		if err != nil {
 			return err
 		}
-		threshold = severity(v)
+		threshold = severity.Severity(v)
 	}
 	logging.stderrThreshold.set(threshold)
 	return nil
 }
 
-func severityByName(s string) (severity, bool) {
-	s = strings.ToUpper(s)
-	for i, name := range severityName {
-		if name == s {
-			return severity(i), true
-		}
-	}
-	return 0, false
-}
-
 // OutputStats tracks the number of output lines and bytes written.
 type OutputStats struct {
 	lines int64
@@ -187,10 +163,10 @@ var Stats struct {
 	Info, Warning, Error OutputStats
 }
 
-var severityStats = [numSeverity]*OutputStats{
-	infoLog:    &Stats.Info,
-	warningLog: &Stats.Warning,
-	errorLog:   &Stats.Error,
+var severityStats = [severity.NumSeverity]*OutputStats{
+	severity.InfoLog:    &Stats.Info,
+	severity.WarningLog: &Stats.Warning,
+	severity.ErrorLog:   &Stats.Error,
 }
 
 // Level is exported because it appears in the arguments to V and is
@@ -404,9 +380,11 @@ type flushSyncWriter interface {
 	io.Writer
 }
 
-// init sets up the defaults and runs flushDaemon.
+// init sets up the defaults.
 func init() {
-	logging.stderrThreshold = errorLog // Default stderrThreshold is ERROR.
+	logging.stderrThreshold = severityValue{
+		Severity: severity.ErrorLog, // Default stderrThreshold is ERROR.
+	}
 	logging.setVState(0, nil, false)
 	logging.logDir = ""
 	logging.logFile = ""
@@ -417,7 +395,7 @@ func init() {
 	logging.addDirHeader = false
 	logging.skipLogHeaders = false
 	logging.oneOutput = false
-	go logging.flushDaemon()
+	logging.flushD = newFlushDaemon(flushInterval, logging.lockAndFlushAll, nil)
 }
 
 // InitFlags is for explicitly initializing the flags.
@@ -457,20 +435,20 @@ type loggingT struct {
 	alsoToStderr bool // The -alsologtostderr flag.
 
 	// Level flag. Handled atomically.
-	stderrThreshold severity // The -stderrthreshold flag.
+	stderrThreshold severityValue // The -stderrthreshold flag.
 
-	// freeList is a list of byte buffers, maintained under freeListMu.
-	freeList *buffer
-	// freeListMu maintains the free list. It is separate from the main mutex
+	// bufferCache maintains the free list. It uses its own mutex
 	// so buffers can be grabbed and printed to without holding the main lock,
 	// for better parallelization.
-	freeListMu sync.Mutex
+	bufferCache buffer.Buffers
 
 	// mu protects the remaining elements of this structure and is
 	// used to synchronize logging.
 	mu sync.Mutex
 	// file holds writer for each of the log types.
-	file [numSeverity]flushSyncWriter
+	file [severity.NumSeverity]flushSyncWriter
+	// flushD holds a flushDaemon that frequently flushes log file buffers.
+	flushD *flushDaemon
 	// 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.
@@ -508,9 +486,6 @@ type loggingT struct {
 	// If true, add the file directory to the header
 	addDirHeader bool
 
-	// If set, all output will be redirected unconditionally to the provided logr.Logger
-	logr *logr.Logger
-
 	// If true, messages will not be propagated to lower severity log levels
 	oneOutput bool
 
@@ -518,13 +493,6 @@ type loggingT struct {
 	filter LogFilter
 }
 
-// buffer holds a byte Buffer for reuse. The zero value is ready for use.
-type buffer struct {
-	bytes.Buffer
-	tmp  [64]byte // temporary byte array for creating headers.
-	next *buffer
-}
-
 var logging loggingT
 
 // setVState sets a consistent state for V logging.
@@ -547,35 +515,6 @@ func (l *loggingT) setVState(verbosity Level, filter []modulePat, setFilter bool
 	l.verbosity.set(verbosity)
 }
 
-// getBuffer returns a new, ready-to-use buffer.
-func (l *loggingT) getBuffer() *buffer {
-	l.freeListMu.Lock()
-	b := l.freeList
-	if b != nil {
-		l.freeList = b.next
-	}
-	l.freeListMu.Unlock()
-	if b == nil {
-		b = new(buffer)
-	} else {
-		b.next = nil
-		b.Reset()
-	}
-	return b
-}
-
-// putBuffer returns a buffer to the free list.
-func (l *loggingT) putBuffer(b *buffer) {
-	if b.Len() >= 256 {
-		// Let big buffers die a natural death.
-		return
-	}
-	l.freeListMu.Lock()
-	b.next = l.freeList
-	l.freeList = b
-	l.freeListMu.Unlock()
-}
-
 var timeNow = time.Now // Stubbed out for testing.
 
 /*
@@ -595,7 +534,7 @@ where the fields are defined as follows:
 	line             The line number
 	msg              The user-supplied message
 */
-func (l *loggingT) header(s severity, depth int) (*buffer, string, int) {
+func (l *loggingT) header(s severity.Severity, depth int) (*buffer.Buffer, string, int) {
 	_, file, line, ok := runtime.Caller(3 + depth)
 	if !ok {
 		file = "???"
@@ -615,133 +554,68 @@ func (l *loggingT) header(s severity, depth int) (*buffer, string, int) {
 }
 
 // formatHeader formats a log header using the provided file name and line number.
-func (l *loggingT) formatHeader(s severity, file string, line int) *buffer {
-	now := timeNow()
-	if line < 0 {
-		line = 0 // not a real line number, but acceptable to someDigits
-	}
-	if s > fatalLog {
-		s = infoLog // for safety.
-	}
-	buf := l.getBuffer()
+func (l *loggingT) formatHeader(s severity.Severity, file string, line int) *buffer.Buffer {
+	buf := l.bufferCache.GetBuffer()
 	if l.skipHeaders {
 		return buf
 	}
-
-	// 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.
-	_, month, day := now.Date()
-	hour, minute, second := now.Clock()
-	// Lmmdd hh:mm:ss.uuuuuu threadid file:line]
-	buf.tmp[0] = severityChar[s]
-	buf.twoDigits(1, int(month))
-	buf.twoDigits(3, day)
-	buf.tmp[5] = ' '
-	buf.twoDigits(6, hour)
-	buf.tmp[8] = ':'
-	buf.twoDigits(9, minute)
-	buf.tmp[11] = ':'
-	buf.twoDigits(12, second)
-	buf.tmp[14] = '.'
-	buf.nDigits(6, 15, now.Nanosecond()/1000, '0')
-	buf.tmp[21] = ' '
-	buf.nDigits(7, 22, pid, ' ') // TODO: should be TID
-	buf.tmp[29] = ' '
-	buf.Write(buf.tmp[:30])
-	buf.WriteString(file)
-	buf.tmp[0] = ':'
-	n := buf.someDigits(1, line)
-	buf.tmp[n+1] = ']'
-	buf.tmp[n+2] = ' '
-	buf.Write(buf.tmp[:n+3])
+	now := timeNow()
+	buf.FormatHeader(s, file, line, now)
 	return buf
 }
 
-// Some custom tiny helper functions to print the log header efficiently.
-
-const digits = "0123456789"
-
-// twoDigits formats a zero-prefixed two-digit integer at buf.tmp[i].
-func (buf *buffer) twoDigits(i, d int) {
-	buf.tmp[i+1] = digits[d%10]
-	d /= 10
-	buf.tmp[i] = digits[d%10]
-}
-
-// nDigits formats an n-digit integer at buf.tmp[i],
-// padding with pad on the left.
-// It assumes d >= 0.
-func (buf *buffer) nDigits(n, i, d int, pad byte) {
-	j := n - 1
-	for ; j >= 0 && d > 0; j-- {
-		buf.tmp[i+j] = digits[d%10]
-		d /= 10
-	}
-	for ; j >= 0; j-- {
-		buf.tmp[i+j] = pad
-	}
-}
-
-// someDigits formats a zero-prefixed variable-width integer at buf.tmp[i].
-func (buf *buffer) someDigits(i, d int) int {
-	// Print into the top, then copy down. We know there's space for at least
-	// a 10-digit number.
-	j := len(buf.tmp)
-	for {
-		j--
-		buf.tmp[j] = digits[d%10]
-		d /= 10
-		if d == 0 {
-			break
-		}
-	}
-	return copy(buf.tmp[i:], buf.tmp[j:])
+func (l *loggingT) println(s severity.Severity, logger *logr.Logger, filter LogFilter, args ...interface{}) {
+	l.printlnDepth(s, logger, filter, 1, args...)
 }
 
-func (l *loggingT) println(s severity, logger *logr.Logger, filter LogFilter, args ...interface{}) {
-	buf, file, line := l.header(s, 0)
+func (l *loggingT) printlnDepth(s severity.Severity, logger *logr.Logger, filter LogFilter, depth int, args ...interface{}) {
+	buf, file, line := l.header(s, depth)
 	// if logger is set, we clear the generated header as we rely on the backing
 	// logger implementation to print headers
 	if logger != nil {
-		l.putBuffer(buf)
-		buf = l.getBuffer()
+		l.bufferCache.PutBuffer(buf)
+		buf = l.bufferCache.GetBuffer()
 	}
 	if filter != nil {
 		args = filter.Filter(args)
 	}
 	fmt.Fprintln(buf, args...)
-	l.output(s, logger, buf, 0 /* depth */, file, line, false)
+	l.output(s, logger, buf, depth, file, line, false)
 }
 
-func (l *loggingT) print(s severity, logger *logr.Logger, filter LogFilter, args ...interface{}) {
+func (l *loggingT) print(s severity.Severity, logger *logr.Logger, filter LogFilter, args ...interface{}) {
 	l.printDepth(s, logger, filter, 1, args...)
 }
 
-func (l *loggingT) printDepth(s severity, logger *logr.Logger, filter LogFilter, depth int, args ...interface{}) {
+func (l *loggingT) printDepth(s severity.Severity, logger *logr.Logger, filter LogFilter, depth int, args ...interface{}) {
 	buf, file, line := l.header(s, depth)
 	// if logr is set, we clear the generated header as we rely on the backing
 	// logr implementation to print headers
 	if logger != nil {
-		l.putBuffer(buf)
-		buf = l.getBuffer()
+		l.bufferCache.PutBuffer(buf)
+		buf = l.bufferCache.GetBuffer()
 	}
 	if filter != nil {
 		args = filter.Filter(args)
 	}
 	fmt.Fprint(buf, args...)
-	if buf.Bytes()[buf.Len()-1] != '\n' {
+	if buf.Len() == 0 || buf.Bytes()[buf.Len()-1] != '\n' {
 		buf.WriteByte('\n')
 	}
 	l.output(s, logger, buf, depth, file, line, false)
 }
 
-func (l *loggingT) printf(s severity, logger *logr.Logger, filter LogFilter, format string, args ...interface{}) {
-	buf, file, line := l.header(s, 0)
+func (l *loggingT) printf(s severity.Severity, logger *logr.Logger, filter LogFilter, format string, args ...interface{}) {
+	l.printfDepth(s, logger, filter, 1, format, args...)
+}
+
+func (l *loggingT) printfDepth(s severity.Severity, logger *logr.Logger, filter LogFilter, depth int, format string, args ...interface{}) {
+	buf, file, line := l.header(s, depth)
 	// if logr is set, we clear the generated header as we rely on the backing
 	// logr implementation to print headers
 	if logger != nil {
-		l.putBuffer(buf)
-		buf = l.getBuffer()
+		l.bufferCache.PutBuffer(buf)
+		buf = l.bufferCache.GetBuffer()
 	}
 	if filter != nil {
 		format, args = filter.FilterF(format, args)
@@ -750,19 +624,19 @@ func (l *loggingT) printf(s severity, logger *logr.Logger, filter LogFilter, for
 	if buf.Bytes()[buf.Len()-1] != '\n' {
 		buf.WriteByte('\n')
 	}
-	l.output(s, logger, buf, 0 /* depth */, file, line, false)
+	l.output(s, logger, buf, depth, file, line, false)
 }
 
 // printWithFileLine behaves like print but uses the provided file and line number.  If
 // 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, logger *logr.Logger, filter LogFilter, file string, line int, alsoToStderr bool, args ...interface{}) {
+func (l *loggingT) printWithFileLine(s severity.Severity, logger *logr.Logger, filter LogFilter, file string, line int, alsoToStderr bool, args ...interface{}) {
 	buf := l.formatHeader(s, file, line)
 	// if logr is set, we clear the generated header as we rely on the backing
 	// logr implementation to print headers
 	if logger != nil {
-		l.putBuffer(buf)
-		buf = l.getBuffer()
+		l.bufferCache.PutBuffer(buf)
+		buf = l.bufferCache.GetBuffer()
 	}
 	if filter != nil {
 		args = filter.Filter(args)
@@ -783,7 +657,7 @@ func (l *loggingT) errorS(err error, logger *logr.Logger, filter LogFilter, dept
 		logger.WithCallDepth(depth+2).Error(err, msg, keysAndValues...)
 		return
 	}
-	l.printS(err, errorLog, depth+1, msg, keysAndValues...)
+	l.printS(err, severity.ErrorLog, depth+1, msg, keysAndValues...)
 }
 
 // if loggr is specified, will call loggr.Info, otherwise output with logging module.
@@ -795,140 +669,25 @@ func (l *loggingT) infoS(logger *logr.Logger, filter LogFilter, depth int, msg s
 		logger.WithCallDepth(depth+2).Info(msg, keysAndValues...)
 		return
 	}
-	l.printS(nil, infoLog, depth+1, msg, keysAndValues...)
+	l.printS(nil, severity.InfoLog, depth+1, msg, keysAndValues...)
 }
 
 // printS is called from infoS and errorS if loggr is not specified.
 // set log severity by s
-func (l *loggingT) printS(err error, s severity, depth int, msg string, keysAndValues ...interface{}) {
+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.
-	b := l.getBuffer()
+	b := l.bufferCache.GetBuffer()
 	// 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 {
-		kvListFormat(&b.Buffer, "err", err)
+		serialize.KVListFormat(&b.Buffer, "err", err)
 	}
-	kvListFormat(&b.Buffer, keysAndValues...)
-	l.printDepth(s, logging.logr, nil, depth+1, &b.Buffer)
+	serialize.KVListFormat(&b.Buffer, keysAndValues...)
+	l.printDepth(s, globalLogger, nil, depth+1, &b.Buffer)
 	// Make the buffer available for reuse.
-	l.putBuffer(b)
-}
-
-const missingValue = "(MISSING)"
-
-func kvListFormat(b *bytes.Buffer, keysAndValues ...interface{}) {
-	for i := 0; i < len(keysAndValues); i += 2 {
-		var v interface{}
-		k := keysAndValues[i]
-		if i+1 < len(keysAndValues) {
-			v = keysAndValues[i+1]
-		} else {
-			v = missingValue
-		}
-		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 k, ok := k.(string); ok {
-			// Avoid one allocation when the key is a string, which
-			// normally it should be.
-			b.WriteString(k)
-		} 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 fmt.Stringer:
-			writeStringValue(b, true, stringerToString(v))
-		case string:
-			writeStringValue(b, true, v)
-		case error:
-			writeStringValue(b, true, v.Error())
-		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:
-			writeStringValue(b, false, fmt.Sprintf("%+v", v))
-		}
-	}
-}
-
-func stringerToString(s fmt.Stringer) (ret string) {
-	defer func() {
-		if err := recover(); err != nil {
-			ret = "nil"
-		}
-	}()
-	ret = s.String()
-	return
-}
-
-func writeStringValue(b *bytes.Buffer, quote bool, v string) {
-	data := []byte(v)
-	index := bytes.IndexByte(data, '\n')
-	if index == -1 {
-		b.WriteByte('=')
-		if quote {
-			// Simple string, quote quotation marks and non-printable characters.
-			b.WriteString(strconv.Quote(v))
-			return
-		}
-		// Non-string with no line breaks.
-		b.WriteString(v)
-		return
-	}
-
-	// Complex multi-line string, show as-is with indention like this:
-	// I... "hello world" key=<
-	// <tab>line 1
-	// <tab>line 2
-	//  >
-	//
-	// Tabs indent the lines of the value while the end of string delimiter
-	// is indented with a space. That has two purposes:
-	// - visual difference between the two for a human reader because indention
-	//   will be different
-	// - no ambiguity when some value line starts with the end delimiter
-	//
-	// One downside is that the output cannot distinguish between strings that
-	// end with a line break and those that don't because the end delimiter
-	// will always be on the next line.
-	b.WriteString("=<\n")
-	for index != -1 {
-		b.WriteByte('\t')
-		b.Write(data[0 : index+1])
-		data = data[index+1:]
-		index = bytes.IndexByte(data, '\n')
-	}
-	if len(data) == 0 {
-		// String ended with line break, don't add another.
-		b.WriteString(" >")
-	} else {
-		// No line break at end of last line, write rest of string and
-		// add one.
-		b.WriteByte('\t')
-		b.Write(data)
-		b.WriteString("\n >")
-	}
+	l.bufferCache.PutBuffer(b)
 }
 
 // redirectBuffer is used to set an alternate destination for the logs
@@ -948,36 +707,11 @@ func (rb *redirectBuffer) Write(bytes []byte) (n int, err error) {
 	return rb.w.Write(bytes)
 }
 
-// SetLogger will set the backing logr implementation for klog.
-// If set, all log lines will be suppressed from the regular Output, and
-// redirected to the logr implementation.
-// Use as:
-//   ...
-//   klog.SetLogger(zapr.NewLogger(zapLog))
-//
-// To remove a backing logr implemention, use ClearLogger. Setting an
-// empty logger with SetLogger(logr.Logger{}) does not work.
-func SetLogger(logr logr.Logger) {
-	logging.mu.Lock()
-	defer logging.mu.Unlock()
-
-	logging.logr = &logr
-}
-
-// ClearLogger removes a backing logr implementation if one was set earlier
-// with SetLogger.
-func ClearLogger() {
-	logging.mu.Lock()
-	defer logging.mu.Unlock()
-
-	logging.logr = nil
-}
-
 // SetOutput sets the output destination for all severities
 func SetOutput(w io.Writer) {
 	logging.mu.Lock()
 	defer logging.mu.Unlock()
-	for s := fatalLog; s >= infoLog; s-- {
+	for s := severity.FatalLog; s >= severity.InfoLog; s-- {
 		rb := &redirectBuffer{
 			w: w,
 		}
@@ -989,7 +723,7 @@ func SetOutput(w io.Writer) {
 func SetOutputBySeverity(name string, w io.Writer) {
 	logging.mu.Lock()
 	defer logging.mu.Unlock()
-	sev, ok := severityByName(name)
+	sev, ok := severity.ByName(name)
 	if !ok {
 		panic(fmt.Sprintf("SetOutputBySeverity(%q): unrecognized severity name", name))
 	}
@@ -1008,7 +742,7 @@ func LogToStderr(stderr bool) {
 }
 
 // output writes the data to the log files and releases the buffer.
-func (l *loggingT) output(s severity, log *logr.Logger, buf *buffer, depth int, file string, line int, alsoToStderr bool) {
+func (l *loggingT) output(s severity.Severity, log *logr.Logger, buf *buffer.Buffer, depth int, file string, line int, alsoToStderr bool) {
 	var isLocked = true
 	l.mu.Lock()
 	defer func() {
@@ -1027,8 +761,8 @@ func (l *loggingT) output(s severity, log *logr.Logger, buf *buffer, depth int,
 	if log != nil {
 		// TODO: set 'severity' and caller information as structured log info
 		// keysAndValues := []interface{}{"severity", severityName[s], "file", file, "line", line}
-		if s == errorLog {
-			l.logr.WithCallDepth(depth+3).Error(nil, string(data))
+		if s == severity.ErrorLog {
+			globalLogger.WithCallDepth(depth+3).Error(nil, string(data))
 		} else {
 			log.WithCallDepth(depth + 3).Info(string(data))
 		}
@@ -1042,13 +776,13 @@ func (l *loggingT) output(s severity, log *logr.Logger, buf *buffer, depth int,
 		if logging.logFile != "" {
 			// Since we are using a single log file, all of the items in l.file array
 			// will point to the same file, so just use one of them to write data.
-			if l.file[infoLog] == nil {
-				if err := l.createFiles(infoLog); err != nil {
+			if l.file[severity.InfoLog] == nil {
+				if err := l.createFiles(severity.InfoLog); err != nil {
 					os.Stderr.Write(data) // Make sure the message appears somewhere.
 					l.exit(err)
 				}
 			}
-			l.file[infoLog].Write(data)
+			l.file[severity.InfoLog].Write(data)
 		} else {
 			if l.file[s] == nil {
 				if err := l.createFiles(s); err != nil {
@@ -1061,28 +795,28 @@ func (l *loggingT) output(s severity, log *logr.Logger, buf *buffer, depth int,
 				l.file[s].Write(data)
 			} else {
 				switch s {
-				case fatalLog:
-					l.file[fatalLog].Write(data)
+				case severity.FatalLog:
+					l.file[severity.FatalLog].Write(data)
 					fallthrough
-				case errorLog:
-					l.file[errorLog].Write(data)
+				case severity.ErrorLog:
+					l.file[severity.ErrorLog].Write(data)
 					fallthrough
-				case warningLog:
-					l.file[warningLog].Write(data)
+				case severity.WarningLog:
+					l.file[severity.WarningLog].Write(data)
 					fallthrough
-				case infoLog:
-					l.file[infoLog].Write(data)
+				case severity.InfoLog:
+					l.file[severity.InfoLog].Write(data)
 				}
 			}
 		}
 	}
-	if s == fatalLog {
+	if s == severity.FatalLog {
 		// If we got here via Exit rather than Fatal, print no stacks.
 		if atomic.LoadUint32(&fatalNoStacks) > 0 {
 			l.mu.Unlock()
 			isLocked = false
-			timeoutFlush(10 * time.Second)
-			os.Exit(1)
+			timeoutFlush(ExitFlushTimeout)
+			OsExit(1)
 		}
 		// Dump all goroutine stacks before exiting.
 		trace := stacks(true)
@@ -1092,17 +826,17 @@ func (l *loggingT) output(s severity, log *logr.Logger, buf *buffer, depth int,
 		}
 		// Write the stack trace for all goroutines to the files.
 		logExitFunc = func(error) {} // If we get a write error, we'll still exit below.
-		for log := fatalLog; log >= infoLog; log-- {
+		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)
 			}
 		}
 		l.mu.Unlock()
 		isLocked = false
-		timeoutFlush(10 * time.Second)
-		os.Exit(255) // C++ uses -1, which is silly because it's anded(&) with 255 anyway.
+		timeoutFlush(ExitFlushTimeout)
+		OsExit(255) // C++ uses -1, which is silly because it's anded with 255 anyway.
 	}
-	l.putBuffer(buf)
+	l.bufferCache.PutBuffer(buf)
 
 	if stats := severityStats[s]; stats != nil {
 		atomic.AddInt64(&stats.lines, 1)
@@ -1110,23 +844,6 @@ func (l *loggingT) output(s severity, log *logr.Logger, buf *buffer, depth int,
 	}
 }
 
-// timeoutFlush calls Flush and returns when it completes or after timeout
-// elapses, whichever happens first.  This is needed because the hooks invoked
-// by Flush may deadlock when klog.Fatal is called from a hook that holds
-// a lock.
-func timeoutFlush(timeout time.Duration) {
-	done := make(chan bool, 1)
-	go func() {
-		Flush() // calls logging.lockAndFlushAll()
-		done <- true
-	}()
-	select {
-	case <-done:
-	case <-time.After(timeout):
-		fmt.Fprintln(os.Stderr, "klog: Flush took longer than", timeout)
-	}
-}
-
 // stacks is a wrapper for runtime.Stack that attempts to recover the data for all goroutines.
 func stacks(all bool) []byte {
 	// We don't know how big the traces are, so grow a few times if they don't fit. Start large, though.
@@ -1163,7 +880,7 @@ func (l *loggingT) exit(err error) {
 		return
 	}
 	l.flushAll()
-	os.Exit(2)
+	OsExit(2)
 }
 
 // syncBuffer joins a bufio.Writer to its underlying file, providing access to the
@@ -1174,7 +891,7 @@ type syncBuffer struct {
 	logger *loggingT
 	*bufio.Writer
 	file     *os.File
-	sev      severity
+	sev      severity.Severity
 	nbytes   uint64 // The number of bytes written to this file
 	maxbytes uint64 // The max number of bytes this syncBuffer.file can hold before cleaning up.
 }
@@ -1220,7 +937,7 @@ func (sb *syncBuffer) rotateFile(now time.Time, startup bool) error {
 		sb.file.Close()
 	}
 	var err error
-	sb.file, _, err = create(severityName[sb.sev], now, startup)
+	sb.file, _, err = create(severity.Name[sb.sev], now, startup)
 	if err != nil {
 		return err
 	}
@@ -1258,11 +975,12 @@ const bufferSize = 256 * 1024
 
 // createFiles creates all the log files for severity from sev down to infoLog.
 // l.mu is held.
-func (l *loggingT) createFiles(sev severity) error {
+func (l *loggingT) createFiles(sev severity.Severity) error {
+	l.flushD.run()
 	now := time.Now()
 	// Files are created in decreasing severity order, so as soon as we find one
 	// has already been created, we can stop.
-	for s := sev; s >= infoLog && l.file[s] == nil; s-- {
+	for s := sev; s >= severity.InfoLog && l.file[s] == nil; s-- {
 		sb := &syncBuffer{
 			logger:   l,
 			sev:      s,
@@ -1279,12 +997,88 @@ func (l *loggingT) createFiles(sev severity) error {
 const flushInterval = 5 * time.Second
 
 // flushDaemon periodically flushes the log file buffers.
-func (l *loggingT) flushDaemon() {
-	for range time.NewTicker(flushInterval).C {
-		l.lockAndFlushAll()
+type flushDaemon struct {
+	mu       sync.Mutex
+	clock    clock.WithTicker
+	interval time.Duration
+	flush    func()
+	stopC    chan struct{}
+	stopDone chan struct{}
+}
+
+// newFlushDaemon returns a new flushDaemon. If the passed clock is nil, a
+// clock.RealClock is used.
+func newFlushDaemon(interval time.Duration, flush func(), tickClock clock.WithTicker) *flushDaemon {
+	if tickClock == nil {
+		tickClock = clock.RealClock{}
+	}
+	return &flushDaemon{
+		interval: interval,
+		flush:    flush,
+		clock:    tickClock,
 	}
 }
 
+// run starts a goroutine that periodically calls the daemons flush function.
+// Calling run on an already running daemon will have no effect.
+func (f *flushDaemon) run() {
+	f.mu.Lock()
+	defer f.mu.Unlock()
+
+	if f.stopC != nil { // daemon already running
+		return
+	}
+
+	f.stopC = make(chan struct{}, 1)
+	f.stopDone = make(chan struct{}, 1)
+
+	ticker := f.clock.NewTicker(f.interval)
+	go func() {
+		defer ticker.Stop()
+		defer func() { f.stopDone <- struct{}{} }()
+		for {
+			select {
+			case <-ticker.C():
+				f.flush()
+			case <-f.stopC:
+				f.flush()
+				return
+			}
+		}
+	}()
+}
+
+// stop stops the running flushDaemon and waits until the daemon has shut down.
+// Calling stop on a daemon that isn't running will have no effect.
+func (f *flushDaemon) stop() {
+	f.mu.Lock()
+	defer f.mu.Unlock()
+
+	if f.stopC == nil { // daemon not running
+		return
+	}
+
+	f.stopC <- struct{}{}
+	<-f.stopDone
+
+	f.stopC = nil
+	f.stopDone = nil
+}
+
+// isRunning returns true if the flush daemon is running.
+func (f *flushDaemon) isRunning() bool {
+	f.mu.Lock()
+	defer f.mu.Unlock()
+	return f.stopC != nil
+}
+
+// StopFlushDaemon stops the flush daemon, if running.
+// This prevents klog from leaking goroutines on shutdown. After stopping
+// the daemon, you can still manually flush buffers by calling Flush().
+func StopFlushDaemon() {
+	logging.flushD.stop()
+}
+
 // lockAndFlushAll is like flushAll but locks l.mu first.
 func (l *loggingT) lockAndFlushAll() {
 	l.mu.Lock()
@@ -1296,7 +1090,7 @@ func (l *loggingT) lockAndFlushAll() {
 // l.mu is held.
 func (l *loggingT) flushAll() {
 	// Flush from fatal down, in case there's trouble flushing.
-	for s := fatalLog; s >= infoLog; s-- {
+	for s := severity.FatalLog; s >= severity.InfoLog; s-- {
 		file := l.file[s]
 		if file != nil {
 			file.Flush() // ignore error
@@ -1313,7 +1107,7 @@ func (l *loggingT) flushAll() {
 // Valid names are "INFO", "WARNING", "ERROR", and "FATAL".  If the name is not
 // recognized, CopyStandardLogTo panics.
 func CopyStandardLogTo(name string) {
-	sev, ok := severityByName(name)
+	sev, ok := severity.ByName(name)
 	if !ok {
 		panic(fmt.Sprintf("log.CopyStandardLogTo(%q): unrecognized severity name", name))
 	}
@@ -1325,7 +1119,7 @@ func CopyStandardLogTo(name string) {
 
 // logBridge provides the Write method that enables CopyStandardLogTo to connect
 // Go's standard logs to the logs provided by this package.
-type logBridge severity
+type logBridge severity.Severity
 
 // Write parses the standard logging line and passes its components to the
 // logger for severity(lb).
@@ -1349,7 +1143,7 @@ func (lb logBridge) Write(b []byte) (n int, err error) {
 	}
 	// printWithFileLine with alsoToStderr=true, so standard log messages
 	// always appear on standard error.
-	logging.printWithFileLine(severity(lb), logging.logr, logging.filter, file, line, true, text)
+	logging.printWithFileLine(severity.Severity(lb), globalLogger, logging.filter, file, line, true, text)
 	return len(b), nil
 }
 
@@ -1384,22 +1178,21 @@ func (l *loggingT) setV(pc uintptr) Level {
 type Verbose struct {
 	enabled bool
 	logr    *logr.Logger
-	filter  LogFilter
 }
 
 func newVerbose(level Level, b bool) Verbose {
-	if logging.logr == nil {
-		return Verbose{b, nil, logging.filter}
+	if globalLogger == nil {
+		return Verbose{b, nil}
 	}
-	v := logging.logr.V(int(level))
-	return Verbose{b, &v, logging.filter}
+	v := globalLogger.V(int(level))
+	return Verbose{b, &v}
 }
 
 // V reports whether verbosity at the call site is at least the requested level.
 // The returned value is a struct of type Verbose, which implements Info, Infoln
 // and Infof. These methods will write to the Info log if called.
 // Thus, one may write either
-//	if glog.V(2).Enabled() { klog.Info("log this") }
+//	if klog.V(2).Enabled() { klog.Info("log this") }
 // or
 //	klog.V(2).Info("log this")
 // The second form is shorter but the first is cheaper if logging is off because it does
@@ -1455,7 +1248,15 @@ func (v Verbose) Enabled() bool {
 // See the documentation of V for usage.
 func (v Verbose) Info(args ...interface{}) {
 	if v.enabled {
-		logging.print(infoLog, v.logr, v.filter, args...)
+		logging.print(severity.InfoLog, v.logr, logging.filter, args...)
+	}
+}
+
+// InfoDepth is equivalent to the global InfoDepth function, guarded by the value of v.
+// See the documentation of V for usage.
+func (v Verbose) InfoDepth(depth int, args ...interface{}) {
+	if v.enabled {
+		logging.printDepth(severity.InfoLog, v.logr, logging.filter, depth, args...)
 	}
 }
 
@@ -1463,7 +1264,15 @@ func (v Verbose) Info(args ...interface{}) {
 // See the documentation of V for usage.
 func (v Verbose) Infoln(args ...interface{}) {
 	if v.enabled {
-		logging.println(infoLog, v.logr, v.filter, args...)
+		logging.println(severity.InfoLog, v.logr, logging.filter, args...)
+	}
+}
+
+// InfolnDepth is equivalent to the global InfolnDepth function, guarded by the value of v.
+// See the documentation of V for usage.
+func (v Verbose) InfolnDepth(depth int, args ...interface{}) {
+	if v.enabled {
+		logging.printlnDepth(severity.InfoLog, v.logr, logging.filter, depth, args...)
 	}
 }
 
@@ -1471,7 +1280,15 @@ func (v Verbose) Infoln(args ...interface{}) {
 // See the documentation of V for usage.
 func (v Verbose) Infof(format string, args ...interface{}) {
 	if v.enabled {
-		logging.printf(infoLog, v.logr, v.filter, format, args...)
+		logging.printf(severity.InfoLog, v.logr, logging.filter, format, args...)
+	}
+}
+
+// InfofDepth is equivalent to the global InfofDepth function, guarded by the value of v.
+// See the documentation of V for usage.
+func (v Verbose) InfofDepth(depth int, format string, args ...interface{}) {
+	if v.enabled {
+		logging.printfDepth(severity.InfoLog, v.logr, logging.filter, depth, format, args...)
 	}
 }
 
@@ -1479,28 +1296,28 @@ func (v Verbose) Infof(format string, args ...interface{}) {
 // See the documentation of V for usage.
 func (v Verbose) InfoS(msg string, keysAndValues ...interface{}) {
 	if v.enabled {
-		logging.infoS(v.logr, v.filter, 0, msg, keysAndValues...)
+		logging.infoS(v.logr, logging.filter, 0, msg, keysAndValues...)
 	}
 }
 
 // InfoSDepth acts as InfoS but uses depth to determine which call frame to log.
 // InfoSDepth(0, "msg") is the same as InfoS("msg").
 func InfoSDepth(depth int, msg string, keysAndValues ...interface{}) {
-	logging.infoS(logging.logr, logging.filter, depth, msg, keysAndValues...)
+	logging.infoS(globalLogger, logging.filter, depth, msg, keysAndValues...)
 }
 
 // InfoSDepth is equivalent to the global InfoSDepth function, guarded by the value of v.
 // See the documentation of V for usage.
 func (v Verbose) InfoSDepth(depth int, msg string, keysAndValues ...interface{}) {
 	if v.enabled {
-		logging.infoS(v.logr, v.filter, depth, msg, keysAndValues...)
+		logging.infoS(v.logr, logging.filter, depth, msg, keysAndValues...)
 	}
 }
 
 // Deprecated: Use ErrorS instead.
 func (v Verbose) Error(err error, msg string, args ...interface{}) {
 	if v.enabled {
-		logging.errorS(err, v.logr, v.filter, 0, msg, args...)
+		logging.errorS(err, v.logr, logging.filter, 0, msg, args...)
 	}
 }
 
@@ -1508,32 +1325,44 @@ func (v Verbose) Error(err error, msg string, args ...interface{}) {
 // See the documentation of V for usage.
 func (v Verbose) ErrorS(err error, msg string, keysAndValues ...interface{}) {
 	if v.enabled {
-		logging.errorS(err, v.logr, v.filter, 0, msg, keysAndValues...)
+		logging.errorS(err, v.logr, logging.filter, 0, msg, keysAndValues...)
 	}
 }
 
 // Info logs to the INFO log.
 // Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
 func Info(args ...interface{}) {
-	logging.print(infoLog, logging.logr, logging.filter, args...)
+	logging.print(severity.InfoLog, globalLogger, logging.filter, args...)
 }
 
 // InfoDepth acts as Info but uses depth to determine which call frame to log.
 // InfoDepth(0, "msg") is the same as Info("msg").
 func InfoDepth(depth int, args ...interface{}) {
-	logging.printDepth(infoLog, logging.logr, logging.filter, depth, args...)
+	logging.printDepth(severity.InfoLog, globalLogger, logging.filter, depth, args...)
 }
 
 // Infoln logs to the INFO log.
 // Arguments are handled in the manner of fmt.Println; a newline is always appended.
 func Infoln(args ...interface{}) {
-	logging.println(infoLog, logging.logr, logging.filter, args...)
+	logging.println(severity.InfoLog, globalLogger, logging.filter, args...)
+}
+
+// InfolnDepth acts as Infoln but uses depth to determine which call frame to log.
+// InfolnDepth(0, "msg") is the same as Infoln("msg").
+func InfolnDepth(depth int, args ...interface{}) {
+	logging.printlnDepth(severity.InfoLog, globalLogger, logging.filter, depth, args...)
 }
 
 // Infof logs to the INFO log.
 // Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
 func Infof(format string, args ...interface{}) {
-	logging.printf(infoLog, logging.logr, logging.filter, format, args...)
+	logging.printf(severity.InfoLog, globalLogger, logging.filter, format, args...)
+}
+
+// InfofDepth acts as Infof but uses depth to determine which call frame to log.
+// InfofDepth(0, "msg", args...) is the same as Infof("msg", args...).
+func InfofDepth(depth int, format string, args ...interface{}) {
+	logging.printfDepth(severity.InfoLog, globalLogger, logging.filter, depth, format, args...)
 }
 
 // InfoS structured logs to the INFO log.
@@ -1545,55 +1374,79 @@ func Infof(format string, args ...interface{}) {
 // output:
 // >> I1025 00:15:15.525108       1 controller_utils.go:116] "Pod status updated" pod="kubedns" status="ready"
 func InfoS(msg string, keysAndValues ...interface{}) {
-	logging.infoS(logging.logr, logging.filter, 0, msg, keysAndValues...)
+	logging.infoS(globalLogger, logging.filter, 0, msg, keysAndValues...)
 }
 
 // Warning logs to the WARNING and INFO logs.
 // Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
 func Warning(args ...interface{}) {
-	logging.print(warningLog, logging.logr, logging.filter, args...)
+	logging.print(severity.WarningLog, globalLogger, logging.filter, args...)
 }
 
 // WarningDepth acts as Warning but uses depth to determine which call frame to log.
 // WarningDepth(0, "msg") is the same as Warning("msg").
 func WarningDepth(depth int, args ...interface{}) {
-	logging.printDepth(warningLog, logging.logr, logging.filter, depth, args...)
+	logging.printDepth(severity.WarningLog, globalLogger, logging.filter, depth, args...)
 }
 
 // Warningln logs to the WARNING and INFO logs.
 // Arguments are handled in the manner of fmt.Println; a newline is always appended.
 func Warningln(args ...interface{}) {
-	logging.println(warningLog, logging.logr, logging.filter, args...)
+	logging.println(severity.WarningLog, globalLogger, logging.filter, args...)
+}
+
+// WarninglnDepth acts as Warningln but uses depth to determine which call frame to log.
+// WarninglnDepth(0, "msg") is the same as Warningln("msg").
+func WarninglnDepth(depth int, args ...interface{}) {
+	logging.printlnDepth(severity.WarningLog, globalLogger, logging.filter, depth, args...)
 }
 
 // Warningf logs to the WARNING and INFO logs.
 // Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
 func Warningf(format string, args ...interface{}) {
-	logging.printf(warningLog, logging.logr, logging.filter, format, args...)
+	logging.printf(severity.WarningLog, globalLogger, logging.filter, format, args...)
+}
+
+// WarningfDepth acts as Warningf but uses depth to determine which call frame to log.
+// WarningfDepth(0, "msg", args...) is the same as Warningf("msg", args...).
+func WarningfDepth(depth int, format string, args ...interface{}) {
+	logging.printfDepth(severity.WarningLog, globalLogger, logging.filter, depth, format, args...)
 }
 
 // Error logs to the ERROR, WARNING, and INFO logs.
 // Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
 func Error(args ...interface{}) {
-	logging.print(errorLog, logging.logr, logging.filter, args...)
+	logging.print(severity.ErrorLog, globalLogger, logging.filter, args...)
 }
 
 // ErrorDepth acts as Error but uses depth to determine which call frame to log.
 // ErrorDepth(0, "msg") is the same as Error("msg").
 func ErrorDepth(depth int, args ...interface{}) {
-	logging.printDepth(errorLog, logging.logr, logging.filter, depth, args...)
+	logging.printDepth(severity.ErrorLog, globalLogger, logging.filter, depth, args...)
 }
 
 // Errorln logs to the ERROR, WARNING, and INFO logs.
 // Arguments are handled in the manner of fmt.Println; a newline is always appended.
 func Errorln(args ...interface{}) {
-	logging.println(errorLog, logging.logr, logging.filter, args...)
+	logging.println(severity.ErrorLog, globalLogger, logging.filter, args...)
+}
+
+// ErrorlnDepth acts as Errorln but uses depth to determine which call frame to log.
+// ErrorlnDepth(0, "msg") is the same as Errorln("msg").
+func ErrorlnDepth(depth int, args ...interface{}) {
+	logging.printlnDepth(severity.ErrorLog, globalLogger, logging.filter, depth, args...)
 }
 
 // Errorf logs to the ERROR, WARNING, and INFO logs.
 // Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
 func Errorf(format string, args ...interface{}) {
-	logging.printf(errorLog, logging.logr, logging.filter, format, args...)
+	logging.printf(severity.ErrorLog, globalLogger, logging.filter, format, args...)
+}
+
+// ErrorfDepth acts as Errorf but uses depth to determine which call frame to log.
+// ErrorfDepth(0, "msg", args...) is the same as Errorf("msg", args...).
+func ErrorfDepth(depth int, format string, args ...interface{}) {
+	logging.printfDepth(severity.ErrorLog, globalLogger, logging.filter, depth, format, args...)
 }
 
 // ErrorS structured logs to the ERROR, WARNING, and INFO logs.
@@ -1606,71 +1459,97 @@ func Errorf(format string, args ...interface{}) {
 // output:
 // >> E1025 00:15:15.525108       1 controller_utils.go:114] "Failed to update pod status" err="timeout"
 func ErrorS(err error, msg string, keysAndValues ...interface{}) {
-	logging.errorS(err, logging.logr, logging.filter, 0, msg, keysAndValues...)
+	logging.errorS(err, globalLogger, logging.filter, 0, msg, keysAndValues...)
 }
 
 // ErrorSDepth acts as ErrorS but uses depth to determine which call frame to log.
 // ErrorSDepth(0, "msg") is the same as ErrorS("msg").
 func ErrorSDepth(depth int, err error, msg string, keysAndValues ...interface{}) {
-	logging.errorS(err, logging.logr, logging.filter, depth, msg, keysAndValues...)
+	logging.errorS(err, globalLogger, logging.filter, depth, msg, keysAndValues...)
 }
 
 // Fatal logs to the FATAL, ERROR, WARNING, and INFO logs,
-// including a stack trace of all running goroutines, then calls os.Exit(255).
+// including a stack trace of all running goroutines, then calls OsExit(255).
 // Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
 func Fatal(args ...interface{}) {
-	logging.print(fatalLog, logging.logr, logging.filter, args...)
+	logging.print(severity.FatalLog, globalLogger, logging.filter, args...)
 }
 
 // FatalDepth acts as Fatal but uses depth to determine which call frame to log.
 // FatalDepth(0, "msg") is the same as Fatal("msg").
 func FatalDepth(depth int, args ...interface{}) {
-	logging.printDepth(fatalLog, logging.logr, logging.filter, depth, args...)
+	logging.printDepth(severity.FatalLog, globalLogger, logging.filter, depth, args...)
 }
 
 // Fatalln logs to the FATAL, ERROR, WARNING, and INFO logs,
-// including a stack trace of all running goroutines, then calls os.Exit(255).
+// including a stack trace of all running goroutines, then calls OsExit(255).
 // Arguments are handled in the manner of fmt.Println; a newline is always appended.
 func Fatalln(args ...interface{}) {
-	logging.println(fatalLog, logging.logr, logging.filter, args...)
+	logging.println(severity.FatalLog, globalLogger, logging.filter, args...)
+}
+
+// FatallnDepth acts as Fatalln but uses depth to determine which call frame to log.
+// FatallnDepth(0, "msg") is the same as Fatalln("msg").
+func FatallnDepth(depth int, args ...interface{}) {
+	logging.printlnDepth(severity.FatalLog, globalLogger, logging.filter, depth, args...)
 }
 
 // Fatalf logs to the FATAL, ERROR, WARNING, and INFO logs,
-// including a stack trace of all running goroutines, then calls os.Exit(255).
+// including a stack trace of all running goroutines, then calls OsExit(255).
 // Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
 func Fatalf(format string, args ...interface{}) {
-	logging.printf(fatalLog, logging.logr, logging.filter, format, args...)
+	logging.printf(severity.FatalLog, globalLogger, logging.filter, format, args...)
+}
+
+// FatalfDepth acts as Fatalf but uses depth to determine which call frame to log.
+// FatalfDepth(0, "msg", args...) is the same as Fatalf("msg", args...).
+func FatalfDepth(depth int, format string, args ...interface{}) {
+	logging.printfDepth(severity.FatalLog, globalLogger, logging.filter, depth, format, args...)
 }
 
 // fatalNoStacks is non-zero if we are to exit without dumping goroutine stacks.
 // It allows Exit and relatives to use the Fatal logs.
 var fatalNoStacks uint32
 
-// Exit logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1).
+// Exit logs to the FATAL, ERROR, WARNING, and INFO logs, then calls OsExit(1).
 // Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
 func Exit(args ...interface{}) {
 	atomic.StoreUint32(&fatalNoStacks, 1)
-	logging.print(fatalLog, logging.logr, logging.filter, args...)
+	logging.print(severity.FatalLog, globalLogger, logging.filter, args...)
 }
 
 // ExitDepth acts as Exit but uses depth to determine which call frame to log.
 // ExitDepth(0, "msg") is the same as Exit("msg").
 func ExitDepth(depth int, args ...interface{}) {
 	atomic.StoreUint32(&fatalNoStacks, 1)
-	logging.printDepth(fatalLog, logging.logr, logging.filter, depth, args...)
+	logging.printDepth(severity.FatalLog, globalLogger, logging.filter, depth, args...)
 }
 
-// Exitln logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1).
+// Exitln logs to the FATAL, ERROR, WARNING, and INFO logs, then calls OsExit(1).
 func Exitln(args ...interface{}) {
 	atomic.StoreUint32(&fatalNoStacks, 1)
-	logging.println(fatalLog, logging.logr, logging.filter, args...)
+	logging.println(severity.FatalLog, globalLogger, logging.filter, args...)
 }
 
-// Exitf logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1).
+// ExitlnDepth acts as Exitln but uses depth to determine which call frame to log.
+// ExitlnDepth(0, "msg") is the same as Exitln("msg").
+func ExitlnDepth(depth int, args ...interface{}) {
+	atomic.StoreUint32(&fatalNoStacks, 1)
+	logging.printlnDepth(severity.FatalLog, globalLogger, logging.filter, depth, args...)
+}
+
+// Exitf logs to the FATAL, ERROR, WARNING, and INFO logs, then calls OsExit(1).
 // Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
 func Exitf(format string, args ...interface{}) {
 	atomic.StoreUint32(&fatalNoStacks, 1)
-	logging.printf(fatalLog, logging.logr, logging.filter, format, args...)
+	logging.printf(severity.FatalLog, globalLogger, logging.filter, format, args...)
+}
+
+// ExitfDepth acts as Exitf but uses depth to determine which call frame to log.
+// ExitfDepth(0, "msg", args...) is the same as Exitf("msg", args...).
+func ExitfDepth(depth int, format string, args ...interface{}) {
+	atomic.StoreUint32(&fatalNoStacks, 1)
+	logging.printfDepth(severity.FatalLog, globalLogger, logging.filter, depth, format, args...)
 }
 
 // LogFilter is a collection of functions that can filter all logging calls,
@@ -1681,79 +1560,10 @@ type LogFilter interface {
 	FilterS(msg string, keysAndValues []interface{}) (string, []interface{})
 }
 
+// SetLogFilter installs a filter that is used for all log calls.
+//
+// Modifying the filter is not thread-safe and should be done while no other
+// goroutines invoke log calls, usually during program initialization.
 func SetLogFilter(filter LogFilter) {
-	logging.mu.Lock()
-	defer logging.mu.Unlock()
-
 	logging.filter = filter
 }
-
-// ObjectRef references a kubernetes object
-type ObjectRef struct {
-	Name      string `json:"name"`
-	Namespace string `json:"namespace,omitempty"`
-}
-
-func (ref ObjectRef) String() string {
-	if ref.Namespace != "" {
-		return fmt.Sprintf("%s/%s", ref.Namespace, ref.Name)
-	}
-	return ref.Name
-}
-
-// MarshalLog ensures that loggers with support for structured output will log
-// as a struct by removing the String method via a custom type.
-func (ref ObjectRef) MarshalLog() interface{} {
-	type or ObjectRef
-	return or(ref)
-}
-
-var _ logr.Marshaler = ObjectRef{}
-
-// KMetadata is a subset of the kubernetes k8s.io/apimachinery/pkg/apis/meta/v1.Object interface
-// this interface may expand in the future, but will always be a subset of the
-// kubernetes k8s.io/apimachinery/pkg/apis/meta/v1.Object interface
-type KMetadata interface {
-	GetName() string
-	GetNamespace() string
-}
-
-// KObj returns ObjectRef from ObjectMeta
-func KObj(obj KMetadata) ObjectRef {
-	if obj == nil {
-		return ObjectRef{}
-	}
-	if val := reflect.ValueOf(obj); val.Kind() == reflect.Ptr && val.IsNil() {
-		return ObjectRef{}
-	}
-
-	return ObjectRef{
-		Name:      obj.GetName(),
-		Namespace: obj.GetNamespace(),
-	}
-}
-
-// KRef returns ObjectRef from name and namespace
-func KRef(namespace, name string) ObjectRef {
-	return ObjectRef{
-		Name:      name,
-		Namespace: namespace,
-	}
-}
-
-// KObjs returns slice of ObjectRef from an slice of ObjectMeta
-func KObjs(arg interface{}) []ObjectRef {
-	s := reflect.ValueOf(arg)
-	if s.Kind() != reflect.Slice {
-		return nil
-	}
-	objectRefs := make([]ObjectRef, 0, s.Len())
-	for i := 0; i < s.Len(); i++ {
-		if v, ok := s.Index(i).Interface().(KMetadata); ok {
-			objectRefs = append(objectRefs, KObj(v))
-		} else {
-			return nil
-		}
-	}
-	return objectRefs
-}
diff --git a/vendor/k8s.io/klog/v2/klogr.go b/vendor/k8s.io/klog/v2/klogr.go
new file mode 100644
index 000000000..cdb3834fa
--- /dev/null
+++ b/vendor/k8s.io/klog/v2/klogr.go
@@ -0,0 +1,92 @@
+/*
+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 (
+	"github.com/go-logr/logr"
+
+	"k8s.io/klog/v2/internal/serialize"
+)
+
+// 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.
+//
+// Experimental
+//
+// Notice: This function is EXPERIMENTAL and may be changed or removed in a
+// later release.
+func NewKlogr() Logger {
+	return New(&klogger{})
+}
+
+// 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{}
+}
+
+func (l *klogger) Init(info logr.RuntimeInfo) {
+	l.callDepth += info.CallDepth
+}
+
+func (l klogger) Info(level int, msg string, kvList ...interface{}) {
+	trimmed := serialize.TrimDuplicates(l.values, kvList)
+	if l.prefix != "" {
+		msg = l.prefix + ": " + msg
+	}
+	V(Level(level)).InfoSDepth(l.callDepth+1, msg, append(trimmed[0], trimmed[1]...)...)
+}
+
+func (l klogger) Enabled(level int) bool {
+	return V(Level(level)).Enabled()
+}
+
+func (l klogger) Error(err error, msg string, kvList ...interface{}) {
+	trimmed := serialize.TrimDuplicates(l.values, kvList)
+	if l.prefix != "" {
+		msg = l.prefix + ": " + msg
+	}
+	ErrorSDepth(l.callDepth+1, err, msg, append(trimmed[0], trimmed[1]...)...)
+}
+
+// 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 klogger) WithName(name string) logr.LogSink {
+	if len(l.prefix) > 0 {
+		l.prefix = l.prefix + "/"
+	}
+	l.prefix += name
+	return &l
+}
+
+func (l klogger) WithValues(kvList ...interface{}) logr.LogSink {
+	l.values = serialize.WithValues(l.values, kvList)
+	return &l
+}
+
+func (l klogger) WithCallDepth(depth int) logr.LogSink {
+	l.callDepth += depth
+	return &l
+}
+
+var _ logr.LogSink = &klogger{}
+var _ logr.CallDepthLogSink = &klogger{}
diff --git a/vendor/k8s.io/klog/v2/klogr/klogr.go b/vendor/k8s.io/klog/v2/klogr/klogr.go
index a347a8849..48cb65815 100644
--- a/vendor/k8s.io/klog/v2/klogr/klogr.go
+++ b/vendor/k8s.io/klog/v2/klogr/klogr.go
@@ -6,10 +6,13 @@ import (
 	"bytes"
 	"encoding/json"
 	"fmt"
-	"github.com/go-logr/logr"
-	"k8s.io/klog/v2"
 	"sort"
 	"strings"
+
+	"github.com/go-logr/logr"
+
+	"k8s.io/klog/v2"
+	"k8s.io/klog/v2/internal/serialize"
 )
 
 // Option is a functional option that reconfigures the logger created with New.
@@ -70,51 +73,6 @@ func (l *klogger) Init(info logr.RuntimeInfo) {
 	l.callDepth += info.CallDepth
 }
 
-// trimDuplicates will deduplicate elements provided in multiple KV tuple
-// slices, whilst maintaining the distinction between where the items are
-// contained.
-func trimDuplicates(kvLists ...[]interface{}) [][]interface{} {
-	// maintain a map of all seen keys
-	seenKeys := map[interface{}]struct{}{}
-	// build the same number of output slices as inputs
-	outs := make([][]interface{}, len(kvLists))
-	// iterate over the input slices backwards, as 'later' kv specifications
-	// of the same key will take precedence over earlier ones
-	for i := len(kvLists) - 1; i >= 0; i-- {
-		// initialise this output slice
-		outs[i] = []interface{}{}
-		// obtain a reference to the kvList we are processing
-		kvList := kvLists[i]
-
-		// start iterating at len(kvList) - 2 (i.e. the 2nd last item) for
-		// slices that have an even number of elements.
-		// We add (len(kvList) % 2) here to handle the case where there is an
-		// odd number of elements in a kvList.
-		// If there is an odd number, then the last element in the slice will
-		// have the value 'null'.
-		for i2 := len(kvList) - 2 + (len(kvList) % 2); i2 >= 0; i2 -= 2 {
-			k := kvList[i2]
-			// if we have already seen this key, do not include it again
-			if _, ok := seenKeys[k]; ok {
-				continue
-			}
-			// make a note that we've observed a new key
-			seenKeys[k] = struct{}{}
-			// attempt to obtain the value of the key
-			var v interface{}
-			// i2+1 should only ever be out of bounds if we handling the first
-			// iteration over a slice with an odd number of elements
-			if i2+1 < len(kvList) {
-				v = kvList[i2+1]
-			}
-			// add this KV tuple to the *start* of the output list to maintain
-			// the original order as we are iterating over the slice backwards
-			outs[i] = append([]interface{}{k, v}, outs[i]...)
-		}
-	}
-	return outs
-}
-
 func flatten(kvList ...interface{}) string {
 	keys := make([]string, 0, len(kvList))
 	vals := make(map[string]interface{}, len(kvList))
@@ -161,16 +119,16 @@ func (l klogger) Info(level int, msg string, kvList ...interface{}) {
 	switch l.format {
 	case FormatSerialize:
 		msgStr := flatten("msg", msg)
-		trimmed := trimDuplicates(l.values, kvList)
+		trimmed := serialize.TrimDuplicates(l.values, kvList)
 		fixedStr := flatten(trimmed[0]...)
 		userStr := flatten(trimmed[1]...)
-		klog.InfoDepth(l.callDepth+1, l.prefix, " ", msgStr, " ", fixedStr, " ", userStr)
+		klog.V(klog.Level(level)).InfoDepth(l.callDepth+1, l.prefix, " ", msgStr, " ", fixedStr, " ", userStr)
 	case FormatKlog:
-		trimmed := trimDuplicates(l.values, kvList)
+		trimmed := serialize.TrimDuplicates(l.values, kvList)
 		if l.prefix != "" {
 			msg = l.prefix + ": " + msg
 		}
-		klog.InfoSDepth(l.callDepth+1, msg, append(trimmed[0], trimmed[1]...)...)
+		klog.V(klog.Level(level)).InfoSDepth(l.callDepth+1, msg, append(trimmed[0], trimmed[1]...)...)
 	}
 }
 
@@ -182,17 +140,17 @@ func (l klogger) Error(err error, msg string, kvList ...interface{}) {
 	msgStr := flatten("msg", msg)
 	var loggableErr interface{}
 	if err != nil {
-		loggableErr = err.Error()
+		loggableErr = serialize.ErrorToString(err)
 	}
 	switch l.format {
 	case FormatSerialize:
 		errStr := flatten("error", loggableErr)
-		trimmed := trimDuplicates(l.values, kvList)
+		trimmed := serialize.TrimDuplicates(l.values, kvList)
 		fixedStr := flatten(trimmed[0]...)
 		userStr := flatten(trimmed[1]...)
 		klog.ErrorDepth(l.callDepth+1, l.prefix, " ", msgStr, " ", errStr, " ", fixedStr, " ", userStr)
 	case FormatKlog:
-		trimmed := trimDuplicates(l.values, kvList)
+		trimmed := serialize.TrimDuplicates(l.values, kvList)
 		if l.prefix != "" {
 			msg = l.prefix + ": " + msg
 		}
@@ -212,9 +170,7 @@ func (l klogger) WithName(name string) logr.LogSink {
 }
 
 func (l klogger) WithValues(kvList ...interface{}) logr.LogSink {
-	// Three slice args forces a copy.
-	n := len(l.values)
-	l.values = append(l.values[:n:n], kvList...)
+	l.values = serialize.WithValues(l.values, kvList)
 	return &l
 }
 
diff --git a/vendor/k8s.io/utils/clock/clock.go b/vendor/k8s.io/utils/clock/clock.go
index dd181ce8d..b8b6af5c8 100644
--- a/vendor/k8s.io/utils/clock/clock.go
+++ b/vendor/k8s.io/utils/clock/clock.go
@@ -63,6 +63,16 @@ type WithDelayedExecution interface {
 	AfterFunc(d time.Duration, f func()) Timer
 }
 
+// WithTickerAndDelayedExecution allows for injecting fake or real clocks
+// into code that needs Ticker and AfterFunc functionality
+type WithTickerAndDelayedExecution interface {
+	WithTicker
+	// 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.
+	AfterFunc(d time.Duration, f func()) Timer
+}
+
 // Ticker defines the Ticker interface.
 type Ticker interface {
 	C() <-chan time.Time
diff --git a/vendor/k8s.io/utils/pointer/pointer.go b/vendor/k8s.io/utils/pointer/pointer.go
index 2cab2c580..f5802d2e8 100644
--- a/vendor/k8s.io/utils/pointer/pointer.go
+++ b/vendor/k8s.io/utils/pointer/pointer.go
@@ -19,6 +19,7 @@ package pointer
 import (
 	"fmt"
 	"reflect"
+	"time"
 )
 
 // AllPtrFieldsNil tests whether all pointer fields in a struct are nil.  This is useful when,
@@ -184,7 +185,7 @@ func StringEqual(a, b *string) bool {
 	return *a == *b
 }
 
-// Float32 returns a pointer to the a float32.
+// Float32 returns a pointer to a float32.
 func Float32(i float32) *float32 {
 	return &i
 }
@@ -214,7 +215,7 @@ func Float32Equal(a, b *float32) bool {
 	return *a == *b
 }
 
-// Float64 returns a pointer to the a float64.
+// Float64 returns a pointer to a float64.
 func Float64(i float64) *float64 {
 	return &i
 }
@@ -243,3 +244,29 @@ func Float64Equal(a, b *float64) bool {
 	}
 	return *a == *b
 }
+
+// Duration returns a pointer to a time.Duration.
+func Duration(d time.Duration) *time.Duration {
+	return &d
+}
+
+// DurationDeref dereferences the time.Duration ptr and returns it if not nil, or else
+// returns def.
+func DurationDeref(ptr *time.Duration, def time.Duration) time.Duration {
+	if ptr != nil {
+		return *ptr
+	}
+	return def
+}
+
+// DurationEqual returns true if both arguments are nil or both arguments
+// dereference to the same value.
+func DurationEqual(a, b *time.Duration) bool {
+	if (a == nil) != (b == nil) {
+		return false
+	}
+	if a == nil {
+		return true
+	}
+	return *a == *b
+}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 7945a5220..759a3d857 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -743,14 +743,17 @@ k8s.io/client-go/util/workqueue
 # k8s.io/component-base v0.23.1
 k8s.io/component-base/config
 k8s.io/component-base/config/v1alpha1
-# k8s.io/klog/v2 v2.40.1
+# k8s.io/klog/v2 v2.50.0
 ## explicit
 k8s.io/klog/v2
+k8s.io/klog/v2/internal/buffer
+k8s.io/klog/v2/internal/serialize
+k8s.io/klog/v2/internal/severity
 k8s.io/klog/v2/klogr
 # k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65
 k8s.io/kube-openapi/pkg/schemaconv
 k8s.io/kube-openapi/pkg/util/proto
-# k8s.io/utils v0.0.0-20211116205334-6203023598ed
+# k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9
 k8s.io/utils/buffer
 k8s.io/utils/clock
 k8s.io/utils/clock/testing


Test patch:
diff --git a/vendor/k8s.io/utils/clock/testing/fake_clock.go b/vendor/k8s.io/utils/clock/testing/fake_clock.go
index fb493c4ba..79e11deb6 100644
--- a/vendor/k8s.io/utils/clock/testing/fake_clock.go
+++ b/vendor/k8s.io/utils/clock/testing/fake_clock.go
@@ -239,7 +239,8 @@ func (f *FakeClock) Sleep(d time.Duration) {
 
 // IntervalClock implements clock.PassiveClock, but each invocation of Now steps the clock forward the specified duration.
 // IntervalClock technically implements the other methods of clock.Clock, but each implementation is just a panic.
-// See SimpleIntervalClock for an alternative that only has the methods of PassiveClock.
+//
+// Deprecated: See SimpleIntervalClock for an alternative that only has the methods of PassiveClock.
 type IntervalClock struct {
 	Time     time.Time
 	Duration time.Duration
@@ -282,9 +283,9 @@ func (*IntervalClock) Tick(d time.Duration) <-chan time.Time {
 
 // NewTicker has no implementation yet and is omitted.
 // TODO: make interval clock use FakeClock so this can be implemented.
-//func (*IntervalClock) NewTicker(d time.Duration) clock.Ticker {
-//	panic("IntervalClock doesn't implement NewTicker")
-//}
+func (*IntervalClock) NewTicker(d time.Duration) clock.Ticker {
+	panic("IntervalClock doesn't implement NewTicker")
+}
 
 // Sleep is unimplemented, will panic.
 func (*IntervalClock) Sleep(d time.Duration) {