Repository: kubernetes-sigs/security-profiles-operator
Pull request #1002 (2022-06-23 04:40:40)

build(deps): bump k8s.io/klog/v2 from 2.60.1 to 2.70.0
Bumps [k8s.io/klog/v2](https://github.com/kubernetes/klog) from 2.60.1 to 2.70.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.25 (Take 1)</h2>
<h2>What's Changed</h2>
<ul>
<li>logcheck: contextual logging + enhanced checks by <a href="https://github.com/pohly"><code>@​pohly</code></a> in <a href="https://github-redirect.dependabot.com/kubernetes/klog/pull/297">kubernetes/klog#297</a></li>
<li>hack/tools: drop dependency on golangci-lint by <a href="https://github.com/pohly"><code>@​pohly</code></a> in <a href="https://github-redirect.dependabot.com/kubernetes/klog/pull/313">kubernetes/klog#313</a></li>
<li>StopFlushDaemon: document flushing on shutdown by <a href="https://github.com/pohly"><code>@​pohly</code></a> in <a href="https://github-redirect.dependabot.com/kubernetes/klog/pull/314">kubernetes/klog#314</a></li>
<li>logcheck: fix detection of invalid * regexp in filter by <a href="https://github.com/pohly"><code>@​pohly</code></a> in <a href="https://github-redirect.dependabot.com/kubernetes/klog/pull/315">kubernetes/klog#315</a></li>
<li>README.md: clarify -logtostderr by <a href="https://github.com/pohly"><code>@​pohly</code></a> in <a href="https://github-redirect.dependabot.com/kubernetes/klog/pull/319">kubernetes/klog#319</a></li>
<li>Trim duplicates by <a href="https://github.com/pohly"><code>@​pohly</code></a> in <a href="https://github-redirect.dependabot.com/kubernetes/klog/pull/324">kubernetes/klog#324</a></li>
<li>replace KObjs with KObjSlice by <a href="https://github.com/pohly"><code>@​pohly</code></a> in <a href="https://github-redirect.dependabot.com/kubernetes/klog/pull/322">kubernetes/klog#322</a></li>
<li>support logr.Marshaler by <a href="https://github.com/pohly"><code>@​pohly</code></a> in <a href="https://github-redirect.dependabot.com/kubernetes/klog/pull/325">kubernetes/klog#325</a></li>
<li>internal: remove unused TrimDuplicates by <a href="https://github.com/pohly"><code>@​pohly</code></a> in <a href="https://github-redirect.dependabot.com/kubernetes/klog/pull/326">kubernetes/klog#326</a></li>
<li>save and restore state by <a href="https://github.com/pohly"><code>@​pohly</code></a> in <a href="https://github-redirect.dependabot.com/kubernetes/klog/pull/320">kubernetes/klog#320</a></li>
<li>GitHub: use apidiff with more recent Go by <a href="https://github.com/pohly"><code>@​pohly</code></a> in <a href="https://github-redirect.dependabot.com/kubernetes/klog/pull/329">kubernetes/klog#329</a></li>
<li>remove hack/tools by <a href="https://github.com/pohly"><code>@​pohly</code></a> in <a href="https://github-redirect.dependabot.com/kubernetes/klog/pull/330">kubernetes/klog#330</a></li>
<li>GIT-331: fix shadowing key from the kv pair by <a href="https://github.com/harshanarayana"><code>@​harshanarayana</code></a> in <a href="https://github-redirect.dependabot.com/kubernetes/klog/pull/332">kubernetes/klog#332</a></li>
<li>klog.Fatal backtrace revert by <a href="https://github.com/pohly"><code>@​pohly</code></a> in <a href="https://github-redirect.dependabot.com/kubernetes/klog/pull/328">kubernetes/klog#328</a></li>
<li>ktesting: capture log data in memory by <a href="https://github.com/pohly"><code>@​pohly</code></a> in <a href="https://github-redirect.dependabot.com/kubernetes/klog/pull/327">kubernetes/klog#327</a></li>
<li>GIT-275: add tests for int and struct keys by <a href="https://github.com/harshanarayana"><code>@​harshanarayana</code></a> in <a href="https://github-redirect.dependabot.com/kubernetes/klog/pull/333">kubernetes/klog#333</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/harshanarayana"><code>@​harshanarayana</code></a> made their first contribution in <a href="https://github-redirect.dependabot.com/kubernetes/klog/pull/332">kubernetes/klog#332</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a href="https://github.com/kubernetes/klog/compare/v2.60.1...v2.70.0">https://github.com/kubernetes/klog/compare/v2.60.1...v2.70.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/kubernetes/klog/commit/7a070b4a826bb06ac0765f064ac116be6de0eedf"><code>7a070b4</code></a> Merge pull request <a href="https://github-redirect.dependabot.com/kubernetes/klog/issues/333">#333</a> from harshanarayana/tests/GIT-275-add-additional-test...</li>
<li><a href="https://github.com/kubernetes/klog/commit/265aa25270db4375ce57ab69a9c9a75e1ac8e554"><code>265aa25</code></a> Merge pull request <a href="https://github-redirect.dependabot.com/kubernetes/klog/issues/327">#327</a> from pohly/buffer-logger</li>
<li><a href="https://github.com/kubernetes/klog/commit/280fc18291b4a0c146c8e94caccd282880da506b"><code>280fc18</code></a> GIT-275: add tests for int and struct keys</li>
<li><a href="https://github.com/kubernetes/klog/commit/ca252b11fb895bfba56b64c3438f9e9def2b9916"><code>ca252b1</code></a> Merge pull request <a href="https://github-redirect.dependabot.com/kubernetes/klog/issues/328">#328</a> from pohly/fatal-backtrace-revert</li>
<li><a href="https://github.com/kubernetes/klog/commit/325472db89c9891fc9ee848a951d5415f0b302be"><code>325472d</code></a> ktesting: capture log data in memory</li>
<li><a href="https://github.com/kubernetes/klog/commit/49e17d5a7eaadb7bcac569ce6ecce8713d545387"><code>49e17d5</code></a> Merge pull request <a href="https://github-redirect.dependabot.com/kubernetes/klog/issues/332">#332</a> from harshanarayana/bug/GIT-331/fix-variable-shadown</li>
<li><a href="https://github.com/kubernetes/klog/commit/e4329d261ac4f3d712784832a201019bc4472efb"><code>e4329d2</code></a> GIT-331: fix shadowing key from the kv pair</li>
<li><a href="https://github.com/kubernetes/klog/commit/9c48b7d0ba16c13eb24c2311f591a69175f5cb86"><code>9c48b7d</code></a> Merge pull request <a href="https://github-redirect.dependabot.com/kubernetes/klog/issues/330">#330</a> from pohly/logtools-removal</li>
<li><a href="https://github.com/kubernetes/klog/commit/c5c3132f9ea3861d13a76db3040b1a2b567e2163"><code>c5c3132</code></a> klog.Fatal: document behavior and alternatives</li>
<li><a href="https://github.com/kubernetes/klog/commit/e9884d225064b2e731decb8be5b157eae5ce188f"><code>e9884d2</code></a> remove hack/tools</li>
<li>Additional commits viewable in <a href="https://github.com/kubernetes/klog/compare/v2.60.1...v2.70.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.60.1&new-version=2.70.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/1002#" 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 **saschagrunert** after the PR has been reviewed.
You can assign the PR to them by writing `/assign @saschagrunert` 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":["saschagrunert"]} -->
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/1002#" title="Author self-approved">dependabot[bot]</a>*, *<a href="https://github.com/kubernetes-sigs/security-profiles-operator/pull/1002#pullrequestreview-1016346683" 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 2511ceffe..5107d91ee 100644
--- a/go.mod
+++ b/go.mod
@@ -29,7 +29,7 @@ require (
 	k8s.io/api v0.24.2
 	k8s.io/apimachinery v0.24.2
 	k8s.io/client-go v0.24.2
-	k8s.io/klog/v2 v2.60.1
+	k8s.io/klog/v2 v2.70.0
 	sigs.k8s.io/controller-runtime v0.12.1
 	sigs.k8s.io/controller-tools v0.9.0
 	sigs.k8s.io/mdtoc v1.1.0
diff --git a/go.sum b/go.sum
index de6acaaa0..ff723d654 100644
--- a/go.sum
+++ b/go.sum
@@ -1787,8 +1787,9 @@ 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.10.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.60.1 h1:VW25q3bZx9uE3vvdL6M8ezOX79vA2Aq1nEWLqNQclHc=
 k8s.io/klog/v2 v2.60.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
+k8s.io/klog/v2 v2.70.0 h1:GMmmjoFOrNepPN0ZeGCzvD2Gh5IKRwdFx8W5PBxVTQU=
+k8s.io/klog/v2 v2.70.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
 k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o=
 k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM=
 k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7/go.mod h1:wXW5VT87nVfh/iLV8FpR2uDvrFyomxbtb1KivDbvPTE=
diff --git a/vendor/k8s.io/klog/v2/README.md b/vendor/k8s.io/klog/v2/README.md
index 7de2212cc..d45cbe172 100644
--- a/vendor/k8s.io/klog/v2/README.md
+++ b/vendor/k8s.io/klog/v2/README.md
@@ -28,7 +28,6 @@ Historical context is available here:
 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/hack/tools` - no stable API yet (may change eventually or get moved to separate repo), `hack/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.)
diff --git a/vendor/k8s.io/klog/v2/contextual.go b/vendor/k8s.io/klog/v2/contextual.go
index 0bf19280e..65ac479ab 100644
--- a/vendor/k8s.io/klog/v2/contextual.go
+++ b/vendor/k8s.io/klog/v2/contextual.go
@@ -34,18 +34,6 @@ import (
 // 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
-
-	// globalLoggerOptions contains the options that were supplied for
-	// globalLogger.
-	globalLoggerOptions loggerOptions
-
 	// klogLogger is used as fallback for logging through the normal klog code
 	// when no Logger is set.
 	klogLogger logr.Logger = logr.New(&klogger{})
@@ -81,10 +69,10 @@ func SetLogger(logger logr.Logger) {
 // routing log entries through klogr into klog and then into the actual Logger
 // backend.
 func SetLoggerWithOptions(logger logr.Logger, opts ...LoggerOption) {
-	globalLogger = &logger
-	globalLoggerOptions = loggerOptions{}
+	logging.logger = &logger
+	logging.loggerOptions = loggerOptions{}
 	for _, opt := range opts {
-		opt(&globalLoggerOptions)
+		opt(&logging.loggerOptions)
 	}
 }
 
@@ -119,8 +107,8 @@ type loggerOptions struct {
 // 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
-	globalLoggerOptions = loggerOptions{}
+	logging.logger = nil
+	logging.loggerOptions = loggerOptions{}
 }
 
 // EnableContextualLogging controls whether contextual logging is enabled.
@@ -132,14 +120,14 @@ func ClearLogger() {
 //
 // This must be called during initialization before goroutines are started.
 func EnableContextualLogging(enabled bool) {
-	contextualLoggingEnabled = enabled
+	logging.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).
 func FromContext(ctx context.Context) Logger {
-	if contextualLoggingEnabled {
+	if logging.contextualLoggingEnabled {
 		if logger, err := logr.FromContext(ctx); err == nil {
 			return logger
 		}
@@ -160,10 +148,10 @@ func TODO() Logger {
 // better receive a logger via its parameters. TODO can be used as a temporary
 // solution for such code.
 func Background() Logger {
-	if globalLoggerOptions.contextualLogger {
-		// Is non-nil because globalLoggerOptions.contextualLogger is
+	if logging.loggerOptions.contextualLogger {
+		// Is non-nil because logging.loggerOptions.contextualLogger is
 		// only true if a logger was set.
-		return *globalLogger
+		return *logging.logger
 	}
 
 	return klogLogger
@@ -172,7 +160,7 @@ func Background() Logger {
 // LoggerWithValues returns logger.WithValues(...kv) when
 // contextual logging is enabled, otherwise the logger.
 func LoggerWithValues(logger Logger, kv ...interface{}) Logger {
-	if contextualLoggingEnabled {
+	if logging.contextualLoggingEnabled {
 		return logger.WithValues(kv...)
 	}
 	return logger
@@ -181,7 +169,7 @@ func LoggerWithValues(logger Logger, kv ...interface{}) Logger {
 // LoggerWithName returns logger.WithName(name) when contextual logging is
 // enabled, otherwise the logger.
 func LoggerWithName(logger Logger, name string) Logger {
-	if contextualLoggingEnabled {
+	if logging.contextualLoggingEnabled {
 		return logger.WithName(name)
 	}
 	return logger
@@ -190,7 +178,7 @@ func LoggerWithName(logger Logger, name string) Logger {
 // NewContext returns logr.NewContext(ctx, logger) when
 // contextual logging is enabled, otherwise ctx.
 func NewContext(ctx context.Context, logger Logger) context.Context {
-	if contextualLoggingEnabled {
+	if logging.contextualLoggingEnabled {
 		return logr.NewContext(ctx, logger)
 	}
 	return ctx
diff --git a/vendor/k8s.io/klog/v2/internal/serialize/keyvalues.go b/vendor/k8s.io/klog/v2/internal/serialize/keyvalues.go
index d89731368..f85d7ccf8 100644
--- a/vendor/k8s.io/klog/v2/internal/serialize/keyvalues.go
+++ b/vendor/k8s.io/klog/v2/internal/serialize/keyvalues.go
@@ -20,6 +20,8 @@ import (
 	"bytes"
 	"fmt"
 	"strconv"
+
+	"github.com/go-logr/logr"
 )
 
 // WithValues implements LogSink.WithValues. The old key/value pairs are
@@ -44,53 +46,49 @@ func WithValues(oldKV, newKV []interface{}) []interface{} {
 	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)
-		}
+// MergeKVs deduplicates elements provided in two key/value slices.
+//
+// Keys in each slice are expected to be unique, so duplicates can only occur
+// when the first and second slice contain the same key. When that happens, the
+// key/value pair from the second slice is used. The first slice must be well-formed
+// (= even key/value pairs). The second one may have a missing value, in which
+// case the special "missing value" is added to the result.
+func MergeKVs(first, second []interface{}) []interface{} {
+	maxLength := len(first) + (len(second)+1)/2*2
+	if maxLength == 0 {
+		// Nothing to do at all.
+		return nil
+	}
 
-		// 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]...)
+	if len(first) == 0 && len(second)%2 == 0 {
+		// Nothing to be overridden, second slice is well-formed
+		// and can be used directly.
+		return second
+	}
+
+	// Determine which keys are in the second slice so that we can skip
+	// them when iterating over the first one. The code intentionally
+	// favors performance over completeness: we assume that keys are string
+	// constants and thus compare equal when the string values are equal. A
+	// string constant being overridden by, for example, a fmt.Stringer is
+	// not handled.
+	overrides := map[interface{}]bool{}
+	for i := 0; i < len(second); i += 2 {
+		overrides[second[i]] = true
+	}
+	merged := make([]interface{}, 0, maxLength)
+	for i := 0; i+1 < len(first); i += 2 {
+		key := first[i]
+		if overrides[key] {
+			continue
 		}
+		merged = append(merged, key, first[i+1])
 	}
-	return outs
+	merged = append(merged, second...)
+	if len(merged)%2 != 0 {
+		merged = append(merged, missingValue)
+	}
+	return merged
 }
 
 const missingValue = "(MISSING)"
@@ -111,10 +109,10 @@ func KVListFormat(b *bytes.Buffer, keysAndValues ...interface{}) {
 		// 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 {
+		if sK, ok := k.(string); ok {
 			// Avoid one allocation when the key is a string, which
 			// normally it should be.
-			b.WriteString(k)
+			b.WriteString(sK)
 		} else {
 			b.WriteString(fmt.Sprintf("%s", k))
 		}
@@ -131,6 +129,24 @@ func KVListFormat(b *bytes.Buffer, keysAndValues ...interface{}) {
 			writeStringValue(b, true, v)
 		case error:
 			writeStringValue(b, true, ErrorToString(v))
+		case logr.Marshaler:
+			value := MarshalerToValue(v)
+			// A marshaler that returns a string is useful for
+			// delayed formatting of complex values. We treat this
+			// case like a normal string. This is useful for
+			// multi-line support.
+			//
+			// We could do this by recursively formatting a value,
+			// but that comes with the risk of infinite recursion
+			// if a marshaler returns itself. Instead we call it
+			// only once and rely on it returning the intended
+			// value directly.
+			switch value := value.(type) {
+			case string:
+				writeStringValue(b, true, value)
+			default:
+				writeStringValue(b, false, fmt.Sprintf("%+v", 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:
@@ -163,6 +179,18 @@ func StringerToString(s fmt.Stringer) (ret string) {
 	return
 }
 
+// MarshalerToValue invokes a marshaler and catches
+// panics.
+func MarshalerToValue(m logr.Marshaler) (ret interface{}) {
+	defer func() {
+		if err := recover(); err != nil {
+			ret = fmt.Sprintf("<panic: %s>", err)
+		}
+	}()
+	ret = m.MarshalLog()
+	return
+}
+
 // ErrorToString converts an error to a string,
 // handling panics if they occur.
 func ErrorToString(err error) (ret string) {
diff --git a/vendor/k8s.io/klog/v2/k8s_references.go b/vendor/k8s.io/klog/v2/k8s_references.go
index db58f8baa..2c218f698 100644
--- a/vendor/k8s.io/klog/v2/k8s_references.go
+++ b/vendor/k8s.io/klog/v2/k8s_references.go
@@ -77,6 +77,8 @@ func KRef(namespace, name string) ObjectRef {
 }
 
 // KObjs returns slice of ObjectRef from an slice of ObjectMeta
+//
+// DEPRECATED: Use KObjSlice instead, it has better performance.
 func KObjs(arg interface{}) []ObjectRef {
 	s := reflect.ValueOf(arg)
 	if s.Kind() != reflect.Slice {
@@ -92,3 +94,65 @@ func KObjs(arg interface{}) []ObjectRef {
 	}
 	return objectRefs
 }
+
+// KObjSlice takes a slice of objects that implement the KMetadata interface
+// and returns an object that gets logged as a slice of ObjectRef values or a
+// string containing those values, depending on whether the logger prefers text
+// output or structured output.
+//
+// An error string is logged when KObjSlice is not passed a suitable slice.
+//
+// Processing of the argument is delayed until the value actually gets logged,
+// in contrast to KObjs where that overhead is incurred regardless of whether
+// the result is needed.
+func KObjSlice(arg interface{}) interface{} {
+	return kobjSlice{arg: arg}
+}
+
+type kobjSlice struct {
+	arg interface{}
+}
+
+var _ fmt.Stringer = kobjSlice{}
+var _ logr.Marshaler = kobjSlice{}
+
+func (ks kobjSlice) String() string {
+	objectRefs, err := ks.process()
+	if err != nil {
+		return err.Error()
+	}
+	return fmt.Sprintf("%v", objectRefs)
+}
+
+func (ks kobjSlice) MarshalLog() interface{} {
+	objectRefs, err := ks.process()
+	if err != nil {
+		return err.Error()
+	}
+	return objectRefs
+}
+
+func (ks kobjSlice) process() ([]interface{}, error) {
+	s := reflect.ValueOf(ks.arg)
+	switch s.Kind() {
+	case reflect.Invalid:
+		// nil parameter, print as nil.
+		return nil, nil
+	case reflect.Slice:
+		// Okay, handle below.
+	default:
+		return nil, fmt.Errorf("<KObjSlice needs a slice, got type %T>", ks.arg)
+	}
+	objectRefs := make([]interface{}, 0, s.Len())
+	for i := 0; i < s.Len(); i++ {
+		item := s.Index(i).Interface()
+		if item == nil {
+			objectRefs = append(objectRefs, nil)
+		} else if v, ok := item.(KMetadata); ok {
+			objectRefs = append(objectRefs, KObj(v))
+		} else {
+			return nil, fmt.Errorf("<KObjSlice needs a slice of values implementing KMetadata, got type %T>", item)
+		}
+	}
+	return objectRefs, nil
+}
diff --git a/vendor/k8s.io/klog/v2/klog.go b/vendor/k8s.io/klog/v2/klog.go
index cb04590fe..8305e2520 100644
--- a/vendor/k8s.io/klog/v2/klog.go
+++ b/vendor/k8s.io/klog/v2/klog.go
@@ -41,6 +41,10 @@
 //
 //	-logtostderr=true
 //		Logs are written to standard error instead of to files.
+//              This shortcuts most of the usual output routing:
+//              -alsologtostderr, -stderrthreshold and -log_dir have no
+//              effect and output redirection at runtime with SetOutput is
+//              ignored.
 //	-alsologtostderr=false
 //		Logs are written to standard error as well as to files.
 //	-stderrthreshold=ERROR
@@ -242,6 +246,10 @@ func (m *moduleSpec) String() string {
 	// Lock because the type is not atomic. TODO: clean this up.
 	logging.mu.Lock()
 	defer logging.mu.Unlock()
+	return m.serialize()
+}
+
+func (m *moduleSpec) serialize() string {
 	var b bytes.Buffer
 	for i, f := range m.filter {
 		if i > 0 {
@@ -263,6 +271,17 @@ var errVmoduleSyntax = errors.New("syntax error: expect comma-separated list of
 // Set will sets module value
 // Syntax: -vmodule=recordio=2,file=1,gfs*=3
 func (m *moduleSpec) Set(value string) error {
+	filter, err := parseModuleSpec(value)
+	if err != nil {
+		return err
+	}
+	logging.mu.Lock()
+	defer logging.mu.Unlock()
+	logging.setVState(logging.verbosity, filter, true)
+	return nil
+}
+
+func parseModuleSpec(value string) ([]modulePat, error) {
 	var filter []modulePat
 	for _, pat := range strings.Split(value, ",") {
 		if len(pat) == 0 {
@@ -271,15 +290,15 @@ func (m *moduleSpec) Set(value string) error {
 		}
 		patLev := strings.Split(pat, "=")
 		if len(patLev) != 2 || len(patLev[0]) == 0 || len(patLev[1]) == 0 {
-			return errVmoduleSyntax
+			return nil, errVmoduleSyntax
 		}
 		pattern := patLev[0]
 		v, err := strconv.ParseInt(patLev[1], 10, 32)
 		if err != nil {
-			return errors.New("syntax error: expect comma-separated list of filename=N")
+			return nil, errors.New("syntax error: expect comma-separated list of filename=N")
 		}
 		if v < 0 {
-			return errors.New("negative value for vmodule level")
+			return nil, errors.New("negative value for vmodule level")
 		}
 		if v == 0 {
 			continue // Ignore. It's harmless but no point in paying the overhead.
@@ -287,10 +306,7 @@ func (m *moduleSpec) Set(value string) error {
 		// TODO: check syntax of filter?
 		filter = append(filter, modulePat{pattern, isLiteral(pattern), Level(v)})
 	}
-	logging.mu.Lock()
-	defer logging.mu.Unlock()
-	logging.setVState(logging.verbosity, filter, true)
-	return nil
+	return filter, nil
 }
 
 // isLiteral reports whether the pattern is a literal string, that is, has no metacharacters
@@ -404,19 +420,19 @@ func InitFlags(flagset *flag.FlagSet) {
 		flagset = flag.CommandLine
 	}
 
-	flagset.StringVar(&logging.logDir, "log_dir", logging.logDir, "If non-empty, write log files in this directory")
-	flagset.StringVar(&logging.logFile, "log_file", logging.logFile, "If non-empty, use this log file")
+	flagset.StringVar(&logging.logDir, "log_dir", logging.logDir, "If non-empty, write log files in this directory (no effect when -logtostderr=true)")
+	flagset.StringVar(&logging.logFile, "log_file", logging.logFile, "If non-empty, use this log file (no effect when -logtostderr=true)")
 	flagset.Uint64Var(&logging.logFileMaxSizeMB, "log_file_max_size", logging.logFileMaxSizeMB,
-		"Defines the maximum size a log file can grow to. Unit is megabytes. "+
+		"Defines the maximum size a log file can grow to (no effect when -logtostderr=true). Unit is megabytes. "+
 			"If the value is 0, the maximum file size is unlimited.")
 	flagset.BoolVar(&logging.toStderr, "logtostderr", logging.toStderr, "log to standard error instead of files")
-	flagset.BoolVar(&logging.alsoToStderr, "alsologtostderr", logging.alsoToStderr, "log to standard error as well as files")
+	flagset.BoolVar(&logging.alsoToStderr, "alsologtostderr", logging.alsoToStderr, "log to standard error as well as files (no effect when -logtostderr=true)")
 	flagset.Var(&logging.verbosity, "v", "number for the log level verbosity")
 	flagset.BoolVar(&logging.addDirHeader, "add_dir_header", logging.addDirHeader, "If true, adds the file directory to the header of the log messages")
 	flagset.BoolVar(&logging.skipHeaders, "skip_headers", logging.skipHeaders, "If true, avoid header prefixes in the log messages")
-	flagset.BoolVar(&logging.oneOutput, "one_output", logging.oneOutput, "If true, only write logs to their native severity level (vs also writing to each lower severity level)")
-	flagset.BoolVar(&logging.skipLogHeaders, "skip_log_headers", logging.skipLogHeaders, "If true, avoid headers when opening log files")
-	flagset.Var(&logging.stderrThreshold, "stderrthreshold", "logs at or above this threshold go to stderr")
+	flagset.BoolVar(&logging.oneOutput, "one_output", logging.oneOutput, "If true, only write logs to their native severity level (vs also writing to each lower severity level; no effect when -logtostderr=true)")
+	flagset.BoolVar(&logging.skipLogHeaders, "skip_log_headers", logging.skipLogHeaders, "If true, avoid headers when opening log files (no effect when -logtostderr=true)")
+	flagset.Var(&logging.stderrThreshold, "stderrthreshold", "logs at or above this threshold go to stderr when writing to files and stderr (no effect when -logtostderr=true or -alsologtostderr=false)")
 	flagset.Var(&logging.vmodule, "vmodule", "comma-separated list of pattern=N settings for file-filtered logging")
 	flagset.Var(&logging.traceLocation, "log_backtrace_at", "when logging hits line file:N, emit a stack trace")
 }
@@ -426,8 +442,20 @@ func Flush() {
 	logging.lockAndFlushAll()
 }
 
-// loggingT collects all the global state of the logging setup.
-type loggingT struct {
+// settings collects global settings.
+type settings struct {
+	// contextualLoggingEnabled controls whether contextual logging is
+	// active. Disabling it may have some small performance benefit.
+	contextualLoggingEnabled bool
+
+	// logger is the global Logger chosen by users of klog, nil if
+	// none is available.
+	logger *Logger
+
+	// loggerOptions contains the options that were supplied for
+	// globalLogger.
+	loggerOptions loggerOptions
+
 	// Boolean flags. Not handled atomically because the flag.Value interface
 	// does not let us avoid the =true, and that shorthand is necessary for
 	// compatibility. TODO: does this matter enough to fix? Seems unlikely.
@@ -437,26 +465,14 @@ type loggingT struct {
 	// Level flag. Handled atomically.
 	stderrThreshold severityValue // The -stderrthreshold flag.
 
-	// 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.
-	bufferCache buffer.Buffers
+	// Access to all of the following fields must be protected via a mutex.
 
-	// 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 [severity.NumSeverity]flushSyncWriter
-	// flushD holds a flushDaemon that frequently flushes log file buffers.
-	flushD *flushDaemon
 	// flushInterval is the interval for periodic flushing. If zero,
 	// the global default will be used.
 	flushInterval time.Duration
-	// pcs is used in V to avoid an allocation when computing the caller's PC.
-	pcs [1]uintptr
-	// vmap is a cache of the V Level for each V() call site, identified by PC.
-	// It is wiped whenever the vmodule flag changes state.
-	vmap map[uintptr]Level
+
 	// filterLength stores the length of the vmodule filter chain. If greater
 	// than zero, it means vmodule is enabled. It may be read safely
 	// using sync.LoadInt32, but is only modified under mu.
@@ -496,6 +512,43 @@ type loggingT struct {
 	filter LogFilter
 }
 
+// deepCopy creates a copy that doesn't share anything with the original
+// instance.
+func (s settings) deepCopy() settings {
+	// vmodule is a slice and would be shared, so we have copy it.
+	filter := make([]modulePat, len(s.vmodule.filter))
+	for i := range s.vmodule.filter {
+		filter[i] = s.vmodule.filter[i]
+	}
+	s.vmodule.filter = filter
+
+	return s
+}
+
+// loggingT collects all the global state of the logging setup.
+type loggingT struct {
+	settings
+
+	// 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.
+	bufferCache buffer.Buffers
+
+	// flushD holds a flushDaemon that frequently flushes log file buffers.
+	// Uses its own mutex.
+	flushD *flushDaemon
+
+	// mu protects the remaining elements of this structure and the fields
+	// in settingsT which need a mutex lock.
+	mu sync.Mutex
+
+	// pcs is used in V to avoid an allocation when computing the caller's PC.
+	pcs [1]uintptr
+	// vmap is a cache of the V Level for each V() call site, identified by PC.
+	// It is wiped whenever the vmodule flag changes state.
+	vmap map[uintptr]Level
+}
+
 var logging loggingT
 
 // setVState sets a consistent state for V logging.
@@ -520,6 +573,55 @@ func (l *loggingT) setVState(verbosity Level, filter []modulePat, setFilter bool
 
 var timeNow = time.Now // Stubbed out for testing.
 
+// CaptureState gathers information about all current klog settings.
+// The result can be used to restore those settings.
+func CaptureState() State {
+	logging.mu.Lock()
+	defer logging.mu.Unlock()
+	return &state{
+		settings:      logging.settings.deepCopy(),
+		flushDRunning: logging.flushD.isRunning(),
+		maxSize:       MaxSize,
+	}
+}
+
+// State stores a snapshot of klog settings. It gets created with CaptureState
+// and can be used to restore the entire state. Modifying individual settings
+// is supported via the command line flags.
+type State interface {
+	// Restore restore the entire state. It may get called more than once.
+	Restore()
+}
+
+type state struct {
+	settings
+
+	flushDRunning bool
+	maxSize       uint64
+}
+
+func (s *state) Restore() {
+	// This needs to be done before mutex locking.
+	if s.flushDRunning && !logging.flushD.isRunning() {
+		// This is not quite accurate: StartFlushDaemon might
+		// have been called with some different interval.
+		interval := s.flushInterval
+		if interval == 0 {
+			interval = flushInterval
+		}
+		logging.flushD.run(interval)
+	} else if !s.flushDRunning && logging.flushD.isRunning() {
+		logging.flushD.stop()
+	}
+
+	logging.mu.Lock()
+	defer logging.mu.Unlock()
+
+	logging.settings = s.settings
+	logging.setVState(s.verbosity, s.vmodule.filter, true)
+	MaxSize = s.maxSize
+}
+
 /*
 header formats a log header as defined by the C++ implementation.
 It returns a buffer containing the formatted header and the user's file and line number.
@@ -688,7 +790,7 @@ func (l *loggingT) printS(err error, s severity.Severity, depth int, msg string,
 		serialize.KVListFormat(&b.Buffer, "err", err)
 	}
 	serialize.KVListFormat(&b.Buffer, keysAndValues...)
-	l.printDepth(s, globalLogger, nil, depth+1, &b.Buffer)
+	l.printDepth(s, logging.logger, nil, depth+1, &b.Buffer)
 	// Make the buffer available for reuse.
 	l.bufferCache.PutBuffer(b)
 }
@@ -765,7 +867,7 @@ func (l *loggingT) output(s severity.Severity, log *logr.Logger, buf *buffer.Buf
 		// TODO: set 'severity' and caller information as structured log info
 		// keysAndValues := []interface{}{"severity", severityName[s], "file", file, "line", line}
 		if s == severity.ErrorLog {
-			globalLogger.WithCallDepth(depth+3).Error(nil, string(data))
+			logging.logger.WithCallDepth(depth+3).Error(nil, string(data))
 		} else {
 			log.WithCallDepth(depth + 3).Info(string(data))
 		}
@@ -822,12 +924,15 @@ func (l *loggingT) output(s severity.Severity, log *logr.Logger, buf *buffer.Buf
 			OsExit(1)
 		}
 		// Dump all goroutine stacks before exiting.
-		trace := stacks(true)
-		// Write the stack trace for all goroutines to the stderr.
-		if l.toStderr || l.alsoToStderr || s >= l.stderrThreshold.get() || alsoToStderr {
-			os.Stderr.Write(trace)
+		// First, make sure we see the trace for the current goroutine on standard error.
+		// If -logtostderr has been specified, the loop below will do that anyway
+		// as the first stack in the full dump.
+		if !l.toStderr {
+			os.Stderr.Write(stacks(false))
 		}
+
 		// Write the stack trace for all goroutines to the files.
+		trace := stacks(true)
 		logExitFunc = func(error) {} // If we get a write error, we'll still exit below.
 		for log := severity.FatalLog; log >= severity.InfoLog; log-- {
 			if f := l.file[log]; f != nil { // Can be nil if -logtostderr is set.
@@ -1077,9 +1182,9 @@ func (f *flushDaemon) isRunning() bool {
 	return f.stopC != nil
 }
 
-// StopFlushDaemon stops the flush daemon, if running.
+// StopFlushDaemon stops the flush daemon, if running, and flushes once.
 // This prevents klog from leaking goroutines on shutdown. After stopping
-// the daemon, you can still manually flush buffers by calling Flush().
+// the daemon, you can still manually flush buffers again by calling Flush().
 func StopFlushDaemon() {
 	logging.flushD.stop()
 }
@@ -1109,8 +1214,8 @@ func (l *loggingT) flushAll() {
 			file.Sync()  // ignore error
 		}
 	}
-	if globalLoggerOptions.flush != nil {
-		globalLoggerOptions.flush()
+	if logging.loggerOptions.flush != nil {
+		logging.loggerOptions.flush()
 	}
 }
 
@@ -1158,7 +1263,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.Severity(lb), globalLogger, logging.filter, file, line, true, text)
+	logging.printWithFileLine(severity.Severity(lb), logging.logger, logging.filter, file, line, true, text)
 	return len(b), nil
 }
 
@@ -1196,10 +1301,10 @@ type Verbose struct {
 }
 
 func newVerbose(level Level, b bool) Verbose {
-	if globalLogger == nil {
+	if logging.logger == nil {
 		return Verbose{b, nil}
 	}
-	v := globalLogger.V(int(level))
+	v := logging.logger.V(int(level))
 	return Verbose{b, &v}
 }
 
@@ -1318,7 +1423,7 @@ func (v Verbose) InfoS(msg string, keysAndValues ...interface{}) {
 // 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(globalLogger, logging.filter, depth, msg, keysAndValues...)
+	logging.infoS(logging.logger, logging.filter, depth, msg, keysAndValues...)
 }
 
 // InfoSDepth is equivalent to the global InfoSDepth function, guarded by the value of v.
@@ -1347,37 +1452,37 @@ func (v Verbose) ErrorS(err error, msg string, keysAndValues ...interface{}) {
 // 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(severity.InfoLog, globalLogger, logging.filter, args...)
+	logging.print(severity.InfoLog, logging.logger, 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(severity.InfoLog, globalLogger, logging.filter, depth, args...)
+	logging.printDepth(severity.InfoLog, logging.logger, 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(severity.InfoLog, globalLogger, logging.filter, args...)
+	logging.println(severity.InfoLog, logging.logger, 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...)
+	logging.printlnDepth(severity.InfoLog, logging.logger, 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(severity.InfoLog, globalLogger, logging.filter, format, args...)
+	logging.printf(severity.InfoLog, logging.logger, 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...)
+	logging.printfDepth(severity.InfoLog, logging.logger, logging.filter, depth, format, args...)
 }
 
 // InfoS structured logs to the INFO log.
@@ -1389,79 +1494,79 @@ func InfofDepth(depth int, 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(globalLogger, logging.filter, 0, msg, keysAndValues...)
+	logging.infoS(logging.logger, 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(severity.WarningLog, globalLogger, logging.filter, args...)
+	logging.print(severity.WarningLog, logging.logger, 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(severity.WarningLog, globalLogger, logging.filter, depth, args...)
+	logging.printDepth(severity.WarningLog, logging.logger, 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(severity.WarningLog, globalLogger, logging.filter, args...)
+	logging.println(severity.WarningLog, logging.logger, 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...)
+	logging.printlnDepth(severity.WarningLog, logging.logger, 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(severity.WarningLog, globalLogger, logging.filter, format, args...)
+	logging.printf(severity.WarningLog, logging.logger, 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...)
+	logging.printfDepth(severity.WarningLog, logging.logger, 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(severity.ErrorLog, globalLogger, logging.filter, args...)
+	logging.print(severity.ErrorLog, logging.logger, 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(severity.ErrorLog, globalLogger, logging.filter, depth, args...)
+	logging.printDepth(severity.ErrorLog, logging.logger, 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(severity.ErrorLog, globalLogger, logging.filter, args...)
+	logging.println(severity.ErrorLog, logging.logger, 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...)
+	logging.printlnDepth(severity.ErrorLog, logging.logger, 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(severity.ErrorLog, globalLogger, logging.filter, format, args...)
+	logging.printf(severity.ErrorLog, logging.logger, 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...)
+	logging.printfDepth(severity.ErrorLog, logging.logger, logging.filter, depth, format, args...)
 }
 
 // ErrorS structured logs to the ERROR, WARNING, and INFO logs.
@@ -1474,52 +1579,63 @@ func ErrorfDepth(depth int, 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, globalLogger, logging.filter, 0, msg, keysAndValues...)
+	logging.errorS(err, logging.logger, 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, globalLogger, logging.filter, depth, msg, keysAndValues...)
+	logging.errorS(err, logging.logger, 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 OsExit(255).
+// prints stack trace(s), then calls OsExit(255).
+//
+// Stderr only receives a dump of the current goroutine's stack trace. Log files,
+// if there are any, receive a dump of the stack traces in all goroutines.
+//
+// Callers who want more control over handling of fatal events may instead use a
+// combination of different functions:
+// - some info or error logging function, optionally with a stack trace
+//   value generated by github.com/go-logr/lib/dbg.Backtrace
+// - Flush to flush pending log data
+// - panic, os.Exit or returning to the caller with an error
+//
 // Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
 func Fatal(args ...interface{}) {
-	logging.print(severity.FatalLog, globalLogger, logging.filter, args...)
+	logging.print(severity.FatalLog, logging.logger, 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(severity.FatalLog, globalLogger, logging.filter, depth, args...)
+	logging.printDepth(severity.FatalLog, logging.logger, logging.filter, depth, args...)
 }
 
 // Fatalln logs to the FATAL, ERROR, WARNING, and INFO logs,
 // 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(severity.FatalLog, globalLogger, logging.filter, args...)
+	logging.println(severity.FatalLog, logging.logger, 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...)
+	logging.printlnDepth(severity.FatalLog, logging.logger, logging.filter, depth, args...)
 }
 
 // Fatalf logs to the FATAL, ERROR, WARNING, and INFO logs,
 // 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(severity.FatalLog, globalLogger, logging.filter, format, args...)
+	logging.printf(severity.FatalLog, logging.logger, 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...)
+	logging.printfDepth(severity.FatalLog, logging.logger, logging.filter, depth, format, args...)
 }
 
 // fatalNoStacks is non-zero if we are to exit without dumping goroutine stacks.
@@ -1530,41 +1646,41 @@ var fatalNoStacks uint32
 // 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(severity.FatalLog, globalLogger, logging.filter, args...)
+	logging.print(severity.FatalLog, logging.logger, 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(severity.FatalLog, globalLogger, logging.filter, depth, args...)
+	logging.printDepth(severity.FatalLog, logging.logger, logging.filter, depth, args...)
 }
 
 // Exitln logs to the FATAL, ERROR, WARNING, and INFO logs, then calls OsExit(1).
 func Exitln(args ...interface{}) {
 	atomic.StoreUint32(&fatalNoStacks, 1)
-	logging.println(severity.FatalLog, globalLogger, logging.filter, args...)
+	logging.println(severity.FatalLog, logging.logger, logging.filter, args...)
 }
 
 // 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...)
+	logging.printlnDepth(severity.FatalLog, logging.logger, 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(severity.FatalLog, globalLogger, logging.filter, format, args...)
+	logging.printf(severity.FatalLog, logging.logger, 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...)
+	logging.printfDepth(severity.FatalLog, logging.logger, logging.filter, depth, format, args...)
 }
 
 // LogFilter is a collection of functions that can filter all logging calls,
diff --git a/vendor/k8s.io/klog/v2/klogr.go b/vendor/k8s.io/klog/v2/klogr.go
index 351d7a740..027a4014a 100644
--- a/vendor/k8s.io/klog/v2/klogr.go
+++ b/vendor/k8s.io/klog/v2/klogr.go
@@ -43,11 +43,11 @@ func (l *klogger) Init(info logr.RuntimeInfo) {
 }
 
 func (l klogger) Info(level int, msg string, kvList ...interface{}) {
-	trimmed := serialize.TrimDuplicates(l.values, kvList)
+	merged := serialize.MergeKVs(l.values, kvList)
 	if l.prefix != "" {
 		msg = l.prefix + ": " + msg
 	}
-	V(Level(level)).InfoSDepth(l.callDepth+1, msg, append(trimmed[0], trimmed[1]...)...)
+	V(Level(level)).InfoSDepth(l.callDepth+1, msg, merged...)
 }
 
 func (l klogger) Enabled(level int) bool {
@@ -55,11 +55,11 @@ func (l klogger) Enabled(level int) bool {
 }
 
 func (l klogger) Error(err error, msg string, kvList ...interface{}) {
-	trimmed := serialize.TrimDuplicates(l.values, kvList)
+	merged := serialize.MergeKVs(l.values, kvList)
 	if l.prefix != "" {
 		msg = l.prefix + ": " + msg
 	}
-	ErrorSDepth(l.callDepth+1, err, msg, append(trimmed[0], trimmed[1]...)...)
+	ErrorSDepth(l.callDepth+1, err, msg, merged...)
 }
 
 // WithName returns a new logr.Logger with the specified name appended.  klogr
diff --git a/vendor/k8s.io/klog/v2/klogr/klogr.go b/vendor/k8s.io/klog/v2/klogr/klogr.go
index 48cb65815..c94e551ed 100644
--- a/vendor/k8s.io/klog/v2/klogr/klogr.go
+++ b/vendor/k8s.io/klog/v2/klogr/klogr.go
@@ -85,7 +85,11 @@ func flatten(kvList ...interface{}) string {
 		if i+1 < len(kvList) {
 			v = kvList[i+1]
 		}
-		keys = append(keys, k)
+		// Only print each key once...
+		if _, seen := vals[k]; !seen {
+			keys = append(keys, k)
+		}
+		// ... with the latest value.
 		vals[k] = v
 	}
 	sort.Strings(keys)
@@ -119,16 +123,15 @@ func (l klogger) Info(level int, msg string, kvList ...interface{}) {
 	switch l.format {
 	case FormatSerialize:
 		msgStr := flatten("msg", msg)
-		trimmed := serialize.TrimDuplicates(l.values, kvList)
-		fixedStr := flatten(trimmed[0]...)
-		userStr := flatten(trimmed[1]...)
-		klog.V(klog.Level(level)).InfoDepth(l.callDepth+1, l.prefix, " ", msgStr, " ", fixedStr, " ", userStr)
+		merged := serialize.MergeKVs(l.values, kvList)
+		kvStr := flatten(merged...)
+		klog.V(klog.Level(level)).InfoDepth(l.callDepth+1, l.prefix, " ", msgStr, " ", kvStr)
 	case FormatKlog:
-		trimmed := serialize.TrimDuplicates(l.values, kvList)
+		merged := serialize.MergeKVs(l.values, kvList)
 		if l.prefix != "" {
 			msg = l.prefix + ": " + msg
 		}
-		klog.V(klog.Level(level)).InfoSDepth(l.callDepth+1, msg, append(trimmed[0], trimmed[1]...)...)
+		klog.V(klog.Level(level)).InfoSDepth(l.callDepth+1, msg, merged...)
 	}
 }
 
@@ -145,16 +148,15 @@ func (l klogger) Error(err error, msg string, kvList ...interface{}) {
 	switch l.format {
 	case FormatSerialize:
 		errStr := flatten("error", loggableErr)
-		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)
+		merged := serialize.MergeKVs(l.values, kvList)
+		kvStr := flatten(merged...)
+		klog.ErrorDepth(l.callDepth+1, l.prefix, " ", msgStr, " ", errStr, " ", kvStr)
 	case FormatKlog:
-		trimmed := serialize.TrimDuplicates(l.values, kvList)
+		merged := serialize.MergeKVs(l.values, kvList)
 		if l.prefix != "" {
 			msg = l.prefix + ": " + msg
 		}
-		klog.ErrorSDepth(l.callDepth+1, err, msg, append(trimmed[0], trimmed[1]...)...)
+		klog.ErrorSDepth(l.callDepth+1, err, msg, merged...)
 	}
 }
 
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 674fed6a3..095e46d9c 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -863,7 +863,7 @@ k8s.io/client-go/util/workqueue
 ## explicit; go 1.16
 k8s.io/component-base/config
 k8s.io/component-base/config/v1alpha1
-# k8s.io/klog/v2 v2.60.1
+# k8s.io/klog/v2 v2.70.0
 ## explicit; go 1.13
 k8s.io/klog/v2
 k8s.io/klog/v2/internal/buffer


Test patch:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 0da940d1b..133eb7880 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -29,11 +29,11 @@ jobs:
     runs-on: ubuntu-20.04
     steps:
       - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b
-      - uses: github/codeql-action/init@d00e8c09a38ef8c1ca1091fc55ef490776d2de73
+      - uses: github/codeql-action/init@41a4ada31ba866a7f1196b9602703a89edd69e22
         with:
           languages: go
-      - uses: github/codeql-action/autobuild@d00e8c09a38ef8c1ca1091fc55ef490776d2de73
-      - uses: github/codeql-action/analyze@d00e8c09a38ef8c1ca1091fc55ef490776d2de73
+      - uses: github/codeql-action/autobuild@41a4ada31ba866a7f1196b9602703a89edd69e22
+      - uses: github/codeql-action/analyze@41a4ada31ba866a7f1196b9602703a89edd69e22
 
   coverage:
     runs-on: ubuntu-20.04