summaryrefslogtreecommitdiff
path: root/vendor/sigs.k8s.io/controller-runtime
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/sigs.k8s.io/controller-runtime')
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/.golangci.yml224
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/Makefile14
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/OWNERS_ALIASES10
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/README.md6
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/RELEASE.md2
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/SECURITY_CONTACTS5
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/alias.go4
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/doc.go4
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/builder/controller.go88
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/builder/options.go16
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/builder/webhook.go63
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/cache/cache.go475
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/cache/informer_cache.go84
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/cache_reader.go4
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/deleg_map.go126
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/disabledeepcopy.go35
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/informers.go560
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/informers_map.go480
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/selector.go15
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/transformers.go8
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/cache/multi_namespace_cache.go96
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/certwatcher/certwatcher.go52
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/apimachinery.go66
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/dynamicrestmapper.go301
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/restmapper.go (renamed from vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/lazyrestmapper.go)165
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/client/client.go187
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/client/client_rest_resources.go (renamed from vendor/sigs.k8s.io/controller-runtime/pkg/client/client_cache.go)27
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/client/config/config.go6
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/client/doc.go3
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/client/dryrun.go11
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/client/fake/client.go430
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/client/interceptor/intercept.go166
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/client/interfaces.go5
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/client/namespaced_client.go33
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/client/options.go30
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/client/split.go143
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/client/typed_client.go26
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/client/unstructured_client.go79
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/client/watch.go30
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/cluster/cluster.go196
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/cluster/internal.go41
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/config/config.go12
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/config/controller.go49
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/config/doc.go10
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/config/v1alpha1/doc.go2
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/config/v1alpha1/register.go6
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/config/v1alpha1/types.go19
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/controller/controller.go43
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/envtest/crd.go4
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/envtest/server.go24
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/envtest/webhook.go16
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/handler/enqueue.go10
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/handler/enqueue_mapped.go37
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/handler/enqueue_owner.go86
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/handler/eventhandler.go34
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/controller.go43
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/metrics/metrics.go8
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/internal/objectutil/objectutil.go36
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/internal/recorder/recorder.go9
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/internal/source/event_handler.go (renamed from vendor/sigs.k8s.io/controller-runtime/pkg/source/internal/eventsource.go)58
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/internal/source/kind.go117
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/log/deleg.go40
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/log/log.go46
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/manager/internal.go120
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/manager/manager.go185
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/manager/runnable_group.go2
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/manager/server.go61
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/metrics/client_go_adapter.go89
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/predicate/predicate.go23
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/reconcile/reconcile.go28
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/runtime/inject/doc.go22
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/runtime/inject/inject.go164
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/source/source.go183
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/decode.go17
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/defaulter.go15
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/defaulter_custom.go14
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/doc.go6
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/http.go20
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/inject.go31
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/multi.go52
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/response.go23
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/validator.go105
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/validator_custom.go53
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/webhook.go119
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/webhook/conversion/conversion.go34
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/webhook/conversion/decoder.go7
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/webhook/server.go221
87 files changed, 3317 insertions, 3302 deletions
diff --git a/vendor/sigs.k8s.io/controller-runtime/.golangci.yml b/vendor/sigs.k8s.io/controller-runtime/.golangci.yml
index 209b7f4e6..817c2c723 100644
--- a/vendor/sigs.k8s.io/controller-runtime/.golangci.yml
+++ b/vendor/sigs.k8s.io/controller-runtime/.golangci.yml
@@ -1,38 +1,44 @@
linters:
disable-all: true
enable:
- - asciicheck
- - bodyclose
- - depguard
- - dogsled
- - errcheck
- - errorlint
- - exportloopref
- - goconst
- - gocritic
- - gocyclo
- - gofmt
- - goimports
- - goprintffuncname
- - gosec
- - gosimple
- - govet
- - importas
- - ineffassign
- - misspell
- - nakedret
- - nilerr
- - nolintlint
- - prealloc
- - revive
- - rowserrcheck
- - staticcheck
- - stylecheck
- - typecheck
- - unconvert
- - unparam
- - unused
- - whitespace
+ - asasalint
+ - asciicheck
+ - bidichk
+ - bodyclose
+ - depguard
+ - dogsled
+ - dupl
+ - errcheck
+ - errchkjson
+ - errorlint
+ - exhaustive
+ - exportloopref
+ - goconst
+ - gocritic
+ - gocyclo
+ - gofmt
+ - goimports
+ - goprintffuncname
+ - gosec
+ - gosimple
+ - govet
+ - importas
+ - ineffassign
+ - makezero
+ - misspell
+ - nakedret
+ - nilerr
+ - nolintlint
+ - prealloc
+ - revive
+ - staticcheck
+ - stylecheck
+ - tagliatelle
+ - typecheck
+ - unconvert
+ - unparam
+ - unused
+ - whitespace
linters-settings:
importas:
@@ -53,13 +59,42 @@ linters-settings:
- pkg: sigs.k8s.io/controller-runtime
alias: ctrl
staticcheck:
- go: "1.19"
+ go: "1.20"
stylecheck:
- go: "1.19"
+ go: "1.20"
depguard:
include-go-root: true
packages:
- io/ioutil # https://go.dev/doc/go1.16#ioutil
+ revive:
+ rules:
+ # The following rules are recommended https://github.com/mgechev/revive#recommended-configuration
+ - name: blank-imports
+ - name: context-as-argument
+ - name: context-keys-type
+ - name: dot-imports
+ - name: error-return
+ - name: error-strings
+ - name: error-naming
+ - name: exported
+ - name: if-return
+ - name: increment-decrement
+ - name: var-naming
+ - name: var-declaration
+ - name: range
+ - name: receiver-naming
+ - name: time-naming
+ - name: unexported-return
+ - name: indent-error-flow
+ - name: errorf
+ - name: superfluous-else
+ - name: unreachable-code
+ - name: redefines-builtin-id
+ #
+ # Rules in addition to the recommended configuration above.
+ #
+ - name: bool-literal-in-expr
+ - name: constant-logical-expr
issues:
max-same-issues: 0
@@ -69,68 +104,71 @@ issues:
exclude-use-default: false
# List of regexps of issue texts to exclude, empty list by default.
exclude:
- # The following are being worked on to remove their exclusion. This list should be reduced or go away all together over time.
- # If it is decided they will not be addressed they should be moved above this comment.
- - Subprocess launch(ed with variable|ing should be audited)
- - (G204|G104|G307)
- - "ST1000: at least one file in a package should have a package comment"
+ # The following are being worked on to remove their exclusion. This list should be reduced or go away all together over time.
+ # If it is decided they will not be addressed they should be moved above this comment.
+ - Subprocess launch(ed with variable|ing should be audited)
+ - (G204|G104|G307)
+ - "ST1000: at least one file in a package should have a package comment"
exclude-rules:
- - linters:
- - gosec
- text: "G108: Profiling endpoint is automatically exposed on /debug/pprof"
- - linters:
- - revive
- text: "exported: exported method .*\\.(Reconcile|SetupWithManager|SetupWebhookWithManager) should have comment or be unexported"
- - linters:
- - errcheck
- text: Error return value of .((os\.)?std(out|err)\..*|.*Close|.*Flush|os\.Remove(All)?|.*print(f|ln)?|os\.(Un)?Setenv). is not checked
- # With Go 1.16, the new embed directive can be used with an un-named import,
- # revive (previously, golint) only allows these to be imported in a main.go, which wouldn't work for us.
- # This directive allows the embed package to be imported with an underscore everywhere.
- - linters:
- - revive
- source: _ "embed"
- # Exclude some packages or code to require comments, for example test code, or fake clients.
- - linters:
- - revive
- text: exported (method|function|type|const) (.+) should have comment or be unexported
- source: (func|type).*Fake.*
- - linters:
- - revive
- text: exported (method|function|type|const) (.+) should have comment or be unexported
- path: fake_\.go
- # Disable unparam "always receives" which might not be really
- # useful when building libraries.
- - linters:
- - unparam
- text: always receives
- # Dot imports for gomega or ginkgo are allowed
- # within test files.
- - path: _test\.go
- text: should not use dot imports
- - path: _test\.go
- text: cyclomatic complexity
- - path: _test\.go
- text: "G107: Potential HTTP request made with variable url"
- # Append should be able to assign to a different var/slice.
- - linters:
- - gocritic
- text: "appendAssign: append result not assigned to the same slice"
- - linters:
- - gocritic
- text: "singleCaseSwitch: should rewrite switch statement to if statement"
- # It considers all file access to a filename that comes from a variable problematic,
- # which is naiv at best.
- - linters:
- - gosec
- text: "G304: Potential file inclusion via variable"
- - linters:
- - revive
- text: "package-comments: should have a package comment"
+ - linters:
+ - gosec
+ text: "G108: Profiling endpoint is automatically exposed on /debug/pprof"
+ - linters:
+ - revive
+ text: "exported: exported method .*\\.(Reconcile|SetupWithManager|SetupWebhookWithManager) should have comment or be unexported"
+ - linters:
+ - errcheck
+ text: Error return value of .((os\.)?std(out|err)\..*|.*Close|.*Flush|os\.Remove(All)?|.*print(f|ln)?|os\.(Un)?Setenv). is not checked
+ - linters:
+ - staticcheck
+ text: "SA1019: .*The component config package has been deprecated and will be removed in a future release."
+ # With Go 1.16, the new embed directive can be used with an un-named import,
+ # revive (previously, golint) only allows these to be imported in a main.go, which wouldn't work for us.
+ # This directive allows the embed package to be imported with an underscore everywhere.
+ - linters:
+ - revive
+ source: _ "embed"
+ # Exclude some packages or code to require comments, for example test code, or fake clients.
+ - linters:
+ - revive
+ text: exported (method|function|type|const) (.+) should have comment or be unexported
+ source: (func|type).*Fake.*
+ - linters:
+ - revive
+ text: exported (method|function|type|const) (.+) should have comment or be unexported
+ path: fake_\.go
+ # Disable unparam "always receives" which might not be really
+ # useful when building libraries.
+ - linters:
+ - unparam
+ text: always receives
+ # Dot imports for gomega and ginkgo are allowed
+ # within test files.
+ - path: _test\.go
+ text: should not use dot imports
+ - path: _test\.go
+ text: cyclomatic complexity
+ - path: _test\.go
+ text: "G107: Potential HTTP request made with variable url"
+ # Append should be able to assign to a different var/slice.
+ - linters:
+ - gocritic
+ text: "appendAssign: append result not assigned to the same slice"
+ - linters:
+ - gocritic
+ text: "singleCaseSwitch: should rewrite switch statement to if statement"
+ # It considers all file access to a filename that comes from a variable problematic,
+ # which is naiv at best.
+ - linters:
+ - gosec
+ text: "G304: Potential file inclusion via variable"
+ - linters:
+ - dupl
+ path: _test\.go
run:
timeout: 10m
skip-files:
- - "zz_generated.*\\.go$"
- - ".*conversion.*\\.go$"
+ - "zz_generated.*\\.go$"
+ - ".*conversion.*\\.go$"
allow-parallel-runners: true
diff --git a/vendor/sigs.k8s.io/controller-runtime/Makefile b/vendor/sigs.k8s.io/controller-runtime/Makefile
index 36647c697..71ec644de 100644
--- a/vendor/sigs.k8s.io/controller-runtime/Makefile
+++ b/vendor/sigs.k8s.io/controller-runtime/Makefile
@@ -75,7 +75,7 @@ $(CONTROLLER_GEN): $(TOOLS_DIR)/go.mod # Build controller-gen from tools folder.
$(GOLANGCI_LINT): .github/workflows/golangci-lint.yml # Download golanci-lint using hack script into tools folder.
hack/ensure-golangci-lint.sh \
-b $(TOOLS_BIN_DIR) \
- $(shell cat .github/workflows/golangci-lint.yml | grep version | sed 's/.*version: //')
+ $(shell cat .github/workflows/golangci-lint.yml | grep "version: v" | sed 's/.*version: //')
## --------------------------------------
## Linting
@@ -117,7 +117,15 @@ clean-bin: ## Remove all generated binaries.
rm -rf hack/tools/bin
.PHONY: verify-modules
-verify-modules: modules
- @if !(git diff --quiet HEAD -- go.sum go.mod); then \
+verify-modules: modules ## Verify go modules are up to date
+ @if !(git diff --quiet HEAD -- go.sum go.mod $(TOOLS_DIR)/go.mod $(TOOLS_DIR)/go.sum $(ENVTEST_DIR)/go.mod $(ENVTEST_DIR)/go.sum); then \
+ git diff; \
echo "go module files are out of date, please run 'make modules'"; exit 1; \
fi
+
+.PHONY: verify-generate
+verify-generate: generate ## Verify generated files are up to date
+ @if !(git diff --quiet HEAD); then \
+ git diff; \
+ echo "generated files are out of date, run make generate"; exit 1; \
+ fi
diff --git a/vendor/sigs.k8s.io/controller-runtime/OWNERS_ALIASES b/vendor/sigs.k8s.io/controller-runtime/OWNERS_ALIASES
index 710894784..7848941d5 100644
--- a/vendor/sigs.k8s.io/controller-runtime/OWNERS_ALIASES
+++ b/vendor/sigs.k8s.io/controller-runtime/OWNERS_ALIASES
@@ -9,23 +9,21 @@ aliases:
# non-admin folks who have write-access and can approve any PRs in the repo
controller-runtime-maintainers:
- - vincepri
+ - alvaroaleman
- joelanford
+ - sbueringer
+ - vincepri
# non-admin folks who can approve any PRs in the repo
controller-runtime-approvers:
- - alvaroaleman
- fillzpp
- - sbueringer
# folks who can review and LGTM any PRs in the repo (doesn't
# include approvers & admins -- those count too via the OWNERS
# file)
controller-runtime-reviewers:
- - vincepri
- varshaprasad96
- - fillzpp
- - sbueringer
+ - inteon
# folks to can approve things in the directly-ported
# testing_frameworks portions of the codebase
diff --git a/vendor/sigs.k8s.io/controller-runtime/README.md b/vendor/sigs.k8s.io/controller-runtime/README.md
index 484881dce..e785abdd7 100644
--- a/vendor/sigs.k8s.io/controller-runtime/README.md
+++ b/vendor/sigs.k8s.io/controller-runtime/README.md
@@ -16,8 +16,8 @@ Documentation:
- [Basic controller using builder](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/builder#example-Builder)
- [Creating a manager](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/manager#example-New)
- [Creating a controller](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/controller#example-New)
-- [Examples](https://github.com/kubernetes-sigs/controller-runtime/blob/master/examples)
-- [Designs](https://github.com/kubernetes-sigs/controller-runtime/blob/master/designs)
+- [Examples](https://github.com/kubernetes-sigs/controller-runtime/blob/main/examples)
+- [Designs](https://github.com/kubernetes-sigs/controller-runtime/blob/main/designs)
# Versioning, Maintenance, and Compatibility
@@ -27,7 +27,7 @@ Users:
- We follow [Semantic Versioning (semver)](https://semver.org)
- Use releases with your dependency management to ensure that you get compatible code
-- The master branch contains all the latest code, some of which may break compatibility (so "normal" `go get` is not recommended)
+- The main branch contains all the latest code, some of which may break compatibility (so "normal" `go get` is not recommended)
Contributors:
diff --git a/vendor/sigs.k8s.io/controller-runtime/RELEASE.md b/vendor/sigs.k8s.io/controller-runtime/RELEASE.md
index 134a73a31..f234494fe 100644
--- a/vendor/sigs.k8s.io/controller-runtime/RELEASE.md
+++ b/vendor/sigs.k8s.io/controller-runtime/RELEASE.md
@@ -10,7 +10,7 @@ to create a new branch you will just need to ensure that all big fixes are cherr
### Create the new branch and the release tag
-1. Create a new branch `git checkout -b release-<MAJOR.MINOR>` from master
+1. Create a new branch `git checkout -b release-<MAJOR.MINOR>` from main
2. Push the new branch to the remote repository
### Now, let's generate the changelog
diff --git a/vendor/sigs.k8s.io/controller-runtime/SECURITY_CONTACTS b/vendor/sigs.k8s.io/controller-runtime/SECURITY_CONTACTS
index 32e6a3b90..9c5241c6b 100644
--- a/vendor/sigs.k8s.io/controller-runtime/SECURITY_CONTACTS
+++ b/vendor/sigs.k8s.io/controller-runtime/SECURITY_CONTACTS
@@ -10,5 +10,6 @@
# DO NOT REPORT SECURITY VULNERABILITIES DIRECTLY TO THESE NAMES, FOLLOW THE
# INSTRUCTIONS AT https://kubernetes.io/security/
-pwittrock
-droot
+alvaroaleman
+sbueringer
+vincepri
diff --git a/vendor/sigs.k8s.io/controller-runtime/alias.go b/vendor/sigs.k8s.io/controller-runtime/alias.go
index 35cba30be..237963889 100644
--- a/vendor/sigs.k8s.io/controller-runtime/alias.go
+++ b/vendor/sigs.k8s.io/controller-runtime/alias.go
@@ -99,6 +99,8 @@ var (
// ConfigFile returns the cfg.File function for deferred config file loading,
// this is passed into Options{}.From() to populate the Options fields for
// the manager.
+ //
+ // Deprecated: This is deprecated in favor of using Options directly.
ConfigFile = cfg.File
// NewControllerManagedBy returns a new controller builder that will be started by the provided Manager.
@@ -139,7 +141,7 @@ var (
// The logger, when used with controllers, can be expected to contain basic information about the object
// that's being reconciled like:
// - `reconciler group` and `reconciler kind` coming from the For(...) object passed in when building a controller.
- // - `name` and `namespace` injected from the reconciliation request.
+ // - `name` and `namespace` from the reconciliation request.
//
// This is meant to be used with the context supplied in a struct that satisfies the Reconciler interface.
LoggerFrom = log.FromContext
diff --git a/vendor/sigs.k8s.io/controller-runtime/doc.go b/vendor/sigs.k8s.io/controller-runtime/doc.go
index fa6c532c4..0319bc3ff 100644
--- a/vendor/sigs.k8s.io/controller-runtime/doc.go
+++ b/vendor/sigs.k8s.io/controller-runtime/doc.go
@@ -46,13 +46,13 @@ limitations under the License.
//
// Frequently asked questions about using controller-runtime and designing
// controllers can be found at
-// https://github.com/kubernetes-sigs/controller-runtime/blob/master/FAQ.md.
+// https://github.com/kubernetes-sigs/controller-runtime/blob/main/FAQ.md.
//
// # Managers
//
// Every controller and webhook is ultimately run by a Manager (pkg/manager). A
// manager is responsible for running controllers and webhooks, and setting up
-// common dependencies (pkg/runtime/inject), like shared caches and clients, as
+// common dependencies, like shared caches and clients, as
// well as managing leader election (pkg/leaderelection). Managers are
// generally configured to gracefully shut down controllers on pod termination
// by wiring up a signal handler (pkg/manager/signals).
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/builder/controller.go b/vendor/sigs.k8s.io/controller-runtime/pkg/builder/controller.go
index 03f9633a7..570cfd63d 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/builder/controller.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/builder/controller.go
@@ -30,6 +30,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/handler"
+ internalsource "sigs.k8s.io/controller-runtime/pkg/internal/source"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/predicate"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
@@ -96,14 +97,20 @@ func (blder *Builder) For(object client.Object, opts ...ForOption) *Builder {
// OwnsInput represents the information set by Owns method.
type OwnsInput struct {
+ matchEveryOwner bool
object client.Object
predicates []predicate.Predicate
objectProjection objectProjection
}
// Owns defines types of Objects being *generated* by the ControllerManagedBy, and configures the ControllerManagedBy to respond to
-// create / delete / update events by *reconciling the owner object*. This is the equivalent of calling
-// Watches(&source.Kind{Type: <ForType-forInput>}, &handler.EnqueueRequestForOwner{OwnerType: apiType, IsController: true}).
+// create / delete / update events by *reconciling the owner object*.
+//
+// The default behavior reconciles only the first controller-type OwnerReference of the given type.
+// Use Owns(object, builder.MatchEveryOwner) to reconcile all owners.
+//
+// By default, this is the equivalent of calling
+// Watches(object, handler.EnqueueRequestForOwner([...], ownerType, OnlyControllerOwner())).
func (blder *Builder) Owns(object client.Object, opts ...OwnsOption) *Builder {
input := OwnsInput{object: object}
for _, opt := range opts {
@@ -122,10 +129,54 @@ type WatchesInput struct {
objectProjection objectProjection
}
-// Watches exposes the lower-level ControllerManagedBy Watches functions through the builder. Consider using
-// Owns or For instead of Watches directly.
+// Watches defines the type of Object to watch, and configures the ControllerManagedBy to respond to create / delete /
+// update events by *reconciling the object* with the given EventHandler.
+//
+// This is the equivalent of calling
+// WatchesRawSource(source.Kind(scheme, object), eventhandler, opts...).
+func (blder *Builder) Watches(object client.Object, eventhandler handler.EventHandler, opts ...WatchesOption) *Builder {
+ src := source.Kind(blder.mgr.GetCache(), object)
+ return blder.WatchesRawSource(src, eventhandler, opts...)
+}
+
+// WatchesMetadata is the same as Watches, but forces the internal cache to only watch PartialObjectMetadata.
+//
+// This is useful when watching lots of objects, really big objects, or objects for which you only know
+// the GVK, but not the structure. You'll need to pass metav1.PartialObjectMetadata to the client
+// when fetching objects in your reconciler, otherwise you'll end up with a duplicate structured or unstructured cache.
+//
+// When watching a resource with metadata only, for example the v1.Pod, you should not Get and List using the v1.Pod type.
+// Instead, you should use the special metav1.PartialObjectMetadata type.
+//
+// ❌ Incorrect:
+//
+// pod := &v1.Pod{}
+// mgr.GetClient().Get(ctx, nsAndName, pod)
+//
+// ✅ Correct:
+//
+// pod := &metav1.PartialObjectMetadata{}
+// pod.SetGroupVersionKind(schema.GroupVersionKind{
+// Group: "",
+// Version: "v1",
+// Kind: "Pod",
+// })
+// mgr.GetClient().Get(ctx, nsAndName, pod)
+//
+// In the first case, controller-runtime will create another cache for the
+// concrete type on top of the metadata cache; this increases memory
+// consumption and leads to race conditions as caches are not in sync.
+func (blder *Builder) WatchesMetadata(object client.Object, eventhandler handler.EventHandler, opts ...WatchesOption) *Builder {
+ opts = append(opts, OnlyMetadata)
+ return blder.Watches(object, eventhandler, opts...)
+}
+
+// WatchesRawSource exposes the lower-level ControllerManagedBy Watches functions through the builder.
// Specified predicates are registered only for given source.
-func (blder *Builder) Watches(src source.Source, eventhandler handler.EventHandler, opts ...WatchesOption) *Builder {
+//
+// STOP! Consider using For(...), Owns(...), Watches(...), WatchesMetadata(...) instead.
+// This method is only exposed for more advanced use cases, most users should use higher level functions.
+func (blder *Builder) WatchesRawSource(src source.Source, eventhandler handler.EventHandler, opts ...WatchesOption) *Builder {
input := WatchesInput{src: src, eventhandler: eventhandler}
for _, opt := range opts {
opt.ApplyToWatches(&input)
@@ -217,11 +268,11 @@ func (blder *Builder) project(obj client.Object, proj objectProjection) (client.
func (blder *Builder) doWatch() error {
// Reconcile type
if blder.forInput.object != nil {
- typeForSrc, err := blder.project(blder.forInput.object, blder.forInput.objectProjection)
+ obj, err := blder.project(blder.forInput.object, blder.forInput.objectProjection)
if err != nil {
return err
}
- src := &source.Kind{Type: typeForSrc}
+ src := source.Kind(blder.mgr.GetCache(), obj)
hdler := &handler.EnqueueRequestForObject{}
allPredicates := append(blder.globalPredicates, blder.forInput.predicates...)
if err := blder.ctrl.Watch(src, hdler, allPredicates...); err != nil {
@@ -234,15 +285,20 @@ func (blder *Builder) doWatch() error {
return errors.New("Owns() can only be used together with For()")
}
for _, own := range blder.ownsInput {
- typeForSrc, err := blder.project(own.object, own.objectProjection)
+ obj, err := blder.project(own.object, own.objectProjection)
if err != nil {
return err
}
- src := &source.Kind{Type: typeForSrc}
- hdler := &handler.EnqueueRequestForOwner{
- OwnerType: blder.forInput.object,
- IsController: true,
+ src := source.Kind(blder.mgr.GetCache(), obj)
+ opts := []handler.OwnerOption{}
+ if !own.matchEveryOwner {
+ opts = append(opts, handler.OnlyControllerOwner())
}
+ hdler := handler.EnqueueRequestForOwner(
+ blder.mgr.GetScheme(), blder.mgr.GetRESTMapper(),
+ blder.forInput.object,
+ opts...,
+ )
allPredicates := append([]predicate.Predicate(nil), blder.globalPredicates...)
allPredicates = append(allPredicates, own.predicates...)
if err := blder.ctrl.Watch(src, hdler, allPredicates...); err != nil {
@@ -258,8 +314,8 @@ func (blder *Builder) doWatch() error {
allPredicates := append([]predicate.Predicate(nil), blder.globalPredicates...)
allPredicates = append(allPredicates, w.predicates...)
- // If the source of this watch is of type *source.Kind, project it.
- if srckind, ok := w.src.(*source.Kind); ok {
+ // If the source of this watch is of type Kind, project it.
+ if srckind, ok := w.src.(*internalsource.Kind); ok {
typeForSrc, err := blder.project(srckind.Type, w.objectProjection)
if err != nil {
return err
@@ -314,8 +370,8 @@ func (blder *Builder) doController(r reconcile.Reconciler) error {
}
// Setup cache sync timeout.
- if ctrlOptions.CacheSyncTimeout == 0 && globalOpts.CacheSyncTimeout != nil {
- ctrlOptions.CacheSyncTimeout = *globalOpts.CacheSyncTimeout
+ if ctrlOptions.CacheSyncTimeout == 0 && globalOpts.CacheSyncTimeout > 0 {
+ ctrlOptions.CacheSyncTimeout = globalOpts.CacheSyncTimeout
}
controllerName, err := blder.getControllerName(gvk, hasGVK)
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/builder/options.go b/vendor/sigs.k8s.io/controller-runtime/pkg/builder/options.go
index 3a66491bf..bce2065ef 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/builder/options.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/builder/options.go
@@ -138,3 +138,19 @@ var (
)
// }}}
+
+// MatchEveryOwner determines whether the watch should be filtered based on
+// controller ownership. As in, when the OwnerReference.Controller field is set.
+//
+// If passed as an option,
+// the handler receives notification for every owner of the object with the given type.
+// If unset (default), the handler receives notification only for the first
+// OwnerReference with `Controller: true`.
+var MatchEveryOwner = &matchEveryOwner{}
+
+type matchEveryOwner struct{}
+
+// ApplyToOwns applies this configuration to the given OwnsInput options.
+func (o matchEveryOwner) ApplyToOwns(opts *OwnsInput) {
+ opts.matchEveryOwner = true
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/builder/webhook.go b/vendor/sigs.k8s.io/controller-runtime/pkg/builder/webhook.go
index 534e6d64c..82fac4d8f 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/builder/webhook.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/builder/webhook.go
@@ -22,9 +22,12 @@ import (
"net/url"
"strings"
+ "github.com/go-logr/logr"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/rest"
+ "k8s.io/klog/v2"
+
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
@@ -33,13 +36,14 @@ import (
// WebhookBuilder builds a Webhook.
type WebhookBuilder struct {
- apiType runtime.Object
- withDefaulter admission.CustomDefaulter
- withValidator admission.CustomValidator
- gvk schema.GroupVersionKind
- mgr manager.Manager
- config *rest.Config
- recoverPanic bool
+ apiType runtime.Object
+ withDefaulter admission.CustomDefaulter
+ withValidator admission.CustomValidator
+ gvk schema.GroupVersionKind
+ mgr manager.Manager
+ config *rest.Config
+ recoverPanic bool
+ logConstructor func(base logr.Logger, req *admission.Request) logr.Logger
}
// WebhookManagedBy allows inform its manager.Manager.
@@ -69,6 +73,12 @@ func (blder *WebhookBuilder) WithValidator(validator admission.CustomValidator)
return blder
}
+// WithLogConstructor overrides the webhook's LogConstructor.
+func (blder *WebhookBuilder) WithLogConstructor(logConstructor func(base logr.Logger, req *admission.Request) logr.Logger) *WebhookBuilder {
+ blder.logConstructor = logConstructor
+ return blder
+}
+
// RecoverPanic indicates whether the panic caused by webhook should be recovered.
func (blder *WebhookBuilder) RecoverPanic() *WebhookBuilder {
blder.recoverPanic = true
@@ -80,6 +90,9 @@ func (blder *WebhookBuilder) Complete() error {
// Set the Config
blder.loadRestConfig()
+ // Configure the default LogConstructor
+ blder.setLogConstructor()
+
// Set the Webhook if needed
return blder.registerWebhooks()
}
@@ -90,6 +103,26 @@ func (blder *WebhookBuilder) loadRestConfig() {
}
}
+func (blder *WebhookBuilder) setLogConstructor() {
+ if blder.logConstructor == nil {
+ blder.logConstructor = func(base logr.Logger, req *admission.Request) logr.Logger {
+ log := base.WithValues(
+ "webhookGroup", blder.gvk.Group,
+ "webhookKind", blder.gvk.Kind,
+ )
+ if req != nil {
+ return log.WithValues(
+ blder.gvk.Kind, klog.KRef(req.Namespace, req.Name),
+ "namespace", req.Namespace, "name", req.Name,
+ "resource", req.Resource, "user", req.UserInfo.Username,
+ "requestID", req.UID,
+ )
+ }
+ return log
+ }
+ }
+}
+
func (blder *WebhookBuilder) registerWebhooks() error {
typ, err := blder.getType()
if err != nil {
@@ -116,6 +149,7 @@ func (blder *WebhookBuilder) registerWebhooks() error {
func (blder *WebhookBuilder) registerDefaultingWebhook() {
mwh := blder.getDefaultingWebhook()
if mwh != nil {
+ mwh.LogConstructor = blder.logConstructor
path := generateMutatePath(blder.gvk)
// Checking if the path is already registered.
@@ -131,10 +165,10 @@ func (blder *WebhookBuilder) registerDefaultingWebhook() {
func (blder *WebhookBuilder) getDefaultingWebhook() *admission.Webhook {
if defaulter := blder.withDefaulter; defaulter != nil {
- return admission.WithCustomDefaulter(blder.apiType, defaulter).WithRecoverPanic(blder.recoverPanic)
+ return admission.WithCustomDefaulter(blder.mgr.GetScheme(), blder.apiType, defaulter).WithRecoverPanic(blder.recoverPanic)
}
if defaulter, ok := blder.apiType.(admission.Defaulter); ok {
- return admission.DefaultingWebhookFor(defaulter).WithRecoverPanic(blder.recoverPanic)
+ return admission.DefaultingWebhookFor(blder.mgr.GetScheme(), defaulter).WithRecoverPanic(blder.recoverPanic)
}
log.Info(
"skip registering a mutating webhook, object does not implement admission.Defaulter or WithDefaulter wasn't called",
@@ -145,6 +179,7 @@ func (blder *WebhookBuilder) getDefaultingWebhook() *admission.Webhook {
func (blder *WebhookBuilder) registerValidatingWebhook() {
vwh := blder.getValidatingWebhook()
if vwh != nil {
+ vwh.LogConstructor = blder.logConstructor
path := generateValidatePath(blder.gvk)
// Checking if the path is already registered.
@@ -160,10 +195,10 @@ func (blder *WebhookBuilder) registerValidatingWebhook() {
func (blder *WebhookBuilder) getValidatingWebhook() *admission.Webhook {
if validator := blder.withValidator; validator != nil {
- return admission.WithCustomValidator(blder.apiType, validator).WithRecoverPanic(blder.recoverPanic)
+ return admission.WithCustomValidator(blder.mgr.GetScheme(), blder.apiType, validator).WithRecoverPanic(blder.recoverPanic)
}
if validator, ok := blder.apiType.(admission.Validator); ok {
- return admission.ValidatingWebhookFor(validator).WithRecoverPanic(blder.recoverPanic)
+ return admission.ValidatingWebhookFor(blder.mgr.GetScheme(), validator).WithRecoverPanic(blder.recoverPanic)
}
log.Info(
"skip registering a validating webhook, object does not implement admission.Validator or WithValidator wasn't called",
@@ -179,7 +214,7 @@ func (blder *WebhookBuilder) registerConversionWebhook() error {
}
if ok {
if !blder.isAlreadyHandled("/convert") {
- blder.mgr.GetWebhookServer().Register("/convert", &conversion.Webhook{})
+ blder.mgr.GetWebhookServer().Register("/convert", conversion.NewWebhookHandler(blder.mgr.GetScheme()))
}
log.Info("Conversion webhook enabled", "GVK", blder.gvk)
}
@@ -195,10 +230,10 @@ func (blder *WebhookBuilder) getType() (runtime.Object, error) {
}
func (blder *WebhookBuilder) isAlreadyHandled(path string) bool {
- if blder.mgr.GetWebhookServer().WebhookMux == nil {
+ if blder.mgr.GetWebhookServer().WebhookMux() == nil {
return false
}
- h, p := blder.mgr.GetWebhookServer().WebhookMux.Handler(&http.Request{URL: &url.URL{Path: path}})
+ h, p := blder.mgr.GetWebhookServer().WebhookMux().Handler(&http.Request{URL: &url.URL{Path: path}})
if p == path && h != nil {
return true
}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/cache/cache.go b/vendor/sigs.k8s.io/controller-runtime/pkg/cache/cache.go
index bcb1141a5..760038704 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/cache/cache.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/cache/cache.go
@@ -19,10 +19,11 @@ package cache
import (
"context"
"fmt"
- "reflect"
+ "net/http"
"time"
"k8s.io/apimachinery/pkg/api/meta"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
@@ -37,7 +38,10 @@ import (
logf "sigs.k8s.io/controller-runtime/pkg/internal/log"
)
-var log = logf.RuntimeLog.WithName("object-cache")
+var (
+ log = logf.RuntimeLog.WithName("object-cache")
+ defaultSyncPeriod = 10 * time.Hour
+)
// Cache knows how to load Kubernetes objects, fetch informers to request
// to receive events for Kubernetes objects (at a low-level),
@@ -98,310 +102,157 @@ type Informer interface {
HasSynced() bool
}
-// ObjectSelector is an alias name of internal.Selector.
-type ObjectSelector internal.Selector
-
-// SelectorsByObject associate a client.Object's GVK to a field/label selector.
-// There is also `DefaultSelector` to set a global default (which will be overridden by
-// a more specific setting here, if any).
-type SelectorsByObject map[client.Object]ObjectSelector
-
// Options are the optional arguments for creating a new InformersMap object.
type Options struct {
+ // HTTPClient is the http client to use for the REST client
+ HTTPClient *http.Client
+
// Scheme is the scheme to use for mapping objects to GroupVersionKinds
Scheme *runtime.Scheme
// Mapper is the RESTMapper to use for mapping GroupVersionKinds to Resources
Mapper meta.RESTMapper
- // Resync is the base frequency the informers are resynced.
- // Defaults to defaultResyncTime.
- // A 10 percent jitter will be added to the Resync period between informers
- // So that all informers will not send list requests simultaneously.
- Resync *time.Duration
+ // SyncPeriod determines the minimum frequency at which watched resources are
+ // reconciled. A lower period will correct entropy more quickly, but reduce
+ // responsiveness to change if there are many watched resources. Change this
+ // value only if you know what you are doing. Defaults to 10 hours if unset.
+ // there will a 10 percent jitter between the SyncPeriod of all controllers
+ // so that all controllers will not send list requests simultaneously.
+ //
+ // This applies to all controllers.
+ //
+ // A period sync happens for two reasons:
+ // 1. To insure against a bug in the controller that causes an object to not
+ // be requeued, when it otherwise should be requeued.
+ // 2. To insure against an unknown bug in controller-runtime, or its dependencies,
+ // that causes an object to not be requeued, when it otherwise should be
+ // requeued, or to be removed from the queue, when it otherwise should not
+ // be removed.
+ //
+ // If you want
+ // 1. to insure against missed watch events, or
+ // 2. to poll services that cannot be watched,
+ // then we recommend that, instead of changing the default period, the
+ // controller requeue, with a constant duration `t`, whenever the controller
+ // is "done" with an object, and would otherwise not requeue it, i.e., we
+ // recommend the `Reconcile` function return `reconcile.Result{RequeueAfter: t}`,
+ // instead of `reconcile.Result{}`.
+ SyncPeriod *time.Duration
- // Namespace restricts the cache's ListWatch to the desired namespace
+ // Namespaces restricts the cache's ListWatch to the desired namespaces
// Default watches all namespaces
- Namespace string
+ Namespaces []string
- // SelectorsByObject restricts the cache's ListWatch to the desired
- // fields per GVK at the specified object, the map's value must implement
- // Selector [1] using for example a Set [2]
- // [1] https://pkg.go.dev/k8s.io/apimachinery/pkg/fields#Selector
- // [2] https://pkg.go.dev/k8s.io/apimachinery/pkg/fields#Set
- SelectorsByObject SelectorsByObject
+ // DefaultLabelSelector will be used as a label selectors for all object types
+ // unless they have a more specific selector set in ByObject.
+ DefaultLabelSelector labels.Selector
- // DefaultSelector will be used as selectors for all object types
- // that do not have a selector in SelectorsByObject defined.
- DefaultSelector ObjectSelector
+ // DefaultFieldSelector will be used as a field selectors for all object types
+ // unless they have a more specific selector set in ByObject.
+ DefaultFieldSelector fields.Selector
- // UnsafeDisableDeepCopyByObject indicates not to deep copy objects during get or
- // list objects per GVK at the specified object.
+ // DefaultTransform will be used as transform for all object types
+ // unless they have a more specific transform set in ByObject.
+ DefaultTransform toolscache.TransformFunc
+
+ // ByObject restricts the cache's ListWatch to the desired fields per GVK at the specified object.
+ ByObject map[client.Object]ByObject
+
+ // UnsafeDisableDeepCopy indicates not to deep copy objects during get or
+ // list objects for EVERY object.
// Be very careful with this, when enabled you must DeepCopy any object before mutating it,
// otherwise you will mutate the object in the cache.
- UnsafeDisableDeepCopyByObject DisableDeepCopyByObject
+ //
+ // This is a global setting for all objects, and can be overridden by the ByObject setting.
+ UnsafeDisableDeepCopy *bool
+}
- // TransformByObject is a map from GVKs to transformer functions which
+// ByObject offers more fine-grained control over the cache's ListWatch by object.
+type ByObject struct {
+ // Label represents a label selector for the object.
+ Label labels.Selector
+
+ // Field represents a field selector for the object.
+ Field fields.Selector
+
+ // Transform is a map from objects to transformer functions which
// get applied when objects of the transformation are about to be committed
// to cache.
//
// This function is called both for new objects to enter the cache,
- // and for updated objects.
- TransformByObject TransformByObject
+ // and for updated objects.
+ Transform toolscache.TransformFunc
- // DefaultTransform is the transform used for all GVKs which do
- // not have an explicit transform func set in TransformByObject
- DefaultTransform toolscache.TransformFunc
+ // UnsafeDisableDeepCopy indicates not to deep copy objects during get or
+ // list objects per GVK at the specified object.
+ // Be very careful with this, when enabled you must DeepCopy any object before mutating it,
+ // otherwise you will mutate the object in the cache.
+ UnsafeDisableDeepCopy *bool
}
-var defaultResyncTime = 10 * time.Hour
+// NewCacheFunc - Function for creating a new cache from the options and a rest config.
+type NewCacheFunc func(config *rest.Config, opts Options) (Cache, error)
// New initializes and returns a new Cache.
func New(config *rest.Config, opts Options) (Cache, error) {
- opts, err := defaultOpts(config, opts)
- if err != nil {
- return nil, err
+ if len(opts.Namespaces) == 0 {
+ opts.Namespaces = []string{metav1.NamespaceAll}
}
- selectorsByGVK, err := convertToByGVK(opts.SelectorsByObject, opts.DefaultSelector, opts.Scheme)
- if err != nil {
- return nil, err
- }
- disableDeepCopyByGVK, err := convertToDisableDeepCopyByGVK(opts.UnsafeDisableDeepCopyByObject, opts.Scheme)
- if err != nil {
- return nil, err
- }
- transformByGVK, err := convertToByGVK(opts.TransformByObject, opts.DefaultTransform, opts.Scheme)
- if err != nil {
- return nil, err
- }
- transformByObj := internal.TransformFuncByObjectFromMap(transformByGVK)
-
- internalSelectorsByGVK := internal.SelectorsByGVK{}
- for gvk, selector := range selectorsByGVK {
- internalSelectorsByGVK[gvk] = internal.Selector(selector)
- }
-
- im := internal.NewInformersMap(config, opts.Scheme, opts.Mapper, *opts.Resync, opts.Namespace, internalSelectorsByGVK, disableDeepCopyByGVK, transformByObj)
- return &informerCache{InformersMap: im}, nil
-}
-
-// BuilderWithOptions returns a Cache constructor that will build a cache
-// honoring the options argument, this is useful to specify options like
-// SelectorsByObject
-// WARNING: If SelectorsByObject is specified, filtered out resources are not
-// returned.
-// WARNING: If UnsafeDisableDeepCopy is enabled, you must DeepCopy any object
-// returned from cache get/list before mutating it.
-func BuilderWithOptions(options Options) NewCacheFunc {
- return func(config *rest.Config, inherited Options) (Cache, error) {
- var err error
- inherited, err = defaultOpts(config, inherited)
- if err != nil {
- return nil, err
- }
- options, err = defaultOpts(config, options)
- if err != nil {
- return nil, err
- }
- combined, err := options.inheritFrom(inherited)
- if err != nil {
- return nil, err
- }
- return New(config, *combined)
+ if len(opts.Namespaces) > 1 {
+ return newMultiNamespaceCache(config, opts)
}
-}
-func (options Options) inheritFrom(inherited Options) (*Options, error) {
- var (
- combined Options
- err error
- )
- combined.Scheme = combineScheme(inherited.Scheme, options.Scheme)
- combined.Mapper = selectMapper(inherited.Mapper, options.Mapper)
- combined.Resync = selectResync(inherited.Resync, options.Resync)
- combined.Namespace = selectNamespace(inherited.Namespace, options.Namespace)
- combined.SelectorsByObject, combined.DefaultSelector, err = combineSelectors(inherited, options, combined.Scheme)
- if err != nil {
- return nil, err
- }
- combined.UnsafeDisableDeepCopyByObject, err = combineUnsafeDeepCopy(inherited, options, combined.Scheme)
- if err != nil {
- return nil, err
- }
- combined.TransformByObject, combined.DefaultTransform, err = combineTransforms(inherited, options, combined.Scheme)
+ opts, err := defaultOpts(config, opts)
if err != nil {
return nil, err
}
- return &combined, nil
-}
-
-func combineScheme(schemes ...*runtime.Scheme) *runtime.Scheme {
- var out *runtime.Scheme
- for _, sch := range schemes {
- if sch == nil {
- continue
- }
- for gvk, t := range sch.AllKnownTypes() {
- if out == nil {
- out = runtime.NewScheme()
- }
- out.AddKnownTypeWithName(gvk, reflect.New(t).Interface().(runtime.Object))
- }
- }
- return out
-}
-func selectMapper(def, override meta.RESTMapper) meta.RESTMapper {
- if override != nil {
- return override
- }
- return def
-}
-
-func selectResync(def, override *time.Duration) *time.Duration {
- if override != nil {
- return override
- }
- return def
-}
-
-func selectNamespace(def, override string) string {
- if override != "" {
- return override
- }
- return def
-}
-
-func combineSelectors(inherited, options Options, scheme *runtime.Scheme) (SelectorsByObject, ObjectSelector, error) {
- // Selectors are combined via logical AND.
- // - Combined label selector is a union of the selectors requirements from both sets of options.
- // - Combined field selector uses fields.AndSelectors with the combined list of non-nil field selectors
- // defined in both sets of options.
- //
- // There is a bunch of complexity here because we need to convert to SelectorsByGVK
- // to be able to match keys between options and inherited and then convert back to SelectorsByObject
- optionsSelectorsByGVK, err := convertToByGVK(options.SelectorsByObject, options.DefaultSelector, scheme)
- if err != nil {
- return nil, ObjectSelector{}, err
- }
- inheritedSelectorsByGVK, err := convertToByGVK(inherited.SelectorsByObject, inherited.DefaultSelector, inherited.Scheme)
- if err != nil {
- return nil, ObjectSelector{}, err
- }
-
- for gvk, inheritedSelector := range inheritedSelectorsByGVK {
- optionsSelectorsByGVK[gvk] = combineSelector(inheritedSelector, optionsSelectorsByGVK[gvk])
- }
- return convertToByObject(optionsSelectorsByGVK, scheme)
-}
-
-func combineSelector(selectors ...ObjectSelector) ObjectSelector {
- ls := make([]labels.Selector, 0, len(selectors))
- fs := make([]fields.Selector, 0, len(selectors))
- for _, s := range selectors {
- ls = append(ls, s.Label)
- fs = append(fs, s.Field)
- }
- return ObjectSelector{
- Label: combineLabelSelectors(ls...),
- Field: combineFieldSelectors(fs...),
- }
-}
-
-func combineLabelSelectors(ls ...labels.Selector) labels.Selector {
- var combined labels.Selector
- for _, l := range ls {
- if l == nil {
- continue
- }
- if combined == nil {
- combined = labels.NewSelector()
- }
- reqs, _ := l.Requirements()
- combined = combined.Add(reqs...)
- }
- return combined
-}
-
-func combineFieldSelectors(fs ...fields.Selector) fields.Selector {
- nonNil := fs[:0]
- for _, f := range fs {
- if f == nil {
- continue
- }
- nonNil = append(nonNil, f)
- }
- if len(nonNil) == 0 {
- return nil
- }
- if len(nonNil) == 1 {
- return nonNil[0]
- }
- return fields.AndSelectors(nonNil...)
-}
-
-func combineUnsafeDeepCopy(inherited, options Options, scheme *runtime.Scheme) (DisableDeepCopyByObject, error) {
- // UnsafeDisableDeepCopyByObject is combined via precedence. Only if a value for a particular GVK is unset
- // in options will a value from inherited be used.
- optionsDisableDeepCopyByGVK, err := convertToDisableDeepCopyByGVK(options.UnsafeDisableDeepCopyByObject, options.Scheme)
+ byGVK, err := convertToInformerOptsByGVK(opts.ByObject, opts.Scheme)
if err != nil {
return nil, err
}
- inheritedDisableDeepCopyByGVK, err := convertToDisableDeepCopyByGVK(inherited.UnsafeDisableDeepCopyByObject, inherited.Scheme)
- if err != nil {
- return nil, err
+ // Set the default selector and transform.
+ byGVK[schema.GroupVersionKind{}] = internal.InformersOptsByGVK{
+ Selector: internal.Selector{
+ Label: opts.DefaultLabelSelector,
+ Field: opts.DefaultFieldSelector,
+ },
+ Transform: opts.DefaultTransform,
+ UnsafeDisableDeepCopy: opts.UnsafeDisableDeepCopy,
}
- for gvk, inheritedDeepCopy := range inheritedDisableDeepCopyByGVK {
- if _, ok := optionsDisableDeepCopyByGVK[gvk]; !ok {
- if optionsDisableDeepCopyByGVK == nil {
- optionsDisableDeepCopyByGVK = map[schema.GroupVersionKind]bool{}
- }
- optionsDisableDeepCopyByGVK[gvk] = inheritedDeepCopy
- }
- }
- return convertToDisableDeepCopyByObject(optionsDisableDeepCopyByGVK, scheme)
+ return &informerCache{
+ scheme: opts.Scheme,
+ Informers: internal.NewInformers(config, &internal.InformersOpts{
+ HTTPClient: opts.HTTPClient,
+ Scheme: opts.Scheme,
+ Mapper: opts.Mapper,
+ ResyncPeriod: *opts.SyncPeriod,
+ Namespace: opts.Namespaces[0],
+ ByGVK: byGVK,
+ }),
+ }, nil
}
-func combineTransforms(inherited, options Options, scheme *runtime.Scheme) (TransformByObject, toolscache.TransformFunc, error) {
- // Transform functions are combined via chaining. If both inherited and options define a transform
- // function, the transform function from inherited will be called first, and the transform function from
- // options will be called second.
- optionsTransformByGVK, err := convertToByGVK(options.TransformByObject, options.DefaultTransform, options.Scheme)
- if err != nil {
- return nil, nil, err
- }
- inheritedTransformByGVK, err := convertToByGVK(inherited.TransformByObject, inherited.DefaultTransform, inherited.Scheme)
- if err != nil {
- return nil, nil, err
+func defaultOpts(config *rest.Config, opts Options) (Options, error) {
+ config = rest.CopyConfig(config)
+ if config.UserAgent == "" {
+ config.UserAgent = rest.DefaultKubernetesUserAgent()
}
- for gvk, inheritedTransform := range inheritedTransformByGVK {
- if optionsTransformByGVK == nil {
- optionsTransformByGVK = map[schema.GroupVersionKind]toolscache.TransformFunc{}
- }
- optionsTransformByGVK[gvk] = combineTransform(inheritedTransform, optionsTransformByGVK[gvk])
- }
- return convertToByObject(optionsTransformByGVK, scheme)
-}
+ logger := log.WithName("setup")
-func combineTransform(inherited, current toolscache.TransformFunc) toolscache.TransformFunc {
- if inherited == nil {
- return current
- }
- if current == nil {
- return inherited
- }
- return func(in interface{}) (interface{}, error) {
- mid, err := inherited(in)
+ // Use the rest HTTP client for the provided config if unset
+ if opts.HTTPClient == nil {
+ var err error
+ opts.HTTPClient, err = rest.HTTPClientFor(config)
if err != nil {
- return nil, err
+ logger.Error(err, "Failed to get HTTP client")
+ return opts, fmt.Errorf("could not create HTTP client from config: %w", err)
}
- return current(mid)
}
-}
-func defaultOpts(config *rest.Config, opts Options) (Options, error) {
// Use the default Kubernetes Scheme if unset
if opts.Scheme == nil {
opts.Scheme = scheme.Scheme
@@ -410,108 +261,38 @@ func defaultOpts(config *rest.Config, opts Options) (Options, error) {
// Construct a new Mapper if unset
if opts.Mapper == nil {
var err error
- opts.Mapper, err = apiutil.NewDiscoveryRESTMapper(config)
+ opts.Mapper, err = apiutil.NewDiscoveryRESTMapper(config, opts.HTTPClient)
if err != nil {
- log.WithName("setup").Error(err, "Failed to get API Group-Resources")
- return opts, fmt.Errorf("could not create RESTMapper from config")
+ logger.Error(err, "Failed to get API Group-Resources")
+ return opts, fmt.Errorf("could not create RESTMapper from config: %w", err)
}
}
// Default the resync period to 10 hours if unset
- if opts.Resync == nil {
- opts.Resync = &defaultResyncTime
+ if opts.SyncPeriod == nil {
+ opts.SyncPeriod = &defaultSyncPeriod
}
return opts, nil
}
-func convertToByGVK[T any](byObject map[client.Object]T, def T, scheme *runtime.Scheme) (map[schema.GroupVersionKind]T, error) {
- byGVK := map[schema.GroupVersionKind]T{}
- for object, value := range byObject {
+func convertToInformerOptsByGVK(in map[client.Object]ByObject, scheme *runtime.Scheme) (map[schema.GroupVersionKind]internal.InformersOptsByGVK, error) {
+ out := map[schema.GroupVersionKind]internal.InformersOptsByGVK{}
+ for object, byObject := range in {
gvk, err := apiutil.GVKForObject(object, scheme)
if err != nil {
return nil, err
}
- byGVK[gvk] = value
- }
- byGVK[schema.GroupVersionKind{}] = def
- return byGVK, nil
-}
-
-func convertToByObject[T any](byGVK map[schema.GroupVersionKind]T, scheme *runtime.Scheme) (map[client.Object]T, T, error) {
- var byObject map[client.Object]T
- def := byGVK[schema.GroupVersionKind{}]
- for gvk, value := range byGVK {
- if gvk == (schema.GroupVersionKind{}) {
- continue
- }
- obj, err := scheme.New(gvk)
- if err != nil {
- return nil, def, err
+ if _, ok := out[gvk]; ok {
+ return nil, fmt.Errorf("duplicate cache options for GVK %v, cache.Options.ByObject has multiple types with the same GroupVersionKind", gvk)
}
- cObj, ok := obj.(client.Object)
- if !ok {
- return nil, def, fmt.Errorf("object %T for GVK %q does not implement client.Object", obj, gvk)
+ out[gvk] = internal.InformersOptsByGVK{
+ Selector: internal.Selector{
+ Field: byObject.Field,
+ Label: byObject.Label,
+ },
+ Transform: byObject.Transform,
+ UnsafeDisableDeepCopy: byObject.UnsafeDisableDeepCopy,
}
- cObj.GetObjectKind().SetGroupVersionKind(gvk)
- if byObject == nil {
- byObject = map[client.Object]T{}
- }
- byObject[cObj] = value
}
- return byObject, def, nil
+ return out, nil
}
-
-// DisableDeepCopyByObject associate a client.Object's GVK to disable DeepCopy during get or list from cache.
-type DisableDeepCopyByObject map[client.Object]bool
-
-var _ client.Object = &ObjectAll{}
-
-// ObjectAll is the argument to represent all objects' types.
-type ObjectAll struct {
- client.Object
-}
-
-func convertToDisableDeepCopyByGVK(disableDeepCopyByObject DisableDeepCopyByObject, scheme *runtime.Scheme) (internal.DisableDeepCopyByGVK, error) {
- disableDeepCopyByGVK := internal.DisableDeepCopyByGVK{}
- for obj, disable := range disableDeepCopyByObject {
- switch obj.(type) {
- case ObjectAll, *ObjectAll:
- disableDeepCopyByGVK[internal.GroupVersionKindAll] = disable
- default:
- gvk, err := apiutil.GVKForObject(obj, scheme)
- if err != nil {
- return nil, err
- }
- disableDeepCopyByGVK[gvk] = disable
- }
- }
- return disableDeepCopyByGVK, nil
-}
-
-func convertToDisableDeepCopyByObject(byGVK internal.DisableDeepCopyByGVK, scheme *runtime.Scheme) (DisableDeepCopyByObject, error) {
- var byObject DisableDeepCopyByObject
- for gvk, value := range byGVK {
- if byObject == nil {
- byObject = DisableDeepCopyByObject{}
- }
- if gvk == (schema.GroupVersionKind{}) {
- byObject[ObjectAll{}] = value
- continue
- }
- obj, err := scheme.New(gvk)
- if err != nil {
- return nil, err
- }
- cObj, ok := obj.(client.Object)
- if !ok {
- return nil, fmt.Errorf("object %T for GVK %q does not implement client.Object", obj, gvk)
- }
-
- byObject[cObj] = value
- }
- return byObject, nil
-}
-
-// TransformByObject associate a client.Object's GVK to a transformer function
-// to be applied when storing the object into the cache.
-type TransformByObject map[client.Object]toolscache.TransformFunc
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/cache/informer_cache.go b/vendor/sigs.k8s.io/controller-runtime/pkg/cache/informer_cache.go
index 08e4e6df5..771244d52 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/cache/informer_cache.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/cache/informer_cache.go
@@ -19,10 +19,10 @@ package cache
import (
"context"
"fmt"
- "reflect"
"strings"
apimeta "k8s.io/apimachinery/pkg/api/meta"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
@@ -45,19 +45,21 @@ func (*ErrCacheNotStarted) Error() string {
return "the cache is not started, can not read objects"
}
-// informerCache is a Kubernetes Object cache populated from InformersMap. informerCache wraps an InformersMap.
+// informerCache is a Kubernetes Object cache populated from internal.Informers.
+// informerCache wraps internal.Informers.
type informerCache struct {
- *internal.InformersMap
+ scheme *runtime.Scheme
+ *internal.Informers
}
// Get implements Reader.
-func (ip *informerCache) Get(ctx context.Context, key client.ObjectKey, out client.Object, opts ...client.GetOption) error {
- gvk, err := apiutil.GVKForObject(out, ip.Scheme)
+func (ic *informerCache) Get(ctx context.Context, key client.ObjectKey, out client.Object, opts ...client.GetOption) error {
+ gvk, err := apiutil.GVKForObject(out, ic.scheme)
if err != nil {
return err
}
- started, cache, err := ip.InformersMap.Get(ctx, gvk, out)
+ started, cache, err := ic.Informers.Get(ctx, gvk, out)
if err != nil {
return err
}
@@ -69,13 +71,13 @@ func (ip *informerCache) Get(ctx context.Context, key client.ObjectKey, out clie
}
// List implements Reader.
-func (ip *informerCache) List(ctx context.Context, out client.ObjectList, opts ...client.ListOption) error {
- gvk, cacheTypeObj, err := ip.objectTypeForListObject(out)
+func (ic *informerCache) List(ctx context.Context, out client.ObjectList, opts ...client.ListOption) error {
+ gvk, cacheTypeObj, err := ic.objectTypeForListObject(out)
if err != nil {
return err
}
- started, cache, err := ip.InformersMap.Get(ctx, *gvk, cacheTypeObj)
+ started, cache, err := ic.Informers.Get(ctx, *gvk, cacheTypeObj)
if err != nil {
return err
}
@@ -90,54 +92,46 @@ func (ip *informerCache) List(ctx context.Context, out client.ObjectList, opts .
// objectTypeForListObject tries to find the runtime.Object and associated GVK
// for a single object corresponding to the passed-in list type. We need them
// because they are used as cache map key.
-func (ip *informerCache) objectTypeForListObject(list client.ObjectList) (*schema.GroupVersionKind, runtime.Object, error) {
- gvk, err := apiutil.GVKForObject(list, ip.Scheme)
+func (ic *informerCache) objectTypeForListObject(list client.ObjectList) (*schema.GroupVersionKind, runtime.Object, error) {
+ gvk, err := apiutil.GVKForObject(list, ic.scheme)
if err != nil {
return nil, nil, err
}
- // we need the non-list GVK, so chop off the "List" from the end of the kind
- if strings.HasSuffix(gvk.Kind, "List") && apimeta.IsListType(list) {
- gvk.Kind = gvk.Kind[:len(gvk.Kind)-4]
- }
+ // We need the non-list GVK, so chop off the "List" from the end of the kind.
+ gvk.Kind = strings.TrimSuffix(gvk.Kind, "List")
- _, isUnstructured := list.(*unstructured.UnstructuredList)
- var cacheTypeObj runtime.Object
- if isUnstructured {
+ // Handle unstructured.UnstructuredList.
+ if _, isUnstructured := list.(runtime.Unstructured); isUnstructured {
u := &unstructured.Unstructured{}
u.SetGroupVersionKind(gvk)
- cacheTypeObj = u
- } else {
- itemsPtr, err := apimeta.GetItemsPtr(list)
- if err != nil {
- return nil, nil, err
- }
- // http://knowyourmeme.com/memes/this-is-fine
- elemType := reflect.Indirect(reflect.ValueOf(itemsPtr)).Type().Elem()
- if elemType.Kind() != reflect.Ptr {
- elemType = reflect.PtrTo(elemType)
- }
-
- cacheTypeValue := reflect.Zero(elemType)
- var ok bool
- cacheTypeObj, ok = cacheTypeValue.Interface().(runtime.Object)
- if !ok {
- return nil, nil, fmt.Errorf("cannot get cache for %T, its element %T is not a runtime.Object", list, cacheTypeValue.Interface())
- }
+ return &gvk, u, nil
+ }
+ // Handle metav1.PartialObjectMetadataList.
+ if _, isPartialObjectMetadata := list.(*metav1.PartialObjectMetadataList); isPartialObjectMetadata {
+ pom := &metav1.PartialObjectMetadata{}
+ pom.SetGroupVersionKind(gvk)
+ return &gvk, pom, nil
}
+ // Any other list type should have a corresponding non-list type registered
+ // in the scheme. Use that to create a new instance of the non-list type.
+ cacheTypeObj, err := ic.scheme.New(gvk)
+ if err != nil {
+ return nil, nil, err
+ }
return &gvk, cacheTypeObj, nil
}
// GetInformerForKind returns the informer for the GroupVersionKind.
-func (ip *informerCache) GetInformerForKind(ctx context.Context, gvk schema.GroupVersionKind) (Informer, error) {
+func (ic *informerCache) GetInformerForKind(ctx context.Context, gvk schema.GroupVersionKind) (Informer, error) {
// Map the gvk to an object
- obj, err := ip.Scheme.New(gvk)
+ obj, err := ic.scheme.New(gvk)
if err != nil {
return nil, err
}
- _, i, err := ip.InformersMap.Get(ctx, gvk, obj)
+ _, i, err := ic.Informers.Get(ctx, gvk, obj)
if err != nil {
return nil, err
}
@@ -145,13 +139,13 @@ func (ip *informerCache) GetInformerForKind(ctx context.Context, gvk schema.Grou
}
// GetInformer returns the informer for the obj.
-func (ip *informerCache) GetInformer(ctx context.Context, obj client.Object) (Informer, error) {
- gvk, err := apiutil.GVKForObject(obj, ip.Scheme)
+func (ic *informerCache) GetInformer(ctx context.Context, obj client.Object) (Informer, error) {
+ gvk, err := apiutil.GVKForObject(obj, ic.scheme)
if err != nil {
return nil, err
}
- _, i, err := ip.InformersMap.Get(ctx, gvk, obj)
+ _, i, err := ic.Informers.Get(ctx, gvk, obj)
if err != nil {
return nil, err
}
@@ -160,7 +154,7 @@ func (ip *informerCache) GetInformer(ctx context.Context, obj client.Object) (In
// NeedLeaderElection implements the LeaderElectionRunnable interface
// to indicate that this can be started without requiring the leader lock.
-func (ip *informerCache) NeedLeaderElection() bool {
+func (ic *informerCache) NeedLeaderElection() bool {
return false
}
@@ -169,8 +163,8 @@ func (ip *informerCache) NeedLeaderElection() bool {
// to List. For one-to-one compatibility with "normal" field selectors, only return one value.
// The values may be anything. They will automatically be prefixed with the namespace of the
// given object, if present. The objects passed are guaranteed to be objects of the correct type.
-func (ip *informerCache) IndexField(ctx context.Context, obj client.Object, field string, extractValue client.IndexerFunc) error {
- informer, err := ip.GetInformer(ctx, obj)
+func (ic *informerCache) IndexField(ctx context.Context, obj client.Object, field string, extractValue client.IndexerFunc) error {
+ informer, err := ic.GetInformer(ctx, obj)
if err != nil {
return err
}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/cache_reader.go b/vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/cache_reader.go
index f78b08338..3c8355bbd 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/cache_reader.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/cache_reader.go
@@ -27,9 +27,9 @@ import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/tools/cache"
- "sigs.k8s.io/controller-runtime/pkg/internal/field/selector"
"sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/internal/field/selector"
)
// CacheReader is a client.Reader.
@@ -147,7 +147,7 @@ func (c *CacheReader) List(_ context.Context, out client.ObjectList, opts ...cli
}
obj, isObj := item.(runtime.Object)
if !isObj {
- return fmt.Errorf("cache contained %T, which is not an Object", obj)
+ return fmt.Errorf("cache contained %T, which is not an Object", item)
}
meta, err := apimeta.Accessor(obj)
if err != nil {
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/deleg_map.go b/vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/deleg_map.go
deleted file mode 100644
index 27f46e327..000000000
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/deleg_map.go
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
-Copyright 2018 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 internal
-
-import (
- "context"
- "time"
-
- "k8s.io/apimachinery/pkg/api/meta"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
- "k8s.io/apimachinery/pkg/runtime"
- "k8s.io/apimachinery/pkg/runtime/schema"
- "k8s.io/client-go/rest"
- "k8s.io/client-go/tools/cache"
-)
-
-// InformersMap create and caches Informers for (runtime.Object, schema.GroupVersionKind) pairs.
-// It uses a standard parameter codec constructed based on the given generated Scheme.
-type InformersMap struct {
- // we abstract over the details of structured/unstructured/metadata with the specificInformerMaps
- // TODO(directxman12): genericize this over different projections now that we have 3 different maps
-
- structured *specificInformersMap
- unstructured *specificInformersMap
- metadata *specificInformersMap
-
- // Scheme maps runtime.Objects to GroupVersionKinds
- Scheme *runtime.Scheme
-}
-
-// NewInformersMap creates a new InformersMap that can create informers for
-// both structured and unstructured objects.
-func NewInformersMap(config *rest.Config,
- scheme *runtime.Scheme,
- mapper meta.RESTMapper,
- resync time.Duration,
- namespace string,
- selectors SelectorsByGVK,
- disableDeepCopy DisableDeepCopyByGVK,
- transformers TransformFuncByObject,
-) *InformersMap {
- return &InformersMap{
- structured: newStructuredInformersMap(config, scheme, mapper, resync, namespace, selectors, disableDeepCopy, transformers),
- unstructured: newUnstructuredInformersMap(config, scheme, mapper, resync, namespace, selectors, disableDeepCopy, transformers),
- metadata: newMetadataInformersMap(config, scheme, mapper, resync, namespace, selectors, disableDeepCopy, transformers),
-
- Scheme: scheme,
- }
-}
-
-// Start calls Run on each of the informers and sets started to true. Blocks on the context.
-func (m *InformersMap) Start(ctx context.Context) error {
- go m.structured.Start(ctx)
- go m.unstructured.Start(ctx)
- go m.metadata.Start(ctx)
- <-ctx.Done()
- return nil
-}
-
-// WaitForCacheSync waits until all the caches have been started and synced.
-func (m *InformersMap) WaitForCacheSync(ctx context.Context) bool {
- syncedFuncs := append([]cache.InformerSynced(nil), m.structured.HasSyncedFuncs()...)
- syncedFuncs = append(syncedFuncs, m.unstructured.HasSyncedFuncs()...)
- syncedFuncs = append(syncedFuncs, m.metadata.HasSyncedFuncs()...)
-
- if !m.structured.waitForStarted(ctx) {
- return false
- }
- if !m.unstructured.waitForStarted(ctx) {
- return false
- }
- if !m.metadata.waitForStarted(ctx) {
- return false
- }
- return cache.WaitForCacheSync(ctx.Done(), syncedFuncs...)
-}
-
-// Get will create a new Informer and add it to the map of InformersMap if none exists. Returns
-// the Informer from the map.
-func (m *InformersMap) Get(ctx context.Context, gvk schema.GroupVersionKind, obj runtime.Object) (bool, *MapEntry, error) {
- switch obj.(type) {
- case *unstructured.Unstructured:
- return m.unstructured.Get(ctx, gvk, obj)
- case *unstructured.UnstructuredList:
- return m.unstructured.Get(ctx, gvk, obj)
- case *metav1.PartialObjectMetadata:
- return m.metadata.Get(ctx, gvk, obj)
- case *metav1.PartialObjectMetadataList:
- return m.metadata.Get(ctx, gvk, obj)
- default:
- return m.structured.Get(ctx, gvk, obj)
- }
-}
-
-// newStructuredInformersMap creates a new InformersMap for structured objects.
-func newStructuredInformersMap(config *rest.Config, scheme *runtime.Scheme, mapper meta.RESTMapper, resync time.Duration,
- namespace string, selectors SelectorsByGVK, disableDeepCopy DisableDeepCopyByGVK, transformers TransformFuncByObject) *specificInformersMap {
- return newSpecificInformersMap(config, scheme, mapper, resync, namespace, selectors, disableDeepCopy, transformers, createStructuredListWatch)
-}
-
-// newUnstructuredInformersMap creates a new InformersMap for unstructured objects.
-func newUnstructuredInformersMap(config *rest.Config, scheme *runtime.Scheme, mapper meta.RESTMapper, resync time.Duration,
- namespace string, selectors SelectorsByGVK, disableDeepCopy DisableDeepCopyByGVK, transformers TransformFuncByObject) *specificInformersMap {
- return newSpecificInformersMap(config, scheme, mapper, resync, namespace, selectors, disableDeepCopy, transformers, createUnstructuredListWatch)
-}
-
-// newMetadataInformersMap creates a new InformersMap for metadata-only objects.
-func newMetadataInformersMap(config *rest.Config, scheme *runtime.Scheme, mapper meta.RESTMapper, resync time.Duration,
- namespace string, selectors SelectorsByGVK, disableDeepCopy DisableDeepCopyByGVK, transformers TransformFuncByObject) *specificInformersMap {
- return newSpecificInformersMap(config, scheme, mapper, resync, namespace, selectors, disableDeepCopy, transformers, createMetadataListWatch)
-}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/disabledeepcopy.go b/vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/disabledeepcopy.go
deleted file mode 100644
index 54bd7eec9..000000000
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/disabledeepcopy.go
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
-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 internal
-
-import "k8s.io/apimachinery/pkg/runtime/schema"
-
-// GroupVersionKindAll is the argument to represent all GroupVersionKind types.
-var GroupVersionKindAll = schema.GroupVersionKind{}
-
-// DisableDeepCopyByGVK associate a GroupVersionKind to disable DeepCopy during get or list from cache.
-type DisableDeepCopyByGVK map[schema.GroupVersionKind]bool
-
-// IsDisabled returns whether a GroupVersionKind is disabled DeepCopy.
-func (disableByGVK DisableDeepCopyByGVK) IsDisabled(gvk schema.GroupVersionKind) bool {
- if d, ok := disableByGVK[gvk]; ok {
- return d
- } else if d, ok = disableByGVK[GroupVersionKindAll]; ok {
- return d
- }
- return false
-}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/informers.go b/vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/informers.go
new file mode 100644
index 000000000..09e011111
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/informers.go
@@ -0,0 +1,560 @@
+/*
+Copyright 2018 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 internal
+
+import (
+ "context"
+ "fmt"
+ "math/rand"
+ "net/http"
+ "sync"
+ "time"
+
+ apierrors "k8s.io/apimachinery/pkg/api/errors"
+ "k8s.io/apimachinery/pkg/api/meta"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "k8s.io/apimachinery/pkg/runtime/serializer"
+ "k8s.io/apimachinery/pkg/watch"
+ "k8s.io/client-go/dynamic"
+ "k8s.io/client-go/metadata"
+ "k8s.io/client-go/rest"
+ "k8s.io/client-go/tools/cache"
+ "sigs.k8s.io/controller-runtime/pkg/client/apiutil"
+)
+
+// InformersOpts configures an InformerMap.
+type InformersOpts struct {
+ HTTPClient *http.Client
+ Scheme *runtime.Scheme
+ Mapper meta.RESTMapper
+ ResyncPeriod time.Duration
+ Namespace string
+ ByGVK map[schema.GroupVersionKind]InformersOptsByGVK
+}
+
+// InformersOptsByGVK configured additional by group version kind (or object)
+// in an InformerMap.
+type InformersOptsByGVK struct {
+ Selector Selector
+ Transform cache.TransformFunc
+ UnsafeDisableDeepCopy *bool
+}
+
+// NewInformers creates a new InformersMap that can create informers under the hood.
+func NewInformers(config *rest.Config, options *InformersOpts) *Informers {
+ return &Informers{
+ config: config,
+ httpClient: options.HTTPClient,
+ scheme: options.Scheme,
+ mapper: options.Mapper,
+ tracker: tracker{
+ Structured: make(map[schema.GroupVersionKind]*Cache),
+ Unstructured: make(map[schema.GroupVersionKind]*Cache),
+ Metadata: make(map[schema.GroupVersionKind]*Cache),
+ },
+ codecs: serializer.NewCodecFactory(options.Scheme),
+ paramCodec: runtime.NewParameterCodec(options.Scheme),
+ resync: options.ResyncPeriod,
+ startWait: make(chan struct{}),
+ namespace: options.Namespace,
+ byGVK: options.ByGVK,
+ }
+}
+
+// Cache contains the cached data for an Cache.
+type Cache struct {
+ // Informer is the cached informer
+ Informer cache.SharedIndexInformer
+
+ // CacheReader wraps Informer and implements the CacheReader interface for a single type
+ Reader CacheReader
+}
+
+type tracker struct {
+ Structured map[schema.GroupVersionKind]*Cache
+ Unstructured map[schema.GroupVersionKind]*Cache
+ Metadata map[schema.GroupVersionKind]*Cache
+}
+
+// Informers create and caches Informers for (runtime.Object, schema.GroupVersionKind) pairs.
+// It uses a standard parameter codec constructed based on the given generated Scheme.
+type Informers struct {
+ // httpClient is used to create a new REST client
+ httpClient *http.Client
+
+ // scheme maps runtime.Objects to GroupVersionKinds
+ scheme *runtime.Scheme
+
+ // config is used to talk to the apiserver
+ config *rest.Config
+
+ // mapper maps GroupVersionKinds to Resources
+ mapper meta.RESTMapper
+
+ // tracker tracks informers keyed by their type and groupVersionKind
+ tracker tracker
+
+ // codecs is used to create a new REST client
+ codecs serializer.CodecFactory
+
+ // paramCodec is used by list and watch
+ paramCodec runtime.ParameterCodec
+
+ // resync is the base frequency the informers are resynced
+ // a 10 percent jitter will be added to the resync period between informers
+ // so that all informers will not send list requests simultaneously.
+ resync time.Duration
+
+ // mu guards access to the map
+ mu sync.RWMutex
+
+ // started is true if the informers have been started
+ started bool
+
+ // startWait is a channel that is closed after the
+ // informer has been started.
+ startWait chan struct{}
+
+ // waitGroup is the wait group that is used to wait for all informers to stop
+ waitGroup sync.WaitGroup
+
+ // stopped is true if the informers have been stopped
+ stopped bool
+
+ // ctx is the context to stop informers
+ ctx context.Context
+
+ // namespace is the namespace that all ListWatches are restricted to
+ // default or empty string means all namespaces
+ namespace string
+
+ byGVK map[schema.GroupVersionKind]InformersOptsByGVK
+}
+
+func (ip *Informers) getSelector(gvk schema.GroupVersionKind) Selector {
+ if ip.byGVK == nil {
+ return Selector{}
+ }
+ if res, ok := ip.byGVK[gvk]; ok {
+ return res.Selector
+ }
+ if res, ok := ip.byGVK[schema.GroupVersionKind{}]; ok {
+ return res.Selector
+ }
+ return Selector{}
+}
+
+func (ip *Informers) getTransform(gvk schema.GroupVersionKind) cache.TransformFunc {
+ if ip.byGVK == nil {
+ return nil
+ }
+ if res, ok := ip.byGVK[gvk]; ok {
+ return res.Transform
+ }
+ if res, ok := ip.byGVK[schema.GroupVersionKind{}]; ok {
+ return res.Transform
+ }
+ return nil
+}
+
+func (ip *Informers) getDisableDeepCopy(gvk schema.GroupVersionKind) bool {
+ if ip.byGVK == nil {
+ return false
+ }
+ if res, ok := ip.byGVK[gvk]; ok && res.UnsafeDisableDeepCopy != nil {
+ return *res.UnsafeDisableDeepCopy
+ }
+ if res, ok := ip.byGVK[schema.GroupVersionKind{}]; ok && res.UnsafeDisableDeepCopy != nil {
+ return *res.UnsafeDisableDeepCopy
+ }
+ return false
+}
+
+// Start calls Run on each of the informers and sets started to true. Blocks on the context.
+// It doesn't return start because it can't return an error, and it's not a runnable directly.
+func (ip *Informers) Start(ctx context.Context) error {
+ func() {
+ ip.mu.Lock()
+ defer ip.mu.Unlock()
+
+ // Set the context so it can be passed to informers that are added later
+ ip.ctx = ctx
+
+ // Start each informer
+ for _, i := range ip.tracker.Structured {
+ ip.startInformerLocked(i.Informer)
+ }
+ for _, i := range ip.tracker.Unstructured {
+ ip.startInformerLocked(i.Informer)
+ }
+ for _, i := range ip.tracker.Metadata {
+ ip.startInformerLocked(i.Informer)
+ }
+
+ // Set started to true so we immediately start any informers added later.
+ ip.started = true
+ close(ip.startWait)
+ }()
+ <-ctx.Done() // Block until the context is done
+ ip.mu.Lock()
+ ip.stopped = true // Set stopped to true so we don't start any new informers
+ ip.mu.Unlock()
+ ip.waitGroup.Wait() // Block until all informers have stopped
+ return nil
+}
+
+func (ip *Informers) startInformerLocked(informer cache.SharedIndexInformer) {
+ // Don't start the informer in case we are already waiting for the items in
+ // the waitGroup to finish, since waitGroups don't support waiting and adding
+ // at the same time.
+ if ip.stopped {
+ return
+ }
+
+ ip.waitGroup.Add(1)
+ go func() {
+ defer ip.waitGroup.Done()
+ informer.Run(ip.ctx.Done())
+ }()
+}
+
+func (ip *Informers) waitForStarted(ctx context.Context) bool {
+ select {
+ case <-ip.startWait:
+ return true
+ case <-ctx.Done():
+ return false
+ }
+}
+
+// getHasSyncedFuncs returns all the HasSynced functions for the informers in this map.
+func (ip *Informers) getHasSyncedFuncs() []cache.InformerSynced {
+ ip.mu.RLock()
+ defer ip.mu.RUnlock()
+
+ res := make([]cache.InformerSynced, 0,
+ len(ip.tracker.Structured)+len(ip.tracker.Unstructured)+len(ip.tracker.Metadata),
+ )
+ for _, i := range ip.tracker.Structured {
+ res = append(res, i.Informer.HasSynced)
+ }
+ for _, i := range ip.tracker.Unstructured {
+ res = append(res, i.Informer.HasSynced)
+ }
+ for _, i := range ip.tracker.Metadata {
+ res = append(res, i.Informer.HasSynced)
+ }
+ return res
+}
+
+// WaitForCacheSync waits until all the caches have been started and synced.
+func (ip *Informers) WaitForCacheSync(ctx context.Context) bool {
+ if !ip.waitForStarted(ctx) {
+ return false
+ }
+ return cache.WaitForCacheSync(ctx.Done(), ip.getHasSyncedFuncs()...)
+}
+
+func (ip *Informers) get(gvk schema.GroupVersionKind, obj runtime.Object) (res *Cache, started bool, ok bool) {
+ ip.mu.RLock()
+ defer ip.mu.RUnlock()
+ i, ok := ip.informersByType(obj)[gvk]
+ return i, ip.started, ok
+}
+
+// Get will create a new Informer and add it to the map of specificInformersMap if none exists. Returns
+// the Informer from the map.
+func (ip *Informers) Get(ctx context.Context, gvk schema.GroupVersionKind, obj runtime.Object) (bool, *Cache, error) {
+ // Return the informer if it is found
+ i, started, ok := ip.get(gvk, obj)
+ if !ok {
+ var err error
+ if i, started, err = ip.addInformerToMap(gvk, obj); err != nil {
+ return started, nil, err
+ }
+ }
+
+ if started && !i.Informer.HasSynced() {
+ // Wait for it to sync before returning the Informer so that folks don't read from a stale cache.
+ if !cache.WaitForCacheSync(ctx.Done(), i.Informer.HasSynced) {
+ return started, nil, apierrors.NewTimeoutError(fmt.Sprintf("failed waiting for %T Informer to sync", obj), 0)
+ }
+ }
+
+ return started, i, nil
+}
+
+func (ip *Informers) informersByType(obj runtime.Object) map[schema.GroupVersionKind]*Cache {
+ switch obj.(type) {
+ case runtime.Unstructured:
+ return ip.tracker.Unstructured
+ case *metav1.PartialObjectMetadata, *metav1.PartialObjectMetadataList:
+ return ip.tracker.Metadata
+ default:
+ return ip.tracker.Structured
+ }
+}
+
+func (ip *Informers) addInformerToMap(gvk schema.GroupVersionKind, obj runtime.Object) (*Cache, bool, error) {
+ ip.mu.Lock()
+ defer ip.mu.Unlock()
+
+ // Check the cache to see if we already have an Informer. If we do, return the Informer.
+ // This is for the case where 2 routines tried to get the informer when it wasn't in the map
+ // so neither returned early, but the first one created it.
+ if i, ok := ip.informersByType(obj)[gvk]; ok {
+ return i, ip.started, nil
+ }
+
+ // Create a NewSharedIndexInformer and add it to the map.
+ listWatcher, err := ip.makeListWatcher(gvk, obj)
+ if err != nil {
+ return nil, false, err
+ }
+ sharedIndexInformer := cache.NewSharedIndexInformer(&cache.ListWatch{
+ ListFunc: func(opts metav1.ListOptions) (runtime.Object, error) {
+ ip.getSelector(gvk).ApplyToList(&opts)
+ return listWatcher.ListFunc(opts)
+ },
+ WatchFunc: func(opts metav1.ListOptions) (watch.Interface, error) {
+ ip.getSelector(gvk).ApplyToList(&opts)
+ opts.Watch = true // Watch needs to be set to true separately
+ return listWatcher.WatchFunc(opts)
+ },
+ }, obj, calculateResyncPeriod(ip.resync), cache.Indexers{
+ cache.NamespaceIndex: cache.MetaNamespaceIndexFunc,
+ })
+
+ // Check to see if there is a transformer for this gvk
+ if err := sharedIndexInformer.SetTransform(ip.getTransform(gvk)); err != nil {
+ return nil, false, err
+ }
+
+ mapping, err := ip.mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
+ if err != nil {
+ return nil, false, err
+ }
+
+ // Create the new entry and set it in the map.
+ i := &Cache{
+ Informer: sharedIndexInformer,
+ Reader: CacheReader{
+ indexer: sharedIndexInformer.GetIndexer(),
+ groupVersionKind: gvk,
+ scopeName: mapping.Scope.Name(),
+ disableDeepCopy: ip.getDisableDeepCopy(gvk),
+ },
+ }
+ ip.informersByType(obj)[gvk] = i
+
+ // Start the informer in case the InformersMap has started, otherwise it will be
+ // started when the InformersMap starts.
+ if ip.started {
+ ip.startInformerLocked(i.Informer)
+ }
+ return i, ip.started, nil
+}
+
+func (ip *Informers) makeListWatcher(gvk schema.GroupVersionKind, obj runtime.Object) (*cache.ListWatch, error) {
+ // Kubernetes APIs work against Resources, not GroupVersionKinds. Map the
+ // groupVersionKind to the Resource API we will use.
+ mapping, err := ip.mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
+ if err != nil {
+ return nil, err
+ }
+
+ // Figure out if the GVK we're dealing with is global, or namespace scoped.
+ var namespace string
+ if mapping.Scope.Name() == meta.RESTScopeNameNamespace {
+ namespace = restrictNamespaceBySelector(ip.namespace, ip.getSelector(gvk))
+ }
+
+ switch obj.(type) {
+ //
+ // Unstructured
+ //
+ case runtime.Unstructured:
+ // If the rest configuration has a negotiated serializer passed in,
+ // we should remove it and use the one that the dynamic client sets for us.
+ cfg := rest.CopyConfig(ip.config)
+ cfg.NegotiatedSerializer = nil
+ dynamicClient, err := dynamic.NewForConfigAndClient(cfg, ip.httpClient)
+ if err != nil {
+ return nil, err
+ }
+ resources := dynamicClient.Resource(mapping.Resource)
+ return &cache.ListWatch{
+ ListFunc: func(opts metav1.ListOptions) (runtime.Object, error) {
+ if namespace != "" {
+ return resources.Namespace(namespace).List(ip.ctx, opts)
+ }
+ return resources.List(ip.ctx, opts)
+ },
+ // Setup the watch function
+ WatchFunc: func(opts metav1.ListOptions) (watch.Interface, error) {
+ if namespace != "" {
+ return resources.Namespace(namespace).Watch(ip.ctx, opts)
+ }
+ return resources.Watch(ip.ctx, opts)
+ },
+ }, nil
+ //
+ // Metadata
+ //
+ case *metav1.PartialObjectMetadata, *metav1.PartialObjectMetadataList:
+ // Always clear the negotiated serializer and use the one
+ // set from the metadata client.
+ cfg := rest.CopyConfig(ip.config)
+ cfg.NegotiatedSerializer = nil
+
+ // Grab the metadata metadataClient.
+ metadataClient, err := metadata.NewForConfigAndClient(cfg, ip.httpClient)
+ if err != nil {
+ return nil, err
+ }
+ resources := metadataClient.Resource(mapping.Resource)
+
+ return &cache.ListWatch{
+ ListFunc: func(opts metav1.ListOptions) (runtime.Object, error) {
+ var (
+ list *metav1.PartialObjectMetadataList
+ err error
+ )
+ if namespace != "" {
+ list, err = resources.Namespace(namespace).List(ip.ctx, opts)
+ } else {
+ list, err = resources.List(ip.ctx, opts)
+ }
+ if list != nil {
+ for i := range list.Items {
+ list.Items[i].SetGroupVersionKind(gvk)
+ }
+ }
+ return list, err
+ },
+ // Setup the watch function
+ WatchFunc: func(opts metav1.ListOptions) (watcher watch.Interface, err error) {
+ if namespace != "" {
+ watcher, err = resources.Namespace(namespace).Watch(ip.ctx, opts)
+ } else {
+ watcher, err = resources.Watch(ip.ctx, opts)
+ }
+ if err != nil {
+ return nil, err
+ }
+ return newGVKFixupWatcher(gvk, watcher), nil
+ },
+ }, nil
+ //
+ // Structured.
+ //
+ default:
+ client, err := apiutil.RESTClientForGVK(gvk, false, ip.config, ip.codecs, ip.httpClient)
+ if err != nil {
+ return nil, err
+ }
+ listGVK := gvk.GroupVersion().WithKind(gvk.Kind + "List")
+ listObj, err := ip.scheme.New(listGVK)
+ if err != nil {
+ return nil, err
+ }
+ return &cache.ListWatch{
+ ListFunc: func(opts metav1.ListOptions) (runtime.Object, error) {
+ // Build the request.
+ req := client.Get().Resource(mapping.Resource.Resource).VersionedParams(&opts, ip.paramCodec)
+ if namespace != "" {
+ req.Namespace(namespace)
+ }
+
+ // Create the resulting object, and execute the request.
+ res := listObj.DeepCopyObject()
+ if err := req.Do(ip.ctx).Into(res); err != nil {
+ return nil, err
+ }
+ return res, nil
+ },
+ // Setup the watch function
+ WatchFunc: func(opts metav1.ListOptions) (watch.Interface, error) {
+ // Build the request.
+ req := client.Get().Resource(mapping.Resource.Resource).VersionedParams(&opts, ip.paramCodec)
+ if namespace != "" {
+ req.Namespace(namespace)
+ }
+ // Call the watch.
+ return req.Watch(ip.ctx)
+ },
+ }, nil
+ }
+}
+
+// newGVKFixupWatcher adds a wrapper that preserves the GVK information when
+// events come in.
+//
+// This works around a bug where GVK information is not passed into mapping
+// functions when using the OnlyMetadata option in the builder.
+// This issue is most likely caused by kubernetes/kubernetes#80609.
+// See kubernetes-sigs/controller-runtime#1484.
+//
+// This was originally implemented as a cache.ResourceEventHandler wrapper but
+// that contained a data race which was resolved by setting the GVK in a watch
+// wrapper, before the objects are written to the cache.
+// See kubernetes-sigs/controller-runtime#1650.
+//
+// The original watch wrapper was found to be incompatible with
+// k8s.io/client-go/tools/cache.Reflector so it has been re-implemented as a
+// watch.Filter which is compatible.
+// See kubernetes-sigs/controller-runtime#1789.
+func newGVKFixupWatcher(gvk schema.GroupVersionKind, watcher watch.Interface) watch.Interface {
+ return watch.Filter(
+ watcher,
+ func(in watch.Event) (watch.Event, bool) {
+ in.Object.GetObjectKind().SetGroupVersionKind(gvk)
+ return in, true
+ },
+ )
+}
+
+// calculateResyncPeriod returns a duration based on the desired input
+// this is so that multiple controllers don't get into lock-step and all
+// hammer the apiserver with list requests simultaneously.
+func calculateResyncPeriod(resync time.Duration) time.Duration {
+ // the factor will fall into [0.9, 1.1)
+ factor := rand.Float64()/5.0 + 0.9 //nolint:gosec
+ return time.Duration(float64(resync.Nanoseconds()) * factor)
+}
+
+// restrictNamespaceBySelector returns either a global restriction for all ListWatches
+// if not default/empty, or the namespace that a ListWatch for the specific resource
+// is restricted to, based on a specified field selector for metadata.namespace field.
+func restrictNamespaceBySelector(namespaceOpt string, s Selector) string {
+ if namespaceOpt != "" {
+ // namespace is already restricted
+ return namespaceOpt
+ }
+ fieldSelector := s.Field
+ if fieldSelector == nil || fieldSelector.Empty() {
+ return ""
+ }
+ // check whether a selector includes the namespace field
+ value, found := fieldSelector.RequiresExactMatch("metadata.namespace")
+ if found {
+ return value
+ }
+ return ""
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/informers_map.go b/vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/informers_map.go
deleted file mode 100644
index 1524d2316..000000000
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/informers_map.go
+++ /dev/null
@@ -1,480 +0,0 @@
-/*
-Copyright 2018 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 internal
-
-import (
- "context"
- "fmt"
- "math/rand"
- "sync"
- "time"
-
- apierrors "k8s.io/apimachinery/pkg/api/errors"
- "k8s.io/apimachinery/pkg/api/meta"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/runtime"
- "k8s.io/apimachinery/pkg/runtime/schema"
- "k8s.io/apimachinery/pkg/runtime/serializer"
- "k8s.io/apimachinery/pkg/watch"
- "k8s.io/client-go/dynamic"
- "k8s.io/client-go/metadata"
- "k8s.io/client-go/rest"
- "k8s.io/client-go/tools/cache"
-
- "sigs.k8s.io/controller-runtime/pkg/client/apiutil"
-)
-
-func init() {
- rand.Seed(time.Now().UnixNano())
-}
-
-// clientListWatcherFunc knows how to create a ListWatcher.
-type createListWatcherFunc func(gvk schema.GroupVersionKind, ip *specificInformersMap) (*cache.ListWatch, error)
-
-// newSpecificInformersMap returns a new specificInformersMap (like
-// the generical InformersMap, except that it doesn't implement WaitForCacheSync).
-func newSpecificInformersMap(config *rest.Config,
- scheme *runtime.Scheme,
- mapper meta.RESTMapper,
- resync time.Duration,
- namespace string,
- selectors SelectorsByGVK,
- disableDeepCopy DisableDeepCopyByGVK,
- transformers TransformFuncByObject,
- createListWatcher createListWatcherFunc,
-) *specificInformersMap {
- ip := &specificInformersMap{
- config: config,
- Scheme: scheme,
- mapper: mapper,
- informersByGVK: make(map[schema.GroupVersionKind]*MapEntry),
- codecs: serializer.NewCodecFactory(scheme),
- paramCodec: runtime.NewParameterCodec(scheme),
- resync: resync,
- startWait: make(chan struct{}),
- createListWatcher: createListWatcher,
- namespace: namespace,
- selectors: selectors.forGVK,
- disableDeepCopy: disableDeepCopy,
- transformers: transformers,
- }
- return ip
-}
-
-// MapEntry contains the cached data for an Informer.
-type MapEntry struct {
- // Informer is the cached informer
- Informer cache.SharedIndexInformer
-
- // CacheReader wraps Informer and implements the CacheReader interface for a single type
- Reader CacheReader
-}
-
-// specificInformersMap create and caches Informers for (runtime.Object, schema.GroupVersionKind) pairs.
-// It uses a standard parameter codec constructed based on the given generated Scheme.
-type specificInformersMap struct {
- // Scheme maps runtime.Objects to GroupVersionKinds
- Scheme *runtime.Scheme
-
- // config is used to talk to the apiserver
- config *rest.Config
-
- // mapper maps GroupVersionKinds to Resources
- mapper meta.RESTMapper
-
- // informersByGVK is the cache of informers keyed by groupVersionKind
- informersByGVK map[schema.GroupVersionKind]*MapEntry
-
- // codecs is used to create a new REST client
- codecs serializer.CodecFactory
-
- // paramCodec is used by list and watch
- paramCodec runtime.ParameterCodec
-
- // stop is the stop channel to stop informers
- stop <-chan struct{}
-
- // resync is the base frequency the informers are resynced
- // a 10 percent jitter will be added to the resync period between informers
- // so that all informers will not send list requests simultaneously.
- resync time.Duration
-
- // mu guards access to the map
- mu sync.RWMutex
-
- // start is true if the informers have been started
- started bool
-
- // startWait is a channel that is closed after the
- // informer has been started.
- startWait chan struct{}
-
- // createClient knows how to create a client and a list object,
- // and allows for abstracting over the particulars of structured vs
- // unstructured objects.
- createListWatcher createListWatcherFunc
-
- // namespace is the namespace that all ListWatches are restricted to
- // default or empty string means all namespaces
- namespace string
-
- // selectors are the label or field selectors that will be added to the
- // ListWatch ListOptions.
- selectors func(gvk schema.GroupVersionKind) Selector
-
- // disableDeepCopy indicates not to deep copy objects during get or list objects.
- disableDeepCopy DisableDeepCopyByGVK
-
- // transform funcs are applied to objects before they are committed to the cache
- transformers TransformFuncByObject
-}
-
-// Start calls Run on each of the informers and sets started to true. Blocks on the context.
-// It doesn't return start because it can't return an error, and it's not a runnable directly.
-func (ip *specificInformersMap) Start(ctx context.Context) {
- func() {
- ip.mu.Lock()
- defer ip.mu.Unlock()
-
- // Set the stop channel so it can be passed to informers that are added later
- ip.stop = ctx.Done()
-
- // Start each informer
- for _, informer := range ip.informersByGVK {
- go informer.Informer.Run(ctx.Done())
- }
-
- // Set started to true so we immediately start any informers added later.
- ip.started = true
- close(ip.startWait)
- }()
- <-ctx.Done()
-}
-
-func (ip *specificInformersMap) waitForStarted(ctx context.Context) bool {
- select {
- case <-ip.startWait:
- return true
- case <-ctx.Done():
- return false
- }
-}
-
-// HasSyncedFuncs returns all the HasSynced functions for the informers in this map.
-func (ip *specificInformersMap) HasSyncedFuncs() []cache.InformerSynced {
- ip.mu.RLock()
- defer ip.mu.RUnlock()
- syncedFuncs := make([]cache.InformerSynced, 0, len(ip.informersByGVK))
- for _, informer := range ip.informersByGVK {
- syncedFuncs = append(syncedFuncs, informer.Informer.HasSynced)
- }
- return syncedFuncs
-}
-
-// Get will create a new Informer and add it to the map of specificInformersMap if none exists. Returns
-// the Informer from the map.
-func (ip *specificInformersMap) Get(ctx context.Context, gvk schema.GroupVersionKind, obj runtime.Object) (bool, *MapEntry, error) {
- // Return the informer if it is found
- i, started, ok := func() (*MapEntry, bool, bool) {
- ip.mu.RLock()
- defer ip.mu.RUnlock()
- i, ok := ip.informersByGVK[gvk]
- return i, ip.started, ok
- }()
-
- if !ok {
- var err error
- if i, started, err = ip.addInformerToMap(gvk, obj); err != nil {
- return started, nil, err
- }
- }
-
- if started && !i.Informer.HasSynced() {
- // Wait for it to sync before returning the Informer so that folks don't read from a stale cache.
- if !cache.WaitForCacheSync(ctx.Done(), i.Informer.HasSynced) {
- return started, nil, apierrors.NewTimeoutError(fmt.Sprintf("failed waiting for %T Informer to sync", obj), 0)
- }
- }
-
- return started, i, nil
-}
-
-func (ip *specificInformersMap) addInformerToMap(gvk schema.GroupVersionKind, obj runtime.Object) (*MapEntry, bool, error) {
- ip.mu.Lock()
- defer ip.mu.Unlock()
-
- // Check the cache to see if we already have an Informer. If we do, return the Informer.
- // This is for the case where 2 routines tried to get the informer when it wasn't in the map
- // so neither returned early, but the first one created it.
- if i, ok := ip.informersByGVK[gvk]; ok {
- return i, ip.started, nil
- }
-
- // Create a NewSharedIndexInformer and add it to the map.
- var lw *cache.ListWatch
- lw, err := ip.createListWatcher(gvk, ip)
- if err != nil {
- return nil, false, err
- }
- ni := cache.NewSharedIndexInformer(lw, obj, resyncPeriod(ip.resync)(), cache.Indexers{
- cache.NamespaceIndex: cache.MetaNamespaceIndexFunc,
- })
-
- // Check to see if there is a transformer for this gvk
- if err := ni.SetTransform(ip.transformers.Get(gvk)); err != nil {
- return nil, false, err
- }
-
- rm, err := ip.mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
- if err != nil {
- return nil, false, err
- }
-
- i := &MapEntry{
- Informer: ni,
- Reader: CacheReader{
- indexer: ni.GetIndexer(),
- groupVersionKind: gvk,
- scopeName: rm.Scope.Name(),
- disableDeepCopy: ip.disableDeepCopy.IsDisabled(gvk),
- },
- }
- ip.informersByGVK[gvk] = i
-
- // Start the Informer if need by
- // TODO(seans): write thorough tests and document what happens here - can you add indexers?
- // can you add eventhandlers?
- if ip.started {
- go i.Informer.Run(ip.stop)
- }
- return i, ip.started, nil
-}
-
-// newListWatch returns a new ListWatch object that can be used to create a SharedIndexInformer.
-func createStructuredListWatch(gvk schema.GroupVersionKind, ip *specificInformersMap) (*cache.ListWatch, error) {
- // Kubernetes APIs work against Resources, not GroupVersionKinds. Map the
- // groupVersionKind to the Resource API we will use.
- mapping, err := ip.mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
- if err != nil {
- return nil, err
- }
-
- client, err := apiutil.RESTClientForGVK(gvk, false, ip.config, ip.codecs)
- if err != nil {
- return nil, err
- }
- listGVK := gvk.GroupVersion().WithKind(gvk.Kind + "List")
- listObj, err := ip.Scheme.New(listGVK)
- if err != nil {
- return nil, err
- }
-
- // TODO: the functions that make use of this ListWatch should be adapted to
- // pass in their own contexts instead of relying on this fixed one here.
- ctx := context.TODO()
- // Create a new ListWatch for the obj
- return &cache.ListWatch{
- ListFunc: func(opts metav1.ListOptions) (runtime.Object, error) {
- ip.selectors(gvk).ApplyToList(&opts)
- res := listObj.DeepCopyObject()
- namespace := restrictNamespaceBySelector(ip.namespace, ip.selectors(gvk))
- isNamespaceScoped := namespace != "" && mapping.Scope.Name() != meta.RESTScopeNameRoot
- err := client.Get().NamespaceIfScoped(namespace, isNamespaceScoped).Resource(mapping.Resource.Resource).VersionedParams(&opts, ip.paramCodec).Do(ctx).Into(res)
- return res, err
- },
- // Setup the watch function
- WatchFunc: func(opts metav1.ListOptions) (watch.Interface, error) {
- ip.selectors(gvk).ApplyToList(&opts)
- // Watch needs to be set to true separately
- opts.Watch = true
- namespace := restrictNamespaceBySelector(ip.namespace, ip.selectors(gvk))
- isNamespaceScoped := namespace != "" && mapping.Scope.Name() != meta.RESTScopeNameRoot
- return client.Get().NamespaceIfScoped(namespace, isNamespaceScoped).Resource(mapping.Resource.Resource).VersionedParams(&opts, ip.paramCodec).Watch(ctx)
- },
- }, nil
-}
-
-func createUnstructuredListWatch(gvk schema.GroupVersionKind, ip *specificInformersMap) (*cache.ListWatch, error) {
- // Kubernetes APIs work against Resources, not GroupVersionKinds. Map the
- // groupVersionKind to the Resource API we will use.
- mapping, err := ip.mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
- if err != nil {
- return nil, err
- }
-
- // If the rest configuration has a negotiated serializer passed in,
- // we should remove it and use the one that the dynamic client sets for us.
- cfg := rest.CopyConfig(ip.config)
- cfg.NegotiatedSerializer = nil
- dynamicClient, err := dynamic.NewForConfig(cfg)
- if err != nil {
- return nil, err
- }
-
- // TODO: the functions that make use of this ListWatch should be adapted to
- // pass in their own contexts instead of relying on this fixed one here.
- ctx := context.TODO()
- // Create a new ListWatch for the obj
- return &cache.ListWatch{
- ListFunc: func(opts metav1.ListOptions) (runtime.Object, error) {
- ip.selectors(gvk).ApplyToList(&opts)
- namespace := restrictNamespaceBySelector(ip.namespace, ip.selectors(gvk))
- if namespace != "" && mapping.Scope.Name() != meta.RESTScopeNameRoot {
- return dynamicClient.Resource(mapping.Resource).Namespace(namespace).List(ctx, opts)
- }
- return dynamicClient.Resource(mapping.Resource).List(ctx, opts)
- },
- // Setup the watch function
- WatchFunc: func(opts metav1.ListOptions) (watch.Interface, error) {
- ip.selectors(gvk).ApplyToList(&opts)
- // Watch needs to be set to true separately
- opts.Watch = true
- namespace := restrictNamespaceBySelector(ip.namespace, ip.selectors(gvk))
- if namespace != "" && mapping.Scope.Name() != meta.RESTScopeNameRoot {
- return dynamicClient.Resource(mapping.Resource).Namespace(namespace).Watch(ctx, opts)
- }
- return dynamicClient.Resource(mapping.Resource).Watch(ctx, opts)
- },
- }, nil
-}
-
-func createMetadataListWatch(gvk schema.GroupVersionKind, ip *specificInformersMap) (*cache.ListWatch, error) {
- // Kubernetes APIs work against Resources, not GroupVersionKinds. Map the
- // groupVersionKind to the Resource API we will use.
- mapping, err := ip.mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
- if err != nil {
- return nil, err
- }
-
- // Always clear the negotiated serializer and use the one
- // set from the metadata client.
- cfg := rest.CopyConfig(ip.config)
- cfg.NegotiatedSerializer = nil
-
- // grab the metadata client
- client, err := metadata.NewForConfig(cfg)
- if err != nil {
- return nil, err
- }
-
- // TODO: the functions that make use of this ListWatch should be adapted to
- // pass in their own contexts instead of relying on this fixed one here.
- ctx := context.TODO()
-
- // create the relevant listwatch
- return &cache.ListWatch{
- ListFunc: func(opts metav1.ListOptions) (runtime.Object, error) {
- ip.selectors(gvk).ApplyToList(&opts)
-
- var (
- list *metav1.PartialObjectMetadataList
- err error
- )
- namespace := restrictNamespaceBySelector(ip.namespace, ip.selectors(gvk))
- if namespace != "" && mapping.Scope.Name() != meta.RESTScopeNameRoot {
- list, err = client.Resource(mapping.Resource).Namespace(namespace).List(ctx, opts)
- } else {
- list, err = client.Resource(mapping.Resource).List(ctx, opts)
- }
- if list != nil {
- for i := range list.Items {
- list.Items[i].SetGroupVersionKind(gvk)
- }
- }
- return list, err
- },
- // Setup the watch function
- WatchFunc: func(opts metav1.ListOptions) (watch.Interface, error) {
- ip.selectors(gvk).ApplyToList(&opts)
- // Watch needs to be set to true separately
- opts.Watch = true
-
- var (
- watcher watch.Interface
- err error
- )
- namespace := restrictNamespaceBySelector(ip.namespace, ip.selectors(gvk))
- if namespace != "" && mapping.Scope.Name() != meta.RESTScopeNameRoot {
- watcher, err = client.Resource(mapping.Resource).Namespace(namespace).Watch(ctx, opts)
- } else {
- watcher, err = client.Resource(mapping.Resource).Watch(ctx, opts)
- }
- if watcher != nil {
- watcher = newGVKFixupWatcher(gvk, watcher)
- }
- return watcher, err
- },
- }, nil
-}
-
-// newGVKFixupWatcher adds a wrapper that preserves the GVK information when
-// events come in.
-//
-// This works around a bug where GVK information is not passed into mapping
-// functions when using the OnlyMetadata option in the builder.
-// This issue is most likely caused by kubernetes/kubernetes#80609.
-// See kubernetes-sigs/controller-runtime#1484.
-//
-// This was originally implemented as a cache.ResourceEventHandler wrapper but
-// that contained a data race which was resolved by setting the GVK in a watch
-// wrapper, before the objects are written to the cache.
-// See kubernetes-sigs/controller-runtime#1650.
-//
-// The original watch wrapper was found to be incompatible with
-// k8s.io/client-go/tools/cache.Reflector so it has been re-implemented as a
-// watch.Filter which is compatible.
-// See kubernetes-sigs/controller-runtime#1789.
-func newGVKFixupWatcher(gvk schema.GroupVersionKind, watcher watch.Interface) watch.Interface {
- return watch.Filter(
- watcher,
- func(in watch.Event) (watch.Event, bool) {
- in.Object.GetObjectKind().SetGroupVersionKind(gvk)
- return in, true
- },
- )
-}
-
-// resyncPeriod returns a function which generates a duration each time it is
-// invoked; this is so that multiple controllers don't get into lock-step and all
-// hammer the apiserver with list requests simultaneously.
-func resyncPeriod(resync time.Duration) func() time.Duration {
- return func() time.Duration {
- // the factor will fall into [0.9, 1.1)
- factor := rand.Float64()/5.0 + 0.9 //nolint:gosec
- return time.Duration(float64(resync.Nanoseconds()) * factor)
- }
-}
-
-// restrictNamespaceBySelector returns either a global restriction for all ListWatches
-// if not default/empty, or the namespace that a ListWatch for the specific resource
-// is restricted to, based on a specified field selector for metadata.namespace field.
-func restrictNamespaceBySelector(namespaceOpt string, s Selector) string {
- if namespaceOpt != "" {
- // namespace is already restricted
- return namespaceOpt
- }
- fieldSelector := s.Field
- if fieldSelector == nil || fieldSelector.Empty() {
- return ""
- }
- // check whether a selector includes the namespace field
- value, found := fieldSelector.RequiresExactMatch("metadata.namespace")
- if found {
- return value
- }
- return ""
-}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/selector.go b/vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/selector.go
index 4eff32fb3..c674379b9 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/selector.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/selector.go
@@ -20,23 +20,8 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
- "k8s.io/apimachinery/pkg/runtime/schema"
)
-// SelectorsByGVK associate a GroupVersionKind to a field/label selector.
-type SelectorsByGVK map[schema.GroupVersionKind]Selector
-
-func (s SelectorsByGVK) forGVK(gvk schema.GroupVersionKind) Selector {
- if specific, found := s[gvk]; found {
- return specific
- }
- if defaultSelector, found := s[schema.GroupVersionKind{}]; found {
- return defaultSelector
- }
-
- return Selector{}
-}
-
// Selector specify the label/field selector to fill in ListOptions.
type Selector struct {
Label labels.Selector
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/transformers.go b/vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/transformers.go
index f69e02262..0725f550c 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/transformers.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/transformers.go
@@ -8,9 +8,9 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
)
-// TransformFuncByObject provides access to the correct transform function for
+// TransformFuncByGVK provides access to the correct transform function for
// any given GVK.
-type TransformFuncByObject interface {
+type TransformFuncByGVK interface {
Set(runtime.Object, *runtime.Scheme, cache.TransformFunc) error
Get(schema.GroupVersionKind) cache.TransformFunc
SetDefault(transformer cache.TransformFunc)
@@ -21,9 +21,9 @@ type transformFuncByGVK struct {
transformers map[schema.GroupVersionKind]cache.TransformFunc
}
-// TransformFuncByObjectFromMap creates a TransformFuncByObject from a map that
+// TransformFuncByGVKFromMap creates a TransformFuncByGVK from a map that
// maps GVKs to TransformFuncs.
-func TransformFuncByObjectFromMap(in map[schema.GroupVersionKind]cache.TransformFunc) TransformFuncByObject {
+func TransformFuncByGVKFromMap(in map[schema.GroupVersionKind]cache.TransformFunc) TransformFuncByGVK {
byGVK := &transformFuncByGVK{}
if defaultFunc, hasDefault := in[schema.GroupVersionKind{}]; hasDefault {
byGVK.defaultTransform = defaultFunc
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/cache/multi_namespace_cache.go b/vendor/sigs.k8s.io/controller-runtime/pkg/cache/multi_namespace_cache.go
index fccb36471..ac97beae9 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/cache/multi_namespace_cache.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/cache/multi_namespace_cache.go
@@ -28,12 +28,9 @@ import (
"k8s.io/client-go/rest"
toolscache "k8s.io/client-go/tools/cache"
"sigs.k8s.io/controller-runtime/pkg/client"
- "sigs.k8s.io/controller-runtime/pkg/internal/objectutil"
+ "sigs.k8s.io/controller-runtime/pkg/client/apiutil"
)
-// NewCacheFunc - Function for creating a new cache from the options and a rest config.
-type NewCacheFunc func(config *rest.Config, opts Options) (Cache, error)
-
// a new global namespaced cache to handle cluster scoped resources.
const globalCache = "_cluster-scope"
@@ -43,31 +40,43 @@ const globalCache = "_cluster-scope"
// a global cache for cluster scoped resource. Note that this is not intended
// to be used for excluding namespaces, this is better done via a Predicate. Also note that
// you may face performance issues when using this with a high number of namespaces.
+//
+// Deprecated: Use cache.Options.Namespaces instead.
func MultiNamespacedCacheBuilder(namespaces []string) NewCacheFunc {
return func(config *rest.Config, opts Options) (Cache, error) {
- opts, err := defaultOpts(config, opts)
- if err != nil {
- return nil, err
- }
+ opts.Namespaces = namespaces
+ return newMultiNamespaceCache(config, opts)
+ }
+}
- caches := map[string]Cache{}
+func newMultiNamespaceCache(config *rest.Config, opts Options) (Cache, error) {
+ if len(opts.Namespaces) < 2 {
+ return nil, fmt.Errorf("must specify more than one namespace to use multi-namespace cache")
+ }
+ opts, err := defaultOpts(config, opts)
+ if err != nil {
+ return nil, err
+ }
- // create a cache for cluster scoped resources
- gCache, err := New(config, opts)
+ // Create every namespace cache.
+ caches := map[string]Cache{}
+ for _, ns := range opts.Namespaces {
+ opts.Namespaces = []string{ns}
+ c, err := New(config, opts)
if err != nil {
- return nil, fmt.Errorf("error creating global cache: %w", err)
+ return nil, err
}
+ caches[ns] = c
+ }
- for _, ns := range namespaces {
- opts.Namespace = ns
- c, err := New(config, opts)
- if err != nil {
- return nil, err
- }
- caches[ns] = c
- }
- return &multiNamespaceCache{namespaceToCache: caches, Scheme: opts.Scheme, RESTMapper: opts.Mapper, clusterCache: gCache}, nil
+ // Create a cache for cluster scoped resources.
+ opts.Namespaces = []string{}
+ gCache, err := New(config, opts)
+ if err != nil {
+ return nil, fmt.Errorf("error creating global cache: %w", err)
}
+
+ return &multiNamespaceCache{namespaceToCache: caches, Scheme: opts.Scheme, RESTMapper: opts.Mapper, clusterCache: gCache}, nil
}
// multiNamespaceCache knows how to handle multiple namespaced caches
@@ -89,7 +98,7 @@ func (c *multiNamespaceCache) GetInformer(ctx context.Context, obj client.Object
// If the object is clusterscoped, get the informer from clusterCache,
// if not use the namespaced caches.
- isNamespaced, err := objectutil.IsAPINamespaced(obj, c.Scheme, c.RESTMapper)
+ isNamespaced, err := apiutil.IsObjectNamespaced(obj, c.Scheme, c.RESTMapper)
if err != nil {
return nil, err
}
@@ -119,7 +128,7 @@ func (c *multiNamespaceCache) GetInformerForKind(ctx context.Context, gvk schema
// If the object is clusterscoped, get the informer from clusterCache,
// if not use the namespaced caches.
- isNamespaced, err := objectutil.IsAPINamespacedWithGVK(gvk, c.Scheme, c.RESTMapper)
+ isNamespaced, err := apiutil.IsGVKNamespaced(gvk, c.RESTMapper)
if err != nil {
return nil, err
}
@@ -183,9 +192,9 @@ func (c *multiNamespaceCache) WaitForCacheSync(ctx context.Context) bool {
}
func (c *multiNamespaceCache) IndexField(ctx context.Context, obj client.Object, field string, extractValue client.IndexerFunc) error {
- isNamespaced, err := objectutil.IsAPINamespaced(obj, c.Scheme, c.RESTMapper)
+ isNamespaced, err := apiutil.IsObjectNamespaced(obj, c.Scheme, c.RESTMapper)
if err != nil {
- return nil //nolint:nilerr
+ return err
}
if !isNamespaced {
@@ -201,7 +210,7 @@ func (c *multiNamespaceCache) IndexField(ctx context.Context, obj client.Object,
}
func (c *multiNamespaceCache) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error {
- isNamespaced, err := objectutil.IsAPINamespaced(obj, c.Scheme, c.RESTMapper)
+ isNamespaced, err := apiutil.IsObjectNamespaced(obj, c.Scheme, c.RESTMapper)
if err != nil {
return err
}
@@ -223,7 +232,7 @@ func (c *multiNamespaceCache) List(ctx context.Context, list client.ObjectList,
listOpts := client.ListOptions{}
listOpts.ApplyOptions(opts)
- isNamespaced, err := objectutil.IsAPINamespaced(list, c.Scheme, c.RESTMapper)
+ isNamespaced, err := apiutil.IsObjectNamespaced(list, c.Scheme, c.RESTMapper)
if err != nil {
return err
}
@@ -293,42 +302,63 @@ type multiNamespaceInformer struct {
namespaceToInformer map[string]Informer
}
+type handlerRegistration struct {
+ handles map[string]toolscache.ResourceEventHandlerRegistration
+}
+
+type syncer interface {
+ HasSynced() bool
+}
+
+// HasSynced asserts that the handler has been called for the full initial state of the informer.
+// This uses syncer to be compatible between client-go 1.27+ and older versions when the interface changed.
+func (h handlerRegistration) HasSynced() bool {
+ for _, reg := range h.handles {
+ if s, ok := reg.(syncer); ok {
+ if !s.HasSynced() {
+ return false
+ }
+ }
+ }
+ return true
+}
+
var _ Informer = &multiNamespaceInformer{}
// AddEventHandler adds the handler to each namespaced informer.
func (i *multiNamespaceInformer) AddEventHandler(handler toolscache.ResourceEventHandler) (toolscache.ResourceEventHandlerRegistration, error) {
- handles := make(map[string]toolscache.ResourceEventHandlerRegistration, len(i.namespaceToInformer))
+ handles := handlerRegistration{handles: make(map[string]toolscache.ResourceEventHandlerRegistration, len(i.namespaceToInformer))}
for ns, informer := range i.namespaceToInformer {
registration, err := informer.AddEventHandler(handler)
if err != nil {
return nil, err
}
- handles[ns] = registration
+ handles.handles[ns] = registration
}
return handles, nil
}
// AddEventHandlerWithResyncPeriod adds the handler with a resync period to each namespaced informer.
func (i *multiNamespaceInformer) AddEventHandlerWithResyncPeriod(handler toolscache.ResourceEventHandler, resyncPeriod time.Duration) (toolscache.ResourceEventHandlerRegistration, error) {
- handles := make(map[string]toolscache.ResourceEventHandlerRegistration, len(i.namespaceToInformer))
+ handles := handlerRegistration{handles: make(map[string]toolscache.ResourceEventHandlerRegistration, len(i.namespaceToInformer))}
for ns, informer := range i.namespaceToInformer {
registration, err := informer.AddEventHandlerWithResyncPeriod(handler, resyncPeriod)
if err != nil {
return nil, err
}
- handles[ns] = registration
+ handles.handles[ns] = registration
}
return handles, nil
}
// RemoveEventHandler removes a formerly added event handler given by its registration handle.
func (i *multiNamespaceInformer) RemoveEventHandler(h toolscache.ResourceEventHandlerRegistration) error {
- handles, ok := h.(map[string]toolscache.ResourceEventHandlerRegistration)
+ handles, ok := h.(handlerRegistration)
if !ok {
return fmt.Errorf("it is not the registration returned by multiNamespaceInformer")
}
for ns, informer := range i.namespaceToInformer {
- registration, ok := handles[ns]
+ registration, ok := handles.handles[ns]
if !ok {
continue
}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/certwatcher/certwatcher.go b/vendor/sigs.k8s.io/controller-runtime/pkg/certwatcher/certwatcher.go
index 1030013db..2b9b60d8d 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/certwatcher/certwatcher.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/certwatcher/certwatcher.go
@@ -19,9 +19,14 @@ package certwatcher
import (
"context"
"crypto/tls"
+ "fmt"
"sync"
+ "time"
"github.com/fsnotify/fsnotify"
+ kerrors "k8s.io/apimachinery/pkg/util/errors"
+ "k8s.io/apimachinery/pkg/util/sets"
+ "k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/controller-runtime/pkg/certwatcher/metrics"
logf "sigs.k8s.io/controller-runtime/pkg/internal/log"
)
@@ -39,6 +44,9 @@ type CertWatcher struct {
certPath string
keyPath string
+
+ // callback is a function to be invoked when the certificate changes.
+ callback func(tls.Certificate)
}
// New returns a new CertWatcher watching the given certificate and key.
@@ -63,6 +71,17 @@ func New(certPath, keyPath string) (*CertWatcher, error) {
return cw, nil
}
+// RegisterCallback registers a callback to be invoked when the certificate changes.
+func (cw *CertWatcher) RegisterCallback(callback func(tls.Certificate)) {
+ cw.Lock()
+ defer cw.Unlock()
+ // If the current certificate is not nil, invoke the callback immediately.
+ if cw.currentCert != nil {
+ callback(*cw.currentCert)
+ }
+ cw.callback = callback
+}
+
// GetCertificate fetches the currently loaded certificate, which may be nil.
func (cw *CertWatcher) GetCertificate(_ *tls.ClientHelloInfo) (*tls.Certificate, error) {
cw.RLock()
@@ -72,11 +91,22 @@ func (cw *CertWatcher) GetCertificate(_ *tls.ClientHelloInfo) (*tls.Certificate,
// Start starts the watch on the certificate and key files.
func (cw *CertWatcher) Start(ctx context.Context) error {
- files := []string{cw.certPath, cw.keyPath}
+ files := sets.New(cw.certPath, cw.keyPath)
- for _, f := range files {
- if err := cw.watcher.Add(f); err != nil {
- return err
+ {
+ var watchErr error
+ if err := wait.PollUntilContextTimeout(ctx, 1*time.Second, 10*time.Second, true, func(ctx context.Context) (done bool, err error) {
+ for _, f := range files.UnsortedList() {
+ if err := cw.watcher.Add(f); err != nil {
+ watchErr = err
+ return false, nil //nolint:nilerr // We want to keep trying.
+ }
+ // We've added the watch, remove it from the set.
+ files.Delete(f)
+ }
+ return true, nil
+ }); err != nil {
+ return fmt.Errorf("failed to add watches: %w", kerrors.NewAggregate([]error{err, watchErr}))
}
}
@@ -130,6 +160,14 @@ func (cw *CertWatcher) ReadCertificate() error {
log.Info("Updated current TLS certificate")
+ // If a callback is registered, invoke it with the new certificate.
+ cw.RLock()
+ defer cw.RUnlock()
+ if cw.callback != nil {
+ go func() {
+ cw.callback(cert)
+ }()
+ }
return nil
}
@@ -154,13 +192,13 @@ func (cw *CertWatcher) handleEvent(event fsnotify.Event) {
}
func isWrite(event fsnotify.Event) bool {
- return event.Op&fsnotify.Write == fsnotify.Write
+ return event.Op.Has(fsnotify.Write)
}
func isCreate(event fsnotify.Event) bool {
- return event.Op&fsnotify.Create == fsnotify.Create
+ return event.Op.Has(fsnotify.Create)
}
func isRemove(event fsnotify.Event) bool {
- return event.Op&fsnotify.Remove == fsnotify.Remove
+ return event.Op.Has(fsnotify.Remove)
}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/apimachinery.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/apimachinery.go
index 8e2ac48fa..6a1bfb546 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/apimachinery.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/apimachinery.go
@@ -20,7 +20,9 @@ limitations under the License.
package apiutil
import (
+ "errors"
"fmt"
+ "net/http"
"reflect"
"sync"
@@ -30,6 +32,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/client-go/discovery"
+ "k8s.io/client-go/dynamic"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
"k8s.io/client-go/restmapper"
@@ -59,9 +62,13 @@ func AddToProtobufScheme(addToScheme func(*runtime.Scheme) error) error {
// NewDiscoveryRESTMapper constructs a new RESTMapper based on discovery
// information fetched by a new client with the given config.
-func NewDiscoveryRESTMapper(c *rest.Config) (meta.RESTMapper, error) {
+func NewDiscoveryRESTMapper(c *rest.Config, httpClient *http.Client) (meta.RESTMapper, error) {
+ if httpClient == nil {
+ return nil, fmt.Errorf("httpClient must not be nil, consider using rest.HTTPClientFor(c) to create a client")
+ }
+
// Get a mapper
- dc, err := discovery.NewDiscoveryClientForConfig(c)
+ dc, err := discovery.NewDiscoveryClientForConfigAndClient(c, httpClient)
if err != nil {
return nil, err
}
@@ -72,6 +79,36 @@ func NewDiscoveryRESTMapper(c *rest.Config) (meta.RESTMapper, error) {
return restmapper.NewDiscoveryRESTMapper(gr), nil
}
+// IsObjectNamespaced returns true if the object is namespace scoped.
+// For unstructured objects the gvk is found from the object itself.
+func IsObjectNamespaced(obj runtime.Object, scheme *runtime.Scheme, restmapper meta.RESTMapper) (bool, error) {
+ gvk, err := GVKForObject(obj, scheme)
+ if err != nil {
+ return false, err
+ }
+
+ return IsGVKNamespaced(gvk, restmapper)
+}
+
+// IsGVKNamespaced returns true if the object having the provided
+// GVK is namespace scoped.
+func IsGVKNamespaced(gvk schema.GroupVersionKind, restmapper meta.RESTMapper) (bool, error) {
+ restmapping, err := restmapper.RESTMapping(schema.GroupKind{Group: gvk.Group, Kind: gvk.Kind})
+ if err != nil {
+ return false, fmt.Errorf("failed to get restmapping: %w", err)
+ }
+
+ scope := restmapping.Scope.Name()
+ if scope == "" {
+ return false, errors.New("scope cannot be identified, empty scope returned")
+ }
+
+ if scope != meta.RESTScopeNameRoot {
+ return true, nil
+ }
+ return false, nil
+}
+
// GVKForObject finds the GroupVersionKind associated with the given object, if there is only a single such GVK.
func GVKForObject(obj runtime.Object, scheme *runtime.Scheme) (schema.GroupVersionKind, error) {
// TODO(directxman12): do we want to generalize this to arbitrary container types?
@@ -142,21 +179,11 @@ func GVKForObject(obj runtime.Object, scheme *runtime.Scheme) (schema.GroupVersi
// RESTClientForGVK constructs a new rest.Interface capable of accessing the resource associated
// with the given GroupVersionKind. The REST client will be configured to use the negotiated serializer from
// baseConfig, if set, otherwise a default serializer will be set.
-func RESTClientForGVK(gvk schema.GroupVersionKind, isUnstructured bool, baseConfig *rest.Config, codecs serializer.CodecFactory) (rest.Interface, error) {
- return rest.RESTClientFor(createRestConfig(gvk, isUnstructured, baseConfig, codecs))
-}
-
-// serializerWithDecodedGVK is a CodecFactory that overrides the DecoderToVersion of a WithoutConversionCodecFactory
-// in order to avoid clearing the GVK from the decoded object.
-//
-// See https://github.com/kubernetes/kubernetes/issues/80609.
-type serializerWithDecodedGVK struct {
- serializer.WithoutConversionCodecFactory
-}
-
-// DecoderToVersion returns an decoder that does not do conversion.
-func (f serializerWithDecodedGVK) DecoderToVersion(serializer runtime.Decoder, _ runtime.GroupVersioner) runtime.Decoder {
- return serializer
+func RESTClientForGVK(gvk schema.GroupVersionKind, isUnstructured bool, baseConfig *rest.Config, codecs serializer.CodecFactory, httpClient *http.Client) (rest.Interface, error) {
+ if httpClient == nil {
+ return nil, fmt.Errorf("httpClient must not be nil, consider using rest.HTTPClientFor(c) to create a client")
+ }
+ return rest.RESTClientForConfigAndClient(createRestConfig(gvk, isUnstructured, baseConfig, codecs), httpClient)
}
// createRestConfig copies the base config and updates needed fields for a new rest config.
@@ -183,9 +210,8 @@ func createRestConfig(gvk schema.GroupVersionKind, isUnstructured bool, baseConf
}
if isUnstructured {
- // If the object is unstructured, we need to preserve the GVK information.
- // Use our own custom serializer.
- cfg.NegotiatedSerializer = serializerWithDecodedGVK{serializer.WithoutConversionCodecFactory{CodecFactory: codecs}}
+ // If the object is unstructured, we use the client-go dynamic serializer.
+ cfg = dynamic.ConfigFor(cfg)
} else {
cfg.NegotiatedSerializer = serializerWithTargetZeroingDecode{NegotiatedSerializer: serializer.WithoutConversionCodecFactory{CodecFactory: codecs}}
}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/dynamicrestmapper.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/dynamicrestmapper.go
deleted file mode 100644
index 6b9dcf68a..000000000
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/dynamicrestmapper.go
+++ /dev/null
@@ -1,301 +0,0 @@
-/*
-Copyright 2019 The Kubernetes Authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package apiutil
-
-import (
- "sync"
- "sync/atomic"
-
- "golang.org/x/time/rate"
- "k8s.io/apimachinery/pkg/api/meta"
- "k8s.io/apimachinery/pkg/runtime/schema"
- "k8s.io/client-go/discovery"
- "k8s.io/client-go/rest"
- "k8s.io/client-go/restmapper"
-)
-
-// dynamicRESTMapper is a RESTMapper that dynamically discovers resource
-// types at runtime.
-type dynamicRESTMapper struct {
- mu sync.RWMutex // protects the following fields
- staticMapper meta.RESTMapper
- limiter *rate.Limiter
- newMapper func() (meta.RESTMapper, error)
-
- lazy bool
- // Used for lazy init.
- inited uint32
- initMtx sync.Mutex
-
- useLazyRestmapper bool
-}
-
-// DynamicRESTMapperOption is a functional option on the dynamicRESTMapper.
-type DynamicRESTMapperOption func(*dynamicRESTMapper) error
-
-// WithLimiter sets the RESTMapper's underlying limiter to lim.
-func WithLimiter(lim *rate.Limiter) DynamicRESTMapperOption {
- return func(drm *dynamicRESTMapper) error {
- drm.limiter = lim
- return nil
- }
-}
-
-// WithLazyDiscovery prevents the RESTMapper from discovering REST mappings
-// until an API call is made.
-var WithLazyDiscovery DynamicRESTMapperOption = func(drm *dynamicRESTMapper) error {
- drm.lazy = true
- return nil
-}
-
-// WithExperimentalLazyMapper enables experimental more advanced Lazy Restmapping mechanism.
-var WithExperimentalLazyMapper DynamicRESTMapperOption = func(drm *dynamicRESTMapper) error {
- drm.useLazyRestmapper = true
- return nil
-}
-
-// WithCustomMapper supports setting a custom RESTMapper refresher instead of
-// the default method, which uses a discovery client.
-//
-// This exists mainly for testing, but can be useful if you need tighter control
-// over how discovery is performed, which discovery endpoints are queried, etc.
-func WithCustomMapper(newMapper func() (meta.RESTMapper, error)) DynamicRESTMapperOption {
- return func(drm *dynamicRESTMapper) error {
- drm.newMapper = newMapper
- return nil
- }
-}
-
-// NewDynamicRESTMapper returns a dynamic RESTMapper for cfg. The dynamic
-// RESTMapper dynamically discovers resource types at runtime. opts
-// configure the RESTMapper.
-func NewDynamicRESTMapper(cfg *rest.Config, opts ...DynamicRESTMapperOption) (meta.RESTMapper, error) {
- client, err := discovery.NewDiscoveryClientForConfig(cfg)
- if err != nil {
- return nil, err
- }
- drm := &dynamicRESTMapper{
- limiter: rate.NewLimiter(rate.Limit(defaultRefillRate), defaultLimitSize),
- newMapper: func() (meta.RESTMapper, error) {
- groupResources, err := restmapper.GetAPIGroupResources(client)
- if err != nil {
- return nil, err
- }
- return restmapper.NewDiscoveryRESTMapper(groupResources), nil
- },
- }
- for _, opt := range opts {
- if err = opt(drm); err != nil {
- return nil, err
- }
- }
- if drm.useLazyRestmapper {
- return newLazyRESTMapperWithClient(client)
- }
- if !drm.lazy {
- if err := drm.setStaticMapper(); err != nil {
- return nil, err
- }
- }
- return drm, nil
-}
-
-var (
- // defaultRefilRate is the default rate at which potential calls are
- // added back to the "bucket" of allowed calls.
- defaultRefillRate = 5
- // defaultLimitSize is the default starting/max number of potential calls
- // per second. Once a call is used, it's added back to the bucket at a rate
- // of defaultRefillRate per second.
- defaultLimitSize = 5
-)
-
-// setStaticMapper sets drm's staticMapper by querying its client, regardless
-// of reload backoff.
-func (drm *dynamicRESTMapper) setStaticMapper() error {
- newMapper, err := drm.newMapper()
- if err != nil {
- return err
- }
- drm.staticMapper = newMapper
- return nil
-}
-
-// init initializes drm only once if drm is lazy.
-func (drm *dynamicRESTMapper) init() (err error) {
- // skip init if drm is not lazy or has initialized
- if !drm.lazy || atomic.LoadUint32(&drm.inited) != 0 {
- return nil
- }
-
- drm.initMtx.Lock()
- defer drm.initMtx.Unlock()
- if drm.inited == 0 {
- if err = drm.setStaticMapper(); err == nil {
- atomic.StoreUint32(&drm.inited, 1)
- }
- }
- return err
-}
-
-// checkAndReload attempts to call the given callback, which is assumed to be dependent
-// on the data in the restmapper.
-//
-// If the callback returns an error matching meta.IsNoMatchErr, it will attempt to reload
-// the RESTMapper's data and re-call the callback once that's occurred.
-// If the callback returns any other error, the function will return immediately regardless.
-//
-// It will take care of ensuring that reloads are rate-limited and that extraneous calls
-// aren't made. If a reload would exceed the limiters rate, it returns the error return by
-// the callback.
-// It's thread-safe, and worries about thread-safety for the callback (so the callback does
-// not need to attempt to lock the restmapper).
-func (drm *dynamicRESTMapper) checkAndReload(checkNeedsReload func() error) error {
- // first, check the common path -- data is fresh enough
- // (use an IIFE for the lock's defer)
- err := func() error {
- drm.mu.RLock()
- defer drm.mu.RUnlock()
-
- return checkNeedsReload()
- }()
-
- needsReload := meta.IsNoMatchError(err)
- if !needsReload {
- return err
- }
-
- // if the data wasn't fresh, we'll need to try and update it, so grab the lock...
- drm.mu.Lock()
- defer drm.mu.Unlock()
-
- // ... and double-check that we didn't reload in the meantime
- err = checkNeedsReload()
- needsReload = meta.IsNoMatchError(err)
- if !needsReload {
- return err
- }
-
- // we're still stale, so grab a rate-limit token if we can...
- if !drm.limiter.Allow() {
- // return error from static mapper here, we have refreshed often enough (exceeding rate of provided limiter)
- // so that client's can handle this the same way as a "normal" NoResourceMatchError / NoKindMatchError
- return err
- }
-
- // ...reload...
- if err := drm.setStaticMapper(); err != nil {
- return err
- }
-
- // ...and return the results of the closure regardless
- return checkNeedsReload()
-}
-
-// TODO: wrap reload errors on NoKindMatchError with go 1.13 errors.
-
-func (drm *dynamicRESTMapper) KindFor(resource schema.GroupVersionResource) (schema.GroupVersionKind, error) {
- if err := drm.init(); err != nil {
- return schema.GroupVersionKind{}, err
- }
- var gvk schema.GroupVersionKind
- err := drm.checkAndReload(func() error {
- var err error
- gvk, err = drm.staticMapper.KindFor(resource)
- return err
- })
- return gvk, err
-}
-
-func (drm *dynamicRESTMapper) KindsFor(resource schema.GroupVersionResource) ([]schema.GroupVersionKind, error) {
- if err := drm.init(); err != nil {
- return nil, err
- }
- var gvks []schema.GroupVersionKind
- err := drm.checkAndReload(func() error {
- var err error
- gvks, err = drm.staticMapper.KindsFor(resource)
- return err
- })
- return gvks, err
-}
-
-func (drm *dynamicRESTMapper) ResourceFor(input schema.GroupVersionResource) (schema.GroupVersionResource, error) {
- if err := drm.init(); err != nil {
- return schema.GroupVersionResource{}, err
- }
-
- var gvr schema.GroupVersionResource
- err := drm.checkAndReload(func() error {
- var err error
- gvr, err = drm.staticMapper.ResourceFor(input)
- return err
- })
- return gvr, err
-}
-
-func (drm *dynamicRESTMapper) ResourcesFor(input schema.GroupVersionResource) ([]schema.GroupVersionResource, error) {
- if err := drm.init(); err != nil {
- return nil, err
- }
- var gvrs []schema.GroupVersionResource
- err := drm.checkAndReload(func() error {
- var err error
- gvrs, err = drm.staticMapper.ResourcesFor(input)
- return err
- })
- return gvrs, err
-}
-
-func (drm *dynamicRESTMapper) RESTMapping(gk schema.GroupKind, versions ...string) (*meta.RESTMapping, error) {
- if err := drm.init(); err != nil {
- return nil, err
- }
- var mapping *meta.RESTMapping
- err := drm.checkAndReload(func() error {
- var err error
- mapping, err = drm.staticMapper.RESTMapping(gk, versions...)
- return err
- })
- return mapping, err
-}
-
-func (drm *dynamicRESTMapper) RESTMappings(gk schema.GroupKind, versions ...string) ([]*meta.RESTMapping, error) {
- if err := drm.init(); err != nil {
- return nil, err
- }
- var mappings []*meta.RESTMapping
- err := drm.checkAndReload(func() error {
- var err error
- mappings, err = drm.staticMapper.RESTMappings(gk, versions...)
- return err
- })
- return mappings, err
-}
-
-func (drm *dynamicRESTMapper) ResourceSingularizer(resource string) (string, error) {
- if err := drm.init(); err != nil {
- return "", err
- }
- var singular string
- err := drm.checkAndReload(func() error {
- var err error
- singular, err = drm.staticMapper.ResourceSingularizer(resource)
- return err
- })
- return singular, err
-}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/lazyrestmapper.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/restmapper.go
index e9b1e710c..e0ff72dc1 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/lazyrestmapper.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/restmapper.go
@@ -18,137 +18,151 @@ package apiutil
import (
"fmt"
+ "net/http"
"sync"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/discovery"
+ "k8s.io/client-go/rest"
"k8s.io/client-go/restmapper"
)
-// lazyRESTMapper is a RESTMapper that will lazily query the provided
+// NewDynamicRESTMapper returns a dynamic RESTMapper for cfg. The dynamic
+// RESTMapper dynamically discovers resource types at runtime.
+func NewDynamicRESTMapper(cfg *rest.Config, httpClient *http.Client) (meta.RESTMapper, error) {
+ if httpClient == nil {
+ return nil, fmt.Errorf("httpClient must not be nil, consider using rest.HTTPClientFor(c) to create a client")
+ }
+
+ client, err := discovery.NewDiscoveryClientForConfigAndClient(cfg, httpClient)
+ if err != nil {
+ return nil, err
+ }
+ return &mapper{
+ mapper: restmapper.NewDiscoveryRESTMapper([]*restmapper.APIGroupResources{}),
+ client: client,
+ knownGroups: map[string]*restmapper.APIGroupResources{},
+ apiGroups: map[string]*metav1.APIGroup{},
+ }, nil
+}
+
+// mapper is a RESTMapper that will lazily query the provided
// client for discovery information to do REST mappings.
-type lazyRESTMapper struct {
+type mapper struct {
mapper meta.RESTMapper
client *discovery.DiscoveryClient
knownGroups map[string]*restmapper.APIGroupResources
- apiGroups []metav1.APIGroup
+ apiGroups map[string]*metav1.APIGroup
// mutex to provide thread-safe mapper reloading.
- mu sync.Mutex
-}
-
-// newLazyRESTMapperWithClient initializes a LazyRESTMapper with a custom discovery client.
-func newLazyRESTMapperWithClient(discoveryClient *discovery.DiscoveryClient) (meta.RESTMapper, error) {
- return &lazyRESTMapper{
- mapper: restmapper.NewDiscoveryRESTMapper([]*restmapper.APIGroupResources{}),
- client: discoveryClient,
- knownGroups: map[string]*restmapper.APIGroupResources{},
- apiGroups: []metav1.APIGroup{},
- }, nil
+ mu sync.RWMutex
}
// KindFor implements Mapper.KindFor.
-func (m *lazyRESTMapper) KindFor(resource schema.GroupVersionResource) (schema.GroupVersionKind, error) {
- res, err := m.mapper.KindFor(resource)
+func (m *mapper) KindFor(resource schema.GroupVersionResource) (schema.GroupVersionKind, error) {
+ res, err := m.getMapper().KindFor(resource)
if meta.IsNoMatchError(err) {
- if err = m.addKnownGroupAndReload(resource.Group, resource.Version); err != nil {
- return res, err
+ if err := m.addKnownGroupAndReload(resource.Group, resource.Version); err != nil {
+ return schema.GroupVersionKind{}, err
}
-
- res, err = m.mapper.KindFor(resource)
+ res, err = m.getMapper().KindFor(resource)
}
return res, err
}
// KindsFor implements Mapper.KindsFor.
-func (m *lazyRESTMapper) KindsFor(resource schema.GroupVersionResource) ([]schema.GroupVersionKind, error) {
- res, err := m.mapper.KindsFor(resource)
+func (m *mapper) KindsFor(resource schema.GroupVersionResource) ([]schema.GroupVersionKind, error) {
+ res, err := m.getMapper().KindsFor(resource)
if meta.IsNoMatchError(err) {
- if err = m.addKnownGroupAndReload(resource.Group, resource.Version); err != nil {
- return res, err
+ if err := m.addKnownGroupAndReload(resource.Group, resource.Version); err != nil {
+ return nil, err
}
-
- res, err = m.mapper.KindsFor(resource)
+ res, err = m.getMapper().KindsFor(resource)
}
return res, err
}
// ResourceFor implements Mapper.ResourceFor.
-func (m *lazyRESTMapper) ResourceFor(input schema.GroupVersionResource) (schema.GroupVersionResource, error) {
- res, err := m.mapper.ResourceFor(input)
+func (m *mapper) ResourceFor(input schema.GroupVersionResource) (schema.GroupVersionResource, error) {
+ res, err := m.getMapper().ResourceFor(input)
if meta.IsNoMatchError(err) {
- if err = m.addKnownGroupAndReload(input.Group, input.Version); err != nil {
- return res, err
+ if err := m.addKnownGroupAndReload(input.Group, input.Version); err != nil {
+ return schema.GroupVersionResource{}, err
}
-
- res, err = m.mapper.ResourceFor(input)
+ res, err = m.getMapper().ResourceFor(input)
}
return res, err
}
// ResourcesFor implements Mapper.ResourcesFor.
-func (m *lazyRESTMapper) ResourcesFor(input schema.GroupVersionResource) ([]schema.GroupVersionResource, error) {
- res, err := m.mapper.ResourcesFor(input)
+func (m *mapper) ResourcesFor(input schema.GroupVersionResource) ([]schema.GroupVersionResource, error) {
+ res, err := m.getMapper().ResourcesFor(input)
if meta.IsNoMatchError(err) {
- if err = m.addKnownGroupAndReload(input.Group, input.Version); err != nil {
- return res, err
+ if err := m.addKnownGroupAndReload(input.Group, input.Version); err != nil {
+ return nil, err
}
-
- res, err = m.mapper.ResourcesFor(input)
+ res, err = m.getMapper().ResourcesFor(input)
}
return res, err
}
// RESTMapping implements Mapper.RESTMapping.
-func (m *lazyRESTMapper) RESTMapping(gk schema.GroupKind, versions ...string) (*meta.RESTMapping, error) {
- res, err := m.mapper.RESTMapping(gk, versions...)
+func (m *mapper) RESTMapping(gk schema.GroupKind, versions ...string) (*meta.RESTMapping, error) {
+ res, err := m.getMapper().RESTMapping(gk, versions...)
if meta.IsNoMatchError(err) {
- if err = m.addKnownGroupAndReload(gk.Group, versions...); err != nil {
- return res, err
+ if err := m.addKnownGroupAndReload(gk.Group, versions...); err != nil {
+ return nil, err
}
-
- res, err = m.mapper.RESTMapping(gk, versions...)
+ res, err = m.getMapper().RESTMapping(gk, versions...)
}
return res, err
}
// RESTMappings implements Mapper.RESTMappings.
-func (m *lazyRESTMapper) RESTMappings(gk schema.GroupKind, versions ...string) ([]*meta.RESTMapping, error) {
- res, err := m.mapper.RESTMappings(gk, versions...)
+func (m *mapper) RESTMappings(gk schema.GroupKind, versions ...string) ([]*meta.RESTMapping, error) {
+ res, err := m.getMapper().RESTMappings(gk, versions...)
if meta.IsNoMatchError(err) {
- if err = m.addKnownGroupAndReload(gk.Group, versions...); err != nil {
- return res, err
+ if err := m.addKnownGroupAndReload(gk.Group, versions...); err != nil {
+ return nil, err
}
-
- res, err = m.mapper.RESTMappings(gk, versions...)
+ res, err = m.getMapper().RESTMappings(gk, versions...)
}
return res, err
}
// ResourceSingularizer implements Mapper.ResourceSingularizer.
-func (m *lazyRESTMapper) ResourceSingularizer(resource string) (string, error) {
- return m.mapper.ResourceSingularizer(resource)
+func (m *mapper) ResourceSingularizer(resource string) (string, error) {
+ return m.getMapper().ResourceSingularizer(resource)
+}
+
+func (m *mapper) getMapper() meta.RESTMapper {
+ m.mu.RLock()
+ defer m.mu.RUnlock()
+ return m.mapper
}
// addKnownGroupAndReload reloads the mapper with updated information about missing API group.
// versions can be specified for partial updates, for instance for v1beta1 version only.
-func (m *lazyRESTMapper) addKnownGroupAndReload(groupName string, versions ...string) error {
- m.mu.Lock()
- defer m.mu.Unlock()
+func (m *mapper) addKnownGroupAndReload(groupName string, versions ...string) error {
+ // versions will here be [""] if the forwarded Version value of
+ // GroupVersionResource (in calling method) was not specified.
+ if len(versions) == 1 && versions[0] == "" {
+ versions = nil
+ }
// If no specific versions are set by user, we will scan all available ones for the API group.
// This operation requires 2 requests: /api and /apis, but only once. For all subsequent calls
// this data will be taken from cache.
if len(versions) == 0 {
- apiGroup, err := m.findAPIGroupByNameLocked(groupName)
+ apiGroup, err := m.findAPIGroupByName(groupName)
if err != nil {
return err
}
@@ -157,6 +171,9 @@ func (m *lazyRESTMapper) addKnownGroupAndReload(groupName string, versions ...st
}
}
+ m.mu.Lock()
+ defer m.mu.Unlock()
+
// Create or fetch group resources from cache.
groupResources := &restmapper.APIGroupResources{
Group: metav1.APIGroup{Name: groupName},
@@ -205,43 +222,53 @@ func (m *lazyRESTMapper) addKnownGroupAndReload(groupName string, versions ...st
}
m.mapper = restmapper.NewDiscoveryRESTMapper(updatedGroupResources)
-
return nil
}
// findAPIGroupByNameLocked returns API group by its name.
-func (m *lazyRESTMapper) findAPIGroupByNameLocked(groupName string) (metav1.APIGroup, error) {
+func (m *mapper) findAPIGroupByName(groupName string) (*metav1.APIGroup, error) {
// Looking in the cache first.
- for _, apiGroup := range m.apiGroups {
- if groupName == apiGroup.Name {
- return apiGroup, nil
+ {
+ m.mu.RLock()
+ group, ok := m.apiGroups[groupName]
+ m.mu.RUnlock()
+ if ok {
+ return group, nil
}
}
// Update the cache if nothing was found.
apiGroups, err := m.client.ServerGroups()
if err != nil {
- return metav1.APIGroup{}, fmt.Errorf("failed to get server groups: %w", err)
+ return nil, fmt.Errorf("failed to get server groups: %w", err)
}
if len(apiGroups.Groups) == 0 {
- return metav1.APIGroup{}, fmt.Errorf("received an empty API groups list")
+ return nil, fmt.Errorf("received an empty API groups list")
}
- m.apiGroups = apiGroups.Groups
+ m.mu.Lock()
+ for i := range apiGroups.Groups {
+ group := &apiGroups.Groups[i]
+ m.apiGroups[group.Name] = group
+ }
+ m.mu.Unlock()
// Looking in the cache again.
- for _, apiGroup := range m.apiGroups {
- if groupName == apiGroup.Name {
- return apiGroup, nil
+ {
+ m.mu.RLock()
+ group, ok := m.apiGroups[groupName]
+ m.mu.RUnlock()
+ if ok {
+ return group, nil
}
}
// If there is still nothing, return an error.
- return metav1.APIGroup{}, fmt.Errorf("failed to find API group %s", groupName)
+ return nil, fmt.Errorf("failed to find API group %q", groupName)
}
// fetchGroupVersionResources fetches the resources for the specified group and its versions.
-func (m *lazyRESTMapper) fetchGroupVersionResources(groupName string, versions ...string) (map[schema.GroupVersion]*metav1.APIResourceList, error) {
+func (m *mapper) fetchGroupVersionResources(groupName string, versions ...string) (map[schema.GroupVersion]*metav1.APIResourceList, error) {
groupVersionResources := make(map[schema.GroupVersion]*metav1.APIResourceList)
failedGroups := make(map[schema.GroupVersion]error)
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/client.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/client.go
index 7d1ed5c96..0d8b9fbe1 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/client/client.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/client.go
@@ -20,11 +20,11 @@ import (
"context"
"errors"
"fmt"
+ "net/http"
"strings"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer"
@@ -36,6 +36,28 @@ import (
"sigs.k8s.io/controller-runtime/pkg/log"
)
+// Options are creation options for a Client.
+type Options struct {
+ // HTTPClient is the HTTP client to use for requests.
+ HTTPClient *http.Client
+
+ // Scheme, if provided, will be used to map go structs to GroupVersionKinds
+ Scheme *runtime.Scheme
+
+ // Mapper, if provided, will be used to map GroupVersionKinds to Resources
+ Mapper meta.RESTMapper
+
+ // Cache, if provided, is used to read objects from the cache.
+ Cache *CacheOptions
+
+ // WarningHandler is used to configure the warning handler responsible for
+ // surfacing and handling warnings messages sent by the API server.
+ WarningHandler WarningHandlerOptions
+
+ // DryRun instructs the client to only perform dry run requests.
+ DryRun *bool
+}
+
// WarningHandlerOptions are options for configuring a
// warning handler for the client which is responsible
// for surfacing API Server warnings.
@@ -50,19 +72,21 @@ type WarningHandlerOptions struct {
AllowDuplicateLogs bool
}
-// Options are creation options for a Client.
-type Options struct {
- // Scheme, if provided, will be used to map go structs to GroupVersionKinds
- Scheme *runtime.Scheme
-
- // Mapper, if provided, will be used to map GroupVersionKinds to Resources
- Mapper meta.RESTMapper
-
- // Opts is used to configure the warning handler responsible for
- // surfacing and handling warnings messages sent by the API server.
- Opts WarningHandlerOptions
+// CacheOptions are options for creating a cache-backed client.
+type CacheOptions struct {
+ // Reader is a cache-backed reader that will be used to read objects from the cache.
+ // +required
+ Reader Reader
+ // DisableFor is a list of objects that should not be read from the cache.
+ DisableFor []Object
+ // Unstructured is a flag that indicates whether the cache-backed client should
+ // read unstructured objects or lists from the cache.
+ Unstructured bool
}
+// NewClientFunc allows a user to define how to create a client.
+type NewClientFunc func(config *rest.Config, options Options) (Client, error)
+
// New returns a new Client using the provided config and Options.
// The returned client reads *and* writes directly from the server
// (it doesn't use object caches). It understands how to work with
@@ -73,8 +97,12 @@ type Options struct {
// corresponding group, version, and kind for the given type. In the
// case of unstructured types, the group, version, and kind will be extracted
// from the corresponding fields on the object.
-func New(config *rest.Config, options Options) (Client, error) {
- return newClient(config, options)
+func New(config *rest.Config, options Options) (c Client, err error) {
+ c, err = newClient(config, options)
+ if err == nil && options.DryRun != nil && *options.DryRun {
+ c = NewDryRunClient(c)
+ }
+ return c, err
}
func newClient(config *rest.Config, options Options) (*client, error) {
@@ -82,22 +110,35 @@ func newClient(config *rest.Config, options Options) (*client, error) {
return nil, fmt.Errorf("must provide non-nil rest.Config to client.New")
}
- if !options.Opts.SuppressWarnings {
+ config = rest.CopyConfig(config)
+ if config.UserAgent == "" {
+ config.UserAgent = rest.DefaultKubernetesUserAgent()
+ }
+
+ if !options.WarningHandler.SuppressWarnings {
// surface warnings
logger := log.Log.WithName("KubeAPIWarningLogger")
// Set a WarningHandler, the default WarningHandler
// is log.KubeAPIWarningLogger with deduplication enabled.
// See log.KubeAPIWarningLoggerOptions for considerations
// regarding deduplication.
- config = rest.CopyConfig(config)
config.WarningHandler = log.NewKubeAPIWarningLogger(
logger,
log.KubeAPIWarningLoggerOptions{
- Deduplicate: !options.Opts.AllowDuplicateLogs,
+ Deduplicate: !options.WarningHandler.AllowDuplicateLogs,
},
)
}
+ // Use the rest HTTP client for the provided config if unset
+ if options.HTTPClient == nil {
+ var err error
+ options.HTTPClient, err = rest.HTTPClientFor(config)
+ if err != nil {
+ return nil, err
+ }
+ }
+
// Init a scheme if none provided
if options.Scheme == nil {
options.Scheme = scheme.Scheme
@@ -106,34 +147,35 @@ func newClient(config *rest.Config, options Options) (*client, error) {
// Init a Mapper if none provided
if options.Mapper == nil {
var err error
- options.Mapper, err = apiutil.NewDynamicRESTMapper(config)
+ options.Mapper, err = apiutil.NewDynamicRESTMapper(config, options.HTTPClient)
if err != nil {
return nil, err
}
}
- clientcache := &clientCache{
- config: config,
- scheme: options.Scheme,
- mapper: options.Mapper,
- codecs: serializer.NewCodecFactory(options.Scheme),
+ resources := &clientRestResources{
+ httpClient: options.HTTPClient,
+ config: config,
+ scheme: options.Scheme,
+ mapper: options.Mapper,
+ codecs: serializer.NewCodecFactory(options.Scheme),
structuredResourceByType: make(map[schema.GroupVersionKind]*resourceMeta),
unstructuredResourceByType: make(map[schema.GroupVersionKind]*resourceMeta),
}
- rawMetaClient, err := metadata.NewForConfig(config)
+ rawMetaClient, err := metadata.NewForConfigAndClient(metadata.ConfigFor(config), options.HTTPClient)
if err != nil {
return nil, fmt.Errorf("unable to construct metadata-only client for use as part of client: %w", err)
}
c := &client{
typedClient: typedClient{
- cache: clientcache,
+ resources: resources,
paramCodec: runtime.NewParameterCodec(options.Scheme),
},
unstructuredClient: unstructuredClient{
- cache: clientcache,
+ resources: resources,
paramCodec: noConversionParamCodec{},
},
metadataClient: metadataClient{
@@ -143,20 +185,65 @@ func newClient(config *rest.Config, options Options) (*client, error) {
scheme: options.Scheme,
mapper: options.Mapper,
}
+ if options.Cache == nil || options.Cache.Reader == nil {
+ return c, nil
+ }
+ // We want a cache if we're here.
+ // Set the cache.
+ c.cache = options.Cache.Reader
+
+ // Load uncached GVKs.
+ c.cacheUnstructured = options.Cache.Unstructured
+ c.uncachedGVKs = map[schema.GroupVersionKind]struct{}{}
+ for _, obj := range options.Cache.DisableFor {
+ gvk, err := c.GroupVersionKindFor(obj)
+ if err != nil {
+ return nil, err
+ }
+ c.uncachedGVKs[gvk] = struct{}{}
+ }
return c, nil
}
var _ Client = &client{}
-// client is a client.Client that reads and writes directly from/to an API server. It lazily initializes
-// new clients at the time they are used, and caches the client.
+// client is a client.Client that reads and writes directly from/to an API server.
+// It lazily initializes new clients at the time they are used.
type client struct {
typedClient typedClient
unstructuredClient unstructuredClient
metadataClient metadataClient
scheme *runtime.Scheme
mapper meta.RESTMapper
+
+ cache Reader
+ uncachedGVKs map[schema.GroupVersionKind]struct{}
+ cacheUnstructured bool
+}
+
+func (c *client) shouldBypassCache(obj runtime.Object) (bool, error) {
+ if c.cache == nil {
+ return true, nil
+ }
+
+ gvk, err := c.GroupVersionKindFor(obj)
+ if err != nil {
+ return false, err
+ }
+ // TODO: this is producing unsafe guesses that don't actually work,
+ // but it matches ~99% of the cases out there.
+ if meta.IsListType(obj) {
+ gvk.Kind = strings.TrimSuffix(gvk.Kind, "List")
+ }
+ if _, isUncached := c.uncachedGVKs[gvk]; isUncached {
+ return true, nil
+ }
+ if !c.cacheUnstructured {
+ _, isUnstructured := obj.(runtime.Unstructured)
+ return isUnstructured, nil
+ }
+ return false, nil
}
// resetGroupVersionKind is a helper function to restore and preserve GroupVersionKind on an object.
@@ -168,6 +255,16 @@ func (c *client) resetGroupVersionKind(obj runtime.Object, gvk schema.GroupVersi
}
}
+// GroupVersionKindFor returns the GroupVersionKind for the given object.
+func (c *client) GroupVersionKindFor(obj runtime.Object) (schema.GroupVersionKind, error) {
+ return apiutil.GVKForObject(obj, c.scheme)
+}
+
+// IsObjectNamespaced returns true if the GroupVersionKind of the object is namespaced.
+func (c *client) IsObjectNamespaced(obj runtime.Object) (bool, error) {
+ return apiutil.IsObjectNamespaced(obj, c.scheme, c.mapper)
+}
+
// Scheme returns the scheme this client is using.
func (c *client) Scheme() *runtime.Scheme {
return c.scheme
@@ -181,7 +278,7 @@ func (c *client) RESTMapper() meta.RESTMapper {
// Create implements client.Client.
func (c *client) Create(ctx context.Context, obj Object, opts ...CreateOption) error {
switch obj.(type) {
- case *unstructured.Unstructured:
+ case runtime.Unstructured:
return c.unstructuredClient.Create(ctx, obj, opts...)
case *metav1.PartialObjectMetadata:
return fmt.Errorf("cannot create using only metadata")
@@ -194,7 +291,7 @@ func (c *client) Create(ctx context.Context, obj Object, opts ...CreateOption) e
func (c *client) Update(ctx context.Context, obj Object, opts ...UpdateOption) error {
defer c.resetGroupVersionKind(obj, obj.GetObjectKind().GroupVersionKind())
switch obj.(type) {
- case *unstructured.Unstructured:
+ case runtime.Unstructured:
return c.unstructuredClient.Update(ctx, obj, opts...)
case *metav1.PartialObjectMetadata:
return fmt.Errorf("cannot update using only metadata -- did you mean to patch?")
@@ -206,7 +303,7 @@ func (c *client) Update(ctx context.Context, obj Object, opts ...UpdateOption) e
// Delete implements client.Client.
func (c *client) Delete(ctx context.Context, obj Object, opts ...DeleteOption) error {
switch obj.(type) {
- case *unstructured.Unstructured:
+ case runtime.Unstructured:
return c.unstructuredClient.Delete(ctx, obj, opts...)
case *metav1.PartialObjectMetadata:
return c.metadataClient.Delete(ctx, obj, opts...)
@@ -218,7 +315,7 @@ func (c *client) Delete(ctx context.Context, obj Object, opts ...DeleteOption) e
// DeleteAllOf implements client.Client.
func (c *client) DeleteAllOf(ctx context.Context, obj Object, opts ...DeleteAllOfOption) error {
switch obj.(type) {
- case *unstructured.Unstructured:
+ case runtime.Unstructured:
return c.unstructuredClient.DeleteAllOf(ctx, obj, opts...)
case *metav1.PartialObjectMetadata:
return c.metadataClient.DeleteAllOf(ctx, obj, opts...)
@@ -231,7 +328,7 @@ func (c *client) DeleteAllOf(ctx context.Context, obj Object, opts ...DeleteAllO
func (c *client) Patch(ctx context.Context, obj Object, patch Patch, opts ...PatchOption) error {
defer c.resetGroupVersionKind(obj, obj.GetObjectKind().GroupVersionKind())
switch obj.(type) {
- case *unstructured.Unstructured:
+ case runtime.Unstructured:
return c.unstructuredClient.Patch(ctx, obj, patch, opts...)
case *metav1.PartialObjectMetadata:
return c.metadataClient.Patch(ctx, obj, patch, opts...)
@@ -242,8 +339,14 @@ func (c *client) Patch(ctx context.Context, obj Object, patch Patch, opts ...Pat
// Get implements client.Client.
func (c *client) Get(ctx context.Context, key ObjectKey, obj Object, opts ...GetOption) error {
+ if isUncached, err := c.shouldBypassCache(obj); err != nil {
+ return err
+ } else if !isUncached {
+ return c.cache.Get(ctx, key, obj, opts...)
+ }
+
switch obj.(type) {
- case *unstructured.Unstructured:
+ case runtime.Unstructured:
return c.unstructuredClient.Get(ctx, key, obj, opts...)
case *metav1.PartialObjectMetadata:
// Metadata only object should always preserve the GVK coming in from the caller.
@@ -256,8 +359,14 @@ func (c *client) Get(ctx context.Context, key ObjectKey, obj Object, opts ...Get
// List implements client.Client.
func (c *client) List(ctx context.Context, obj ObjectList, opts ...ListOption) error {
+ if isUncached, err := c.shouldBypassCache(obj); err != nil {
+ return err
+ } else if !isUncached {
+ return c.cache.List(ctx, obj, opts...)
+ }
+
switch x := obj.(type) {
- case *unstructured.UnstructuredList:
+ case runtime.Unstructured:
return c.unstructuredClient.List(ctx, obj, opts...)
case *metav1.PartialObjectMetadataList:
// Metadata only object should always preserve the GVK.
@@ -431,7 +540,7 @@ func (po *SubResourcePatchOptions) ApplyToSubResourcePatch(o *SubResourcePatchOp
func (sc *subResourceClient) Get(ctx context.Context, obj Object, subResource Object, opts ...SubResourceGetOption) error {
switch obj.(type) {
- case *unstructured.Unstructured:
+ case runtime.Unstructured:
return sc.client.unstructuredClient.GetSubResource(ctx, obj, subResource, sc.subResource, opts...)
case *metav1.PartialObjectMetadata:
return errors.New("can not get subresource using only metadata")
@@ -446,7 +555,7 @@ func (sc *subResourceClient) Create(ctx context.Context, obj Object, subResource
defer sc.client.resetGroupVersionKind(subResource, subResource.GetObjectKind().GroupVersionKind())
switch obj.(type) {
- case *unstructured.Unstructured:
+ case runtime.Unstructured:
return sc.client.unstructuredClient.CreateSubResource(ctx, obj, subResource, sc.subResource, opts...)
case *metav1.PartialObjectMetadata:
return fmt.Errorf("cannot update status using only metadata -- did you mean to patch?")
@@ -459,7 +568,7 @@ func (sc *subResourceClient) Create(ctx context.Context, obj Object, subResource
func (sc *subResourceClient) Update(ctx context.Context, obj Object, opts ...SubResourceUpdateOption) error {
defer sc.client.resetGroupVersionKind(obj, obj.GetObjectKind().GroupVersionKind())
switch obj.(type) {
- case *unstructured.Unstructured:
+ case runtime.Unstructured:
return sc.client.unstructuredClient.UpdateSubResource(ctx, obj, sc.subResource, opts...)
case *metav1.PartialObjectMetadata:
return fmt.Errorf("cannot update status using only metadata -- did you mean to patch?")
@@ -472,7 +581,7 @@ func (sc *subResourceClient) Update(ctx context.Context, obj Object, opts ...Sub
func (sc *subResourceClient) Patch(ctx context.Context, obj Object, patch Patch, opts ...SubResourcePatchOption) error {
defer sc.client.resetGroupVersionKind(obj, obj.GetObjectKind().GroupVersionKind())
switch obj.(type) {
- case *unstructured.Unstructured:
+ case runtime.Unstructured:
return sc.client.unstructuredClient.PatchSubResource(ctx, obj, sc.subResource, patch, opts...)
case *metav1.PartialObjectMetadata:
return sc.client.metadataClient.PatchSubResource(ctx, obj, sc.subResource, patch, opts...)
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/client_cache.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/client_rest_resources.go
index 857a0b38a..2d0787952 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/client/client_cache.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/client_rest_resources.go
@@ -17,12 +17,12 @@ limitations under the License.
package client
import (
+ "net/http"
"strings"
"sync"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer"
@@ -30,8 +30,11 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
)
-// clientCache creates and caches rest clients and metadata for Kubernetes types.
-type clientCache struct {
+// clientRestResources creates and stores rest clients and metadata for Kubernetes types.
+type clientRestResources struct {
+ // httpClient is the http client to use for requests
+ httpClient *http.Client
+
// config is the rest.Config to talk to an apiserver
config *rest.Config
@@ -44,22 +47,22 @@ type clientCache struct {
// codecs are used to create a REST client for a gvk
codecs serializer.CodecFactory
- // structuredResourceByType caches structured type metadata
+ // structuredResourceByType stores structured type metadata
structuredResourceByType map[schema.GroupVersionKind]*resourceMeta
- // unstructuredResourceByType caches unstructured type metadata
+ // unstructuredResourceByType stores unstructured type metadata
unstructuredResourceByType map[schema.GroupVersionKind]*resourceMeta
mu sync.RWMutex
}
// newResource maps obj to a Kubernetes Resource and constructs a client for that Resource.
// If the object is a list, the resource represents the item's type instead.
-func (c *clientCache) newResource(gvk schema.GroupVersionKind, isList, isUnstructured bool) (*resourceMeta, error) {
+func (c *clientRestResources) newResource(gvk schema.GroupVersionKind, isList, isUnstructured bool) (*resourceMeta, error) {
if strings.HasSuffix(gvk.Kind, "List") && isList {
// if this was a list, treat it as a request for the item's resource
gvk.Kind = gvk.Kind[:len(gvk.Kind)-4]
}
- client, err := apiutil.RESTClientForGVK(gvk, isUnstructured, c.config, c.codecs)
+ client, err := apiutil.RESTClientForGVK(gvk, isUnstructured, c.config, c.codecs, c.httpClient)
if err != nil {
return nil, err
}
@@ -72,15 +75,13 @@ func (c *clientCache) newResource(gvk schema.GroupVersionKind, isList, isUnstruc
// getResource returns the resource meta information for the given type of object.
// If the object is a list, the resource represents the item's type instead.
-func (c *clientCache) getResource(obj runtime.Object) (*resourceMeta, error) {
+func (c *clientRestResources) getResource(obj runtime.Object) (*resourceMeta, error) {
gvk, err := apiutil.GVKForObject(obj, c.scheme)
if err != nil {
return nil, err
}
- _, isUnstructured := obj.(*unstructured.Unstructured)
- _, isUnstructuredList := obj.(*unstructured.UnstructuredList)
- isUnstructured = isUnstructured || isUnstructuredList
+ _, isUnstructured := obj.(runtime.Unstructured)
// It's better to do creation work twice than to not let multiple
// people make requests at once
@@ -108,7 +109,7 @@ func (c *clientCache) getResource(obj runtime.Object) (*resourceMeta, error) {
}
// getObjMeta returns objMeta containing both type and object metadata and state.
-func (c *clientCache) getObjMeta(obj runtime.Object) (*objMeta, error) {
+func (c *clientRestResources) getObjMeta(obj runtime.Object) (*objMeta, error) {
r, err := c.getResource(obj)
if err != nil {
return nil, err
@@ -120,7 +121,7 @@ func (c *clientCache) getObjMeta(obj runtime.Object) (*objMeta, error) {
return &objMeta{resourceMeta: r, Object: m}, err
}
-// resourceMeta caches state for a Kubernetes type.
+// resourceMeta stores state for a Kubernetes type.
type resourceMeta struct {
// client is the rest client used to talk to the apiserver
rest.Interface
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/config/config.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/config/config.go
index e4e8585cb..5f0a6d4b1 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/client/config/config.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/config/config.go
@@ -98,12 +98,12 @@ func GetConfigWithContext(context string) (*rest.Config, error) {
if err != nil {
return nil, err
}
-
if cfg.QPS == 0.0 {
cfg.QPS = 20.0
- cfg.Burst = 30.0
}
-
+ if cfg.Burst == 0 {
+ cfg.Burst = 30
+ }
return cfg, nil
}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/doc.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/doc.go
index e0e288509..b2e202494 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/client/doc.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/doc.go
@@ -26,8 +26,7 @@ limitations under the License.
// to the API server.
//
// It is a common pattern in Kubernetes to read from a cache and write to the API
-// server. This pattern is covered by the DelegatingClient type, which can
-// be used to have a client whose Reader is different from the Writer.
+// server. This pattern is covered by the creating the Client with a Cache.
//
// # Options
//
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/dryrun.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/dryrun.go
index 73b56429e..bbcdd3832 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/client/dryrun.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/dryrun.go
@@ -21,6 +21,7 @@ import (
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/schema"
)
// NewDryRunClient wraps an existing client and enforces DryRun mode
@@ -46,6 +47,16 @@ func (c *dryRunClient) RESTMapper() meta.RESTMapper {
return c.client.RESTMapper()
}
+// GroupVersionKindFor returns the GroupVersionKind for the given object.
+func (c *dryRunClient) GroupVersionKindFor(obj runtime.Object) (schema.GroupVersionKind, error) {
+ return c.client.GroupVersionKindFor(obj)
+}
+
+// IsObjectNamespaced returns true if the GroupVersionKind of the object is namespaced.
+func (c *dryRunClient) IsObjectNamespaced(obj runtime.Object) (bool, error) {
+ return c.client.IsObjectNamespaced(obj)
+}
+
// Create implements client.Client.
func (c *dryRunClient) Create(ctx context.Context, obj Object, opts ...CreateOption) error {
return c.client.Create(ctx, obj, append(opts, DryRunAll)...)
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/fake/client.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/fake/client.go
index 4da642319..aaedac844 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/client/fake/client.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/fake/client.go
@@ -17,15 +17,25 @@ limitations under the License.
package fake
import (
+ "bytes"
"context"
"encoding/json"
"errors"
"fmt"
"reflect"
+ "runtime/debug"
"strconv"
"strings"
"sync"
+ "time"
+ // Using v4 to match upstream
+ jsonpatch "github.com/evanphx/json-patch"
+ "sigs.k8s.io/controller-runtime/pkg/client/interceptor"
+
+ corev1 "k8s.io/api/core/v1"
+ policyv1 "k8s.io/api/policy/v1"
+ policyv1beta1 "k8s.io/api/policy/v1beta1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -34,7 +44,10 @@ import (
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
+ "k8s.io/apimachinery/pkg/types"
utilrand "k8s.io/apimachinery/pkg/util/rand"
+ "k8s.io/apimachinery/pkg/util/sets"
+ "k8s.io/apimachinery/pkg/util/strategicpatch"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/kubernetes/scheme"
@@ -48,13 +61,15 @@ import (
type versionedTracker struct {
testing.ObjectTracker
- scheme *runtime.Scheme
+ scheme *runtime.Scheme
+ withStatusSubresource sets.Set[schema.GroupVersionKind]
}
type fakeClient struct {
- tracker versionedTracker
- scheme *runtime.Scheme
- restMapper meta.RESTMapper
+ tracker versionedTracker
+ scheme *runtime.Scheme
+ restMapper meta.RESTMapper
+ withStatusSubresource sets.Set[schema.GroupVersionKind]
// indexes maps each GroupVersionKind (GVK) to the indexes registered for that GVK.
// The inner map maps from index name to IndexerFunc.
@@ -95,12 +110,14 @@ func NewClientBuilder() *ClientBuilder {
// ClientBuilder builds a fake client.
type ClientBuilder struct {
- scheme *runtime.Scheme
- restMapper meta.RESTMapper
- initObject []client.Object
- initLists []client.ObjectList
- initRuntimeObjects []runtime.Object
- objectTracker testing.ObjectTracker
+ scheme *runtime.Scheme
+ restMapper meta.RESTMapper
+ initObject []client.Object
+ initLists []client.ObjectList
+ initRuntimeObjects []runtime.Object
+ withStatusSubresource []client.Object
+ objectTracker testing.ObjectTracker
+ interceptorFuncs *interceptor.Funcs
// indexes maps each GroupVersionKind (GVK) to the indexes registered for that GVK.
// The inner map maps from index name to IndexerFunc.
@@ -185,6 +202,19 @@ func (f *ClientBuilder) WithIndex(obj runtime.Object, field string, extractValue
return f
}
+// WithStatusSubresource configures the passed object with a status subresource, which means
+// calls to Update and Patch will not alter its status.
+func (f *ClientBuilder) WithStatusSubresource(o ...client.Object) *ClientBuilder {
+ f.withStatusSubresource = append(f.withStatusSubresource, o...)
+ return f
+}
+
+// WithInterceptorFuncs configures the client methods to be intercepted using the provided interceptor.Funcs.
+func (f *ClientBuilder) WithInterceptorFuncs(interceptorFuncs interceptor.Funcs) *ClientBuilder {
+ f.interceptorFuncs = &interceptorFuncs
+ return f
+}
+
// Build builds and returns a new fake client.
func (f *ClientBuilder) Build() client.WithWatch {
if f.scheme == nil {
@@ -196,10 +226,19 @@ func (f *ClientBuilder) Build() client.WithWatch {
var tracker versionedTracker
+ withStatusSubResource := sets.New(inTreeResourcesWithStatus()...)
+ for _, o := range f.withStatusSubresource {
+ gvk, err := apiutil.GVKForObject(o, f.scheme)
+ if err != nil {
+ panic(fmt.Errorf("failed to get gvk for object %T: %w", withStatusSubResource, err))
+ }
+ withStatusSubResource.Insert(gvk)
+ }
+
if f.objectTracker == nil {
- tracker = versionedTracker{ObjectTracker: testing.NewObjectTracker(f.scheme, scheme.Codecs.UniversalDecoder()), scheme: f.scheme}
+ tracker = versionedTracker{ObjectTracker: testing.NewObjectTracker(f.scheme, scheme.Codecs.UniversalDecoder()), scheme: f.scheme, withStatusSubresource: withStatusSubResource}
} else {
- tracker = versionedTracker{ObjectTracker: f.objectTracker, scheme: f.scheme}
+ tracker = versionedTracker{ObjectTracker: f.objectTracker, scheme: f.scheme, withStatusSubresource: withStatusSubResource}
}
for _, obj := range f.initObject {
@@ -217,12 +256,20 @@ func (f *ClientBuilder) Build() client.WithWatch {
panic(fmt.Errorf("failed to add runtime object %v to fake client: %w", obj, err))
}
}
- return &fakeClient{
- tracker: tracker,
- scheme: f.scheme,
- restMapper: f.restMapper,
- indexes: f.indexes,
+
+ var result client.WithWatch = &fakeClient{
+ tracker: tracker,
+ scheme: f.scheme,
+ restMapper: f.restMapper,
+ indexes: f.indexes,
+ withStatusSubresource: withStatusSubResource,
+ }
+
+ if f.interceptorFuncs != nil {
+ result = interceptor.NewClient(result, *f.interceptorFuncs)
}
+
+ return result
}
const trackerAddResourceVersion = "999"
@@ -243,6 +290,9 @@ func (t versionedTracker) Add(obj runtime.Object) error {
if err != nil {
return fmt.Errorf("failed to get accessor for object: %w", err)
}
+ if accessor.GetDeletionTimestamp() != nil && len(accessor.GetFinalizers()) == 0 {
+ return fmt.Errorf("refusing to create obj %s with metadata.deletionTimestamp but no finalizers", accessor.GetName())
+ }
if accessor.GetResourceVersion() == "" {
// We use a "magic" value of 999 here because this field
// is parsed as uint and and 0 is already used in Update.
@@ -290,20 +340,22 @@ func (t versionedTracker) Create(gvr schema.GroupVersionResource, obj runtime.Ob
return nil
}
-// convertFromUnstructuredIfNecessary will convert *unstructured.Unstructured for a GVK that is recocnized
+// convertFromUnstructuredIfNecessary will convert runtime.Unstructured for a GVK that is recognized
// by the schema into the whatever the schema produces with New() for said GVK.
// This is required because the tracker unconditionally saves on manipulations, but its List() implementation
// tries to assign whatever it finds into a ListType it gets from schema.New() - Thus we have to ensure
// we save as the very same type, otherwise subsequent List requests will fail.
func convertFromUnstructuredIfNecessary(s *runtime.Scheme, o runtime.Object) (runtime.Object, error) {
- u, isUnstructured := o.(*unstructured.Unstructured)
- if !isUnstructured || !s.Recognizes(u.GroupVersionKind()) {
+ gvk := o.GetObjectKind().GroupVersionKind()
+
+ u, isUnstructured := o.(runtime.Unstructured)
+ if !isUnstructured || !s.Recognizes(gvk) {
return o, nil
}
- typed, err := s.New(u.GroupVersionKind())
+ typed, err := s.New(gvk)
if err != nil {
- return nil, fmt.Errorf("scheme recognizes %s but failed to produce an object for it: %w", u.GroupVersionKind().String(), err)
+ return nil, fmt.Errorf("scheme recognizes %s but failed to produce an object for it: %w", gvk, err)
}
unstructuredSerialized, err := json.Marshal(u)
@@ -318,6 +370,16 @@ func convertFromUnstructuredIfNecessary(s *runtime.Scheme, o runtime.Object) (ru
}
func (t versionedTracker) Update(gvr schema.GroupVersionResource, obj runtime.Object, ns string) error {
+ isStatus := false
+ // We apply patches using a client-go reaction that ends up calling the trackers Update. As we can't change
+ // that reaction, we use the callstack to figure out if this originated from the status client.
+ if bytes.Contains(debug.Stack(), []byte("sigs.k8s.io/controller-runtime/pkg/client/fake.(*fakeSubResourceClient).Patch")) {
+ isStatus = true
+ }
+ return t.update(gvr, obj, ns, isStatus, false)
+}
+
+func (t versionedTracker) update(gvr schema.GroupVersionResource, obj runtime.Object, ns string, isStatus bool, deleting bool) error {
accessor, err := meta.Accessor(obj)
if err != nil {
return fmt.Errorf("failed to get accessor for object: %w", err)
@@ -348,6 +410,20 @@ func (t versionedTracker) Update(gvr schema.GroupVersionResource, obj runtime.Ob
return err
}
+ if t.withStatusSubresource.Has(gvk) {
+ if isStatus { // copy everything but status and metadata.ResourceVersion from original object
+ if err := copyNonStatusFrom(oldObject, obj); err != nil {
+ return fmt.Errorf("failed to copy non-status field for object with status subresouce: %w", err)
+ }
+ } else { // copy status from original object
+ if err := copyStatusFrom(oldObject, obj); err != nil {
+ return fmt.Errorf("failed to copy the status for object with status subresource: %w", err)
+ }
+ }
+ } else if isStatus {
+ return apierrors.NewNotFound(gvr.GroupResource(), accessor.GetName())
+ }
+
oldAccessor, err := meta.Accessor(oldObject)
if err != nil {
return err
@@ -370,6 +446,11 @@ func (t versionedTracker) Update(gvr schema.GroupVersionResource, obj runtime.Ob
}
intResourceVersion++
accessor.SetResourceVersion(strconv.FormatUint(intResourceVersion, 10))
+
+ if !deleting && !deletionTimestampEqual(accessor, oldAccessor) {
+ return fmt.Errorf("error: Unable to edit %s: metadata.deletionTimestamp field is immutable", accessor.GetName())
+ }
+
if !accessor.GetDeletionTimestamp().IsZero() && len(accessor.GetFinalizers()) == 0 {
return t.ObjectTracker.Delete(gvr, accessor.GetNamespace(), accessor.GetName())
}
@@ -436,7 +517,7 @@ func (c *fakeClient) List(ctx context.Context, obj client.ObjectList, opts ...cl
gvk.Kind = strings.TrimSuffix(gvk.Kind, "List")
- if _, isUnstructuredList := obj.(*unstructured.UnstructuredList); isUnstructuredList && !c.scheme.Recognizes(gvk) {
+ if _, isUnstructuredList := obj.(runtime.Unstructured); isUnstructuredList && !c.scheme.Recognizes(gvk) {
// We need to register the ListKind with UnstructuredList:
// https://github.com/kubernetes/kubernetes/blob/7b2776b89fb1be28d4e9203bdeec079be903c103/staging/src/k8s.io/client-go/dynamic/fake/simple.go#L44-L51
c.schemeWriteLock.Lock()
@@ -563,6 +644,16 @@ func (c *fakeClient) RESTMapper() meta.RESTMapper {
return c.restMapper
}
+// GroupVersionKindFor returns the GroupVersionKind for the given object.
+func (c *fakeClient) GroupVersionKindFor(obj runtime.Object) (schema.GroupVersionKind, error) {
+ return apiutil.GVKForObject(obj, c.scheme)
+}
+
+// IsObjectNamespaced returns true if the GroupVersionKind of the object is namespaced.
+func (c *fakeClient) IsObjectNamespaced(obj runtime.Object) (bool, error) {
+ return apiutil.IsObjectNamespaced(obj, c.scheme, c.restMapper)
+}
+
func (c *fakeClient) Create(ctx context.Context, obj client.Object, opts ...client.CreateOption) error {
createOptions := &client.CreateOptions{}
createOptions.ApplyOptions(opts)
@@ -589,6 +680,10 @@ func (c *fakeClient) Create(ctx context.Context, obj client.Object, opts ...clie
}
accessor.SetName(fmt.Sprintf("%s%s", base, utilrand.String(randomLength)))
}
+ // Ignore attempts to set deletion timestamp
+ if !accessor.GetDeletionTimestamp().IsZero() {
+ accessor.SetDeletionTimestamp(nil)
+ }
return c.tracker.Create(gvr, obj, accessor.GetNamespace())
}
@@ -679,6 +774,10 @@ func (c *fakeClient) DeleteAllOf(ctx context.Context, obj client.Object, opts ..
}
func (c *fakeClient) Update(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error {
+ return c.update(obj, false, opts...)
+}
+
+func (c *fakeClient) update(obj client.Object, isStatus bool, opts ...client.UpdateOption) error {
updateOptions := &client.UpdateOptions{}
updateOptions.ApplyOptions(opts)
@@ -696,10 +795,14 @@ func (c *fakeClient) Update(ctx context.Context, obj client.Object, opts ...clie
if err != nil {
return err
}
- return c.tracker.Update(gvr, obj, accessor.GetNamespace())
+ return c.tracker.update(gvr, obj, accessor.GetNamespace(), isStatus, false)
}
func (c *fakeClient) Patch(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.PatchOption) error {
+ return c.patch(obj, patch, opts...)
+}
+
+func (c *fakeClient) patch(obj client.Object, patch client.Patch, opts ...client.PatchOption) error {
patchOptions := &client.PatchOptions{}
patchOptions.ApplyOptions(opts)
@@ -722,19 +825,50 @@ func (c *fakeClient) Patch(ctx context.Context, obj client.Object, patch client.
return err
}
- reaction := testing.ObjectReaction(c.tracker)
- handled, o, err := reaction(testing.NewPatchAction(gvr, accessor.GetNamespace(), accessor.GetName(), patch.Type(), data))
+ gvk, err := apiutil.GVKForObject(obj, c.scheme)
if err != nil {
return err
}
- if !handled {
- panic("tracker could not handle patch method")
+
+ oldObj, err := c.tracker.Get(gvr, accessor.GetNamespace(), accessor.GetName())
+ if err != nil {
+ return err
+ }
+ oldAccessor, err := meta.Accessor(oldObj)
+ if err != nil {
+ return err
}
- gvk, err := apiutil.GVKForObject(obj, c.scheme)
+ // Apply patch without updating object.
+ // To remain in accordance with the behavior of k8s api behavior,
+ // a patch must not allow for changes to the deletionTimestamp of an object.
+ // The reaction() function applies the patch to the object and calls Update(),
+ // whereas dryPatch() replicates this behavior but skips the call to Update().
+ // This ensures that the patch may be rejected if a deletionTimestamp is modified, prior
+ // to updating the object.
+ action := testing.NewPatchAction(gvr, accessor.GetNamespace(), accessor.GetName(), patch.Type(), data)
+ o, err := dryPatch(action, c.tracker)
if err != nil {
return err
}
+ newObj, err := meta.Accessor(o)
+ if err != nil {
+ return err
+ }
+
+ // Validate that deletionTimestamp has not been changed
+ if !deletionTimestampEqual(newObj, oldAccessor) {
+ return fmt.Errorf("rejected patch, metadata.deletionTimestamp immutable")
+ }
+
+ reaction := testing.ObjectReaction(c.tracker)
+ handled, o, err := reaction(action)
+ if err != nil {
+ return err
+ }
+ if !handled {
+ panic("tracker could not handle patch method")
+ }
ta, err := meta.TypeAccessor(o)
if err != nil {
return err
@@ -752,12 +886,178 @@ func (c *fakeClient) Patch(ctx context.Context, obj client.Object, patch client.
return err
}
+// Applying a patch results in a deletionTimestamp that is truncated to the nearest second.
+// Check that the diff between a new and old deletion timestamp is within a reasonable threshold
+// to be considered unchanged.
+func deletionTimestampEqual(newObj metav1.Object, obj metav1.Object) bool {
+ newTime := newObj.GetDeletionTimestamp()
+ oldTime := obj.GetDeletionTimestamp()
+
+ if newTime == nil || oldTime == nil {
+ return newTime == oldTime
+ }
+ return newTime.Time.Sub(oldTime.Time).Abs() < time.Second
+}
+
+// The behavior of applying the patch is pulled out into dryPatch(),
+// which applies the patch and returns an object, but does not Update() the object.
+// This function returns a patched runtime object that may then be validated before a call to Update() is executed.
+// This results in some code duplication, but was found to be a cleaner alternative than unmarshalling and introspecting the patch data
+// and easier than refactoring the k8s client-go method upstream.
+// Duplicate of upstream: https://github.com/kubernetes/client-go/blob/783d0d33626e59d55d52bfd7696b775851f92107/testing/fixture.go#L146-L194
+func dryPatch(action testing.PatchActionImpl, tracker testing.ObjectTracker) (runtime.Object, error) {
+ ns := action.GetNamespace()
+ gvr := action.GetResource()
+
+ obj, err := tracker.Get(gvr, ns, action.GetName())
+ if err != nil {
+ return nil, err
+ }
+
+ old, err := json.Marshal(obj)
+ if err != nil {
+ return nil, err
+ }
+
+ // reset the object in preparation to unmarshal, since unmarshal does not guarantee that fields
+ // in obj that are removed by patch are cleared
+ value := reflect.ValueOf(obj)
+ value.Elem().Set(reflect.New(value.Type().Elem()).Elem())
+
+ switch action.GetPatchType() {
+ case types.JSONPatchType:
+ patch, err := jsonpatch.DecodePatch(action.GetPatch())
+ if err != nil {
+ return nil, err
+ }
+ modified, err := patch.Apply(old)
+ if err != nil {
+ return nil, err
+ }
+
+ if err = json.Unmarshal(modified, obj); err != nil {
+ return nil, err
+ }
+ case types.MergePatchType:
+ modified, err := jsonpatch.MergePatch(old, action.GetPatch())
+ if err != nil {
+ return nil, err
+ }
+
+ if err := json.Unmarshal(modified, obj); err != nil {
+ return nil, err
+ }
+ case types.StrategicMergePatchType, types.ApplyPatchType:
+ mergedByte, err := strategicpatch.StrategicMergePatch(old, action.GetPatch(), obj)
+ if err != nil {
+ return nil, err
+ }
+ if err = json.Unmarshal(mergedByte, obj); err != nil {
+ return nil, err
+ }
+ default:
+ return nil, fmt.Errorf("PatchType is not supported")
+ }
+ return obj, nil
+}
+
+func copyNonStatusFrom(old, new runtime.Object) error {
+ newClientObject, ok := new.(client.Object)
+ if !ok {
+ return fmt.Errorf("%T is not a client.Object", new)
+ }
+ // The only thing other than status we have to retain
+ rv := newClientObject.GetResourceVersion()
+
+ oldMapStringAny, err := toMapStringAny(old)
+ if err != nil {
+ return fmt.Errorf("failed to convert old to *unstructured.Unstructured: %w", err)
+ }
+ newMapStringAny, err := toMapStringAny(new)
+ if err != nil {
+ return fmt.Errorf("failed to convert new to *unststructured.Unstructured: %w", err)
+ }
+
+ // delete everything other than status in case it has fields that were not present in
+ // the old object
+ for k := range newMapStringAny {
+ if k != "status" {
+ delete(newMapStringAny, k)
+ }
+ }
+ // copy everything other than status from the old object
+ for k := range oldMapStringAny {
+ if k != "status" {
+ newMapStringAny[k] = oldMapStringAny[k]
+ }
+ }
+
+ if err := fromMapStringAny(newMapStringAny, new); err != nil {
+ return fmt.Errorf("failed to convert back from map[string]any: %w", err)
+ }
+ newClientObject.SetResourceVersion(rv)
+
+ return nil
+}
+
+// copyStatusFrom copies the status from old into new
+func copyStatusFrom(old, new runtime.Object) error {
+ oldMapStringAny, err := toMapStringAny(old)
+ if err != nil {
+ return fmt.Errorf("failed to convert old to *unstructured.Unstructured: %w", err)
+ }
+ newMapStringAny, err := toMapStringAny(new)
+ if err != nil {
+ return fmt.Errorf("failed to convert new to *unststructured.Unstructured: %w", err)
+ }
+
+ newMapStringAny["status"] = oldMapStringAny["status"]
+
+ if err := fromMapStringAny(newMapStringAny, new); err != nil {
+ return fmt.Errorf("failed to convert back from map[string]any: %w", err)
+ }
+
+ return nil
+}
+
+func toMapStringAny(obj runtime.Object) (map[string]any, error) {
+ if unstructured, isUnstructured := obj.(*unstructured.Unstructured); isUnstructured {
+ return unstructured.Object, nil
+ }
+
+ serialized, err := json.Marshal(obj)
+ if err != nil {
+ return nil, err
+ }
+
+ u := map[string]any{}
+ return u, json.Unmarshal(serialized, &u)
+}
+
+func fromMapStringAny(u map[string]any, target runtime.Object) error {
+ if targetUnstructured, isUnstructured := target.(*unstructured.Unstructured); isUnstructured {
+ targetUnstructured.Object = u
+ return nil
+ }
+
+ serialized, err := json.Marshal(u)
+ if err != nil {
+ return fmt.Errorf("failed to serialize: %w", err)
+ }
+
+ if err := json.Unmarshal(serialized, &target); err != nil {
+ return fmt.Errorf("failed to deserialize: %w", err)
+ }
+
+ return nil
+}
+
func (c *fakeClient) Status() client.SubResourceWriter {
return c.SubResource("status")
}
func (c *fakeClient) SubResource(subResource string) client.SubResourceClient {
- return &fakeSubResourceClient{client: c}
+ return &fakeSubResourceClient{client: c, subResource: subResource}
}
func (c *fakeClient) deleteObject(gvr schema.GroupVersionResource, accessor metav1.Object) error {
@@ -768,7 +1068,9 @@ func (c *fakeClient) deleteObject(gvr schema.GroupVersionResource, accessor meta
if len(oldAccessor.GetFinalizers()) > 0 {
now := metav1.Now()
oldAccessor.SetDeletionTimestamp(&now)
- return c.tracker.Update(gvr, old, accessor.GetNamespace())
+ // Call update directly with mutability parameter set to true to allow
+ // changes to deletionTimestamp
+ return c.tracker.update(gvr, old, accessor.GetNamespace(), false, true)
}
}
}
@@ -787,7 +1089,8 @@ func getGVRFromObject(obj runtime.Object, scheme *runtime.Scheme) (schema.GroupV
}
type fakeSubResourceClient struct {
- client *fakeClient
+ client *fakeClient
+ subResource string
}
func (sw *fakeSubResourceClient) Get(ctx context.Context, obj, subResource client.Object, opts ...client.SubResourceGetOption) error {
@@ -795,12 +1098,26 @@ func (sw *fakeSubResourceClient) Get(ctx context.Context, obj, subResource clien
}
func (sw *fakeSubResourceClient) Create(ctx context.Context, obj client.Object, subResource client.Object, opts ...client.SubResourceCreateOption) error {
- panic("fakeSubResourceWriter does not support create")
+ switch sw.subResource {
+ case "eviction":
+ _, isEviction := subResource.(*policyv1beta1.Eviction)
+ if !isEviction {
+ _, isEviction = subResource.(*policyv1.Eviction)
+ }
+ if !isEviction {
+ return apierrors.NewBadRequest(fmt.Sprintf("got invalid type %t, expected Eviction", subResource))
+ }
+ if _, isPod := obj.(*corev1.Pod); !isPod {
+ return apierrors.NewNotFound(schema.GroupResource{}, "")
+ }
+
+ return sw.client.Delete(ctx, obj)
+ default:
+ return fmt.Errorf("fakeSubResourceWriter does not support create for %s", sw.subResource)
+ }
}
func (sw *fakeSubResourceClient) Update(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error {
- // TODO(droot): This results in full update of the obj (spec + subresources). Need
- // a way to update subresource only.
updateOptions := client.SubResourceUpdateOptions{}
updateOptions.ApplyOptions(opts)
@@ -808,13 +1125,10 @@ func (sw *fakeSubResourceClient) Update(ctx context.Context, obj client.Object,
if updateOptions.SubResourceBody != nil {
body = updateOptions.SubResourceBody
}
- return sw.client.Update(ctx, body, &updateOptions.UpdateOptions)
+ return sw.client.update(body, true, &updateOptions.UpdateOptions)
}
func (sw *fakeSubResourceClient) Patch(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.SubResourcePatchOption) error {
- // TODO(droot): This results in full update of the obj (spec + subresources). Need
- // a way to update subresource only.
-
patchOptions := client.SubResourcePatchOptions{}
patchOptions.ApplyOptions(opts)
@@ -823,7 +1137,7 @@ func (sw *fakeSubResourceClient) Patch(ctx context.Context, obj client.Object, p
body = patchOptions.SubResourceBody
}
- return sw.client.Patch(ctx, body, patch, &patchOptions.PatchOptions)
+ return sw.client.patch(body, patch, &patchOptions.PatchOptions)
}
func allowsUnconditionalUpdate(gvk schema.GroupVersionKind) bool {
@@ -923,6 +1237,42 @@ func allowsCreateOnUpdate(gvk schema.GroupVersionKind) bool {
return false
}
+func inTreeResourcesWithStatus() []schema.GroupVersionKind {
+ return []schema.GroupVersionKind{
+ {Version: "v1", Kind: "Namespace"},
+ {Version: "v1", Kind: "Node"},
+ {Version: "v1", Kind: "PersistentVolumeClaim"},
+ {Version: "v1", Kind: "PersistentVolume"},
+ {Version: "v1", Kind: "Pod"},
+ {Version: "v1", Kind: "ReplicationController"},
+ {Version: "v1", Kind: "Service"},
+
+ {Group: "apps", Version: "v1", Kind: "Deployment"},
+ {Group: "apps", Version: "v1", Kind: "DaemonSet"},
+ {Group: "apps", Version: "v1", Kind: "ReplicaSet"},
+ {Group: "apps", Version: "v1", Kind: "StatefulSet"},
+
+ {Group: "autoscaling", Version: "v1", Kind: "HorizontalPodAutoscaler"},
+
+ {Group: "batch", Version: "v1", Kind: "CronJob"},
+ {Group: "batch", Version: "v1", Kind: "Job"},
+
+ {Group: "certificates.k8s.io", Version: "v1", Kind: "CertificateSigningRequest"},
+
+ {Group: "networking.k8s.io", Version: "v1", Kind: "Ingress"},
+ {Group: "networking.k8s.io", Version: "v1", Kind: "NetworkPolicy"},
+
+ {Group: "policy", Version: "v1", Kind: "PodDisruptionBudget"},
+
+ {Group: "storage.k8s.io", Version: "v1", Kind: "VolumeAttachment"},
+
+ {Group: "apiextensions.k8s.io", Version: "v1", Kind: "CustomResourceDefinition"},
+
+ {Group: "flowcontrol.apiserver.k8s.io", Version: "v1beta2", Kind: "FlowSchema"},
+ {Group: "flowcontrol.apiserver.k8s.io", Version: "v1beta2", Kind: "PriorityLevelConfiguration"},
+ }
+}
+
// zero zeros the value of a pointer.
func zero(x interface{}) {
if x == nil {
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/interceptor/intercept.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/interceptor/intercept.go
new file mode 100644
index 000000000..3d3f3cb01
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/interceptor/intercept.go
@@ -0,0 +1,166 @@
+package interceptor
+
+import (
+ "context"
+
+ "k8s.io/apimachinery/pkg/api/meta"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "k8s.io/apimachinery/pkg/watch"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+)
+
+// Funcs contains functions that are called instead of the underlying client's methods.
+type Funcs struct {
+ Get func(ctx context.Context, client client.WithWatch, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error
+ List func(ctx context.Context, client client.WithWatch, list client.ObjectList, opts ...client.ListOption) error
+ Create func(ctx context.Context, client client.WithWatch, obj client.Object, opts ...client.CreateOption) error
+ Delete func(ctx context.Context, client client.WithWatch, obj client.Object, opts ...client.DeleteOption) error
+ DeleteAllOf func(ctx context.Context, client client.WithWatch, obj client.Object, opts ...client.DeleteAllOfOption) error
+ Update func(ctx context.Context, client client.WithWatch, obj client.Object, opts ...client.UpdateOption) error
+ Patch func(ctx context.Context, client client.WithWatch, obj client.Object, patch client.Patch, opts ...client.PatchOption) error
+ Watch func(ctx context.Context, client client.WithWatch, obj client.ObjectList, opts ...client.ListOption) (watch.Interface, error)
+ SubResource func(client client.WithWatch, subResource string) client.SubResourceClient
+ SubResourceGet func(ctx context.Context, client client.Client, subResourceName string, obj client.Object, subResource client.Object, opts ...client.SubResourceGetOption) error
+ SubResourceCreate func(ctx context.Context, client client.Client, subResourceName string, obj client.Object, subResource client.Object, opts ...client.SubResourceCreateOption) error
+ SubResourceUpdate func(ctx context.Context, client client.Client, subResourceName string, obj client.Object, opts ...client.SubResourceUpdateOption) error
+ SubResourcePatch func(ctx context.Context, client client.Client, subResourceName string, obj client.Object, patch client.Patch, opts ...client.SubResourcePatchOption) error
+}
+
+// NewClient returns a new interceptor client that calls the functions in funcs instead of the underlying client's methods, if they are not nil.
+func NewClient(interceptedClient client.WithWatch, funcs Funcs) client.WithWatch {
+ return interceptor{
+ client: interceptedClient,
+ funcs: funcs,
+ }
+}
+
+type interceptor struct {
+ client client.WithWatch
+ funcs Funcs
+}
+
+var _ client.WithWatch = &interceptor{}
+
+func (c interceptor) GroupVersionKindFor(obj runtime.Object) (schema.GroupVersionKind, error) {
+ return c.client.GroupVersionKindFor(obj)
+}
+
+func (c interceptor) IsObjectNamespaced(obj runtime.Object) (bool, error) {
+ return c.client.IsObjectNamespaced(obj)
+}
+
+func (c interceptor) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error {
+ if c.funcs.Get != nil {
+ return c.funcs.Get(ctx, c.client, key, obj, opts...)
+ }
+ return c.client.Get(ctx, key, obj, opts...)
+}
+
+func (c interceptor) List(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error {
+ if c.funcs.List != nil {
+ return c.funcs.List(ctx, c.client, list, opts...)
+ }
+ return c.client.List(ctx, list, opts...)
+}
+
+func (c interceptor) Create(ctx context.Context, obj client.Object, opts ...client.CreateOption) error {
+ if c.funcs.Create != nil {
+ return c.funcs.Create(ctx, c.client, obj, opts...)
+ }
+ return c.client.Create(ctx, obj, opts...)
+}
+
+func (c interceptor) Delete(ctx context.Context, obj client.Object, opts ...client.DeleteOption) error {
+ if c.funcs.Delete != nil {
+ return c.funcs.Delete(ctx, c.client, obj, opts...)
+ }
+ return c.client.Delete(ctx, obj, opts...)
+}
+
+func (c interceptor) Update(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error {
+ if c.funcs.Update != nil {
+ return c.funcs.Update(ctx, c.client, obj, opts...)
+ }
+ return c.client.Update(ctx, obj, opts...)
+}
+
+func (c interceptor) Patch(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.PatchOption) error {
+ if c.funcs.Patch != nil {
+ return c.funcs.Patch(ctx, c.client, obj, patch, opts...)
+ }
+ return c.client.Patch(ctx, obj, patch, opts...)
+}
+
+func (c interceptor) DeleteAllOf(ctx context.Context, obj client.Object, opts ...client.DeleteAllOfOption) error {
+ if c.funcs.DeleteAllOf != nil {
+ return c.funcs.DeleteAllOf(ctx, c.client, obj, opts...)
+ }
+ return c.client.DeleteAllOf(ctx, obj, opts...)
+}
+
+func (c interceptor) Status() client.SubResourceWriter {
+ return c.SubResource("status")
+}
+
+func (c interceptor) SubResource(subResource string) client.SubResourceClient {
+ if c.funcs.SubResource != nil {
+ return c.funcs.SubResource(c.client, subResource)
+ }
+ return subResourceInterceptor{
+ subResourceName: subResource,
+ client: c.client,
+ funcs: c.funcs,
+ }
+}
+
+func (c interceptor) Scheme() *runtime.Scheme {
+ return c.client.Scheme()
+}
+
+func (c interceptor) RESTMapper() meta.RESTMapper {
+ return c.client.RESTMapper()
+}
+
+func (c interceptor) Watch(ctx context.Context, obj client.ObjectList, opts ...client.ListOption) (watch.Interface, error) {
+ if c.funcs.Watch != nil {
+ return c.funcs.Watch(ctx, c.client, obj, opts...)
+ }
+ return c.client.Watch(ctx, obj, opts...)
+}
+
+type subResourceInterceptor struct {
+ subResourceName string
+ client client.Client
+ funcs Funcs
+}
+
+var _ client.SubResourceClient = &subResourceInterceptor{}
+
+func (s subResourceInterceptor) Get(ctx context.Context, obj client.Object, subResource client.Object, opts ...client.SubResourceGetOption) error {
+ if s.funcs.SubResourceGet != nil {
+ return s.funcs.SubResourceGet(ctx, s.client, s.subResourceName, obj, subResource, opts...)
+ }
+ return s.client.SubResource(s.subResourceName).Get(ctx, obj, subResource, opts...)
+}
+
+func (s subResourceInterceptor) Create(ctx context.Context, obj client.Object, subResource client.Object, opts ...client.SubResourceCreateOption) error {
+ if s.funcs.SubResourceCreate != nil {
+ return s.funcs.SubResourceCreate(ctx, s.client, s.subResourceName, obj, subResource, opts...)
+ }
+ return s.client.SubResource(s.subResourceName).Create(ctx, obj, subResource, opts...)
+}
+
+func (s subResourceInterceptor) Update(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error {
+ if s.funcs.SubResourceUpdate != nil {
+ return s.funcs.SubResourceUpdate(ctx, s.client, s.subResourceName, obj, opts...)
+ }
+ return s.client.SubResource(s.subResourceName).Update(ctx, obj, opts...)
+}
+
+func (s subResourceInterceptor) Patch(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.SubResourcePatchOption) error {
+ if s.funcs.SubResourcePatch != nil {
+ return s.funcs.SubResourcePatch(ctx, s.client, s.subResourceName, obj, patch, opts...)
+ }
+ return s.client.SubResource(s.subResourceName).Patch(ctx, obj, patch, opts...)
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/interfaces.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/interfaces.go
index b642f7f88..0ddda3163 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/client/interfaces.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/interfaces.go
@@ -20,6 +20,7 @@ import (
"context"
apierrors "k8s.io/apimachinery/pkg/api/errors"
+ "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
@@ -169,6 +170,10 @@ type Client interface {
Scheme() *runtime.Scheme
// RESTMapper returns the rest this client is using.
RESTMapper() meta.RESTMapper
+ // GroupVersionKindFor returns the GroupVersionKind for the given object.
+ GroupVersionKindFor(obj runtime.Object) (schema.GroupVersionKind, error)
+ // IsObjectNamespaced returns true if the GroupVersionKind of the object is namespaced.
+ IsObjectNamespaced(obj runtime.Object) (bool, error)
}
// WithWatch supports Watch on top of the CRUD operations supported by
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/namespaced_client.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/namespaced_client.go
index 00bc2175c..222dc7957 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/client/namespaced_client.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/namespaced_client.go
@@ -22,7 +22,7 @@ import (
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
- "sigs.k8s.io/controller-runtime/pkg/internal/objectutil"
+ "k8s.io/apimachinery/pkg/runtime/schema"
)
// NewNamespacedClient wraps an existing client enforcing the namespace value.
@@ -52,9 +52,19 @@ func (n *namespacedClient) RESTMapper() meta.RESTMapper {
return n.client.RESTMapper()
}
+// GroupVersionKindFor returns the GroupVersionKind for the given object.
+func (n *namespacedClient) GroupVersionKindFor(obj runtime.Object) (schema.GroupVersionKind, error) {
+ return n.client.GroupVersionKindFor(obj)
+}
+
+// IsObjectNamespaced returns true if the GroupVersionKind of the object is namespaced.
+func (n *namespacedClient) IsObjectNamespaced(obj runtime.Object) (bool, error) {
+ return n.client.IsObjectNamespaced(obj)
+}
+
// Create implements client.Client.
func (n *namespacedClient) Create(ctx context.Context, obj Object, opts ...CreateOption) error {
- isNamespaceScoped, err := objectutil.IsAPINamespaced(obj, n.Scheme(), n.RESTMapper())
+ isNamespaceScoped, err := n.IsObjectNamespaced(obj)
if err != nil {
return fmt.Errorf("error finding the scope of the object: %w", err)
}
@@ -72,7 +82,7 @@ func (n *namespacedClient) Create(ctx context.Context, obj Object, opts ...Creat
// Update implements client.Client.
func (n *namespacedClient) Update(ctx context.Context, obj Object, opts ...UpdateOption) error {
- isNamespaceScoped, err := objectutil.IsAPINamespaced(obj, n.Scheme(), n.RESTMapper())
+ isNamespaceScoped, err := n.IsObjectNamespaced(obj)
if err != nil {
return fmt.Errorf("error finding the scope of the object: %w", err)
}
@@ -90,7 +100,7 @@ func (n *namespacedClient) Update(ctx context.Context, obj Object, opts ...Updat
// Delete implements client.Client.
func (n *namespacedClient) Delete(ctx context.Context, obj Object, opts ...DeleteOption) error {
- isNamespaceScoped, err := objectutil.IsAPINamespaced(obj, n.Scheme(), n.RESTMapper())
+ isNamespaceScoped, err := n.IsObjectNamespaced(obj)
if err != nil {
return fmt.Errorf("error finding the scope of the object: %w", err)
}
@@ -108,7 +118,7 @@ func (n *namespacedClient) Delete(ctx context.Context, obj Object, opts ...Delet
// DeleteAllOf implements client.Client.
func (n *namespacedClient) DeleteAllOf(ctx context.Context, obj Object, opts ...DeleteAllOfOption) error {
- isNamespaceScoped, err := objectutil.IsAPINamespaced(obj, n.Scheme(), n.RESTMapper())
+ isNamespaceScoped, err := n.IsObjectNamespaced(obj)
if err != nil {
return fmt.Errorf("error finding the scope of the object: %w", err)
}
@@ -121,7 +131,7 @@ func (n *namespacedClient) DeleteAllOf(ctx context.Context, obj Object, opts ...
// Patch implements client.Client.
func (n *namespacedClient) Patch(ctx context.Context, obj Object, patch Patch, opts ...PatchOption) error {
- isNamespaceScoped, err := objectutil.IsAPINamespaced(obj, n.Scheme(), n.RESTMapper())
+ isNamespaceScoped, err := n.IsObjectNamespaced(obj)
if err != nil {
return fmt.Errorf("error finding the scope of the object: %w", err)
}
@@ -139,7 +149,7 @@ func (n *namespacedClient) Patch(ctx context.Context, obj Object, patch Patch, o
// Get implements client.Client.
func (n *namespacedClient) Get(ctx context.Context, key ObjectKey, obj Object, opts ...GetOption) error {
- isNamespaceScoped, err := objectutil.IsAPINamespaced(obj, n.Scheme(), n.RESTMapper())
+ isNamespaceScoped, err := n.IsObjectNamespaced(obj)
if err != nil {
return fmt.Errorf("error finding the scope of the object: %w", err)
}
@@ -180,7 +190,7 @@ type namespacedClientSubResourceClient struct {
}
func (nsw *namespacedClientSubResourceClient) Get(ctx context.Context, obj, subResource Object, opts ...SubResourceGetOption) error {
- isNamespaceScoped, err := objectutil.IsAPINamespaced(obj, nsw.namespacedclient.Scheme(), nsw.namespacedclient.RESTMapper())
+ isNamespaceScoped, err := nsw.namespacedclient.IsObjectNamespaced(obj)
if err != nil {
return fmt.Errorf("error finding the scope of the object: %w", err)
}
@@ -198,7 +208,7 @@ func (nsw *namespacedClientSubResourceClient) Get(ctx context.Context, obj, subR
}
func (nsw *namespacedClientSubResourceClient) Create(ctx context.Context, obj, subResource Object, opts ...SubResourceCreateOption) error {
- isNamespaceScoped, err := objectutil.IsAPINamespaced(obj, nsw.namespacedclient.Scheme(), nsw.namespacedclient.RESTMapper())
+ isNamespaceScoped, err := nsw.namespacedclient.IsObjectNamespaced(obj)
if err != nil {
return fmt.Errorf("error finding the scope of the object: %w", err)
}
@@ -217,7 +227,7 @@ func (nsw *namespacedClientSubResourceClient) Create(ctx context.Context, obj, s
// Update implements client.SubResourceWriter.
func (nsw *namespacedClientSubResourceClient) Update(ctx context.Context, obj Object, opts ...SubResourceUpdateOption) error {
- isNamespaceScoped, err := objectutil.IsAPINamespaced(obj, nsw.namespacedclient.Scheme(), nsw.namespacedclient.RESTMapper())
+ isNamespaceScoped, err := nsw.namespacedclient.IsObjectNamespaced(obj)
if err != nil {
return fmt.Errorf("error finding the scope of the object: %w", err)
}
@@ -235,8 +245,7 @@ func (nsw *namespacedClientSubResourceClient) Update(ctx context.Context, obj Ob
// Patch implements client.SubResourceWriter.
func (nsw *namespacedClientSubResourceClient) Patch(ctx context.Context, obj Object, patch Patch, opts ...SubResourcePatchOption) error {
- isNamespaceScoped, err := objectutil.IsAPINamespaced(obj, nsw.namespacedclient.Scheme(), nsw.namespacedclient.RESTMapper())
-
+ isNamespaceScoped, err := nsw.namespacedclient.IsObjectNamespaced(obj)
if err != nil {
return fmt.Errorf("error finding the scope of the object: %w", err)
}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/options.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/options.go
index 7f6f5b83f..d81bf25de 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/client/options.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/options.go
@@ -513,8 +513,15 @@ type MatchingLabels map[string]string
// ApplyToList applies this configuration to the given list options.
func (m MatchingLabels) ApplyToList(opts *ListOptions) {
// TODO(directxman12): can we avoid reserializing this over and over?
- sel := labels.SelectorFromValidatedSet(map[string]string(m))
- opts.LabelSelector = sel
+ if opts.LabelSelector == nil {
+ opts.LabelSelector = labels.NewSelector()
+ }
+ // If there's already a selector, we need to AND the two together.
+ noValidSel := labels.SelectorFromValidatedSet(map[string]string(m))
+ reqs, _ := noValidSel.Requirements()
+ for _, req := range reqs {
+ opts.LabelSelector = opts.LabelSelector.Add(req)
+ }
}
// ApplyToDeleteAllOf applies this configuration to the given an List options.
@@ -528,14 +535,17 @@ type HasLabels []string
// ApplyToList applies this configuration to the given list options.
func (m HasLabels) ApplyToList(opts *ListOptions) {
- sel := labels.NewSelector()
+ if opts.LabelSelector == nil {
+ opts.LabelSelector = labels.NewSelector()
+ }
+ // TODO: ignore invalid labels will result in an empty selector.
+ // This is inconsistent to the that of MatchingLabels.
for _, label := range m {
r, err := labels.NewRequirement(label, selection.Exists, nil)
if err == nil {
- sel = sel.Add(*r)
+ opts.LabelSelector = opts.LabelSelector.Add(*r)
}
}
- opts.LabelSelector = sel
}
// ApplyToDeleteAllOf applies this configuration to the given an List options.
@@ -606,6 +616,11 @@ func (n InNamespace) ApplyToDeleteAllOf(opts *DeleteAllOfOptions) {
n.ApplyToList(&opts.ListOptions)
}
+// AsSelector returns a selector that matches objects in the given namespace.
+func (n InNamespace) AsSelector() fields.Selector {
+ return fields.SelectorFromSet(fields.Set{"metadata.namespace": string(n)})
+}
+
// Limit specifies the maximum number of results to return from the server.
// Limit does not implement DeleteAllOfOption interface because the server
// does not support setting it for deletecollection operations.
@@ -788,6 +803,11 @@ func (forceOwnership) ApplyToPatch(opts *PatchOptions) {
opts.Force = &definitelyTrue
}
+func (forceOwnership) ApplyToSubResourcePatch(opts *SubResourcePatchOptions) {
+ definitelyTrue := true
+ opts.Force = &definitelyTrue
+}
+
// }}}
// {{{ DeleteAllOf Options
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/split.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/split.go
deleted file mode 100644
index 19d1ab4db..000000000
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/client/split.go
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
-Copyright 2018 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 client
-
-import (
- "context"
- "strings"
-
- "k8s.io/apimachinery/pkg/api/meta"
- "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
- "k8s.io/apimachinery/pkg/runtime"
- "k8s.io/apimachinery/pkg/runtime/schema"
-
- "sigs.k8s.io/controller-runtime/pkg/client/apiutil"
-)
-
-// NewDelegatingClientInput encapsulates the input parameters to create a new delegating client.
-type NewDelegatingClientInput struct {
- CacheReader Reader
- Client Client
- UncachedObjects []Object
- CacheUnstructured bool
-}
-
-// NewDelegatingClient creates a new delegating client.
-//
-// A delegating client forms a Client by composing separate reader, writer and
-// statusclient interfaces. This way, you can have an Client that reads from a
-// cache and writes to the API server.
-func NewDelegatingClient(in NewDelegatingClientInput) (Client, error) {
- uncachedGVKs := map[schema.GroupVersionKind]struct{}{}
- for _, obj := range in.UncachedObjects {
- gvk, err := apiutil.GVKForObject(obj, in.Client.Scheme())
- if err != nil {
- return nil, err
- }
- uncachedGVKs[gvk] = struct{}{}
- }
-
- return &delegatingClient{
- scheme: in.Client.Scheme(),
- mapper: in.Client.RESTMapper(),
- Reader: &delegatingReader{
- CacheReader: in.CacheReader,
- ClientReader: in.Client,
- scheme: in.Client.Scheme(),
- uncachedGVKs: uncachedGVKs,
- cacheUnstructured: in.CacheUnstructured,
- },
- Writer: in.Client,
- StatusClient: in.Client,
- SubResourceClientConstructor: in.Client,
- }, nil
-}
-
-type delegatingClient struct {
- Reader
- Writer
- StatusClient
- SubResourceClientConstructor
-
- scheme *runtime.Scheme
- mapper meta.RESTMapper
-}
-
-// Scheme returns the scheme this client is using.
-func (d *delegatingClient) Scheme() *runtime.Scheme {
- return d.scheme
-}
-
-// RESTMapper returns the rest mapper this client is using.
-func (d *delegatingClient) RESTMapper() meta.RESTMapper {
- return d.mapper
-}
-
-// delegatingReader forms a Reader that will cause Get and List requests for
-// unstructured types to use the ClientReader while requests for any other type
-// of object with use the CacheReader. This avoids accidentally caching the
-// entire cluster in the common case of loading arbitrary unstructured objects
-// (e.g. from OwnerReferences).
-type delegatingReader struct {
- CacheReader Reader
- ClientReader Reader
-
- uncachedGVKs map[schema.GroupVersionKind]struct{}
- scheme *runtime.Scheme
- cacheUnstructured bool
-}
-
-func (d *delegatingReader) shouldBypassCache(obj runtime.Object) (bool, error) {
- gvk, err := apiutil.GVKForObject(obj, d.scheme)
- if err != nil {
- return false, err
- }
- // TODO: this is producing unsafe guesses that don't actually work,
- // but it matches ~99% of the cases out there.
- if meta.IsListType(obj) {
- gvk.Kind = strings.TrimSuffix(gvk.Kind, "List")
- }
- if _, isUncached := d.uncachedGVKs[gvk]; isUncached {
- return true, nil
- }
- if !d.cacheUnstructured {
- _, isUnstructured := obj.(*unstructured.Unstructured)
- _, isUnstructuredList := obj.(*unstructured.UnstructuredList)
- return isUnstructured || isUnstructuredList, nil
- }
- return false, nil
-}
-
-// Get retrieves an obj for a given object key from the Kubernetes Cluster.
-func (d *delegatingReader) Get(ctx context.Context, key ObjectKey, obj Object, opts ...GetOption) error {
- if isUncached, err := d.shouldBypassCache(obj); err != nil {
- return err
- } else if isUncached {
- return d.ClientReader.Get(ctx, key, obj, opts...)
- }
- return d.CacheReader.Get(ctx, key, obj, opts...)
-}
-
-// List retrieves list of objects for a given namespace and list options.
-func (d *delegatingReader) List(ctx context.Context, list ObjectList, opts ...ListOption) error {
- if isUncached, err := d.shouldBypassCache(list); err != nil {
- return err
- } else if isUncached {
- return d.ClientReader.List(ctx, list, opts...)
- }
- return d.CacheReader.List(ctx, list, opts...)
-}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/typed_client.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/typed_client.go
index ade251572..92afd9a9c 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/client/typed_client.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/typed_client.go
@@ -25,16 +25,14 @@ import (
var _ Reader = &typedClient{}
var _ Writer = &typedClient{}
-// client is a client.Client that reads and writes directly from/to an API server. It lazily initializes
-// new clients at the time they are used, and caches the client.
type typedClient struct {
- cache *clientCache
+ resources *clientRestResources
paramCodec runtime.ParameterCodec
}
// Create implements client.Client.
func (c *typedClient) Create(ctx context.Context, obj Object, opts ...CreateOption) error {
- o, err := c.cache.getObjMeta(obj)
+ o, err := c.resources.getObjMeta(obj)
if err != nil {
return err
}
@@ -53,7 +51,7 @@ func (c *typedClient) Create(ctx context.Context, obj Object, opts ...CreateOpti
// Update implements client.Client.
func (c *typedClient) Update(ctx context.Context, obj Object, opts ...UpdateOption) error {
- o, err := c.cache.getObjMeta(obj)
+ o, err := c.resources.getObjMeta(obj)
if err != nil {
return err
}
@@ -73,7 +71,7 @@ func (c *typedClient) Update(ctx context.Context, obj Object, opts ...UpdateOpti
// Delete implements client.Client.
func (c *typedClient) Delete(ctx context.Context, obj Object, opts ...DeleteOption) error {
- o, err := c.cache.getObjMeta(obj)
+ o, err := c.resources.getObjMeta(obj)
if err != nil {
return err
}
@@ -92,7 +90,7 @@ func (c *typedClient) Delete(ctx context.Context, obj Object, opts ...DeleteOpti
// DeleteAllOf implements client.Client.
func (c *typedClient) DeleteAllOf(ctx context.Context, obj Object, opts ...DeleteAllOfOption) error {
- o, err := c.cache.getObjMeta(obj)
+ o, err := c.resources.getObjMeta(obj)
if err != nil {
return err
}
@@ -111,7 +109,7 @@ func (c *typedClient) DeleteAllOf(ctx context.Context, obj Object, opts ...Delet
// Patch implements client.Client.
func (c *typedClient) Patch(ctx context.Context, obj Object, patch Patch, opts ...PatchOption) error {
- o, err := c.cache.getObjMeta(obj)
+ o, err := c.resources.getObjMeta(obj)
if err != nil {
return err
}
@@ -136,7 +134,7 @@ func (c *typedClient) Patch(ctx context.Context, obj Object, patch Patch, opts .
// Get implements client.Client.
func (c *typedClient) Get(ctx context.Context, key ObjectKey, obj Object, opts ...GetOption) error {
- r, err := c.cache.getResource(obj)
+ r, err := c.resources.getResource(obj)
if err != nil {
return err
}
@@ -151,7 +149,7 @@ func (c *typedClient) Get(ctx context.Context, key ObjectKey, obj Object, opts .
// List implements client.Client.
func (c *typedClient) List(ctx context.Context, obj ObjectList, opts ...ListOption) error {
- r, err := c.cache.getResource(obj)
+ r, err := c.resources.getResource(obj)
if err != nil {
return err
}
@@ -168,7 +166,7 @@ func (c *typedClient) List(ctx context.Context, obj ObjectList, opts ...ListOpti
}
func (c *typedClient) GetSubResource(ctx context.Context, obj, subResourceObj Object, subResource string, opts ...SubResourceGetOption) error {
- o, err := c.cache.getObjMeta(obj)
+ o, err := c.resources.getObjMeta(obj)
if err != nil {
return err
}
@@ -191,7 +189,7 @@ func (c *typedClient) GetSubResource(ctx context.Context, obj, subResourceObj Ob
}
func (c *typedClient) CreateSubResource(ctx context.Context, obj Object, subResourceObj Object, subResource string, opts ...SubResourceCreateOption) error {
- o, err := c.cache.getObjMeta(obj)
+ o, err := c.resources.getObjMeta(obj)
if err != nil {
return err
}
@@ -216,7 +214,7 @@ func (c *typedClient) CreateSubResource(ctx context.Context, obj Object, subReso
// UpdateSubResource used by SubResourceWriter to write status.
func (c *typedClient) UpdateSubResource(ctx context.Context, obj Object, subResource string, opts ...SubResourceUpdateOption) error {
- o, err := c.cache.getObjMeta(obj)
+ o, err := c.resources.getObjMeta(obj)
if err != nil {
return err
}
@@ -251,7 +249,7 @@ func (c *typedClient) UpdateSubResource(ctx context.Context, obj Object, subReso
// PatchSubResource used by SubResourceWriter to write subresource.
func (c *typedClient) PatchSubResource(ctx context.Context, obj Object, subResource string, patch Patch, opts ...SubResourcePatchOption) error {
- o, err := c.cache.getObjMeta(obj)
+ o, err := c.resources.getObjMeta(obj)
if err != nil {
return err
}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/unstructured_client.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/unstructured_client.go
index 7f25c7be9..0d9695178 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/client/unstructured_client.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/unstructured_client.go
@@ -21,30 +21,27 @@ import (
"fmt"
"strings"
- "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
)
var _ Reader = &unstructuredClient{}
var _ Writer = &unstructuredClient{}
-// client is a client.Client that reads and writes directly from/to an API server. It lazily initializes
-// new clients at the time they are used, and caches the client.
type unstructuredClient struct {
- cache *clientCache
+ resources *clientRestResources
paramCodec runtime.ParameterCodec
}
// Create implements client.Client.
func (uc *unstructuredClient) Create(ctx context.Context, obj Object, opts ...CreateOption) error {
- u, ok := obj.(*unstructured.Unstructured)
+ u, ok := obj.(runtime.Unstructured)
if !ok {
return fmt.Errorf("unstructured client did not understand object: %T", obj)
}
- gvk := u.GroupVersionKind()
+ gvk := u.GetObjectKind().GroupVersionKind()
- o, err := uc.cache.getObjMeta(obj)
+ o, err := uc.resources.getObjMeta(obj)
if err != nil {
return err
}
@@ -60,20 +57,20 @@ func (uc *unstructuredClient) Create(ctx context.Context, obj Object, opts ...Cr
Do(ctx).
Into(obj)
- u.SetGroupVersionKind(gvk)
+ u.GetObjectKind().SetGroupVersionKind(gvk)
return result
}
// Update implements client.Client.
func (uc *unstructuredClient) Update(ctx context.Context, obj Object, opts ...UpdateOption) error {
- u, ok := obj.(*unstructured.Unstructured)
+ u, ok := obj.(runtime.Unstructured)
if !ok {
return fmt.Errorf("unstructured client did not understand object: %T", obj)
}
- gvk := u.GroupVersionKind()
+ gvk := u.GetObjectKind().GroupVersionKind()
- o, err := uc.cache.getObjMeta(obj)
+ o, err := uc.resources.getObjMeta(obj)
if err != nil {
return err
}
@@ -90,17 +87,17 @@ func (uc *unstructuredClient) Update(ctx context.Context, obj Object, opts ...Up
Do(ctx).
Into(obj)
- u.SetGroupVersionKind(gvk)
+ u.GetObjectKind().SetGroupVersionKind(gvk)
return result
}
// Delete implements client.Client.
func (uc *unstructuredClient) Delete(ctx context.Context, obj Object, opts ...DeleteOption) error {
- if _, ok := obj.(*unstructured.Unstructured); !ok {
+ if _, ok := obj.(runtime.Unstructured); !ok {
return fmt.Errorf("unstructured client did not understand object: %T", obj)
}
- o, err := uc.cache.getObjMeta(obj)
+ o, err := uc.resources.getObjMeta(obj)
if err != nil {
return err
}
@@ -119,11 +116,11 @@ func (uc *unstructuredClient) Delete(ctx context.Context, obj Object, opts ...De
// DeleteAllOf implements client.Client.
func (uc *unstructuredClient) DeleteAllOf(ctx context.Context, obj Object, opts ...DeleteAllOfOption) error {
- if _, ok := obj.(*unstructured.Unstructured); !ok {
+ if _, ok := obj.(runtime.Unstructured); !ok {
return fmt.Errorf("unstructured client did not understand object: %T", obj)
}
- o, err := uc.cache.getObjMeta(obj)
+ o, err := uc.resources.getObjMeta(obj)
if err != nil {
return err
}
@@ -142,11 +139,11 @@ func (uc *unstructuredClient) DeleteAllOf(ctx context.Context, obj Object, opts
// Patch implements client.Client.
func (uc *unstructuredClient) Patch(ctx context.Context, obj Object, patch Patch, opts ...PatchOption) error {
- if _, ok := obj.(*unstructured.Unstructured); !ok {
+ if _, ok := obj.(runtime.Unstructured); !ok {
return fmt.Errorf("unstructured client did not understand object: %T", obj)
}
- o, err := uc.cache.getObjMeta(obj)
+ o, err := uc.resources.getObjMeta(obj)
if err != nil {
return err
}
@@ -171,17 +168,17 @@ func (uc *unstructuredClient) Patch(ctx context.Context, obj Object, patch Patch
// Get implements client.Client.
func (uc *unstructuredClient) Get(ctx context.Context, key ObjectKey, obj Object, opts ...GetOption) error {
- u, ok := obj.(*unstructured.Unstructured)
+ u, ok := obj.(runtime.Unstructured)
if !ok {
return fmt.Errorf("unstructured client did not understand object: %T", obj)
}
- gvk := u.GroupVersionKind()
+ gvk := u.GetObjectKind().GroupVersionKind()
getOpts := GetOptions{}
getOpts.ApplyOptions(opts)
- r, err := uc.cache.getResource(obj)
+ r, err := uc.resources.getResource(obj)
if err != nil {
return err
}
@@ -194,22 +191,22 @@ func (uc *unstructuredClient) Get(ctx context.Context, key ObjectKey, obj Object
Do(ctx).
Into(obj)
- u.SetGroupVersionKind(gvk)
+ u.GetObjectKind().SetGroupVersionKind(gvk)
return result
}
// List implements client.Client.
func (uc *unstructuredClient) List(ctx context.Context, obj ObjectList, opts ...ListOption) error {
- u, ok := obj.(*unstructured.UnstructuredList)
+ u, ok := obj.(runtime.Unstructured)
if !ok {
return fmt.Errorf("unstructured client did not understand object: %T", obj)
}
- gvk := u.GroupVersionKind()
+ gvk := u.GetObjectKind().GroupVersionKind()
gvk.Kind = strings.TrimSuffix(gvk.Kind, "List")
- r, err := uc.cache.getResource(obj)
+ r, err := uc.resources.getResource(obj)
if err != nil {
return err
}
@@ -226,19 +223,19 @@ func (uc *unstructuredClient) List(ctx context.Context, obj ObjectList, opts ...
}
func (uc *unstructuredClient) GetSubResource(ctx context.Context, obj, subResourceObj Object, subResource string, opts ...SubResourceGetOption) error {
- if _, ok := obj.(*unstructured.Unstructured); !ok {
- return fmt.Errorf("unstructured client did not understand object: %T", subResource)
+ if _, ok := obj.(runtime.Unstructured); !ok {
+ return fmt.Errorf("unstructured client did not understand object: %T", obj)
}
- if _, ok := subResourceObj.(*unstructured.Unstructured); !ok {
- return fmt.Errorf("unstructured client did not understand object: %T", obj)
+ if _, ok := subResourceObj.(runtime.Unstructured); !ok {
+ return fmt.Errorf("unstructured client did not understand object: %T", subResourceObj)
}
if subResourceObj.GetName() == "" {
subResourceObj.SetName(obj.GetName())
}
- o, err := uc.cache.getObjMeta(obj)
+ o, err := uc.resources.getObjMeta(obj)
if err != nil {
return err
}
@@ -257,19 +254,19 @@ func (uc *unstructuredClient) GetSubResource(ctx context.Context, obj, subResour
}
func (uc *unstructuredClient) CreateSubResource(ctx context.Context, obj, subResourceObj Object, subResource string, opts ...SubResourceCreateOption) error {
- if _, ok := obj.(*unstructured.Unstructured); !ok {
- return fmt.Errorf("unstructured client did not understand object: %T", subResourceObj)
+ if _, ok := obj.(runtime.Unstructured); !ok {
+ return fmt.Errorf("unstructured client did not understand object: %T", obj)
}
- if _, ok := subResourceObj.(*unstructured.Unstructured); !ok {
- return fmt.Errorf("unstructured client did not understand object: %T", obj)
+ if _, ok := subResourceObj.(runtime.Unstructured); !ok {
+ return fmt.Errorf("unstructured client did not understand object: %T", subResourceObj)
}
if subResourceObj.GetName() == "" {
subResourceObj.SetName(obj.GetName())
}
- o, err := uc.cache.getObjMeta(obj)
+ o, err := uc.resources.getObjMeta(obj)
if err != nil {
return err
}
@@ -289,11 +286,11 @@ func (uc *unstructuredClient) CreateSubResource(ctx context.Context, obj, subRes
}
func (uc *unstructuredClient) UpdateSubResource(ctx context.Context, obj Object, subResource string, opts ...SubResourceUpdateOption) error {
- if _, ok := obj.(*unstructured.Unstructured); !ok {
+ if _, ok := obj.(runtime.Unstructured); !ok {
return fmt.Errorf("unstructured client did not understand object: %T", obj)
}
- o, err := uc.cache.getObjMeta(obj)
+ o, err := uc.resources.getObjMeta(obj)
if err != nil {
return err
}
@@ -324,14 +321,14 @@ func (uc *unstructuredClient) UpdateSubResource(ctx context.Context, obj Object,
}
func (uc *unstructuredClient) PatchSubResource(ctx context.Context, obj Object, subResource string, patch Patch, opts ...SubResourcePatchOption) error {
- u, ok := obj.(*unstructured.Unstructured)
+ u, ok := obj.(runtime.Unstructured)
if !ok {
return fmt.Errorf("unstructured client did not understand object: %T", obj)
}
- gvk := u.GroupVersionKind()
+ gvk := u.GetObjectKind().GroupVersionKind()
- o, err := uc.cache.getObjMeta(obj)
+ o, err := uc.resources.getObjMeta(obj)
if err != nil {
return err
}
@@ -359,6 +356,6 @@ func (uc *unstructuredClient) PatchSubResource(ctx context.Context, obj Object,
Do(ctx).
Into(body)
- u.SetGroupVersionKind(gvk)
+ u.GetObjectKind().SetGroupVersionKind(gvk)
return result
}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/watch.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/watch.go
index 70490664b..181b22a67 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/client/watch.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/watch.go
@@ -21,9 +21,8 @@ import (
"strings"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+ "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/watch"
- "k8s.io/client-go/dynamic"
"k8s.io/client-go/rest"
)
@@ -33,21 +32,16 @@ func NewWithWatch(config *rest.Config, options Options) (WithWatch, error) {
if err != nil {
return nil, err
}
- dynamicClient, err := dynamic.NewForConfig(config)
- if err != nil {
- return nil, err
- }
- return &watchingClient{client: client, dynamic: dynamicClient}, nil
+ return &watchingClient{client: client}, nil
}
type watchingClient struct {
*client
- dynamic dynamic.Interface
}
func (w *watchingClient) Watch(ctx context.Context, list ObjectList, opts ...ListOption) (watch.Interface, error) {
switch l := list.(type) {
- case *unstructured.UnstructuredList:
+ case runtime.Unstructured:
return w.unstructuredWatch(ctx, l, opts...)
case *metav1.PartialObjectMetadataList:
return w.metadataWatch(ctx, l, opts...)
@@ -81,25 +75,23 @@ func (w *watchingClient) metadataWatch(ctx context.Context, obj *metav1.PartialO
return resInt.Watch(ctx, *listOpts.AsListOptions())
}
-func (w *watchingClient) unstructuredWatch(ctx context.Context, obj *unstructured.UnstructuredList, opts ...ListOption) (watch.Interface, error) {
- gvk := obj.GroupVersionKind()
- gvk.Kind = strings.TrimSuffix(gvk.Kind, "List")
-
- r, err := w.client.unstructuredClient.cache.getResource(obj)
+func (w *watchingClient) unstructuredWatch(ctx context.Context, obj runtime.Unstructured, opts ...ListOption) (watch.Interface, error) {
+ r, err := w.client.unstructuredClient.resources.getResource(obj)
if err != nil {
return nil, err
}
listOpts := w.listOpts(opts...)
- if listOpts.Namespace != "" && r.isNamespaced() {
- return w.dynamic.Resource(r.mapping.Resource).Namespace(listOpts.Namespace).Watch(ctx, *listOpts.AsListOptions())
- }
- return w.dynamic.Resource(r.mapping.Resource).Watch(ctx, *listOpts.AsListOptions())
+ return r.Get().
+ NamespaceIfScoped(listOpts.Namespace, r.isNamespaced()).
+ Resource(r.resource()).
+ VersionedParams(listOpts.AsListOptions(), w.client.unstructuredClient.paramCodec).
+ Watch(ctx)
}
func (w *watchingClient) typedWatch(ctx context.Context, obj ObjectList, opts ...ListOption) (watch.Interface, error) {
- r, err := w.client.typedClient.cache.getResource(obj)
+ r, err := w.client.typedClient.resources.getResource(obj)
if err != nil {
return nil, err
}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/cluster/cluster.go b/vendor/sigs.k8s.io/controller-runtime/pkg/cluster/cluster.go
index 905296cd3..7ab76555b 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/cluster/cluster.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/cluster/cluster.go
@@ -19,6 +19,7 @@ package cluster
import (
"context"
"errors"
+ "net/http"
"time"
"github.com/go-logr/logr"
@@ -27,6 +28,7 @@ import (
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/record"
+ "k8s.io/utils/pointer"
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
logf "sigs.k8s.io/controller-runtime/pkg/internal/log"
@@ -37,14 +39,15 @@ import (
// Cluster provides various methods to interact with a cluster.
type Cluster interface {
- // SetFields will set any dependencies on an object for which the object has implemented the inject
- // interface - e.g. inject.Client.
- // Deprecated: use the equivalent Options field to set a field. This method will be removed in v0.10.
- SetFields(interface{}) error
+ // GetHTTPClient returns an HTTP client that can be used to talk to the apiserver
+ GetHTTPClient() *http.Client
// GetConfig returns an initialized Config
GetConfig() *rest.Config
+ // GetCache returns a cache.Cache
+ GetCache() cache.Cache
+
// GetScheme returns an initialized Scheme
GetScheme() *runtime.Scheme
@@ -57,9 +60,6 @@ type Cluster interface {
// GetFieldIndexer returns a client.FieldIndexer configured with the client
GetFieldIndexer() client.FieldIndexer
- // GetCache returns a cache.Cache
- GetCache() cache.Cache
-
// GetEventRecorderFor returns a new EventRecorder for the provided name
GetEventRecorderFor(name string) record.EventRecorder
@@ -83,7 +83,7 @@ type Options struct {
Scheme *runtime.Scheme
// MapperProvider provides the rest mapper used to map go types to Kubernetes APIs
- MapperProvider func(c *rest.Config) (meta.RESTMapper, error)
+ MapperProvider func(c *rest.Config, httpClient *http.Client) (meta.RESTMapper, error)
// Logger is the logger that should be used by this Cluster.
// If none is set, it defaults to log.Log global logger.
@@ -103,24 +103,54 @@ type Options struct {
// Note: If a namespace is specified, controllers can still Watch for a
// cluster-scoped resource (e.g Node). For namespaced resources the cache
// will only hold objects from the desired namespace.
+ //
+ // Deprecated: Use Cache.Namespaces instead.
Namespace string
+ // HTTPClient is the http client that will be used to create the default
+ // Cache and Client. If not set the rest.HTTPClientFor function will be used
+ // to create the http client.
+ HTTPClient *http.Client
+
+ // Cache is the cache.Options that will be used to create the default Cache.
+ // By default, the cache will watch and list requested objects in all namespaces.
+ Cache cache.Options
+
// NewCache is the function that will create the cache to be used
// by the manager. If not set this will use the default new cache function.
+ //
+ // When using a custom NewCache, the Cache options will be passed to the
+ // NewCache function.
+ //
+ // NOTE: LOW LEVEL PRIMITIVE!
+ // Only use a custom NewCache if you know what you are doing.
NewCache cache.NewCacheFunc
+ // Client is the client.Options that will be used to create the default Client.
+ // By default, the client will use the cache for reads and direct calls for writes.
+ Client client.Options
+
// NewClient is the func that creates the client to be used by the manager.
- // If not set this will create the default DelegatingClient that will
- // use the cache for reads and the client for writes.
- // NOTE: The default client will not cache Unstructured.
- NewClient NewClientFunc
+ // If not set this will create a Client backed by a Cache for read operations
+ // and a direct Client for write operations.
+ //
+ // When using a custom NewClient, the Client options will be passed to the
+ // NewClient function.
+ //
+ // NOTE: LOW LEVEL PRIMITIVE!
+ // Only use a custom NewClient if you know what you are doing.
+ NewClient client.NewClientFunc
// ClientDisableCacheFor tells the client that, if any cache is used, to bypass it
// for the given objects.
+ //
+ // Deprecated: Use Client.Cache.DisableFor instead.
ClientDisableCacheFor []client.Object
// DryRunClient specifies whether the client should be configured to enforce
// dryRun mode.
+ //
+ // Deprecated: Use Client.DryRun instead.
DryRunClient bool
// EventBroadcaster records Events emitted by the manager and sends them to the Kubernetes API
@@ -137,7 +167,7 @@ type Options struct {
makeBroadcaster intrec.EventBroadcasterProducer
// Dependency injection for testing
- newRecorderProvider func(config *rest.Config, scheme *runtime.Scheme, logger logr.Logger, makeBroadcaster intrec.EventBroadcasterProducer) (*intrec.Provider, error)
+ newRecorderProvider func(config *rest.Config, httpClient *http.Client, scheme *runtime.Scheme, logger logr.Logger, makeBroadcaster intrec.EventBroadcasterProducer) (*intrec.Provider, error)
}
// Option can be used to manipulate Options.
@@ -149,56 +179,116 @@ func New(config *rest.Config, opts ...Option) (Cluster, error) {
return nil, errors.New("must specify Config")
}
+ originalConfig := config
+
+ config = rest.CopyConfig(config)
+ if config.UserAgent == "" {
+ config.UserAgent = rest.DefaultKubernetesUserAgent()
+ }
+
options := Options{}
for _, opt := range opts {
opt(&options)
}
- options = setOptionsDefaults(options)
+ options, err := setOptionsDefaults(options, config)
+ if err != nil {
+ options.Logger.Error(err, "Failed to set defaults")
+ return nil, err
+ }
// Create the mapper provider
- mapper, err := options.MapperProvider(config)
+ mapper, err := options.MapperProvider(config, options.HTTPClient)
if err != nil {
options.Logger.Error(err, "Failed to get API Group-Resources")
return nil, err
}
// Create the cache for the cached read client and registering informers
- cache, err := options.NewCache(config, cache.Options{Scheme: options.Scheme, Mapper: mapper, Resync: options.SyncPeriod, Namespace: options.Namespace})
+ cacheOpts := options.Cache
+ {
+ if cacheOpts.Scheme == nil {
+ cacheOpts.Scheme = options.Scheme
+ }
+ if cacheOpts.Mapper == nil {
+ cacheOpts.Mapper = mapper
+ }
+ if cacheOpts.HTTPClient == nil {
+ cacheOpts.HTTPClient = options.HTTPClient
+ }
+ if cacheOpts.SyncPeriod == nil {
+ cacheOpts.SyncPeriod = options.SyncPeriod
+ }
+ if len(cacheOpts.Namespaces) == 0 && options.Namespace != "" {
+ cacheOpts.Namespaces = []string{options.Namespace}
+ }
+ }
+ cache, err := options.NewCache(config, cacheOpts)
if err != nil {
return nil, err
}
- clientOptions := client.Options{Scheme: options.Scheme, Mapper: mapper}
+ // Create the client, and default its options.
+ clientOpts := options.Client
+ {
+ if clientOpts.Scheme == nil {
+ clientOpts.Scheme = options.Scheme
+ }
+ if clientOpts.Mapper == nil {
+ clientOpts.Mapper = mapper
+ }
+ if clientOpts.HTTPClient == nil {
+ clientOpts.HTTPClient = options.HTTPClient
+ }
+ if clientOpts.Cache == nil {
+ clientOpts.Cache = &client.CacheOptions{
+ Unstructured: false,
+ }
+ }
+ if clientOpts.Cache.Reader == nil {
+ clientOpts.Cache.Reader = cache
+ }
+
+ // For backward compatibility, the ClientDisableCacheFor option should
+ // be appended to the DisableFor option in the client.
+ clientOpts.Cache.DisableFor = append(clientOpts.Cache.DisableFor, options.ClientDisableCacheFor...)
- apiReader, err := client.New(config, clientOptions)
+ if clientOpts.DryRun == nil && options.DryRunClient {
+ // For backward compatibility, the DryRunClient (if set) option should override
+ // the DryRun option in the client (if unset).
+ clientOpts.DryRun = pointer.Bool(true)
+ }
+ }
+ clientWriter, err := options.NewClient(config, clientOpts)
if err != nil {
return nil, err
}
- writeObj, err := options.NewClient(cache, config, clientOptions, options.ClientDisableCacheFor...)
+ // Create the API Reader, a client with no cache.
+ clientReader, err := client.New(config, client.Options{
+ HTTPClient: options.HTTPClient,
+ Scheme: options.Scheme,
+ Mapper: mapper,
+ })
if err != nil {
return nil, err
}
- if options.DryRunClient {
- writeObj = client.NewDryRunClient(writeObj)
- }
-
// Create the recorder provider to inject event recorders for the components.
// TODO(directxman12): the log for the event provider should have a context (name, tags, etc) specific
// to the particular controller that it's being injected into, rather than a generic one like is here.
- recorderProvider, err := options.newRecorderProvider(config, options.Scheme, options.Logger.WithName("events"), options.makeBroadcaster)
+ recorderProvider, err := options.newRecorderProvider(config, options.HTTPClient, options.Scheme, options.Logger.WithName("events"), options.makeBroadcaster)
if err != nil {
return nil, err
}
return &cluster{
- config: config,
+ config: originalConfig,
+ httpClient: options.HTTPClient,
scheme: options.Scheme,
cache: cache,
fieldIndexes: cache,
- client: writeObj,
- apiReader: apiReader,
+ client: clientWriter,
+ apiReader: clientReader,
recorderProvider: recorderProvider,
mapper: mapper,
logger: options.Logger,
@@ -206,21 +296,27 @@ func New(config *rest.Config, opts ...Option) (Cluster, error) {
}
// setOptionsDefaults set default values for Options fields.
-func setOptionsDefaults(options Options) Options {
+func setOptionsDefaults(options Options, config *rest.Config) (Options, error) {
+ if options.HTTPClient == nil {
+ var err error
+ options.HTTPClient, err = rest.HTTPClientFor(config)
+ if err != nil {
+ return options, err
+ }
+ }
+
// Use the Kubernetes client-go scheme if none is specified
if options.Scheme == nil {
options.Scheme = scheme.Scheme
}
if options.MapperProvider == nil {
- options.MapperProvider = func(c *rest.Config) (meta.RESTMapper, error) {
- return apiutil.NewDynamicRESTMapper(c)
- }
+ options.MapperProvider = apiutil.NewDynamicRESTMapper
}
// Allow users to define how to create a new client
if options.NewClient == nil {
- options.NewClient = DefaultNewClient
+ options.NewClient = client.New
}
// Allow newCache to be mocked
@@ -250,39 +346,5 @@ func setOptionsDefaults(options Options) Options {
options.Logger = logf.RuntimeLog.WithName("cluster")
}
- return options
-}
-
-// NewClientFunc allows a user to define how to create a client.
-type NewClientFunc func(cache cache.Cache, config *rest.Config, options client.Options, uncachedObjects ...client.Object) (client.Client, error)
-
-// ClientOptions are the optional arguments for tuning the caching client.
-type ClientOptions struct {
- UncachedObjects []client.Object
- CacheUnstructured bool
-}
-
-// DefaultNewClient creates the default caching client, that will never cache Unstructured.
-func DefaultNewClient(cache cache.Cache, config *rest.Config, options client.Options, uncachedObjects ...client.Object) (client.Client, error) {
- return ClientBuilderWithOptions(ClientOptions{})(cache, config, options, uncachedObjects...)
-}
-
-// ClientBuilderWithOptions returns a Client constructor that will build a client
-// honoring the options argument
-func ClientBuilderWithOptions(options ClientOptions) NewClientFunc {
- return func(cache cache.Cache, config *rest.Config, clientOpts client.Options, uncachedObjects ...client.Object) (client.Client, error) {
- options.UncachedObjects = append(options.UncachedObjects, uncachedObjects...)
-
- c, err := client.New(config, clientOpts)
- if err != nil {
- return nil, err
- }
-
- return client.NewDelegatingClient(client.NewDelegatingClientInput{
- CacheReader: cache,
- Client: c,
- UncachedObjects: options.UncachedObjects,
- CacheUnstructured: options.CacheUnstructured,
- })
- }
+ return options, nil
}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/cluster/internal.go b/vendor/sigs.k8s.io/controller-runtime/pkg/cluster/internal.go
index 125e1d144..274276423 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/cluster/internal.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/cluster/internal.go
@@ -18,6 +18,7 @@ package cluster
import (
"context"
+ "net/http"
"github.com/go-logr/logr"
"k8s.io/apimachinery/pkg/api/meta"
@@ -28,22 +29,16 @@ import (
"sigs.k8s.io/controller-runtime/pkg/cache"
"sigs.k8s.io/controller-runtime/pkg/client"
intrec "sigs.k8s.io/controller-runtime/pkg/internal/recorder"
- "sigs.k8s.io/controller-runtime/pkg/runtime/inject"
)
type cluster struct {
// config is the rest.config used to talk to the apiserver. Required.
config *rest.Config
- // scheme is the scheme injected into Controllers, EventHandlers, Sources and Predicates. Defaults
- // to scheme.scheme.
- scheme *runtime.Scheme
-
- cache cache.Cache
-
- // TODO(directxman12): Provide an escape hatch to get individual indexers
- // client is the client injected into Controllers (and EventHandlers, Sources and Predicates).
- client client.Client
+ httpClient *http.Client
+ scheme *runtime.Scheme
+ cache cache.Cache
+ client client.Client
// apiReader is the reader that will make requests to the api server and not the cache.
apiReader client.Reader
@@ -64,32 +59,14 @@ type cluster struct {
logger logr.Logger
}
-func (c *cluster) SetFields(i interface{}) error {
- if _, err := inject.ConfigInto(c.config, i); err != nil {
- return err
- }
- if _, err := inject.ClientInto(c.client, i); err != nil {
- return err
- }
- if _, err := inject.APIReaderInto(c.apiReader, i); err != nil {
- return err
- }
- if _, err := inject.SchemeInto(c.scheme, i); err != nil {
- return err
- }
- if _, err := inject.CacheInto(c.cache, i); err != nil {
- return err
- }
- if _, err := inject.MapperInto(c.mapper, i); err != nil {
- return err
- }
- return nil
-}
-
func (c *cluster) GetConfig() *rest.Config {
return c.config
}
+func (c *cluster) GetHTTPClient() *http.Client {
+ return c.httpClient
+}
+
func (c *cluster) GetClient() client.Client {
return c.client
}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/config/config.go b/vendor/sigs.k8s.io/controller-runtime/pkg/config/config.go
index 8e853d6a0..9c7b875a8 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/config/config.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/config/config.go
@@ -29,6 +29,8 @@ import (
// ControllerManagerConfiguration defines the functions necessary to parse a config file
// and to configure the Options struct for the ctrl.Manager.
+//
+// Deprecated: The component config package has been deprecated and will be removed in a future release. Users should migrate to their own config implementation, please share feedback in https://github.com/kubernetes-sigs/controller-runtime/issues/895.
type ControllerManagerConfiguration interface {
runtime.Object
@@ -38,6 +40,8 @@ type ControllerManagerConfiguration interface {
// DeferredFileLoader is used to configure the decoder for loading controller
// runtime component config types.
+//
+// Deprecated: The component config package has been deprecated and will be removed in a future release. Users should migrate to their own config implementation, please share feedback in https://github.com/kubernetes-sigs/controller-runtime/issues/895.
type DeferredFileLoader struct {
ControllerManagerConfiguration
path string
@@ -52,6 +56,8 @@ type DeferredFileLoader struct {
// Defaults:
// * Path: "./config.yaml"
// * Kind: GenericControllerManagerConfiguration
+//
+// Deprecated: The component config package has been deprecated and will be removed in a future release. Users should migrate to their own config implementation, please share feedback in https://github.com/kubernetes-sigs/controller-runtime/issues/895.
func File() *DeferredFileLoader {
scheme := runtime.NewScheme()
utilruntime.Must(v1alpha1.AddToScheme(scheme))
@@ -83,12 +89,6 @@ func (d *DeferredFileLoader) OfKind(obj ControllerManagerConfiguration) *Deferre
return d
}
-// InjectScheme will configure the scheme to be used for decoding the file.
-func (d *DeferredFileLoader) InjectScheme(scheme *runtime.Scheme) error {
- d.scheme = scheme
- return nil
-}
-
// loadFile is used from the mutex.Once to load the file.
func (d *DeferredFileLoader) loadFile() {
if d.scheme == nil {
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/config/controller.go b/vendor/sigs.k8s.io/controller-runtime/pkg/config/controller.go
new file mode 100644
index 000000000..b37dffaee
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/config/controller.go
@@ -0,0 +1,49 @@
+/*
+Copyright 2023 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package config
+
+import "time"
+
+// Controller contains configuration options for a controller.
+type Controller struct {
+ // GroupKindConcurrency is a map from a Kind to the number of concurrent reconciliation
+ // allowed for that controller.
+ //
+ // When a controller is registered within this manager using the builder utilities,
+ // users have to specify the type the controller reconciles in the For(...) call.
+ // If the object's kind passed matches one of the keys in this map, the concurrency
+ // for that controller is set to the number specified.
+ //
+ // The key is expected to be consistent in form with GroupKind.String(),
+ // e.g. ReplicaSet in apps group (regardless of version) would be `ReplicaSet.apps`.
+ GroupKindConcurrency map[string]int
+
+ // MaxConcurrentReconciles is the maximum number of concurrent Reconciles which can be run. Defaults to 1.
+ MaxConcurrentReconciles int
+
+ // CacheSyncTimeout refers to the time limit set to wait for syncing caches.
+ // Defaults to 2 minutes if not set.
+ CacheSyncTimeout time.Duration
+
+ // RecoverPanic indicates whether the panic caused by reconcile should be recovered.
+ // Defaults to the Controller.RecoverPanic setting from the Manager if unset.
+ RecoverPanic *bool
+
+ // NeedLeaderElection indicates whether the controller needs to use leader election.
+ // Defaults to true, which means the controller will use leader election.
+ NeedLeaderElection *bool
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/config/doc.go b/vendor/sigs.k8s.io/controller-runtime/pkg/config/doc.go
index a169ec559..47a5a2f1d 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/config/doc.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/config/doc.go
@@ -14,12 +14,6 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-// Package config contains functionality for interacting with ComponentConfig
-// files
-//
-// # DeferredFileLoader
-//
-// This uses a deferred file decoding allowing you to chain your configuration
-// setup. You can pass this into manager.Options#File and it will load your
-// config.
+// Package config contains functionality for interacting with
+// configuration for controller-runtime components.
package config
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/config/v1alpha1/doc.go b/vendor/sigs.k8s.io/controller-runtime/pkg/config/v1alpha1/doc.go
index 1e3adbafb..8fdf14d39 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/config/v1alpha1/doc.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/config/v1alpha1/doc.go
@@ -17,4 +17,6 @@ limitations under the License.
// Package v1alpha1 provides the ControllerManagerConfiguration used for
// configuring ctrl.Manager
// +kubebuilder:object:generate=true
+//
+// Deprecated: The component config package has been deprecated and will be removed in a future release. Users should migrate to their own config implementation, please share feedback in https://github.com/kubernetes-sigs/controller-runtime/issues/895.
package v1alpha1
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/config/v1alpha1/register.go b/vendor/sigs.k8s.io/controller-runtime/pkg/config/v1alpha1/register.go
index 9efdbc066..ca854bcf3 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/config/v1alpha1/register.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/config/v1alpha1/register.go
@@ -23,12 +23,18 @@ import (
var (
// GroupVersion is group version used to register these objects.
+ //
+ // Deprecated: The component config package has been deprecated and will be removed in a future release. Users should migrate to their own config implementation, please share feedback in https://github.com/kubernetes-sigs/controller-runtime/issues/895.
GroupVersion = schema.GroupVersion{Group: "controller-runtime.sigs.k8s.io", Version: "v1alpha1"}
// SchemeBuilder is used to add go types to the GroupVersionKind scheme.
+ //
+ // Deprecated: The component config package has been deprecated and will be removed in a future release. Users should migrate to their own config implementation, please share feedback in https://github.com/kubernetes-sigs/controller-runtime/issues/895.
SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
// AddToScheme adds the types in this group-version to the given scheme.
+ //
+ // Deprecated: The component config package has been deprecated and will be removed in a future release. Users should migrate to their own config implementation, please share feedback in https://github.com/kubernetes-sigs/controller-runtime/issues/895.
AddToScheme = SchemeBuilder.AddToScheme
)
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/config/v1alpha1/types.go b/vendor/sigs.k8s.io/controller-runtime/pkg/config/v1alpha1/types.go
index f2226278c..52c8ab300 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/config/v1alpha1/types.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/config/v1alpha1/types.go
@@ -25,6 +25,8 @@ import (
)
// ControllerManagerConfigurationSpec defines the desired state of GenericControllerManagerConfiguration.
+//
+// Deprecated: The component config package has been deprecated and will be removed in a future release. Users should migrate to their own config implementation, please share feedback in https://github.com/kubernetes-sigs/controller-runtime/issues/895.
type ControllerManagerConfigurationSpec struct {
// SyncPeriod determines the minimum frequency at which watched resources are
// reconciled. A lower period will correct entropy more quickly, but reduce
@@ -60,7 +62,7 @@ type ControllerManagerConfigurationSpec struct {
// +optional
Controller *ControllerConfigurationSpec `json:"controller,omitempty"`
- // Metrics contains thw controller metrics configuration
+ // Metrics contains the controller metrics configuration
// +optional
Metrics ControllerMetrics `json:"metrics,omitempty"`
@@ -75,6 +77,11 @@ type ControllerManagerConfigurationSpec struct {
// ControllerConfigurationSpec defines the global configuration for
// controllers registered with the manager.
+//
+// Deprecated: The component config package has been deprecated and will be removed in a future release. Users should migrate to their own config implementation, please share feedback in https://github.com/kubernetes-sigs/controller-runtime/issues/895.
+//
+// Deprecated: Controller global configuration can now be set at the manager level,
+// using the manager.Options.Controller field.
type ControllerConfigurationSpec struct {
// GroupKindConcurrency is a map from a Kind to the number of concurrent reconciliation
// allowed for that controller.
@@ -101,6 +108,8 @@ type ControllerConfigurationSpec struct {
}
// ControllerMetrics defines the metrics configs.
+//
+// Deprecated: The component config package has been deprecated and will be removed in a future release. Users should migrate to their own config implementation, please share feedback in https://github.com/kubernetes-sigs/controller-runtime/issues/895.
type ControllerMetrics struct {
// BindAddress is the TCP address that the controller should bind to
// for serving prometheus metrics.
@@ -110,6 +119,8 @@ type ControllerMetrics struct {
}
// ControllerHealth defines the health configs.
+//
+// Deprecated: The component config package has been deprecated and will be removed in a future release. Users should migrate to their own config implementation, please share feedback in https://github.com/kubernetes-sigs/controller-runtime/issues/895.
type ControllerHealth struct {
// HealthProbeBindAddress is the TCP address that the controller should bind to
// for serving health probes
@@ -127,6 +138,8 @@ type ControllerHealth struct {
}
// ControllerWebhook defines the webhook server for the controller.
+//
+// Deprecated: The component config package has been deprecated and will be removed in a future release. Users should migrate to their own config implementation, please share feedback in https://github.com/kubernetes-sigs/controller-runtime/issues/895.
type ControllerWebhook struct {
// Port is the port that the webhook server serves at.
// It is used to set webhook.Server.Port.
@@ -149,6 +162,8 @@ type ControllerWebhook struct {
// +kubebuilder:object:root=true
// ControllerManagerConfiguration is the Schema for the GenericControllerManagerConfigurations API.
+//
+// Deprecated: The component config package has been deprecated and will be removed in a future release. Users should migrate to their own config implementation, please share feedback in https://github.com/kubernetes-sigs/controller-runtime/issues/895.
type ControllerManagerConfiguration struct {
metav1.TypeMeta `json:",inline"`
@@ -157,6 +172,8 @@ type ControllerManagerConfiguration struct {
}
// Complete returns the configuration for controller-runtime.
+//
+// Deprecated: The component config package has been deprecated and will be removed in a future release. Users should migrate to their own config implementation, please share feedback in https://github.com/kubernetes-sigs/controller-runtime/issues/895.
func (c *ControllerManagerConfigurationSpec) Complete() (ControllerManagerConfigurationSpec, error) {
return *c, nil
}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/controller/controller.go b/vendor/sigs.k8s.io/controller-runtime/pkg/controller/controller.go
index fe7f94fdc..6732b6f70 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/controller/controller.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/controller/controller.go
@@ -39,6 +39,18 @@ type Options struct {
// MaxConcurrentReconciles is the maximum number of concurrent Reconciles which can be run. Defaults to 1.
MaxConcurrentReconciles int
+ // CacheSyncTimeout refers to the time limit set to wait for syncing caches.
+ // Defaults to 2 minutes if not set.
+ CacheSyncTimeout time.Duration
+
+ // RecoverPanic indicates whether the panic caused by reconcile should be recovered.
+ // Defaults to the Controller.RecoverPanic setting from the Manager if unset.
+ RecoverPanic *bool
+
+ // NeedLeaderElection indicates whether the controller needs to use leader election.
+ // Defaults to true, which means the controller will use leader election.
+ NeedLeaderElection *bool
+
// Reconciler reconciles an object
Reconciler reconcile.Reconciler
@@ -50,14 +62,6 @@ type Options struct {
// LogConstructor is used to construct a logger used for this controller and passed
// to each reconciliation via the context field.
LogConstructor func(request *reconcile.Request) logr.Logger
-
- // CacheSyncTimeout refers to the time limit set to wait for syncing caches.
- // Defaults to 2 minutes if not set.
- CacheSyncTimeout time.Duration
-
- // RecoverPanic indicates whether the panic caused by reconcile should be recovered.
- // Defaults to the Controller.RecoverPanic setting from the Manager if unset.
- RecoverPanic *bool
}
// Controller implements a Kubernetes API. A Controller manages a work queue fed reconcile.Requests
@@ -124,26 +128,33 @@ func NewUnmanaged(name string, mgr manager.Manager, options Options) (Controller
}
if options.MaxConcurrentReconciles <= 0 {
- options.MaxConcurrentReconciles = 1
+ if mgr.GetControllerOptions().MaxConcurrentReconciles > 0 {
+ options.MaxConcurrentReconciles = mgr.GetControllerOptions().MaxConcurrentReconciles
+ } else {
+ options.MaxConcurrentReconciles = 1
+ }
}
if options.CacheSyncTimeout == 0 {
- options.CacheSyncTimeout = 2 * time.Minute
+ if mgr.GetControllerOptions().CacheSyncTimeout != 0 {
+ options.CacheSyncTimeout = mgr.GetControllerOptions().CacheSyncTimeout
+ } else {
+ options.CacheSyncTimeout = 2 * time.Minute
+ }
}
if options.RateLimiter == nil {
options.RateLimiter = workqueue.DefaultControllerRateLimiter()
}
- // Inject dependencies into Reconciler
- if err := mgr.SetFields(options.Reconciler); err != nil {
- return nil, err
- }
-
if options.RecoverPanic == nil {
options.RecoverPanic = mgr.GetControllerOptions().RecoverPanic
}
+ if options.NeedLeaderElection == nil {
+ options.NeedLeaderElection = mgr.GetControllerOptions().NeedLeaderElection
+ }
+
// Create controller with dependencies set
return &controller.Controller{
Do: options.Reconciler,
@@ -152,10 +163,10 @@ func NewUnmanaged(name string, mgr manager.Manager, options Options) (Controller
},
MaxConcurrentReconciles: options.MaxConcurrentReconciles,
CacheSyncTimeout: options.CacheSyncTimeout,
- SetFields: mgr.SetFields,
Name: name,
LogConstructor: options.LogConstructor,
RecoverPanic: options.RecoverPanic,
+ LeaderElected: options.NeedLeaderElection,
}, nil
}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/envtest/crd.go b/vendor/sigs.k8s.io/controller-runtime/pkg/envtest/crd.go
index dc38b793b..f9c58ea26 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/envtest/crd.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/envtest/crd.go
@@ -166,7 +166,7 @@ func WaitForCRDs(config *rest.Config, crds []*apiextensionsv1.CustomResourceDefi
// Poll until all resources are found in discovery
p := &poller{config: config, waitingFor: waitingFor}
- return wait.PollImmediate(options.PollInterval, options.MaxTime, p.poll)
+ return wait.PollUntilContextTimeout(context.TODO(), options.PollInterval, options.MaxTime, true, p.poll)
}
// poller checks if all the resources have been found in discovery, and returns false if not.
@@ -179,7 +179,7 @@ type poller struct {
}
// poll checks if all the resources have been found in discovery, and returns false if not.
-func (p *poller) poll() (done bool, err error) {
+func (p *poller) poll(ctx context.Context) (done bool, err error) {
// Create a new clientset to avoid any client caching of discovery
cs, err := clientset.NewForConfig(p.config)
if err != nil {
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/envtest/server.go b/vendor/sigs.k8s.io/controller-runtime/pkg/envtest/server.go
index f9e0bb8ab..4ee440df9 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/envtest/server.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/envtest/server.go
@@ -230,17 +230,19 @@ func (te *Environment) Start() (*rest.Config, error) {
if os.Getenv(envAttachOutput) == "true" {
te.AttachControlPlaneOutput = true
}
- if apiServer.Out == nil && te.AttachControlPlaneOutput {
- apiServer.Out = os.Stdout
- }
- if apiServer.Err == nil && te.AttachControlPlaneOutput {
- apiServer.Err = os.Stderr
- }
- if te.ControlPlane.Etcd.Out == nil && te.AttachControlPlaneOutput {
- te.ControlPlane.Etcd.Out = os.Stdout
- }
- if te.ControlPlane.Etcd.Err == nil && te.AttachControlPlaneOutput {
- te.ControlPlane.Etcd.Err = os.Stderr
+ if te.AttachControlPlaneOutput {
+ if apiServer.Out == nil {
+ apiServer.Out = os.Stdout
+ }
+ if apiServer.Err == nil {
+ apiServer.Err = os.Stderr
+ }
+ if te.ControlPlane.Etcd.Out == nil {
+ te.ControlPlane.Etcd.Out = os.Stdout
+ }
+ if te.ControlPlane.Etcd.Err == nil {
+ te.ControlPlane.Etcd.Err = os.Stderr
+ }
}
apiServer.Path = process.BinPathFinder("kube-apiserver", te.BinaryAssetsDirectory)
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/envtest/webhook.go b/vendor/sigs.k8s.io/controller-runtime/pkg/envtest/webhook.go
index 49d877358..f7e43a148 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/envtest/webhook.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/envtest/webhook.go
@@ -147,6 +147,8 @@ func (o *WebhookInstallOptions) PrepWithoutInstalling() error {
// Install installs specified webhooks to the API server.
func (o *WebhookInstallOptions) Install(config *rest.Config) error {
+ defaultWebhookOptions(o)
+
if len(o.LocalServingCAData) == 0 {
if err := o.PrepWithoutInstalling(); err != nil {
return err
@@ -168,6 +170,16 @@ func (o *WebhookInstallOptions) Cleanup() error {
return nil
}
+// defaultWebhookOptions sets the default values for Webhooks.
+func defaultWebhookOptions(o *WebhookInstallOptions) {
+ if o.MaxTime == 0 {
+ o.MaxTime = defaultMaxWait
+ }
+ if o.PollInterval == 0 {
+ o.PollInterval = defaultPollInterval
+ }
+}
+
// WaitForWebhooks waits for the Webhooks to be available through API server.
func WaitForWebhooks(config *rest.Config,
mutatingWebhooks []*admissionv1.MutatingWebhookConfiguration,
@@ -203,7 +215,7 @@ func WaitForWebhooks(config *rest.Config,
// Poll until all resources are found in discovery
p := &webhookPoller{config: config, waitingFor: waitingFor}
- return wait.PollImmediate(options.PollInterval, options.MaxTime, p.poll)
+ return wait.PollUntilContextTimeout(context.TODO(), options.PollInterval, options.MaxTime, true, p.poll)
}
// poller checks if all the resources have been found in discovery, and returns false if not.
@@ -216,7 +228,7 @@ type webhookPoller struct {
}
// poll checks if all the resources have been found in discovery, and returns false if not.
-func (p *webhookPoller) poll() (done bool, err error) {
+func (p *webhookPoller) poll(ctx context.Context) (done bool, err error) {
// Create a new clientset to avoid any client caching of discovery
c, err := client.New(p.config, client.Options{})
if err != nil {
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/handler/enqueue.go b/vendor/sigs.k8s.io/controller-runtime/pkg/handler/enqueue.go
index e6d3a4eaa..c72b2e1eb 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/handler/enqueue.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/handler/enqueue.go
@@ -17,6 +17,8 @@ limitations under the License.
package handler
import (
+ "context"
+
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/util/workqueue"
"sigs.k8s.io/controller-runtime/pkg/event"
@@ -36,7 +38,7 @@ var _ EventHandler = &EnqueueRequestForObject{}
type EnqueueRequestForObject struct{}
// Create implements EventHandler.
-func (e *EnqueueRequestForObject) Create(evt event.CreateEvent, q workqueue.RateLimitingInterface) {
+func (e *EnqueueRequestForObject) Create(ctx context.Context, evt event.CreateEvent, q workqueue.RateLimitingInterface) {
if evt.Object == nil {
enqueueLog.Error(nil, "CreateEvent received with no metadata", "event", evt)
return
@@ -48,7 +50,7 @@ func (e *EnqueueRequestForObject) Create(evt event.CreateEvent, q workqueue.Rate
}
// Update implements EventHandler.
-func (e *EnqueueRequestForObject) Update(evt event.UpdateEvent, q workqueue.RateLimitingInterface) {
+func (e *EnqueueRequestForObject) Update(ctx context.Context, evt event.UpdateEvent, q workqueue.RateLimitingInterface) {
switch {
case evt.ObjectNew != nil:
q.Add(reconcile.Request{NamespacedName: types.NamespacedName{
@@ -66,7 +68,7 @@ func (e *EnqueueRequestForObject) Update(evt event.UpdateEvent, q workqueue.Rate
}
// Delete implements EventHandler.
-func (e *EnqueueRequestForObject) Delete(evt event.DeleteEvent, q workqueue.RateLimitingInterface) {
+func (e *EnqueueRequestForObject) Delete(ctx context.Context, evt event.DeleteEvent, q workqueue.RateLimitingInterface) {
if evt.Object == nil {
enqueueLog.Error(nil, "DeleteEvent received with no metadata", "event", evt)
return
@@ -78,7 +80,7 @@ func (e *EnqueueRequestForObject) Delete(evt event.DeleteEvent, q workqueue.Rate
}
// Generic implements EventHandler.
-func (e *EnqueueRequestForObject) Generic(evt event.GenericEvent, q workqueue.RateLimitingInterface) {
+func (e *EnqueueRequestForObject) Generic(ctx context.Context, evt event.GenericEvent, q workqueue.RateLimitingInterface) {
if evt.Object == nil {
enqueueLog.Error(nil, "GenericEvent received with no metadata", "event", evt)
return
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/handler/enqueue_mapped.go b/vendor/sigs.k8s.io/controller-runtime/pkg/handler/enqueue_mapped.go
index 17401b1fd..b55fdde6b 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/handler/enqueue_mapped.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/handler/enqueue_mapped.go
@@ -17,16 +17,17 @@ limitations under the License.
package handler
import (
+ "context"
+
"k8s.io/client-go/util/workqueue"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/event"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
- "sigs.k8s.io/controller-runtime/pkg/runtime/inject"
)
// MapFunc is the signature required for enqueueing requests from a generic function.
// This type is usually used with EnqueueRequestsFromMapFunc when registering an event handler.
-type MapFunc func(client.Object) []reconcile.Request
+type MapFunc func(context.Context, client.Object) []reconcile.Request
// EnqueueRequestsFromMapFunc enqueues Requests by running a transformation function that outputs a collection
// of reconcile.Requests on each Event. The reconcile.Requests may be for an arbitrary set of objects
@@ -52,32 +53,32 @@ type enqueueRequestsFromMapFunc struct {
}
// Create implements EventHandler.
-func (e *enqueueRequestsFromMapFunc) Create(evt event.CreateEvent, q workqueue.RateLimitingInterface) {
+func (e *enqueueRequestsFromMapFunc) Create(ctx context.Context, evt event.CreateEvent, q workqueue.RateLimitingInterface) {
reqs := map[reconcile.Request]empty{}
- e.mapAndEnqueue(q, evt.Object, reqs)
+ e.mapAndEnqueue(ctx, q, evt.Object, reqs)
}
// Update implements EventHandler.
-func (e *enqueueRequestsFromMapFunc) Update(evt event.UpdateEvent, q workqueue.RateLimitingInterface) {
+func (e *enqueueRequestsFromMapFunc) Update(ctx context.Context, evt event.UpdateEvent, q workqueue.RateLimitingInterface) {
reqs := map[reconcile.Request]empty{}
- e.mapAndEnqueue(q, evt.ObjectOld, reqs)
- e.mapAndEnqueue(q, evt.ObjectNew, reqs)
+ e.mapAndEnqueue(ctx, q, evt.ObjectOld, reqs)
+ e.mapAndEnqueue(ctx, q, evt.ObjectNew, reqs)
}
// Delete implements EventHandler.
-func (e *enqueueRequestsFromMapFunc) Delete(evt event.DeleteEvent, q workqueue.RateLimitingInterface) {
+func (e *enqueueRequestsFromMapFunc) Delete(ctx context.Context, evt event.DeleteEvent, q workqueue.RateLimitingInterface) {
reqs := map[reconcile.Request]empty{}
- e.mapAndEnqueue(q, evt.Object, reqs)
+ e.mapAndEnqueue(ctx, q, evt.Object, reqs)
}
// Generic implements EventHandler.
-func (e *enqueueRequestsFromMapFunc) Generic(evt event.GenericEvent, q workqueue.RateLimitingInterface) {
+func (e *enqueueRequestsFromMapFunc) Generic(ctx context.Context, evt event.GenericEvent, q workqueue.RateLimitingInterface) {
reqs := map[reconcile.Request]empty{}
- e.mapAndEnqueue(q, evt.Object, reqs)
+ e.mapAndEnqueue(ctx, q, evt.Object, reqs)
}
-func (e *enqueueRequestsFromMapFunc) mapAndEnqueue(q workqueue.RateLimitingInterface, object client.Object, reqs map[reconcile.Request]empty) {
- for _, req := range e.toRequests(object) {
+func (e *enqueueRequestsFromMapFunc) mapAndEnqueue(ctx context.Context, q workqueue.RateLimitingInterface, object client.Object, reqs map[reconcile.Request]empty) {
+ for _, req := range e.toRequests(ctx, object) {
_, ok := reqs[req]
if !ok {
q.Add(req)
@@ -85,13 +86,3 @@ func (e *enqueueRequestsFromMapFunc) mapAndEnqueue(q workqueue.RateLimitingInter
}
}
}
-
-// EnqueueRequestsFromMapFunc can inject fields into the mapper.
-
-// InjectFunc implements inject.Injector.
-func (e *enqueueRequestsFromMapFunc) InjectFunc(f inject.Func) error {
- if f == nil {
- return nil
- }
- return f(e.toRequests)
-}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/handler/enqueue_owner.go b/vendor/sigs.k8s.io/controller-runtime/pkg/handler/enqueue_owner.go
index 63699893f..02e7d756f 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/handler/enqueue_owner.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/handler/enqueue_owner.go
@@ -17,6 +17,7 @@ limitations under the License.
package handler
import (
+ "context"
"fmt"
"k8s.io/apimachinery/pkg/api/meta"
@@ -25,15 +26,18 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/util/workqueue"
+ "sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/event"
logf "sigs.k8s.io/controller-runtime/pkg/internal/log"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
- "sigs.k8s.io/controller-runtime/pkg/runtime/inject"
)
-var _ EventHandler = &EnqueueRequestForOwner{}
+var _ EventHandler = &enqueueRequestForOwner{}
-var log = logf.RuntimeLog.WithName("eventhandler").WithName("EnqueueRequestForOwner")
+var log = logf.RuntimeLog.WithName("eventhandler").WithName("enqueueRequestForOwner")
+
+// OwnerOption modifies an EnqueueRequestForOwner EventHandler.
+type OwnerOption func(e *enqueueRequestForOwner)
// EnqueueRequestForOwner enqueues Requests for the Owners of an object. E.g. the object that created
// the object that was the source of the Event.
@@ -42,13 +46,34 @@ var log = logf.RuntimeLog.WithName("eventhandler").WithName("EnqueueRequestForOw
//
// - a source.Kind Source with Type of Pod.
//
-// - a handler.EnqueueRequestForOwner EventHandler with an OwnerType of ReplicaSet and IsController set to true.
-type EnqueueRequestForOwner struct {
- // OwnerType is the type of the Owner object to look for in OwnerReferences. Only Group and Kind are compared.
- OwnerType runtime.Object
+// - a handler.enqueueRequestForOwner EventHandler with an OwnerType of ReplicaSet and OnlyControllerOwner set to true.
+func EnqueueRequestForOwner(scheme *runtime.Scheme, mapper meta.RESTMapper, ownerType client.Object, opts ...OwnerOption) EventHandler {
+ e := &enqueueRequestForOwner{
+ ownerType: ownerType,
+ mapper: mapper,
+ }
+ if err := e.parseOwnerTypeGroupKind(scheme); err != nil {
+ panic(err)
+ }
+ for _, opt := range opts {
+ opt(e)
+ }
+ return e
+}
+
+// OnlyControllerOwner if provided will only look at the first OwnerReference with Controller: true.
+func OnlyControllerOwner() OwnerOption {
+ return func(e *enqueueRequestForOwner) {
+ e.isController = true
+ }
+}
- // IsController if set will only look at the first OwnerReference with Controller: true.
- IsController bool
+type enqueueRequestForOwner struct {
+ // ownerType is the type of the Owner object to look for in OwnerReferences. Only Group and Kind are compared.
+ ownerType runtime.Object
+
+ // isController if set will only look at the first OwnerReference with Controller: true.
+ isController bool
// groupKind is the cached Group and Kind from OwnerType
groupKind schema.GroupKind
@@ -58,7 +83,7 @@ type EnqueueRequestForOwner struct {
}
// Create implements EventHandler.
-func (e *EnqueueRequestForOwner) Create(evt event.CreateEvent, q workqueue.RateLimitingInterface) {
+func (e *enqueueRequestForOwner) Create(ctx context.Context, evt event.CreateEvent, q workqueue.RateLimitingInterface) {
reqs := map[reconcile.Request]empty{}
e.getOwnerReconcileRequest(evt.Object, reqs)
for req := range reqs {
@@ -67,7 +92,7 @@ func (e *EnqueueRequestForOwner) Create(evt event.CreateEvent, q workqueue.RateL
}
// Update implements EventHandler.
-func (e *EnqueueRequestForOwner) Update(evt event.UpdateEvent, q workqueue.RateLimitingInterface) {
+func (e *enqueueRequestForOwner) Update(ctx context.Context, evt event.UpdateEvent, q workqueue.RateLimitingInterface) {
reqs := map[reconcile.Request]empty{}
e.getOwnerReconcileRequest(evt.ObjectOld, reqs)
e.getOwnerReconcileRequest(evt.ObjectNew, reqs)
@@ -77,7 +102,7 @@ func (e *EnqueueRequestForOwner) Update(evt event.UpdateEvent, q workqueue.RateL
}
// Delete implements EventHandler.
-func (e *EnqueueRequestForOwner) Delete(evt event.DeleteEvent, q workqueue.RateLimitingInterface) {
+func (e *enqueueRequestForOwner) Delete(ctx context.Context, evt event.DeleteEvent, q workqueue.RateLimitingInterface) {
reqs := map[reconcile.Request]empty{}
e.getOwnerReconcileRequest(evt.Object, reqs)
for req := range reqs {
@@ -86,7 +111,7 @@ func (e *EnqueueRequestForOwner) Delete(evt event.DeleteEvent, q workqueue.RateL
}
// Generic implements EventHandler.
-func (e *EnqueueRequestForOwner) Generic(evt event.GenericEvent, q workqueue.RateLimitingInterface) {
+func (e *enqueueRequestForOwner) Generic(ctx context.Context, evt event.GenericEvent, q workqueue.RateLimitingInterface) {
reqs := map[reconcile.Request]empty{}
e.getOwnerReconcileRequest(evt.Object, reqs)
for req := range reqs {
@@ -96,17 +121,17 @@ func (e *EnqueueRequestForOwner) Generic(evt event.GenericEvent, q workqueue.Rat
// parseOwnerTypeGroupKind parses the OwnerType into a Group and Kind and caches the result. Returns false
// if the OwnerType could not be parsed using the scheme.
-func (e *EnqueueRequestForOwner) parseOwnerTypeGroupKind(scheme *runtime.Scheme) error {
+func (e *enqueueRequestForOwner) parseOwnerTypeGroupKind(scheme *runtime.Scheme) error {
// Get the kinds of the type
- kinds, _, err := scheme.ObjectKinds(e.OwnerType)
+ kinds, _, err := scheme.ObjectKinds(e.ownerType)
if err != nil {
- log.Error(err, "Could not get ObjectKinds for OwnerType", "owner type", fmt.Sprintf("%T", e.OwnerType))
+ log.Error(err, "Could not get ObjectKinds for OwnerType", "owner type", fmt.Sprintf("%T", e.ownerType))
return err
}
// Expect only 1 kind. If there is more than one kind this is probably an edge case such as ListOptions.
if len(kinds) != 1 {
- err := fmt.Errorf("expected exactly 1 kind for OwnerType %T, but found %s kinds", e.OwnerType, kinds)
- log.Error(nil, "expected exactly 1 kind for OwnerType", "owner type", fmt.Sprintf("%T", e.OwnerType), "kinds", kinds)
+ err := fmt.Errorf("expected exactly 1 kind for OwnerType %T, but found %s kinds", e.ownerType, kinds)
+ log.Error(nil, "expected exactly 1 kind for OwnerType", "owner type", fmt.Sprintf("%T", e.ownerType), "kinds", kinds)
return err
}
// Cache the Group and Kind for the OwnerType
@@ -116,7 +141,7 @@ func (e *EnqueueRequestForOwner) parseOwnerTypeGroupKind(scheme *runtime.Scheme)
// getOwnerReconcileRequest looks at object and builds a map of reconcile.Request to reconcile
// owners of object that match e.OwnerType.
-func (e *EnqueueRequestForOwner) getOwnerReconcileRequest(object metav1.Object, result map[reconcile.Request]empty) {
+func (e *enqueueRequestForOwner) getOwnerReconcileRequest(object metav1.Object, result map[reconcile.Request]empty) {
// Iterate through the OwnerReferences looking for a match on Group and Kind against what was requested
// by the user
for _, ref := range e.getOwnersReferences(object) {
@@ -138,7 +163,7 @@ func (e *EnqueueRequestForOwner) getOwnerReconcileRequest(object metav1.Object,
Name: ref.Name,
}}
- // if owner is not namespaced then we should set the namespace to the empty
+ // if owner is not namespaced then we should not set the namespace
mapping, err := e.mapper.RESTMapping(e.groupKind, refGV.Version)
if err != nil {
log.Error(err, "Could not retrieve rest mapping", "kind", e.groupKind)
@@ -153,16 +178,16 @@ func (e *EnqueueRequestForOwner) getOwnerReconcileRequest(object metav1.Object,
}
}
-// getOwnersReferences returns the OwnerReferences for an object as specified by the EnqueueRequestForOwner
+// getOwnersReferences returns the OwnerReferences for an object as specified by the enqueueRequestForOwner
// - if IsController is true: only take the Controller OwnerReference (if found)
// - if IsController is false: take all OwnerReferences.
-func (e *EnqueueRequestForOwner) getOwnersReferences(object metav1.Object) []metav1.OwnerReference {
+func (e *enqueueRequestForOwner) getOwnersReferences(object metav1.Object) []metav1.OwnerReference {
if object == nil {
return nil
}
// If not filtered as Controller only, then use all the OwnerReferences
- if !e.IsController {
+ if !e.isController {
return object.GetOwnerReferences()
}
// If filtered to a Controller, only take the Controller OwnerReference
@@ -172,18 +197,3 @@ func (e *EnqueueRequestForOwner) getOwnersReferences(object metav1.Object) []met
// No Controller OwnerReference found
return nil
}
-
-var _ inject.Scheme = &EnqueueRequestForOwner{}
-
-// InjectScheme is called by the Controller to provide a singleton scheme to the EnqueueRequestForOwner.
-func (e *EnqueueRequestForOwner) InjectScheme(s *runtime.Scheme) error {
- return e.parseOwnerTypeGroupKind(s)
-}
-
-var _ inject.Mapper = &EnqueueRequestForOwner{}
-
-// InjectMapper is called by the Controller to provide the rest mapper used by the manager.
-func (e *EnqueueRequestForOwner) InjectMapper(m meta.RESTMapper) error {
- e.mapper = m
- return nil
-}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/handler/eventhandler.go b/vendor/sigs.k8s.io/controller-runtime/pkg/handler/eventhandler.go
index 8652d22d7..2f380f4fc 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/handler/eventhandler.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/handler/eventhandler.go
@@ -17,6 +17,8 @@ limitations under the License.
package handler
import (
+ "context"
+
"k8s.io/client-go/util/workqueue"
"sigs.k8s.io/controller-runtime/pkg/event"
)
@@ -41,17 +43,17 @@ import (
// Most users shouldn't need to implement their own EventHandler.
type EventHandler interface {
// Create is called in response to an create event - e.g. Pod Creation.
- Create(event.CreateEvent, workqueue.RateLimitingInterface)
+ Create(context.Context, event.CreateEvent, workqueue.RateLimitingInterface)
// Update is called in response to an update event - e.g. Pod Updated.
- Update(event.UpdateEvent, workqueue.RateLimitingInterface)
+ Update(context.Context, event.UpdateEvent, workqueue.RateLimitingInterface)
// Delete is called in response to a delete event - e.g. Pod Deleted.
- Delete(event.DeleteEvent, workqueue.RateLimitingInterface)
+ Delete(context.Context, event.DeleteEvent, workqueue.RateLimitingInterface)
// Generic is called in response to an event of an unknown type or a synthetic event triggered as a cron or
// external trigger request - e.g. reconcile Autoscaling, or a Webhook.
- Generic(event.GenericEvent, workqueue.RateLimitingInterface)
+ Generic(context.Context, event.GenericEvent, workqueue.RateLimitingInterface)
}
var _ EventHandler = Funcs{}
@@ -60,45 +62,45 @@ var _ EventHandler = Funcs{}
type Funcs struct {
// Create is called in response to an add event. Defaults to no-op.
// RateLimitingInterface is used to enqueue reconcile.Requests.
- CreateFunc func(event.CreateEvent, workqueue.RateLimitingInterface)
+ CreateFunc func(context.Context, event.CreateEvent, workqueue.RateLimitingInterface)
// Update is called in response to an update event. Defaults to no-op.
// RateLimitingInterface is used to enqueue reconcile.Requests.
- UpdateFunc func(event.UpdateEvent, workqueue.RateLimitingInterface)
+ UpdateFunc func(context.Context, event.UpdateEvent, workqueue.RateLimitingInterface)
// Delete is called in response to a delete event. Defaults to no-op.
// RateLimitingInterface is used to enqueue reconcile.Requests.
- DeleteFunc func(event.DeleteEvent, workqueue.RateLimitingInterface)
+ DeleteFunc func(context.Context, event.DeleteEvent, workqueue.RateLimitingInterface)
// GenericFunc is called in response to a generic event. Defaults to no-op.
// RateLimitingInterface is used to enqueue reconcile.Requests.
- GenericFunc func(event.GenericEvent, workqueue.RateLimitingInterface)
+ GenericFunc func(context.Context, event.GenericEvent, workqueue.RateLimitingInterface)
}
// Create implements EventHandler.
-func (h Funcs) Create(e event.CreateEvent, q workqueue.RateLimitingInterface) {
+func (h Funcs) Create(ctx context.Context, e event.CreateEvent, q workqueue.RateLimitingInterface) {
if h.CreateFunc != nil {
- h.CreateFunc(e, q)
+ h.CreateFunc(ctx, e, q)
}
}
// Delete implements EventHandler.
-func (h Funcs) Delete(e event.DeleteEvent, q workqueue.RateLimitingInterface) {
+func (h Funcs) Delete(ctx context.Context, e event.DeleteEvent, q workqueue.RateLimitingInterface) {
if h.DeleteFunc != nil {
- h.DeleteFunc(e, q)
+ h.DeleteFunc(ctx, e, q)
}
}
// Update implements EventHandler.
-func (h Funcs) Update(e event.UpdateEvent, q workqueue.RateLimitingInterface) {
+func (h Funcs) Update(ctx context.Context, e event.UpdateEvent, q workqueue.RateLimitingInterface) {
if h.UpdateFunc != nil {
- h.UpdateFunc(e, q)
+ h.UpdateFunc(ctx, e, q)
}
}
// Generic implements EventHandler.
-func (h Funcs) Generic(e event.GenericEvent, q workqueue.RateLimitingInterface) {
+func (h Funcs) Generic(ctx context.Context, e event.GenericEvent, q workqueue.RateLimitingInterface) {
if h.GenericFunc != nil {
- h.GenericFunc(e, q)
+ h.GenericFunc(ctx, e, q)
}
}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/controller.go b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/controller.go
index f7734695c..83aba28cb 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/controller.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/controller.go
@@ -33,12 +33,9 @@ import (
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/predicate"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
- "sigs.k8s.io/controller-runtime/pkg/runtime/inject"
"sigs.k8s.io/controller-runtime/pkg/source"
)
-var _ inject.Injector = &Controller{}
-
// Controller implements controller.Controller.
type Controller struct {
// Name is used to uniquely identify a Controller in tracing, logging and monitoring. Name is required.
@@ -61,10 +58,6 @@ type Controller struct {
// the Queue for processing
Queue workqueue.RateLimitingInterface
- // SetFields is used to inject dependencies into other objects such as Sources, EventHandlers and Predicates
- // Deprecated: the caller should handle injected fields itself.
- SetFields func(i interface{}) error
-
// mu is used to synchronize Controller setup
mu sync.Mutex
@@ -93,6 +86,9 @@ type Controller struct {
// RecoverPanic indicates whether the panic caused by reconcile should be recovered.
RecoverPanic *bool
+
+ // LeaderElected indicates whether the controller is leader elected or always running.
+ LeaderElected *bool
}
// watchDescription contains all the information necessary to start a watch.
@@ -127,19 +123,6 @@ func (c *Controller) Watch(src source.Source, evthdler handler.EventHandler, prc
c.mu.Lock()
defer c.mu.Unlock()
- // Inject Cache into arguments
- if err := c.SetFields(src); err != nil {
- return err
- }
- if err := c.SetFields(evthdler); err != nil {
- return err
- }
- for _, pr := range prct {
- if err := c.SetFields(pr); err != nil {
- return err
- }
- }
-
// Controller hasn't started yet, store the watches locally and return.
//
// These watches are going to be held on the controller struct until the manager or user calls Start(...).
@@ -152,6 +135,14 @@ func (c *Controller) Watch(src source.Source, evthdler handler.EventHandler, prc
return src.Start(c.ctx, evthdler, c.Queue, prct...)
}
+// NeedLeaderElection implements the manager.LeaderElectionRunnable interface.
+func (c *Controller) NeedLeaderElection() bool {
+ if c.LeaderElected == nil {
+ return true
+ }
+ return *c.LeaderElected
+}
+
// Start implements controller.Controller.
func (c *Controller) Start(ctx context.Context) error {
// use an IIFE to get proper lock handling
@@ -323,7 +314,11 @@ func (c *Controller) reconcileHandler(ctx context.Context, obj interface{}) {
result, err := c.Reconcile(ctx, req)
switch {
case err != nil:
- c.Queue.AddRateLimited(req)
+ if errors.Is(err, reconcile.TerminalError(nil)) {
+ ctrlmetrics.TerminalReconcileErrors.WithLabelValues(c.Name).Inc()
+ } else {
+ c.Queue.AddRateLimited(req)
+ }
ctrlmetrics.ReconcileErrors.WithLabelValues(c.Name).Inc()
ctrlmetrics.ReconcileTotal.WithLabelValues(c.Name, labelError).Inc()
log.Error(err, "Reconciler error")
@@ -351,12 +346,6 @@ func (c *Controller) GetLogger() logr.Logger {
return c.LogConstructor(nil)
}
-// InjectFunc implement SetFields.Injector.
-func (c *Controller) InjectFunc(f inject.Func) error {
- c.SetFields = f
- return nil
-}
-
// updateMetrics updates prometheus metrics within the controller.
func (c *Controller) updateMetrics(reconcileTime time.Duration) {
ctrlmetrics.ReconcileTime.WithLabelValues(c.Name).Observe(reconcileTime.Seconds())
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/metrics/metrics.go b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/metrics/metrics.go
index baec66927..b74ce062b 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/metrics/metrics.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/metrics/metrics.go
@@ -39,6 +39,13 @@ var (
Help: "Total number of reconciliation errors per controller",
}, []string{"controller"})
+ // TerminalReconcileErrors is a prometheus counter metrics which holds the total
+ // number of terminal errors from the Reconciler.
+ TerminalReconcileErrors = prometheus.NewCounterVec(prometheus.CounterOpts{
+ Name: "controller_runtime_terminal_reconcile_errors_total",
+ Help: "Total number of terminal reconciliation errors per controller",
+ }, []string{"controller"})
+
// ReconcileTime is a prometheus metric which keeps track of the duration
// of reconciliations.
ReconcileTime = prometheus.NewHistogramVec(prometheus.HistogramOpts{
@@ -67,6 +74,7 @@ func init() {
metrics.Registry.MustRegister(
ReconcileTotal,
ReconcileErrors,
+ TerminalReconcileErrors,
ReconcileTime,
WorkerCount,
ActiveWorkers,
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/internal/objectutil/objectutil.go b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/objectutil/objectutil.go
index 7057f3dbe..0189c0432 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/internal/objectutil/objectutil.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/objectutil/objectutil.go
@@ -17,14 +17,9 @@ limitations under the License.
package objectutil
import (
- "errors"
- "fmt"
-
apimeta "k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
- "k8s.io/apimachinery/pkg/runtime/schema"
- "sigs.k8s.io/controller-runtime/pkg/client/apiutil"
)
// FilterWithLabels returns a copy of the items in objs matching labelSel.
@@ -45,34 +40,3 @@ func FilterWithLabels(objs []runtime.Object, labelSel labels.Selector) ([]runtim
}
return outItems, nil
}
-
-// IsAPINamespaced returns true if the object is namespace scoped.
-// For unstructured objects the gvk is found from the object itself.
-func IsAPINamespaced(obj runtime.Object, scheme *runtime.Scheme, restmapper apimeta.RESTMapper) (bool, error) {
- gvk, err := apiutil.GVKForObject(obj, scheme)
- if err != nil {
- return false, err
- }
-
- return IsAPINamespacedWithGVK(gvk, scheme, restmapper)
-}
-
-// IsAPINamespacedWithGVK returns true if the object having the provided
-// GVK is namespace scoped.
-func IsAPINamespacedWithGVK(gk schema.GroupVersionKind, scheme *runtime.Scheme, restmapper apimeta.RESTMapper) (bool, error) {
- restmapping, err := restmapper.RESTMapping(schema.GroupKind{Group: gk.Group, Kind: gk.Kind})
- if err != nil {
- return false, fmt.Errorf("failed to get restmapping: %w", err)
- }
-
- scope := restmapping.Scope.Name()
-
- if scope == "" {
- return false, errors.New("scope cannot be identified, empty scope returned")
- }
-
- if scope != apimeta.RESTScopeNameRoot {
- return true, nil
- }
- return false, nil
-}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/internal/recorder/recorder.go b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/recorder/recorder.go
index 9d8b2f074..21f0146ba 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/internal/recorder/recorder.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/recorder/recorder.go
@@ -19,6 +19,7 @@ package recorder
import (
"context"
"fmt"
+ "net/http"
"sync"
"github.com/go-logr/logr"
@@ -110,8 +111,12 @@ func (p *Provider) getBroadcaster() record.EventBroadcaster {
}
// NewProvider create a new Provider instance.
-func NewProvider(config *rest.Config, scheme *runtime.Scheme, logger logr.Logger, makeBroadcaster EventBroadcasterProducer) (*Provider, error) {
- corev1Client, err := corev1client.NewForConfig(config)
+func NewProvider(config *rest.Config, httpClient *http.Client, scheme *runtime.Scheme, logger logr.Logger, makeBroadcaster EventBroadcasterProducer) (*Provider, error) {
+ if httpClient == nil {
+ panic("httpClient must not be nil")
+ }
+
+ corev1Client, err := corev1client.NewForConfigAndClient(config, httpClient)
if err != nil {
return nil, fmt.Errorf("failed to init client: %w", err)
}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/source/internal/eventsource.go b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/source/event_handler.go
index f0cfe212e..ae8404a1f 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/source/internal/eventsource.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/source/event_handler.go
@@ -17,6 +17,7 @@ limitations under the License.
package internal
import (
+ "context"
"fmt"
"k8s.io/client-go/tools/cache"
@@ -31,17 +32,39 @@ import (
var log = logf.RuntimeLog.WithName("source").WithName("EventHandler")
-var _ cache.ResourceEventHandler = EventHandler{}
+// NewEventHandler creates a new EventHandler.
+func NewEventHandler(ctx context.Context, queue workqueue.RateLimitingInterface, handler handler.EventHandler, predicates []predicate.Predicate) *EventHandler {
+ return &EventHandler{
+ ctx: ctx,
+ handler: handler,
+ queue: queue,
+ predicates: predicates,
+ }
+}
// EventHandler adapts a handler.EventHandler interface to a cache.ResourceEventHandler interface.
type EventHandler struct {
- EventHandler handler.EventHandler
- Queue workqueue.RateLimitingInterface
- Predicates []predicate.Predicate
+ // ctx stores the context that created the event handler
+ // that is used to propagate cancellation signals to each handler function.
+ ctx context.Context
+
+ handler handler.EventHandler
+ queue workqueue.RateLimitingInterface
+ predicates []predicate.Predicate
+}
+
+// HandlerFuncs converts EventHandler to a ResourceEventHandlerFuncs
+// TODO: switch to ResourceEventHandlerDetailedFuncs with client-go 1.27
+func (e *EventHandler) HandlerFuncs() cache.ResourceEventHandlerFuncs {
+ return cache.ResourceEventHandlerFuncs{
+ AddFunc: e.OnAdd,
+ UpdateFunc: e.OnUpdate,
+ DeleteFunc: e.OnDelete,
+ }
}
// OnAdd creates CreateEvent and calls Create on EventHandler.
-func (e EventHandler) OnAdd(obj interface{}) {
+func (e *EventHandler) OnAdd(obj interface{}) {
c := event.CreateEvent{}
// Pull Object out of the object
@@ -53,18 +76,20 @@ func (e EventHandler) OnAdd(obj interface{}) {
return
}
- for _, p := range e.Predicates {
+ for _, p := range e.predicates {
if !p.Create(c) {
return
}
}
// Invoke create handler
- e.EventHandler.Create(c, e.Queue)
+ ctx, cancel := context.WithCancel(e.ctx)
+ defer cancel()
+ e.handler.Create(ctx, c, e.queue)
}
// OnUpdate creates UpdateEvent and calls Update on EventHandler.
-func (e EventHandler) OnUpdate(oldObj, newObj interface{}) {
+func (e *EventHandler) OnUpdate(oldObj, newObj interface{}) {
u := event.UpdateEvent{}
if o, ok := oldObj.(client.Object); ok {
@@ -84,18 +109,20 @@ func (e EventHandler) OnUpdate(oldObj, newObj interface{}) {
return
}
- for _, p := range e.Predicates {
+ for _, p := range e.predicates {
if !p.Update(u) {
return
}
}
// Invoke update handler
- e.EventHandler.Update(u, e.Queue)
+ ctx, cancel := context.WithCancel(e.ctx)
+ defer cancel()
+ e.handler.Update(ctx, u, e.queue)
}
// OnDelete creates DeleteEvent and calls Delete on EventHandler.
-func (e EventHandler) OnDelete(obj interface{}) {
+func (e *EventHandler) OnDelete(obj interface{}) {
d := event.DeleteEvent{}
// Deal with tombstone events by pulling the object out. Tombstone events wrap the object in a
@@ -114,6 +141,9 @@ func (e EventHandler) OnDelete(obj interface{}) {
return
}
+ // Set DeleteStateUnknown to true
+ d.DeleteStateUnknown = true
+
// Set obj to the tombstone obj
obj = tombstone.Obj
}
@@ -127,12 +157,14 @@ func (e EventHandler) OnDelete(obj interface{}) {
return
}
- for _, p := range e.Predicates {
+ for _, p := range e.predicates {
if !p.Delete(d) {
return
}
}
// Invoke delete handler
- e.EventHandler.Delete(d, e.Queue)
+ ctx, cancel := context.WithCancel(e.ctx)
+ defer cancel()
+ e.handler.Delete(ctx, d, e.queue)
}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/internal/source/kind.go b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/source/kind.go
new file mode 100644
index 000000000..b3a822712
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/source/kind.go
@@ -0,0 +1,117 @@
+package internal
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "time"
+
+ "k8s.io/apimachinery/pkg/api/meta"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/util/wait"
+ "k8s.io/client-go/util/workqueue"
+ "sigs.k8s.io/controller-runtime/pkg/cache"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/handler"
+ "sigs.k8s.io/controller-runtime/pkg/predicate"
+)
+
+// Kind is used to provide a source of events originating inside the cluster from Watches (e.g. Pod Create).
+type Kind struct {
+ // Type is the type of object to watch. e.g. &v1.Pod{}
+ Type client.Object
+
+ // Cache used to watch APIs
+ Cache cache.Cache
+
+ // started may contain an error if one was encountered during startup. If its closed and does not
+ // contain an error, startup and syncing finished.
+ started chan error
+ startCancel func()
+}
+
+// Start is internal and should be called only by the Controller to register an EventHandler with the Informer
+// to enqueue reconcile.Requests.
+func (ks *Kind) Start(ctx context.Context, handler handler.EventHandler, queue workqueue.RateLimitingInterface,
+ prct ...predicate.Predicate) error {
+ if ks.Type == nil {
+ return fmt.Errorf("must create Kind with a non-nil object")
+ }
+ if ks.Cache == nil {
+ return fmt.Errorf("must create Kind with a non-nil cache")
+ }
+
+ // cache.GetInformer will block until its context is cancelled if the cache was already started and it can not
+ // sync that informer (most commonly due to RBAC issues).
+ ctx, ks.startCancel = context.WithCancel(ctx)
+ ks.started = make(chan error)
+ go func() {
+ var (
+ i cache.Informer
+ lastErr error
+ )
+
+ // Tries to get an informer until it returns true,
+ // an error or the specified context is cancelled or expired.
+ if err := wait.PollUntilContextCancel(ctx, 10*time.Second, true, func(ctx context.Context) (bool, error) {
+ // Lookup the Informer from the Cache and add an EventHandler which populates the Queue
+ i, lastErr = ks.Cache.GetInformer(ctx, ks.Type)
+ if lastErr != nil {
+ kindMatchErr := &meta.NoKindMatchError{}
+ switch {
+ case errors.As(lastErr, &kindMatchErr):
+ log.Error(lastErr, "if kind is a CRD, it should be installed before calling Start",
+ "kind", kindMatchErr.GroupKind)
+ case runtime.IsNotRegisteredError(lastErr):
+ log.Error(lastErr, "kind must be registered to the Scheme")
+ default:
+ log.Error(lastErr, "failed to get informer from cache")
+ }
+ return false, nil // Retry.
+ }
+ return true, nil
+ }); err != nil {
+ if lastErr != nil {
+ ks.started <- fmt.Errorf("failed to get informer from cache: %w", lastErr)
+ return
+ }
+ ks.started <- err
+ return
+ }
+
+ _, err := i.AddEventHandler(NewEventHandler(ctx, queue, handler, prct).HandlerFuncs())
+ if err != nil {
+ ks.started <- err
+ return
+ }
+ if !ks.Cache.WaitForCacheSync(ctx) {
+ // Would be great to return something more informative here
+ ks.started <- errors.New("cache did not sync")
+ }
+ close(ks.started)
+ }()
+
+ return nil
+}
+
+func (ks *Kind) String() string {
+ if ks.Type != nil {
+ return fmt.Sprintf("kind source: %T", ks.Type)
+ }
+ return "kind source: unknown type"
+}
+
+// WaitForSync implements SyncingSource to allow controllers to wait with starting
+// workers until the cache is synced.
+func (ks *Kind) WaitForSync(ctx context.Context) error {
+ select {
+ case err := <-ks.started:
+ return err
+ case <-ctx.Done():
+ ks.startCancel()
+ if errors.Is(ctx.Err(), context.Canceled) {
+ return nil
+ }
+ return fmt.Errorf("timed out waiting for cache to be synced for Kind %T", ks.Type)
+ }
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/log/deleg.go b/vendor/sigs.k8s.io/controller-runtime/pkg/log/deleg.go
index c82447d91..c27b4305f 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/log/deleg.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/log/deleg.go
@@ -25,7 +25,7 @@ import (
// loggerPromise knows how to populate a concrete logr.Logger
// with options, given an actual base logger later on down the line.
type loggerPromise struct {
- logger *DelegatingLogSink
+ logger *delegatingLogSink
childPromises []*loggerPromise
promisesLock sync.Mutex
@@ -33,7 +33,7 @@ type loggerPromise struct {
tags []interface{}
}
-func (p *loggerPromise) WithName(l *DelegatingLogSink, name string) *loggerPromise {
+func (p *loggerPromise) WithName(l *delegatingLogSink, name string) *loggerPromise {
res := &loggerPromise{
logger: l,
name: &name,
@@ -47,7 +47,7 @@ func (p *loggerPromise) WithName(l *DelegatingLogSink, name string) *loggerPromi
}
// WithValues provides a new Logger with the tags appended.
-func (p *loggerPromise) WithValues(l *DelegatingLogSink, tags ...interface{}) *loggerPromise {
+func (p *loggerPromise) WithValues(l *delegatingLogSink, tags ...interface{}) *loggerPromise {
res := &loggerPromise{
logger: l,
tags: tags,
@@ -84,12 +84,12 @@ func (p *loggerPromise) Fulfill(parentLogSink logr.LogSink) {
}
}
-// DelegatingLogSink is a logsink that delegates to another logr.LogSink.
+// delegatingLogSink is a logsink that delegates to another logr.LogSink.
// If the underlying promise is not nil, it registers calls to sub-loggers with
// the logging factory to be populated later, and returns a new delegating
// logger. It expects to have *some* logr.Logger set at all times (generally
// a no-op logger before the promises are fulfilled).
-type DelegatingLogSink struct {
+type delegatingLogSink struct {
lock sync.RWMutex
logger logr.LogSink
promise *loggerPromise
@@ -97,7 +97,8 @@ type DelegatingLogSink struct {
}
// Init implements logr.LogSink.
-func (l *DelegatingLogSink) Init(info logr.RuntimeInfo) {
+func (l *delegatingLogSink) Init(info logr.RuntimeInfo) {
+ eventuallyFulfillRoot()
l.lock.Lock()
defer l.lock.Unlock()
l.info = info
@@ -106,7 +107,8 @@ func (l *DelegatingLogSink) Init(info logr.RuntimeInfo) {
// Enabled tests whether this Logger is enabled. For example, commandline
// flags might be used to set the logging verbosity and disable some info
// logs.
-func (l *DelegatingLogSink) Enabled(level int) bool {
+func (l *delegatingLogSink) Enabled(level int) bool {
+ eventuallyFulfillRoot()
l.lock.RLock()
defer l.lock.RUnlock()
return l.logger.Enabled(level)
@@ -118,7 +120,8 @@ func (l *DelegatingLogSink) Enabled(level int) bool {
// the log line. The key/value pairs can then be used to add additional
// variable information. The key/value pairs should alternate string
// keys and arbitrary values.
-func (l *DelegatingLogSink) Info(level int, msg string, keysAndValues ...interface{}) {
+func (l *delegatingLogSink) Info(level int, msg string, keysAndValues ...interface{}) {
+ eventuallyFulfillRoot()
l.lock.RLock()
defer l.lock.RUnlock()
l.logger.Info(level, msg, keysAndValues...)
@@ -132,14 +135,16 @@ func (l *DelegatingLogSink) Info(level int, msg string, keysAndValues ...interfa
// The msg field should be used to add context to any underlying error,
// while the err field should be used to attach the actual error that
// triggered this log line, if present.
-func (l *DelegatingLogSink) Error(err error, msg string, keysAndValues ...interface{}) {
+func (l *delegatingLogSink) Error(err error, msg string, keysAndValues ...interface{}) {
+ eventuallyFulfillRoot()
l.lock.RLock()
defer l.lock.RUnlock()
l.logger.Error(err, msg, keysAndValues...)
}
// WithName provides a new Logger with the name appended.
-func (l *DelegatingLogSink) WithName(name string) logr.LogSink {
+func (l *delegatingLogSink) WithName(name string) logr.LogSink {
+ eventuallyFulfillRoot()
l.lock.RLock()
defer l.lock.RUnlock()
@@ -151,7 +156,7 @@ func (l *DelegatingLogSink) WithName(name string) logr.LogSink {
return sink
}
- res := &DelegatingLogSink{logger: l.logger}
+ res := &delegatingLogSink{logger: l.logger}
promise := l.promise.WithName(res, name)
res.promise = promise
@@ -159,7 +164,8 @@ func (l *DelegatingLogSink) WithName(name string) logr.LogSink {
}
// WithValues provides a new Logger with the tags appended.
-func (l *DelegatingLogSink) WithValues(tags ...interface{}) logr.LogSink {
+func (l *delegatingLogSink) WithValues(tags ...interface{}) logr.LogSink {
+ eventuallyFulfillRoot()
l.lock.RLock()
defer l.lock.RUnlock()
@@ -171,7 +177,7 @@ func (l *DelegatingLogSink) WithValues(tags ...interface{}) logr.LogSink {
return sink
}
- res := &DelegatingLogSink{logger: l.logger}
+ res := &delegatingLogSink{logger: l.logger}
promise := l.promise.WithValues(res, tags...)
res.promise = promise
@@ -181,16 +187,16 @@ func (l *DelegatingLogSink) WithValues(tags ...interface{}) logr.LogSink {
// Fulfill switches the logger over to use the actual logger
// provided, instead of the temporary initial one, if this method
// has not been previously called.
-func (l *DelegatingLogSink) Fulfill(actual logr.LogSink) {
+func (l *delegatingLogSink) Fulfill(actual logr.LogSink) {
if l.promise != nil {
l.promise.Fulfill(actual)
}
}
-// NewDelegatingLogSink constructs a new DelegatingLogSink which uses
+// newDelegatingLogSink constructs a new DelegatingLogSink which uses
// the given logger before its promise is fulfilled.
-func NewDelegatingLogSink(initial logr.LogSink) *DelegatingLogSink {
- l := &DelegatingLogSink{
+func newDelegatingLogSink(initial logr.LogSink) *delegatingLogSink {
+ l := &delegatingLogSink{
logger: initial,
promise: &loggerPromise{promisesLock: sync.Mutex{}},
}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/log/log.go b/vendor/sigs.k8s.io/controller-runtime/pkg/log/log.go
index 082dce3ad..a79151c69 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/log/log.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/log/log.go
@@ -35,7 +35,10 @@ package log
import (
"context"
- "sync"
+ "fmt"
+ "os"
+ "runtime/debug"
+ "sync/atomic"
"time"
"github.com/go-logr/logr"
@@ -43,35 +46,24 @@ import (
// SetLogger sets a concrete logging implementation for all deferred Loggers.
func SetLogger(l logr.Logger) {
- loggerWasSetLock.Lock()
- defer loggerWasSetLock.Unlock()
-
- loggerWasSet = true
- dlog.Fulfill(l.GetSink())
+ logFullfilled.Store(true)
+ rootLog.Fulfill(l.GetSink())
}
-// It is safe to assume that if this wasn't set within the first 30 seconds of a binaries
-// lifetime, it will never get set. The DelegatingLogSink causes a high number of memory
-// allocations when not given an actual Logger, so we set a NullLogSink to avoid that.
-//
-// We need to keep the DelegatingLogSink because we have various inits() that get a logger from
-// here. They will always get executed before any code that imports controller-runtime
-// has a chance to run and hence to set an actual logger.
-func init() {
- // Init is blocking, so start a new goroutine
- go func() {
- time.Sleep(30 * time.Second)
- loggerWasSetLock.Lock()
- defer loggerWasSetLock.Unlock()
- if !loggerWasSet {
- dlog.Fulfill(NullLogSink{})
+func eventuallyFulfillRoot() {
+ if logFullfilled.Load() {
+ return
+ }
+ if time.Since(rootLogCreated).Seconds() >= 30 {
+ if logFullfilled.CompareAndSwap(false, true) {
+ fmt.Fprintf(os.Stderr, "[controller-runtime] log.SetLogger(...) was never called, logs will not be displayed:\n%s", debug.Stack())
+ SetLogger(logr.New(NullLogSink{}))
}
- }()
+ }
}
var (
- loggerWasSetLock sync.Mutex
- loggerWasSet bool
+ logFullfilled atomic.Bool
)
// Log is the base logger used by kubebuilder. It delegates
@@ -80,8 +72,10 @@ var (
// the first 30 seconds of a binaries lifetime, it will get
// set to a NullLogSink.
var (
- dlog = NewDelegatingLogSink(NullLogSink{})
- Log = logr.New(dlog)
+ rootLog, rootLogCreated = func() (*delegatingLogSink, time.Time) {
+ return newDelegatingLogSink(NullLogSink{}), time.Now()
+ }()
+ Log = logr.New(rootLog)
)
// FromContext returns a logger with predefined values from a context.Context.
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/manager/internal.go b/vendor/sigs.k8s.io/controller-runtime/pkg/manager/internal.go
index 5ccff8b78..f298229e5 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/manager/internal.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/manager/internal.go
@@ -18,11 +18,11 @@ package manager
import (
"context"
- "crypto/tls"
"errors"
"fmt"
"net"
"net/http"
+ "net/http/pprof"
"sync"
"sync/atomic"
"time"
@@ -32,7 +32,6 @@ import (
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
kerrors "k8s.io/apimachinery/pkg/util/errors"
- "k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/leaderelection"
"k8s.io/client-go/tools/leaderelection/resourcelock"
@@ -41,12 +40,11 @@ import (
"sigs.k8s.io/controller-runtime/pkg/cache"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/cluster"
- "sigs.k8s.io/controller-runtime/pkg/config/v1alpha1"
+ "sigs.k8s.io/controller-runtime/pkg/config"
"sigs.k8s.io/controller-runtime/pkg/healthz"
"sigs.k8s.io/controller-runtime/pkg/internal/httpserver"
intrec "sigs.k8s.io/controller-runtime/pkg/internal/recorder"
"sigs.k8s.io/controller-runtime/pkg/metrics"
- "sigs.k8s.io/controller-runtime/pkg/runtime/inject"
"sigs.k8s.io/controller-runtime/pkg/webhook"
)
@@ -107,8 +105,11 @@ type controllerManager struct {
// Healthz probe handler
healthzHandler *healthz.Handler
- // controllerOptions are the global controller options.
- controllerOptions v1alpha1.ControllerConfigurationSpec
+ // pprofListener is used to serve pprof
+ pprofListener net.Listener
+
+ // controllerConfig are the global controller options.
+ controllerConfig config.Controller
// Logger is the logger that should be used by this manager.
// If none is set, it defaults to log.Log global logger.
@@ -128,18 +129,7 @@ type controllerManager struct {
// election was configured.
elected chan struct{}
- // port is the port that the webhook server serves at.
- port int
- // host is the hostname that the webhook server binds to.
- host string
- // CertDir is the directory that contains the server key and certificate.
- // if not set, webhook server would look up the server key and certificate in
- // {TempDir}/k8s-webhook-server/serving-certs
- certDir string
- // tlsOpts is used to allow configuring the TLS config used for the webhook server.
- tlsOpts []func(*tls.Config)
-
- webhookServer *webhook.Server
+ webhookServer webhook.Server
// webhookServerOnce will be called in GetWebhookServer() to optionally initialize
// webhookServer if unset, and Add() it to controllerManager.
webhookServerOnce sync.Once
@@ -191,31 +181,9 @@ func (cm *controllerManager) Add(r Runnable) error {
}
func (cm *controllerManager) add(r Runnable) error {
- // Set dependencies on the object
- if err := cm.SetFields(r); err != nil {
- return err
- }
return cm.runnables.Add(r)
}
-// Deprecated: use the equivalent Options field to set a field. This method will be removed in v0.10.
-func (cm *controllerManager) SetFields(i interface{}) error {
- if err := cm.cluster.SetFields(i); err != nil {
- return err
- }
- if _, err := inject.InjectorInto(cm.SetFields, i); err != nil {
- return err
- }
- if _, err := inject.StopChannelInto(cm.internalProceduresStop, i); err != nil {
- return err
- }
- if _, err := inject.LoggerInto(cm.logger, i); err != nil {
- return err
- }
-
- return nil
-}
-
// AddMetricsExtraHandler adds extra handler served on path to the http server that serves metrics.
func (cm *controllerManager) AddMetricsExtraHandler(path string, handler http.Handler) error {
cm.Lock()
@@ -272,6 +240,10 @@ func (cm *controllerManager) AddReadyzCheck(name string, check healthz.Checker)
return nil
}
+func (cm *controllerManager) GetHTTPClient() *http.Client {
+ return cm.cluster.GetHTTPClient()
+}
+
func (cm *controllerManager) GetConfig() *rest.Config {
return cm.cluster.GetConfig()
}
@@ -304,15 +276,10 @@ func (cm *controllerManager) GetAPIReader() client.Reader {
return cm.cluster.GetAPIReader()
}
-func (cm *controllerManager) GetWebhookServer() *webhook.Server {
+func (cm *controllerManager) GetWebhookServer() webhook.Server {
cm.webhookServerOnce.Do(func() {
if cm.webhookServer == nil {
- cm.webhookServer = &webhook.Server{
- Port: cm.port,
- Host: cm.host,
- CertDir: cm.certDir,
- TLSOpts: cm.tlsOpts,
- }
+ panic("webhook should not be nil")
}
if err := cm.Add(cm.webhookServer); err != nil {
panic(fmt.Sprintf("unable to add webhook server to the controller manager: %s", err))
@@ -325,23 +292,29 @@ func (cm *controllerManager) GetLogger() logr.Logger {
return cm.logger
}
-func (cm *controllerManager) GetControllerOptions() v1alpha1.ControllerConfigurationSpec {
- return cm.controllerOptions
+func (cm *controllerManager) GetControllerOptions() config.Controller {
+ return cm.controllerConfig
}
-func (cm *controllerManager) serveMetrics() {
+func (cm *controllerManager) addMetricsServer() error {
+ mux := http.NewServeMux()
+ srv := httpserver.New(mux)
+
handler := promhttp.HandlerFor(metrics.Registry, promhttp.HandlerOpts{
ErrorHandling: promhttp.HTTPErrorOnError,
})
// TODO(JoelSpeed): Use existing Kubernetes machinery for serving metrics
- mux := http.NewServeMux()
mux.Handle(defaultMetricsEndpoint, handler)
for path, extraHandler := range cm.metricsExtraHandlers {
mux.Handle(path, extraHandler)
}
- server := httpserver.New(mux)
- go cm.httpServe("metrics", cm.logger.WithValues("path", defaultMetricsEndpoint), server, cm.metricsListener)
+ return cm.add(&server{
+ Kind: "metrics",
+ Log: cm.logger.WithValues("path", defaultMetricsEndpoint),
+ Server: srv,
+ Listener: cm.metricsListener,
+ })
}
func (cm *controllerManager) serveHealthProbes() {
@@ -362,6 +335,24 @@ func (cm *controllerManager) serveHealthProbes() {
go cm.httpServe("health probe", cm.logger, server, cm.healthProbeListener)
}
+func (cm *controllerManager) addPprofServer() error {
+ mux := http.NewServeMux()
+ srv := httpserver.New(mux)
+
+ mux.HandleFunc("/debug/pprof/", pprof.Index)
+ mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
+ mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
+ mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
+ mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
+
+ return cm.add(&server{
+ Kind: "pprof",
+ Log: cm.logger,
+ Server: srv,
+ Listener: cm.pprofListener,
+ })
+}
+
func (cm *controllerManager) httpServe(kind string, log logr.Logger, server *http.Server, ln net.Listener) {
log = log.WithValues("kind", kind, "addr", ln.Addr())
@@ -451,7 +442,9 @@ func (cm *controllerManager) Start(ctx context.Context) (err error) {
// (If we don't serve metrics for non-leaders, prometheus will still scrape
// the pod but will get a connection refused).
if cm.metricsListener != nil {
- cm.serveMetrics()
+ if err := cm.addMetricsServer(); err != nil {
+ return fmt.Errorf("failed to add metrics server: %w", err)
+ }
}
// Serve health probes.
@@ -459,6 +452,13 @@ func (cm *controllerManager) Start(ctx context.Context) (err error) {
cm.serveHealthProbes()
}
+ // Add pprof server
+ if cm.pprofListener != nil {
+ if err := cm.addPprofServer(); err != nil {
+ return fmt.Errorf("failed to add pprof server: %w", err)
+ }
+ }
+
// First start any webhook servers, which includes conversion, validation, and defaulting
// webhooks that are registered.
//
@@ -466,22 +466,22 @@ func (cm *controllerManager) Start(ctx context.Context) (err error) {
// between conversion webhooks and the cache sync (usually initial list) which causes the webhooks
// to never start because no cache can be populated.
if err := cm.runnables.Webhooks.Start(cm.internalCtx); err != nil {
- if !errors.Is(err, wait.ErrWaitTimeout) {
- return err
+ if err != nil {
+ return fmt.Errorf("failed to start webhooks: %w", err)
}
}
// Start and wait for caches.
if err := cm.runnables.Caches.Start(cm.internalCtx); err != nil {
- if !errors.Is(err, wait.ErrWaitTimeout) {
- return err
+ if err != nil {
+ return fmt.Errorf("failed to start caches: %w", err)
}
}
// Start the non-leaderelection Runnables after the cache has synced.
if err := cm.runnables.Others.Start(cm.internalCtx); err != nil {
- if !errors.Is(err, wait.ErrWaitTimeout) {
- return err
+ if err != nil {
+ return fmt.Errorf("failed to start other runnables: %w", err)
}
}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/manager/manager.go b/vendor/sigs.k8s.io/controller-runtime/pkg/manager/manager.go
index 2facb1c91..72a4a7801 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/manager/manager.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/manager/manager.go
@@ -19,6 +19,7 @@ package manager
import (
"context"
"crypto/tls"
+ "errors"
"fmt"
"net"
"net/http"
@@ -33,6 +34,7 @@ import (
"k8s.io/client-go/tools/leaderelection/resourcelock"
"k8s.io/client-go/tools/record"
"k8s.io/utils/pointer"
+
"sigs.k8s.io/controller-runtime/pkg/cache"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/cluster"
@@ -44,7 +46,6 @@ import (
"sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/metrics"
"sigs.k8s.io/controller-runtime/pkg/recorder"
- "sigs.k8s.io/controller-runtime/pkg/runtime/inject"
"sigs.k8s.io/controller-runtime/pkg/webhook"
)
@@ -55,8 +56,7 @@ type Manager interface {
cluster.Cluster
// Add will set requested dependencies on the component, and cause the component to be
- // started when Start is called. Add will inject any dependencies for which the argument
- // implements the inject interface - e.g. inject.Client.
+ // started when Start is called.
// Depending on if a Runnable implements LeaderElectionRunnable interface, a Runnable can be run in either
// non-leaderelection mode (always running) or leader election mode (managed by leader election if enabled).
Add(Runnable) error
@@ -88,13 +88,13 @@ type Manager interface {
Start(ctx context.Context) error
// GetWebhookServer returns a webhook.Server
- GetWebhookServer() *webhook.Server
+ GetWebhookServer() webhook.Server
// GetLogger returns this manager's logger.
GetLogger() logr.Logger
// GetControllerOptions returns controller global configuration options.
- GetControllerOptions() v1alpha1.ControllerConfigurationSpec
+ GetControllerOptions() config.Controller
}
// Options are the arguments for creating a new Manager.
@@ -102,10 +102,44 @@ type Options struct {
// Scheme is the scheme used to resolve runtime.Objects to GroupVersionKinds / Resources.
// Defaults to the kubernetes/client-go scheme.Scheme, but it's almost always better
// to pass your own scheme in. See the documentation in pkg/scheme for more information.
+ //
+ // If set, the Scheme will be used to create the default Client and Cache.
Scheme *runtime.Scheme
- // MapperProvider provides the rest mapper used to map go types to Kubernetes APIs
- MapperProvider func(c *rest.Config) (meta.RESTMapper, error)
+ // MapperProvider provides the rest mapper used to map go types to Kubernetes APIs.
+ //
+ // If set, the RESTMapper returned by this function is used to create the RESTMapper
+ // used by the Client and Cache.
+ MapperProvider func(c *rest.Config, httpClient *http.Client) (meta.RESTMapper, error)
+
+ // Cache is the cache.Options that will be used to create the default Cache.
+ // By default, the cache will watch and list requested objects in all namespaces.
+ Cache cache.Options
+
+ // NewCache is the function that will create the cache to be used
+ // by the manager. If not set this will use the default new cache function.
+ //
+ // When using a custom NewCache, the Cache options will be passed to the
+ // NewCache function.
+ //
+ // NOTE: LOW LEVEL PRIMITIVE!
+ // Only use a custom NewCache if you know what you are doing.
+ NewCache cache.NewCacheFunc
+
+ // Client is the client.Options that will be used to create the default Client.
+ // By default, the client will use the cache for reads and direct calls for writes.
+ Client client.Options
+
+ // NewClient is the func that creates the client to be used by the manager.
+ // If not set this will create a Client backed by a Cache for read operations
+ // and a direct Client for write operations.
+ //
+ // When using a custom NewClient, the Client options will be passed to the
+ // NewClient function.
+ //
+ // NOTE: LOW LEVEL PRIMITIVE!
+ // Only use a custom NewClient if you know what you are doing.
+ NewClient client.NewClientFunc
// SyncPeriod determines the minimum frequency at which watched resources are
// reconciled. A lower period will correct entropy more quickly, but reduce
@@ -132,6 +166,8 @@ type Options struct {
// is "done" with an object, and would otherwise not requeue it, i.e., we
// recommend the `Reconcile` function return `reconcile.Result{RequeueAfter: t}`,
// instead of `reconcile.Result{}`.
+ //
+ // Deprecated: Use Cache.SyncPeriod instead.
SyncPeriod *time.Duration
// Logger is the logger that should be used by this manager.
@@ -217,6 +253,8 @@ type Options struct {
// Note: If a namespace is specified, controllers can still Watch for a
// cluster-scoped resource (e.g Node). For namespaced resources, the cache
// will only hold objects from the desired namespace.
+ //
+ // Deprecated: Use Cache.Namespaces instead.
Namespace string
// MetricsBindAddress is the TCP address that the controller should bind to
@@ -235,11 +273,22 @@ type Options struct {
// Liveness probe endpoint name, defaults to "healthz"
LivenessEndpointName string
+ // PprofBindAddress is the TCP address that the controller should bind to
+ // for serving pprof.
+ // It can be set to "" or "0" to disable the pprof serving.
+ // Since pprof may contain sensitive information, make sure to protect it
+ // before exposing it to public.
+ PprofBindAddress string
+
// Port is the port that the webhook server serves at.
// It is used to set webhook.Server.Port if WebhookServer is not set.
+ //
+ // Deprecated: Use WebhookServer instead. A WebhookServer can be created via webhook.NewServer.
Port int
// Host is the hostname that the webhook server binds to.
// It is used to set webhook.Server.Host if WebhookServer is not set.
+ //
+ // Deprecated: Use WebhookServer instead. A WebhookServer can be created via webhook.NewServer.
Host string
// CertDir is the directory that contains the server key and certificate.
@@ -247,26 +296,19 @@ type Options struct {
// {TempDir}/k8s-webhook-server/serving-certs. The server key and certificate
// must be named tls.key and tls.crt, respectively.
// It is used to set webhook.Server.CertDir if WebhookServer is not set.
+ //
+ // Deprecated: Use WebhookServer instead. A WebhookServer can be created via webhook.NewServer.
CertDir string
// TLSOpts is used to allow configuring the TLS config used for the webhook server.
+ //
+ // Deprecated: Use WebhookServer instead. A WebhookServer can be created via webhook.NewServer.
TLSOpts []func(*tls.Config)
// WebhookServer is an externally configured webhook.Server. By default,
// a Manager will create a default server using Port, Host, and CertDir;
// if this is set, the Manager will use this server instead.
- WebhookServer *webhook.Server
-
- // Functions to allow for a user to customize values that will be injected.
-
- // NewCache is the function that will create the cache to be used
- // by the manager. If not set this will use the default new cache function.
- NewCache cache.NewCacheFunc
-
- // NewClient is the func that creates the client to be used by the manager.
- // If not set this will create the default DelegatingClient that will
- // use the cache for reads and the client for writes.
- NewClient cluster.NewClientFunc
+ WebhookServer webhook.Server
// BaseContext is the function that provides Context values to Runnables
// managed by the Manager. If a BaseContext function isn't provided, Runnables
@@ -275,10 +317,14 @@ type Options struct {
// ClientDisableCacheFor tells the client that, if any cache is used, to bypass it
// for the given objects.
+ //
+ // Deprecated: Use Client.Cache.DisableCacheFor instead.
ClientDisableCacheFor []client.Object
// DryRunClient specifies whether the client should be configured to enforce
// dryRun mode.
+ //
+ // Deprecated: Use Client.DryRun instead.
DryRunClient bool
// EventBroadcaster records Events emitted by the manager and sends them to the Kubernetes API
@@ -297,7 +343,7 @@ type Options struct {
// Controller contains global configuration options for controllers
// registered within this manager.
// +optional
- Controller v1alpha1.ControllerConfigurationSpec
+ Controller config.Controller
// makeBroadcaster allows deferring the creation of the broadcaster to
// avoid leaking goroutines if we never call Start on this manager. It also
@@ -306,10 +352,11 @@ type Options struct {
makeBroadcaster intrec.EventBroadcasterProducer
// Dependency injection for testing
- newRecorderProvider func(config *rest.Config, scheme *runtime.Scheme, logger logr.Logger, makeBroadcaster intrec.EventBroadcasterProducer) (*intrec.Provider, error)
+ newRecorderProvider func(config *rest.Config, httpClient *http.Client, scheme *runtime.Scheme, logger logr.Logger, makeBroadcaster intrec.EventBroadcasterProducer) (*intrec.Provider, error)
newResourceLock func(config *rest.Config, recorderProvider recorder.Provider, options leaderelection.Options) (resourcelock.Interface, error)
newMetricsListener func(addr string) (net.Listener, error)
newHealthProbeListener func(addr string) (net.Listener, error)
+ newPprofListener func(addr string) (net.Listener, error)
}
// BaseContextFunc is a function used to provide a base Context to Runnables
@@ -345,6 +392,9 @@ type LeaderElectionRunnable interface {
// New returns a new Manager for creating Controllers.
func New(config *rest.Config, options Options) (Manager, error) {
+ if config == nil {
+ return nil, errors.New("must specify Config")
+ }
// Set default values for options fields
options = setOptionsDefaults(options)
@@ -353,21 +403,28 @@ func New(config *rest.Config, options Options) (Manager, error) {
clusterOptions.MapperProvider = options.MapperProvider
clusterOptions.Logger = options.Logger
clusterOptions.SyncPeriod = options.SyncPeriod
- clusterOptions.Namespace = options.Namespace
clusterOptions.NewCache = options.NewCache
clusterOptions.NewClient = options.NewClient
- clusterOptions.ClientDisableCacheFor = options.ClientDisableCacheFor
- clusterOptions.DryRunClient = options.DryRunClient
- clusterOptions.EventBroadcaster = options.EventBroadcaster //nolint:staticcheck
+ clusterOptions.Cache = options.Cache
+ clusterOptions.Client = options.Client
+ clusterOptions.Namespace = options.Namespace //nolint:staticcheck
+ clusterOptions.ClientDisableCacheFor = options.ClientDisableCacheFor //nolint:staticcheck
+ clusterOptions.DryRunClient = options.DryRunClient //nolint:staticcheck
+ clusterOptions.EventBroadcaster = options.EventBroadcaster //nolint:staticcheck
})
if err != nil {
return nil, err
}
+ config = rest.CopyConfig(config)
+ if config.UserAgent == "" {
+ config.UserAgent = rest.DefaultKubernetesUserAgent()
+ }
+
// Create the recorder provider to inject event recorders for the components.
// TODO(directxman12): the log for the event provider should have a context (name, tags, etc) specific
// to the particular controller that it's being injected into, rather than a generic one like is here.
- recorderProvider, err := options.newRecorderProvider(config, cluster.GetScheme(), options.Logger.WithName("events"), options.makeBroadcaster)
+ recorderProvider, err := options.newRecorderProvider(config, cluster.GetHTTPClient(), cluster.GetScheme(), options.Logger.WithName("events"), options.makeBroadcaster)
if err != nil {
return nil, err
}
@@ -381,7 +438,7 @@ func New(config *rest.Config, options Options) (Manager, error) {
leaderRecorderProvider = recorderProvider
} else {
leaderConfig = rest.CopyConfig(options.LeaderElectionConfig)
- leaderRecorderProvider, err = options.newRecorderProvider(leaderConfig, cluster.GetScheme(), options.Logger.WithName("events"), options.makeBroadcaster)
+ leaderRecorderProvider, err = options.newRecorderProvider(leaderConfig, cluster.GetHTTPClient(), cluster.GetScheme(), options.Logger.WithName("events"), options.makeBroadcaster)
if err != nil {
return nil, err
}
@@ -419,6 +476,13 @@ func New(config *rest.Config, options Options) (Manager, error) {
return nil, err
}
+ // Create pprof listener. This will throw an error if the bind
+ // address is invalid or already in use.
+ pprofListener, err := options.newPprofListener(options.PprofBindAddress)
+ if err != nil {
+ return nil, fmt.Errorf("failed to new pprof listener: %w", err)
+ }
+
errChan := make(chan error)
runnables := newRunnables(options.BaseContext, errChan)
@@ -431,13 +495,9 @@ func New(config *rest.Config, options Options) (Manager, error) {
resourceLock: resourceLock,
metricsListener: metricsListener,
metricsExtraHandlers: metricsExtraHandlers,
- controllerOptions: options.Controller,
+ controllerConfig: options.Controller,
logger: options.Logger,
elected: make(chan struct{}),
- port: options.Port,
- host: options.Host,
- certDir: options.CertDir,
- tlsOpts: options.TLSOpts,
webhookServer: options.WebhookServer,
leaderElectionID: options.LeaderElectionID,
leaseDuration: *options.LeaseDuration,
@@ -446,6 +506,7 @@ func New(config *rest.Config, options Options) (Manager, error) {
healthProbeListener: healthProbeListener,
readinessEndpointName: options.ReadinessEndpointName,
livenessEndpointName: options.LivenessEndpointName,
+ pprofListener: pprofListener,
gracefulShutdownTimeout: *options.GracefulShutdownTimeout,
internalProceduresStop: make(chan struct{}),
leaderElectionStopped: make(chan struct{}),
@@ -456,14 +517,14 @@ func New(config *rest.Config, options Options) (Manager, error) {
// AndFrom will use a supplied type and convert to Options
// any options already set on Options will be ignored, this is used to allow
// cli flags to override anything specified in the config file.
+//
+// Deprecated: This function has been deprecated and will be removed in a future release,
+// The Component Configuration package has been unmaintained for over a year and is no longer
+// actively developed. Users should migrate to their own configuration format
+// and configure Manager.Options directly.
+// See https://github.com/kubernetes-sigs/controller-runtime/issues/895
+// for more information, feedback, and comments.
func (o Options) AndFrom(loader config.ControllerManagerConfiguration) (Options, error) {
- if inj, wantsScheme := loader.(inject.Scheme); wantsScheme {
- err := inj.InjectScheme(o.Scheme)
- if err != nil {
- return o, err
- }
- }
-
newObj, err := loader.Complete()
if err != nil {
return o, err
@@ -498,18 +559,23 @@ func (o Options) AndFrom(loader config.ControllerManagerConfiguration) (Options,
if o.Port == 0 && newObj.Webhook.Port != nil {
o.Port = *newObj.Webhook.Port
}
-
if o.Host == "" && newObj.Webhook.Host != "" {
o.Host = newObj.Webhook.Host
}
-
if o.CertDir == "" && newObj.Webhook.CertDir != "" {
o.CertDir = newObj.Webhook.CertDir
}
+ if o.WebhookServer == nil {
+ o.WebhookServer = webhook.NewServer(webhook.Options{
+ Port: o.Port,
+ Host: o.Host,
+ CertDir: o.CertDir,
+ })
+ }
if newObj.Controller != nil {
- if o.Controller.CacheSyncTimeout == nil && newObj.Controller.CacheSyncTimeout != nil {
- o.Controller.CacheSyncTimeout = newObj.Controller.CacheSyncTimeout
+ if o.Controller.CacheSyncTimeout == 0 && newObj.Controller.CacheSyncTimeout != nil {
+ o.Controller.CacheSyncTimeout = *newObj.Controller.CacheSyncTimeout
}
if len(o.Controller.GroupKindConcurrency) == 0 && len(newObj.Controller.GroupKindConcurrency) > 0 {
@@ -521,6 +587,13 @@ func (o Options) AndFrom(loader config.ControllerManagerConfiguration) (Options,
}
// AndFromOrDie will use options.AndFrom() and will panic if there are errors.
+//
+// Deprecated: This function has been deprecated and will be removed in a future release,
+// The Component Configuration package has been unmaintained for over a year and is no longer
+// actively developed. Users should migrate to their own configuration format
+// and configure Manager.Options directly.
+// See https://github.com/kubernetes-sigs/controller-runtime/issues/895
+// for more information, feedback, and comments.
func (o Options) AndFromOrDie(loader config.ControllerManagerConfiguration) Options {
o, err := o.AndFrom(loader)
if err != nil {
@@ -579,6 +652,19 @@ func defaultHealthProbeListener(addr string) (net.Listener, error) {
return ln, nil
}
+// defaultPprofListener creates the default pprof listener bound to the given address.
+func defaultPprofListener(addr string) (net.Listener, error) {
+ if addr == "" || addr == "0" {
+ return nil, nil
+ }
+
+ ln, err := net.Listen("tcp", addr)
+ if err != nil {
+ return nil, fmt.Errorf("error listening on %s: %w", addr, err)
+ }
+ return ln, nil
+}
+
// defaultBaseContext is used as the BaseContext value in Options if one
// has not already been set.
func defaultBaseContext() context.Context {
@@ -639,6 +725,10 @@ func setOptionsDefaults(options Options) Options {
options.newHealthProbeListener = defaultHealthProbeListener
}
+ if options.newPprofListener == nil {
+ options.newPprofListener = defaultPprofListener
+ }
+
if options.GracefulShutdownTimeout == nil {
gracefulShutdownTimeout := defaultGracefulShutdownPeriod
options.GracefulShutdownTimeout = &gracefulShutdownTimeout
@@ -652,5 +742,14 @@ func setOptionsDefaults(options Options) Options {
options.BaseContext = defaultBaseContext
}
+ if options.WebhookServer == nil {
+ options.WebhookServer = webhook.NewServer(webhook.Options{
+ Host: options.Host,
+ Port: options.Port,
+ CertDir: options.CertDir,
+ TLSOpts: options.TLSOpts,
+ })
+ }
+
return options
}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/manager/runnable_group.go b/vendor/sigs.k8s.io/controller-runtime/pkg/manager/runnable_group.go
index f7b91a209..549741e6e 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/manager/runnable_group.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/manager/runnable_group.go
@@ -56,7 +56,7 @@ func (r *runnables) Add(fn Runnable) error {
return r.Caches.Add(fn, func(ctx context.Context) bool {
return runnable.GetCache().WaitForCacheSync(ctx)
})
- case *webhook.Server:
+ case webhook.Server:
return r.Webhooks.Add(fn, nil)
case LeaderElectionRunnable:
if !runnable.NeedLeaderElection() {
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/manager/server.go b/vendor/sigs.k8s.io/controller-runtime/pkg/manager/server.go
new file mode 100644
index 000000000..b6509f48f
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/manager/server.go
@@ -0,0 +1,61 @@
+/*
+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 manager
+
+import (
+ "context"
+ "errors"
+ "net"
+ "net/http"
+
+ "github.com/go-logr/logr"
+)
+
+// server is a general purpose HTTP server Runnable for a manager
+// to serve some internal handlers such as health probes, metrics and profiling.
+type server struct {
+ Kind string
+ Log logr.Logger
+ Server *http.Server
+ Listener net.Listener
+}
+
+func (s *server) Start(ctx context.Context) error {
+ log := s.Log.WithValues("kind", s.Kind, "addr", s.Listener.Addr())
+
+ serverShutdown := make(chan struct{})
+ go func() {
+ <-ctx.Done()
+ log.Info("shutting down server")
+ if err := s.Server.Shutdown(context.Background()); err != nil {
+ log.Error(err, "error shutting down server")
+ }
+ close(serverShutdown)
+ }()
+
+ log.Info("starting server")
+ if err := s.Server.Serve(s.Listener); err != nil && !errors.Is(err, http.ErrServerClosed) {
+ return err
+ }
+
+ <-serverShutdown
+ return nil
+}
+
+func (s *server) NeedLeaderElection() bool {
+ return false
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/metrics/client_go_adapter.go b/vendor/sigs.k8s.io/controller-runtime/pkg/metrics/client_go_adapter.go
index a8b43ea0a..ff28998c4 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/metrics/client_go_adapter.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/metrics/client_go_adapter.go
@@ -18,8 +18,6 @@ package metrics
import (
"context"
- "net/url"
- "time"
"github.com/prometheus/client_golang/prometheus"
clientmetrics "k8s.io/client-go/tools/metrics"
@@ -29,70 +27,9 @@ import (
// that client-go registers metrics. We copy the names and formats
// from Kubernetes so that we match the core controllers.
-// Metrics subsystem and all of the keys used by the rest client.
-const (
- RestClientSubsystem = "rest_client"
- LatencyKey = "request_latency_seconds"
- ResultKey = "requests_total"
-)
-
var (
// client metrics.
- // RequestLatency reports the request latency in seconds per verb/URL.
- // Deprecated: This metric is deprecated for removal in a future release: using the URL as a
- // dimension results in cardinality explosion for some consumers. It was deprecated upstream
- // in k8s v1.14 and hidden in v1.17 via https://github.com/kubernetes/kubernetes/pull/83836.
- // It is not registered by default. To register:
- // import (
- // clientmetrics "k8s.io/client-go/tools/metrics"
- // clmetrics "sigs.k8s.io/controller-runtime/metrics"
- // )
- //
- // func init() {
- // clmetrics.Registry.MustRegister(clmetrics.RequestLatency)
- // clientmetrics.Register(clientmetrics.RegisterOpts{
- // RequestLatency: clmetrics.LatencyAdapter
- // })
- // }
- RequestLatency = prometheus.NewHistogramVec(prometheus.HistogramOpts{
- Subsystem: RestClientSubsystem,
- Name: LatencyKey,
- Help: "Request latency in seconds. Broken down by verb and URL.",
- Buckets: prometheus.ExponentialBuckets(0.001, 2, 10),
- }, []string{"verb", "url"})
-
- // requestLatency is a Prometheus Histogram metric type partitioned by
- // "verb", and "host" labels. It is used for the rest client latency metrics.
- requestLatency = prometheus.NewHistogramVec(
- prometheus.HistogramOpts{
- Name: "rest_client_request_duration_seconds",
- Help: "Request latency in seconds. Broken down by verb, and host.",
- Buckets: []float64{0.005, 0.025, 0.1, 0.25, 0.5, 1.0, 2.0, 4.0, 8.0, 15.0, 30.0, 60.0},
- },
- []string{"verb", "host"},
- )
-
- requestSize = prometheus.NewHistogramVec(
- prometheus.HistogramOpts{
- Name: "rest_client_request_size_bytes",
- Help: "Request size in bytes. Broken down by verb and host.",
- // 64 bytes to 16MB
- Buckets: []float64{64, 256, 512, 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216},
- },
- []string{"verb", "host"},
- )
-
- responseSize = prometheus.NewHistogramVec(
- prometheus.HistogramOpts{
- Name: "rest_client_response_size_bytes",
- Help: "Response size in bytes. Broken down by verb and host.",
- // 64 bytes to 16MB
- Buckets: []float64{64, 256, 512, 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216},
- },
- []string{"verb", "host"},
- )
-
requestResult = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "rest_client_requests_total",
@@ -109,17 +46,11 @@ func init() {
// registerClientMetrics sets up the client latency metrics from client-go.
func registerClientMetrics() {
// register the metrics with our registry
- Registry.MustRegister(requestLatency)
- Registry.MustRegister(requestSize)
- Registry.MustRegister(responseSize)
Registry.MustRegister(requestResult)
// register the metrics with client-go
clientmetrics.Register(clientmetrics.RegisterOpts{
- RequestLatency: &LatencyAdapter{metric: requestLatency},
- RequestSize: &sizeAdapter{metric: requestSize},
- ResponseSize: &sizeAdapter{metric: responseSize},
- RequestResult: &resultAdapter{metric: requestResult},
+ RequestResult: &resultAdapter{metric: requestResult},
})
}
@@ -131,24 +62,6 @@ func registerClientMetrics() {
// copied (more-or-less directly) from k8s.io/kubernetes setup code
// (which isn't anywhere in an easily-importable place).
-// LatencyAdapter implements LatencyMetric.
-type LatencyAdapter struct {
- metric *prometheus.HistogramVec
-}
-
-// Observe increments the request latency metric for the given verb/URL.
-func (l *LatencyAdapter) Observe(_ context.Context, verb string, u url.URL, latency time.Duration) {
- l.metric.WithLabelValues(verb, u.String()).Observe(latency.Seconds())
-}
-
-type sizeAdapter struct {
- metric *prometheus.HistogramVec
-}
-
-func (s *sizeAdapter) Observe(ctx context.Context, verb string, host string, size float64) {
- s.metric.WithLabelValues(verb, host).Observe(size)
-}
-
type resultAdapter struct {
metric *prometheus.CounterVec
}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/predicate/predicate.go b/vendor/sigs.k8s.io/controller-runtime/pkg/predicate/predicate.go
index 8b0f3634e..314635875 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/predicate/predicate.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/predicate/predicate.go
@@ -24,7 +24,6 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/event"
logf "sigs.k8s.io/controller-runtime/pkg/internal/log"
- "sigs.k8s.io/controller-runtime/pkg/runtime/inject"
)
var log = logf.RuntimeLog.WithName("predicate").WithName("eventFilters")
@@ -242,15 +241,6 @@ type and struct {
predicates []Predicate
}
-func (a and) InjectFunc(f inject.Func) error {
- for _, p := range a.predicates {
- if err := f(p); err != nil {
- return err
- }
- }
- return nil
-}
-
func (a and) Create(e event.CreateEvent) bool {
for _, p := range a.predicates {
if !p.Create(e) {
@@ -296,15 +286,6 @@ type or struct {
predicates []Predicate
}
-func (o or) InjectFunc(f inject.Func) error {
- for _, p := range o.predicates {
- if err := f(p); err != nil {
- return err
- }
- }
- return nil
-}
-
func (o or) Create(e event.CreateEvent) bool {
for _, p := range o.predicates {
if p.Create(e) {
@@ -350,10 +331,6 @@ type not struct {
predicate Predicate
}
-func (n not) InjectFunc(f inject.Func) error {
- return f(n.predicate)
-}
-
func (n not) Create(e event.CreateEvent) bool {
return !n.predicate.Create(e)
}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/reconcile/reconcile.go b/vendor/sigs.k8s.io/controller-runtime/pkg/reconcile/reconcile.go
index 8285e2ca9..4c8f8357a 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/reconcile/reconcile.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/reconcile/reconcile.go
@@ -18,6 +18,7 @@ package reconcile
import (
"context"
+ "errors"
"time"
"k8s.io/apimachinery/pkg/types"
@@ -100,3 +101,30 @@ var _ Reconciler = Func(nil)
// Reconcile implements Reconciler.
func (r Func) Reconcile(ctx context.Context, o Request) (Result, error) { return r(ctx, o) }
+
+// TerminalError is an error that will not be retried but still be logged
+// and recorded in metrics.
+func TerminalError(wrapped error) error {
+ return &terminalError{err: wrapped}
+}
+
+type terminalError struct {
+ err error
+}
+
+// This function will return nil if te.err is nil.
+func (te *terminalError) Unwrap() error {
+ return te.err
+}
+
+func (te *terminalError) Error() string {
+ if te.err == nil {
+ return "nil terminal error"
+ }
+ return "terminal error: " + te.err.Error()
+}
+
+func (te *terminalError) Is(target error) bool {
+ tp := &terminalError{}
+ return errors.As(target, &tp)
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/runtime/inject/doc.go b/vendor/sigs.k8s.io/controller-runtime/pkg/runtime/inject/doc.go
deleted file mode 100644
index 17c60895f..000000000
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/runtime/inject/doc.go
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
-Copyright 2018 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 inject defines interfaces and functions for propagating dependencies from a ControllerManager to
-the components registered with it. Dependencies are propagated to Reconciler, Source, EventHandler and Predicate
-objects which implement the Injectable interfaces.
-*/
-package inject
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/runtime/inject/inject.go b/vendor/sigs.k8s.io/controller-runtime/pkg/runtime/inject/inject.go
deleted file mode 100644
index c8c56ba81..000000000
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/runtime/inject/inject.go
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
-Copyright 2018 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 inject is used by a Manager to inject types into Sources, EventHandlers, Predicates, and Reconciles.
-// Deprecated: Use manager.Options fields directly. This package will be removed in v0.10.
-package inject
-
-import (
- "github.com/go-logr/logr"
- "k8s.io/apimachinery/pkg/api/meta"
- "k8s.io/apimachinery/pkg/runtime"
- "k8s.io/client-go/rest"
-
- "sigs.k8s.io/controller-runtime/pkg/cache"
- "sigs.k8s.io/controller-runtime/pkg/client"
-)
-
-// Cache is used by the ControllerManager to inject Cache into Sources, EventHandlers, Predicates, and
-// Reconciles.
-type Cache interface {
- InjectCache(cache cache.Cache) error
-}
-
-// CacheInto will set informers on i and return the result if it implements Cache. Returns
-// false if i does not implement Cache.
-func CacheInto(c cache.Cache, i interface{}) (bool, error) {
- if s, ok := i.(Cache); ok {
- return true, s.InjectCache(c)
- }
- return false, nil
-}
-
-// APIReader is used by the Manager to inject the APIReader into necessary types.
-type APIReader interface {
- InjectAPIReader(client.Reader) error
-}
-
-// APIReaderInto will set APIReader on i and return the result if it implements APIReaderInto.
-// Returns false if i does not implement APIReader.
-func APIReaderInto(reader client.Reader, i interface{}) (bool, error) {
- if s, ok := i.(APIReader); ok {
- return true, s.InjectAPIReader(reader)
- }
- return false, nil
-}
-
-// Config is used by the ControllerManager to inject Config into Sources, EventHandlers, Predicates, and
-// Reconciles.
-type Config interface {
- InjectConfig(*rest.Config) error
-}
-
-// ConfigInto will set config on i and return the result if it implements Config. Returns
-// false if i does not implement Config.
-func ConfigInto(config *rest.Config, i interface{}) (bool, error) {
- if s, ok := i.(Config); ok {
- return true, s.InjectConfig(config)
- }
- return false, nil
-}
-
-// Client is used by the ControllerManager to inject client into Sources, EventHandlers, Predicates, and
-// Reconciles.
-type Client interface {
- InjectClient(client.Client) error
-}
-
-// ClientInto will set client on i and return the result if it implements Client. Returns
-// false if i does not implement Client.
-func ClientInto(client client.Client, i interface{}) (bool, error) {
- if s, ok := i.(Client); ok {
- return true, s.InjectClient(client)
- }
- return false, nil
-}
-
-// Scheme is used by the ControllerManager to inject Scheme into Sources, EventHandlers, Predicates, and
-// Reconciles.
-type Scheme interface {
- InjectScheme(scheme *runtime.Scheme) error
-}
-
-// SchemeInto will set scheme and return the result on i if it implements Scheme. Returns
-// false if i does not implement Scheme.
-func SchemeInto(scheme *runtime.Scheme, i interface{}) (bool, error) {
- if is, ok := i.(Scheme); ok {
- return true, is.InjectScheme(scheme)
- }
- return false, nil
-}
-
-// Stoppable is used by the ControllerManager to inject stop channel into Sources,
-// EventHandlers, Predicates, and Reconciles.
-type Stoppable interface {
- InjectStopChannel(<-chan struct{}) error
-}
-
-// StopChannelInto will set stop channel on i and return the result if it implements Stoppable.
-// Returns false if i does not implement Stoppable.
-func StopChannelInto(stop <-chan struct{}, i interface{}) (bool, error) {
- if s, ok := i.(Stoppable); ok {
- return true, s.InjectStopChannel(stop)
- }
- return false, nil
-}
-
-// Mapper is used to inject the rest mapper to components that may need it.
-type Mapper interface {
- InjectMapper(meta.RESTMapper) error
-}
-
-// MapperInto will set the rest mapper on i and return the result if it implements Mapper.
-// Returns false if i does not implement Mapper.
-func MapperInto(mapper meta.RESTMapper, i interface{}) (bool, error) {
- if m, ok := i.(Mapper); ok {
- return true, m.InjectMapper(mapper)
- }
- return false, nil
-}
-
-// Func injects dependencies into i.
-type Func func(i interface{}) error
-
-// Injector is used by the ControllerManager to inject Func into Controllers.
-type Injector interface {
- InjectFunc(f Func) error
-}
-
-// InjectorInto will set f and return the result on i if it implements Injector. Returns
-// false if i does not implement Injector.
-func InjectorInto(f Func, i interface{}) (bool, error) {
- if ii, ok := i.(Injector); ok {
- return true, ii.InjectFunc(f)
- }
- return false, nil
-}
-
-// Logger is used to inject Loggers into components that need them
-// and don't otherwise have opinions.
-type Logger interface {
- InjectLogger(l logr.Logger) error
-}
-
-// LoggerInto will set the logger on the given object if it implements inject.Logger,
-// returning true if a InjectLogger was called, and false otherwise.
-func LoggerInto(l logr.Logger, i interface{}) (bool, error) {
- if injectable, wantsLogger := i.(Logger); wantsLogger {
- return true, injectable.InjectLogger(l)
- }
- return false, nil
-}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/source/source.go b/vendor/sigs.k8s.io/controller-runtime/pkg/source/source.go
index 6b6756392..099c8d68f 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/source/source.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/source/source.go
@@ -18,28 +18,19 @@ package source
import (
"context"
- "errors"
"fmt"
"sync"
- "time"
- "k8s.io/apimachinery/pkg/api/meta"
- "k8s.io/apimachinery/pkg/runtime"
- "k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/util/workqueue"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/event"
"sigs.k8s.io/controller-runtime/pkg/handler"
- logf "sigs.k8s.io/controller-runtime/pkg/internal/log"
- "sigs.k8s.io/controller-runtime/pkg/runtime/inject"
- "sigs.k8s.io/controller-runtime/pkg/source/internal"
+ internal "sigs.k8s.io/controller-runtime/pkg/internal/source"
"sigs.k8s.io/controller-runtime/pkg/cache"
"sigs.k8s.io/controller-runtime/pkg/predicate"
)
-var log = logf.RuntimeLog.WithName("source")
-
const (
// defaultBufferSize is the default number of event notifications that can be buffered.
defaultBufferSize = 1024
@@ -52,8 +43,7 @@ const (
//
// * Use Channel for events originating outside the cluster (eh.g. GitHub Webhook callback, Polling external urls).
//
-// Users may build their own Source implementations. If their implementations implement any of the inject package
-// interfaces, the dependencies will be injected by the Controller when Watch is called.
+// Users may build their own Source implementations.
type Source interface {
// Start is internal and should be called only by the Controller to register an EventHandler with the Informer
// to enqueue reconcile.Requests.
@@ -67,144 +57,9 @@ type SyncingSource interface {
WaitForSync(ctx context.Context) error
}
-// NewKindWithCache creates a Source without InjectCache, so that it is assured that the given cache is used
-// and not overwritten. It can be used to watch objects in a different cluster by passing the cache
-// from that other cluster.
-func NewKindWithCache(object client.Object, cache cache.Cache) SyncingSource {
- return &kindWithCache{kind: Kind{Type: object, cache: cache}}
-}
-
-type kindWithCache struct {
- kind Kind
-}
-
-func (ks *kindWithCache) Start(ctx context.Context, handler handler.EventHandler, queue workqueue.RateLimitingInterface,
- prct ...predicate.Predicate) error {
- return ks.kind.Start(ctx, handler, queue, prct...)
-}
-
-func (ks *kindWithCache) String() string {
- return ks.kind.String()
-}
-
-func (ks *kindWithCache) WaitForSync(ctx context.Context) error {
- return ks.kind.WaitForSync(ctx)
-}
-
-// Kind is used to provide a source of events originating inside the cluster from Watches (e.g. Pod Create).
-type Kind struct {
- // Type is the type of object to watch. e.g. &v1.Pod{}
- Type client.Object
-
- // cache used to watch APIs
- cache cache.Cache
-
- // started may contain an error if one was encountered during startup. If its closed and does not
- // contain an error, startup and syncing finished.
- started chan error
- startCancel func()
-}
-
-var _ SyncingSource = &Kind{}
-
-// Start is internal and should be called only by the Controller to register an EventHandler with the Informer
-// to enqueue reconcile.Requests.
-func (ks *Kind) Start(ctx context.Context, handler handler.EventHandler, queue workqueue.RateLimitingInterface,
- prct ...predicate.Predicate) error {
- // Type should have been specified by the user.
- if ks.Type == nil {
- return fmt.Errorf("must specify Kind.Type")
- }
-
- // cache should have been injected before Start was called
- if ks.cache == nil {
- return fmt.Errorf("must call CacheInto on Kind before calling Start")
- }
-
- // cache.GetInformer will block until its context is cancelled if the cache was already started and it can not
- // sync that informer (most commonly due to RBAC issues).
- ctx, ks.startCancel = context.WithCancel(ctx)
- ks.started = make(chan error)
- go func() {
- var (
- i cache.Informer
- lastErr error
- )
-
- // Tries to get an informer until it returns true,
- // an error or the specified context is cancelled or expired.
- if err := wait.PollImmediateUntilWithContext(ctx, 10*time.Second, func(ctx context.Context) (bool, error) {
- // Lookup the Informer from the Cache and add an EventHandler which populates the Queue
- i, lastErr = ks.cache.GetInformer(ctx, ks.Type)
- if lastErr != nil {
- kindMatchErr := &meta.NoKindMatchError{}
- switch {
- case errors.As(lastErr, &kindMatchErr):
- log.Error(lastErr, "if kind is a CRD, it should be installed before calling Start",
- "kind", kindMatchErr.GroupKind)
- case runtime.IsNotRegisteredError(lastErr):
- log.Error(lastErr, "kind must be registered to the Scheme")
- default:
- log.Error(lastErr, "failed to get informer from cache")
- }
- return false, nil // Retry.
- }
- return true, nil
- }); err != nil {
- if lastErr != nil {
- ks.started <- fmt.Errorf("failed to get informer from cache: %w", lastErr)
- return
- }
- ks.started <- err
- return
- }
-
- _, err := i.AddEventHandler(internal.EventHandler{Queue: queue, EventHandler: handler, Predicates: prct})
- if err != nil {
- ks.started <- err
- return
- }
- if !ks.cache.WaitForCacheSync(ctx) {
- // Would be great to return something more informative here
- ks.started <- errors.New("cache did not sync")
- }
- close(ks.started)
- }()
-
- return nil
-}
-
-func (ks *Kind) String() string {
- if ks.Type != nil {
- return fmt.Sprintf("kind source: %T", ks.Type)
- }
- return "kind source: unknown type"
-}
-
-// WaitForSync implements SyncingSource to allow controllers to wait with starting
-// workers until the cache is synced.
-func (ks *Kind) WaitForSync(ctx context.Context) error {
- select {
- case err := <-ks.started:
- return err
- case <-ctx.Done():
- ks.startCancel()
- if errors.Is(ctx.Err(), context.Canceled) {
- return nil
- }
- return errors.New("timed out waiting for cache to be synced")
- }
-}
-
-var _ inject.Cache = &Kind{}
-
-// InjectCache is internal should be called only by the Controller. InjectCache is used to inject
-// the Cache dependency initialized by the ControllerManager.
-func (ks *Kind) InjectCache(c cache.Cache) error {
- if ks.cache == nil {
- ks.cache = c
- }
- return nil
+// Kind creates a KindSource with the given cache provider.
+func Kind(cache cache.Cache, object client.Object) SyncingSource {
+ return &internal.Kind{Type: object, Cache: cache}
}
var _ Source = &Channel{}
@@ -219,9 +74,6 @@ type Channel struct {
// Source is the source channel to fetch GenericEvents
Source <-chan event.GenericEvent
- // stop is to end ongoing goroutine, and close the channels
- stop <-chan struct{}
-
// dest is the destination channels of the added event handlers
dest []chan event.GenericEvent
@@ -237,18 +89,6 @@ func (cs *Channel) String() string {
return fmt.Sprintf("channel source: %p", cs)
}
-var _ inject.Stoppable = &Channel{}
-
-// InjectStopChannel is internal should be called only by the Controller.
-// It is used to inject the stop channel initialized by the ControllerManager.
-func (cs *Channel) InjectStopChannel(stop <-chan struct{}) error {
- if cs.stop == nil {
- cs.stop = stop
- }
-
- return nil
-}
-
// Start implements Source and should only be called by the Controller.
func (cs *Channel) Start(
ctx context.Context,
@@ -260,11 +100,6 @@ func (cs *Channel) Start(
return fmt.Errorf("must specify Channel.Source")
}
- // stop should have been injected before Start was called
- if cs.stop == nil {
- return fmt.Errorf("must call InjectStop on Channel before calling Start")
- }
-
// use default value if DestBufferSize not specified
if cs.DestBufferSize == 0 {
cs.DestBufferSize = defaultBufferSize
@@ -292,7 +127,11 @@ func (cs *Channel) Start(
}
if shouldHandle {
- handler.Generic(evt, queue)
+ func() {
+ ctx, cancel := context.WithCancel(ctx)
+ defer cancel()
+ handler.Generic(ctx, evt, queue)
+ }()
}
}
}()
@@ -359,7 +198,7 @@ func (is *Informer) Start(ctx context.Context, handler handler.EventHandler, que
return fmt.Errorf("must specify Informer.Informer")
}
- _, err := is.Informer.AddEventHandler(internal.EventHandler{Queue: queue, EventHandler: handler, Predicates: prct})
+ _, err := is.Informer.AddEventHandler(internal.NewEventHandler(ctx, queue, handler, prct).HandlerFuncs())
if err != nil {
return err
}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/decode.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/decode.go
index c7cb71b75..7e9c0a96b 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/decode.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/decode.go
@@ -19,7 +19,6 @@ package admission
import (
"fmt"
- "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/apimachinery/pkg/util/json"
@@ -32,8 +31,11 @@ type Decoder struct {
}
// NewDecoder creates a Decoder given the runtime.Scheme.
-func NewDecoder(scheme *runtime.Scheme) (*Decoder, error) {
- return &Decoder{codecs: serializer.NewCodecFactory(scheme)}, nil
+func NewDecoder(scheme *runtime.Scheme) *Decoder {
+ if scheme == nil {
+ panic("scheme should never be nil")
+ }
+ return &Decoder{codecs: serializer.NewCodecFactory(scheme)}
}
// Decode decodes the inlined object in the AdmissionRequest into the passed-in runtime.Object.
@@ -62,9 +64,14 @@ func (d *Decoder) DecodeRaw(rawObj runtime.RawExtension, into runtime.Object) er
if len(rawObj.Raw) == 0 {
return fmt.Errorf("there is no content to decode")
}
- if unstructuredInto, isUnstructured := into.(*unstructured.Unstructured); isUnstructured {
+ if unstructuredInto, isUnstructured := into.(runtime.Unstructured); isUnstructured {
// unmarshal into unstructured's underlying object to avoid calling the decoder
- return json.Unmarshal(rawObj.Raw, &unstructuredInto.Object)
+ var object map[string]interface{}
+ if err := json.Unmarshal(rawObj.Raw, &object); err != nil {
+ return err
+ }
+ unstructuredInto.SetUnstructuredContent(object)
+ return nil
}
deserializer := d.codecs.UniversalDeserializer()
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/defaulter.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/defaulter.go
index e4e0778f5..a3b720716 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/defaulter.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/defaulter.go
@@ -33,9 +33,9 @@ type Defaulter interface {
}
// DefaultingWebhookFor creates a new Webhook for Defaulting the provided type.
-func DefaultingWebhookFor(defaulter Defaulter) *Webhook {
+func DefaultingWebhookFor(scheme *runtime.Scheme, defaulter Defaulter) *Webhook {
return &Webhook{
- Handler: &mutatingHandler{defaulter: defaulter},
+ Handler: &mutatingHandler{defaulter: defaulter, decoder: NewDecoder(scheme)},
}
}
@@ -44,16 +44,11 @@ type mutatingHandler struct {
decoder *Decoder
}
-var _ DecoderInjector = &mutatingHandler{}
-
-// InjectDecoder injects the decoder into a mutatingHandler.
-func (h *mutatingHandler) InjectDecoder(d *Decoder) error {
- h.decoder = d
- return nil
-}
-
// Handle handles admission requests.
func (h *mutatingHandler) Handle(ctx context.Context, req Request) Response {
+ if h.decoder == nil {
+ panic("decoder should never be nil")
+ }
if h.defaulter == nil {
panic("defaulter should never be nil")
}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/defaulter_custom.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/defaulter_custom.go
index 700798424..5f697e7dc 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/defaulter_custom.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/defaulter_custom.go
@@ -34,9 +34,9 @@ type CustomDefaulter interface {
}
// WithCustomDefaulter creates a new Webhook for a CustomDefaulter interface.
-func WithCustomDefaulter(obj runtime.Object, defaulter CustomDefaulter) *Webhook {
+func WithCustomDefaulter(scheme *runtime.Scheme, obj runtime.Object, defaulter CustomDefaulter) *Webhook {
return &Webhook{
- Handler: &defaulterForType{object: obj, defaulter: defaulter},
+ Handler: &defaulterForType{object: obj, defaulter: defaulter, decoder: NewDecoder(scheme)},
}
}
@@ -46,15 +46,11 @@ type defaulterForType struct {
decoder *Decoder
}
-var _ DecoderInjector = &defaulterForType{}
-
-func (h *defaulterForType) InjectDecoder(d *Decoder) error {
- h.decoder = d
- return nil
-}
-
// Handle handles admission requests.
func (h *defaulterForType) Handle(ctx context.Context, req Request) Response {
+ if h.decoder == nil {
+ panic("decoder should never be nil")
+ }
if h.defaulter == nil {
panic("defaulter should never be nil")
}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/doc.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/doc.go
index 0b274dd02..8dc0cbec6 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/doc.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/doc.go
@@ -20,9 +20,3 @@ Package admission provides implementation for admission webhook and methods to i
See examples/mutatingwebhook.go and examples/validatingwebhook.go for examples of admission webhooks.
*/
package admission
-
-import (
- logf "sigs.k8s.io/controller-runtime/pkg/internal/log"
-)
-
-var log = logf.RuntimeLog.WithName("admission")
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/http.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/http.go
index 066cc4225..84ab5e75a 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/http.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/http.go
@@ -52,7 +52,7 @@ func (wh *Webhook) ServeHTTP(w http.ResponseWriter, r *http.Request) {
var reviewResponse Response
if r.Body == nil {
err = errors.New("request body is empty")
- wh.log.Error(err, "bad request")
+ wh.getLogger(nil).Error(err, "bad request")
reviewResponse = Errored(http.StatusBadRequest, err)
wh.writeResponse(w, reviewResponse)
return
@@ -60,7 +60,7 @@ func (wh *Webhook) ServeHTTP(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
if body, err = io.ReadAll(r.Body); err != nil {
- wh.log.Error(err, "unable to read the body from the incoming request")
+ wh.getLogger(nil).Error(err, "unable to read the body from the incoming request")
reviewResponse = Errored(http.StatusBadRequest, err)
wh.writeResponse(w, reviewResponse)
return
@@ -69,7 +69,7 @@ func (wh *Webhook) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// verify the content type is accurate
if contentType := r.Header.Get("Content-Type"); contentType != "application/json" {
err = fmt.Errorf("contentType=%s, expected application/json", contentType)
- wh.log.Error(err, "unable to process a request with an unknown content type", "content type", contentType)
+ wh.getLogger(nil).Error(err, "unable to process a request with unknown content type")
reviewResponse = Errored(http.StatusBadRequest, err)
wh.writeResponse(w, reviewResponse)
return
@@ -88,12 +88,12 @@ func (wh *Webhook) ServeHTTP(w http.ResponseWriter, r *http.Request) {
ar.SetGroupVersionKind(v1.SchemeGroupVersion.WithKind("AdmissionReview"))
_, actualAdmRevGVK, err := admissionCodecs.UniversalDeserializer().Decode(body, nil, &ar)
if err != nil {
- wh.log.Error(err, "unable to decode the request")
+ wh.getLogger(nil).Error(err, "unable to decode the request")
reviewResponse = Errored(http.StatusBadRequest, err)
wh.writeResponse(w, reviewResponse)
return
}
- wh.log.V(1).Info("received request", "UID", req.UID, "kind", req.Kind, "resource", req.Resource)
+ wh.getLogger(&req).V(4).Info("received request")
reviewResponse = wh.Handle(ctx, req)
wh.writeResponseTyped(w, reviewResponse, actualAdmRevGVK)
@@ -124,7 +124,7 @@ func (wh *Webhook) writeResponseTyped(w io.Writer, response Response, admRevGVK
// writeAdmissionResponse writes ar to w.
func (wh *Webhook) writeAdmissionResponse(w io.Writer, ar v1.AdmissionReview) {
if err := json.NewEncoder(w).Encode(ar); err != nil {
- wh.log.Error(err, "unable to encode and write the response")
+ wh.getLogger(nil).Error(err, "unable to encode and write the response")
// Since the `ar v1.AdmissionReview` is a clear and legal object,
// it should not have problem to be marshalled into bytes.
// The error here is probably caused by the abnormal HTTP connection,
@@ -132,15 +132,15 @@ func (wh *Webhook) writeAdmissionResponse(w io.Writer, ar v1.AdmissionReview) {
// to avoid endless circular calling.
serverError := Errored(http.StatusInternalServerError, err)
if err = json.NewEncoder(w).Encode(v1.AdmissionReview{Response: &serverError.AdmissionResponse}); err != nil {
- wh.log.Error(err, "still unable to encode and write the InternalServerError response")
+ wh.getLogger(nil).Error(err, "still unable to encode and write the InternalServerError response")
}
} else {
res := ar.Response
- if log := wh.log; log.V(1).Enabled() {
+ if log := wh.getLogger(nil); log.V(4).Enabled() {
if res.Result != nil {
- log = log.WithValues("code", res.Result.Code, "reason", res.Result.Reason)
+ log = log.WithValues("code", res.Result.Code, "reason", res.Result.Reason, "message", res.Result.Message)
}
- log.V(1).Info("wrote response", "UID", res.UID, "allowed", res.Allowed)
+ log.V(4).Info("wrote response", "requestID", res.UID, "allowed", res.Allowed)
}
}
}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/inject.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/inject.go
deleted file mode 100644
index d5af0d598..000000000
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/inject.go
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
-Copyright 2019 The Kubernetes Authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package admission
-
-// DecoderInjector is used by the ControllerManager to inject decoder into webhook handlers.
-type DecoderInjector interface {
- InjectDecoder(*Decoder) error
-}
-
-// InjectDecoderInto will set decoder on i and return the result if it implements Decoder. Returns
-// false if i does not implement Decoder.
-func InjectDecoderInto(decoder *Decoder, i interface{}) (bool, error) {
- if s, ok := i.(DecoderInjector); ok {
- return true, s.InjectDecoder(decoder)
- }
- return false, nil
-}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/multi.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/multi.go
index 26900cf2e..2f7820d04 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/multi.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/multi.go
@@ -25,8 +25,6 @@ import (
jsonpatch "gomodules.xyz/jsonpatch/v2"
admissionv1 "k8s.io/api/admission/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-
- "sigs.k8s.io/controller-runtime/pkg/runtime/inject"
)
type multiMutating []Handler
@@ -62,31 +60,6 @@ func (hs multiMutating) Handle(ctx context.Context, req Request) Response {
}
}
-// InjectFunc injects the field setter into the handlers.
-func (hs multiMutating) InjectFunc(f inject.Func) error {
- // inject directly into the handlers. It would be more correct
- // to do this in a sync.Once in Handle (since we don't have some
- // other start/finalize-type method), but it's more efficient to
- // do it here, presumably.
- for _, handler := range hs {
- if err := f(handler); err != nil {
- return err
- }
- }
-
- return nil
-}
-
-// InjectDecoder injects the decoder into the handlers.
-func (hs multiMutating) InjectDecoder(d *Decoder) error {
- for _, handler := range hs {
- if _, err := InjectDecoderInto(d, handler); err != nil {
- return err
- }
- }
- return nil
-}
-
// MultiMutatingHandler combines multiple mutating webhook handlers into a single
// mutating webhook handler. Handlers are called in sequential order, and the first
// `allowed: false` response may short-circuit the rest. Users must take care to
@@ -120,28 +93,3 @@ func (hs multiValidating) Handle(ctx context.Context, req Request) Response {
func MultiValidatingHandler(handlers ...Handler) Handler {
return multiValidating(handlers)
}
-
-// InjectFunc injects the field setter into the handlers.
-func (hs multiValidating) InjectFunc(f inject.Func) error {
- // inject directly into the handlers. It would be more correct
- // to do this in a sync.Once in Handle (since we don't have some
- // other start/finalize-type method), but it's more efficient to
- // do it here, presumably.
- for _, handler := range hs {
- if err := f(handler); err != nil {
- return err
- }
- }
-
- return nil
-}
-
-// InjectDecoder injects the decoder into the handlers.
-func (hs multiValidating) InjectDecoder(d *Decoder) error {
- for _, handler := range hs {
- if _, err := InjectDecoderInto(d, handler); err != nil {
- return err
- }
- }
- return nil
-}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/response.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/response.go
index 24ff1dee3..ec1c88c98 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/response.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/response.go
@@ -26,21 +26,21 @@ import (
// Allowed constructs a response indicating that the given operation
// is allowed (without any patches).
-func Allowed(reason string) Response {
- return ValidationResponse(true, reason)
+func Allowed(message string) Response {
+ return ValidationResponse(true, message)
}
// Denied constructs a response indicating that the given operation
// is not allowed.
-func Denied(reason string) Response {
- return ValidationResponse(false, reason)
+func Denied(message string) Response {
+ return ValidationResponse(false, message)
}
// Patched constructs a response indicating that the given operation is
// allowed, and that the target object should be modified by the given
// JSONPatch operations.
-func Patched(reason string, patches ...jsonpatch.JsonPatchOperation) Response {
- resp := Allowed(reason)
+func Patched(message string, patches ...jsonpatch.JsonPatchOperation) Response {
+ resp := Allowed(message)
resp.Patches = patches
return resp
@@ -60,21 +60,24 @@ func Errored(code int32, err error) Response {
}
// ValidationResponse returns a response for admitting a request.
-func ValidationResponse(allowed bool, reason string) Response {
+func ValidationResponse(allowed bool, message string) Response {
code := http.StatusForbidden
+ reason := metav1.StatusReasonForbidden
if allowed {
code = http.StatusOK
+ reason = ""
}
resp := Response{
AdmissionResponse: admissionv1.AdmissionResponse{
Allowed: allowed,
Result: &metav1.Status{
- Code: int32(code),
+ Code: int32(code),
+ Reason: reason,
},
},
}
- if len(reason) > 0 {
- resp.Result.Reason = metav1.StatusReason(reason)
+ if len(message) > 0 {
+ resp.Result.Message = message
}
return resp
}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/validator.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/validator.go
index 4b27e75ed..00bda8a4c 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/validator.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/validator.go
@@ -18,7 +18,8 @@ package admission
import (
"context"
- goerrors "errors"
+ "errors"
+ "fmt"
"net/http"
v1 "k8s.io/api/admission/v1"
@@ -26,18 +27,35 @@ import (
"k8s.io/apimachinery/pkg/runtime"
)
+// Warnings represents warning messages.
+type Warnings []string
+
// Validator defines functions for validating an operation.
+// The custom resource kind which implements this interface can validate itself.
+// To validate the custom resource with another specific struct, use CustomValidator instead.
type Validator interface {
runtime.Object
- ValidateCreate() error
- ValidateUpdate(old runtime.Object) error
- ValidateDelete() error
+
+ // ValidateCreate validates the object on creation.
+ // The optional warnings will be added to the response as warning messages.
+ // Return an error if the object is invalid.
+ ValidateCreate() (warnings Warnings, err error)
+
+ // ValidateUpdate validates the object on update. The oldObj is the object before the update.
+ // The optional warnings will be added to the response as warning messages.
+ // Return an error if the object is invalid.
+ ValidateUpdate(old runtime.Object) (warnings Warnings, err error)
+
+ // ValidateDelete validates the object on deletion.
+ // The optional warnings will be added to the response as warning messages.
+ // Return an error if the object is invalid.
+ ValidateDelete() (warnings Warnings, err error)
}
// ValidatingWebhookFor creates a new Webhook for validating the provided type.
-func ValidatingWebhookFor(validator Validator) *Webhook {
+func ValidatingWebhookFor(scheme *runtime.Scheme, validator Validator) *Webhook {
return &Webhook{
- Handler: &validatingHandler{validator: validator},
+ Handler: &validatingHandler{validator: validator, decoder: NewDecoder(scheme)},
}
}
@@ -46,42 +64,34 @@ type validatingHandler struct {
decoder *Decoder
}
-var _ DecoderInjector = &validatingHandler{}
-
-// InjectDecoder injects the decoder into a validatingHandler.
-func (h *validatingHandler) InjectDecoder(d *Decoder) error {
- h.decoder = d
- return nil
-}
-
// Handle handles admission requests.
func (h *validatingHandler) Handle(ctx context.Context, req Request) Response {
+ if h.decoder == nil {
+ panic("decoder should never be nil")
+ }
if h.validator == nil {
panic("validator should never be nil")
}
-
// Get the object in the request
obj := h.validator.DeepCopyObject().(Validator)
- if req.Operation == v1.Create {
- err := h.decoder.Decode(req, obj)
- if err != nil {
- return Errored(http.StatusBadRequest, err)
- }
- err = obj.ValidateCreate()
- if err != nil {
- var apiStatus apierrors.APIStatus
- if goerrors.As(err, &apiStatus) {
- return validationResponseFromStatus(false, apiStatus.Status())
- }
- return Denied(err.Error())
+ var err error
+ var warnings []string
+
+ switch req.Operation {
+ case v1.Connect:
+ // No validation for connect requests.
+ // TODO(vincepri): Should we validate CONNECT requests? In what cases?
+ case v1.Create:
+ if err = h.decoder.Decode(req, obj); err != nil {
+ return Errored(http.StatusBadRequest, err)
}
- }
- if req.Operation == v1.Update {
+ warnings, err = obj.ValidateCreate()
+ case v1.Update:
oldObj := obj.DeepCopyObject()
- err := h.decoder.DecodeRaw(req.Object, obj)
+ err = h.decoder.DecodeRaw(req.Object, obj)
if err != nil {
return Errored(http.StatusBadRequest, err)
}
@@ -90,33 +100,26 @@ func (h *validatingHandler) Handle(ctx context.Context, req Request) Response {
return Errored(http.StatusBadRequest, err)
}
- err = obj.ValidateUpdate(oldObj)
- if err != nil {
- var apiStatus apierrors.APIStatus
- if goerrors.As(err, &apiStatus) {
- return validationResponseFromStatus(false, apiStatus.Status())
- }
- return Denied(err.Error())
- }
- }
-
- if req.Operation == v1.Delete {
+ warnings, err = obj.ValidateUpdate(oldObj)
+ case v1.Delete:
// In reference to PR: https://github.com/kubernetes/kubernetes/pull/76346
// OldObject contains the object being deleted
- err := h.decoder.DecodeRaw(req.OldObject, obj)
+ err = h.decoder.DecodeRaw(req.OldObject, obj)
if err != nil {
return Errored(http.StatusBadRequest, err)
}
- err = obj.ValidateDelete()
- if err != nil {
- var apiStatus apierrors.APIStatus
- if goerrors.As(err, &apiStatus) {
- return validationResponseFromStatus(false, apiStatus.Status())
- }
- return Denied(err.Error())
- }
+ warnings, err = obj.ValidateDelete()
+ default:
+ return Errored(http.StatusBadRequest, fmt.Errorf("unknown operation %q", req.Operation))
}
- return Allowed("")
+ if err != nil {
+ var apiStatus apierrors.APIStatus
+ if errors.As(err, &apiStatus) {
+ return validationResponseFromStatus(false, apiStatus.Status()).WithWarnings(warnings...)
+ }
+ return Denied(err.Error()).WithWarnings(warnings...)
+ }
+ return Allowed("").WithWarnings(warnings...)
}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/validator_custom.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/validator_custom.go
index 33252f113..e99fbd8a8 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/validator_custom.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/validator_custom.go
@@ -28,16 +28,29 @@ import (
)
// CustomValidator defines functions for validating an operation.
+// The object to be validated is passed into methods as a parameter.
type CustomValidator interface {
- ValidateCreate(ctx context.Context, obj runtime.Object) error
- ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) error
- ValidateDelete(ctx context.Context, obj runtime.Object) error
+
+ // ValidateCreate validates the object on creation.
+ // The optional warnings will be added to the response as warning messages.
+ // Return an error if the object is invalid.
+ ValidateCreate(ctx context.Context, obj runtime.Object) (warnings Warnings, err error)
+
+ // ValidateUpdate validates the object on update.
+ // The optional warnings will be added to the response as warning messages.
+ // Return an error if the object is invalid.
+ ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (warnings Warnings, err error)
+
+ // ValidateDelete validates the object on deletion.
+ // The optional warnings will be added to the response as warning messages.
+ // Return an error if the object is invalid.
+ ValidateDelete(ctx context.Context, obj runtime.Object) (warnings Warnings, err error)
}
// WithCustomValidator creates a new Webhook for validating the provided type.
-func WithCustomValidator(obj runtime.Object, validator CustomValidator) *Webhook {
+func WithCustomValidator(scheme *runtime.Scheme, obj runtime.Object, validator CustomValidator) *Webhook {
return &Webhook{
- Handler: &validatorForType{object: obj, validator: validator},
+ Handler: &validatorForType{object: obj, validator: validator, decoder: NewDecoder(scheme)},
}
}
@@ -47,16 +60,11 @@ type validatorForType struct {
decoder *Decoder
}
-var _ DecoderInjector = &validatorForType{}
-
-// InjectDecoder injects the decoder into a validatingHandler.
-func (h *validatorForType) InjectDecoder(d *Decoder) error {
- h.decoder = d
- return nil
-}
-
// Handle handles admission requests.
func (h *validatorForType) Handle(ctx context.Context, req Request) Response {
+ if h.decoder == nil {
+ panic("decoder should never be nil")
+ }
if h.validator == nil {
panic("validator should never be nil")
}
@@ -70,13 +78,18 @@ func (h *validatorForType) Handle(ctx context.Context, req Request) Response {
obj := h.object.DeepCopyObject()
var err error
+ var warnings []string
+
switch req.Operation {
+ case v1.Connect:
+ // No validation for connect requests.
+ // TODO(vincepri): Should we validate CONNECT requests? In what cases?
case v1.Create:
if err := h.decoder.Decode(req, obj); err != nil {
return Errored(http.StatusBadRequest, err)
}
- err = h.validator.ValidateCreate(ctx, obj)
+ warnings, err = h.validator.ValidateCreate(ctx, obj)
case v1.Update:
oldObj := obj.DeepCopyObject()
if err := h.decoder.DecodeRaw(req.Object, obj); err != nil {
@@ -86,7 +99,7 @@ func (h *validatorForType) Handle(ctx context.Context, req Request) Response {
return Errored(http.StatusBadRequest, err)
}
- err = h.validator.ValidateUpdate(ctx, oldObj, obj)
+ warnings, err = h.validator.ValidateUpdate(ctx, oldObj, obj)
case v1.Delete:
// In reference to PR: https://github.com/kubernetes/kubernetes/pull/76346
// OldObject contains the object being deleted
@@ -94,20 +107,20 @@ func (h *validatorForType) Handle(ctx context.Context, req Request) Response {
return Errored(http.StatusBadRequest, err)
}
- err = h.validator.ValidateDelete(ctx, obj)
+ warnings, err = h.validator.ValidateDelete(ctx, obj)
default:
- return Errored(http.StatusBadRequest, fmt.Errorf("unknown operation request %q", req.Operation))
+ return Errored(http.StatusBadRequest, fmt.Errorf("unknown operation %q", req.Operation))
}
// Check the error message first.
if err != nil {
var apiStatus apierrors.APIStatus
if errors.As(err, &apiStatus) {
- return validationResponseFromStatus(false, apiStatus.Status())
+ return validationResponseFromStatus(false, apiStatus.Status()).WithWarnings(warnings...)
}
- return Denied(err.Error())
+ return Denied(err.Error()).WithWarnings(warnings...)
}
// Return allowed if everything succeeded.
- return Allowed("")
+ return Allowed("").WithWarnings(warnings...)
}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/webhook.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/webhook.go
index d10b97ddd..f1767f31b 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/webhook.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/webhook.go
@@ -21,18 +21,17 @@ import (
"errors"
"fmt"
"net/http"
+ "sync"
"github.com/go-logr/logr"
- jsonpatch "gomodules.xyz/jsonpatch/v2"
+ "gomodules.xyz/jsonpatch/v2"
admissionv1 "k8s.io/api/admission/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/json"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
- "k8s.io/client-go/kubernetes/scheme"
+ "k8s.io/klog/v2"
- logf "sigs.k8s.io/controller-runtime/pkg/internal/log"
- "sigs.k8s.io/controller-runtime/pkg/runtime/inject"
+ logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/webhook/internal/metrics"
)
@@ -131,16 +130,14 @@ type Webhook struct {
// headers thus allowing you to read them from within the handler
WithContextFunc func(context.Context, *http.Request) context.Context
- // decoder is constructed on receiving a scheme and passed down to then handler
- decoder *Decoder
+ // LogConstructor is used to construct a logger for logging messages during webhook calls
+ // based on the given base logger (which might carry more values like the webhook's path).
+ // Note: LogConstructor has to be able to handle nil requests as we are also using it
+ // outside the context of requests.
+ LogConstructor func(base logr.Logger, req *Request) logr.Logger
- log logr.Logger
-}
-
-// InjectLogger gets a handle to a logging instance, hopefully with more info about this particular webhook.
-func (wh *Webhook) InjectLogger(l logr.Logger) error {
- wh.log = l
- return nil
+ setupLogOnce sync.Once
+ log logr.Logger
}
// WithRecoverPanic takes a bool flag which indicates whether the panic caused by webhook should be recovered.
@@ -166,79 +163,47 @@ func (wh *Webhook) Handle(ctx context.Context, req Request) (response Response)
}()
}
+ reqLog := wh.getLogger(&req)
+ ctx = logf.IntoContext(ctx, reqLog)
+
resp := wh.Handler.Handle(ctx, req)
if err := resp.Complete(req); err != nil {
- wh.log.Error(err, "unable to encode response")
+ reqLog.Error(err, "unable to encode response")
return Errored(http.StatusInternalServerError, errUnableToEncodeResponse)
}
return resp
}
-// InjectScheme injects a scheme into the webhook, in order to construct a Decoder.
-func (wh *Webhook) InjectScheme(s *runtime.Scheme) error {
- // TODO(directxman12): we should have a better way to pass this down
-
- var err error
- wh.decoder, err = NewDecoder(s)
- if err != nil {
- return err
- }
-
- // inject the decoder here too, just in case the order of calling this is not
- // scheme first, then inject func
- if wh.Handler != nil {
- if _, err := InjectDecoderInto(wh.GetDecoder(), wh.Handler); err != nil {
- return err
+// getLogger constructs a logger from the injected log and LogConstructor.
+func (wh *Webhook) getLogger(req *Request) logr.Logger {
+ wh.setupLogOnce.Do(func() {
+ if wh.log.GetSink() == nil {
+ wh.log = logf.Log.WithName("admission")
}
- }
+ })
- return nil
-}
-
-// GetDecoder returns a decoder to decode the objects embedded in admission requests.
-// It may be nil if we haven't received a scheme to use to determine object types yet.
-func (wh *Webhook) GetDecoder() *Decoder {
- return wh.decoder
+ logConstructor := wh.LogConstructor
+ if logConstructor == nil {
+ logConstructor = DefaultLogConstructor
+ }
+ return logConstructor(wh.log, req)
}
-// InjectFunc injects the field setter into the webhook.
-func (wh *Webhook) InjectFunc(f inject.Func) error {
- // inject directly into the handlers. It would be more correct
- // to do this in a sync.Once in Handle (since we don't have some
- // other start/finalize-type method), but it's more efficient to
- // do it here, presumably.
-
- // also inject a decoder, and wrap this so that we get a setFields
- // that injects a decoder (hopefully things don't ignore the duplicate
- // InjectorInto call).
-
- var setFields inject.Func
- setFields = func(target interface{}) error {
- if err := f(target); err != nil {
- return err
- }
-
- if _, err := inject.InjectorInto(setFields, target); err != nil {
- return err
- }
-
- if _, err := InjectDecoderInto(wh.GetDecoder(), target); err != nil {
- return err
- }
-
- return nil
+// DefaultLogConstructor adds some commonly interesting fields to the given logger.
+func DefaultLogConstructor(base logr.Logger, req *Request) logr.Logger {
+ if req != nil {
+ return base.WithValues("object", klog.KRef(req.Namespace, req.Name),
+ "namespace", req.Namespace, "name", req.Name,
+ "resource", req.Resource, "user", req.UserInfo.Username,
+ "requestID", req.UID,
+ )
}
-
- return setFields(wh.Handler)
+ return base
}
// StandaloneOptions let you configure a StandaloneWebhook.
type StandaloneOptions struct {
- // Scheme is the scheme used to resolve runtime.Objects to GroupVersionKinds / Resources
- // Defaults to the kubernetes/client-go scheme.Scheme, but it's almost always better
- // idea to pass your own scheme in. See the documentation in pkg/scheme for more information.
- Scheme *runtime.Scheme
// Logger to be used by the webhook.
// If none is set, it defaults to log.Log global logger.
Logger logr.Logger
@@ -258,19 +223,9 @@ type StandaloneOptions struct {
// in your own server/mux. In order to be accessed by a kubernetes cluster,
// all webhook servers require TLS.
func StandaloneWebhook(hook *Webhook, opts StandaloneOptions) (http.Handler, error) {
- if opts.Scheme == nil {
- opts.Scheme = scheme.Scheme
- }
-
- if err := hook.InjectScheme(opts.Scheme); err != nil {
- return nil, err
+ if opts.Logger.GetSink() != nil {
+ hook.log = opts.Logger
}
-
- if opts.Logger.GetSink() == nil {
- opts.Logger = logf.RuntimeLog.WithName("webhook")
- }
- hook.log = opts.Logger
-
if opts.MetricsPath == "" {
return hook, nil
}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/conversion/conversion.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/conversion/conversion.go
index 879aae3c9..249a364b3 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/conversion/conversion.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/conversion/conversion.go
@@ -39,28 +39,20 @@ var (
log = logf.Log.WithName("conversion-webhook")
)
-// Webhook implements a CRD conversion webhook HTTP handler.
-type Webhook struct {
- scheme *runtime.Scheme
- decoder *Decoder
+func NewWebhookHandler(scheme *runtime.Scheme) http.Handler {
+ return &webhook{scheme: scheme, decoder: NewDecoder(scheme)}
}
-// InjectScheme injects a scheme into the webhook, in order to construct a Decoder.
-func (wh *Webhook) InjectScheme(s *runtime.Scheme) error {
- var err error
- wh.scheme = s
- wh.decoder, err = NewDecoder(s)
- if err != nil {
- return err
- }
-
- return nil
+// webhook implements a CRD conversion webhook HTTP handler.
+type webhook struct {
+ scheme *runtime.Scheme
+ decoder *Decoder
}
// ensure Webhook implements http.Handler
-var _ http.Handler = &Webhook{}
+var _ http.Handler = &webhook{}
-func (wh *Webhook) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+func (wh *webhook) ServeHTTP(w http.ResponseWriter, r *http.Request) {
convertReview := &apix.ConversionReview{}
err := json.NewDecoder(r.Body).Decode(convertReview)
if err != nil {
@@ -95,7 +87,7 @@ func (wh *Webhook) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
// handles a version conversion request.
-func (wh *Webhook) handleConvertRequest(req *apix.ConversionRequest) (*apix.ConversionResponse, error) {
+func (wh *webhook) handleConvertRequest(req *apix.ConversionRequest) (*apix.ConversionResponse, error) {
if req == nil {
return nil, fmt.Errorf("conversion request is nil")
}
@@ -128,7 +120,7 @@ func (wh *Webhook) handleConvertRequest(req *apix.ConversionRequest) (*apix.Conv
// convertObject will convert given a src object to dst object.
// Note(droot): couldn't find a way to reduce the cyclomatic complexity under 10
// without compromising readability, so disabling gocyclo linter
-func (wh *Webhook) convertObject(src, dst runtime.Object) error {
+func (wh *webhook) convertObject(src, dst runtime.Object) error {
srcGVK := src.GetObjectKind().GroupVersionKind()
dstGVK := dst.GetObjectKind().GroupVersionKind()
@@ -155,7 +147,7 @@ func (wh *Webhook) convertObject(src, dst runtime.Object) error {
}
}
-func (wh *Webhook) convertViaHub(src, dst conversion.Convertible) error {
+func (wh *webhook) convertViaHub(src, dst conversion.Convertible) error {
hub, err := wh.getHub(src)
if err != nil {
return err
@@ -179,7 +171,7 @@ func (wh *Webhook) convertViaHub(src, dst conversion.Convertible) error {
}
// getHub returns an instance of the Hub for passed-in object's group/kind.
-func (wh *Webhook) getHub(obj runtime.Object) (conversion.Hub, error) {
+func (wh *webhook) getHub(obj runtime.Object) (conversion.Hub, error) {
gvks, err := objectGVKs(wh.scheme, obj)
if err != nil {
return nil, err
@@ -207,7 +199,7 @@ func (wh *Webhook) getHub(obj runtime.Object) (conversion.Hub, error) {
}
// allocateDstObject returns an instance for a given GVK.
-func (wh *Webhook) allocateDstObject(apiVersion, kind string) (runtime.Object, error) {
+func (wh *webhook) allocateDstObject(apiVersion, kind string) (runtime.Object, error) {
gvk := schema.FromAPIVersionAndKind(apiVersion, kind)
obj, err := wh.scheme.New(gvk)
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/conversion/decoder.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/conversion/decoder.go
index 6a9e9c236..b6bb8bd93 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/conversion/decoder.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/conversion/decoder.go
@@ -30,8 +30,11 @@ type Decoder struct {
}
// NewDecoder creates a Decoder given the runtime.Scheme
-func NewDecoder(scheme *runtime.Scheme) (*Decoder, error) {
- return &Decoder{codecs: serializer.NewCodecFactory(scheme)}, nil
+func NewDecoder(scheme *runtime.Scheme) *Decoder {
+ if scheme == nil {
+ panic("scheme should never be nil")
+ }
+ return &Decoder{codecs: serializer.NewCodecFactory(scheme)}
}
// Decode decodes the inlined object.
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/server.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/server.go
index 99c863264..23d5bf435 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/server.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/server.go
@@ -29,12 +29,9 @@ import (
"sync"
"time"
- "k8s.io/apimachinery/pkg/runtime"
- kscheme "k8s.io/client-go/kubernetes/scheme"
"sigs.k8s.io/controller-runtime/pkg/certwatcher"
"sigs.k8s.io/controller-runtime/pkg/healthz"
"sigs.k8s.io/controller-runtime/pkg/internal/httpserver"
- "sigs.k8s.io/controller-runtime/pkg/runtime/inject"
"sigs.k8s.io/controller-runtime/pkg/webhook/internal/metrics"
)
@@ -49,7 +46,29 @@ var DefaultPort = 9443
// at the default locations (tls.crt and tls.key). If you do not
// want to configure TLS (i.e for testing purposes) run an
// admission.StandaloneWebhook in your own server.
-type Server struct {
+type Server interface {
+ // NeedLeaderElection implements the LeaderElectionRunnable interface, which indicates
+ // the webhook server doesn't need leader election.
+ NeedLeaderElection() bool
+
+ // Register marks the given webhook as being served at the given path.
+ // It panics if two hooks are registered on the same path.
+ Register(path string, hook http.Handler)
+
+ // Start runs the server.
+ // It will install the webhook related resources depend on the server configuration.
+ Start(ctx context.Context) error
+
+ // StartedChecker returns an healthz.Checker which is healthy after the
+ // server has been started.
+ StartedChecker() healthz.Checker
+
+ // WebhookMux returns the servers WebhookMux
+ WebhookMux() *http.ServeMux
+}
+
+// Options are all the available options for a webhook.Server
+type Options struct {
// Host is the address that the server will listen on.
// Defaults to "" - all addresses.
Host string
@@ -63,9 +82,13 @@ type Server struct {
CertDir string
// CertName is the server certificate name. Defaults to tls.crt.
+ //
+ // Note: This option should only be set when TLSOpts does not override GetCertificate.
CertName string
// KeyName is the server key name. Defaults to tls.key.
+ //
+ // Note: This option should only be set when TLSOpts does not override GetCertificate.
KeyName string
// ClientCAName is the CA certificate name which server used to verify remote(client)'s certificate.
@@ -82,13 +105,21 @@ type Server struct {
// WebhookMux is the multiplexer that handles different webhooks.
WebhookMux *http.ServeMux
+}
- // webhooks keep track of all registered webhooks for dependency injection,
- // and to provide better panic messages on duplicate webhook registration.
- webhooks map[string]http.Handler
+// NewServer constructs a new Server from the provided options.
+func NewServer(o Options) Server {
+ return &DefaultServer{
+ Options: o,
+ }
+}
- // setFields allows injecting dependencies from an external source
- setFields inject.Func
+// DefaultServer is the default implementation used for Server.
+type DefaultServer struct {
+ Options Options
+
+ // webhooks keep track of all registered webhooks
+ webhooks map[string]http.Handler
// defaultingOnce ensures that the default fields are only ever set once.
defaultingOnce sync.Once
@@ -99,41 +130,49 @@ type Server struct {
// mu protects access to the webhook map & setFields for Start, Register, etc
mu sync.Mutex
+
+ webhookMux *http.ServeMux
}
// setDefaults does defaulting for the Server.
-func (s *Server) setDefaults() {
- s.webhooks = map[string]http.Handler{}
- if s.WebhookMux == nil {
- s.WebhookMux = http.NewServeMux()
+func (o *Options) setDefaults() {
+ if o.WebhookMux == nil {
+ o.WebhookMux = http.NewServeMux()
}
- if s.Port <= 0 {
- s.Port = DefaultPort
+ if o.Port <= 0 {
+ o.Port = DefaultPort
}
- if len(s.CertDir) == 0 {
- s.CertDir = filepath.Join(os.TempDir(), "k8s-webhook-server", "serving-certs")
+ if len(o.CertDir) == 0 {
+ o.CertDir = filepath.Join(os.TempDir(), "k8s-webhook-server", "serving-certs")
}
- if len(s.CertName) == 0 {
- s.CertName = "tls.crt"
+ if len(o.CertName) == 0 {
+ o.CertName = "tls.crt"
}
- if len(s.KeyName) == 0 {
- s.KeyName = "tls.key"
+ if len(o.KeyName) == 0 {
+ o.KeyName = "tls.key"
}
}
+func (s *DefaultServer) setDefaults() {
+ s.webhooks = map[string]http.Handler{}
+ s.Options.setDefaults()
+
+ s.webhookMux = s.Options.WebhookMux
+}
+
// NeedLeaderElection implements the LeaderElectionRunnable interface, which indicates
// the webhook server doesn't need leader election.
-func (*Server) NeedLeaderElection() bool {
+func (*DefaultServer) NeedLeaderElection() bool {
return false
}
// Register marks the given webhook as being served at the given path.
// It panics if two hooks are registered on the same path.
-func (s *Server) Register(path string, hook http.Handler) {
+func (s *DefaultServer) Register(path string, hook http.Handler) {
s.mu.Lock()
defer s.mu.Unlock()
@@ -141,51 +180,11 @@ func (s *Server) Register(path string, hook http.Handler) {
if _, found := s.webhooks[path]; found {
panic(fmt.Errorf("can't register duplicate path: %v", path))
}
- // TODO(directxman12): call setfields if we've already started the server
s.webhooks[path] = hook
- s.WebhookMux.Handle(path, metrics.InstrumentedHook(path, hook))
+ s.webhookMux.Handle(path, metrics.InstrumentedHook(path, hook))
regLog := log.WithValues("path", path)
regLog.Info("Registering webhook")
-
- // we've already been "started", inject dependencies here.
- // Otherwise, InjectFunc will do this for us later.
- if s.setFields != nil {
- if err := s.setFields(hook); err != nil {
- // TODO(directxman12): swallowing this error isn't great, but we'd have to
- // change the signature to fix that
- regLog.Error(err, "unable to inject fields into webhook during registration")
- }
-
- baseHookLog := log.WithName("webhooks")
-
- // NB(directxman12): we don't propagate this further by wrapping setFields because it's
- // unclear if this is how we want to deal with log propagation. In this specific instance,
- // we want to be able to pass a logger to webhooks because they don't know their own path.
- if _, err := inject.LoggerInto(baseHookLog.WithValues("webhook", path), hook); err != nil {
- regLog.Error(err, "unable to logger into webhook during registration")
- }
- }
-}
-
-// StartStandalone runs a webhook server without
-// a controller manager.
-func (s *Server) StartStandalone(ctx context.Context, scheme *runtime.Scheme) error {
- // Use the Kubernetes client-go scheme if none is specified
- if scheme == nil {
- scheme = kscheme.Scheme
- }
-
- if err := s.InjectFunc(func(i interface{}) error {
- if _, err := inject.SchemeInto(scheme, i); err != nil {
- return err
- }
- return nil
- }); err != nil {
- return err
- }
-
- return s.Start(ctx)
}
// tlsVersion converts from human-readable TLS version (for example "1.1")
@@ -210,41 +209,49 @@ func tlsVersion(version string) (uint16, error) {
// Start runs the server.
// It will install the webhook related resources depend on the server configuration.
-func (s *Server) Start(ctx context.Context) error {
+func (s *DefaultServer) Start(ctx context.Context) error {
s.defaultingOnce.Do(s.setDefaults)
baseHookLog := log.WithName("webhooks")
baseHookLog.Info("Starting webhook server")
- certPath := filepath.Join(s.CertDir, s.CertName)
- keyPath := filepath.Join(s.CertDir, s.KeyName)
-
- certWatcher, err := certwatcher.New(certPath, keyPath)
+ tlsMinVersion, err := tlsVersion(s.Options.TLSMinVersion)
if err != nil {
return err
}
- go func() {
- if err := certWatcher.Start(ctx); err != nil {
- log.Error(err, "certificate watcher error")
- }
- }()
-
- tlsMinVersion, err := tlsVersion(s.TLSMinVersion)
- if err != nil {
- return err
+ cfg := &tls.Config{ //nolint:gosec
+ NextProtos: []string{"h2"},
+ MinVersion: tlsMinVersion,
+ }
+ // fallback TLS config ready, will now mutate if passer wants full control over it
+ for _, op := range s.Options.TLSOpts {
+ op(cfg)
}
- cfg := &tls.Config{ //nolint:gosec
- NextProtos: []string{"h2"},
- GetCertificate: certWatcher.GetCertificate,
- MinVersion: tlsMinVersion,
+ if cfg.GetCertificate == nil {
+ certPath := filepath.Join(s.Options.CertDir, s.Options.CertName)
+ keyPath := filepath.Join(s.Options.CertDir, s.Options.KeyName)
+
+ // Create the certificate watcher and
+ // set the config's GetCertificate on the TLSConfig
+ certWatcher, err := certwatcher.New(certPath, keyPath)
+ if err != nil {
+ return err
+ }
+ cfg.GetCertificate = certWatcher.GetCertificate
+
+ go func() {
+ if err := certWatcher.Start(ctx); err != nil {
+ log.Error(err, "certificate watcher error")
+ }
+ }()
}
- // load CA to verify client certificate
- if s.ClientCAName != "" {
+ // Load CA to verify client certificate, if configured.
+ if s.Options.ClientCAName != "" {
certPool := x509.NewCertPool()
- clientCABytes, err := os.ReadFile(filepath.Join(s.CertDir, s.ClientCAName))
+ clientCABytes, err := os.ReadFile(filepath.Join(s.Options.CertDir, s.Options.ClientCAName))
if err != nil {
return fmt.Errorf("failed to read client CA cert: %w", err)
}
@@ -258,27 +265,23 @@ func (s *Server) Start(ctx context.Context) error {
cfg.ClientAuth = tls.RequireAndVerifyClientCert
}
- // fallback TLS config ready, will now mutate if passer wants full control over it
- for _, op := range s.TLSOpts {
- op(cfg)
- }
-
- listener, err := tls.Listen("tcp", net.JoinHostPort(s.Host, strconv.Itoa(s.Port)), cfg)
+ listener, err := tls.Listen("tcp", net.JoinHostPort(s.Options.Host, strconv.Itoa(s.Options.Port)), cfg)
if err != nil {
return err
}
- log.Info("Serving webhook server", "host", s.Host, "port", s.Port)
+ log.Info("Serving webhook server", "host", s.Options.Host, "port", s.Options.Port)
- srv := httpserver.New(s.WebhookMux)
+ srv := httpserver.New(s.webhookMux)
idleConnsClosed := make(chan struct{})
go func() {
<-ctx.Done()
- log.Info("shutting down webhook server")
+ log.Info("Shutting down webhook server with timeout of 1 minute")
- // TODO: use a context with reasonable timeout
- if err := srv.Shutdown(context.Background()); err != nil {
+ ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)
+ defer cancel()
+ if err := srv.Shutdown(ctx); err != nil {
// Error from closing listeners, or context timeout
log.Error(err, "error shutting down the HTTP server")
}
@@ -298,7 +301,7 @@ func (s *Server) Start(ctx context.Context) error {
// StartedChecker returns an healthz.Checker which is healthy after the
// server has been started.
-func (s *Server) StartedChecker() healthz.Checker {
+func (s *DefaultServer) StartedChecker() healthz.Checker {
config := &tls.Config{
InsecureSkipVerify: true, //nolint:gosec // config is used to connect to our own webhook port.
}
@@ -311,7 +314,7 @@ func (s *Server) StartedChecker() healthz.Checker {
}
d := &net.Dialer{Timeout: 10 * time.Second}
- conn, err := tls.DialWithDialer(d, "tcp", net.JoinHostPort(s.Host, strconv.Itoa(s.Port)), config)
+ conn, err := tls.DialWithDialer(d, "tcp", net.JoinHostPort(s.Options.Host, strconv.Itoa(s.Options.Port)), config)
if err != nil {
return fmt.Errorf("webhook server is not reachable: %w", err)
}
@@ -324,23 +327,7 @@ func (s *Server) StartedChecker() healthz.Checker {
}
}
-// InjectFunc injects the field setter into the server.
-func (s *Server) InjectFunc(f inject.Func) error {
- s.setFields = f
-
- // inject fields here that weren't injected in Register because we didn't have setFields yet.
- baseHookLog := log.WithName("webhooks")
- for hookPath, webhook := range s.webhooks {
- if err := s.setFields(webhook); err != nil {
- return err
- }
-
- // NB(directxman12): we don't propagate this further by wrapping setFields because it's
- // unclear if this is how we want to deal with log propagation. In this specific instance,
- // we want to be able to pass a logger to webhooks because they don't know their own path.
- if _, err := inject.LoggerInto(baseHookLog.WithValues("webhook", hookPath), webhook); err != nil {
- return err
- }
- }
- return nil
+// WebhookMux returns the servers WebhookMux
+func (s *DefaultServer) WebhookMux() *http.ServeMux {
+ return s.webhookMux
}