update dependencies (#6267)
Signed-off-by: hongming <coder.scala@gmail.com>
(cherry picked from commit cfebd96a1f)
This commit is contained in:
5
vendor/sigs.k8s.io/controller-runtime/.gitignore
generated
vendored
5
vendor/sigs.k8s.io/controller-runtime/.gitignore
generated
vendored
@@ -23,5 +23,8 @@
|
||||
# Tools binaries.
|
||||
hack/tools/bin
|
||||
|
||||
# Release artifacts
|
||||
tools/setup-envtest/out
|
||||
|
||||
junit-report.xml
|
||||
/artifacts
|
||||
/artifacts
|
||||
|
||||
16
vendor/sigs.k8s.io/controller-runtime/.golangci.yml
generated
vendored
16
vendor/sigs.k8s.io/controller-runtime/.golangci.yml
generated
vendored
@@ -41,6 +41,11 @@ linters:
|
||||
- whitespace
|
||||
|
||||
linters-settings:
|
||||
govet:
|
||||
enable-all: true
|
||||
disable:
|
||||
- fieldalignment
|
||||
- shadow
|
||||
importas:
|
||||
no-unaliased: true
|
||||
alias:
|
||||
@@ -58,10 +63,6 @@ linters-settings:
|
||||
# Controller Runtime
|
||||
- pkg: sigs.k8s.io/controller-runtime
|
||||
alias: ctrl
|
||||
staticcheck:
|
||||
go: "1.21"
|
||||
stylecheck:
|
||||
go: "1.21"
|
||||
revive:
|
||||
rules:
|
||||
# The following rules are recommended https://github.com/mgechev/revive#recommended-configuration
|
||||
@@ -105,6 +106,9 @@ issues:
|
||||
- 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-files:
|
||||
- "zz_generated.*\\.go$"
|
||||
- ".*conversion.*\\.go$"
|
||||
exclude-rules:
|
||||
- linters:
|
||||
- gosec
|
||||
@@ -163,8 +167,6 @@ issues:
|
||||
path: _test\.go
|
||||
|
||||
run:
|
||||
go: "1.22"
|
||||
timeout: 10m
|
||||
skip-files:
|
||||
- "zz_generated.*\\.go$"
|
||||
- ".*conversion.*\\.go$"
|
||||
allow-parallel-runners: true
|
||||
|
||||
14
vendor/sigs.k8s.io/controller-runtime/.gomodcheck.yaml
generated
vendored
Normal file
14
vendor/sigs.k8s.io/controller-runtime/.gomodcheck.yaml
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
upstreamRefs:
|
||||
- k8s.io/api
|
||||
- k8s.io/apiextensions-apiserver
|
||||
- k8s.io/apimachinery
|
||||
- k8s.io/apiserver
|
||||
- k8s.io/client-go
|
||||
- k8s.io/component-base
|
||||
# k8s.io/klog/v2 -> conflicts with k/k deps
|
||||
# k8s.io/utils -> conflicts with k/k deps
|
||||
|
||||
excludedModules:
|
||||
# --- test dependencies:
|
||||
- github.com/onsi/ginkgo/v2
|
||||
- github.com/onsi/gomega
|
||||
4
vendor/sigs.k8s.io/controller-runtime/FAQ.md
generated
vendored
4
vendor/sigs.k8s.io/controller-runtime/FAQ.md
generated
vendored
@@ -4,13 +4,13 @@
|
||||
|
||||
**A**: Each controller should only reconcile one object type. Other
|
||||
affected objects should be mapped to a single type of root object, using
|
||||
the `EnqueueRequestForOwner` or `EnqueueRequestsFromMapFunc` event
|
||||
the `handler.EnqueueRequestForOwner` or `handler.EnqueueRequestsFromMapFunc` event
|
||||
handlers, and potentially indices. Then, your Reconcile method should
|
||||
attempt to reconcile *all* state for that given root objects.
|
||||
|
||||
### Q: How do I have different logic in my reconciler for different types of events (e.g. create, update, delete)?
|
||||
|
||||
**A**: You should not. Reconcile functions should be idempotent, and
|
||||
**A**: You should not. Reconcile functions should be idempotent, and
|
||||
should always reconcile state by reading all the state it needs, then
|
||||
writing updates. This allows your reconciler to correctly respond to
|
||||
generic events, adjust to skipped or coalesced events, and easily deal
|
||||
|
||||
122
vendor/sigs.k8s.io/controller-runtime/Makefile
generated
vendored
122
vendor/sigs.k8s.io/controller-runtime/Makefile
generated
vendored
@@ -24,6 +24,11 @@
|
||||
SHELL:=/usr/bin/env bash
|
||||
.DEFAULT_GOAL:=help
|
||||
|
||||
#
|
||||
# Go.
|
||||
#
|
||||
GO_VERSION ?= 1.22.5
|
||||
|
||||
# Use GOPROXY environment variable if set
|
||||
GOPROXY := $(shell go env GOPROXY)
|
||||
ifeq ($(GOPROXY),)
|
||||
@@ -34,14 +39,22 @@ export GOPROXY
|
||||
# Active module mode, as we use go modules to manage dependencies
|
||||
export GO111MODULE=on
|
||||
|
||||
# Hosts running SELinux need :z added to volume mounts
|
||||
SELINUX_ENABLED := $(shell cat /sys/fs/selinux/enforce 2> /dev/null || echo 0)
|
||||
|
||||
ifeq ($(SELINUX_ENABLED),1)
|
||||
DOCKER_VOL_OPTS?=:z
|
||||
endif
|
||||
|
||||
# Tools.
|
||||
TOOLS_DIR := hack/tools
|
||||
TOOLS_BIN_DIR := $(TOOLS_DIR)/bin
|
||||
TOOLS_BIN_DIR := $(abspath $(TOOLS_DIR)/bin)
|
||||
GOLANGCI_LINT := $(abspath $(TOOLS_BIN_DIR)/golangci-lint)
|
||||
GO_APIDIFF := $(TOOLS_BIN_DIR)/go-apidiff
|
||||
CONTROLLER_GEN := $(TOOLS_BIN_DIR)/controller-gen
|
||||
ENVTEST_DIR := $(abspath tools/setup-envtest)
|
||||
SCRATCH_ENV_DIR := $(abspath examples/scratch-env)
|
||||
GO_INSTALL := ./hack/go-install.sh
|
||||
|
||||
# The help will print out all targets with their descriptions organized bellow their categories. The categories are represented by `##@` and the target descriptions by `##`.
|
||||
# The awk commands is responsible to read the entire set of makefiles included in this invocation, looking for lines of the file as xyz: ## something, and then pretty-format the target and help. Then, if there's a line with ##@ something, that gets pretty-printed as a category.
|
||||
@@ -67,16 +80,36 @@ test-tools: ## tests the tools codebase (setup-envtest)
|
||||
## Binaries
|
||||
## --------------------------------------
|
||||
|
||||
$(GO_APIDIFF): $(TOOLS_DIR)/go.mod # Build go-apidiff from tools folder.
|
||||
cd $(TOOLS_DIR) && go build -tags=tools -o bin/go-apidiff github.com/joelanford/go-apidiff
|
||||
GO_APIDIFF_VER := v0.8.2
|
||||
GO_APIDIFF_BIN := go-apidiff
|
||||
GO_APIDIFF := $(abspath $(TOOLS_BIN_DIR)/$(GO_APIDIFF_BIN)-$(GO_APIDIFF_VER))
|
||||
GO_APIDIFF_PKG := github.com/joelanford/go-apidiff
|
||||
|
||||
$(CONTROLLER_GEN): $(TOOLS_DIR)/go.mod # Build controller-gen from tools folder.
|
||||
cd $(TOOLS_DIR) && go build -tags=tools -o bin/controller-gen sigs.k8s.io/controller-tools/cmd/controller-gen
|
||||
$(GO_APIDIFF): # Build go-apidiff from tools folder.
|
||||
GOBIN=$(TOOLS_BIN_DIR) $(GO_INSTALL) $(GO_APIDIFF_PKG) $(GO_APIDIFF_BIN) $(GO_APIDIFF_VER)
|
||||
|
||||
$(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: v" | sed 's/.*version: //')
|
||||
CONTROLLER_GEN_VER := v0.14.0
|
||||
CONTROLLER_GEN_BIN := controller-gen
|
||||
CONTROLLER_GEN := $(abspath $(TOOLS_BIN_DIR)/$(CONTROLLER_GEN_BIN)-$(CONTROLLER_GEN_VER))
|
||||
CONTROLLER_GEN_PKG := sigs.k8s.io/controller-tools/cmd/controller-gen
|
||||
|
||||
$(CONTROLLER_GEN): # Build controller-gen from tools folder.
|
||||
GOBIN=$(TOOLS_BIN_DIR) $(GO_INSTALL) $(CONTROLLER_GEN_PKG) $(CONTROLLER_GEN_BIN) $(CONTROLLER_GEN_VER)
|
||||
|
||||
GOLANGCI_LINT_BIN := golangci-lint
|
||||
GOLANGCI_LINT_VER := $(shell cat .github/workflows/golangci-lint.yml | grep [[:space:]]version: | sed 's/.*version: //')
|
||||
GOLANGCI_LINT := $(abspath $(TOOLS_BIN_DIR)/$(GOLANGCI_LINT_BIN)-$(GOLANGCI_LINT_VER))
|
||||
GOLANGCI_LINT_PKG := github.com/golangci/golangci-lint/cmd/golangci-lint
|
||||
|
||||
$(GOLANGCI_LINT): # Build golangci-lint from tools folder.
|
||||
GOBIN=$(TOOLS_BIN_DIR) $(GO_INSTALL) $(GOLANGCI_LINT_PKG) $(GOLANGCI_LINT_BIN) $(GOLANGCI_LINT_VER)
|
||||
|
||||
GO_MOD_CHECK_DIR := $(abspath ./hack/tools/cmd/gomodcheck)
|
||||
GO_MOD_CHECK := $(abspath $(TOOLS_BIN_DIR)/gomodcheck)
|
||||
GO_MOD_CHECK_IGNORE := $(abspath .gomodcheck.yaml)
|
||||
.PHONY: $(GO_MOD_CHECK)
|
||||
$(GO_MOD_CHECK): # Build gomodcheck
|
||||
go build -C $(GO_MOD_CHECK_DIR) -o $(GO_MOD_CHECK)
|
||||
|
||||
## --------------------------------------
|
||||
## Linting
|
||||
@@ -102,9 +135,47 @@ modules: ## Runs go mod to ensure modules are up to date.
|
||||
cd $(ENVTEST_DIR); go mod tidy
|
||||
cd $(SCRATCH_ENV_DIR); go mod tidy
|
||||
|
||||
.PHONY: generate
|
||||
generate: $(CONTROLLER_GEN) ## Runs controller-gen for internal types for config file
|
||||
$(CONTROLLER_GEN) object paths="./pkg/config/v1alpha1/...;./examples/configfile/custom/v1alpha1/..."
|
||||
## --------------------------------------
|
||||
## Release
|
||||
## --------------------------------------
|
||||
|
||||
RELEASE_DIR := tools/setup-envtest/out
|
||||
|
||||
.PHONY: $(RELEASE_DIR)
|
||||
$(RELEASE_DIR):
|
||||
mkdir -p $(RELEASE_DIR)/
|
||||
|
||||
.PHONY: release
|
||||
release: clean-release $(RELEASE_DIR) ## Build release.
|
||||
@if ! [ -z "$$(git status --porcelain)" ]; then echo "Your local git repository contains uncommitted changes, use git clean before proceeding."; exit 1; fi
|
||||
|
||||
# Build binaries first.
|
||||
$(MAKE) release-binaries
|
||||
|
||||
.PHONY: release-binaries
|
||||
release-binaries: ## Build release binaries.
|
||||
RELEASE_BINARY=setup-envtest-linux-amd64 GOOS=linux GOARCH=amd64 $(MAKE) release-binary
|
||||
RELEASE_BINARY=setup-envtest-linux-arm64 GOOS=linux GOARCH=arm64 $(MAKE) release-binary
|
||||
RELEASE_BINARY=setup-envtest-linux-ppc64le GOOS=linux GOARCH=ppc64le $(MAKE) release-binary
|
||||
RELEASE_BINARY=setup-envtest-linux-s390x GOOS=linux GOARCH=s390x $(MAKE) release-binary
|
||||
RELEASE_BINARY=setup-envtest-darwin-amd64 GOOS=darwin GOARCH=amd64 $(MAKE) release-binary
|
||||
RELEASE_BINARY=setup-envtest-darwin-arm64 GOOS=darwin GOARCH=arm64 $(MAKE) release-binary
|
||||
RELEASE_BINARY=setup-envtest-windows-amd64.exe GOOS=windows GOARCH=amd64 $(MAKE) release-binary
|
||||
|
||||
.PHONY: release-binary
|
||||
release-binary: $(RELEASE_DIR)
|
||||
docker run \
|
||||
--rm \
|
||||
-e CGO_ENABLED=0 \
|
||||
-e GOOS=$(GOOS) \
|
||||
-e GOARCH=$(GOARCH) \
|
||||
-e GOCACHE=/tmp/ \
|
||||
--user $$(id -u):$$(id -g) \
|
||||
-v "$$(pwd):/workspace$(DOCKER_VOL_OPTS)" \
|
||||
-w /workspace/tools/setup-envtest \
|
||||
golang:$(GO_VERSION) \
|
||||
go build -a -trimpath -ldflags "-extldflags '-static'" \
|
||||
-o ./out/$(RELEASE_BINARY) ./
|
||||
|
||||
## --------------------------------------
|
||||
## Cleanup / Verification
|
||||
@@ -119,16 +190,29 @@ clean: ## Cleanup.
|
||||
clean-bin: ## Remove all generated binaries.
|
||||
rm -rf hack/tools/bin
|
||||
|
||||
.PHONY: clean-release
|
||||
clean-release: ## Remove the release folder
|
||||
rm -rf $(RELEASE_DIR)
|
||||
|
||||
.PHONY: verify-modules
|
||||
verify-modules: modules ## Verify go modules are up to date
|
||||
verify-modules: modules $(GO_MOD_CHECK) ## 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 $(SCRATCH_ENV_DIR)/go.sum); then \
|
||||
git diff; \
|
||||
echo "go module files are out of date, please run 'make modules'"; exit 1; \
|
||||
fi
|
||||
$(GO_MOD_CHECK) $(GO_MOD_CHECK_IGNORE)
|
||||
|
||||
.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
|
||||
APIDIFF_OLD_COMMIT ?= $(shell git rev-parse origin/main)
|
||||
|
||||
.PHONY: apidiff
|
||||
verify-apidiff: $(GO_APIDIFF) ## Check for API differences
|
||||
$(GO_APIDIFF) $(APIDIFF_OLD_COMMIT) --print-compatible
|
||||
|
||||
## --------------------------------------
|
||||
## Helpers
|
||||
## --------------------------------------
|
||||
|
||||
##@ helpers:
|
||||
|
||||
go-version: ## Print the go version we use to compile our binaries and images
|
||||
@echo $(GO_VERSION)
|
||||
|
||||
3
vendor/sigs.k8s.io/controller-runtime/OWNERS
generated
vendored
3
vendor/sigs.k8s.io/controller-runtime/OWNERS
generated
vendored
@@ -6,5 +6,6 @@ approvers:
|
||||
- controller-runtime-approvers
|
||||
reviewers:
|
||||
- controller-runtime-admins
|
||||
- controller-runtime-reviewers
|
||||
- controller-runtime-maintainers
|
||||
- controller-runtime-approvers
|
||||
- controller-runtime-reviewers
|
||||
|
||||
6
vendor/sigs.k8s.io/controller-runtime/OWNERS_ALIASES
generated
vendored
6
vendor/sigs.k8s.io/controller-runtime/OWNERS_ALIASES
generated
vendored
@@ -25,12 +25,6 @@ aliases:
|
||||
- varshaprasad96
|
||||
- inteon
|
||||
|
||||
# folks to can approve things in the directly-ported
|
||||
# testing_frameworks portions of the codebase
|
||||
testing-integration-approvers:
|
||||
- apelisse
|
||||
- hoegaarden
|
||||
|
||||
# folks who may have context on ancient history,
|
||||
# but are no longer directly involved
|
||||
controller-runtime-emeritus-maintainers:
|
||||
|
||||
20
vendor/sigs.k8s.io/controller-runtime/README.md
generated
vendored
20
vendor/sigs.k8s.io/controller-runtime/README.md
generated
vendored
@@ -40,6 +40,25 @@ Contributors:
|
||||
* [Documentation Changes](/.github/PULL_REQUEST_TEMPLATE/docs.md)
|
||||
* [Test/Build/Other Changes](/.github/PULL_REQUEST_TEMPLATE/other.md)
|
||||
|
||||
## Compatibility
|
||||
|
||||
Every minor version of controller-runtime has been tested with a specific minor version of client-go. A controller-runtime minor version *may* be compatible with
|
||||
other client-go minor versions, but this is by chance and neither supported nor tested. In general, we create one minor version of controller-runtime
|
||||
for each minor version of client-go and other k8s.io/* dependencies.
|
||||
|
||||
The minimum Go version of controller-runtime is the highest minimum Go version of our Go dependencies. Usually, this will
|
||||
be identical to the minimum Go version of the corresponding k8s.io/* dependencies.
|
||||
|
||||
Compatible k8s.io/*, client-go and minimum Go versions can be looked up in our [go.mod](go.mod) file.
|
||||
|
||||
| | k8s.io/*, client-go | minimum Go version |
|
||||
|----------|:-------------------:|:------------------:|
|
||||
| CR v0.19 | v0.31 | 1.22 |
|
||||
| CR v0.18 | v0.30 | 1.22 |
|
||||
| CR v0.17 | v0.29 | 1.21 |
|
||||
| CR v0.16 | v0.28 | 1.20 |
|
||||
| CR v0.15 | v0.27 | 1.20 |
|
||||
|
||||
## FAQ
|
||||
|
||||
See [FAQ.md](FAQ.md)
|
||||
@@ -57,6 +76,7 @@ You can reach the maintainers of this project at:
|
||||
- Google Group: [kubebuilder@googlegroups.com](https://groups.google.com/forum/#!forum/kubebuilder)
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are greatly appreciated. The maintainers actively manage the issues list, and try to highlight issues suitable for newcomers.
|
||||
The project follows the typical GitHub pull request model. See [CONTRIBUTING.md](CONTRIBUTING.md) for more details.
|
||||
Before starting any work, please either comment on an existing issue, or file a new one.
|
||||
|
||||
12
vendor/sigs.k8s.io/controller-runtime/alias.go
generated
vendored
12
vendor/sigs.k8s.io/controller-runtime/alias.go
generated
vendored
@@ -21,7 +21,6 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"sigs.k8s.io/controller-runtime/pkg/builder"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/config"
|
||||
cfg "sigs.k8s.io/controller-runtime/pkg/config"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||
"sigs.k8s.io/controller-runtime/pkg/manager"
|
||||
@@ -96,13 +95,6 @@ var (
|
||||
// * $HOME/.kube/config if exists.
|
||||
GetConfig = config.GetConfig
|
||||
|
||||
// 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.
|
||||
NewControllerManagedBy = builder.ControllerManagedBy
|
||||
|
||||
@@ -130,8 +122,8 @@ var (
|
||||
// there is another OwnerReference with Controller flag set.
|
||||
SetControllerReference = controllerutil.SetControllerReference
|
||||
|
||||
// SetupSignalHandler registered for SIGTERM and SIGINT. A stop channel is returned
|
||||
// which is closed on one of these signals. If a second signal is caught, the program
|
||||
// SetupSignalHandler registers for SIGTERM and SIGINT. A context is returned
|
||||
// which is canceled on one of these signals. If a second signal is caught, the program
|
||||
// is terminated with exit code 1.
|
||||
SetupSignalHandler = signals.SetupSignalHandler
|
||||
|
||||
|
||||
181
vendor/sigs.k8s.io/controller-runtime/pkg/builder/controller.go
generated
vendored
181
vendor/sigs.k8s.io/controller-runtime/pkg/builder/controller.go
generated
vendored
@@ -19,6 +19,7 @@ package builder
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
@@ -30,17 +31,12 @@ 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"
|
||||
"sigs.k8s.io/controller-runtime/pkg/source"
|
||||
)
|
||||
|
||||
// Supporting mocking out functions for testing.
|
||||
var newController = controller.New
|
||||
var getGvk = apiutil.GVKForObject
|
||||
|
||||
// project represents other forms that we can use to
|
||||
// send/receive a given resource (metadata-only, unstructured, etc).
|
||||
type objectProjection int
|
||||
@@ -53,20 +49,32 @@ const (
|
||||
)
|
||||
|
||||
// Builder builds a Controller.
|
||||
type Builder struct {
|
||||
type Builder = TypedBuilder[reconcile.Request]
|
||||
|
||||
// TypedBuilder builds a Controller. The request is the request type
|
||||
// that is passed to the workqueue and then to the Reconciler.
|
||||
// The workqueue de-duplicates identical requests.
|
||||
type TypedBuilder[request comparable] struct {
|
||||
forInput ForInput
|
||||
ownsInput []OwnsInput
|
||||
watchesInput []WatchesInput
|
||||
rawSources []source.TypedSource[request]
|
||||
watchesInput []WatchesInput[request]
|
||||
mgr manager.Manager
|
||||
globalPredicates []predicate.Predicate
|
||||
ctrl controller.Controller
|
||||
ctrlOptions controller.Options
|
||||
ctrl controller.TypedController[request]
|
||||
ctrlOptions controller.TypedOptions[request]
|
||||
name string
|
||||
newController func(name string, mgr manager.Manager, options controller.TypedOptions[request]) (controller.TypedController[request], error)
|
||||
}
|
||||
|
||||
// ControllerManagedBy returns a new controller builder that will be started by the provided Manager.
|
||||
func ControllerManagedBy(m manager.Manager) *Builder {
|
||||
return &Builder{mgr: m}
|
||||
return TypedControllerManagedBy[reconcile.Request](m)
|
||||
}
|
||||
|
||||
// TypedControllerManagedBy returns a new typed controller builder that will be started by the provided Manager.
|
||||
func TypedControllerManagedBy[request comparable](m manager.Manager) *TypedBuilder[request] {
|
||||
return &TypedBuilder[request]{mgr: m}
|
||||
}
|
||||
|
||||
// ForInput represents the information set by the For method.
|
||||
@@ -79,9 +87,10 @@ type ForInput struct {
|
||||
|
||||
// For defines the type of Object being *reconciled*, and configures the ControllerManagedBy to respond to create / delete /
|
||||
// update events by *reconciling the object*.
|
||||
//
|
||||
// This is the equivalent of calling
|
||||
// Watches(&source.Kind{Type: apiType}, &handler.EnqueueRequestForObject{}).
|
||||
func (blder *Builder) For(object client.Object, opts ...ForOption) *Builder {
|
||||
// Watches(source.Kind(cache, &Type{}, &handler.EnqueueRequestForObject{})).
|
||||
func (blder *TypedBuilder[request]) For(object client.Object, opts ...ForOption) *TypedBuilder[request] {
|
||||
if blder.forInput.object != nil {
|
||||
blder.forInput.err = fmt.Errorf("For(...) should only be called once, could not assign multiple objects for reconciliation")
|
||||
return blder
|
||||
@@ -110,8 +119,8 @@ type OwnsInput struct {
|
||||
// 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 {
|
||||
// Watches(source.Kind(cache, &Type{}, handler.EnqueueRequestForOwner([...], &OwnerType{}, OnlyControllerOwner()))).
|
||||
func (blder *TypedBuilder[request]) Owns(object client.Object, opts ...OwnsOption) *TypedBuilder[request] {
|
||||
input := OwnsInput{object: object}
|
||||
for _, opt := range opts {
|
||||
opt.ApplyToOwns(&input)
|
||||
@@ -121,22 +130,48 @@ func (blder *Builder) Owns(object client.Object, opts ...OwnsOption) *Builder {
|
||||
return blder
|
||||
}
|
||||
|
||||
type untypedWatchesInput interface {
|
||||
setPredicates([]predicate.Predicate)
|
||||
setObjectProjection(objectProjection)
|
||||
}
|
||||
|
||||
// WatchesInput represents the information set by Watches method.
|
||||
type WatchesInput struct {
|
||||
src source.Source
|
||||
eventHandler handler.EventHandler
|
||||
type WatchesInput[request comparable] struct {
|
||||
obj client.Object
|
||||
handler handler.TypedEventHandler[client.Object, request]
|
||||
predicates []predicate.Predicate
|
||||
objectProjection objectProjection
|
||||
}
|
||||
|
||||
func (w *WatchesInput[request]) setPredicates(predicates []predicate.Predicate) {
|
||||
w.predicates = predicates
|
||||
}
|
||||
|
||||
func (w *WatchesInput[request]) setObjectProjection(objectProjection objectProjection) {
|
||||
w.objectProjection = objectProjection
|
||||
}
|
||||
|
||||
// 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(cache, 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...)
|
||||
// WatchesRawSource(source.Kind(cache, object, eventHandler, predicates...)).
|
||||
func (blder *TypedBuilder[request]) Watches(
|
||||
object client.Object,
|
||||
eventHandler handler.TypedEventHandler[client.Object, request],
|
||||
opts ...WatchesOption,
|
||||
) *TypedBuilder[request] {
|
||||
input := WatchesInput[request]{
|
||||
obj: object,
|
||||
handler: eventHandler,
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt.ApplyToWatches(&input)
|
||||
}
|
||||
|
||||
blder.watchesInput = append(blder.watchesInput, input)
|
||||
|
||||
return blder
|
||||
}
|
||||
|
||||
// WatchesMetadata is the same as Watches, but forces the internal cache to only watch PartialObjectMetadata.
|
||||
@@ -166,43 +201,45 @@ func (blder *Builder) Watches(object client.Object, eventHandler handler.EventHa
|
||||
// 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 {
|
||||
func (blder *TypedBuilder[request]) WatchesMetadata(
|
||||
object client.Object,
|
||||
eventHandler handler.TypedEventHandler[client.Object, request],
|
||||
opts ...WatchesOption,
|
||||
) *TypedBuilder[request] {
|
||||
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.
|
||||
//
|
||||
// STOP! Consider using For(...), Owns(...), Watches(...), WatchesMetadata(...) instead.
|
||||
// This method is only exposed for more advanced use cases, most users should use one of the 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)
|
||||
}
|
||||
// WatchesRawSource does not respect predicates configured through WithEventFilter.
|
||||
//
|
||||
// WatchesRawSource makes it possible to use typed handlers and predicates with `source.Kind` as well as custom source implementations.
|
||||
func (blder *TypedBuilder[request]) WatchesRawSource(src source.TypedSource[request]) *TypedBuilder[request] {
|
||||
blder.rawSources = append(blder.rawSources, src)
|
||||
|
||||
blder.watchesInput = append(blder.watchesInput, input)
|
||||
return blder
|
||||
}
|
||||
|
||||
// WithEventFilter sets the event filters, to filter which create/update/delete/generic events eventually
|
||||
// trigger reconciliations. For example, filtering on whether the resource version has changed.
|
||||
// Given predicate is added for all watched objects.
|
||||
// Given predicate is added for all watched objects and thus must be able to deal with the type
|
||||
// of all watched objects.
|
||||
//
|
||||
// Defaults to the empty list.
|
||||
func (blder *Builder) WithEventFilter(p predicate.Predicate) *Builder {
|
||||
func (blder *TypedBuilder[request]) WithEventFilter(p predicate.Predicate) *TypedBuilder[request] {
|
||||
blder.globalPredicates = append(blder.globalPredicates, p)
|
||||
return blder
|
||||
}
|
||||
|
||||
// WithOptions overrides the controller options used in doController. Defaults to empty.
|
||||
func (blder *Builder) WithOptions(options controller.Options) *Builder {
|
||||
func (blder *TypedBuilder[request]) WithOptions(options controller.TypedOptions[request]) *TypedBuilder[request] {
|
||||
blder.ctrlOptions = options
|
||||
return blder
|
||||
}
|
||||
|
||||
// WithLogConstructor overrides the controller options's LogConstructor.
|
||||
func (blder *Builder) WithLogConstructor(logConstructor func(*reconcile.Request) logr.Logger) *Builder {
|
||||
func (blder *TypedBuilder[request]) WithLogConstructor(logConstructor func(*request) logr.Logger) *TypedBuilder[request] {
|
||||
blder.ctrlOptions.LogConstructor = logConstructor
|
||||
return blder
|
||||
}
|
||||
@@ -212,19 +249,21 @@ func (blder *Builder) WithLogConstructor(logConstructor func(*reconcile.Request)
|
||||
// (underscores and alphanumeric characters only).
|
||||
//
|
||||
// By default, controllers are named using the lowercase version of their kind.
|
||||
func (blder *Builder) Named(name string) *Builder {
|
||||
//
|
||||
// The name must be unique as it is used to identify the controller in metrics and logs.
|
||||
func (blder *TypedBuilder[request]) Named(name string) *TypedBuilder[request] {
|
||||
blder.name = name
|
||||
return blder
|
||||
}
|
||||
|
||||
// Complete builds the Application Controller.
|
||||
func (blder *Builder) Complete(r reconcile.Reconciler) error {
|
||||
func (blder *TypedBuilder[request]) Complete(r reconcile.TypedReconciler[request]) error {
|
||||
_, err := blder.Build(r)
|
||||
return err
|
||||
}
|
||||
|
||||
// Build builds the Application Controller and returns the Controller it created.
|
||||
func (blder *Builder) Build(r reconcile.Reconciler) (controller.Controller, error) {
|
||||
func (blder *TypedBuilder[request]) Build(r reconcile.TypedReconciler[request]) (controller.TypedController[request], error) {
|
||||
if r == nil {
|
||||
return nil, fmt.Errorf("must provide a non-nil Reconciler")
|
||||
}
|
||||
@@ -248,13 +287,13 @@ func (blder *Builder) Build(r reconcile.Reconciler) (controller.Controller, erro
|
||||
return blder.ctrl, nil
|
||||
}
|
||||
|
||||
func (blder *Builder) project(obj client.Object, proj objectProjection) (client.Object, error) {
|
||||
func (blder *TypedBuilder[request]) project(obj client.Object, proj objectProjection) (client.Object, error) {
|
||||
switch proj {
|
||||
case projectAsNormal:
|
||||
return obj, nil
|
||||
case projectAsMetadata:
|
||||
metaObj := &metav1.PartialObjectMetadata{}
|
||||
gvk, err := getGvk(obj, blder.mgr.GetScheme())
|
||||
gvk, err := apiutil.GVKForObject(obj, blder.mgr.GetScheme())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to determine GVK of %T for a metadata-only watch: %w", obj, err)
|
||||
}
|
||||
@@ -265,18 +304,24 @@ func (blder *Builder) project(obj client.Object, proj objectProjection) (client.
|
||||
}
|
||||
}
|
||||
|
||||
func (blder *Builder) doWatch() error {
|
||||
func (blder *TypedBuilder[request]) doWatch() error {
|
||||
// Reconcile type
|
||||
if blder.forInput.object != nil {
|
||||
obj, err := blder.project(blder.forInput.object, blder.forInput.objectProjection)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
src := source.Kind(blder.mgr.GetCache(), obj)
|
||||
hdler := &handler.EnqueueRequestForObject{}
|
||||
|
||||
if reflect.TypeFor[request]() != reflect.TypeOf(reconcile.Request{}) {
|
||||
return fmt.Errorf("For() can only be used with reconcile.Request, got %T", *new(request))
|
||||
}
|
||||
|
||||
var hdler handler.TypedEventHandler[client.Object, request]
|
||||
reflect.ValueOf(&hdler).Elem().Set(reflect.ValueOf(&handler.EnqueueRequestForObject{}))
|
||||
allPredicates := append([]predicate.Predicate(nil), blder.globalPredicates...)
|
||||
allPredicates = append(allPredicates, blder.forInput.predicates...)
|
||||
if err := blder.ctrl.Watch(src, hdler, allPredicates...); err != nil {
|
||||
src := source.TypedKind(blder.mgr.GetCache(), obj, hdler, allPredicates...)
|
||||
if err := blder.ctrl.Watch(src); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -290,46 +335,49 @@ func (blder *Builder) doWatch() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
src := source.Kind(blder.mgr.GetCache(), obj)
|
||||
opts := []handler.OwnerOption{}
|
||||
if !own.matchEveryOwner {
|
||||
opts = append(opts, handler.OnlyControllerOwner())
|
||||
}
|
||||
hdler := handler.EnqueueRequestForOwner(
|
||||
|
||||
var hdler handler.TypedEventHandler[client.Object, request]
|
||||
reflect.ValueOf(&hdler).Elem().Set(reflect.ValueOf(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 {
|
||||
src := source.TypedKind(blder.mgr.GetCache(), obj, hdler, allPredicates...)
|
||||
if err := blder.ctrl.Watch(src); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Do the watch requests
|
||||
if len(blder.watchesInput) == 0 && blder.forInput.object == nil {
|
||||
return errors.New("there are no watches configured, controller will never get triggered. Use For(), Owns() or Watches() to set them up")
|
||||
if len(blder.watchesInput) == 0 && blder.forInput.object == nil && len(blder.rawSources) == 0 {
|
||||
return errors.New("there are no watches configured, controller will never get triggered. Use For(), Owns(), Watches() or WatchesRawSource() to set them up")
|
||||
}
|
||||
for _, w := range blder.watchesInput {
|
||||
// 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
|
||||
}
|
||||
srcKind.Type = typeForSrc
|
||||
projected, err := blder.project(w.obj, w.objectProjection)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to project for %T: %w", w.obj, err)
|
||||
}
|
||||
allPredicates := append([]predicate.Predicate(nil), blder.globalPredicates...)
|
||||
allPredicates = append(allPredicates, w.predicates...)
|
||||
if err := blder.ctrl.Watch(w.src, w.eventHandler, allPredicates...); err != nil {
|
||||
if err := blder.ctrl.Watch(source.TypedKind(blder.mgr.GetCache(), projected, w.handler, allPredicates...)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, src := range blder.rawSources {
|
||||
if err := blder.ctrl.Watch(src); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (blder *Builder) getControllerName(gvk schema.GroupVersionKind, hasGVK bool) (string, error) {
|
||||
func (blder *TypedBuilder[request]) getControllerName(gvk schema.GroupVersionKind, hasGVK bool) (string, error) {
|
||||
if blder.name != "" {
|
||||
return blder.name, nil
|
||||
}
|
||||
@@ -339,7 +387,7 @@ func (blder *Builder) getControllerName(gvk schema.GroupVersionKind, hasGVK bool
|
||||
return strings.ToLower(gvk.Kind), nil
|
||||
}
|
||||
|
||||
func (blder *Builder) doController(r reconcile.Reconciler) error {
|
||||
func (blder *TypedBuilder[request]) doController(r reconcile.TypedReconciler[request]) error {
|
||||
globalOpts := blder.mgr.GetControllerOptions()
|
||||
|
||||
ctrlOptions := blder.ctrlOptions
|
||||
@@ -356,7 +404,7 @@ func (blder *Builder) doController(r reconcile.Reconciler) error {
|
||||
hasGVK := blder.forInput.object != nil
|
||||
if hasGVK {
|
||||
var err error
|
||||
gvk, err = getGvk(blder.forInput.object, blder.mgr.GetScheme())
|
||||
gvk, err = apiutil.GVKForObject(blder.forInput.object, blder.mgr.GetScheme())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -393,9 +441,10 @@ func (blder *Builder) doController(r reconcile.Reconciler) error {
|
||||
)
|
||||
}
|
||||
|
||||
ctrlOptions.LogConstructor = func(req *reconcile.Request) logr.Logger {
|
||||
ctrlOptions.LogConstructor = func(in *request) logr.Logger {
|
||||
log := log
|
||||
if req != nil {
|
||||
|
||||
if req, ok := any(in).(*reconcile.Request); ok && req != nil {
|
||||
if hasGVK {
|
||||
log = log.WithValues(gvk.Kind, klog.KRef(req.Namespace, req.Name))
|
||||
}
|
||||
@@ -407,7 +456,11 @@ func (blder *Builder) doController(r reconcile.Reconciler) error {
|
||||
}
|
||||
}
|
||||
|
||||
if blder.newController == nil {
|
||||
blder.newController = controller.NewTyped[request]
|
||||
}
|
||||
|
||||
// Build the controller and return.
|
||||
blder.ctrl, err = newController(controllerName, blder.mgr, ctrlOptions)
|
||||
blder.ctrl, err = blder.newController(controllerName, blder.mgr, ctrlOptions)
|
||||
return err
|
||||
}
|
||||
|
||||
10
vendor/sigs.k8s.io/controller-runtime/pkg/builder/options.go
generated
vendored
10
vendor/sigs.k8s.io/controller-runtime/pkg/builder/options.go
generated
vendored
@@ -37,7 +37,7 @@ type OwnsOption interface {
|
||||
// WatchesOption is some configuration that modifies options for a watches request.
|
||||
type WatchesOption interface {
|
||||
// ApplyToWatches applies this configuration to the given watches options.
|
||||
ApplyToWatches(*WatchesInput)
|
||||
ApplyToWatches(untypedWatchesInput)
|
||||
}
|
||||
|
||||
// }}}
|
||||
@@ -67,8 +67,8 @@ func (w Predicates) ApplyToOwns(opts *OwnsInput) {
|
||||
}
|
||||
|
||||
// ApplyToWatches applies this configuration to the given WatchesInput options.
|
||||
func (w Predicates) ApplyToWatches(opts *WatchesInput) {
|
||||
opts.predicates = w.predicates
|
||||
func (w Predicates) ApplyToWatches(opts untypedWatchesInput) {
|
||||
opts.setPredicates(w.predicates)
|
||||
}
|
||||
|
||||
var _ ForOption = &Predicates{}
|
||||
@@ -95,8 +95,8 @@ func (p projectAs) ApplyToOwns(opts *OwnsInput) {
|
||||
}
|
||||
|
||||
// ApplyToWatches applies this configuration to the given WatchesInput options.
|
||||
func (p projectAs) ApplyToWatches(opts *WatchesInput) {
|
||||
opts.objectProjection = objectProjection(p)
|
||||
func (p projectAs) ApplyToWatches(opts untypedWatchesInput) {
|
||||
opts.setObjectProjection(objectProjection(p))
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
37
vendor/sigs.k8s.io/controller-runtime/pkg/builder/webhook.go
generated
vendored
37
vendor/sigs.k8s.io/controller-runtime/pkg/builder/webhook.go
generated
vendored
@@ -42,8 +42,9 @@ type WebhookBuilder struct {
|
||||
gvk schema.GroupVersionKind
|
||||
mgr manager.Manager
|
||||
config *rest.Config
|
||||
recoverPanic bool
|
||||
recoverPanic *bool
|
||||
logConstructor func(base logr.Logger, req *admission.Request) logr.Logger
|
||||
err error
|
||||
}
|
||||
|
||||
// WebhookManagedBy returns a new webhook builder.
|
||||
@@ -57,6 +58,9 @@ func WebhookManagedBy(m manager.Manager) *WebhookBuilder {
|
||||
// If the given object implements the admission.Defaulter interface, a MutatingWebhook will be wired for this type.
|
||||
// If the given object implements the admission.Validator interface, a ValidatingWebhook will be wired for this type.
|
||||
func (blder *WebhookBuilder) For(apiType runtime.Object) *WebhookBuilder {
|
||||
if blder.apiType != nil {
|
||||
blder.err = errors.New("For(...) should only be called once, could not assign multiple objects for webhook registration")
|
||||
}
|
||||
blder.apiType = apiType
|
||||
return blder
|
||||
}
|
||||
@@ -80,8 +84,9 @@ func (blder *WebhookBuilder) WithLogConstructor(logConstructor func(base logr.Lo
|
||||
}
|
||||
|
||||
// RecoverPanic indicates whether panics caused by the webhook should be recovered.
|
||||
func (blder *WebhookBuilder) RecoverPanic() *WebhookBuilder {
|
||||
blder.recoverPanic = true
|
||||
// Defaults to true.
|
||||
func (blder *WebhookBuilder) RecoverPanic(recoverPanic bool) *WebhookBuilder {
|
||||
blder.recoverPanic = &recoverPanic
|
||||
return blder
|
||||
}
|
||||
|
||||
@@ -142,7 +147,7 @@ func (blder *WebhookBuilder) registerWebhooks() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return blder.err
|
||||
}
|
||||
|
||||
// registerDefaultingWebhook registers a defaulting webhook if necessary.
|
||||
@@ -165,10 +170,18 @@ func (blder *WebhookBuilder) registerDefaultingWebhook() {
|
||||
|
||||
func (blder *WebhookBuilder) getDefaultingWebhook() *admission.Webhook {
|
||||
if defaulter := blder.customDefaulter; defaulter != nil {
|
||||
return admission.WithCustomDefaulter(blder.mgr.GetScheme(), blder.apiType, defaulter).WithRecoverPanic(blder.recoverPanic)
|
||||
w := admission.WithCustomDefaulter(blder.mgr.GetScheme(), blder.apiType, defaulter)
|
||||
if blder.recoverPanic != nil {
|
||||
w = w.WithRecoverPanic(*blder.recoverPanic)
|
||||
}
|
||||
return w
|
||||
}
|
||||
if defaulter, ok := blder.apiType.(admission.Defaulter); ok {
|
||||
return admission.DefaultingWebhookFor(blder.mgr.GetScheme(), defaulter).WithRecoverPanic(blder.recoverPanic)
|
||||
w := admission.DefaultingWebhookFor(blder.mgr.GetScheme(), defaulter)
|
||||
if blder.recoverPanic != nil {
|
||||
w = w.WithRecoverPanic(*blder.recoverPanic)
|
||||
}
|
||||
return w
|
||||
}
|
||||
log.Info(
|
||||
"skip registering a mutating webhook, object does not implement admission.Defaulter or WithDefaulter wasn't called",
|
||||
@@ -196,10 +209,18 @@ func (blder *WebhookBuilder) registerValidatingWebhook() {
|
||||
|
||||
func (blder *WebhookBuilder) getValidatingWebhook() *admission.Webhook {
|
||||
if validator := blder.customValidator; validator != nil {
|
||||
return admission.WithCustomValidator(blder.mgr.GetScheme(), blder.apiType, validator).WithRecoverPanic(blder.recoverPanic)
|
||||
w := admission.WithCustomValidator(blder.mgr.GetScheme(), blder.apiType, validator)
|
||||
if blder.recoverPanic != nil {
|
||||
w = w.WithRecoverPanic(*blder.recoverPanic)
|
||||
}
|
||||
return w
|
||||
}
|
||||
if validator, ok := blder.apiType.(admission.Validator); ok {
|
||||
return admission.ValidatingWebhookFor(blder.mgr.GetScheme(), validator).WithRecoverPanic(blder.recoverPanic)
|
||||
w := admission.ValidatingWebhookFor(blder.mgr.GetScheme(), validator)
|
||||
if blder.recoverPanic != nil {
|
||||
w = w.WithRecoverPanic(*blder.recoverPanic)
|
||||
}
|
||||
return w
|
||||
}
|
||||
log.Info(
|
||||
"skip registering a validating webhook, object does not implement admission.Validator or WithValidator wasn't called",
|
||||
|
||||
84
vendor/sigs.k8s.io/controller-runtime/pkg/cache/cache.go
generated
vendored
84
vendor/sigs.k8s.io/controller-runtime/pkg/cache/cache.go
generated
vendored
@@ -20,6 +20,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"golang.org/x/exp/maps"
|
||||
@@ -38,11 +39,9 @@ import (
|
||||
"sigs.k8s.io/controller-runtime/pkg/cache/internal"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/internal/log"
|
||||
)
|
||||
|
||||
var (
|
||||
log = logf.RuntimeLog.WithName("object-cache")
|
||||
defaultSyncPeriod = 10 * time.Hour
|
||||
)
|
||||
|
||||
@@ -118,8 +117,8 @@ type Informer interface {
|
||||
// This function is guaranteed to be idempotent and thread-safe.
|
||||
RemoveEventHandler(handle toolscache.ResourceEventHandlerRegistration) error
|
||||
|
||||
// AddIndexers adds indexers to this store. If this is called after there is already data
|
||||
// in the store, the results are undefined.
|
||||
// AddIndexers adds indexers to this store. It is valid to add indexers
|
||||
// after an informer was started.
|
||||
AddIndexers(indexers toolscache.Indexers) error
|
||||
|
||||
// HasSynced return true if the informers underlying store has synced.
|
||||
@@ -202,6 +201,9 @@ type Options struct {
|
||||
|
||||
// DefaultTransform will be used as transform for all object types
|
||||
// unless there is already one set in ByObject or DefaultNamespaces.
|
||||
//
|
||||
// A typical usecase for this is to use TransformStripManagedFields
|
||||
// to reduce the caches memory usage.
|
||||
DefaultTransform toolscache.TransformFunc
|
||||
|
||||
// DefaultWatchErrorHandler will be used to the WatchErrorHandler which is called
|
||||
@@ -221,7 +223,7 @@ type Options struct {
|
||||
DefaultUnsafeDisableDeepCopy *bool
|
||||
|
||||
// ByObject restricts the cache's ListWatch to the desired fields per GVK at the specified object.
|
||||
// object, this will fall through to Default* settings.
|
||||
// If unset, this will fall through to the Default* settings.
|
||||
ByObject map[client.Object]ByObject
|
||||
|
||||
// newInformer allows overriding of NewSharedIndexInformer for testing.
|
||||
@@ -345,6 +347,20 @@ func New(cfg *rest.Config, opts Options) (Cache, error) {
|
||||
return delegating, nil
|
||||
}
|
||||
|
||||
// TransformStripManagedFields strips the managed fields of an object before it is committed to the cache.
|
||||
// If you are not explicitly accessing managedFields from your code, setting this as `DefaultTransform`
|
||||
// on the cache can lead to a significant reduction in memory usage.
|
||||
func TransformStripManagedFields() toolscache.TransformFunc {
|
||||
return func(in any) (any, error) {
|
||||
// Nilcheck managed fields to avoid hitting https://github.com/kubernetes/kubernetes/issues/124337
|
||||
if obj, err := meta.Accessor(in); err == nil && obj.GetManagedFields() != nil {
|
||||
obj.SetManagedFields(nil)
|
||||
}
|
||||
|
||||
return in, nil
|
||||
}
|
||||
}
|
||||
|
||||
func optionDefaultsToConfig(opts *Options) Config {
|
||||
return Config{
|
||||
LabelSelector: opts.DefaultLabelSelector,
|
||||
@@ -418,14 +434,6 @@ func defaultOpts(config *rest.Config, opts Options) (Options, error) {
|
||||
}
|
||||
}
|
||||
|
||||
for namespace, cfg := range opts.DefaultNamespaces {
|
||||
cfg = defaultConfig(cfg, optionDefaultsToConfig(&opts))
|
||||
if namespace == metav1.NamespaceAll {
|
||||
cfg.FieldSelector = fields.AndSelectors(appendIfNotNil(namespaceAllSelector(maps.Keys(opts.DefaultNamespaces)), cfg.FieldSelector)...)
|
||||
}
|
||||
opts.DefaultNamespaces[namespace] = cfg
|
||||
}
|
||||
|
||||
for obj, byObject := range opts.ByObject {
|
||||
isNamespaced, err := apiutil.IsObjectNamespaced(obj, opts.Scheme, opts.Mapper)
|
||||
if err != nil {
|
||||
@@ -435,7 +443,12 @@ func defaultOpts(config *rest.Config, opts Options) (Options, error) {
|
||||
return opts, fmt.Errorf("type %T is not namespaced, but its ByObject.Namespaces setting is not nil", obj)
|
||||
}
|
||||
|
||||
// Default the namespace-level configs first, because they need to use the undefaulted type-level config.
|
||||
if isNamespaced && byObject.Namespaces == nil {
|
||||
byObject.Namespaces = maps.Clone(opts.DefaultNamespaces)
|
||||
}
|
||||
|
||||
// Default the namespace-level configs first, because they need to use the undefaulted type-level config
|
||||
// to be able to potentially fall through to settings from DefaultNamespaces.
|
||||
for namespace, config := range byObject.Namespaces {
|
||||
// 1. Default from the undefaulted type-level config
|
||||
config = defaultConfig(config, byObjectToConfig(byObject))
|
||||
@@ -461,19 +474,35 @@ func defaultOpts(config *rest.Config, opts Options) (Options, error) {
|
||||
byObject.Namespaces[namespace] = config
|
||||
}
|
||||
|
||||
defaultedConfig := defaultConfig(byObjectToConfig(byObject), optionDefaultsToConfig(&opts))
|
||||
byObject.Label = defaultedConfig.LabelSelector
|
||||
byObject.Field = defaultedConfig.FieldSelector
|
||||
byObject.Transform = defaultedConfig.Transform
|
||||
byObject.UnsafeDisableDeepCopy = defaultedConfig.UnsafeDisableDeepCopy
|
||||
|
||||
if isNamespaced && byObject.Namespaces == nil {
|
||||
byObject.Namespaces = opts.DefaultNamespaces
|
||||
// Only default ByObject iself if it isn't namespaced or has no namespaces configured, as only
|
||||
// then any of this will be honored.
|
||||
if !isNamespaced || len(byObject.Namespaces) == 0 {
|
||||
defaultedConfig := defaultConfig(byObjectToConfig(byObject), optionDefaultsToConfig(&opts))
|
||||
byObject.Label = defaultedConfig.LabelSelector
|
||||
byObject.Field = defaultedConfig.FieldSelector
|
||||
byObject.Transform = defaultedConfig.Transform
|
||||
byObject.UnsafeDisableDeepCopy = defaultedConfig.UnsafeDisableDeepCopy
|
||||
}
|
||||
|
||||
opts.ByObject[obj] = byObject
|
||||
}
|
||||
|
||||
// Default namespaces after byObject has been defaulted, otherwise a namespace without selectors
|
||||
// will get the `Default` selectors, then get copied to byObject and then not get defaulted from
|
||||
// byObject, as it already has selectors.
|
||||
for namespace, cfg := range opts.DefaultNamespaces {
|
||||
cfg = defaultConfig(cfg, optionDefaultsToConfig(&opts))
|
||||
if namespace == metav1.NamespaceAll {
|
||||
cfg.FieldSelector = fields.AndSelectors(
|
||||
appendIfNotNil(
|
||||
namespaceAllSelector(maps.Keys(opts.DefaultNamespaces)),
|
||||
cfg.FieldSelector,
|
||||
)...,
|
||||
)
|
||||
}
|
||||
opts.DefaultNamespaces[namespace] = cfg
|
||||
}
|
||||
|
||||
// Default the resync period to 10 hours if unset
|
||||
if opts.SyncPeriod == nil {
|
||||
opts.SyncPeriod = &defaultSyncPeriod
|
||||
@@ -498,20 +527,21 @@ func defaultConfig(toDefault, defaultFrom Config) Config {
|
||||
return toDefault
|
||||
}
|
||||
|
||||
func namespaceAllSelector(namespaces []string) fields.Selector {
|
||||
func namespaceAllSelector(namespaces []string) []fields.Selector {
|
||||
selectors := make([]fields.Selector, 0, len(namespaces)-1)
|
||||
sort.Strings(namespaces)
|
||||
for _, namespace := range namespaces {
|
||||
if namespace != metav1.NamespaceAll {
|
||||
selectors = append(selectors, fields.OneTermNotEqualSelector("metadata.namespace", namespace))
|
||||
}
|
||||
}
|
||||
|
||||
return fields.AndSelectors(selectors...)
|
||||
return selectors
|
||||
}
|
||||
|
||||
func appendIfNotNil[T comparable](a, b T) []T {
|
||||
func appendIfNotNil[T comparable](a []T, b T) []T {
|
||||
if b != *new(T) {
|
||||
return []T{a, b}
|
||||
return append(a, b)
|
||||
}
|
||||
return []T{a}
|
||||
return a
|
||||
}
|
||||
|
||||
2
vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/cache_reader.go
generated
vendored
2
vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/cache_reader.go
generated
vendored
@@ -36,7 +36,7 @@ import (
|
||||
// CacheReader is a client.Reader.
|
||||
var _ client.Reader = &CacheReader{}
|
||||
|
||||
// CacheReader wraps a cache.Index to implement the client.CacheReader interface for a single type.
|
||||
// CacheReader wraps a cache.Index to implement the client.Reader interface for a single type.
|
||||
type CacheReader struct {
|
||||
// indexer is the underlying indexer wrapped by this cache.
|
||||
indexer cache.Indexer
|
||||
|
||||
13
vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/informers.go
generated
vendored
13
vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/informers.go
generated
vendored
@@ -18,6 +18,7 @@ package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
@@ -186,10 +187,14 @@ type Informers struct {
|
||||
// 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() {
|
||||
if err := func() error {
|
||||
ip.mu.Lock()
|
||||
defer ip.mu.Unlock()
|
||||
|
||||
if ip.started {
|
||||
return errors.New("Informer already started") //nolint:stylecheck
|
||||
}
|
||||
|
||||
// Set the context so it can be passed to informers that are added later
|
||||
ip.ctx = ctx
|
||||
|
||||
@@ -207,7 +212,11 @@ func (ip *Informers) Start(ctx context.Context) error {
|
||||
// Set started to true so we immediately start any informers added later.
|
||||
ip.started = true
|
||||
close(ip.startWait)
|
||||
}()
|
||||
|
||||
return nil
|
||||
}(); err != nil {
|
||||
return err
|
||||
}
|
||||
<-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
|
||||
|
||||
14
vendor/sigs.k8s.io/controller-runtime/pkg/cache/multi_namespace_cache.go
generated
vendored
14
vendor/sigs.k8s.io/controller-runtime/pkg/cache/multi_namespace_cache.go
generated
vendored
@@ -163,12 +163,13 @@ func (c *multiNamespaceCache) GetInformerForKind(ctx context.Context, gvk schema
|
||||
}
|
||||
|
||||
func (c *multiNamespaceCache) Start(ctx context.Context) error {
|
||||
errs := make(chan error)
|
||||
// start global cache
|
||||
if c.clusterCache != nil {
|
||||
go func() {
|
||||
err := c.clusterCache.Start(ctx)
|
||||
if err != nil {
|
||||
log.Error(err, "cluster scoped cache failed to start")
|
||||
errs <- fmt.Errorf("failed to start cluster-scoped cache: %w", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
@@ -177,13 +178,16 @@ func (c *multiNamespaceCache) Start(ctx context.Context) error {
|
||||
for ns, cache := range c.namespaceToCache {
|
||||
go func(ns string, cache Cache) {
|
||||
if err := cache.Start(ctx); err != nil {
|
||||
log.Error(err, "multi-namespace cache failed to start namespaced informer", "namespace", ns)
|
||||
errs <- fmt.Errorf("failed to start cache for namespace %s: %w", ns, err)
|
||||
}
|
||||
}(ns, cache)
|
||||
}
|
||||
|
||||
<-ctx.Done()
|
||||
return nil
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
case err := <-errs:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (c *multiNamespaceCache) WaitForCacheSync(ctx context.Context) bool {
|
||||
|
||||
10
vendor/sigs.k8s.io/controller-runtime/pkg/certwatcher/certwatcher.go
generated
vendored
10
vendor/sigs.k8s.io/controller-runtime/pkg/certwatcher/certwatcher.go
generated
vendored
@@ -173,14 +173,14 @@ func (cw *CertWatcher) ReadCertificate() error {
|
||||
|
||||
func (cw *CertWatcher) handleEvent(event fsnotify.Event) {
|
||||
// Only care about events which may modify the contents of the file.
|
||||
if !(isWrite(event) || isRemove(event) || isCreate(event)) {
|
||||
if !(isWrite(event) || isRemove(event) || isCreate(event) || isChmod(event)) {
|
||||
return
|
||||
}
|
||||
|
||||
log.V(1).Info("certificate event", "event", event)
|
||||
|
||||
// If the file was removed, re-add the watch.
|
||||
if isRemove(event) {
|
||||
// If the file was removed or renamed, re-add the watch to the previous name
|
||||
if isRemove(event) || isChmod(event) {
|
||||
if err := cw.watcher.Add(event.Name); err != nil {
|
||||
log.Error(err, "error re-watching file")
|
||||
}
|
||||
@@ -202,3 +202,7 @@ func isCreate(event fsnotify.Event) bool {
|
||||
func isRemove(event fsnotify.Event) bool {
|
||||
return event.Op.Has(fsnotify.Remove)
|
||||
}
|
||||
|
||||
func isChmod(event fsnotify.Event) bool {
|
||||
return event.Op.Has(fsnotify.Chmod)
|
||||
}
|
||||
|
||||
5
vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/apimachinery.go
generated
vendored
5
vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/apimachinery.go
generated
vendored
@@ -72,7 +72,10 @@ func IsObjectNamespaced(obj runtime.Object, scheme *runtime.Scheme, restmapper m
|
||||
// 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})
|
||||
// Fetch the RESTMapping using the complete GVK. If we exclude the Version, the Version set
|
||||
// will be populated using the cached Group if available. This can lead to failures updating
|
||||
// the cache with new Versions of CRDs registered at runtime.
|
||||
restmapping, err := restmapper.RESTMapping(schema.GroupKind{Group: gvk.Group, Kind: gvk.Kind}, gvk.Version)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to get restmapping: %w", err)
|
||||
}
|
||||
|
||||
41
vendor/sigs.k8s.io/controller-runtime/pkg/client/client.go
generated
vendored
41
vendor/sigs.k8s.io/controller-runtime/pkg/client/client.go
generated
vendored
@@ -50,28 +50,10 @@ type Options struct {
|
||||
// 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.
|
||||
type WarningHandlerOptions struct {
|
||||
// SuppressWarnings decides if the warnings from the
|
||||
// API server are suppressed or surfaced in the client.
|
||||
SuppressWarnings bool
|
||||
// AllowDuplicateLogs does not deduplicate the to-be
|
||||
// logged surfaced warnings messages. See
|
||||
// log.WarningHandlerOptions for considerations
|
||||
// regarding deduplication
|
||||
AllowDuplicateLogs bool
|
||||
}
|
||||
|
||||
// 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.
|
||||
@@ -91,6 +73,12 @@ type NewClientFunc func(config *rest.Config, options Options) (Client, error)
|
||||
|
||||
// New returns a new Client using the provided config and Options.
|
||||
//
|
||||
// By default, the client surfaces warnings returned by the server. To
|
||||
// suppress warnings, set config.WarningHandler = rest.NoWarnings{}. To
|
||||
// define custom behavior, implement the rest.WarningHandler interface.
|
||||
// See [sigs.k8s.io/controller-runtime/pkg/log.KubeAPIWarningLogger] for
|
||||
// an example.
|
||||
//
|
||||
// The client's read behavior is determined by Options.Cache.
|
||||
// If either Options.Cache or Options.Cache.Reader is nil,
|
||||
// the client reads directly from the API server.
|
||||
@@ -124,17 +112,12 @@ func newClient(config *rest.Config, options Options) (*client, error) {
|
||||
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.
|
||||
if config.WarningHandler == nil {
|
||||
// By default, we de-duplicate and surface warnings.
|
||||
config.WarningHandler = log.NewKubeAPIWarningLogger(
|
||||
logger,
|
||||
log.Log.WithName("KubeAPIWarningLogger"),
|
||||
log.KubeAPIWarningLoggerOptions{
|
||||
Deduplicate: !options.WarningHandler.AllowDuplicateLogs,
|
||||
Deduplicate: true,
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -523,8 +506,8 @@ func (co *SubResourceCreateOptions) ApplyOptions(opts []SubResourceCreateOption)
|
||||
return co
|
||||
}
|
||||
|
||||
// ApplyToSubresourceCreate applies the the configuration on the given create options.
|
||||
func (co *SubResourceCreateOptions) ApplyToSubresourceCreate(o *SubResourceCreateOptions) {
|
||||
// ApplyToSubResourceCreate applies the the configuration on the given create options.
|
||||
func (co *SubResourceCreateOptions) ApplyToSubResourceCreate(o *SubResourceCreateOptions) {
|
||||
co.CreateOptions.ApplyToCreate(&co.CreateOptions)
|
||||
}
|
||||
|
||||
|
||||
329
vendor/sigs.k8s.io/controller-runtime/pkg/client/fake/client.go
generated
vendored
329
vendor/sigs.k8s.io/controller-runtime/pkg/client/fake/client.go
generated
vendored
@@ -30,7 +30,9 @@ import (
|
||||
"time"
|
||||
|
||||
// Using v4 to match upstream
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
jsonpatch "gopkg.in/evanphx/json-patch.v4"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
autoscalingv1 "k8s.io/api/autoscaling/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
policyv1 "k8s.io/api/policy/v1"
|
||||
policyv1beta1 "k8s.io/api/policy/v1beta1"
|
||||
@@ -50,6 +52,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/client-go/testing"
|
||||
"k8s.io/utils/ptr"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
|
||||
@@ -65,16 +68,21 @@ type versionedTracker struct {
|
||||
}
|
||||
|
||||
type fakeClient struct {
|
||||
tracker versionedTracker
|
||||
scheme *runtime.Scheme
|
||||
// trackerWriteLock must be acquired before writing to
|
||||
// the tracker or performing reads that affect a following
|
||||
// write.
|
||||
trackerWriteLock sync.Mutex
|
||||
tracker versionedTracker
|
||||
|
||||
schemeWriteLock sync.Mutex
|
||||
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.
|
||||
indexes map[schema.GroupVersionKind]map[string]client.IndexerFunc
|
||||
|
||||
schemeWriteLock sync.Mutex
|
||||
}
|
||||
|
||||
var _ client.WithWatch = &fakeClient{}
|
||||
@@ -83,6 +91,8 @@ const (
|
||||
maxNameLength = 63
|
||||
randomLength = 5
|
||||
maxGeneratedNameLength = maxNameLength - randomLength
|
||||
|
||||
subResourceScale = "scale"
|
||||
)
|
||||
|
||||
// NewFakeClient creates a new fake client for testing.
|
||||
@@ -301,7 +311,7 @@ func (t versionedTracker) Add(obj runtime.Object) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t versionedTracker) Create(gvr schema.GroupVersionResource, obj runtime.Object, ns string) error {
|
||||
func (t versionedTracker) Create(gvr schema.GroupVersionResource, obj runtime.Object, ns string, opts ...metav1.CreateOptions) error {
|
||||
accessor, err := meta.Accessor(obj)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get accessor for object: %w", err)
|
||||
@@ -320,7 +330,7 @@ func (t versionedTracker) Create(gvr schema.GroupVersionResource, obj runtime.Ob
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := t.ObjectTracker.Create(gvr, obj, ns); err != nil {
|
||||
if err := t.ObjectTracker.Create(gvr, obj, ns, opts...); err != nil {
|
||||
accessor.SetResourceVersion("")
|
||||
return err
|
||||
}
|
||||
@@ -359,24 +369,59 @@ func convertFromUnstructuredIfNecessary(s *runtime.Scheme, o runtime.Object) (ru
|
||||
return typed, nil
|
||||
}
|
||||
|
||||
func (t versionedTracker) Update(gvr schema.GroupVersionResource, obj runtime.Object, ns string) error {
|
||||
func (t versionedTracker) Update(gvr schema.GroupVersionResource, obj runtime.Object, ns string, opts ...metav1.UpdateOptions) error {
|
||||
updateOpts, err := getSingleOrZeroOptions(opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return t.update(gvr, obj, ns, false, false, updateOpts)
|
||||
}
|
||||
|
||||
func (t versionedTracker) update(gvr schema.GroupVersionResource, obj runtime.Object, ns string, isStatus, deleting bool, opts metav1.UpdateOptions) error {
|
||||
obj, err := t.updateObject(gvr, obj, ns, isStatus, deleting, opts.DryRun)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if obj == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return t.ObjectTracker.Update(gvr, obj, ns, opts)
|
||||
}
|
||||
|
||||
func (t versionedTracker) Patch(gvr schema.GroupVersionResource, obj runtime.Object, ns string, opts ...metav1.PatchOptions) error {
|
||||
patchOptions, err := getSingleOrZeroOptions(opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
isStatus := false
|
||||
// We apply patches using a client-go reaction that ends up calling the trackers Update. As we can't change
|
||||
// We apply patches using a client-go reaction that ends up calling the trackers Patch. 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).statusPatch")) {
|
||||
isStatus = true
|
||||
}
|
||||
return t.update(gvr, obj, ns, isStatus, false)
|
||||
|
||||
obj, err = t.updateObject(gvr, obj, ns, isStatus, false, patchOptions.DryRun)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if obj == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return t.ObjectTracker.Patch(gvr, obj, ns, patchOptions)
|
||||
}
|
||||
|
||||
func (t versionedTracker) update(gvr schema.GroupVersionResource, obj runtime.Object, ns string, isStatus bool, deleting bool) error {
|
||||
func (t versionedTracker) updateObject(gvr schema.GroupVersionResource, obj runtime.Object, ns string, isStatus, deleting bool, dryRun []string) (runtime.Object, error) {
|
||||
accessor, err := meta.Accessor(obj)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get accessor for object: %w", err)
|
||||
return nil, fmt.Errorf("failed to get accessor for object: %w", err)
|
||||
}
|
||||
|
||||
if accessor.GetName() == "" {
|
||||
return apierrors.NewInvalid(
|
||||
return nil, apierrors.NewInvalid(
|
||||
obj.GetObjectKind().GroupVersionKind().GroupKind(),
|
||||
accessor.GetName(),
|
||||
field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")})
|
||||
@@ -384,7 +429,7 @@ func (t versionedTracker) update(gvr schema.GroupVersionResource, obj runtime.Ob
|
||||
|
||||
gvk, err := apiutil.GVKForObject(obj, t.scheme)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
oldObject, err := t.ObjectTracker.Get(gvr, ns, accessor.GetName())
|
||||
@@ -392,65 +437,75 @@ func (t versionedTracker) update(gvr schema.GroupVersionResource, obj runtime.Ob
|
||||
// If the resource is not found and the resource allows create on update, issue a
|
||||
// create instead.
|
||||
if apierrors.IsNotFound(err) && allowsCreateOnUpdate(gvk) {
|
||||
return t.Create(gvr, obj, ns)
|
||||
return nil, t.Create(gvr, obj, ns)
|
||||
}
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if t.withStatusSubresource.Has(gvk) {
|
||||
if isStatus { // copy everything but status and metadata.ResourceVersion from original object
|
||||
if err := copyStatusFrom(obj, oldObject); err != nil {
|
||||
return fmt.Errorf("failed to copy non-status field for object with status subresouce: %w", err)
|
||||
return nil, fmt.Errorf("failed to copy non-status field for object with status subresouce: %w", err)
|
||||
}
|
||||
passedRV := accessor.GetResourceVersion()
|
||||
if err := copyFrom(oldObject, obj); err != nil {
|
||||
return fmt.Errorf("failed to restore non-status fields: %w", err)
|
||||
return nil, fmt.Errorf("failed to restore non-status fields: %w", err)
|
||||
}
|
||||
accessor.SetResourceVersion(passedRV)
|
||||
} 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)
|
||||
return nil, fmt.Errorf("failed to copy the status for object with status subresource: %w", err)
|
||||
}
|
||||
}
|
||||
} else if isStatus {
|
||||
return apierrors.NewNotFound(gvr.GroupResource(), accessor.GetName())
|
||||
return nil, apierrors.NewNotFound(gvr.GroupResource(), accessor.GetName())
|
||||
}
|
||||
|
||||
oldAccessor, err := meta.Accessor(oldObject)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If the new object does not have the resource version set and it allows unconditional update,
|
||||
// default it to the resource version of the existing resource
|
||||
if accessor.GetResourceVersion() == "" && allowsUnconditionalUpdate(gvk) {
|
||||
accessor.SetResourceVersion(oldAccessor.GetResourceVersion())
|
||||
if accessor.GetResourceVersion() == "" {
|
||||
switch {
|
||||
case allowsUnconditionalUpdate(gvk):
|
||||
accessor.SetResourceVersion(oldAccessor.GetResourceVersion())
|
||||
// This is needed because if the patch explicitly sets the RV to null, the client-go reaction we use
|
||||
// to apply it and whose output we process here will have it unset. It is not clear why the Kubernetes
|
||||
// apiserver accepts such a patch, but it does so we just copy that behavior.
|
||||
// Kubernetes apiserver behavior can be checked like this:
|
||||
// `kubectl patch configmap foo --patch '{"metadata":{"annotations":{"foo":"bar"},"resourceVersion":null}}' -v=9`
|
||||
case bytes.
|
||||
Contains(debug.Stack(), []byte("sigs.k8s.io/controller-runtime/pkg/client/fake.(*fakeClient).Patch")):
|
||||
// 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 "fakeClient.Patch" func.
|
||||
accessor.SetResourceVersion(oldAccessor.GetResourceVersion())
|
||||
}
|
||||
}
|
||||
|
||||
if accessor.GetResourceVersion() != oldAccessor.GetResourceVersion() {
|
||||
return apierrors.NewConflict(gvr.GroupResource(), accessor.GetName(), errors.New("object was modified"))
|
||||
return nil, apierrors.NewConflict(gvr.GroupResource(), accessor.GetName(), errors.New("object was modified"))
|
||||
}
|
||||
if oldAccessor.GetResourceVersion() == "" {
|
||||
oldAccessor.SetResourceVersion("0")
|
||||
}
|
||||
intResourceVersion, err := strconv.ParseUint(oldAccessor.GetResourceVersion(), 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("can not convert resourceVersion %q to int: %w", oldAccessor.GetResourceVersion(), err)
|
||||
return nil, fmt.Errorf("can not convert resourceVersion %q to int: %w", oldAccessor.GetResourceVersion(), err)
|
||||
}
|
||||
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())
|
||||
return nil, 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())
|
||||
return nil, t.ObjectTracker.Delete(gvr, accessor.GetNamespace(), accessor.GetName(), metav1.DeleteOptions{DryRun: dryRun})
|
||||
}
|
||||
obj, err = convertFromUnstructuredIfNecessary(t.scheme, obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return t.ObjectTracker.Update(gvr, obj, ns)
|
||||
return convertFromUnstructuredIfNecessary(t.scheme, obj)
|
||||
}
|
||||
|
||||
func (c *fakeClient) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error {
|
||||
@@ -463,7 +518,10 @@ func (c *fakeClient) Get(ctx context.Context, key client.ObjectKey, obj client.O
|
||||
return err
|
||||
}
|
||||
|
||||
if _, isUnstructured := obj.(runtime.Unstructured); isUnstructured {
|
||||
_, isUnstructured := obj.(runtime.Unstructured)
|
||||
_, isPartialObject := obj.(*metav1.PartialObjectMetadata)
|
||||
|
||||
if isUnstructured || isPartialObject {
|
||||
gvk, err := apiutil.GVKForObject(obj, c.scheme)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -684,6 +742,8 @@ func (c *fakeClient) Create(ctx context.Context, obj client.Object, opts ...clie
|
||||
accessor.SetDeletionTimestamp(nil)
|
||||
}
|
||||
|
||||
c.trackerWriteLock.Lock()
|
||||
defer c.trackerWriteLock.Unlock()
|
||||
return c.tracker.Create(gvr, obj, accessor.GetNamespace())
|
||||
}
|
||||
|
||||
@@ -705,6 +765,8 @@ func (c *fakeClient) Delete(ctx context.Context, obj client.Object, opts ...clie
|
||||
}
|
||||
}
|
||||
|
||||
c.trackerWriteLock.Lock()
|
||||
defer c.trackerWriteLock.Unlock()
|
||||
// Check the ResourceVersion if that Precondition was specified.
|
||||
if delOptions.Preconditions != nil && delOptions.Preconditions.ResourceVersion != nil {
|
||||
name := accessor.GetName()
|
||||
@@ -727,7 +789,7 @@ func (c *fakeClient) Delete(ctx context.Context, obj client.Object, opts ...clie
|
||||
}
|
||||
}
|
||||
|
||||
return c.deleteObject(gvr, accessor)
|
||||
return c.deleteObjectLocked(gvr, accessor)
|
||||
}
|
||||
|
||||
func (c *fakeClient) DeleteAllOf(ctx context.Context, obj client.Object, opts ...client.DeleteAllOfOption) error {
|
||||
@@ -745,6 +807,9 @@ func (c *fakeClient) DeleteAllOf(ctx context.Context, obj client.Object, opts ..
|
||||
}
|
||||
}
|
||||
|
||||
c.trackerWriteLock.Lock()
|
||||
defer c.trackerWriteLock.Unlock()
|
||||
|
||||
gvr, _ := meta.UnsafeGuessKindToResource(gvk)
|
||||
o, err := c.tracker.List(gvr, gvk, dcOptions.Namespace)
|
||||
if err != nil {
|
||||
@@ -764,7 +829,7 @@ func (c *fakeClient) DeleteAllOf(ctx context.Context, obj client.Object, opts ..
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = c.deleteObject(gvr, accessor)
|
||||
err = c.deleteObjectLocked(gvr, accessor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -794,7 +859,10 @@ func (c *fakeClient) update(obj client.Object, isStatus bool, opts ...client.Upd
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.tracker.update(gvr, obj, accessor.GetNamespace(), isStatus, false)
|
||||
|
||||
c.trackerWriteLock.Lock()
|
||||
defer c.trackerWriteLock.Unlock()
|
||||
return c.tracker.update(gvr, obj, accessor.GetNamespace(), isStatus, false, *updateOptions.AsUpdateOptions())
|
||||
}
|
||||
|
||||
func (c *fakeClient) Patch(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.PatchOption) error {
|
||||
@@ -829,6 +897,8 @@ func (c *fakeClient) patch(obj client.Object, patch client.Patch, opts ...client
|
||||
return err
|
||||
}
|
||||
|
||||
c.trackerWriteLock.Lock()
|
||||
defer c.trackerWriteLock.Unlock()
|
||||
oldObj, err := c.tracker.Get(gvr, accessor.GetNamespace(), accessor.GetName())
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -1037,7 +1107,7 @@ func (c *fakeClient) SubResource(subResource string) client.SubResourceClient {
|
||||
return &fakeSubResourceClient{client: c, subResource: subResource}
|
||||
}
|
||||
|
||||
func (c *fakeClient) deleteObject(gvr schema.GroupVersionResource, accessor metav1.Object) error {
|
||||
func (c *fakeClient) deleteObjectLocked(gvr schema.GroupVersionResource, accessor metav1.Object) error {
|
||||
old, err := c.tracker.Get(gvr, accessor.GetNamespace(), accessor.GetName())
|
||||
if err == nil {
|
||||
oldAccessor, err := meta.Accessor(old)
|
||||
@@ -1047,7 +1117,7 @@ func (c *fakeClient) deleteObject(gvr schema.GroupVersionResource, accessor meta
|
||||
oldAccessor.SetDeletionTimestamp(&now)
|
||||
// Call update directly with mutability parameter set to true to allow
|
||||
// changes to deletionTimestamp
|
||||
return c.tracker.update(gvr, old, accessor.GetNamespace(), false, true)
|
||||
return c.tracker.update(gvr, old, accessor.GetNamespace(), false, true, metav1.UpdateOptions{})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1071,7 +1141,26 @@ type fakeSubResourceClient struct {
|
||||
}
|
||||
|
||||
func (sw *fakeSubResourceClient) Get(ctx context.Context, obj, subResource client.Object, opts ...client.SubResourceGetOption) error {
|
||||
panic("fakeSubResourceClient does not support get")
|
||||
switch sw.subResource {
|
||||
case subResourceScale:
|
||||
// Actual client looks up resource, then extracts the scale sub-resource:
|
||||
// https://github.com/kubernetes/kubernetes/blob/fb6bbc9781d11a87688c398778525c4e1dcb0f08/pkg/registry/apps/deployment/storage/storage.go#L307
|
||||
if err := sw.client.Get(ctx, client.ObjectKeyFromObject(obj), obj); err != nil {
|
||||
return err
|
||||
}
|
||||
scale, isScale := subResource.(*autoscalingv1.Scale)
|
||||
if !isScale {
|
||||
return apierrors.NewBadRequest(fmt.Sprintf("expected Scale, got %t", subResource))
|
||||
}
|
||||
scaleOut, err := extractScale(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*scale = *scaleOut
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("fakeSubResourceClient does not support get for %s", sw.subResource)
|
||||
}
|
||||
}
|
||||
|
||||
func (sw *fakeSubResourceClient) Create(ctx context.Context, obj client.Object, subResource client.Object, opts ...client.SubResourceCreateOption) error {
|
||||
@@ -1098,11 +1187,30 @@ func (sw *fakeSubResourceClient) Update(ctx context.Context, obj client.Object,
|
||||
updateOptions := client.SubResourceUpdateOptions{}
|
||||
updateOptions.ApplyOptions(opts)
|
||||
|
||||
body := obj
|
||||
if updateOptions.SubResourceBody != nil {
|
||||
body = updateOptions.SubResourceBody
|
||||
switch sw.subResource {
|
||||
case subResourceScale:
|
||||
if err := sw.client.Get(ctx, client.ObjectKeyFromObject(obj), obj.DeepCopyObject().(client.Object)); err != nil {
|
||||
return err
|
||||
}
|
||||
if updateOptions.SubResourceBody == nil {
|
||||
return apierrors.NewBadRequest("missing SubResourceBody")
|
||||
}
|
||||
|
||||
scale, isScale := updateOptions.SubResourceBody.(*autoscalingv1.Scale)
|
||||
if !isScale {
|
||||
return apierrors.NewBadRequest(fmt.Sprintf("expected Scale, got %t", updateOptions.SubResourceBody))
|
||||
}
|
||||
if err := applyScale(obj, scale); err != nil {
|
||||
return err
|
||||
}
|
||||
return sw.client.update(obj, false, &updateOptions.UpdateOptions)
|
||||
default:
|
||||
body := obj
|
||||
if updateOptions.SubResourceBody != nil {
|
||||
body = updateOptions.SubResourceBody
|
||||
}
|
||||
return sw.client.update(body, true, &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 {
|
||||
@@ -1269,3 +1377,138 @@ func zero(x interface{}) {
|
||||
res := reflect.ValueOf(x).Elem()
|
||||
res.Set(reflect.Zero(res.Type()))
|
||||
}
|
||||
|
||||
// getSingleOrZeroOptions returns the single options value in the slice, its
|
||||
// zero value if the slice is empty, or an error if the slice contains more than
|
||||
// one option value.
|
||||
func getSingleOrZeroOptions[T any](opts []T) (opt T, err error) {
|
||||
switch len(opts) {
|
||||
case 0:
|
||||
case 1:
|
||||
opt = opts[0]
|
||||
default:
|
||||
err = fmt.Errorf("expected single or no options value, got %d values", len(opts))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func extractScale(obj client.Object) (*autoscalingv1.Scale, error) {
|
||||
switch obj := obj.(type) {
|
||||
case *appsv1.Deployment:
|
||||
var replicas int32 = 1
|
||||
if obj.Spec.Replicas != nil {
|
||||
replicas = *obj.Spec.Replicas
|
||||
}
|
||||
var selector string
|
||||
if obj.Spec.Selector != nil {
|
||||
selector = obj.Spec.Selector.String()
|
||||
}
|
||||
return &autoscalingv1.Scale{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: obj.Namespace,
|
||||
Name: obj.Name,
|
||||
UID: obj.UID,
|
||||
ResourceVersion: obj.ResourceVersion,
|
||||
CreationTimestamp: obj.CreationTimestamp,
|
||||
},
|
||||
Spec: autoscalingv1.ScaleSpec{
|
||||
Replicas: replicas,
|
||||
},
|
||||
Status: autoscalingv1.ScaleStatus{
|
||||
Replicas: obj.Status.Replicas,
|
||||
Selector: selector,
|
||||
},
|
||||
}, nil
|
||||
case *appsv1.ReplicaSet:
|
||||
var replicas int32 = 1
|
||||
if obj.Spec.Replicas != nil {
|
||||
replicas = *obj.Spec.Replicas
|
||||
}
|
||||
var selector string
|
||||
if obj.Spec.Selector != nil {
|
||||
selector = obj.Spec.Selector.String()
|
||||
}
|
||||
return &autoscalingv1.Scale{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: obj.Namespace,
|
||||
Name: obj.Name,
|
||||
UID: obj.UID,
|
||||
ResourceVersion: obj.ResourceVersion,
|
||||
CreationTimestamp: obj.CreationTimestamp,
|
||||
},
|
||||
Spec: autoscalingv1.ScaleSpec{
|
||||
Replicas: replicas,
|
||||
},
|
||||
Status: autoscalingv1.ScaleStatus{
|
||||
Replicas: obj.Status.Replicas,
|
||||
Selector: selector,
|
||||
},
|
||||
}, nil
|
||||
case *corev1.ReplicationController:
|
||||
var replicas int32 = 1
|
||||
if obj.Spec.Replicas != nil {
|
||||
replicas = *obj.Spec.Replicas
|
||||
}
|
||||
return &autoscalingv1.Scale{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: obj.Namespace,
|
||||
Name: obj.Name,
|
||||
UID: obj.UID,
|
||||
ResourceVersion: obj.ResourceVersion,
|
||||
CreationTimestamp: obj.CreationTimestamp,
|
||||
},
|
||||
Spec: autoscalingv1.ScaleSpec{
|
||||
Replicas: replicas,
|
||||
},
|
||||
Status: autoscalingv1.ScaleStatus{
|
||||
Replicas: obj.Status.Replicas,
|
||||
Selector: labels.Set(obj.Spec.Selector).String(),
|
||||
},
|
||||
}, nil
|
||||
case *appsv1.StatefulSet:
|
||||
var replicas int32 = 1
|
||||
if obj.Spec.Replicas != nil {
|
||||
replicas = *obj.Spec.Replicas
|
||||
}
|
||||
var selector string
|
||||
if obj.Spec.Selector != nil {
|
||||
selector = obj.Spec.Selector.String()
|
||||
}
|
||||
return &autoscalingv1.Scale{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: obj.Namespace,
|
||||
Name: obj.Name,
|
||||
UID: obj.UID,
|
||||
ResourceVersion: obj.ResourceVersion,
|
||||
CreationTimestamp: obj.CreationTimestamp,
|
||||
},
|
||||
Spec: autoscalingv1.ScaleSpec{
|
||||
Replicas: replicas,
|
||||
},
|
||||
Status: autoscalingv1.ScaleStatus{
|
||||
Replicas: obj.Status.Replicas,
|
||||
Selector: selector,
|
||||
},
|
||||
}, nil
|
||||
default:
|
||||
// TODO: CRDs https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#scale-subresource
|
||||
return nil, fmt.Errorf("unimplemented scale subresource for resource %T", obj)
|
||||
}
|
||||
}
|
||||
|
||||
func applyScale(obj client.Object, scale *autoscalingv1.Scale) error {
|
||||
switch obj := obj.(type) {
|
||||
case *appsv1.Deployment:
|
||||
obj.Spec.Replicas = ptr.To(scale.Spec.Replicas)
|
||||
case *appsv1.ReplicaSet:
|
||||
obj.Spec.Replicas = ptr.To(scale.Spec.Replicas)
|
||||
case *corev1.ReplicationController:
|
||||
obj.Spec.Replicas = ptr.To(scale.Spec.Replicas)
|
||||
case *appsv1.StatefulSet:
|
||||
obj.Spec.Replicas = ptr.To(scale.Spec.Replicas)
|
||||
default:
|
||||
// TODO: CRDs https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#scale-subresource
|
||||
return fmt.Errorf("unimplemented scale subresource for resource %T", obj)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
106
vendor/sigs.k8s.io/controller-runtime/pkg/client/fieldowner.go
generated
vendored
Normal file
106
vendor/sigs.k8s.io/controller-runtime/pkg/client/fieldowner.go
generated
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
Copyright 2024 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"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
// WithFieldOwner wraps a Client and adds the fieldOwner as the field
|
||||
// manager to all write requests from this client. If additional [FieldOwner]
|
||||
// options are specified on methods of this client, the value specified here
|
||||
// will be overridden.
|
||||
func WithFieldOwner(c Client, fieldOwner string) Client {
|
||||
return &clientWithFieldManager{
|
||||
owner: fieldOwner,
|
||||
c: c,
|
||||
Reader: c,
|
||||
}
|
||||
}
|
||||
|
||||
type clientWithFieldManager struct {
|
||||
owner string
|
||||
c Client
|
||||
Reader
|
||||
}
|
||||
|
||||
func (f *clientWithFieldManager) Create(ctx context.Context, obj Object, opts ...CreateOption) error {
|
||||
return f.c.Create(ctx, obj, append([]CreateOption{FieldOwner(f.owner)}, opts...)...)
|
||||
}
|
||||
|
||||
func (f *clientWithFieldManager) Update(ctx context.Context, obj Object, opts ...UpdateOption) error {
|
||||
return f.c.Update(ctx, obj, append([]UpdateOption{FieldOwner(f.owner)}, opts...)...)
|
||||
}
|
||||
|
||||
func (f *clientWithFieldManager) Patch(ctx context.Context, obj Object, patch Patch, opts ...PatchOption) error {
|
||||
return f.c.Patch(ctx, obj, patch, append([]PatchOption{FieldOwner(f.owner)}, opts...)...)
|
||||
}
|
||||
|
||||
func (f *clientWithFieldManager) Delete(ctx context.Context, obj Object, opts ...DeleteOption) error {
|
||||
return f.c.Delete(ctx, obj, opts...)
|
||||
}
|
||||
|
||||
func (f *clientWithFieldManager) DeleteAllOf(ctx context.Context, obj Object, opts ...DeleteAllOfOption) error {
|
||||
return f.c.DeleteAllOf(ctx, obj, opts...)
|
||||
}
|
||||
|
||||
func (f *clientWithFieldManager) Scheme() *runtime.Scheme { return f.c.Scheme() }
|
||||
func (f *clientWithFieldManager) RESTMapper() meta.RESTMapper { return f.c.RESTMapper() }
|
||||
func (f *clientWithFieldManager) GroupVersionKindFor(obj runtime.Object) (schema.GroupVersionKind, error) {
|
||||
return f.c.GroupVersionKindFor(obj)
|
||||
}
|
||||
func (f *clientWithFieldManager) IsObjectNamespaced(obj runtime.Object) (bool, error) {
|
||||
return f.c.IsObjectNamespaced(obj)
|
||||
}
|
||||
|
||||
func (f *clientWithFieldManager) Status() StatusWriter {
|
||||
return &subresourceClientWithFieldOwner{
|
||||
owner: f.owner,
|
||||
subresourceWriter: f.c.Status(),
|
||||
}
|
||||
}
|
||||
|
||||
func (f *clientWithFieldManager) SubResource(subresource string) SubResourceClient {
|
||||
c := f.c.SubResource(subresource)
|
||||
return &subresourceClientWithFieldOwner{
|
||||
owner: f.owner,
|
||||
subresourceWriter: c,
|
||||
SubResourceReader: c,
|
||||
}
|
||||
}
|
||||
|
||||
type subresourceClientWithFieldOwner struct {
|
||||
owner string
|
||||
subresourceWriter SubResourceWriter
|
||||
SubResourceReader
|
||||
}
|
||||
|
||||
func (f *subresourceClientWithFieldOwner) Create(ctx context.Context, obj Object, subresource Object, opts ...SubResourceCreateOption) error {
|
||||
return f.subresourceWriter.Create(ctx, obj, subresource, append([]SubResourceCreateOption{FieldOwner(f.owner)}, opts...)...)
|
||||
}
|
||||
|
||||
func (f *subresourceClientWithFieldOwner) Update(ctx context.Context, obj Object, opts ...SubResourceUpdateOption) error {
|
||||
return f.subresourceWriter.Update(ctx, obj, append([]SubResourceUpdateOption{FieldOwner(f.owner)}, opts...)...)
|
||||
}
|
||||
|
||||
func (f *subresourceClientWithFieldOwner) Patch(ctx context.Context, obj Object, patch Patch, opts ...SubResourcePatchOption) error {
|
||||
return f.subresourceWriter.Patch(ctx, obj, patch, append([]SubResourcePatchOption{FieldOwner(f.owner)}, opts...)...)
|
||||
}
|
||||
106
vendor/sigs.k8s.io/controller-runtime/pkg/client/fieldvalidation.go
generated
vendored
Normal file
106
vendor/sigs.k8s.io/controller-runtime/pkg/client/fieldvalidation.go
generated
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
Copyright 2024 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"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
// WithFieldValidation wraps a Client and configures field validation, by
|
||||
// default, for all write requests from this client. Users can override field
|
||||
// validation for individual write requests.
|
||||
func WithFieldValidation(c Client, validation FieldValidation) Client {
|
||||
return &clientWithFieldValidation{
|
||||
validation: validation,
|
||||
client: c,
|
||||
Reader: c,
|
||||
}
|
||||
}
|
||||
|
||||
type clientWithFieldValidation struct {
|
||||
validation FieldValidation
|
||||
client Client
|
||||
Reader
|
||||
}
|
||||
|
||||
func (c *clientWithFieldValidation) Create(ctx context.Context, obj Object, opts ...CreateOption) error {
|
||||
return c.client.Create(ctx, obj, append([]CreateOption{c.validation}, opts...)...)
|
||||
}
|
||||
|
||||
func (c *clientWithFieldValidation) Update(ctx context.Context, obj Object, opts ...UpdateOption) error {
|
||||
return c.client.Update(ctx, obj, append([]UpdateOption{c.validation}, opts...)...)
|
||||
}
|
||||
|
||||
func (c *clientWithFieldValidation) Patch(ctx context.Context, obj Object, patch Patch, opts ...PatchOption) error {
|
||||
return c.client.Patch(ctx, obj, patch, append([]PatchOption{c.validation}, opts...)...)
|
||||
}
|
||||
|
||||
func (c *clientWithFieldValidation) Delete(ctx context.Context, obj Object, opts ...DeleteOption) error {
|
||||
return c.client.Delete(ctx, obj, opts...)
|
||||
}
|
||||
|
||||
func (c *clientWithFieldValidation) DeleteAllOf(ctx context.Context, obj Object, opts ...DeleteAllOfOption) error {
|
||||
return c.client.DeleteAllOf(ctx, obj, opts...)
|
||||
}
|
||||
|
||||
func (c *clientWithFieldValidation) Scheme() *runtime.Scheme { return c.client.Scheme() }
|
||||
func (c *clientWithFieldValidation) RESTMapper() meta.RESTMapper { return c.client.RESTMapper() }
|
||||
func (c *clientWithFieldValidation) GroupVersionKindFor(obj runtime.Object) (schema.GroupVersionKind, error) {
|
||||
return c.client.GroupVersionKindFor(obj)
|
||||
}
|
||||
|
||||
func (c *clientWithFieldValidation) IsObjectNamespaced(obj runtime.Object) (bool, error) {
|
||||
return c.client.IsObjectNamespaced(obj)
|
||||
}
|
||||
|
||||
func (c *clientWithFieldValidation) Status() StatusWriter {
|
||||
return &subresourceClientWithFieldValidation{
|
||||
validation: c.validation,
|
||||
subresourceWriter: c.client.Status(),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *clientWithFieldValidation) SubResource(subresource string) SubResourceClient {
|
||||
srClient := c.client.SubResource(subresource)
|
||||
return &subresourceClientWithFieldValidation{
|
||||
validation: c.validation,
|
||||
subresourceWriter: srClient,
|
||||
SubResourceReader: srClient,
|
||||
}
|
||||
}
|
||||
|
||||
type subresourceClientWithFieldValidation struct {
|
||||
validation FieldValidation
|
||||
subresourceWriter SubResourceWriter
|
||||
SubResourceReader
|
||||
}
|
||||
|
||||
func (c *subresourceClientWithFieldValidation) Create(ctx context.Context, obj Object, subresource Object, opts ...SubResourceCreateOption) error {
|
||||
return c.subresourceWriter.Create(ctx, obj, subresource, append([]SubResourceCreateOption{c.validation}, opts...)...)
|
||||
}
|
||||
|
||||
func (c *subresourceClientWithFieldValidation) Update(ctx context.Context, obj Object, opts ...SubResourceUpdateOption) error {
|
||||
return c.subresourceWriter.Update(ctx, obj, append([]SubResourceUpdateOption{c.validation}, opts...)...)
|
||||
}
|
||||
|
||||
func (c *subresourceClientWithFieldValidation) Patch(ctx context.Context, obj Object, patch Patch, opts ...SubResourcePatchOption) error {
|
||||
return c.subresourceWriter.Patch(ctx, obj, patch, append([]SubResourcePatchOption{c.validation}, opts...)...)
|
||||
}
|
||||
99
vendor/sigs.k8s.io/controller-runtime/pkg/client/options.go
generated
vendored
99
vendor/sigs.k8s.io/controller-runtime/pkg/client/options.go
generated
vendored
@@ -169,6 +169,39 @@ func (f FieldOwner) ApplyToSubResourceUpdate(opts *SubResourceUpdateOptions) {
|
||||
opts.FieldManager = string(f)
|
||||
}
|
||||
|
||||
// FieldValidation configures field validation for the given requests.
|
||||
type FieldValidation string
|
||||
|
||||
// ApplyToPatch applies this configuration to the given patch options.
|
||||
func (f FieldValidation) ApplyToPatch(opts *PatchOptions) {
|
||||
opts.FieldValidation = string(f)
|
||||
}
|
||||
|
||||
// ApplyToCreate applies this configuration to the given create options.
|
||||
func (f FieldValidation) ApplyToCreate(opts *CreateOptions) {
|
||||
opts.FieldValidation = string(f)
|
||||
}
|
||||
|
||||
// ApplyToUpdate applies this configuration to the given update options.
|
||||
func (f FieldValidation) ApplyToUpdate(opts *UpdateOptions) {
|
||||
opts.FieldValidation = string(f)
|
||||
}
|
||||
|
||||
// ApplyToSubResourcePatch applies this configuration to the given patch options.
|
||||
func (f FieldValidation) ApplyToSubResourcePatch(opts *SubResourcePatchOptions) {
|
||||
opts.FieldValidation = string(f)
|
||||
}
|
||||
|
||||
// ApplyToSubResourceCreate applies this configuration to the given create options.
|
||||
func (f FieldValidation) ApplyToSubResourceCreate(opts *SubResourceCreateOptions) {
|
||||
opts.FieldValidation = string(f)
|
||||
}
|
||||
|
||||
// ApplyToSubResourceUpdate applies this configuration to the given update options.
|
||||
func (f FieldValidation) ApplyToSubResourceUpdate(opts *SubResourceUpdateOptions) {
|
||||
opts.FieldValidation = string(f)
|
||||
}
|
||||
|
||||
// }}}
|
||||
|
||||
// {{{ Create Options
|
||||
@@ -187,6 +220,24 @@ type CreateOptions struct {
|
||||
// this request. It must be set with server-side apply.
|
||||
FieldManager string
|
||||
|
||||
// fieldValidation instructs the server on how to handle
|
||||
// objects in the request (POST/PUT/PATCH) containing unknown
|
||||
// or duplicate fields. Valid values are:
|
||||
// - Ignore: This will ignore any unknown fields that are silently
|
||||
// dropped from the object, and will ignore all but the last duplicate
|
||||
// field that the decoder encounters. This is the default behavior
|
||||
// prior to v1.23.
|
||||
// - Warn: This will send a warning via the standard warning response
|
||||
// header for each unknown field that is dropped from the object, and
|
||||
// for each duplicate field that is encountered. The request will
|
||||
// still succeed if there are no other errors, and will only persist
|
||||
// the last of any duplicate fields. This is the default in v1.23+
|
||||
// - Strict: This will fail the request with a BadRequest error if
|
||||
// any unknown fields would be dropped from the object, or if any
|
||||
// duplicate fields are present. The error returned from the server
|
||||
// will contain all unknown and duplicate fields encountered.
|
||||
FieldValidation string
|
||||
|
||||
// Raw represents raw CreateOptions, as passed to the API server.
|
||||
Raw *metav1.CreateOptions
|
||||
}
|
||||
@@ -203,6 +254,7 @@ func (o *CreateOptions) AsCreateOptions() *metav1.CreateOptions {
|
||||
|
||||
o.Raw.DryRun = o.DryRun
|
||||
o.Raw.FieldManager = o.FieldManager
|
||||
o.Raw.FieldValidation = o.FieldValidation
|
||||
return o.Raw
|
||||
}
|
||||
|
||||
@@ -223,6 +275,9 @@ func (o *CreateOptions) ApplyToCreate(co *CreateOptions) {
|
||||
if o.FieldManager != "" {
|
||||
co.FieldManager = o.FieldManager
|
||||
}
|
||||
if o.FieldValidation != "" {
|
||||
co.FieldValidation = o.FieldValidation
|
||||
}
|
||||
if o.Raw != nil {
|
||||
co.Raw = o.Raw
|
||||
}
|
||||
@@ -679,6 +734,24 @@ type UpdateOptions struct {
|
||||
// this request. It must be set with server-side apply.
|
||||
FieldManager string
|
||||
|
||||
// fieldValidation instructs the server on how to handle
|
||||
// objects in the request (POST/PUT/PATCH) containing unknown
|
||||
// or duplicate fields. Valid values are:
|
||||
// - Ignore: This will ignore any unknown fields that are silently
|
||||
// dropped from the object, and will ignore all but the last duplicate
|
||||
// field that the decoder encounters. This is the default behavior
|
||||
// prior to v1.23.
|
||||
// - Warn: This will send a warning via the standard warning response
|
||||
// header for each unknown field that is dropped from the object, and
|
||||
// for each duplicate field that is encountered. The request will
|
||||
// still succeed if there are no other errors, and will only persist
|
||||
// the last of any duplicate fields. This is the default in v1.23+
|
||||
// - Strict: This will fail the request with a BadRequest error if
|
||||
// any unknown fields would be dropped from the object, or if any
|
||||
// duplicate fields are present. The error returned from the server
|
||||
// will contain all unknown and duplicate fields encountered.
|
||||
FieldValidation string
|
||||
|
||||
// Raw represents raw UpdateOptions, as passed to the API server.
|
||||
Raw *metav1.UpdateOptions
|
||||
}
|
||||
@@ -695,6 +768,7 @@ func (o *UpdateOptions) AsUpdateOptions() *metav1.UpdateOptions {
|
||||
|
||||
o.Raw.DryRun = o.DryRun
|
||||
o.Raw.FieldManager = o.FieldManager
|
||||
o.Raw.FieldValidation = o.FieldValidation
|
||||
return o.Raw
|
||||
}
|
||||
|
||||
@@ -717,6 +791,9 @@ func (o *UpdateOptions) ApplyToUpdate(uo *UpdateOptions) {
|
||||
if o.FieldManager != "" {
|
||||
uo.FieldManager = o.FieldManager
|
||||
}
|
||||
if o.FieldValidation != "" {
|
||||
uo.FieldValidation = o.FieldValidation
|
||||
}
|
||||
if o.Raw != nil {
|
||||
uo.Raw = o.Raw
|
||||
}
|
||||
@@ -745,6 +822,24 @@ type PatchOptions struct {
|
||||
// this request. It must be set with server-side apply.
|
||||
FieldManager string
|
||||
|
||||
// fieldValidation instructs the server on how to handle
|
||||
// objects in the request (POST/PUT/PATCH) containing unknown
|
||||
// or duplicate fields. Valid values are:
|
||||
// - Ignore: This will ignore any unknown fields that are silently
|
||||
// dropped from the object, and will ignore all but the last duplicate
|
||||
// field that the decoder encounters. This is the default behavior
|
||||
// prior to v1.23.
|
||||
// - Warn: This will send a warning via the standard warning response
|
||||
// header for each unknown field that is dropped from the object, and
|
||||
// for each duplicate field that is encountered. The request will
|
||||
// still succeed if there are no other errors, and will only persist
|
||||
// the last of any duplicate fields. This is the default in v1.23+
|
||||
// - Strict: This will fail the request with a BadRequest error if
|
||||
// any unknown fields would be dropped from the object, or if any
|
||||
// duplicate fields are present. The error returned from the server
|
||||
// will contain all unknown and duplicate fields encountered.
|
||||
FieldValidation string
|
||||
|
||||
// Raw represents raw PatchOptions, as passed to the API server.
|
||||
Raw *metav1.PatchOptions
|
||||
}
|
||||
@@ -771,6 +866,7 @@ func (o *PatchOptions) AsPatchOptions() *metav1.PatchOptions {
|
||||
o.Raw.DryRun = o.DryRun
|
||||
o.Raw.Force = o.Force
|
||||
o.Raw.FieldManager = o.FieldManager
|
||||
o.Raw.FieldValidation = o.FieldValidation
|
||||
return o.Raw
|
||||
}
|
||||
|
||||
@@ -787,6 +883,9 @@ func (o *PatchOptions) ApplyToPatch(po *PatchOptions) {
|
||||
if o.FieldManager != "" {
|
||||
po.FieldManager = o.FieldManager
|
||||
}
|
||||
if o.FieldValidation != "" {
|
||||
po.FieldValidation = o.FieldValidation
|
||||
}
|
||||
if o.Raw != nil {
|
||||
po.Raw = o.Raw
|
||||
}
|
||||
|
||||
112
vendor/sigs.k8s.io/controller-runtime/pkg/config/config.go
generated
vendored
112
vendor/sigs.k8s.io/controller-runtime/pkg/config/config.go
generated
vendored
@@ -1,112 +0,0 @@
|
||||
/*
|
||||
Copyright 2020 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/config/v1alpha1"
|
||||
)
|
||||
|
||||
// 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
|
||||
|
||||
// Complete returns the versioned configuration
|
||||
Complete() (v1alpha1.ControllerManagerConfigurationSpec, error)
|
||||
}
|
||||
|
||||
// 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
|
||||
scheme *runtime.Scheme
|
||||
once sync.Once
|
||||
err error
|
||||
}
|
||||
|
||||
// File will set up the deferred file loader for the configuration
|
||||
// this will also configure the defaults for the loader if nothing is
|
||||
//
|
||||
// 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))
|
||||
return &DeferredFileLoader{
|
||||
path: "./config.yaml",
|
||||
ControllerManagerConfiguration: &v1alpha1.ControllerManagerConfiguration{},
|
||||
scheme: scheme,
|
||||
}
|
||||
}
|
||||
|
||||
// Complete will use sync.Once to set the scheme.
|
||||
func (d *DeferredFileLoader) Complete() (v1alpha1.ControllerManagerConfigurationSpec, error) {
|
||||
d.once.Do(d.loadFile)
|
||||
if d.err != nil {
|
||||
return v1alpha1.ControllerManagerConfigurationSpec{}, d.err
|
||||
}
|
||||
return d.ControllerManagerConfiguration.Complete()
|
||||
}
|
||||
|
||||
// AtPath will set the path to load the file for the decoder.
|
||||
func (d *DeferredFileLoader) AtPath(path string) *DeferredFileLoader {
|
||||
d.path = path
|
||||
return d
|
||||
}
|
||||
|
||||
// OfKind will set the type to be used for decoding the file into.
|
||||
func (d *DeferredFileLoader) OfKind(obj ControllerManagerConfiguration) *DeferredFileLoader {
|
||||
d.ControllerManagerConfiguration = obj
|
||||
return d
|
||||
}
|
||||
|
||||
// loadFile is used from the mutex.Once to load the file.
|
||||
func (d *DeferredFileLoader) loadFile() {
|
||||
if d.scheme == nil {
|
||||
d.err = fmt.Errorf("scheme not supplied to controller configuration loader")
|
||||
return
|
||||
}
|
||||
|
||||
content, err := os.ReadFile(d.path)
|
||||
if err != nil {
|
||||
d.err = fmt.Errorf("could not read file at %s", d.path)
|
||||
return
|
||||
}
|
||||
|
||||
codecs := serializer.NewCodecFactory(d.scheme)
|
||||
|
||||
// Regardless of if the bytes are of any external version,
|
||||
// it will be read successfully and converted into the internal version
|
||||
if err = runtime.DecodeInto(codecs.UniversalDecoder(), content, d.ControllerManagerConfiguration); err != nil {
|
||||
d.err = fmt.Errorf("could not decode file into runtime.Object")
|
||||
}
|
||||
}
|
||||
9
vendor/sigs.k8s.io/controller-runtime/pkg/config/controller.go
generated
vendored
9
vendor/sigs.k8s.io/controller-runtime/pkg/config/controller.go
generated
vendored
@@ -20,6 +20,12 @@ import "time"
|
||||
|
||||
// Controller contains configuration options for a controller.
|
||||
type Controller struct {
|
||||
// SkipNameValidation allows skipping the name validation that ensures that every controller name is unique.
|
||||
// Unique controller names are important to get unique metrics and logs for a controller.
|
||||
// Can be overwritten for a controller via the SkipNameValidation setting on the controller.
|
||||
// Defaults to false if SkipNameValidation setting on controller and Manager are unset.
|
||||
SkipNameValidation *bool
|
||||
|
||||
// GroupKindConcurrency is a map from a Kind to the number of concurrent reconciliation
|
||||
// allowed for that controller.
|
||||
//
|
||||
@@ -40,7 +46,8 @@ type Controller struct {
|
||||
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.
|
||||
// Can be overwritten for a controller via the RecoverPanic setting on the controller.
|
||||
// Defaults to true if RecoverPanic setting on controller and Manager are unset.
|
||||
RecoverPanic *bool
|
||||
|
||||
// NeedLeaderElection indicates whether the controller needs to use leader election.
|
||||
|
||||
19
vendor/sigs.k8s.io/controller-runtime/pkg/config/doc.go
generated
vendored
19
vendor/sigs.k8s.io/controller-runtime/pkg/config/doc.go
generated
vendored
@@ -1,19 +0,0 @@
|
||||
/*
|
||||
Copyright 2020 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package config contains functionality for interacting with
|
||||
// configuration for controller-runtime components.
|
||||
package config
|
||||
22
vendor/sigs.k8s.io/controller-runtime/pkg/config/v1alpha1/doc.go
generated
vendored
22
vendor/sigs.k8s.io/controller-runtime/pkg/config/v1alpha1/doc.go
generated
vendored
@@ -1,22 +0,0 @@
|
||||
/*
|
||||
Copyright 2020 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package 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
|
||||
43
vendor/sigs.k8s.io/controller-runtime/pkg/config/v1alpha1/register.go
generated
vendored
43
vendor/sigs.k8s.io/controller-runtime/pkg/config/v1alpha1/register.go
generated
vendored
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
Copyright 2020 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"sigs.k8s.io/controller-runtime/pkg/scheme"
|
||||
)
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(&ControllerManagerConfiguration{})
|
||||
}
|
||||
179
vendor/sigs.k8s.io/controller-runtime/pkg/config/v1alpha1/types.go
generated
vendored
179
vendor/sigs.k8s.io/controller-runtime/pkg/config/v1alpha1/types.go
generated
vendored
@@ -1,179 +0,0 @@
|
||||
/*
|
||||
Copyright 2020 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
configv1alpha1 "k8s.io/component-base/config/v1alpha1"
|
||||
)
|
||||
|
||||
// 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
|
||||
// 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.
|
||||
// +optional
|
||||
SyncPeriod *metav1.Duration `json:"syncPeriod,omitempty"`
|
||||
|
||||
// LeaderElection is the LeaderElection config to be used when configuring
|
||||
// the manager.Manager leader election
|
||||
// +optional
|
||||
LeaderElection *configv1alpha1.LeaderElectionConfiguration `json:"leaderElection,omitempty"`
|
||||
|
||||
// CacheNamespace if specified restricts the manager's cache to watch objects in
|
||||
// the desired namespace Defaults to all namespaces
|
||||
//
|
||||
// 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.
|
||||
// +optional
|
||||
CacheNamespace string `json:"cacheNamespace,omitempty"`
|
||||
|
||||
// GracefulShutdownTimeout is the duration given to runnable to stop before the manager actually returns on stop.
|
||||
// To disable graceful shutdown, set to time.Duration(0)
|
||||
// To use graceful shutdown without timeout, set to a negative duration, e.G. time.Duration(-1)
|
||||
// The graceful shutdown is skipped for safety reasons in case the leader election lease is lost.
|
||||
GracefulShutdownTimeout *metav1.Duration `json:"gracefulShutDown,omitempty"`
|
||||
|
||||
// Controller contains global configuration options for controllers
|
||||
// registered within this manager.
|
||||
// +optional
|
||||
Controller *ControllerConfigurationSpec `json:"controller,omitempty"`
|
||||
|
||||
// Metrics contains the controller metrics configuration
|
||||
// +optional
|
||||
Metrics ControllerMetrics `json:"metrics,omitempty"`
|
||||
|
||||
// Health contains the controller health configuration
|
||||
// +optional
|
||||
Health ControllerHealth `json:"health,omitempty"`
|
||||
|
||||
// Webhook contains the controllers webhook configuration
|
||||
// +optional
|
||||
Webhook ControllerWebhook `json:"webhook,omitempty"`
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// 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`.
|
||||
//
|
||||
// +optional
|
||||
GroupKindConcurrency map[string]int `json:"groupKindConcurrency,omitempty"`
|
||||
|
||||
// CacheSyncTimeout refers to the time limit set to wait for syncing caches.
|
||||
// Defaults to 2 minutes if not set.
|
||||
// +optional
|
||||
CacheSyncTimeout *time.Duration `json:"cacheSyncTimeout,omitempty"`
|
||||
|
||||
// RecoverPanic indicates if panics should be recovered.
|
||||
// +optional
|
||||
RecoverPanic *bool `json:"recoverPanic,omitempty"`
|
||||
}
|
||||
|
||||
// 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.
|
||||
// It can be set to "0" to disable the metrics serving.
|
||||
// +optional
|
||||
BindAddress string `json:"bindAddress,omitempty"`
|
||||
}
|
||||
|
||||
// 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
|
||||
// It can be set to "0" or "" to disable serving the health probe.
|
||||
// +optional
|
||||
HealthProbeBindAddress string `json:"healthProbeBindAddress,omitempty"`
|
||||
|
||||
// ReadinessEndpointName, defaults to "readyz"
|
||||
// +optional
|
||||
ReadinessEndpointName string `json:"readinessEndpointName,omitempty"`
|
||||
|
||||
// LivenessEndpointName, defaults to "healthz"
|
||||
// +optional
|
||||
LivenessEndpointName string `json:"livenessEndpointName,omitempty"`
|
||||
}
|
||||
|
||||
// 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.
|
||||
// +optional
|
||||
Port *int `json:"port,omitempty"`
|
||||
|
||||
// Host is the hostname that the webhook server binds to.
|
||||
// It is used to set webhook.Server.Host.
|
||||
// +optional
|
||||
Host string `json:"host,omitempty"`
|
||||
|
||||
// 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. The server key and certificate
|
||||
// must be named tls.key and tls.crt, respectively.
|
||||
// +optional
|
||||
CertDir string `json:"certDir,omitempty"`
|
||||
}
|
||||
|
||||
// +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"`
|
||||
|
||||
// ControllerManagerConfiguration returns the contfigurations for controllers
|
||||
ControllerManagerConfigurationSpec `json:",inline"`
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
157
vendor/sigs.k8s.io/controller-runtime/pkg/config/v1alpha1/zz_generated.deepcopy.go
generated
vendored
157
vendor/sigs.k8s.io/controller-runtime/pkg/config/v1alpha1/zz_generated.deepcopy.go
generated
vendored
@@ -1,157 +0,0 @@
|
||||
//go:build !ignore_autogenerated
|
||||
|
||||
// Code generated by controller-gen. DO NOT EDIT.
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
configv1alpha1 "k8s.io/component-base/config/v1alpha1"
|
||||
timex "time"
|
||||
)
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ControllerConfigurationSpec) DeepCopyInto(out *ControllerConfigurationSpec) {
|
||||
*out = *in
|
||||
if in.GroupKindConcurrency != nil {
|
||||
in, out := &in.GroupKindConcurrency, &out.GroupKindConcurrency
|
||||
*out = make(map[string]int, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
if in.CacheSyncTimeout != nil {
|
||||
in, out := &in.CacheSyncTimeout, &out.CacheSyncTimeout
|
||||
*out = new(timex.Duration)
|
||||
**out = **in
|
||||
}
|
||||
if in.RecoverPanic != nil {
|
||||
in, out := &in.RecoverPanic, &out.RecoverPanic
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ControllerConfigurationSpec.
|
||||
func (in *ControllerConfigurationSpec) DeepCopy() *ControllerConfigurationSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ControllerConfigurationSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ControllerHealth) DeepCopyInto(out *ControllerHealth) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ControllerHealth.
|
||||
func (in *ControllerHealth) DeepCopy() *ControllerHealth {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ControllerHealth)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ControllerManagerConfiguration) DeepCopyInto(out *ControllerManagerConfiguration) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ControllerManagerConfigurationSpec.DeepCopyInto(&out.ControllerManagerConfigurationSpec)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ControllerManagerConfiguration.
|
||||
func (in *ControllerManagerConfiguration) DeepCopy() *ControllerManagerConfiguration {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ControllerManagerConfiguration)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *ControllerManagerConfiguration) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ControllerManagerConfigurationSpec) DeepCopyInto(out *ControllerManagerConfigurationSpec) {
|
||||
*out = *in
|
||||
if in.SyncPeriod != nil {
|
||||
in, out := &in.SyncPeriod, &out.SyncPeriod
|
||||
*out = new(v1.Duration)
|
||||
**out = **in
|
||||
}
|
||||
if in.LeaderElection != nil {
|
||||
in, out := &in.LeaderElection, &out.LeaderElection
|
||||
*out = new(configv1alpha1.LeaderElectionConfiguration)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.GracefulShutdownTimeout != nil {
|
||||
in, out := &in.GracefulShutdownTimeout, &out.GracefulShutdownTimeout
|
||||
*out = new(v1.Duration)
|
||||
**out = **in
|
||||
}
|
||||
if in.Controller != nil {
|
||||
in, out := &in.Controller, &out.Controller
|
||||
*out = new(ControllerConfigurationSpec)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
out.Metrics = in.Metrics
|
||||
out.Health = in.Health
|
||||
in.Webhook.DeepCopyInto(&out.Webhook)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ControllerManagerConfigurationSpec.
|
||||
func (in *ControllerManagerConfigurationSpec) DeepCopy() *ControllerManagerConfigurationSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ControllerManagerConfigurationSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ControllerMetrics) DeepCopyInto(out *ControllerMetrics) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ControllerMetrics.
|
||||
func (in *ControllerMetrics) DeepCopy() *ControllerMetrics {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ControllerMetrics)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ControllerWebhook) DeepCopyInto(out *ControllerWebhook) {
|
||||
*out = *in
|
||||
if in.Port != nil {
|
||||
in, out := &in.Port, &out.Port
|
||||
*out = new(int)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ControllerWebhook.
|
||||
func (in *ControllerWebhook) DeepCopy() *ControllerWebhook {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ControllerWebhook)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
106
vendor/sigs.k8s.io/controller-runtime/pkg/controller/controller.go
generated
vendored
106
vendor/sigs.k8s.io/controller-runtime/pkg/controller/controller.go
generated
vendored
@@ -25,17 +25,23 @@ import (
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/handler"
|
||||
"sigs.k8s.io/controller-runtime/pkg/internal/controller"
|
||||
"sigs.k8s.io/controller-runtime/pkg/manager"
|
||||
"sigs.k8s.io/controller-runtime/pkg/predicate"
|
||||
"sigs.k8s.io/controller-runtime/pkg/ratelimiter"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
"sigs.k8s.io/controller-runtime/pkg/source"
|
||||
)
|
||||
|
||||
// Options are the arguments for creating a new Controller.
|
||||
type Options struct {
|
||||
type Options = TypedOptions[reconcile.Request]
|
||||
|
||||
// TypedOptions are the arguments for creating a new Controller.
|
||||
type TypedOptions[request comparable] struct {
|
||||
// SkipNameValidation allows skipping the name validation that ensures that every controller name is unique.
|
||||
// Unique controller names are important to get unique metrics and logs for a controller.
|
||||
// Defaults to the Controller.SkipNameValidation setting from the Manager if unset.
|
||||
// Defaults to false if Controller.SkipNameValidation setting from the Manager is also unset.
|
||||
SkipNameValidation *bool
|
||||
|
||||
// MaxConcurrentReconciles is the maximum number of concurrent Reconciles which can be run. Defaults to 1.
|
||||
MaxConcurrentReconciles int
|
||||
|
||||
@@ -45,6 +51,7 @@ type Options struct {
|
||||
|
||||
// RecoverPanic indicates whether the panic caused by reconcile should be recovered.
|
||||
// Defaults to the Controller.RecoverPanic setting from the Manager if unset.
|
||||
// Defaults to true if Controller.RecoverPanic setting from the Manager is also unset.
|
||||
RecoverPanic *bool
|
||||
|
||||
// NeedLeaderElection indicates whether the controller needs to use leader election.
|
||||
@@ -52,33 +59,43 @@ type Options struct {
|
||||
NeedLeaderElection *bool
|
||||
|
||||
// Reconciler reconciles an object
|
||||
Reconciler reconcile.Reconciler
|
||||
Reconciler reconcile.TypedReconciler[request]
|
||||
|
||||
// RateLimiter is used to limit how frequently requests may be queued.
|
||||
// Defaults to MaxOfRateLimiter which has both overall and per-item rate limiting.
|
||||
// The overall is a token bucket and the per-item is exponential.
|
||||
RateLimiter ratelimiter.RateLimiter
|
||||
RateLimiter workqueue.TypedRateLimiter[request]
|
||||
|
||||
// NewQueue constructs the queue for this controller once the controller is ready to start.
|
||||
// With NewQueue a custom queue implementation can be used, e.g. a priority queue to prioritize with which
|
||||
// priority/order objects are reconciled (e.g. to reconcile objects with changes first).
|
||||
// This is a func because the standard Kubernetes work queues start themselves immediately, which
|
||||
// leads to goroutine leaks if something calls controller.New repeatedly.
|
||||
// The NewQueue func gets the controller name and the RateLimiter option (defaulted if necessary) passed in.
|
||||
// NewQueue defaults to NewRateLimitingQueueWithConfig.
|
||||
//
|
||||
// NOTE: LOW LEVEL PRIMITIVE!
|
||||
// Only use a custom NewQueue if you know what you are doing.
|
||||
NewQueue func(controllerName string, rateLimiter workqueue.TypedRateLimiter[request]) workqueue.TypedRateLimitingInterface[request]
|
||||
|
||||
// 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
|
||||
LogConstructor func(request *request) logr.Logger
|
||||
}
|
||||
|
||||
// Controller implements a Kubernetes API. A Controller manages a work queue fed reconcile.Requests
|
||||
// from source.Sources. Work is performed through the reconcile.Reconciler for each enqueued item.
|
||||
// Work typically is reads and writes Kubernetes objects to make the system state match the state specified
|
||||
// in the object Spec.
|
||||
type Controller interface {
|
||||
// Reconciler is called to reconcile an object by Namespace/Name
|
||||
reconcile.Reconciler
|
||||
type Controller = TypedController[reconcile.Request]
|
||||
|
||||
// Watch takes events provided by a Source and uses the EventHandler to
|
||||
// enqueue reconcile.Requests in response to the events.
|
||||
//
|
||||
// Watch may be provided one or more Predicates to filter events before
|
||||
// they are given to the EventHandler. Events will be passed to the
|
||||
// EventHandler if all provided Predicates evaluate to true.
|
||||
Watch(src source.Source, eventhandler handler.EventHandler, predicates ...predicate.Predicate) error
|
||||
// TypedController implements an API.
|
||||
type TypedController[request comparable] interface {
|
||||
// Reconciler is called to reconcile an object by Namespace/Name
|
||||
reconcile.TypedReconciler[request]
|
||||
|
||||
// Watch watches the provided Source.
|
||||
Watch(src source.TypedSource[request]) error
|
||||
|
||||
// Start starts the controller. Start blocks until the context is closed or a
|
||||
// controller has an error starting.
|
||||
@@ -90,8 +107,17 @@ type Controller interface {
|
||||
|
||||
// New returns a new Controller registered with the Manager. The Manager will ensure that shared Caches have
|
||||
// been synced before the Controller is Started.
|
||||
//
|
||||
// The name must be unique as it is used to identify the controller in metrics and logs.
|
||||
func New(name string, mgr manager.Manager, options Options) (Controller, error) {
|
||||
c, err := NewUnmanaged(name, mgr, options)
|
||||
return NewTyped(name, mgr, options)
|
||||
}
|
||||
|
||||
// NewTyped returns a new typed controller registered with the Manager,
|
||||
//
|
||||
// The name must be unique as it is used to identify the controller in metrics and logs.
|
||||
func NewTyped[request comparable](name string, mgr manager.Manager, options TypedOptions[request]) (TypedController[request], error) {
|
||||
c, err := NewTypedUnmanaged(name, mgr, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -102,7 +128,16 @@ func New(name string, mgr manager.Manager, options Options) (Controller, error)
|
||||
|
||||
// NewUnmanaged returns a new controller without adding it to the manager. The
|
||||
// caller is responsible for starting the returned controller.
|
||||
//
|
||||
// The name must be unique as it is used to identify the controller in metrics and logs.
|
||||
func NewUnmanaged(name string, mgr manager.Manager, options Options) (Controller, error) {
|
||||
return NewTypedUnmanaged(name, mgr, options)
|
||||
}
|
||||
|
||||
// NewTypedUnmanaged returns a new typed controller without adding it to the manager.
|
||||
//
|
||||
// The name must be unique as it is used to identify the controller in metrics and logs.
|
||||
func NewTypedUnmanaged[request comparable](name string, mgr manager.Manager, options TypedOptions[request]) (TypedController[request], error) {
|
||||
if options.Reconciler == nil {
|
||||
return nil, fmt.Errorf("must specify Reconciler")
|
||||
}
|
||||
@@ -111,13 +146,23 @@ func NewUnmanaged(name string, mgr manager.Manager, options Options) (Controller
|
||||
return nil, fmt.Errorf("must specify Name for Controller")
|
||||
}
|
||||
|
||||
if options.SkipNameValidation == nil {
|
||||
options.SkipNameValidation = mgr.GetControllerOptions().SkipNameValidation
|
||||
}
|
||||
|
||||
if options.SkipNameValidation == nil || !*options.SkipNameValidation {
|
||||
if err := checkName(name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if options.LogConstructor == nil {
|
||||
log := mgr.GetLogger().WithValues(
|
||||
"controller", name,
|
||||
)
|
||||
options.LogConstructor = func(req *reconcile.Request) logr.Logger {
|
||||
options.LogConstructor = func(in *request) logr.Logger {
|
||||
log := log
|
||||
if req != nil {
|
||||
if req, ok := any(in).(*reconcile.Request); ok && req != nil {
|
||||
log = log.WithValues(
|
||||
"object", klog.KRef(req.Namespace, req.Name),
|
||||
"namespace", req.Namespace, "name", req.Name,
|
||||
@@ -144,7 +189,15 @@ func NewUnmanaged(name string, mgr manager.Manager, options Options) (Controller
|
||||
}
|
||||
|
||||
if options.RateLimiter == nil {
|
||||
options.RateLimiter = workqueue.DefaultControllerRateLimiter()
|
||||
options.RateLimiter = workqueue.DefaultTypedControllerRateLimiter[request]()
|
||||
}
|
||||
|
||||
if options.NewQueue == nil {
|
||||
options.NewQueue = func(controllerName string, rateLimiter workqueue.TypedRateLimiter[request]) workqueue.TypedRateLimitingInterface[request] {
|
||||
return workqueue.NewTypedRateLimitingQueueWithConfig(rateLimiter, workqueue.TypedRateLimitingQueueConfig[request]{
|
||||
Name: controllerName,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if options.RecoverPanic == nil {
|
||||
@@ -156,13 +209,10 @@ func NewUnmanaged(name string, mgr manager.Manager, options Options) (Controller
|
||||
}
|
||||
|
||||
// Create controller with dependencies set
|
||||
return &controller.Controller{
|
||||
Do: options.Reconciler,
|
||||
MakeQueue: func() workqueue.RateLimitingInterface {
|
||||
return workqueue.NewRateLimitingQueueWithConfig(options.RateLimiter, workqueue.RateLimitingQueueConfig{
|
||||
Name: name,
|
||||
})
|
||||
},
|
||||
return &controller.Controller[request]{
|
||||
Do: options.Reconciler,
|
||||
RateLimiter: options.RateLimiter,
|
||||
NewQueue: options.NewQueue,
|
||||
MaxConcurrentReconciles: options.MaxConcurrentReconciles,
|
||||
CacheSyncTimeout: options.CacheSyncTimeout,
|
||||
Name: name,
|
||||
|
||||
19
vendor/sigs.k8s.io/controller-runtime/pkg/controller/controllertest/testing.go
generated
vendored
19
vendor/sigs.k8s.io/controller-runtime/pkg/controller/controllertest/testing.go
generated
vendored
@@ -23,6 +23,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
)
|
||||
|
||||
var _ runtime.Object = &ErrorType{}
|
||||
@@ -36,23 +37,27 @@ func (ErrorType) GetObjectKind() schema.ObjectKind { return nil }
|
||||
// DeepCopyObject implements runtime.Object.
|
||||
func (ErrorType) DeepCopyObject() runtime.Object { return nil }
|
||||
|
||||
var _ workqueue.RateLimitingInterface = &Queue{}
|
||||
var _ workqueue.TypedRateLimitingInterface[reconcile.Request] = &Queue{}
|
||||
|
||||
// Queue implements a RateLimiting queue as a non-ratelimited queue for testing.
|
||||
// This helps testing by having functions that use a RateLimiting queue synchronously add items to the queue.
|
||||
type Queue struct {
|
||||
workqueue.Interface
|
||||
type Queue = TypedQueue[reconcile.Request]
|
||||
|
||||
// TypedQueue implements a RateLimiting queue as a non-ratelimited queue for testing.
|
||||
// This helps testing by having functions that use a RateLimiting queue synchronously add items to the queue.
|
||||
type TypedQueue[request comparable] struct {
|
||||
workqueue.TypedInterface[request]
|
||||
AddedRateLimitedLock sync.Mutex
|
||||
AddedRatelimited []any
|
||||
}
|
||||
|
||||
// AddAfter implements RateLimitingInterface.
|
||||
func (q *Queue) AddAfter(item interface{}, duration time.Duration) {
|
||||
func (q *TypedQueue[request]) AddAfter(item request, duration time.Duration) {
|
||||
q.Add(item)
|
||||
}
|
||||
|
||||
// AddRateLimited implements RateLimitingInterface. TODO(community): Implement this.
|
||||
func (q *Queue) AddRateLimited(item interface{}) {
|
||||
func (q *TypedQueue[request]) AddRateLimited(item request) {
|
||||
q.AddedRateLimitedLock.Lock()
|
||||
q.AddedRatelimited = append(q.AddedRatelimited, item)
|
||||
q.AddedRateLimitedLock.Unlock()
|
||||
@@ -60,9 +65,9 @@ func (q *Queue) AddRateLimited(item interface{}) {
|
||||
}
|
||||
|
||||
// Forget implements RateLimitingInterface. TODO(community): Implement this.
|
||||
func (q *Queue) Forget(item interface{}) {}
|
||||
func (q *TypedQueue[request]) Forget(item request) {}
|
||||
|
||||
// NumRequeues implements RateLimitingInterface. TODO(community): Implement this.
|
||||
func (q *Queue) NumRequeues(item interface{}) int {
|
||||
func (q *TypedQueue[request]) NumRequeues(item request) int {
|
||||
return 0
|
||||
}
|
||||
|
||||
20
vendor/sigs.k8s.io/controller-runtime/pkg/controller/controllerutil/controllerutil.go
generated
vendored
20
vendor/sigs.k8s.io/controller-runtime/pkg/controller/controllerutil/controllerutil.go
generated
vendored
@@ -52,12 +52,22 @@ func newAlreadyOwnedError(obj metav1.Object, owner metav1.OwnerReference) *Alrea
|
||||
}
|
||||
}
|
||||
|
||||
// OwnerReferenceOption is a function that can modify a `metav1.OwnerReference`.
|
||||
type OwnerReferenceOption func(*metav1.OwnerReference)
|
||||
|
||||
// WithBlockOwnerDeletion allows configuring the BlockOwnerDeletion field on the `metav1.OwnerReference`.
|
||||
func WithBlockOwnerDeletion(blockOwnerDeletion bool) OwnerReferenceOption {
|
||||
return func(ref *metav1.OwnerReference) {
|
||||
ref.BlockOwnerDeletion = &blockOwnerDeletion
|
||||
}
|
||||
}
|
||||
|
||||
// SetControllerReference sets owner as a Controller OwnerReference on controlled.
|
||||
// This is used for garbage collection of the controlled object and for
|
||||
// reconciling the owner object on changes to controlled (with a Watch + EnqueueRequestForOwner).
|
||||
// Since only one OwnerReference can be a controller, it returns an error if
|
||||
// there is another OwnerReference with Controller flag set.
|
||||
func SetControllerReference(owner, controlled metav1.Object, scheme *runtime.Scheme) error {
|
||||
func SetControllerReference(owner, controlled metav1.Object, scheme *runtime.Scheme, opts ...OwnerReferenceOption) error {
|
||||
// Validate the owner.
|
||||
ro, ok := owner.(runtime.Object)
|
||||
if !ok {
|
||||
@@ -80,6 +90,9 @@ func SetControllerReference(owner, controlled metav1.Object, scheme *runtime.Sch
|
||||
BlockOwnerDeletion: ptr.To(true),
|
||||
Controller: ptr.To(true),
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(&ref)
|
||||
}
|
||||
|
||||
// Return early with an error if the object is already controlled.
|
||||
if existing := metav1.GetControllerOf(controlled); existing != nil && !referSameObject(*existing, ref) {
|
||||
@@ -94,7 +107,7 @@ func SetControllerReference(owner, controlled metav1.Object, scheme *runtime.Sch
|
||||
// SetOwnerReference is a helper method to make sure the given object contains an object reference to the object provided.
|
||||
// This allows you to declare that owner has a dependency on the object without specifying it as a controller.
|
||||
// If a reference to the same object already exists, it'll be overwritten with the newly provided version.
|
||||
func SetOwnerReference(owner, object metav1.Object, scheme *runtime.Scheme) error {
|
||||
func SetOwnerReference(owner, object metav1.Object, scheme *runtime.Scheme, opts ...OwnerReferenceOption) error {
|
||||
// Validate the owner.
|
||||
ro, ok := owner.(runtime.Object)
|
||||
if !ok {
|
||||
@@ -115,6 +128,9 @@ func SetOwnerReference(owner, object metav1.Object, scheme *runtime.Scheme) erro
|
||||
UID: owner.GetUID(),
|
||||
Name: owner.GetName(),
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(&ref)
|
||||
}
|
||||
|
||||
// Update owner references and return.
|
||||
upsertOwnerRef(ref, object)
|
||||
|
||||
@@ -14,9 +14,30 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Package ratelimiter defines rate limiters used by Controllers to limit how frequently requests may be queued.
|
||||
package controller
|
||||
|
||||
Typical rate limiters that can be used are implemented in client-go's workqueue package.
|
||||
*/
|
||||
package ratelimiter
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
)
|
||||
|
||||
var nameLock sync.Mutex
|
||||
var usedNames sets.Set[string]
|
||||
|
||||
func checkName(name string) error {
|
||||
nameLock.Lock()
|
||||
defer nameLock.Unlock()
|
||||
if usedNames == nil {
|
||||
usedNames = sets.Set[string]{}
|
||||
}
|
||||
|
||||
if usedNames.Has(name) {
|
||||
return fmt.Errorf("controller with name %s already exists. Controller names must be unique to avoid multiple controllers reporting to the same metric", name)
|
||||
}
|
||||
|
||||
usedNames.Insert(name)
|
||||
|
||||
return nil
|
||||
}
|
||||
25
vendor/sigs.k8s.io/controller-runtime/pkg/envtest/server.go
generated
vendored
25
vendor/sigs.k8s.io/controller-runtime/pkg/envtest/server.go
generated
vendored
@@ -17,15 +17,20 @@ limitations under the License.
|
||||
package envtest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/client-go/rest"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/config"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/internal/log"
|
||||
@@ -265,6 +270,12 @@ func (te *Environment) Start() (*rest.Config, error) {
|
||||
te.Scheme = scheme.Scheme
|
||||
}
|
||||
|
||||
// If we are bringing etcd up for the first time, it can take some time for the
|
||||
// default namespace to actually be created and seen as available to the apiserver
|
||||
if err := te.waitForDefaultNamespace(te.Config); err != nil {
|
||||
return nil, fmt.Errorf("default namespace didn't register within deadline: %w", err)
|
||||
}
|
||||
|
||||
// Call PrepWithoutInstalling to setup certificates first
|
||||
// and have them available to patch CRD conversion webhook as well.
|
||||
if err := te.WebhookInstallOptions.PrepWithoutInstalling(); err != nil {
|
||||
@@ -322,6 +333,20 @@ func (te *Environment) startControlPlane() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (te *Environment) waitForDefaultNamespace(config *rest.Config) error {
|
||||
cs, err := client.New(config, client.Options{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to create client: %w", err)
|
||||
}
|
||||
// It shouldn't take longer than 5s for the default namespace to be brought up in etcd
|
||||
return wait.PollUntilContextTimeout(context.TODO(), time.Millisecond*50, time.Second*5, true, func(ctx context.Context) (bool, error) {
|
||||
if err = cs.Get(ctx, types.NamespacedName{Name: "default"}, &corev1.Namespace{}); err != nil {
|
||||
return false, nil //nolint:nilerr
|
||||
}
|
||||
return true, nil
|
||||
})
|
||||
}
|
||||
|
||||
func (te *Environment) defaultTimeouts() error {
|
||||
var err error
|
||||
if te.ControlPlaneStartTimeout == 0 {
|
||||
|
||||
51
vendor/sigs.k8s.io/controller-runtime/pkg/event/event.go
generated
vendored
51
vendor/sigs.k8s.io/controller-runtime/pkg/event/event.go
generated
vendored
@@ -18,38 +18,55 @@ package event
|
||||
|
||||
import "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
// CreateEvent is an event where a Kubernetes object was created. CreateEvent should be generated
|
||||
// CreateEvent is an event where a Kubernetes object was created. CreateEvent should be generated
|
||||
// by a source.Source and transformed into a reconcile.Request by a handler.EventHandler.
|
||||
type CreateEvent = TypedCreateEvent[client.Object]
|
||||
|
||||
// UpdateEvent is an event where a Kubernetes object was updated. UpdateEvent should be generated
|
||||
// by a source.Source and transformed into a reconcile.Request by an handler.EventHandler.
|
||||
type CreateEvent struct {
|
||||
type UpdateEvent = TypedUpdateEvent[client.Object]
|
||||
|
||||
// DeleteEvent is an event where a Kubernetes object was deleted. DeleteEvent should be generated
|
||||
// by a source.Source and transformed into a reconcile.Request by an handler.EventHandler.
|
||||
type DeleteEvent = TypedDeleteEvent[client.Object]
|
||||
|
||||
// GenericEvent is an event where the operation type is unknown (e.g. polling or event originating outside the cluster).
|
||||
// GenericEvent should be generated by a source.Source and transformed into a reconcile.Request by an
|
||||
// handler.EventHandler.
|
||||
type GenericEvent = TypedGenericEvent[client.Object]
|
||||
|
||||
// TypedCreateEvent is an event where a Kubernetes object was created. TypedCreateEvent should be generated
|
||||
// by a source.Source and transformed into a reconcile.Request by an handler.TypedEventHandler.
|
||||
type TypedCreateEvent[object any] struct {
|
||||
// Object is the object from the event
|
||||
Object client.Object
|
||||
Object object
|
||||
}
|
||||
|
||||
// UpdateEvent is an event where a Kubernetes object was updated. UpdateEvent should be generated
|
||||
// by a source.Source and transformed into a reconcile.Request by an handler.EventHandler.
|
||||
type UpdateEvent struct {
|
||||
// TypedUpdateEvent is an event where a Kubernetes object was updated. TypedUpdateEvent should be generated
|
||||
// by a source.Source and transformed into a reconcile.Request by an handler.TypedEventHandler.
|
||||
type TypedUpdateEvent[object any] struct {
|
||||
// ObjectOld is the object from the event
|
||||
ObjectOld client.Object
|
||||
ObjectOld object
|
||||
|
||||
// ObjectNew is the object from the event
|
||||
ObjectNew client.Object
|
||||
ObjectNew object
|
||||
}
|
||||
|
||||
// DeleteEvent is an event where a Kubernetes object was deleted. DeleteEvent should be generated
|
||||
// by a source.Source and transformed into a reconcile.Request by an handler.EventHandler.
|
||||
type DeleteEvent struct {
|
||||
// TypedDeleteEvent is an event where a Kubernetes object was deleted. TypedDeleteEvent should be generated
|
||||
// by a source.Source and transformed into a reconcile.Request by an handler.TypedEventHandler.
|
||||
type TypedDeleteEvent[object any] struct {
|
||||
// Object is the object from the event
|
||||
Object client.Object
|
||||
Object object
|
||||
|
||||
// DeleteStateUnknown is true if the Delete event was missed but we identified the object
|
||||
// as having been deleted.
|
||||
DeleteStateUnknown bool
|
||||
}
|
||||
|
||||
// GenericEvent is an event where the operation type is unknown (e.g. polling or event originating outside the cluster).
|
||||
// GenericEvent should be generated by a source.Source and transformed into a reconcile.Request by an
|
||||
// handler.EventHandler.
|
||||
type GenericEvent struct {
|
||||
// TypedGenericEvent is an event where the operation type is unknown (e.g. polling or event originating outside the cluster).
|
||||
// TypedGenericEvent should be generated by a source.Source and transformed into a reconcile.Request by an
|
||||
// handler.TypedEventHandler.
|
||||
type TypedGenericEvent[object any] struct {
|
||||
// Object is the object from the event
|
||||
Object client.Object
|
||||
Object object
|
||||
}
|
||||
|
||||
43
vendor/sigs.k8s.io/controller-runtime/pkg/handler/enqueue.go
generated
vendored
43
vendor/sigs.k8s.io/controller-runtime/pkg/handler/enqueue.go
generated
vendored
@@ -18,9 +18,11 @@ package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
|
||||
"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"
|
||||
@@ -33,13 +35,20 @@ type empty struct{}
|
||||
var _ EventHandler = &EnqueueRequestForObject{}
|
||||
|
||||
// EnqueueRequestForObject enqueues a Request containing the Name and Namespace of the object that is the source of the Event.
|
||||
// (e.g. the created / deleted / updated objects Name and Namespace). handler.EnqueueRequestForObject is used by almost all
|
||||
// (e.g. the created / deleted / updated objects Name and Namespace). handler.EnqueueRequestForObject is used by almost all
|
||||
// Controllers that have associated Resources (e.g. CRDs) to reconcile the associated Resource.
|
||||
type EnqueueRequestForObject struct{}
|
||||
type EnqueueRequestForObject = TypedEnqueueRequestForObject[client.Object]
|
||||
|
||||
// TypedEnqueueRequestForObject enqueues a Request containing the Name and Namespace of the object that is the source of the Event.
|
||||
// (e.g. the created / deleted / updated objects Name and Namespace). handler.TypedEnqueueRequestForObject is used by almost all
|
||||
// Controllers that have associated Resources (e.g. CRDs) to reconcile the associated Resource.
|
||||
//
|
||||
// TypedEnqueueRequestForObject is experimental and subject to future change.
|
||||
type TypedEnqueueRequestForObject[object client.Object] struct{}
|
||||
|
||||
// Create implements EventHandler.
|
||||
func (e *EnqueueRequestForObject) Create(ctx context.Context, evt event.CreateEvent, q workqueue.RateLimitingInterface) {
|
||||
if evt.Object == nil {
|
||||
func (e *TypedEnqueueRequestForObject[T]) Create(ctx context.Context, evt event.TypedCreateEvent[T], q workqueue.TypedRateLimitingInterface[reconcile.Request]) {
|
||||
if isNil(evt.Object) {
|
||||
enqueueLog.Error(nil, "CreateEvent received with no metadata", "event", evt)
|
||||
return
|
||||
}
|
||||
@@ -50,14 +59,14 @@ func (e *EnqueueRequestForObject) Create(ctx context.Context, evt event.CreateEv
|
||||
}
|
||||
|
||||
// Update implements EventHandler.
|
||||
func (e *EnqueueRequestForObject) Update(ctx context.Context, evt event.UpdateEvent, q workqueue.RateLimitingInterface) {
|
||||
func (e *TypedEnqueueRequestForObject[T]) Update(ctx context.Context, evt event.TypedUpdateEvent[T], q workqueue.TypedRateLimitingInterface[reconcile.Request]) {
|
||||
switch {
|
||||
case evt.ObjectNew != nil:
|
||||
case !isNil(evt.ObjectNew):
|
||||
q.Add(reconcile.Request{NamespacedName: types.NamespacedName{
|
||||
Name: evt.ObjectNew.GetName(),
|
||||
Namespace: evt.ObjectNew.GetNamespace(),
|
||||
}})
|
||||
case evt.ObjectOld != nil:
|
||||
case !isNil(evt.ObjectOld):
|
||||
q.Add(reconcile.Request{NamespacedName: types.NamespacedName{
|
||||
Name: evt.ObjectOld.GetName(),
|
||||
Namespace: evt.ObjectOld.GetNamespace(),
|
||||
@@ -68,8 +77,8 @@ func (e *EnqueueRequestForObject) Update(ctx context.Context, evt event.UpdateEv
|
||||
}
|
||||
|
||||
// Delete implements EventHandler.
|
||||
func (e *EnqueueRequestForObject) Delete(ctx context.Context, evt event.DeleteEvent, q workqueue.RateLimitingInterface) {
|
||||
if evt.Object == nil {
|
||||
func (e *TypedEnqueueRequestForObject[T]) Delete(ctx context.Context, evt event.TypedDeleteEvent[T], q workqueue.TypedRateLimitingInterface[reconcile.Request]) {
|
||||
if isNil(evt.Object) {
|
||||
enqueueLog.Error(nil, "DeleteEvent received with no metadata", "event", evt)
|
||||
return
|
||||
}
|
||||
@@ -80,8 +89,8 @@ func (e *EnqueueRequestForObject) Delete(ctx context.Context, evt event.DeleteEv
|
||||
}
|
||||
|
||||
// Generic implements EventHandler.
|
||||
func (e *EnqueueRequestForObject) Generic(ctx context.Context, evt event.GenericEvent, q workqueue.RateLimitingInterface) {
|
||||
if evt.Object == nil {
|
||||
func (e *TypedEnqueueRequestForObject[T]) Generic(ctx context.Context, evt event.TypedGenericEvent[T], q workqueue.TypedRateLimitingInterface[reconcile.Request]) {
|
||||
if isNil(evt.Object) {
|
||||
enqueueLog.Error(nil, "GenericEvent received with no metadata", "event", evt)
|
||||
return
|
||||
}
|
||||
@@ -90,3 +99,15 @@ func (e *EnqueueRequestForObject) Generic(ctx context.Context, evt event.Generic
|
||||
Namespace: evt.Object.GetNamespace(),
|
||||
}})
|
||||
}
|
||||
|
||||
func isNil(arg any) bool {
|
||||
if v := reflect.ValueOf(arg); !v.IsValid() || ((v.Kind() == reflect.Ptr ||
|
||||
v.Kind() == reflect.Interface ||
|
||||
v.Kind() == reflect.Slice ||
|
||||
v.Kind() == reflect.Map ||
|
||||
v.Kind() == reflect.Chan ||
|
||||
v.Kind() == reflect.Func) && v.IsNil()) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
68
vendor/sigs.k8s.io/controller-runtime/pkg/handler/enqueue_mapped.go
generated
vendored
68
vendor/sigs.k8s.io/controller-runtime/pkg/handler/enqueue_mapped.go
generated
vendored
@@ -27,7 +27,13 @@ import (
|
||||
|
||||
// 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(context.Context, client.Object) []reconcile.Request
|
||||
type MapFunc = TypedMapFunc[client.Object, reconcile.Request]
|
||||
|
||||
// TypedMapFunc is the signature required for enqueueing requests from a generic function.
|
||||
// This type is usually used with EnqueueRequestsFromTypedMapFunc when registering an event handler.
|
||||
//
|
||||
// TypedMapFunc is experimental and subject to future change.
|
||||
type TypedMapFunc[object any, request comparable] func(context.Context, object) []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
|
||||
@@ -40,45 +46,77 @@ type MapFunc func(context.Context, client.Object) []reconcile.Request
|
||||
// For UpdateEvents which contain both a new and old object, the transformation function is run on both
|
||||
// objects and both sets of Requests are enqueue.
|
||||
func EnqueueRequestsFromMapFunc(fn MapFunc) EventHandler {
|
||||
return &enqueueRequestsFromMapFunc{
|
||||
return TypedEnqueueRequestsFromMapFunc(fn)
|
||||
}
|
||||
|
||||
// TypedEnqueueRequestsFromMapFunc 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
|
||||
// defined by some user specified transformation of the source Event. (e.g. trigger Reconciler for a set of objects
|
||||
// in response to a cluster resize event caused by adding or deleting a Node)
|
||||
//
|
||||
// TypedEnqueueRequestsFromMapFunc is frequently used to fan-out updates from one object to one or more other
|
||||
// objects of a differing type.
|
||||
//
|
||||
// For TypedUpdateEvents which contain both a new and old object, the transformation function is run on both
|
||||
// objects and both sets of Requests are enqueue.
|
||||
//
|
||||
// TypedEnqueueRequestsFromMapFunc is experimental and subject to future change.
|
||||
func TypedEnqueueRequestsFromMapFunc[object any, request comparable](fn TypedMapFunc[object, request]) TypedEventHandler[object, request] {
|
||||
return &enqueueRequestsFromMapFunc[object, request]{
|
||||
toRequests: fn,
|
||||
}
|
||||
}
|
||||
|
||||
var _ EventHandler = &enqueueRequestsFromMapFunc{}
|
||||
var _ EventHandler = &enqueueRequestsFromMapFunc[client.Object, reconcile.Request]{}
|
||||
|
||||
type enqueueRequestsFromMapFunc struct {
|
||||
type enqueueRequestsFromMapFunc[object any, request comparable] struct {
|
||||
// Mapper transforms the argument into a slice of keys to be reconciled
|
||||
toRequests MapFunc
|
||||
toRequests TypedMapFunc[object, request]
|
||||
}
|
||||
|
||||
// Create implements EventHandler.
|
||||
func (e *enqueueRequestsFromMapFunc) Create(ctx context.Context, evt event.CreateEvent, q workqueue.RateLimitingInterface) {
|
||||
reqs := map[reconcile.Request]empty{}
|
||||
func (e *enqueueRequestsFromMapFunc[object, request]) Create(
|
||||
ctx context.Context,
|
||||
evt event.TypedCreateEvent[object],
|
||||
q workqueue.TypedRateLimitingInterface[request],
|
||||
) {
|
||||
reqs := map[request]empty{}
|
||||
e.mapAndEnqueue(ctx, q, evt.Object, reqs)
|
||||
}
|
||||
|
||||
// Update implements EventHandler.
|
||||
func (e *enqueueRequestsFromMapFunc) Update(ctx context.Context, evt event.UpdateEvent, q workqueue.RateLimitingInterface) {
|
||||
reqs := map[reconcile.Request]empty{}
|
||||
func (e *enqueueRequestsFromMapFunc[object, request]) Update(
|
||||
ctx context.Context,
|
||||
evt event.TypedUpdateEvent[object],
|
||||
q workqueue.TypedRateLimitingInterface[request],
|
||||
) {
|
||||
reqs := map[request]empty{}
|
||||
e.mapAndEnqueue(ctx, q, evt.ObjectOld, reqs)
|
||||
e.mapAndEnqueue(ctx, q, evt.ObjectNew, reqs)
|
||||
}
|
||||
|
||||
// Delete implements EventHandler.
|
||||
func (e *enqueueRequestsFromMapFunc) Delete(ctx context.Context, evt event.DeleteEvent, q workqueue.RateLimitingInterface) {
|
||||
reqs := map[reconcile.Request]empty{}
|
||||
func (e *enqueueRequestsFromMapFunc[object, request]) Delete(
|
||||
ctx context.Context,
|
||||
evt event.TypedDeleteEvent[object],
|
||||
q workqueue.TypedRateLimitingInterface[request],
|
||||
) {
|
||||
reqs := map[request]empty{}
|
||||
e.mapAndEnqueue(ctx, q, evt.Object, reqs)
|
||||
}
|
||||
|
||||
// Generic implements EventHandler.
|
||||
func (e *enqueueRequestsFromMapFunc) Generic(ctx context.Context, evt event.GenericEvent, q workqueue.RateLimitingInterface) {
|
||||
reqs := map[reconcile.Request]empty{}
|
||||
func (e *enqueueRequestsFromMapFunc[object, request]) Generic(
|
||||
ctx context.Context,
|
||||
evt event.TypedGenericEvent[object],
|
||||
q workqueue.TypedRateLimitingInterface[request],
|
||||
) {
|
||||
reqs := map[request]empty{}
|
||||
e.mapAndEnqueue(ctx, q, evt.Object, reqs)
|
||||
}
|
||||
|
||||
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) {
|
||||
func (e *enqueueRequestsFromMapFunc[object, request]) mapAndEnqueue(ctx context.Context, q workqueue.TypedRateLimitingInterface[request], o object, reqs map[request]empty) {
|
||||
for _, req := range e.toRequests(ctx, o) {
|
||||
_, ok := reqs[req]
|
||||
if !ok {
|
||||
q.Add(req)
|
||||
|
||||
58
vendor/sigs.k8s.io/controller-runtime/pkg/handler/enqueue_owner.go
generated
vendored
58
vendor/sigs.k8s.io/controller-runtime/pkg/handler/enqueue_owner.go
generated
vendored
@@ -32,12 +32,12 @@ import (
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
)
|
||||
|
||||
var _ EventHandler = &enqueueRequestForOwner{}
|
||||
var _ EventHandler = &enqueueRequestForOwner[client.Object]{}
|
||||
|
||||
var log = logf.RuntimeLog.WithName("eventhandler").WithName("enqueueRequestForOwner")
|
||||
|
||||
// OwnerOption modifies an EnqueueRequestForOwner EventHandler.
|
||||
type OwnerOption func(e *enqueueRequestForOwner)
|
||||
type OwnerOption func(e enqueueRequestForOwnerInterface)
|
||||
|
||||
// EnqueueRequestForOwner enqueues Requests for the Owners of an object. E.g. the object that created
|
||||
// the object that was the source of the Event.
|
||||
@@ -48,7 +48,21 @@ type OwnerOption func(e *enqueueRequestForOwner)
|
||||
//
|
||||
// - 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{
|
||||
return TypedEnqueueRequestForOwner[client.Object](scheme, mapper, ownerType, opts...)
|
||||
}
|
||||
|
||||
// TypedEnqueueRequestForOwner enqueues Requests for the Owners of an object. E.g. the object that created
|
||||
// the object that was the source of the Event.
|
||||
//
|
||||
// If a ReplicaSet creates Pods, users may reconcile the ReplicaSet in response to Pod Events using:
|
||||
//
|
||||
// - a source.Kind Source with Type of Pod.
|
||||
//
|
||||
// - a handler.typedEnqueueRequestForOwner EventHandler with an OwnerType of ReplicaSet and OnlyControllerOwner set to true.
|
||||
//
|
||||
// TypedEnqueueRequestForOwner is experimental and subject to future change.
|
||||
func TypedEnqueueRequestForOwner[object client.Object](scheme *runtime.Scheme, mapper meta.RESTMapper, ownerType client.Object, opts ...OwnerOption) TypedEventHandler[object, reconcile.Request] {
|
||||
e := &enqueueRequestForOwner[object]{
|
||||
ownerType: ownerType,
|
||||
mapper: mapper,
|
||||
}
|
||||
@@ -63,12 +77,16 @@ func EnqueueRequestForOwner(scheme *runtime.Scheme, mapper meta.RESTMapper, owne
|
||||
|
||||
// OnlyControllerOwner if provided will only look at the first OwnerReference with Controller: true.
|
||||
func OnlyControllerOwner() OwnerOption {
|
||||
return func(e *enqueueRequestForOwner) {
|
||||
e.isController = true
|
||||
return func(e enqueueRequestForOwnerInterface) {
|
||||
e.setIsController(true)
|
||||
}
|
||||
}
|
||||
|
||||
type enqueueRequestForOwner struct {
|
||||
type enqueueRequestForOwnerInterface interface {
|
||||
setIsController(bool)
|
||||
}
|
||||
|
||||
type enqueueRequestForOwner[object client.Object] struct {
|
||||
// ownerType is the type of the Owner object to look for in OwnerReferences. Only Group and Kind are compared.
|
||||
ownerType runtime.Object
|
||||
|
||||
@@ -82,8 +100,12 @@ type enqueueRequestForOwner struct {
|
||||
mapper meta.RESTMapper
|
||||
}
|
||||
|
||||
func (e *enqueueRequestForOwner[object]) setIsController(isController bool) {
|
||||
e.isController = isController
|
||||
}
|
||||
|
||||
// Create implements EventHandler.
|
||||
func (e *enqueueRequestForOwner) Create(ctx context.Context, evt event.CreateEvent, q workqueue.RateLimitingInterface) {
|
||||
func (e *enqueueRequestForOwner[object]) Create(ctx context.Context, evt event.TypedCreateEvent[object], q workqueue.TypedRateLimitingInterface[reconcile.Request]) {
|
||||
reqs := map[reconcile.Request]empty{}
|
||||
e.getOwnerReconcileRequest(evt.Object, reqs)
|
||||
for req := range reqs {
|
||||
@@ -92,7 +114,7 @@ func (e *enqueueRequestForOwner) Create(ctx context.Context, evt event.CreateEve
|
||||
}
|
||||
|
||||
// Update implements EventHandler.
|
||||
func (e *enqueueRequestForOwner) Update(ctx context.Context, evt event.UpdateEvent, q workqueue.RateLimitingInterface) {
|
||||
func (e *enqueueRequestForOwner[object]) Update(ctx context.Context, evt event.TypedUpdateEvent[object], q workqueue.TypedRateLimitingInterface[reconcile.Request]) {
|
||||
reqs := map[reconcile.Request]empty{}
|
||||
e.getOwnerReconcileRequest(evt.ObjectOld, reqs)
|
||||
e.getOwnerReconcileRequest(evt.ObjectNew, reqs)
|
||||
@@ -102,7 +124,7 @@ func (e *enqueueRequestForOwner) Update(ctx context.Context, evt event.UpdateEve
|
||||
}
|
||||
|
||||
// Delete implements EventHandler.
|
||||
func (e *enqueueRequestForOwner) Delete(ctx context.Context, evt event.DeleteEvent, q workqueue.RateLimitingInterface) {
|
||||
func (e *enqueueRequestForOwner[object]) Delete(ctx context.Context, evt event.TypedDeleteEvent[object], q workqueue.TypedRateLimitingInterface[reconcile.Request]) {
|
||||
reqs := map[reconcile.Request]empty{}
|
||||
e.getOwnerReconcileRequest(evt.Object, reqs)
|
||||
for req := range reqs {
|
||||
@@ -111,7 +133,7 @@ func (e *enqueueRequestForOwner) Delete(ctx context.Context, evt event.DeleteEve
|
||||
}
|
||||
|
||||
// Generic implements EventHandler.
|
||||
func (e *enqueueRequestForOwner) Generic(ctx context.Context, evt event.GenericEvent, q workqueue.RateLimitingInterface) {
|
||||
func (e *enqueueRequestForOwner[object]) Generic(ctx context.Context, evt event.TypedGenericEvent[object], q workqueue.TypedRateLimitingInterface[reconcile.Request]) {
|
||||
reqs := map[reconcile.Request]empty{}
|
||||
e.getOwnerReconcileRequest(evt.Object, reqs)
|
||||
for req := range reqs {
|
||||
@@ -121,7 +143,7 @@ func (e *enqueueRequestForOwner) Generic(ctx context.Context, evt event.GenericE
|
||||
|
||||
// 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[object]) parseOwnerTypeGroupKind(scheme *runtime.Scheme) error {
|
||||
// Get the kinds of the type
|
||||
kinds, _, err := scheme.ObjectKinds(e.ownerType)
|
||||
if err != nil {
|
||||
@@ -141,10 +163,10 @@ 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[object]) getOwnerReconcileRequest(obj 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) {
|
||||
for _, ref := range e.getOwnersReferences(obj) {
|
||||
// Parse the Group out of the OwnerReference to compare it to what was parsed out of the requested OwnerType
|
||||
refGV, err := schema.ParseGroupVersion(ref.APIVersion)
|
||||
if err != nil {
|
||||
@@ -170,7 +192,7 @@ func (e *enqueueRequestForOwner) getOwnerReconcileRequest(object metav1.Object,
|
||||
return
|
||||
}
|
||||
if mapping.Scope.Name() != meta.RESTScopeNameRoot {
|
||||
request.Namespace = object.GetNamespace()
|
||||
request.Namespace = obj.GetNamespace()
|
||||
}
|
||||
|
||||
result[request] = empty{}
|
||||
@@ -181,17 +203,17 @@ func (e *enqueueRequestForOwner) getOwnerReconcileRequest(object metav1.Object,
|
||||
// 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 {
|
||||
if object == nil {
|
||||
func (e *enqueueRequestForOwner[object]) getOwnersReferences(obj metav1.Object) []metav1.OwnerReference {
|
||||
if obj == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// If not filtered as Controller only, then use all the OwnerReferences
|
||||
if !e.isController {
|
||||
return object.GetOwnerReferences()
|
||||
return obj.GetOwnerReferences()
|
||||
}
|
||||
// If filtered to a Controller, only take the Controller OwnerReference
|
||||
if ownerRef := metav1.GetControllerOf(object); ownerRef != nil {
|
||||
if ownerRef := metav1.GetControllerOf(obj); ownerRef != nil {
|
||||
return []metav1.OwnerReference{*ownerRef}
|
||||
}
|
||||
// No Controller OwnerReference found
|
||||
|
||||
61
vendor/sigs.k8s.io/controller-runtime/pkg/handler/eventhandler.go
generated
vendored
61
vendor/sigs.k8s.io/controller-runtime/pkg/handler/eventhandler.go
generated
vendored
@@ -20,12 +20,14 @@ 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"
|
||||
)
|
||||
|
||||
// EventHandler enqueues reconcile.Requests in response to events (e.g. Pod Create). EventHandlers map an Event
|
||||
// for one object to trigger Reconciles for either the same object or different objects - e.g. if there is an
|
||||
// Event for object with type Foo (using source.KindSource) then reconcile one or more object(s) with type Bar.
|
||||
// Event for object with type Foo (using source.Kind) then reconcile one or more object(s) with type Bar.
|
||||
//
|
||||
// Identical reconcile.Requests will be batched together through the queuing mechanism before reconcile is called.
|
||||
//
|
||||
@@ -41,65 +43,92 @@ import (
|
||||
//
|
||||
// Unless you are implementing your own EventHandler, you can ignore the functions on the EventHandler interface.
|
||||
// Most users shouldn't need to implement their own EventHandler.
|
||||
type EventHandler interface {
|
||||
type EventHandler = TypedEventHandler[client.Object, reconcile.Request]
|
||||
|
||||
// TypedEventHandler enqueues reconcile.Requests in response to events (e.g. Pod Create). TypedEventHandlers map an Event
|
||||
// for one object to trigger Reconciles for either the same object or different objects - e.g. if there is an
|
||||
// Event for object with type Foo (using source.Kind) then reconcile one or more object(s) with type Bar.
|
||||
//
|
||||
// Identical reconcile.Requests will be batched together through the queuing mechanism before reconcile is called.
|
||||
//
|
||||
// * Use TypedEnqueueRequestForObject to reconcile the object the event is for
|
||||
// - do this for events for the type the Controller Reconciles. (e.g. Deployment for a Deployment Controller)
|
||||
//
|
||||
// * Use TypedEnqueueRequestForOwner to reconcile the owner of the object the event is for
|
||||
// - do this for events for the types the Controller creates. (e.g. ReplicaSets created by a Deployment Controller)
|
||||
//
|
||||
// * Use TypedEnqueueRequestsFromMapFunc to transform an event for an object to a reconcile of an object
|
||||
// of a different type - do this for events for types the Controller may be interested in, but doesn't create.
|
||||
// (e.g. If Foo responds to cluster size events, map Node events to Foo objects.)
|
||||
//
|
||||
// Unless you are implementing your own TypedEventHandler, you can ignore the functions on the TypedEventHandler interface.
|
||||
// Most users shouldn't need to implement their own TypedEventHandler.
|
||||
//
|
||||
// TypedEventHandler is experimental and subject to future change.
|
||||
type TypedEventHandler[object any, request comparable] interface {
|
||||
// Create is called in response to a create event - e.g. Pod Creation.
|
||||
Create(context.Context, event.CreateEvent, workqueue.RateLimitingInterface)
|
||||
Create(context.Context, event.TypedCreateEvent[object], workqueue.TypedRateLimitingInterface[request])
|
||||
|
||||
// Update is called in response to an update event - e.g. Pod Updated.
|
||||
Update(context.Context, event.UpdateEvent, workqueue.RateLimitingInterface)
|
||||
Update(context.Context, event.TypedUpdateEvent[object], workqueue.TypedRateLimitingInterface[request])
|
||||
|
||||
// Delete is called in response to a delete event - e.g. Pod Deleted.
|
||||
Delete(context.Context, event.DeleteEvent, workqueue.RateLimitingInterface)
|
||||
Delete(context.Context, event.TypedDeleteEvent[object], workqueue.TypedRateLimitingInterface[request])
|
||||
|
||||
// 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(context.Context, event.GenericEvent, workqueue.RateLimitingInterface)
|
||||
Generic(context.Context, event.TypedGenericEvent[object], workqueue.TypedRateLimitingInterface[request])
|
||||
}
|
||||
|
||||
var _ EventHandler = Funcs{}
|
||||
|
||||
// Funcs implements EventHandler.
|
||||
type Funcs struct {
|
||||
// Funcs implements eventhandler.
|
||||
type Funcs = TypedFuncs[client.Object, reconcile.Request]
|
||||
|
||||
// TypedFuncs implements eventhandler.
|
||||
//
|
||||
// TypedFuncs is experimental and subject to future change.
|
||||
type TypedFuncs[object any, request comparable] struct {
|
||||
// Create is called in response to an add event. Defaults to no-op.
|
||||
// RateLimitingInterface is used to enqueue reconcile.Requests.
|
||||
CreateFunc func(context.Context, event.CreateEvent, workqueue.RateLimitingInterface)
|
||||
CreateFunc func(context.Context, event.TypedCreateEvent[object], workqueue.TypedRateLimitingInterface[request])
|
||||
|
||||
// Update is called in response to an update event. Defaults to no-op.
|
||||
// RateLimitingInterface is used to enqueue reconcile.Requests.
|
||||
UpdateFunc func(context.Context, event.UpdateEvent, workqueue.RateLimitingInterface)
|
||||
UpdateFunc func(context.Context, event.TypedUpdateEvent[object], workqueue.TypedRateLimitingInterface[request])
|
||||
|
||||
// Delete is called in response to a delete event. Defaults to no-op.
|
||||
// RateLimitingInterface is used to enqueue reconcile.Requests.
|
||||
DeleteFunc func(context.Context, event.DeleteEvent, workqueue.RateLimitingInterface)
|
||||
DeleteFunc func(context.Context, event.TypedDeleteEvent[object], workqueue.TypedRateLimitingInterface[request])
|
||||
|
||||
// GenericFunc is called in response to a generic event. Defaults to no-op.
|
||||
// RateLimitingInterface is used to enqueue reconcile.Requests.
|
||||
GenericFunc func(context.Context, event.GenericEvent, workqueue.RateLimitingInterface)
|
||||
GenericFunc func(context.Context, event.TypedGenericEvent[object], workqueue.TypedRateLimitingInterface[request])
|
||||
}
|
||||
|
||||
// Create implements EventHandler.
|
||||
func (h Funcs) Create(ctx context.Context, e event.CreateEvent, q workqueue.RateLimitingInterface) {
|
||||
func (h TypedFuncs[object, request]) Create(ctx context.Context, e event.TypedCreateEvent[object], q workqueue.TypedRateLimitingInterface[request]) {
|
||||
if h.CreateFunc != nil {
|
||||
h.CreateFunc(ctx, e, q)
|
||||
}
|
||||
}
|
||||
|
||||
// Delete implements EventHandler.
|
||||
func (h Funcs) Delete(ctx context.Context, e event.DeleteEvent, q workqueue.RateLimitingInterface) {
|
||||
func (h TypedFuncs[object, request]) Delete(ctx context.Context, e event.TypedDeleteEvent[object], q workqueue.TypedRateLimitingInterface[request]) {
|
||||
if h.DeleteFunc != nil {
|
||||
h.DeleteFunc(ctx, e, q)
|
||||
}
|
||||
}
|
||||
|
||||
// Update implements EventHandler.
|
||||
func (h Funcs) Update(ctx context.Context, e event.UpdateEvent, q workqueue.RateLimitingInterface) {
|
||||
func (h TypedFuncs[object, request]) Update(ctx context.Context, e event.TypedUpdateEvent[object], q workqueue.TypedRateLimitingInterface[request]) {
|
||||
if h.UpdateFunc != nil {
|
||||
h.UpdateFunc(ctx, e, q)
|
||||
}
|
||||
}
|
||||
|
||||
// Generic implements EventHandler.
|
||||
func (h Funcs) Generic(ctx context.Context, e event.GenericEvent, q workqueue.RateLimitingInterface) {
|
||||
func (h TypedFuncs[object, request]) Generic(ctx context.Context, e event.TypedGenericEvent[object], q workqueue.TypedRateLimitingInterface[request]) {
|
||||
if h.GenericFunc != nil {
|
||||
h.GenericFunc(ctx, e, q)
|
||||
}
|
||||
|
||||
87
vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/controller.go
generated
vendored
87
vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/controller.go
generated
vendored
@@ -29,16 +29,14 @@ import (
|
||||
"k8s.io/apimachinery/pkg/util/uuid"
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/handler"
|
||||
ctrlmetrics "sigs.k8s.io/controller-runtime/pkg/internal/controller/metrics"
|
||||
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/source"
|
||||
)
|
||||
|
||||
// Controller implements controller.Controller.
|
||||
type Controller struct {
|
||||
type Controller[request comparable] struct {
|
||||
// Name is used to uniquely identify a Controller in tracing, logging and monitoring. Name is required.
|
||||
Name string
|
||||
|
||||
@@ -48,16 +46,19 @@ type Controller struct {
|
||||
// Reconciler is a function that can be called at any time with the Name / Namespace of an object and
|
||||
// ensures that the state of the system matches the state specified in the object.
|
||||
// Defaults to the DefaultReconcileFunc.
|
||||
Do reconcile.Reconciler
|
||||
Do reconcile.TypedReconciler[request]
|
||||
|
||||
// MakeQueue constructs the queue for this controller once the controller is ready to start.
|
||||
// This exists because the standard Kubernetes workqueues start themselves immediately, which
|
||||
// RateLimiter is used to limit how frequently requests may be queued into the work queue.
|
||||
RateLimiter workqueue.TypedRateLimiter[request]
|
||||
|
||||
// NewQueue constructs the queue for this controller once the controller is ready to start.
|
||||
// This is a func because the standard Kubernetes work queues start themselves immediately, which
|
||||
// leads to goroutine leaks if something calls controller.New repeatedly.
|
||||
MakeQueue func() workqueue.RateLimitingInterface
|
||||
NewQueue func(controllerName string, rateLimiter workqueue.TypedRateLimiter[request]) workqueue.TypedRateLimitingInterface[request]
|
||||
|
||||
// Queue is an listeningQueue that listens for events from Informers and adds object keys to
|
||||
// the Queue for processing
|
||||
Queue workqueue.RateLimitingInterface
|
||||
Queue workqueue.TypedRateLimitingInterface[request]
|
||||
|
||||
// mu is used to synchronize Controller setup
|
||||
mu sync.Mutex
|
||||
@@ -77,35 +78,31 @@ type Controller struct {
|
||||
CacheSyncTimeout time.Duration
|
||||
|
||||
// startWatches maintains a list of sources, handlers, and predicates to start when the controller is started.
|
||||
startWatches []watchDescription
|
||||
startWatches []source.TypedSource[request]
|
||||
|
||||
// LogConstructor is used to construct a logger to then log messages to users during reconciliation,
|
||||
// or for example when a watch is started.
|
||||
// Note: LogConstructor has to be able to handle nil requests as we are also using it
|
||||
// outside the context of a reconciliation.
|
||||
LogConstructor func(request *reconcile.Request) logr.Logger
|
||||
LogConstructor func(request *request) logr.Logger
|
||||
|
||||
// RecoverPanic indicates whether the panic caused by reconcile should be recovered.
|
||||
// Defaults to true.
|
||||
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.
|
||||
type watchDescription struct {
|
||||
src source.Source
|
||||
handler handler.EventHandler
|
||||
predicates []predicate.Predicate
|
||||
}
|
||||
|
||||
// Reconcile implements reconcile.Reconciler.
|
||||
func (c *Controller) Reconcile(ctx context.Context, req reconcile.Request) (_ reconcile.Result, err error) {
|
||||
func (c *Controller[request]) Reconcile(ctx context.Context, req request) (_ reconcile.Result, err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
if c.RecoverPanic != nil && *c.RecoverPanic {
|
||||
ctrlmetrics.ReconcilePanics.WithLabelValues(c.Name).Inc()
|
||||
|
||||
if c.RecoverPanic == nil || *c.RecoverPanic {
|
||||
for _, fn := range utilruntime.PanicHandlers {
|
||||
fn(r)
|
||||
fn(ctx, r)
|
||||
}
|
||||
err = fmt.Errorf("panic: %v [recovered]", r)
|
||||
return
|
||||
@@ -120,7 +117,7 @@ func (c *Controller) Reconcile(ctx context.Context, req reconcile.Request) (_ re
|
||||
}
|
||||
|
||||
// Watch implements controller.Controller.
|
||||
func (c *Controller) Watch(src source.Source, evthdler handler.EventHandler, prct ...predicate.Predicate) error {
|
||||
func (c *Controller[request]) Watch(src source.TypedSource[request]) error {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
@@ -128,16 +125,16 @@ func (c *Controller) Watch(src source.Source, evthdler handler.EventHandler, prc
|
||||
//
|
||||
// These watches are going to be held on the controller struct until the manager or user calls Start(...).
|
||||
if !c.Started {
|
||||
c.startWatches = append(c.startWatches, watchDescription{src: src, handler: evthdler, predicates: prct})
|
||||
c.startWatches = append(c.startWatches, src)
|
||||
return nil
|
||||
}
|
||||
|
||||
c.LogConstructor(nil).Info("Starting EventSource", "source", src)
|
||||
return src.Start(c.ctx, evthdler, c.Queue, prct...)
|
||||
return src.Start(c.ctx, c.Queue)
|
||||
}
|
||||
|
||||
// NeedLeaderElection implements the manager.LeaderElectionRunnable interface.
|
||||
func (c *Controller) NeedLeaderElection() bool {
|
||||
func (c *Controller[request]) NeedLeaderElection() bool {
|
||||
if c.LeaderElected == nil {
|
||||
return true
|
||||
}
|
||||
@@ -145,7 +142,7 @@ func (c *Controller) NeedLeaderElection() bool {
|
||||
}
|
||||
|
||||
// Start implements controller.Controller.
|
||||
func (c *Controller) Start(ctx context.Context) error {
|
||||
func (c *Controller[request]) Start(ctx context.Context) error {
|
||||
// use an IIFE to get proper lock handling
|
||||
// but lock outside to get proper handling of the queue shutdown
|
||||
c.mu.Lock()
|
||||
@@ -158,7 +155,7 @@ func (c *Controller) Start(ctx context.Context) error {
|
||||
// Set the internal context.
|
||||
c.ctx = ctx
|
||||
|
||||
c.Queue = c.MakeQueue()
|
||||
c.Queue = c.NewQueue(c.Name, c.RateLimiter)
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
c.Queue.ShutDown()
|
||||
@@ -175,9 +172,9 @@ func (c *Controller) Start(ctx context.Context) error {
|
||||
// caches to sync so that they have a chance to register their intendeded
|
||||
// caches.
|
||||
for _, watch := range c.startWatches {
|
||||
c.LogConstructor(nil).Info("Starting EventSource", "source", fmt.Sprintf("%s", watch.src))
|
||||
c.LogConstructor(nil).Info("Starting EventSource", "source", fmt.Sprintf("%s", watch))
|
||||
|
||||
if err := watch.src.Start(ctx, watch.handler, c.Queue, watch.predicates...); err != nil {
|
||||
if err := watch.Start(ctx, c.Queue); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -186,7 +183,7 @@ func (c *Controller) Start(ctx context.Context) error {
|
||||
c.LogConstructor(nil).Info("Starting Controller")
|
||||
|
||||
for _, watch := range c.startWatches {
|
||||
syncingSource, ok := watch.src.(source.SyncingSource)
|
||||
syncingSource, ok := watch.(source.SyncingSource)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
@@ -245,7 +242,7 @@ func (c *Controller) Start(ctx context.Context) error {
|
||||
|
||||
// processNextWorkItem will read a single work item off the workqueue and
|
||||
// attempt to process it, by calling the reconcileHandler.
|
||||
func (c *Controller) processNextWorkItem(ctx context.Context) bool {
|
||||
func (c *Controller[request]) processNextWorkItem(ctx context.Context) bool {
|
||||
obj, shutdown := c.Queue.Get()
|
||||
if shutdown {
|
||||
// Stop working
|
||||
@@ -274,35 +271,25 @@ const (
|
||||
labelSuccess = "success"
|
||||
)
|
||||
|
||||
func (c *Controller) initMetrics() {
|
||||
ctrlmetrics.ActiveWorkers.WithLabelValues(c.Name).Set(0)
|
||||
ctrlmetrics.ReconcileErrors.WithLabelValues(c.Name).Add(0)
|
||||
func (c *Controller[request]) initMetrics() {
|
||||
ctrlmetrics.ReconcileTotal.WithLabelValues(c.Name, labelError).Add(0)
|
||||
ctrlmetrics.ReconcileTotal.WithLabelValues(c.Name, labelRequeueAfter).Add(0)
|
||||
ctrlmetrics.ReconcileTotal.WithLabelValues(c.Name, labelRequeue).Add(0)
|
||||
ctrlmetrics.ReconcileTotal.WithLabelValues(c.Name, labelSuccess).Add(0)
|
||||
ctrlmetrics.ReconcileErrors.WithLabelValues(c.Name).Add(0)
|
||||
ctrlmetrics.TerminalReconcileErrors.WithLabelValues(c.Name).Add(0)
|
||||
ctrlmetrics.ReconcilePanics.WithLabelValues(c.Name).Add(0)
|
||||
ctrlmetrics.WorkerCount.WithLabelValues(c.Name).Set(float64(c.MaxConcurrentReconciles))
|
||||
ctrlmetrics.ActiveWorkers.WithLabelValues(c.Name).Set(0)
|
||||
}
|
||||
|
||||
func (c *Controller) reconcileHandler(ctx context.Context, obj interface{}) {
|
||||
func (c *Controller[request]) reconcileHandler(ctx context.Context, req request) {
|
||||
// Update metrics after processing each item
|
||||
reconcileStartTS := time.Now()
|
||||
defer func() {
|
||||
c.updateMetrics(time.Since(reconcileStartTS))
|
||||
}()
|
||||
|
||||
// Make sure that the object is a valid request.
|
||||
req, ok := obj.(reconcile.Request)
|
||||
if !ok {
|
||||
// As the item in the workqueue is actually invalid, we call
|
||||
// Forget here else we'd go into a loop of attempting to
|
||||
// process a work item that is invalid.
|
||||
c.Queue.Forget(obj)
|
||||
c.LogConstructor(nil).Error(nil, "Queue item was not a Request", "type", fmt.Sprintf("%T", obj), "value", obj)
|
||||
// Return true, don't take a break
|
||||
return
|
||||
}
|
||||
|
||||
log := c.LogConstructor(&req)
|
||||
reconcileID := uuid.NewUUID()
|
||||
|
||||
@@ -333,7 +320,7 @@ func (c *Controller) reconcileHandler(ctx context.Context, obj interface{}) {
|
||||
// along with a non-nil error. But this is intended as
|
||||
// We need to drive to stable reconcile loops before queuing due
|
||||
// to result.RequestAfter
|
||||
c.Queue.Forget(obj)
|
||||
c.Queue.Forget(req)
|
||||
c.Queue.AddAfter(req, result.RequeueAfter)
|
||||
ctrlmetrics.ReconcileTotal.WithLabelValues(c.Name, labelRequeueAfter).Inc()
|
||||
case result.Requeue:
|
||||
@@ -344,18 +331,18 @@ func (c *Controller) reconcileHandler(ctx context.Context, obj interface{}) {
|
||||
log.V(5).Info("Reconcile successful")
|
||||
// Finally, if no error occurs we Forget this item so it does not
|
||||
// get queued again until another change happens.
|
||||
c.Queue.Forget(obj)
|
||||
c.Queue.Forget(req)
|
||||
ctrlmetrics.ReconcileTotal.WithLabelValues(c.Name, labelSuccess).Inc()
|
||||
}
|
||||
}
|
||||
|
||||
// GetLogger returns this controller's logger.
|
||||
func (c *Controller) GetLogger() logr.Logger {
|
||||
func (c *Controller[request]) GetLogger() logr.Logger {
|
||||
return c.LogConstructor(nil)
|
||||
}
|
||||
|
||||
// updateMetrics updates prometheus metrics within the controller.
|
||||
func (c *Controller) updateMetrics(reconcileTime time.Duration) {
|
||||
func (c *Controller[request]) updateMetrics(reconcileTime time.Duration) {
|
||||
ctrlmetrics.ReconcileTime.WithLabelValues(c.Name).Observe(reconcileTime.Seconds())
|
||||
}
|
||||
|
||||
|
||||
8
vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/metrics/metrics.go
generated
vendored
8
vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/metrics/metrics.go
generated
vendored
@@ -46,6 +46,13 @@ var (
|
||||
Help: "Total number of terminal reconciliation errors per controller",
|
||||
}, []string{"controller"})
|
||||
|
||||
// ReconcilePanics is a prometheus counter metrics which holds the total
|
||||
// number of panics from the Reconciler.
|
||||
ReconcilePanics = prometheus.NewCounterVec(prometheus.CounterOpts{
|
||||
Name: "controller_runtime_reconcile_panics_total",
|
||||
Help: "Total number of reconciliation panics per controller",
|
||||
}, []string{"controller"})
|
||||
|
||||
// ReconcileTime is a prometheus metric which keeps track of the duration
|
||||
// of reconciliations.
|
||||
ReconcileTime = prometheus.NewHistogramVec(prometheus.HistogramOpts{
|
||||
@@ -75,6 +82,7 @@ func init() {
|
||||
ReconcileTotal,
|
||||
ReconcileErrors,
|
||||
TerminalReconcileErrors,
|
||||
ReconcilePanics,
|
||||
ReconcileTime,
|
||||
WorkerCount,
|
||||
ActiveWorkers,
|
||||
|
||||
38
vendor/sigs.k8s.io/controller-runtime/pkg/internal/source/event_handler.go
generated
vendored
38
vendor/sigs.k8s.io/controller-runtime/pkg/internal/source/event_handler.go
generated
vendored
@@ -33,8 +33,12 @@ import (
|
||||
var log = logf.RuntimeLog.WithName("source").WithName("EventHandler")
|
||||
|
||||
// NewEventHandler creates a new EventHandler.
|
||||
func NewEventHandler(ctx context.Context, queue workqueue.RateLimitingInterface, handler handler.EventHandler, predicates []predicate.Predicate) *EventHandler {
|
||||
return &EventHandler{
|
||||
func NewEventHandler[object client.Object, request comparable](
|
||||
ctx context.Context,
|
||||
queue workqueue.TypedRateLimitingInterface[request],
|
||||
handler handler.TypedEventHandler[object, request],
|
||||
predicates []predicate.TypedPredicate[object]) *EventHandler[object, request] {
|
||||
return &EventHandler[object, request]{
|
||||
ctx: ctx,
|
||||
handler: handler,
|
||||
queue: queue,
|
||||
@@ -43,19 +47,19 @@ func NewEventHandler(ctx context.Context, queue workqueue.RateLimitingInterface,
|
||||
}
|
||||
|
||||
// EventHandler adapts a handler.EventHandler interface to a cache.ResourceEventHandler interface.
|
||||
type EventHandler struct {
|
||||
type EventHandler[object client.Object, request comparable] struct {
|
||||
// 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
|
||||
handler handler.TypedEventHandler[object, request]
|
||||
queue workqueue.TypedRateLimitingInterface[request]
|
||||
predicates []predicate.TypedPredicate[object]
|
||||
}
|
||||
|
||||
// HandlerFuncs converts EventHandler to a ResourceEventHandlerFuncs
|
||||
// TODO: switch to ResourceEventHandlerDetailedFuncs with client-go 1.27
|
||||
func (e *EventHandler) HandlerFuncs() cache.ResourceEventHandlerFuncs {
|
||||
func (e *EventHandler[object, request]) HandlerFuncs() cache.ResourceEventHandlerFuncs {
|
||||
return cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: e.OnAdd,
|
||||
UpdateFunc: e.OnUpdate,
|
||||
@@ -64,11 +68,11 @@ func (e *EventHandler) HandlerFuncs() cache.ResourceEventHandlerFuncs {
|
||||
}
|
||||
|
||||
// OnAdd creates CreateEvent and calls Create on EventHandler.
|
||||
func (e *EventHandler) OnAdd(obj interface{}) {
|
||||
c := event.CreateEvent{}
|
||||
func (e *EventHandler[object, request]) OnAdd(obj interface{}) {
|
||||
c := event.TypedCreateEvent[object]{}
|
||||
|
||||
// Pull Object out of the object
|
||||
if o, ok := obj.(client.Object); ok {
|
||||
if o, ok := obj.(object); ok {
|
||||
c.Object = o
|
||||
} else {
|
||||
log.Error(nil, "OnAdd missing Object",
|
||||
@@ -89,10 +93,10 @@ func (e *EventHandler) OnAdd(obj interface{}) {
|
||||
}
|
||||
|
||||
// OnUpdate creates UpdateEvent and calls Update on EventHandler.
|
||||
func (e *EventHandler) OnUpdate(oldObj, newObj interface{}) {
|
||||
u := event.UpdateEvent{}
|
||||
func (e *EventHandler[object, request]) OnUpdate(oldObj, newObj interface{}) {
|
||||
u := event.TypedUpdateEvent[object]{}
|
||||
|
||||
if o, ok := oldObj.(client.Object); ok {
|
||||
if o, ok := oldObj.(object); ok {
|
||||
u.ObjectOld = o
|
||||
} else {
|
||||
log.Error(nil, "OnUpdate missing ObjectOld",
|
||||
@@ -101,7 +105,7 @@ func (e *EventHandler) OnUpdate(oldObj, newObj interface{}) {
|
||||
}
|
||||
|
||||
// Pull Object out of the object
|
||||
if o, ok := newObj.(client.Object); ok {
|
||||
if o, ok := newObj.(object); ok {
|
||||
u.ObjectNew = o
|
||||
} else {
|
||||
log.Error(nil, "OnUpdate missing ObjectNew",
|
||||
@@ -122,8 +126,8 @@ func (e *EventHandler) OnUpdate(oldObj, newObj interface{}) {
|
||||
}
|
||||
|
||||
// OnDelete creates DeleteEvent and calls Delete on EventHandler.
|
||||
func (e *EventHandler) OnDelete(obj interface{}) {
|
||||
d := event.DeleteEvent{}
|
||||
func (e *EventHandler[object, request]) OnDelete(obj interface{}) {
|
||||
d := event.TypedDeleteEvent[object]{}
|
||||
|
||||
// Deal with tombstone events by pulling the object out. Tombstone events wrap the object in a
|
||||
// DeleteFinalStateUnknown struct, so the object needs to be pulled out.
|
||||
@@ -149,7 +153,7 @@ func (e *EventHandler) OnDelete(obj interface{}) {
|
||||
}
|
||||
|
||||
// Pull Object out of the object
|
||||
if o, ok := obj.(client.Object); ok {
|
||||
if o, ok := obj.(object); ok {
|
||||
d.Object = o
|
||||
} else {
|
||||
log.Error(nil, "OnDelete missing Object",
|
||||
|
||||
58
vendor/sigs.k8s.io/controller-runtime/pkg/internal/source/kind.go
generated
vendored
58
vendor/sigs.k8s.io/controller-runtime/pkg/internal/source/kind.go
generated
vendored
@@ -4,12 +4,14 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"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"
|
||||
@@ -17,34 +19,40 @@ import (
|
||||
)
|
||||
|
||||
// Kind is used to provide a source of events originating inside the cluster from Watches (e.g. Pod Create).
|
||||
type Kind struct {
|
||||
type Kind[object client.Object, request comparable] struct {
|
||||
// Type is the type of object to watch. e.g. &v1.Pod{}
|
||||
Type client.Object
|
||||
Type 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
|
||||
Handler handler.TypedEventHandler[object, request]
|
||||
|
||||
Predicates []predicate.TypedPredicate[object]
|
||||
|
||||
// startedErr 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
|
||||
startedErr 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 {
|
||||
func (ks *Kind[object, request]) Start(ctx context.Context, queue workqueue.TypedRateLimitingInterface[request]) error {
|
||||
if isNil(ks.Type) {
|
||||
return fmt.Errorf("must create Kind with a non-nil object")
|
||||
}
|
||||
if ks.Cache == nil {
|
||||
if isNil(ks.Cache) {
|
||||
return fmt.Errorf("must create Kind with a non-nil cache")
|
||||
}
|
||||
if isNil(ks.Handler) {
|
||||
return errors.New("must create Kind with non-nil handler")
|
||||
}
|
||||
|
||||
// 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)
|
||||
ks.startedErr = make(chan error)
|
||||
go func() {
|
||||
var (
|
||||
i cache.Informer
|
||||
@@ -72,30 +80,30 @@ func (ks *Kind) Start(ctx context.Context, handler handler.EventHandler, queue w
|
||||
return true, nil
|
||||
}); err != nil {
|
||||
if lastErr != nil {
|
||||
ks.started <- fmt.Errorf("failed to get informer from cache: %w", lastErr)
|
||||
ks.startedErr <- fmt.Errorf("failed to get informer from cache: %w", lastErr)
|
||||
return
|
||||
}
|
||||
ks.started <- err
|
||||
ks.startedErr <- err
|
||||
return
|
||||
}
|
||||
|
||||
_, err := i.AddEventHandler(NewEventHandler(ctx, queue, handler, prct).HandlerFuncs())
|
||||
_, err := i.AddEventHandler(NewEventHandler(ctx, queue, ks.Handler, ks.Predicates).HandlerFuncs())
|
||||
if err != nil {
|
||||
ks.started <- err
|
||||
ks.startedErr <- err
|
||||
return
|
||||
}
|
||||
if !ks.Cache.WaitForCacheSync(ctx) {
|
||||
// Would be great to return something more informative here
|
||||
ks.started <- errors.New("cache did not sync")
|
||||
ks.startedErr <- errors.New("cache did not sync")
|
||||
}
|
||||
close(ks.started)
|
||||
close(ks.startedErr)
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ks *Kind) String() string {
|
||||
if ks.Type != nil {
|
||||
func (ks *Kind[object, request]) String() string {
|
||||
if !isNil(ks.Type) {
|
||||
return fmt.Sprintf("kind source: %T", ks.Type)
|
||||
}
|
||||
return "kind source: unknown type"
|
||||
@@ -103,9 +111,9 @@ func (ks *Kind) String() string {
|
||||
|
||||
// WaitForSync implements SyncingSource to allow controllers to wait with starting
|
||||
// workers until the cache is synced.
|
||||
func (ks *Kind) WaitForSync(ctx context.Context) error {
|
||||
func (ks *Kind[object, request]) WaitForSync(ctx context.Context) error {
|
||||
select {
|
||||
case err := <-ks.started:
|
||||
case err := <-ks.startedErr:
|
||||
return err
|
||||
case <-ctx.Done():
|
||||
ks.startCancel()
|
||||
@@ -115,3 +123,15 @@ func (ks *Kind) WaitForSync(ctx context.Context) error {
|
||||
return fmt.Errorf("timed out waiting for cache to be synced for Kind %T", ks.Type)
|
||||
}
|
||||
}
|
||||
|
||||
func isNil(arg any) bool {
|
||||
if v := reflect.ValueOf(arg); !v.IsValid() || ((v.Kind() == reflect.Ptr ||
|
||||
v.Kind() == reflect.Interface ||
|
||||
v.Kind() == reflect.Slice ||
|
||||
v.Kind() == reflect.Map ||
|
||||
v.Kind() == reflect.Chan ||
|
||||
v.Kind() == reflect.Func) && v.IsNil()) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
110
vendor/sigs.k8s.io/controller-runtime/pkg/manager/internal.go
generated
vendored
110
vendor/sigs.k8s.io/controller-runtime/pkg/manager/internal.go
generated
vendored
@@ -179,6 +179,24 @@ func (cm *controllerManager) add(r Runnable) error {
|
||||
return cm.runnables.Add(r)
|
||||
}
|
||||
|
||||
// AddMetricsServerExtraHandler adds extra handler served on path to the http server that serves metrics.
|
||||
func (cm *controllerManager) AddMetricsServerExtraHandler(path string, handler http.Handler) error {
|
||||
cm.Lock()
|
||||
defer cm.Unlock()
|
||||
if cm.started {
|
||||
return fmt.Errorf("unable to add new metrics handler because metrics endpoint has already been created")
|
||||
}
|
||||
if cm.metricsServer == nil {
|
||||
cm.GetLogger().Info("warn: metrics server is currently disabled, registering extra handler will be ignored", "path", path)
|
||||
return nil
|
||||
}
|
||||
if err := cm.metricsServer.AddExtraHandler(path, handler); err != nil {
|
||||
return err
|
||||
}
|
||||
cm.logger.V(2).Info("Registering metrics http server extra handler", "path", path)
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddHealthzCheck allows you to add Healthz checker.
|
||||
func (cm *controllerManager) AddHealthzCheck(name string, check healthz.Checker) error {
|
||||
cm.Lock()
|
||||
@@ -284,9 +302,8 @@ func (cm *controllerManager) addHealthProbeServer() error {
|
||||
mux.Handle(cm.livenessEndpointName+"/", http.StripPrefix(cm.livenessEndpointName, cm.healthzHandler))
|
||||
}
|
||||
|
||||
return cm.add(&server{
|
||||
Kind: "health probe",
|
||||
Log: cm.logger,
|
||||
return cm.add(&Server{
|
||||
Name: "health probe",
|
||||
Server: srv,
|
||||
Listener: cm.healthProbeListener,
|
||||
})
|
||||
@@ -302,9 +319,8 @@ func (cm *controllerManager) addPprofServer() error {
|
||||
mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
|
||||
mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
|
||||
|
||||
return cm.add(&server{
|
||||
Kind: "pprof",
|
||||
Log: cm.logger,
|
||||
return cm.add(&Server{
|
||||
Name: "pprof",
|
||||
Server: srv,
|
||||
Listener: cm.pprofListener,
|
||||
})
|
||||
@@ -335,6 +351,16 @@ func (cm *controllerManager) Start(ctx context.Context) (err error) {
|
||||
// Initialize the internal context.
|
||||
cm.internalCtx, cm.internalCancel = context.WithCancel(ctx)
|
||||
|
||||
// Leader elector must be created before defer that contains engageStopProcedure function
|
||||
// https://github.com/kubernetes-sigs/controller-runtime/issues/2873
|
||||
var leaderElector *leaderelection.LeaderElector
|
||||
if cm.resourceLock != nil {
|
||||
leaderElector, err = cm.initLeaderElector()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed during initialization leader election process: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// This chan indicates that stop is complete, in other words all runnables have returned or timeout on stop request
|
||||
stopComplete := make(chan struct{})
|
||||
defer close(stopComplete)
|
||||
@@ -384,14 +410,13 @@ func (cm *controllerManager) Start(ctx context.Context) (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
// First start any internal HTTP servers, which includes health probes, metrics and profiling if enabled.
|
||||
// First start any HTTP servers, which includes health probes, metrics and profiling if enabled.
|
||||
//
|
||||
// WARNING: Internal HTTP servers MUST start before any cache is populated, otherwise it would block
|
||||
// conversion webhooks to be ready for serving which make the cache never get ready.
|
||||
if err := cm.runnables.HTTPServers.Start(cm.internalCtx); err != nil {
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start HTTP servers: %w", err)
|
||||
}
|
||||
// WARNING: HTTPServers includes the health probes, which MUST start before any cache is populated, otherwise
|
||||
// it would block conversion webhooks to be ready for serving which make the cache never get ready.
|
||||
logCtx := logr.NewContext(cm.internalCtx, cm.logger)
|
||||
if err := cm.runnables.HTTPServers.Start(logCtx); err != nil {
|
||||
return fmt.Errorf("failed to start HTTP servers: %w", err)
|
||||
}
|
||||
|
||||
// Start any webhook servers, which includes conversion, validation, and defaulting
|
||||
@@ -401,42 +426,39 @@ 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 err != nil {
|
||||
return fmt.Errorf("failed to start webhooks: %w", err)
|
||||
}
|
||||
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 err != nil {
|
||||
return fmt.Errorf("failed to start caches: %w", err)
|
||||
}
|
||||
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 err != nil {
|
||||
return fmt.Errorf("failed to start other runnables: %w", err)
|
||||
}
|
||||
return fmt.Errorf("failed to start other runnables: %w", err)
|
||||
}
|
||||
|
||||
// Start the leader election and all required runnables.
|
||||
{
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cm.leaderElectionCancel = cancel
|
||||
go func() {
|
||||
if cm.resourceLock != nil {
|
||||
if err := cm.startLeaderElection(ctx); err != nil {
|
||||
cm.errChan <- err
|
||||
}
|
||||
} else {
|
||||
if leaderElector != nil {
|
||||
// Start the leader elector process
|
||||
go func() {
|
||||
leaderElector.Run(ctx)
|
||||
<-ctx.Done()
|
||||
close(cm.leaderElectionStopped)
|
||||
}()
|
||||
} else {
|
||||
go func() {
|
||||
// Treat not having leader election enabled the same as being elected.
|
||||
if err := cm.startLeaderElectionRunnables(); err != nil {
|
||||
cm.errChan <- err
|
||||
}
|
||||
close(cm.elected)
|
||||
}
|
||||
}()
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
ready = true
|
||||
@@ -485,8 +507,8 @@ func (cm *controllerManager) engageStopProcedure(stopComplete <-chan struct{}) e
|
||||
cm.internalCancel()
|
||||
})
|
||||
select {
|
||||
case err, ok := <-cm.errChan:
|
||||
if ok {
|
||||
case err := <-cm.errChan:
|
||||
if !errors.Is(err, context.Canceled) {
|
||||
cm.logger.Error(err, "error received after stop sequence was engaged")
|
||||
}
|
||||
case <-stopComplete:
|
||||
@@ -518,6 +540,8 @@ func (cm *controllerManager) engageStopProcedure(stopComplete <-chan struct{}) e
|
||||
|
||||
// Stop all the leader election runnables, which includes reconcilers.
|
||||
cm.logger.Info("Stopping and waiting for leader election runnables")
|
||||
// Prevent leader election when shutting down a non-elected manager
|
||||
cm.runnables.LeaderElection.startOnce.Do(func() {})
|
||||
cm.runnables.LeaderElection.StopAndWait(cm.shutdownCtx)
|
||||
|
||||
// Stop the caches before the leader election runnables, this is an important
|
||||
@@ -553,12 +577,8 @@ func (cm *controllerManager) engageStopProcedure(stopComplete <-chan struct{}) e
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cm *controllerManager) startLeaderElectionRunnables() error {
|
||||
return cm.runnables.LeaderElection.Start(cm.internalCtx)
|
||||
}
|
||||
|
||||
func (cm *controllerManager) startLeaderElection(ctx context.Context) (err error) {
|
||||
l, err := leaderelection.NewLeaderElector(leaderelection.LeaderElectionConfig{
|
||||
func (cm *controllerManager) initLeaderElector() (*leaderelection.LeaderElector, error) {
|
||||
leaderElector, err := leaderelection.NewLeaderElector(leaderelection.LeaderElectionConfig{
|
||||
Lock: cm.resourceLock,
|
||||
LeaseDuration: cm.leaseDuration,
|
||||
RenewDeadline: cm.renewDeadline,
|
||||
@@ -588,16 +608,14 @@ func (cm *controllerManager) startLeaderElection(ctx context.Context) (err error
|
||||
Name: cm.leaderElectionID,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Start the leader elector process
|
||||
go func() {
|
||||
l.Run(ctx)
|
||||
<-ctx.Done()
|
||||
close(cm.leaderElectionStopped)
|
||||
}()
|
||||
return nil
|
||||
return leaderElector, nil
|
||||
}
|
||||
|
||||
func (cm *controllerManager) startLeaderElectionRunnables() error {
|
||||
return cm.runnables.LeaderElection.Start(cm.internalCtx)
|
||||
}
|
||||
|
||||
func (cm *controllerManager) Elected() <-chan struct{} {
|
||||
|
||||
132
vendor/sigs.k8s.io/controller-runtime/pkg/manager/manager.go
generated
vendored
132
vendor/sigs.k8s.io/controller-runtime/pkg/manager/manager.go
generated
vendored
@@ -22,14 +22,12 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
coordinationv1 "k8s.io/api/coordination/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/leaderelection/resourcelock"
|
||||
@@ -41,7 +39,6 @@ import (
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/cluster"
|
||||
"sigs.k8s.io/controller-runtime/pkg/config"
|
||||
"sigs.k8s.io/controller-runtime/pkg/config/v1alpha1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/healthz"
|
||||
intrec "sigs.k8s.io/controller-runtime/pkg/internal/recorder"
|
||||
"sigs.k8s.io/controller-runtime/pkg/leaderelection"
|
||||
@@ -67,6 +64,15 @@ type Manager interface {
|
||||
// election was configured.
|
||||
Elected() <-chan struct{}
|
||||
|
||||
// AddMetricsServerExtraHandler adds an extra handler served on path to the http server that serves metrics.
|
||||
// Might be useful to register some diagnostic endpoints e.g. pprof.
|
||||
//
|
||||
// Note that these endpoints are meant to be sensitive and shouldn't be exposed publicly.
|
||||
//
|
||||
// If the simple path -> handler mapping offered here is not enough,
|
||||
// a new http server/listener should be added as Runnable to the manager via Add method.
|
||||
AddMetricsServerExtraHandler(path string, handler http.Handler) error
|
||||
|
||||
// AddHealthzCheck allows you to add Healthz checker
|
||||
AddHealthzCheck(name string, check healthz.Checker) error
|
||||
|
||||
@@ -438,126 +444,6 @@ func New(config *rest.Config, options Options) (Manager, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 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) {
|
||||
newObj, err := loader.Complete()
|
||||
if err != nil {
|
||||
return o, err
|
||||
}
|
||||
|
||||
o = o.setLeaderElectionConfig(newObj)
|
||||
|
||||
if o.Cache.SyncPeriod == nil && newObj.SyncPeriod != nil {
|
||||
o.Cache.SyncPeriod = &newObj.SyncPeriod.Duration
|
||||
}
|
||||
|
||||
if len(o.Cache.DefaultNamespaces) == 0 && newObj.CacheNamespace != "" {
|
||||
o.Cache.DefaultNamespaces = map[string]cache.Config{newObj.CacheNamespace: {}}
|
||||
}
|
||||
|
||||
if o.Metrics.BindAddress == "" && newObj.Metrics.BindAddress != "" {
|
||||
o.Metrics.BindAddress = newObj.Metrics.BindAddress
|
||||
}
|
||||
|
||||
if o.HealthProbeBindAddress == "" && newObj.Health.HealthProbeBindAddress != "" {
|
||||
o.HealthProbeBindAddress = newObj.Health.HealthProbeBindAddress
|
||||
}
|
||||
|
||||
if o.ReadinessEndpointName == "" && newObj.Health.ReadinessEndpointName != "" {
|
||||
o.ReadinessEndpointName = newObj.Health.ReadinessEndpointName
|
||||
}
|
||||
|
||||
if o.LivenessEndpointName == "" && newObj.Health.LivenessEndpointName != "" {
|
||||
o.LivenessEndpointName = newObj.Health.LivenessEndpointName
|
||||
}
|
||||
|
||||
if o.WebhookServer == nil {
|
||||
port := 0
|
||||
if newObj.Webhook.Port != nil {
|
||||
port = *newObj.Webhook.Port
|
||||
}
|
||||
o.WebhookServer = webhook.NewServer(webhook.Options{
|
||||
Port: port,
|
||||
Host: newObj.Webhook.Host,
|
||||
CertDir: newObj.Webhook.CertDir,
|
||||
})
|
||||
}
|
||||
|
||||
if newObj.Controller != nil {
|
||||
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 {
|
||||
o.Controller.GroupKindConcurrency = newObj.Controller.GroupKindConcurrency
|
||||
}
|
||||
}
|
||||
|
||||
return o, nil
|
||||
}
|
||||
|
||||
// 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 {
|
||||
panic(fmt.Sprintf("could not parse config file: %v", err))
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
func (o Options) setLeaderElectionConfig(obj v1alpha1.ControllerManagerConfigurationSpec) Options {
|
||||
if obj.LeaderElection == nil {
|
||||
// The source does not have any configuration; noop
|
||||
return o
|
||||
}
|
||||
|
||||
if !o.LeaderElection && obj.LeaderElection.LeaderElect != nil {
|
||||
o.LeaderElection = *obj.LeaderElection.LeaderElect
|
||||
}
|
||||
|
||||
if o.LeaderElectionResourceLock == "" && obj.LeaderElection.ResourceLock != "" {
|
||||
o.LeaderElectionResourceLock = obj.LeaderElection.ResourceLock
|
||||
}
|
||||
|
||||
if o.LeaderElectionNamespace == "" && obj.LeaderElection.ResourceNamespace != "" {
|
||||
o.LeaderElectionNamespace = obj.LeaderElection.ResourceNamespace
|
||||
}
|
||||
|
||||
if o.LeaderElectionID == "" && obj.LeaderElection.ResourceName != "" {
|
||||
o.LeaderElectionID = obj.LeaderElection.ResourceName
|
||||
}
|
||||
|
||||
if o.LeaseDuration == nil && !reflect.DeepEqual(obj.LeaderElection.LeaseDuration, metav1.Duration{}) {
|
||||
o.LeaseDuration = &obj.LeaderElection.LeaseDuration.Duration
|
||||
}
|
||||
|
||||
if o.RenewDeadline == nil && !reflect.DeepEqual(obj.LeaderElection.RenewDeadline, metav1.Duration{}) {
|
||||
o.RenewDeadline = &obj.LeaderElection.RenewDeadline.Duration
|
||||
}
|
||||
|
||||
if o.RetryPeriod == nil && !reflect.DeepEqual(obj.LeaderElection.RetryPeriod, metav1.Duration{}) {
|
||||
o.RetryPeriod = &obj.LeaderElection.RetryPeriod.Duration
|
||||
}
|
||||
|
||||
return o
|
||||
}
|
||||
|
||||
// defaultHealthProbeListener creates the default health probes listener bound to the given address.
|
||||
func defaultHealthProbeListener(addr string) (net.Listener, error) {
|
||||
if addr == "" || addr == "0" {
|
||||
|
||||
20
vendor/sigs.k8s.io/controller-runtime/pkg/manager/runnable_group.go
generated
vendored
20
vendor/sigs.k8s.io/controller-runtime/pkg/manager/runnable_group.go
generated
vendored
@@ -54,7 +54,10 @@ func newRunnables(baseContext BaseContextFunc, errChan chan error) *runnables {
|
||||
// The runnables added after Start are started directly.
|
||||
func (r *runnables) Add(fn Runnable) error {
|
||||
switch runnable := fn.(type) {
|
||||
case *server:
|
||||
case *Server:
|
||||
if runnable.NeedLeaderElection() {
|
||||
return r.LeaderElection.Add(fn, nil)
|
||||
}
|
||||
return r.HTTPServers.Add(fn, nil)
|
||||
case hasCache:
|
||||
return r.Caches.Add(fn, func(ctx context.Context) bool {
|
||||
@@ -263,6 +266,15 @@ func (r *runnableGroup) Add(rn Runnable, ready runnableCheck) error {
|
||||
r.start.Unlock()
|
||||
}
|
||||
|
||||
// Recheck if we're stopped and hold the readlock, given that the stop and start can be called
|
||||
// at the same time, we can end up in a situation where the runnable is added
|
||||
// after the group is stopped and the channel is closed.
|
||||
r.stop.RLock()
|
||||
defer r.stop.RUnlock()
|
||||
if r.stopped {
|
||||
return errRunnableGroupStopped
|
||||
}
|
||||
|
||||
// Enqueue the runnable.
|
||||
r.ch <- readyRunnable
|
||||
return nil
|
||||
@@ -272,7 +284,11 @@ func (r *runnableGroup) Add(rn Runnable, ready runnableCheck) error {
|
||||
func (r *runnableGroup) StopAndWait(ctx context.Context) {
|
||||
r.stopOnce.Do(func() {
|
||||
// Close the reconciler channel once we're done.
|
||||
defer close(r.ch)
|
||||
defer func() {
|
||||
r.stop.Lock()
|
||||
close(r.ch)
|
||||
r.stop.Unlock()
|
||||
}()
|
||||
|
||||
_ = r.Start(ctx)
|
||||
r.stop.Lock()
|
||||
|
||||
74
vendor/sigs.k8s.io/controller-runtime/pkg/manager/server.go
generated
vendored
74
vendor/sigs.k8s.io/controller-runtime/pkg/manager/server.go
generated
vendored
@@ -21,34 +21,67 @@ import (
|
||||
"errors"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
crlog "sigs.k8s.io/controller-runtime/pkg/log"
|
||||
)
|
||||
|
||||
// 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
|
||||
var (
|
||||
_ Runnable = (*Server)(nil)
|
||||
_ LeaderElectionRunnable = (*Server)(nil)
|
||||
)
|
||||
|
||||
// Server is a general purpose HTTP server Runnable for a manager.
|
||||
// It is used to serve some internal handlers for health probes and profiling,
|
||||
// but it can also be used to run custom servers.
|
||||
type Server struct {
|
||||
// Name is an optional string that describes the purpose of the server. It is used in logs to distinguish
|
||||
// among multiple servers.
|
||||
Name string
|
||||
|
||||
// Server is the HTTP server to run. It is required.
|
||||
Server *http.Server
|
||||
|
||||
// Listener is an optional listener to use. If not set, the server start a listener using the server.Addr.
|
||||
// Using a listener is useful when the port reservation needs to happen in advance of this runnable starting.
|
||||
Listener net.Listener
|
||||
|
||||
// OnlyServeWhenLeader is an optional bool that indicates that the server should only be started when the manager is the leader.
|
||||
OnlyServeWhenLeader bool
|
||||
|
||||
// ShutdownTimeout is an optional duration that indicates how long to wait for the server to shutdown gracefully. If not set,
|
||||
// the server will wait indefinitely for all connections to close.
|
||||
ShutdownTimeout *time.Duration
|
||||
}
|
||||
|
||||
func (s *server) Start(ctx context.Context) error {
|
||||
log := s.Log.WithValues("kind", s.Kind, "addr", s.Listener.Addr())
|
||||
// Start starts the server. It will block until the server is stopped or an error occurs.
|
||||
func (s *Server) Start(ctx context.Context) error {
|
||||
log := crlog.FromContext(ctx)
|
||||
if s.Name != "" {
|
||||
log = log.WithValues("name", s.Name)
|
||||
}
|
||||
log = log.WithValues("addr", s.addr())
|
||||
|
||||
serverShutdown := make(chan struct{})
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
log.Info("shutting down server")
|
||||
if err := s.Server.Shutdown(context.Background()); err != nil {
|
||||
|
||||
shutdownCtx := context.Background()
|
||||
if s.ShutdownTimeout != nil {
|
||||
var shutdownCancel context.CancelFunc
|
||||
shutdownCtx, shutdownCancel = context.WithTimeout(context.Background(), *s.ShutdownTimeout)
|
||||
defer shutdownCancel()
|
||||
}
|
||||
|
||||
if err := s.Server.Shutdown(shutdownCtx); 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) {
|
||||
if err := s.serve(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -56,6 +89,21 @@ func (s *server) Start(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *server) NeedLeaderElection() bool {
|
||||
return false
|
||||
// NeedLeaderElection returns true if the server should only be started when the manager is the leader.
|
||||
func (s *Server) NeedLeaderElection() bool {
|
||||
return s.OnlyServeWhenLeader
|
||||
}
|
||||
|
||||
func (s *Server) addr() string {
|
||||
if s.Listener != nil {
|
||||
return s.Listener.Addr().String()
|
||||
}
|
||||
return s.Server.Addr
|
||||
}
|
||||
|
||||
func (s *Server) serve() error {
|
||||
if s.Listener != nil {
|
||||
return s.Server.Serve(s.Listener)
|
||||
}
|
||||
return s.Server.ListenAndServe()
|
||||
}
|
||||
|
||||
23
vendor/sigs.k8s.io/controller-runtime/pkg/metrics/leaderelection.go
generated
vendored
23
vendor/sigs.k8s.io/controller-runtime/pkg/metrics/leaderelection.go
generated
vendored
@@ -14,6 +14,11 @@ var (
|
||||
Name: "leader_election_master_status",
|
||||
Help: "Gauge of if the reporting system is master of the relevant lease, 0 indicates backup, 1 indicates master. 'name' is the string used to identify the lease. Please make sure to group by name.",
|
||||
}, []string{"name"})
|
||||
|
||||
leaderSlowpathCounter = prometheus.NewCounterVec(prometheus.CounterOpts{
|
||||
Name: "leader_election_slowpath_total",
|
||||
Help: "Total number of slow path exercised in renewing leader leases. 'name' is the string used to identify the lease. Please make sure to group by name.",
|
||||
}, []string{"name"})
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -23,18 +28,20 @@ func init() {
|
||||
|
||||
type leaderelectionMetricsProvider struct{}
|
||||
|
||||
func (leaderelectionMetricsProvider) NewLeaderMetric() leaderelection.SwitchMetric {
|
||||
return &switchAdapter{gauge: leaderGauge}
|
||||
func (leaderelectionMetricsProvider) NewLeaderMetric() leaderelection.LeaderMetric {
|
||||
return leaderElectionPrometheusAdapter{}
|
||||
}
|
||||
|
||||
type switchAdapter struct {
|
||||
gauge *prometheus.GaugeVec
|
||||
type leaderElectionPrometheusAdapter struct{}
|
||||
|
||||
func (s leaderElectionPrometheusAdapter) On(name string) {
|
||||
leaderGauge.WithLabelValues(name).Set(1.0)
|
||||
}
|
||||
|
||||
func (s *switchAdapter) On(name string) {
|
||||
s.gauge.WithLabelValues(name).Set(1.0)
|
||||
func (s leaderElectionPrometheusAdapter) Off(name string) {
|
||||
leaderGauge.WithLabelValues(name).Set(0.0)
|
||||
}
|
||||
|
||||
func (s *switchAdapter) Off(name string) {
|
||||
s.gauge.WithLabelValues(name).Set(0.0)
|
||||
func (leaderElectionPrometheusAdapter) SlowpathExercised(name string) {
|
||||
leaderSlowpathCounter.WithLabelValues(name).Inc()
|
||||
}
|
||||
|
||||
32
vendor/sigs.k8s.io/controller-runtime/pkg/metrics/server/server.go
generated
vendored
32
vendor/sigs.k8s.io/controller-runtime/pkg/metrics/server/server.go
generated
vendored
@@ -46,6 +46,9 @@ var DefaultBindAddress = ":8080"
|
||||
|
||||
// Server is a server that serves metrics.
|
||||
type Server interface {
|
||||
// AddExtraHandler adds extra handler served on path to the http server that serves metrics.
|
||||
AddExtraHandler(path string, handler http.Handler) error
|
||||
|
||||
// NeedLeaderElection implements the LeaderElectionRunnable interface, which indicates
|
||||
// the metrics server doesn't need leader election.
|
||||
NeedLeaderElection() bool
|
||||
@@ -101,6 +104,9 @@ type Options struct {
|
||||
// TLSOpts is used to allow configuring the TLS config used for the server.
|
||||
// This also allows providing a certificate via GetCertificate.
|
||||
TLSOpts []func(*tls.Config)
|
||||
|
||||
// ListenConfig contains options for listening to an address on the metric server.
|
||||
ListenConfig net.ListenConfig
|
||||
}
|
||||
|
||||
// Filter is a func that is added around metrics and extra handlers on the metrics server.
|
||||
@@ -179,6 +185,23 @@ func (*defaultServer) NeedLeaderElection() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// AddExtraHandler adds extra handler served on path to the http server that serves metrics.
|
||||
func (s *defaultServer) AddExtraHandler(path string, handler http.Handler) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
if s.options.ExtraHandlers == nil {
|
||||
s.options.ExtraHandlers = make(map[string]http.Handler)
|
||||
}
|
||||
if path == defaultMetricsEndpoint {
|
||||
return fmt.Errorf("overriding builtin %s endpoint is not allowed", defaultMetricsEndpoint)
|
||||
}
|
||||
if _, found := s.options.ExtraHandlers[path]; found {
|
||||
return fmt.Errorf("can't register extra handler by duplicate path %q on metrics http server", path)
|
||||
}
|
||||
s.options.ExtraHandlers[path] = handler
|
||||
return nil
|
||||
}
|
||||
|
||||
// Start runs the server.
|
||||
// It will install the metrics related resources depend on the server configuration.
|
||||
func (s *defaultServer) Start(ctx context.Context) error {
|
||||
@@ -249,7 +272,7 @@ func (s *defaultServer) Start(ctx context.Context) error {
|
||||
|
||||
func (s *defaultServer) createListener(ctx context.Context, log logr.Logger) (net.Listener, error) {
|
||||
if !s.options.SecureServing {
|
||||
return net.Listen("tcp", s.options.BindAddress)
|
||||
return s.options.ListenConfig.Listen(ctx, "tcp", s.options.BindAddress)
|
||||
}
|
||||
|
||||
cfg := &tls.Config{ //nolint:gosec
|
||||
@@ -302,7 +325,12 @@ func (s *defaultServer) createListener(ctx context.Context, log logr.Logger) (ne
|
||||
cfg.Certificates = []tls.Certificate{keyPair}
|
||||
}
|
||||
|
||||
return tls.Listen("tcp", s.options.BindAddress, cfg)
|
||||
l, err := s.options.ListenConfig.Listen(ctx, "tcp", s.options.BindAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return tls.NewListener(l, cfg), nil
|
||||
}
|
||||
|
||||
func (s *defaultServer) GetBindAddr() string {
|
||||
|
||||
28
vendor/sigs.k8s.io/controller-runtime/pkg/metrics/workqueue.go
generated
vendored
28
vendor/sigs.k8s.io/controller-runtime/pkg/metrics/workqueue.go
generated
vendored
@@ -42,27 +42,27 @@ var (
|
||||
Subsystem: WorkQueueSubsystem,
|
||||
Name: DepthKey,
|
||||
Help: "Current depth of workqueue",
|
||||
}, []string{"name"})
|
||||
}, []string{"name", "controller"})
|
||||
|
||||
adds = prometheus.NewCounterVec(prometheus.CounterOpts{
|
||||
Subsystem: WorkQueueSubsystem,
|
||||
Name: AddsKey,
|
||||
Help: "Total number of adds handled by workqueue",
|
||||
}, []string{"name"})
|
||||
}, []string{"name", "controller"})
|
||||
|
||||
latency = prometheus.NewHistogramVec(prometheus.HistogramOpts{
|
||||
Subsystem: WorkQueueSubsystem,
|
||||
Name: QueueLatencyKey,
|
||||
Help: "How long in seconds an item stays in workqueue before being requested",
|
||||
Buckets: prometheus.ExponentialBuckets(10e-9, 10, 12),
|
||||
}, []string{"name"})
|
||||
}, []string{"name", "controller"})
|
||||
|
||||
workDuration = prometheus.NewHistogramVec(prometheus.HistogramOpts{
|
||||
Subsystem: WorkQueueSubsystem,
|
||||
Name: WorkDurationKey,
|
||||
Help: "How long in seconds processing an item from workqueue takes.",
|
||||
Buckets: prometheus.ExponentialBuckets(10e-9, 10, 12),
|
||||
}, []string{"name"})
|
||||
}, []string{"name", "controller"})
|
||||
|
||||
unfinished = prometheus.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Subsystem: WorkQueueSubsystem,
|
||||
@@ -71,20 +71,20 @@ var (
|
||||
"is in progress and hasn't been observed by work_duration. Large " +
|
||||
"values indicate stuck threads. One can deduce the number of stuck " +
|
||||
"threads by observing the rate at which this increases.",
|
||||
}, []string{"name"})
|
||||
}, []string{"name", "controller"})
|
||||
|
||||
longestRunningProcessor = prometheus.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Subsystem: WorkQueueSubsystem,
|
||||
Name: LongestRunningProcessorKey,
|
||||
Help: "How many seconds has the longest running " +
|
||||
"processor for workqueue been running.",
|
||||
}, []string{"name"})
|
||||
}, []string{"name", "controller"})
|
||||
|
||||
retries = prometheus.NewCounterVec(prometheus.CounterOpts{
|
||||
Subsystem: WorkQueueSubsystem,
|
||||
Name: RetriesKey,
|
||||
Help: "Total number of retries handled by workqueue",
|
||||
}, []string{"name"})
|
||||
}, []string{"name", "controller"})
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -102,29 +102,29 @@ func init() {
|
||||
type workqueueMetricsProvider struct{}
|
||||
|
||||
func (workqueueMetricsProvider) NewDepthMetric(name string) workqueue.GaugeMetric {
|
||||
return depth.WithLabelValues(name)
|
||||
return depth.WithLabelValues(name, name)
|
||||
}
|
||||
|
||||
func (workqueueMetricsProvider) NewAddsMetric(name string) workqueue.CounterMetric {
|
||||
return adds.WithLabelValues(name)
|
||||
return adds.WithLabelValues(name, name)
|
||||
}
|
||||
|
||||
func (workqueueMetricsProvider) NewLatencyMetric(name string) workqueue.HistogramMetric {
|
||||
return latency.WithLabelValues(name)
|
||||
return latency.WithLabelValues(name, name)
|
||||
}
|
||||
|
||||
func (workqueueMetricsProvider) NewWorkDurationMetric(name string) workqueue.HistogramMetric {
|
||||
return workDuration.WithLabelValues(name)
|
||||
return workDuration.WithLabelValues(name, name)
|
||||
}
|
||||
|
||||
func (workqueueMetricsProvider) NewUnfinishedWorkSecondsMetric(name string) workqueue.SettableGaugeMetric {
|
||||
return unfinished.WithLabelValues(name)
|
||||
return unfinished.WithLabelValues(name, name)
|
||||
}
|
||||
|
||||
func (workqueueMetricsProvider) NewLongestRunningProcessorSecondsMetric(name string) workqueue.SettableGaugeMetric {
|
||||
return longestRunningProcessor.WithLabelValues(name)
|
||||
return longestRunningProcessor.WithLabelValues(name, name)
|
||||
}
|
||||
|
||||
func (workqueueMetricsProvider) NewRetriesMetric(name string) workqueue.CounterMetric {
|
||||
return retries.WithLabelValues(name)
|
||||
return retries.WithLabelValues(name, name)
|
||||
}
|
||||
|
||||
192
vendor/sigs.k8s.io/controller-runtime/pkg/predicate/predicate.go
generated
vendored
192
vendor/sigs.k8s.io/controller-runtime/pkg/predicate/predicate.go
generated
vendored
@@ -17,6 +17,7 @@ limitations under the License.
|
||||
package predicate
|
||||
|
||||
import (
|
||||
"maps"
|
||||
"reflect"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -29,45 +30,51 @@ import (
|
||||
var log = logf.RuntimeLog.WithName("predicate").WithName("eventFilters")
|
||||
|
||||
// Predicate filters events before enqueuing the keys.
|
||||
type Predicate interface {
|
||||
type Predicate = TypedPredicate[client.Object]
|
||||
|
||||
// TypedPredicate filters events before enqueuing the keys.
|
||||
type TypedPredicate[object any] interface {
|
||||
// Create returns true if the Create event should be processed
|
||||
Create(event.CreateEvent) bool
|
||||
Create(event.TypedCreateEvent[object]) bool
|
||||
|
||||
// Delete returns true if the Delete event should be processed
|
||||
Delete(event.DeleteEvent) bool
|
||||
Delete(event.TypedDeleteEvent[object]) bool
|
||||
|
||||
// Update returns true if the Update event should be processed
|
||||
Update(event.UpdateEvent) bool
|
||||
Update(event.TypedUpdateEvent[object]) bool
|
||||
|
||||
// Generic returns true if the Generic event should be processed
|
||||
Generic(event.GenericEvent) bool
|
||||
Generic(event.TypedGenericEvent[object]) bool
|
||||
}
|
||||
|
||||
var _ Predicate = Funcs{}
|
||||
var _ Predicate = ResourceVersionChangedPredicate{}
|
||||
var _ Predicate = GenerationChangedPredicate{}
|
||||
var _ Predicate = AnnotationChangedPredicate{}
|
||||
var _ Predicate = or{}
|
||||
var _ Predicate = and{}
|
||||
var _ Predicate = not{}
|
||||
var _ Predicate = or[client.Object]{}
|
||||
var _ Predicate = and[client.Object]{}
|
||||
var _ Predicate = not[client.Object]{}
|
||||
|
||||
// Funcs is a function that implements Predicate.
|
||||
type Funcs struct {
|
||||
type Funcs = TypedFuncs[client.Object]
|
||||
|
||||
// TypedFuncs is a function that implements TypedPredicate.
|
||||
type TypedFuncs[object any] struct {
|
||||
// Create returns true if the Create event should be processed
|
||||
CreateFunc func(event.CreateEvent) bool
|
||||
CreateFunc func(event.TypedCreateEvent[object]) bool
|
||||
|
||||
// Delete returns true if the Delete event should be processed
|
||||
DeleteFunc func(event.DeleteEvent) bool
|
||||
DeleteFunc func(event.TypedDeleteEvent[object]) bool
|
||||
|
||||
// Update returns true if the Update event should be processed
|
||||
UpdateFunc func(event.UpdateEvent) bool
|
||||
UpdateFunc func(event.TypedUpdateEvent[object]) bool
|
||||
|
||||
// Generic returns true if the Generic event should be processed
|
||||
GenericFunc func(event.GenericEvent) bool
|
||||
GenericFunc func(event.TypedGenericEvent[object]) bool
|
||||
}
|
||||
|
||||
// Create implements Predicate.
|
||||
func (p Funcs) Create(e event.CreateEvent) bool {
|
||||
func (p TypedFuncs[object]) Create(e event.TypedCreateEvent[object]) bool {
|
||||
if p.CreateFunc != nil {
|
||||
return p.CreateFunc(e)
|
||||
}
|
||||
@@ -75,7 +82,7 @@ func (p Funcs) Create(e event.CreateEvent) bool {
|
||||
}
|
||||
|
||||
// Delete implements Predicate.
|
||||
func (p Funcs) Delete(e event.DeleteEvent) bool {
|
||||
func (p TypedFuncs[object]) Delete(e event.TypedDeleteEvent[object]) bool {
|
||||
if p.DeleteFunc != nil {
|
||||
return p.DeleteFunc(e)
|
||||
}
|
||||
@@ -83,7 +90,7 @@ func (p Funcs) Delete(e event.DeleteEvent) bool {
|
||||
}
|
||||
|
||||
// Update implements Predicate.
|
||||
func (p Funcs) Update(e event.UpdateEvent) bool {
|
||||
func (p TypedFuncs[object]) Update(e event.TypedUpdateEvent[object]) bool {
|
||||
if p.UpdateFunc != nil {
|
||||
return p.UpdateFunc(e)
|
||||
}
|
||||
@@ -91,7 +98,7 @@ func (p Funcs) Update(e event.UpdateEvent) bool {
|
||||
}
|
||||
|
||||
// Generic implements Predicate.
|
||||
func (p Funcs) Generic(e event.GenericEvent) bool {
|
||||
func (p TypedFuncs[object]) Generic(e event.TypedGenericEvent[object]) bool {
|
||||
if p.GenericFunc != nil {
|
||||
return p.GenericFunc(e)
|
||||
}
|
||||
@@ -118,18 +125,41 @@ func NewPredicateFuncs(filter func(object client.Object) bool) Funcs {
|
||||
}
|
||||
}
|
||||
|
||||
// NewTypedPredicateFuncs returns a predicate funcs that applies the given filter function
|
||||
// on CREATE, UPDATE, DELETE and GENERIC events. For UPDATE events, the filter is applied
|
||||
// to the new object.
|
||||
func NewTypedPredicateFuncs[object any](filter func(object object) bool) TypedFuncs[object] {
|
||||
return TypedFuncs[object]{
|
||||
CreateFunc: func(e event.TypedCreateEvent[object]) bool {
|
||||
return filter(e.Object)
|
||||
},
|
||||
UpdateFunc: func(e event.TypedUpdateEvent[object]) bool {
|
||||
return filter(e.ObjectNew)
|
||||
},
|
||||
DeleteFunc: func(e event.TypedDeleteEvent[object]) bool {
|
||||
return filter(e.Object)
|
||||
},
|
||||
GenericFunc: func(e event.TypedGenericEvent[object]) bool {
|
||||
return filter(e.Object)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// ResourceVersionChangedPredicate implements a default update predicate function on resource version change.
|
||||
type ResourceVersionChangedPredicate struct {
|
||||
Funcs
|
||||
type ResourceVersionChangedPredicate = TypedResourceVersionChangedPredicate[client.Object]
|
||||
|
||||
// TypedResourceVersionChangedPredicate implements a default update predicate function on resource version change.
|
||||
type TypedResourceVersionChangedPredicate[T metav1.Object] struct {
|
||||
TypedFuncs[T]
|
||||
}
|
||||
|
||||
// Update implements default UpdateEvent filter for validating resource version change.
|
||||
func (ResourceVersionChangedPredicate) Update(e event.UpdateEvent) bool {
|
||||
if e.ObjectOld == nil {
|
||||
func (TypedResourceVersionChangedPredicate[T]) Update(e event.TypedUpdateEvent[T]) bool {
|
||||
if isNil(e.ObjectOld) {
|
||||
log.Error(nil, "Update event has no old object to update", "event", e)
|
||||
return false
|
||||
}
|
||||
if e.ObjectNew == nil {
|
||||
if isNil(e.ObjectNew) {
|
||||
log.Error(nil, "Update event has no new object to update", "event", e)
|
||||
return false
|
||||
}
|
||||
@@ -153,17 +183,35 @@ func (ResourceVersionChangedPredicate) Update(e event.UpdateEvent) bool {
|
||||
//
|
||||
// * With this predicate, any update events with writes only to the status field will not be reconciled.
|
||||
// So in the event that the status block is overwritten or wiped by someone else the controller will not self-correct to restore the correct status.
|
||||
type GenerationChangedPredicate struct {
|
||||
Funcs
|
||||
type GenerationChangedPredicate = TypedGenerationChangedPredicate[client.Object]
|
||||
|
||||
// TypedGenerationChangedPredicate implements a default update predicate function on Generation change.
|
||||
//
|
||||
// This predicate will skip update events that have no change in the object's metadata.generation field.
|
||||
// The metadata.generation field of an object is incremented by the API server when writes are made to the spec field of an object.
|
||||
// This allows a controller to ignore update events where the spec is unchanged, and only the metadata and/or status fields are changed.
|
||||
//
|
||||
// For CustomResource objects the Generation is only incremented when the status subresource is enabled.
|
||||
//
|
||||
// Caveats:
|
||||
//
|
||||
// * The assumption that the Generation is incremented only on writing to the spec does not hold for all APIs.
|
||||
// E.g For Deployment objects the Generation is also incremented on writes to the metadata.annotations field.
|
||||
// For object types other than CustomResources be sure to verify which fields will trigger a Generation increment when they are written to.
|
||||
//
|
||||
// * With this predicate, any update events with writes only to the status field will not be reconciled.
|
||||
// So in the event that the status block is overwritten or wiped by someone else the controller will not self-correct to restore the correct status.
|
||||
type TypedGenerationChangedPredicate[object metav1.Object] struct {
|
||||
TypedFuncs[object]
|
||||
}
|
||||
|
||||
// Update implements default UpdateEvent filter for validating generation change.
|
||||
func (GenerationChangedPredicate) Update(e event.UpdateEvent) bool {
|
||||
if e.ObjectOld == nil {
|
||||
func (TypedGenerationChangedPredicate[object]) Update(e event.TypedUpdateEvent[object]) bool {
|
||||
if isNil(e.ObjectOld) {
|
||||
log.Error(nil, "Update event has no old object to update", "event", e)
|
||||
return false
|
||||
}
|
||||
if e.ObjectNew == nil {
|
||||
if isNil(e.ObjectNew) {
|
||||
log.Error(nil, "Update event has no new object for update", "event", e)
|
||||
return false
|
||||
}
|
||||
@@ -183,22 +231,25 @@ func (GenerationChangedPredicate) Update(e event.UpdateEvent) bool {
|
||||
//
|
||||
// This is mostly useful for controllers that needs to trigger both when the resource's generation is incremented
|
||||
// (i.e., when the resource' .spec changes), or an annotation changes (e.g., for a staging/alpha API).
|
||||
type AnnotationChangedPredicate struct {
|
||||
Funcs
|
||||
type AnnotationChangedPredicate = TypedAnnotationChangedPredicate[client.Object]
|
||||
|
||||
// TypedAnnotationChangedPredicate implements a default update predicate function on annotation change.
|
||||
type TypedAnnotationChangedPredicate[object metav1.Object] struct {
|
||||
TypedFuncs[object]
|
||||
}
|
||||
|
||||
// Update implements default UpdateEvent filter for validating annotation change.
|
||||
func (AnnotationChangedPredicate) Update(e event.UpdateEvent) bool {
|
||||
if e.ObjectOld == nil {
|
||||
func (TypedAnnotationChangedPredicate[object]) Update(e event.TypedUpdateEvent[object]) bool {
|
||||
if isNil(e.ObjectOld) {
|
||||
log.Error(nil, "Update event has no old object to update", "event", e)
|
||||
return false
|
||||
}
|
||||
if e.ObjectNew == nil {
|
||||
if isNil(e.ObjectNew) {
|
||||
log.Error(nil, "Update event has no new object for update", "event", e)
|
||||
return false
|
||||
}
|
||||
|
||||
return !reflect.DeepEqual(e.ObjectNew.GetAnnotations(), e.ObjectOld.GetAnnotations())
|
||||
return !maps.Equal(e.ObjectNew.GetAnnotations(), e.ObjectOld.GetAnnotations())
|
||||
}
|
||||
|
||||
// LabelChangedPredicate implements a default update predicate function on label change.
|
||||
@@ -214,34 +265,37 @@ func (AnnotationChangedPredicate) Update(e event.UpdateEvent) bool {
|
||||
//
|
||||
// This will be helpful when object's labels is carrying some extra specification information beyond object's spec,
|
||||
// and the controller will be triggered if any valid spec change (not only in spec, but also in labels) happens.
|
||||
type LabelChangedPredicate struct {
|
||||
Funcs
|
||||
type LabelChangedPredicate = TypedLabelChangedPredicate[client.Object]
|
||||
|
||||
// TypedLabelChangedPredicate implements a default update predicate function on label change.
|
||||
type TypedLabelChangedPredicate[object metav1.Object] struct {
|
||||
TypedFuncs[object]
|
||||
}
|
||||
|
||||
// Update implements default UpdateEvent filter for checking label change.
|
||||
func (LabelChangedPredicate) Update(e event.UpdateEvent) bool {
|
||||
if e.ObjectOld == nil {
|
||||
func (TypedLabelChangedPredicate[object]) Update(e event.TypedUpdateEvent[object]) bool {
|
||||
if isNil(e.ObjectOld) {
|
||||
log.Error(nil, "Update event has no old object to update", "event", e)
|
||||
return false
|
||||
}
|
||||
if e.ObjectNew == nil {
|
||||
if isNil(e.ObjectNew) {
|
||||
log.Error(nil, "Update event has no new object for update", "event", e)
|
||||
return false
|
||||
}
|
||||
|
||||
return !reflect.DeepEqual(e.ObjectNew.GetLabels(), e.ObjectOld.GetLabels())
|
||||
return !maps.Equal(e.ObjectNew.GetLabels(), e.ObjectOld.GetLabels())
|
||||
}
|
||||
|
||||
// And returns a composite predicate that implements a logical AND of the predicates passed to it.
|
||||
func And(predicates ...Predicate) Predicate {
|
||||
return and{predicates}
|
||||
func And[object any](predicates ...TypedPredicate[object]) TypedPredicate[object] {
|
||||
return and[object]{predicates}
|
||||
}
|
||||
|
||||
type and struct {
|
||||
predicates []Predicate
|
||||
type and[object any] struct {
|
||||
predicates []TypedPredicate[object]
|
||||
}
|
||||
|
||||
func (a and) Create(e event.CreateEvent) bool {
|
||||
func (a and[object]) Create(e event.TypedCreateEvent[object]) bool {
|
||||
for _, p := range a.predicates {
|
||||
if !p.Create(e) {
|
||||
return false
|
||||
@@ -250,7 +304,7 @@ func (a and) Create(e event.CreateEvent) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (a and) Update(e event.UpdateEvent) bool {
|
||||
func (a and[object]) Update(e event.TypedUpdateEvent[object]) bool {
|
||||
for _, p := range a.predicates {
|
||||
if !p.Update(e) {
|
||||
return false
|
||||
@@ -259,7 +313,7 @@ func (a and) Update(e event.UpdateEvent) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (a and) Delete(e event.DeleteEvent) bool {
|
||||
func (a and[object]) Delete(e event.TypedDeleteEvent[object]) bool {
|
||||
for _, p := range a.predicates {
|
||||
if !p.Delete(e) {
|
||||
return false
|
||||
@@ -268,7 +322,7 @@ func (a and) Delete(e event.DeleteEvent) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (a and) Generic(e event.GenericEvent) bool {
|
||||
func (a and[object]) Generic(e event.TypedGenericEvent[object]) bool {
|
||||
for _, p := range a.predicates {
|
||||
if !p.Generic(e) {
|
||||
return false
|
||||
@@ -278,15 +332,15 @@ func (a and) Generic(e event.GenericEvent) bool {
|
||||
}
|
||||
|
||||
// Or returns a composite predicate that implements a logical OR of the predicates passed to it.
|
||||
func Or(predicates ...Predicate) Predicate {
|
||||
return or{predicates}
|
||||
func Or[object any](predicates ...TypedPredicate[object]) TypedPredicate[object] {
|
||||
return or[object]{predicates}
|
||||
}
|
||||
|
||||
type or struct {
|
||||
predicates []Predicate
|
||||
type or[object any] struct {
|
||||
predicates []TypedPredicate[object]
|
||||
}
|
||||
|
||||
func (o or) Create(e event.CreateEvent) bool {
|
||||
func (o or[object]) Create(e event.TypedCreateEvent[object]) bool {
|
||||
for _, p := range o.predicates {
|
||||
if p.Create(e) {
|
||||
return true
|
||||
@@ -295,7 +349,7 @@ func (o or) Create(e event.CreateEvent) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (o or) Update(e event.UpdateEvent) bool {
|
||||
func (o or[object]) Update(e event.TypedUpdateEvent[object]) bool {
|
||||
for _, p := range o.predicates {
|
||||
if p.Update(e) {
|
||||
return true
|
||||
@@ -304,7 +358,7 @@ func (o or) Update(e event.UpdateEvent) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (o or) Delete(e event.DeleteEvent) bool {
|
||||
func (o or[object]) Delete(e event.TypedDeleteEvent[object]) bool {
|
||||
for _, p := range o.predicates {
|
||||
if p.Delete(e) {
|
||||
return true
|
||||
@@ -313,7 +367,7 @@ func (o or) Delete(e event.DeleteEvent) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (o or) Generic(e event.GenericEvent) bool {
|
||||
func (o or[object]) Generic(e event.TypedGenericEvent[object]) bool {
|
||||
for _, p := range o.predicates {
|
||||
if p.Generic(e) {
|
||||
return true
|
||||
@@ -323,27 +377,27 @@ func (o or) Generic(e event.GenericEvent) bool {
|
||||
}
|
||||
|
||||
// Not returns a predicate that implements a logical NOT of the predicate passed to it.
|
||||
func Not(predicate Predicate) Predicate {
|
||||
return not{predicate}
|
||||
func Not[object any](predicate TypedPredicate[object]) TypedPredicate[object] {
|
||||
return not[object]{predicate}
|
||||
}
|
||||
|
||||
type not struct {
|
||||
predicate Predicate
|
||||
type not[object any] struct {
|
||||
predicate TypedPredicate[object]
|
||||
}
|
||||
|
||||
func (n not) Create(e event.CreateEvent) bool {
|
||||
func (n not[object]) Create(e event.TypedCreateEvent[object]) bool {
|
||||
return !n.predicate.Create(e)
|
||||
}
|
||||
|
||||
func (n not) Update(e event.UpdateEvent) bool {
|
||||
func (n not[object]) Update(e event.TypedUpdateEvent[object]) bool {
|
||||
return !n.predicate.Update(e)
|
||||
}
|
||||
|
||||
func (n not) Delete(e event.DeleteEvent) bool {
|
||||
func (n not[object]) Delete(e event.TypedDeleteEvent[object]) bool {
|
||||
return !n.predicate.Delete(e)
|
||||
}
|
||||
|
||||
func (n not) Generic(e event.GenericEvent) bool {
|
||||
func (n not[object]) Generic(e event.TypedGenericEvent[object]) bool {
|
||||
return !n.predicate.Generic(e)
|
||||
}
|
||||
|
||||
@@ -358,3 +412,15 @@ func LabelSelectorPredicate(s metav1.LabelSelector) (Predicate, error) {
|
||||
return selector.Matches(labels.Set(o.GetLabels()))
|
||||
}), nil
|
||||
}
|
||||
|
||||
func isNil(arg any) bool {
|
||||
if v := reflect.ValueOf(arg); !v.IsValid() || ((v.Kind() == reflect.Ptr ||
|
||||
v.Kind() == reflect.Interface ||
|
||||
v.Kind() == reflect.Slice ||
|
||||
v.Kind() == reflect.Map ||
|
||||
v.Kind() == reflect.Chan ||
|
||||
v.Kind() == reflect.Func) && v.IsNil()) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
30
vendor/sigs.k8s.io/controller-runtime/pkg/ratelimiter/ratelimiter.go
generated
vendored
30
vendor/sigs.k8s.io/controller-runtime/pkg/ratelimiter/ratelimiter.go
generated
vendored
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
Copyright 2020 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package ratelimiter
|
||||
|
||||
import "time"
|
||||
|
||||
// RateLimiter is an identical interface of client-go workqueue RateLimiter.
|
||||
type RateLimiter interface {
|
||||
// When gets an item and gets to decide how long that item should wait
|
||||
When(item interface{}) time.Duration
|
||||
// Forget indicates that an item is finished being retried. Doesn't matter whether its for perm failing
|
||||
// or for success, we'll stop tracking it
|
||||
Forget(item interface{})
|
||||
// NumRequeues returns back how many failures the item has had
|
||||
NumRequeues(item interface{}) int
|
||||
}
|
||||
36
vendor/sigs.k8s.io/controller-runtime/pkg/reconcile/reconcile.go
generated
vendored
36
vendor/sigs.k8s.io/controller-runtime/pkg/reconcile/reconcile.go
generated
vendored
@@ -89,7 +89,14 @@ driven by actual cluster state read from the apiserver or a local cache.
|
||||
For example if responding to a Pod Delete Event, the Request won't contain that a Pod was deleted,
|
||||
instead the reconcile function observes this when reading the cluster state and seeing the Pod as missing.
|
||||
*/
|
||||
type Reconciler interface {
|
||||
type Reconciler = TypedReconciler[Request]
|
||||
|
||||
// TypedReconciler implements an API for a specific Resource by Creating, Updating or Deleting Kubernetes
|
||||
// objects, or by making changes to systems external to the cluster (e.g. cloudproviders, github, etc).
|
||||
//
|
||||
// The request type is what event handlers put into the workqueue. The workqueue then de-duplicates identical
|
||||
// requests.
|
||||
type TypedReconciler[request comparable] interface {
|
||||
// Reconcile performs a full reconciliation for the object referred to by the Request.
|
||||
//
|
||||
// If the returned error is non-nil, the Result is ignored and the request will be
|
||||
@@ -101,40 +108,45 @@ type Reconciler interface {
|
||||
//
|
||||
// If the error is nil and result.RequeueAfter is zero and result.Requeue is true, the request
|
||||
// will be requeued using exponential backoff.
|
||||
Reconcile(context.Context, Request) (Result, error)
|
||||
Reconcile(context.Context, request) (Result, error)
|
||||
}
|
||||
|
||||
// Func is a function that implements the reconcile interface.
|
||||
type Func func(context.Context, Request) (Result, error)
|
||||
type Func = TypedFunc[Request]
|
||||
|
||||
// TypedFunc is a function that implements the reconcile interface.
|
||||
type TypedFunc[request comparable] func(context.Context, request) (Result, error)
|
||||
|
||||
var _ Reconciler = Func(nil)
|
||||
|
||||
// Reconcile implements Reconciler.
|
||||
func (r Func) Reconcile(ctx context.Context, o Request) (Result, error) { return r(ctx, o) }
|
||||
func (r TypedFunc[request]) Reconcile(ctx context.Context, req request) (Result, error) {
|
||||
return r(ctx, req)
|
||||
}
|
||||
|
||||
// ObjectReconciler is a specialized version of Reconciler that acts on instances of client.Object. Each reconciliation
|
||||
// event gets the associated object from Kubernetes before passing it to Reconcile. An ObjectReconciler can be used in
|
||||
// Builder.Complete by calling AsReconciler. See Reconciler for more details.
|
||||
type ObjectReconciler[T client.Object] interface {
|
||||
Reconcile(context.Context, T) (Result, error)
|
||||
type ObjectReconciler[object client.Object] interface {
|
||||
Reconcile(context.Context, object) (Result, error)
|
||||
}
|
||||
|
||||
// AsReconciler creates a Reconciler based on the given ObjectReconciler.
|
||||
func AsReconciler[T client.Object](client client.Client, rec ObjectReconciler[T]) Reconciler {
|
||||
return &objectReconcilerAdapter[T]{
|
||||
func AsReconciler[object client.Object](client client.Client, rec ObjectReconciler[object]) Reconciler {
|
||||
return &objectReconcilerAdapter[object]{
|
||||
objReconciler: rec,
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
type objectReconcilerAdapter[T client.Object] struct {
|
||||
objReconciler ObjectReconciler[T]
|
||||
type objectReconcilerAdapter[object client.Object] struct {
|
||||
objReconciler ObjectReconciler[object]
|
||||
client client.Client
|
||||
}
|
||||
|
||||
// Reconcile implements Reconciler.
|
||||
func (a *objectReconcilerAdapter[T]) Reconcile(ctx context.Context, req Request) (Result, error) {
|
||||
o := reflect.New(reflect.TypeOf(*new(T)).Elem()).Interface().(T)
|
||||
func (a *objectReconcilerAdapter[object]) Reconcile(ctx context.Context, req Request) (Result, error) {
|
||||
o := reflect.New(reflect.TypeOf(*new(object)).Elem()).Interface().(object)
|
||||
if err := a.client.Get(ctx, req.NamespacedName, o); err != nil {
|
||||
return Result{}, client.IgnoreNotFound(err)
|
||||
}
|
||||
|
||||
188
vendor/sigs.k8s.io/controller-runtime/pkg/source/source.go
generated
vendored
188
vendor/sigs.k8s.io/controller-runtime/pkg/source/source.go
generated
vendored
@@ -18,94 +18,174 @@ package source
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
"k8s.io/utils/ptr"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/event"
|
||||
"sigs.k8s.io/controller-runtime/pkg/handler"
|
||||
internal "sigs.k8s.io/controller-runtime/pkg/internal/source"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/cache"
|
||||
"sigs.k8s.io/controller-runtime/pkg/predicate"
|
||||
)
|
||||
|
||||
const (
|
||||
// defaultBufferSize is the default number of event notifications that can be buffered.
|
||||
defaultBufferSize = 1024
|
||||
)
|
||||
|
||||
// Source is a source of events (eh.g. Create, Update, Delete operations on Kubernetes Objects, Webhook callbacks, etc)
|
||||
// Source is a source of events (e.g. Create, Update, Delete operations on Kubernetes Objects, Webhook callbacks, etc)
|
||||
// which should be processed by event.EventHandlers to enqueue reconcile.Requests.
|
||||
//
|
||||
// * Use Kind for events originating in the cluster (e.g. Pod Create, Pod Update, Deployment Update).
|
||||
//
|
||||
// * Use Channel for events originating outside the cluster (eh.g. GitHub Webhook callback, Polling external urls).
|
||||
// * Use Channel for events originating outside the cluster (e.g. GitHub Webhook callback, Polling external urls).
|
||||
//
|
||||
// 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.
|
||||
Start(context.Context, handler.EventHandler, workqueue.RateLimitingInterface, ...predicate.Predicate) error
|
||||
type Source = TypedSource[reconcile.Request]
|
||||
|
||||
// TypedSource is a generic source of events (e.g. Create, Update, Delete operations on Kubernetes Objects, Webhook callbacks, etc)
|
||||
// which should be processed by event.EventHandlers to enqueue a request.
|
||||
//
|
||||
// * Use Kind for events originating in the cluster (e.g. Pod Create, Pod Update, Deployment Update).
|
||||
//
|
||||
// * Use Channel for events originating outside the cluster (e.g. GitHub Webhook callback, Polling external urls).
|
||||
//
|
||||
// Users may build their own Source implementations.
|
||||
type TypedSource[request comparable] interface {
|
||||
// Start is internal and should be called only by the Controller to start the source.
|
||||
// Start must be non-blocking.
|
||||
Start(context.Context, workqueue.TypedRateLimitingInterface[request]) error
|
||||
}
|
||||
|
||||
// SyncingSource is a source that needs syncing prior to being usable. The controller
|
||||
// will call its WaitForSync prior to starting workers.
|
||||
type SyncingSource interface {
|
||||
Source
|
||||
type SyncingSource = TypedSyncingSource[reconcile.Request]
|
||||
|
||||
// TypedSyncingSource is a source that needs syncing prior to being usable. The controller
|
||||
// will call its WaitForSync prior to starting workers.
|
||||
type TypedSyncingSource[request comparable] interface {
|
||||
TypedSource[request]
|
||||
WaitForSync(ctx context.Context) error
|
||||
}
|
||||
|
||||
// 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}
|
||||
func Kind[object client.Object](
|
||||
cache cache.Cache,
|
||||
obj object,
|
||||
handler handler.TypedEventHandler[object, reconcile.Request],
|
||||
predicates ...predicate.TypedPredicate[object],
|
||||
) SyncingSource {
|
||||
return TypedKind(cache, obj, handler, predicates...)
|
||||
}
|
||||
|
||||
var _ Source = &Channel{}
|
||||
// TypedKind creates a KindSource with the given cache provider.
|
||||
func TypedKind[object client.Object, request comparable](
|
||||
cache cache.Cache,
|
||||
obj object,
|
||||
handler handler.TypedEventHandler[object, request],
|
||||
predicates ...predicate.TypedPredicate[object],
|
||||
) TypedSyncingSource[request] {
|
||||
return &internal.Kind[object, request]{
|
||||
Type: obj,
|
||||
Cache: cache,
|
||||
Handler: handler,
|
||||
Predicates: predicates,
|
||||
}
|
||||
}
|
||||
|
||||
var _ Source = &channel[string, reconcile.Request]{}
|
||||
|
||||
// ChannelOpt allows to configure a source.Channel.
|
||||
type ChannelOpt[object any, request comparable] func(*channel[object, request])
|
||||
|
||||
// WithPredicates adds the configured predicates to a source.Channel.
|
||||
func WithPredicates[object any, request comparable](p ...predicate.TypedPredicate[object]) ChannelOpt[object, request] {
|
||||
return func(c *channel[object, request]) {
|
||||
c.predicates = append(c.predicates, p...)
|
||||
}
|
||||
}
|
||||
|
||||
// WithBufferSize configures the buffer size for a source.Channel. By
|
||||
// default, the buffer size is 1024.
|
||||
func WithBufferSize[object any, request comparable](bufferSize int) ChannelOpt[object, request] {
|
||||
return func(c *channel[object, request]) {
|
||||
c.bufferSize = &bufferSize
|
||||
}
|
||||
}
|
||||
|
||||
// Channel is used to provide a source of events originating outside the cluster
|
||||
// (e.g. GitHub Webhook callback). Channel requires the user to wire the external
|
||||
// source (eh.g. http handler) to write GenericEvents to the underlying channel.
|
||||
type Channel struct {
|
||||
// source (e.g. http handler) to write GenericEvents to the underlying channel.
|
||||
func Channel[object any](
|
||||
source <-chan event.TypedGenericEvent[object],
|
||||
handler handler.TypedEventHandler[object, reconcile.Request],
|
||||
opts ...ChannelOpt[object, reconcile.Request],
|
||||
) Source {
|
||||
return TypedChannel[object, reconcile.Request](source, handler, opts...)
|
||||
}
|
||||
|
||||
// TypedChannel is used to provide a source of events originating outside the cluster
|
||||
// (e.g. GitHub Webhook callback). Channel requires the user to wire the external
|
||||
// source (e.g. http handler) to write GenericEvents to the underlying channel.
|
||||
func TypedChannel[object any, request comparable](
|
||||
source <-chan event.TypedGenericEvent[object],
|
||||
handler handler.TypedEventHandler[object, request],
|
||||
opts ...ChannelOpt[object, request],
|
||||
) TypedSource[request] {
|
||||
c := &channel[object, request]{
|
||||
source: source,
|
||||
handler: handler,
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(c)
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
type channel[object any, request comparable] struct {
|
||||
// once ensures the event distribution goroutine will be performed only once
|
||||
once sync.Once
|
||||
|
||||
// Source is the source channel to fetch GenericEvents
|
||||
Source <-chan event.GenericEvent
|
||||
// source is the source channel to fetch GenericEvents
|
||||
source <-chan event.TypedGenericEvent[object]
|
||||
|
||||
handler handler.TypedEventHandler[object, request]
|
||||
|
||||
predicates []predicate.TypedPredicate[object]
|
||||
|
||||
bufferSize *int
|
||||
|
||||
// dest is the destination channels of the added event handlers
|
||||
dest []chan event.GenericEvent
|
||||
|
||||
// DestBufferSize is the specified buffer size of dest channels.
|
||||
// Default to 1024 if not specified.
|
||||
DestBufferSize int
|
||||
dest []chan event.TypedGenericEvent[object]
|
||||
|
||||
// destLock is to ensure the destination channels are safely added/removed
|
||||
destLock sync.Mutex
|
||||
}
|
||||
|
||||
func (cs *Channel) String() string {
|
||||
func (cs *channel[object, request]) String() string {
|
||||
return fmt.Sprintf("channel source: %p", cs)
|
||||
}
|
||||
|
||||
// Start implements Source and should only be called by the Controller.
|
||||
func (cs *Channel) Start(
|
||||
func (cs *channel[object, request]) Start(
|
||||
ctx context.Context,
|
||||
handler handler.EventHandler,
|
||||
queue workqueue.RateLimitingInterface,
|
||||
prct ...predicate.Predicate) error {
|
||||
queue workqueue.TypedRateLimitingInterface[request],
|
||||
) error {
|
||||
// Source should have been specified by the user.
|
||||
if cs.Source == nil {
|
||||
if cs.source == nil {
|
||||
return fmt.Errorf("must specify Channel.Source")
|
||||
}
|
||||
|
||||
// use default value if DestBufferSize not specified
|
||||
if cs.DestBufferSize == 0 {
|
||||
cs.DestBufferSize = defaultBufferSize
|
||||
if cs.handler == nil {
|
||||
return errors.New("must specify Channel.Handler")
|
||||
}
|
||||
|
||||
dst := make(chan event.GenericEvent, cs.DestBufferSize)
|
||||
if cs.bufferSize == nil {
|
||||
cs.bufferSize = ptr.To(1024)
|
||||
}
|
||||
|
||||
dst := make(chan event.TypedGenericEvent[object], *cs.bufferSize)
|
||||
|
||||
cs.destLock.Lock()
|
||||
cs.dest = append(cs.dest, dst)
|
||||
@@ -119,7 +199,7 @@ func (cs *Channel) Start(
|
||||
go func() {
|
||||
for evt := range dst {
|
||||
shouldHandle := true
|
||||
for _, p := range prct {
|
||||
for _, p := range cs.predicates {
|
||||
if !p.Generic(evt) {
|
||||
shouldHandle = false
|
||||
break
|
||||
@@ -130,7 +210,7 @@ func (cs *Channel) Start(
|
||||
func() {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
handler.Generic(ctx, evt, queue)
|
||||
cs.handler.Generic(ctx, evt, queue)
|
||||
}()
|
||||
}
|
||||
}
|
||||
@@ -139,7 +219,7 @@ func (cs *Channel) Start(
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cs *Channel) doStop() {
|
||||
func (cs *channel[object, request]) doStop() {
|
||||
cs.destLock.Lock()
|
||||
defer cs.destLock.Unlock()
|
||||
|
||||
@@ -148,7 +228,7 @@ func (cs *Channel) doStop() {
|
||||
}
|
||||
}
|
||||
|
||||
func (cs *Channel) distribute(evt event.GenericEvent) {
|
||||
func (cs *channel[object, request]) distribute(evt event.TypedGenericEvent[object]) {
|
||||
cs.destLock.Lock()
|
||||
defer cs.destLock.Unlock()
|
||||
|
||||
@@ -162,14 +242,14 @@ func (cs *Channel) distribute(evt event.GenericEvent) {
|
||||
}
|
||||
}
|
||||
|
||||
func (cs *Channel) syncLoop(ctx context.Context) {
|
||||
func (cs *channel[object, request]) syncLoop(ctx context.Context) {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
// Close destination channels
|
||||
cs.doStop()
|
||||
return
|
||||
case evt, stillOpen := <-cs.Source:
|
||||
case evt, stillOpen := <-cs.source:
|
||||
if !stillOpen {
|
||||
// if the source channel is closed, we're never gonna get
|
||||
// anything more on it, so stop & bail
|
||||
@@ -184,21 +264,25 @@ func (cs *Channel) syncLoop(ctx context.Context) {
|
||||
// Informer is used to provide a source of events originating inside the cluster from Watches (e.g. Pod Create).
|
||||
type Informer struct {
|
||||
// Informer is the controller-runtime Informer
|
||||
Informer cache.Informer
|
||||
Informer cache.Informer
|
||||
Handler handler.EventHandler
|
||||
Predicates []predicate.Predicate
|
||||
}
|
||||
|
||||
var _ Source = &Informer{}
|
||||
|
||||
// Start is internal and should be called only by the Controller to register an EventHandler with the Informer
|
||||
// to enqueue reconcile.Requests.
|
||||
func (is *Informer) Start(ctx context.Context, handler handler.EventHandler, queue workqueue.RateLimitingInterface,
|
||||
prct ...predicate.Predicate) error {
|
||||
func (is *Informer) Start(ctx context.Context, queue workqueue.TypedRateLimitingInterface[reconcile.Request]) error {
|
||||
// Informer should have been specified by the user.
|
||||
if is.Informer == nil {
|
||||
return fmt.Errorf("must specify Informer.Informer")
|
||||
}
|
||||
if is.Handler == nil {
|
||||
return errors.New("must specify Informer.Handler")
|
||||
}
|
||||
|
||||
_, err := is.Informer.AddEventHandler(internal.NewEventHandler(ctx, queue, handler, prct).HandlerFuncs())
|
||||
_, err := is.Informer.AddEventHandler(internal.NewEventHandler(ctx, queue, is.Handler, is.Predicates).HandlerFuncs())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -212,14 +296,16 @@ func (is *Informer) String() string {
|
||||
var _ Source = Func(nil)
|
||||
|
||||
// Func is a function that implements Source.
|
||||
type Func func(context.Context, handler.EventHandler, workqueue.RateLimitingInterface, ...predicate.Predicate) error
|
||||
type Func = TypedFunc[reconcile.Request]
|
||||
|
||||
// TypedFunc is a function that implements Source.
|
||||
type TypedFunc[request comparable] func(context.Context, workqueue.TypedRateLimitingInterface[request]) error
|
||||
|
||||
// Start implements Source.
|
||||
func (f Func) Start(ctx context.Context, evt handler.EventHandler, queue workqueue.RateLimitingInterface,
|
||||
pr ...predicate.Predicate) error {
|
||||
return f(ctx, evt, queue, pr...)
|
||||
func (f TypedFunc[request]) Start(ctx context.Context, queue workqueue.TypedRateLimitingInterface[request]) error {
|
||||
return f(ctx, queue)
|
||||
}
|
||||
|
||||
func (f Func) String() string {
|
||||
func (f TypedFunc[request]) String() string {
|
||||
return fmt.Sprintf("func source: %p", f)
|
||||
}
|
||||
|
||||
25
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/decode.go
generated
vendored
25
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/decode.go
generated
vendored
@@ -26,22 +26,35 @@ import (
|
||||
|
||||
// Decoder knows how to decode the contents of an admission
|
||||
// request into a concrete object.
|
||||
type Decoder struct {
|
||||
type Decoder interface {
|
||||
// Decode decodes the inlined object in the AdmissionRequest into the passed-in runtime.Object.
|
||||
// If you want decode the OldObject in the AdmissionRequest, use DecodeRaw.
|
||||
// It errors out if req.Object.Raw is empty i.e. containing 0 raw bytes.
|
||||
Decode(req Request, into runtime.Object) error
|
||||
|
||||
// DecodeRaw decodes a RawExtension object into the passed-in runtime.Object.
|
||||
// It errors out if rawObj is empty i.e. containing 0 raw bytes.
|
||||
DecodeRaw(rawObj runtime.RawExtension, into runtime.Object) error
|
||||
}
|
||||
|
||||
// decoder knows how to decode the contents of an admission
|
||||
// request into a concrete object.
|
||||
type decoder struct {
|
||||
codecs serializer.CodecFactory
|
||||
}
|
||||
|
||||
// NewDecoder creates a Decoder given the runtime.Scheme.
|
||||
func NewDecoder(scheme *runtime.Scheme) *Decoder {
|
||||
// NewDecoder creates a decoder given the runtime.Scheme.
|
||||
func NewDecoder(scheme *runtime.Scheme) Decoder {
|
||||
if scheme == nil {
|
||||
panic("scheme should never be nil")
|
||||
}
|
||||
return &Decoder{codecs: serializer.NewCodecFactory(scheme)}
|
||||
return &decoder{codecs: serializer.NewCodecFactory(scheme)}
|
||||
}
|
||||
|
||||
// Decode decodes the inlined object in the AdmissionRequest into the passed-in runtime.Object.
|
||||
// If you want decode the OldObject in the AdmissionRequest, use DecodeRaw.
|
||||
// It errors out if req.Object.Raw is empty i.e. containing 0 raw bytes.
|
||||
func (d *Decoder) Decode(req Request, into runtime.Object) error {
|
||||
func (d *decoder) Decode(req Request, into runtime.Object) error {
|
||||
// we error out if rawObj is an empty object.
|
||||
if len(req.Object.Raw) == 0 {
|
||||
return fmt.Errorf("there is no content to decode")
|
||||
@@ -51,7 +64,7 @@ func (d *Decoder) Decode(req Request, into runtime.Object) error {
|
||||
|
||||
// DecodeRaw decodes a RawExtension object into the passed-in runtime.Object.
|
||||
// It errors out if rawObj is empty i.e. containing 0 raw bytes.
|
||||
func (d *Decoder) DecodeRaw(rawObj runtime.RawExtension, into runtime.Object) error {
|
||||
func (d *decoder) DecodeRaw(rawObj runtime.RawExtension, into runtime.Object) error {
|
||||
// NB(directxman12): there's a bug/weird interaction between decoders and
|
||||
// the API server where the API server doesn't send a GVK on the embedded
|
||||
// objects, which means the unstructured decoder refuses to decode. It
|
||||
|
||||
2
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/defaulter.go
generated
vendored
2
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/defaulter.go
generated
vendored
@@ -43,7 +43,7 @@ func DefaultingWebhookFor(scheme *runtime.Scheme, defaulter Defaulter) *Webhook
|
||||
|
||||
type mutatingHandler struct {
|
||||
defaulter Defaulter
|
||||
decoder *Decoder
|
||||
decoder Decoder
|
||||
}
|
||||
|
||||
// Handle handles admission requests.
|
||||
|
||||
2
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/defaulter_custom.go
generated
vendored
2
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/defaulter_custom.go
generated
vendored
@@ -43,7 +43,7 @@ func WithCustomDefaulter(scheme *runtime.Scheme, obj runtime.Object, defaulter C
|
||||
type defaulterForType struct {
|
||||
defaulter CustomDefaulter
|
||||
object runtime.Object
|
||||
decoder *Decoder
|
||||
decoder Decoder
|
||||
}
|
||||
|
||||
// Handle handles admission requests.
|
||||
|
||||
39
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/metrics/metrics.go
generated
vendored
Normal file
39
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/metrics/metrics.go
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
Copyright 2024 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 metrics
|
||||
|
||||
import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"sigs.k8s.io/controller-runtime/pkg/metrics"
|
||||
)
|
||||
|
||||
var (
|
||||
// WebhookPanics is a prometheus counter metrics which holds the total
|
||||
// number of panics from webhooks.
|
||||
WebhookPanics = prometheus.NewCounterVec(prometheus.CounterOpts{
|
||||
Name: "controller_runtime_webhook_panics_total",
|
||||
Help: "Total number of webhook panics",
|
||||
}, []string{})
|
||||
)
|
||||
|
||||
func init() {
|
||||
metrics.Registry.MustRegister(
|
||||
WebhookPanics,
|
||||
)
|
||||
// Init metric.
|
||||
WebhookPanics.WithLabelValues().Add(0)
|
||||
}
|
||||
2
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/validator.go
generated
vendored
2
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/validator.go
generated
vendored
@@ -63,7 +63,7 @@ func ValidatingWebhookFor(scheme *runtime.Scheme, validator Validator) *Webhook
|
||||
|
||||
type validatingHandler struct {
|
||||
validator Validator
|
||||
decoder *Decoder
|
||||
decoder Decoder
|
||||
}
|
||||
|
||||
// Handle handles admission requests.
|
||||
|
||||
2
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/validator_custom.go
generated
vendored
2
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/validator_custom.go
generated
vendored
@@ -56,7 +56,7 @@ func WithCustomValidator(scheme *runtime.Scheme, obj runtime.Object, validator C
|
||||
type validatorForType struct {
|
||||
validator CustomValidator
|
||||
object runtime.Object
|
||||
decoder *Decoder
|
||||
decoder Decoder
|
||||
}
|
||||
|
||||
// Handle handles admission requests.
|
||||
|
||||
33
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/webhook.go
generated
vendored
33
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/webhook.go
generated
vendored
@@ -30,6 +30,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/util/json"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/klog/v2"
|
||||
admissionmetrics "sigs.k8s.io/controller-runtime/pkg/webhook/admission/metrics"
|
||||
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/log"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook/internal/metrics"
|
||||
@@ -123,7 +124,8 @@ type Webhook struct {
|
||||
Handler Handler
|
||||
|
||||
// RecoverPanic indicates whether the panic caused by webhook should be recovered.
|
||||
RecoverPanic bool
|
||||
// Defaults to true.
|
||||
RecoverPanic *bool
|
||||
|
||||
// WithContextFunc will allow you to take the http.Request.Context() and
|
||||
// add any additional information such as passing the request path or
|
||||
@@ -141,8 +143,9 @@ type Webhook struct {
|
||||
}
|
||||
|
||||
// WithRecoverPanic takes a bool flag which indicates whether the panic caused by webhook should be recovered.
|
||||
// Defaults to true.
|
||||
func (wh *Webhook) WithRecoverPanic(recoverPanic bool) *Webhook {
|
||||
wh.RecoverPanic = recoverPanic
|
||||
wh.RecoverPanic = &recoverPanic
|
||||
return wh
|
||||
}
|
||||
|
||||
@@ -151,17 +154,26 @@ func (wh *Webhook) WithRecoverPanic(recoverPanic bool) *Webhook {
|
||||
// If the webhook is validating type, it delegates the AdmissionRequest to each handler and
|
||||
// deny the request if anyone denies.
|
||||
func (wh *Webhook) Handle(ctx context.Context, req Request) (response Response) {
|
||||
if wh.RecoverPanic {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
admissionmetrics.WebhookPanics.WithLabelValues().Inc()
|
||||
|
||||
if wh.RecoverPanic == nil || *wh.RecoverPanic {
|
||||
for _, fn := range utilruntime.PanicHandlers {
|
||||
fn(r)
|
||||
fn(ctx, r)
|
||||
}
|
||||
response = Errored(http.StatusInternalServerError, fmt.Errorf("panic: %v [recovered]", r))
|
||||
// Note: We explicitly have to set the response UID. Usually that is done via resp.Complete below,
|
||||
// but if we encounter a panic in wh.Handler.Handle we are never going to reach resp.Complete.
|
||||
response.UID = req.UID
|
||||
return
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
log := logf.FromContext(ctx)
|
||||
log.Info(fmt.Sprintf("Observed a panic in webhook: %v", r))
|
||||
panic(r)
|
||||
}
|
||||
}()
|
||||
|
||||
reqLog := wh.getLogger(&req)
|
||||
ctx = logf.IntoContext(ctx, reqLog)
|
||||
@@ -169,7 +181,10 @@ func (wh *Webhook) Handle(ctx context.Context, req Request) (response Response)
|
||||
resp := wh.Handler.Handle(ctx, req)
|
||||
if err := resp.Complete(req); err != nil {
|
||||
reqLog.Error(err, "unable to encode response")
|
||||
return Errored(http.StatusInternalServerError, errUnableToEncodeResponse)
|
||||
resp := Errored(http.StatusInternalServerError, errUnableToEncodeResponse)
|
||||
// Note: We explicitly have to set the response UID. Usually that is done via resp.Complete.
|
||||
resp.UID = req.UID
|
||||
return resp
|
||||
}
|
||||
|
||||
return resp
|
||||
|
||||
Reference in New Issue
Block a user