summaryrefslogtreecommitdiff
path: root/vendor/sigs.k8s.io
diff options
context:
space:
mode:
authorCongrool <chpzhangyifei@qq.com>2022-03-14 17:14:10 +0800
committervincentgoat <linguohui1@huawei.com>2022-05-19 15:51:00 +0800
commit828d742660f75a64f24e19401c00a5e41281a462 (patch)
tree84ce74fcfbf78b6917a36526397b768320927986 /vendor/sigs.k8s.io
parentMerge pull request #3856 from ModeEngage/master (diff)
downloadkubeedge-828d742660f75a64f24e19401c00a5e41281a462.tar.gz
update vendor for controller-runtime@v0.10.3
Signed-off-by: Congrool <chpzhangyifei@qq.com> Signed-off-by: Congrool <chpzhangyifei@zju.edu.cn>
Diffstat (limited to 'vendor/sigs.k8s.io')
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/.gitignore24
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/.golangci.yml130
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/CONTRIBUTING.md19
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/FAQ.md81
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/LICENSE201
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/Makefile123
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/OWNERS10
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/OWNERS_ALIASES40
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/README.md66
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/SECURITY_CONTACTS14
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/TMP-LOGGING.md169
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/VERSIONING.md30
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/alias.go150
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/code-of-conduct.md3
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/doc.go127
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/go.mod28
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/go.sum769
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/builder/controller.go316
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/builder/doc.go28
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/builder/options.go117
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/builder/webhook.go209
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/cache/cache.go231
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/cache/doc.go19
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/cache/informer_cache.go217
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/cache_reader.go218
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/deleg_map.go125
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/disabledeepcopy.go35
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/informers_map.go478
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/selector.go43
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/cache/multi_namespace_cache.go331
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/certwatcher/certwatcher.go163
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/certwatcher/doc.go23
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/apimachinery.go196
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/dynamicrestmapper.go285
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/client/client.go328
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/client/client_cache.go150
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/client/codec.go40
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/client/config/config.go157
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/client/config/doc.go18
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/client/doc.go49
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/client/dryrun.go106
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/client/interfaces.go145
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/client/metadata_client.go195
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/client/namespaced_client.go213
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/client/object.go77
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/client/options.go697
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/client/patch.go213
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/client/split.go141
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/client/typed_client.go205
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/client/unstructured_client.go277
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/client/watch.go118
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/cluster/cluster.go270
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/cluster/internal.go128
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/config/config.go112
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/config/doc.go25
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/config/v1alpha1/doc.go20
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/config/v1alpha1/register.go37
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/config/v1alpha1/types.go157
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/config/v1alpha1/zz_generated.deepcopy.go152
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/controller/controller.go141
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/controller/controllerutil/controllerutil.go389
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/controller/controllerutil/doc.go20
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/controller/doc.go25
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/conversion/conversion.go40
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/event/doc.go28
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/event/event.go55
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/handler/doc.go38
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/handler/enqueue.go90
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/handler/enqueue_mapped.go97
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/handler/enqueue_owner.go189
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/handler/eventhandler.go104
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/healthz/doc.go32
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/healthz/healthz.go206
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/controller.go351
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/metrics/metrics.go78
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/internal/log/log.go32
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/internal/objectutil/objectutil.go78
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/internal/recorder/recorder.go176
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/leaderelection/doc.go24
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/leaderelection/leader_election.go126
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/log/deleg.go216
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/log/log.go99
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/log/null.go60
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/log/warning_handler.go76
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/manager/doc.go21
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/manager/internal.go709
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/manager/manager.go579
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/manager/signals/doc.go20
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/manager/signals/signal.go45
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/manager/signals/signal_posix.go26
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/manager/signals/signal_windows.go23
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/metrics/client_go_adapter.go231
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/metrics/doc.go20
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/metrics/listener.go52
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/metrics/registry.go30
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/metrics/workqueue.go130
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/predicate/doc.go20
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/predicate/predicate.go333
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/ratelimiter/doc.go22
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/ratelimiter/ratelimiter.go30
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/reconcile/doc.go21
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/reconcile/reconcile.go102
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/recorder/recorder.go31
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/runtime/inject/doc.go22
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/runtime/inject/inject.go164
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/scheme/scheme.go94
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/source/doc.go22
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/source/internal/eventsource.go138
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/source/source.go366
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/decode.go72
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/defaulter.go74
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/defaulter_custom.go85
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/doc.go28
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/http.go146
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/inject.go31
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/multi.go147
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/response.go121
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/validator.go122
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/validator_custom.go111
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/webhook.go255
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/webhook/alias.go79
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/webhook/conversion/conversion.go345
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/webhook/conversion/decoder.go47
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/webhook/doc.go28
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/metrics/metrics.go85
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/webhook/server.go339
126 files changed, 17184 insertions, 0 deletions
diff --git a/vendor/sigs.k8s.io/controller-runtime/.gitignore b/vendor/sigs.k8s.io/controller-runtime/.gitignore
new file mode 100644
index 000000000..c2c72faf3
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/.gitignore
@@ -0,0 +1,24 @@
+# Binaries for programs and plugins
+*.exe
+*.exe~
+*.dll
+*.so
+*.dylib
+
+# Test binary, build with `go test -c`
+*.test
+
+# Output of the go coverage tool, specifically when used with LiteIDE
+*.out
+
+# editor and IDE paraphernalia
+.idea
+*.swp
+*.swo
+*~
+
+# Vscode files
+.vscode
+
+# Tools binaries.
+hack/tools/bin
diff --git a/vendor/sigs.k8s.io/controller-runtime/.golangci.yml b/vendor/sigs.k8s.io/controller-runtime/.golangci.yml
new file mode 100644
index 000000000..a5a5dad61
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/.golangci.yml
@@ -0,0 +1,130 @@
+linters:
+ disable-all: true
+ enable:
+ - asciicheck
+ - bodyclose
+ - deadcode
+ - depguard
+ - dogsled
+ - errcheck
+ - exportloopref
+ - goconst
+ - gocritic
+ - gocyclo
+ - godot
+ - gofmt
+ - goimports
+ - goprintffuncname
+ - gosec
+ - gosimple
+ - govet
+ - ifshort
+ - importas
+ - ineffassign
+ - misspell
+ - nakedret
+ - nilerr
+ - nolintlint
+ - prealloc
+ - revive
+ - rowserrcheck
+ - staticcheck
+ - structcheck
+ - stylecheck
+ - typecheck
+ - unconvert
+ - unparam
+ - varcheck
+ - whitespace
+
+linters-settings:
+ ifshort:
+ # Maximum length of variable declaration measured in number of characters, after which linter won't suggest using short syntax.
+ max-decl-chars: 50
+ importas:
+ no-unaliased: true
+ alias:
+ # Kubernetes
+ - pkg: k8s.io/api/core/v1
+ alias: corev1
+ - pkg: k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1
+ alias: apiextensionsv1
+ - pkg: k8s.io/apimachinery/pkg/apis/meta/v1
+ alias: metav1
+ - pkg: k8s.io/apimachinery/pkg/api/errors
+ alias: apierrors
+ - pkg: k8s.io/apimachinery/pkg/util/errors
+ alias: kerrors
+ # Controller Runtime
+ - pkg: sigs.k8s.io/controller-runtime
+ alias: ctrl
+ staticcheck:
+ go: "1.16"
+ stylecheck:
+ go: "1.16"
+
+issues:
+ max-same-issues: 0
+ max-issues-per-linter: 0
+ # We are disabling default golangci exclusions because we want to help reviewers to focus on reviewing the most relevant
+ # changes in PRs and avoid nitpicking.
+ exclude-use-default: false
+ # List of regexps of issue texts to exclude, empty list by default.
+ exclude:
+ # The following are being worked on to remove their exclusion. This list should be reduced or go away all together over time.
+ # If it is decided they will not be addressed they should be moved above this comment.
+ - Subprocess launch(ed with variable|ing should be audited)
+ - (G204|G104|G307)
+ - "ST1000: at least one file in a package should have a package comment"
+ exclude-rules:
+ - linters:
+ - gosec
+ text: "G108: Profiling endpoint is automatically exposed on /debug/pprof"
+ - linters:
+ - revive
+ text: "exported: exported method .*\\.(Reconcile|SetupWithManager|SetupWebhookWithManager) should have comment or be unexported"
+ - linters:
+ - errcheck
+ text: Error return value of .((os\.)?std(out|err)\..*|.*Close|.*Flush|os\.Remove(All)?|.*print(f|ln)?|os\.(Un)?Setenv). is not checked
+ # With Go 1.16, the new embed directive can be used with an un-named import,
+ # revive (previously, golint) only allows these to be imported in a main.go, which wouldn't work for us.
+ # This directive allows the embed package to be imported with an underscore everywhere.
+ - linters:
+ - revive
+ source: _ "embed"
+ # Exclude some packages or code to require comments, for example test code, or fake clients.
+ - linters:
+ - revive
+ text: exported (method|function|type|const) (.+) should have comment or be unexported
+ source: (func|type).*Fake.*
+ - linters:
+ - revive
+ text: exported (method|function|type|const) (.+) should have comment or be unexported
+ path: fake_\.go
+ # Disable unparam "always receives" which might not be really
+ # useful when building libraries.
+ - linters:
+ - unparam
+ text: always receives
+ # Dot imports for gomega or ginkgo are allowed
+ # within test files.
+ - path: _test\.go
+ text: should not use dot imports
+ - path: _test\.go
+ text: cyclomatic complexity
+ - path: _test\.go
+ text: "G107: Potential HTTP request made with variable url"
+ # Append should be able to assign to a different var/slice.
+ - linters:
+ - gocritic
+ text: "appendAssign: append result not assigned to the same slice"
+ - linters:
+ - gocritic
+ text: "singleCaseSwitch: should rewrite switch statement to if statement"
+
+run:
+ timeout: 10m
+ skip-files:
+ - "zz_generated.*\\.go$"
+ - ".*conversion.*\\.go$"
+ allow-parallel-runners: true
diff --git a/vendor/sigs.k8s.io/controller-runtime/CONTRIBUTING.md b/vendor/sigs.k8s.io/controller-runtime/CONTRIBUTING.md
new file mode 100644
index 000000000..2c0ea1f66
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/CONTRIBUTING.md
@@ -0,0 +1,19 @@
+# Contributing guidelines
+
+## Sign the CLA
+
+Kubernetes projects require that you sign a Contributor License Agreement (CLA) before we can accept your pull requests.
+
+Please see https://git.k8s.io/community/CLA.md for more info
+
+## Contributing steps
+
+1. Submit an issue describing your proposed change to the repo in question.
+1. The [repo owners](OWNERS) will respond to your issue promptly.
+1. If your proposed change is accepted, and you haven't already done so, sign a Contributor License Agreement (see details above).
+1. Fork the desired repo, develop and test your code changes.
+1. Submit a pull request.
+
+## Test locally
+
+Run the command `make test` to test the changes locally.
diff --git a/vendor/sigs.k8s.io/controller-runtime/FAQ.md b/vendor/sigs.k8s.io/controller-runtime/FAQ.md
new file mode 100644
index 000000000..86c7f9336
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/FAQ.md
@@ -0,0 +1,81 @@
+# FAQ
+
+### Q: How do I know which type of object a controller references?
+
+**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
+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
+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
+with application startup. The controller will enqueue reconcile requests
+for both old and new objects if a mapping changes, but it's your
+responsibility to make sure you have enough information to be able clean
+up state that's no longer referenced.
+
+### Q: My cache might be stale if I read from a cache! How should I deal with that?
+
+**A**: There are several different approaches that can be taken, depending
+on your situation.
+
+- When you can, take advantage of optimistic locking: use deterministic
+ names for objects you create, so that the Kubernetes API server will
+ warn you if the object already exists. Many controllers in Kubernetes
+ take this approach: the StatefulSet controller appends a specific number
+ to each pod that it creates, while the Deployment controller hashes the
+ pod template spec and appends that.
+
+- In the few cases when you cannot take advantage of deterministic names
+ (e.g. when using generateName), it may be useful in to track which
+ actions you took, and assume that they need to be repeated if they don't
+ occur after a given time (e.g. using a requeue result). This is what
+ the ReplicaSet controller does.
+
+In general, write your controller with the assumption that information
+will eventually be correct, but may be slightly out of date. Make sure
+that your reconcile function enforces the entire state of the world each
+time it runs. If none of this works for you, you can always construct
+a client that reads directly from the API server, but this is generally
+considered to be a last resort, and the two approaches above should
+generally cover most circumstances.
+
+### Q: Where's the fake client? How do I use it?
+
+**A**: The fake client
+[exists](https://godoc.org/sigs.k8s.io/controller-runtime/pkg/client/fake),
+but we generally recommend using
+[envtest.Environment](https://godoc.org/sigs.k8s.io/controller-runtime/pkg/envtest#Environment)
+to test against a real API server. In our experience, tests using fake
+clients gradually re-implement poorly-written impressions of a real API
+server, which leads to hard-to-maintain, complex test code.
+
+### Q: How should I write tests? Any suggestions for getting started?
+
+- Use the aforementioned
+ [envtest.Environment](https://godoc.org/sigs.k8s.io/controller-runtime/pkg/envtest#Environment)
+ to spin up a real API server instead of trying to mock one out.
+
+- Structure your tests to check that the state of the world is as you
+ expect it, *not* that a particular set of API calls were made, when
+ working with Kubernetes APIs. This will allow you to more easily
+ refactor and improve the internals of your controllers without changing
+ your tests.
+
+- Remember that any time you're interacting with the API server, changes
+ may have some delay between write time and reconcile time.
+
+### Q: What are these errors about no Kind being registered for a type?
+
+**A**: You're probably missing a fully-set-up Scheme. Schemes record the
+mapping between Go types and group-version-kinds in Kubernetes. In
+general, your application should have its own Scheme containing the types
+from the API groups that it needs (be they Kubernetes types or your own).
+See the [scheme builder
+docs](https://godoc.org/sigs.k8s.io/controller-runtime/pkg/scheme) for
+more information.
diff --git a/vendor/sigs.k8s.io/controller-runtime/LICENSE b/vendor/sigs.k8s.io/controller-runtime/LICENSE
new file mode 100644
index 000000000..8dada3eda
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "{}"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright {yyyy} {name of copyright owner}
+
+ 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.
diff --git a/vendor/sigs.k8s.io/controller-runtime/Makefile b/vendor/sigs.k8s.io/controller-runtime/Makefile
new file mode 100644
index 000000000..36647c697
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/Makefile
@@ -0,0 +1,123 @@
+#!/usr/bin/env bash
+
+# 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.
+
+# If you update this file, please follow
+# https://suva.sh/posts/well-documented-makefiles
+
+## --------------------------------------
+## General
+## --------------------------------------
+
+SHELL:=/usr/bin/env bash
+.DEFAULT_GOAL:=help
+
+# Use GOPROXY environment variable if set
+GOPROXY := $(shell go env GOPROXY)
+ifeq ($(GOPROXY),)
+GOPROXY := https://proxy.golang.org
+endif
+export GOPROXY
+
+# Active module mode, as we use go modules to manage dependencies
+export GO111MODULE=on
+
+# Tools.
+TOOLS_DIR := hack/tools
+TOOLS_BIN_DIR := $(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)
+
+# 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.
+# More info over the usage of ANSI control characters for terminal formatting: https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters
+# More info over awk command: http://linuxcommand.org/lc3_adv_awk.php
+.PHONY: help
+help: ## Display this help
+ @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
+
+## --------------------------------------
+## Testing
+## --------------------------------------
+
+.PHONY: test
+test: test-tools ## Run the script check-everything.sh which will check all.
+ TRACE=1 ./hack/check-everything.sh
+
+.PHONY: test-tools
+test-tools: ## tests the tools codebase (setup-envtest)
+ cd tools/setup-envtest && go test ./...
+
+## --------------------------------------
+## 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
+
+$(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
+
+$(GOLANGCI_LINT): .github/workflows/golangci-lint.yml # Download golanci-lint using hack script into tools folder.
+ hack/ensure-golangci-lint.sh \
+ -b $(TOOLS_BIN_DIR) \
+ $(shell cat .github/workflows/golangci-lint.yml | grep version | sed 's/.*version: //')
+
+## --------------------------------------
+## Linting
+## --------------------------------------
+
+.PHONY: lint
+lint: $(GOLANGCI_LINT) ## Lint codebase
+ $(GOLANGCI_LINT) run -v $(GOLANGCI_LINT_EXTRA_ARGS)
+ cd tools/setup-envtest; $(GOLANGCI_LINT) run -v $(GOLANGCI_LINT_EXTRA_ARGS)
+
+.PHONY: lint-fix
+lint-fix: $(GOLANGCI_LINT) ## Lint the codebase and run auto-fixers if supported by the linter.
+ GOLANGCI_LINT_EXTRA_ARGS=--fix $(MAKE) lint
+
+## --------------------------------------
+## Generate
+## --------------------------------------
+
+.PHONY: modules
+modules: ## Runs go mod to ensure modules are up to date.
+ go mod tidy
+ cd $(TOOLS_DIR); go mod tidy
+ cd $(ENVTEST_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/..."
+
+## --------------------------------------
+## Cleanup / Verification
+## --------------------------------------
+
+.PHONY: clean
+clean: ## Cleanup.
+ $(MAKE) clean-bin
+
+.PHONY: clean-bin
+clean-bin: ## Remove all generated binaries.
+ rm -rf hack/tools/bin
+
+.PHONY: verify-modules
+verify-modules: modules
+ @if !(git diff --quiet HEAD -- go.sum go.mod); then \
+ echo "go module files are out of date, please run 'make modules'"; exit 1; \
+ fi
diff --git a/vendor/sigs.k8s.io/controller-runtime/OWNERS b/vendor/sigs.k8s.io/controller-runtime/OWNERS
new file mode 100644
index 000000000..4b1fa044b
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/OWNERS
@@ -0,0 +1,10 @@
+# See the OWNERS docs: https://git.k8s.io/community/contributors/guide/owners.md
+
+approvers:
+ - controller-runtime-admins
+ - controller-runtime-maintainers
+ - controller-runtime-approvers
+reviewers:
+ - controller-runtime-admins
+ - controller-runtime-reviewers
+ - controller-runtime-approvers
diff --git a/vendor/sigs.k8s.io/controller-runtime/OWNERS_ALIASES b/vendor/sigs.k8s.io/controller-runtime/OWNERS_ALIASES
new file mode 100644
index 000000000..290dcc192
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/OWNERS_ALIASES
@@ -0,0 +1,40 @@
+# See the OWNERS docs: https://git.k8s.io/community/contributors/guide/owners.md
+
+aliases:
+ # active folks who can be contacted to perform admin-related
+ # tasks on the repo, or otherwise approve any PRS.
+ controller-runtime-admins:
+ - droot
+ - mengqiy
+ - pwittrock
+
+ # non-admin folks who have write-access and can approve any PRs in the repo
+ controller-runtime-maintainers:
+ - vincepri
+ - joelanford
+
+ # non-admin folks who can approve any PRs in the repo
+ controller-runtime-approvers:
+ - gerred
+ - shawn-hurley
+ - alvaroaleman
+
+ # folks who can review and LGTM any PRs in the repo (doesn't
+ # include approvers & admins -- those count too via the OWNERS
+ # file)
+ controller-runtime-reviewers:
+ - alenkacz
+ - vincepri
+ - alexeldeib
+ - varshaprasad96
+
+ # 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:
+ - directxman12
diff --git a/vendor/sigs.k8s.io/controller-runtime/README.md b/vendor/sigs.k8s.io/controller-runtime/README.md
new file mode 100644
index 000000000..d857d3e76
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/README.md
@@ -0,0 +1,66 @@
+[![Go Report Card](https://goreportcard.com/badge/sigs.k8s.io/controller-runtime)](https://goreportcard.com/report/sigs.k8s.io/controller-runtime)
+[![godoc](https://godoc.org/sigs.k8s.io/controller-runtime?status.svg)](https://godoc.org/sigs.k8s.io/controller-runtime)
+
+# Kubernetes controller-runtime Project
+
+The Kubernetes controller-runtime Project is a set of go libraries for building
+Controllers. It is leveraged by [Kubebuilder](https://book.kubebuilder.io/) and
+[Operator SDK](https://github.com/operator-framework/operator-sdk). Both are
+a great place to start for new projects. See
+[Kubebuilder's Quick Start](https://book.kubebuilder.io/quick-start.html) to
+see how it can be used.
+
+Documentation:
+
+- [Package overview](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg)
+- [Basic controller using builder](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/builder#example-Builder)
+- [Creating a manager](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/manager#example-New)
+- [Creating a controller](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/controller#example-New)
+- [Examples](https://github.com/kubernetes-sigs/controller-runtime/blob/master/examples)
+- [Designs](https://github.com/kubernetes-sigs/controller-runtime/blob/master/designs)
+
+# Versioning, Maintenance, and Compatibility
+
+The full documentation can be found at [VERSIONING.md](VERSIONING.md), but TL;DR:
+
+Users:
+
+- We follow [Semantic Versioning (semver)](https://semver.org)
+- Use releases with your dependency management to ensure that you get compatible code
+- The master branch contains all the latest code, some of which may break compatibility (so "normal" `go get` is not recommended)
+
+Contributors:
+
+- All code PR must be labeled with :bug: (patch fixes), :sparkles: (backwards-compatible features), or :warning: (breaking changes)
+- Breaking changes will find their way into the next major release, other changes will go into an semi-immediate patch or minor release
+- For a quick PR template suggesting the right information, use one of these PR templates:
+ * [Breaking Changes/Features](/.github/PULL_REQUEST_TEMPLATE/breaking_change.md)
+ * [Backwards-Compatible Features](/.github/PULL_REQUEST_TEMPLATE/compat_feature.md)
+ * [Bug fixes](/.github/PULL_REQUEST_TEMPLATE/bug_fix.md)
+ * [Documentation Changes](/.github/PULL_REQUEST_TEMPLATE/docs.md)
+ * [Test/Build/Other Changes](/.github/PULL_REQUEST_TEMPLATE/other.md)
+
+## FAQ
+
+See [FAQ.md](FAQ.md)
+
+## Community, discussion, contribution, and support
+
+Learn how to engage with the Kubernetes community on the [community page](http://kubernetes.io/community/).
+
+controller-runtime is a subproject of the [kubebuilder](https://github.com/kubernetes-sigs/kubebuilder) project
+in sig apimachinery.
+
+You can reach the maintainers of this project at:
+
+- Slack channel: [#kubebuilder](http://slack.k8s.io/#kubebuilder)
+- 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.
+
+## Code of conduct
+
+Participation in the Kubernetes community is governed by the [Kubernetes Code of Conduct](code-of-conduct.md).
diff --git a/vendor/sigs.k8s.io/controller-runtime/SECURITY_CONTACTS b/vendor/sigs.k8s.io/controller-runtime/SECURITY_CONTACTS
new file mode 100644
index 000000000..32e6a3b90
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/SECURITY_CONTACTS
@@ -0,0 +1,14 @@
+# Defined below are the security contacts for this repo.
+#
+# They are the contact point for the Product Security Team to reach out
+# to for triaging and handling of incoming issues.
+#
+# The below names agree to abide by the
+# [Embargo Policy](https://github.com/kubernetes/sig-release/blob/master/security-release-process-documentation/security-release-process.md#embargo-policy)
+# and will be removed and replaced if they violate that agreement.
+#
+# DO NOT REPORT SECURITY VULNERABILITIES DIRECTLY TO THESE NAMES, FOLLOW THE
+# INSTRUCTIONS AT https://kubernetes.io/security/
+
+pwittrock
+droot
diff --git a/vendor/sigs.k8s.io/controller-runtime/TMP-LOGGING.md b/vendor/sigs.k8s.io/controller-runtime/TMP-LOGGING.md
new file mode 100644
index 000000000..b3cfc6651
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/TMP-LOGGING.md
@@ -0,0 +1,169 @@
+Logging Guidelines
+==================
+
+controller-runtime uses a kind of logging called *structured logging*. If
+you've used a library like Zap or logrus before, you'll be familiar with
+the concepts we use. If you've only used a logging library like the "log"
+package (in the Go standard library) or "glog" (in Kubernetes), you'll
+need to adjust how you think about logging a bit.
+
+### Getting Started With Structured Logging
+
+With structured logging, we associate a *constant* log message with some
+variable key-value pairs. For instance, suppose we wanted to log that we
+were starting reconciliation on a pod. In the Go standard library logger,
+we might write:
+
+```go
+log.Printf("starting reconciliation for pod %s/%s", podNamespace, podName)
+```
+
+In controller-runtime, we'd instead write:
+
+```go
+logger.Info("starting reconciliation", "pod", req.NamespacedNamed)
+```
+
+or even write
+
+```go
+func (r *Reconciler) Reconcile(req reconcile.Request) (reconcile.Response, error) {
+ logger := logger.WithValues("pod", req.NamespacedName)
+ // do some stuff
+ logger.Info("starting reconciliation")
+}
+```
+
+Notice how we've broken out the information that we want to convey into
+a constant message (`"starting reconciliation"`) and some key-value pairs
+that convey variable information (`"pod", req.NamespacedName`). We've
+there-by added "structure" to our logs, which makes them easier to save
+and search later, as well as correlate with metrics and events.
+
+All of controller-runtime's logging is done via
+[logr](https://github.com/go-logr/logr), a generic interface for
+structured logging. You can use whichever logging library you want to
+implement the actual mechanics of the logging. controller-runtime
+provides some helpers to make it easy to use
+[Zap](https://go.uber.org/zap) as the implementation.
+
+You can configure the logging implementation using
+`"sigs.k8s.io/controller-runtime/pkg/log".SetLogger`. That
+package also contains the convenience functions for setting up Zap.
+
+You can get a handle to the the "root" logger using
+`"sigs.k8s.io/controller-runtime/pkg/log".Log`, and can then call
+`WithName` to create individual named loggers. You can call `WithName`
+repeatedly to chain names together:
+
+```go
+logger := log.Log.WithName("controller").WithName("replicaset")
+// in reconcile...
+logger = logger.WithValues("replicaset", req.NamespacedName)
+// later on in reconcile...
+logger.Info("doing things with pods", "pod", newPod)
+```
+
+As seen above, you can also call `WithValue` to create a new sub-logger
+that always attaches some key-value pairs to a logger.
+
+Finally, you can use `V(1)` to mark a particular log line as "debug" logs:
+
+```go
+logger.V(1).Info("this is particularly verbose!", "state of the world",
+allKubernetesObjectsEverywhere)
+```
+
+While it's possible to use higher log levels, it's recommended that you
+stick with `V(1)` or `V(0)` (which is equivalent to not specifying `V`),
+and then filter later based on key-value pairs or messages; different
+numbers tend to lose meaning easily over time, and you'll be left
+wondering why particular logs lines are at `V(5)` instead of `V(7)`.
+
+## Logging errors
+
+Errors should *always* be logged with `log.Error`, which allows logr
+implementations to provide special handling of errors (for instance,
+providing stack traces in debug mode).
+
+It's acceptable to log call `log.Error` with a nil error object. This
+conveys that an error occurred in some capacity, but that no actual
+`error` object was involved.
+
+Errors returned by the `Reconcile` implementation of the `Reconciler` interface are commonly logged as a `Reconciler error`.
+It's a developer choice to create an additional error log in the `Reconcile` implementation so a more specific file name and line for the error are returned.
+
+## Logging messages
+
+- Don't put variable content in your messages -- use key-value pairs for
+ that. Never use `fmt.Sprintf` in your message.
+
+- Try to match the terminology in your messages with your key-value pairs
+ -- for instance, if you have a key-value pairs `api version`, use the
+ term `APIVersion` instead of `GroupVersion` in your message.
+
+## Logging Kubernetes Objects
+
+Kubernetes objects should be logged directly, like `log.Info("this is
+a Kubernetes object", "pod", somePod)`. controller-runtime provides
+a special encoder for Zap that will transform Kubernetes objects into
+`name, namespace, apiVersion, kind` objects, when available and not in
+development mode. Other logr implementations should implement similar
+logic.
+
+## Logging Structured Values (Key-Value pairs)
+
+- Use lower-case, space separated keys. For example `object` for objects,
+ `api version` for `APIVersion`
+
+- Be consistent across your application, and with controller-runtime when
+ possible.
+
+- Try to be brief but descriptive.
+
+- Match terminology in keys with terminology in the message.
+
+- Be careful logging non-Kubernetes objects verbatim if they're very
+ large.
+
+### Groups, Versions, and Kinds
+
+- Kinds should not be logged alone (they're meaningless alone). Use
+ a `GroupKind` object to log them instead, or a `GroupVersionKind` when
+ version is relevant.
+
+- If you need to log an API version string, use `api version` as the key
+ (formatted as with a `GroupVersion`, or as received directly from API
+ discovery).
+
+### Objects and Types
+
+- If code works with a generic Kubernetes `runtime.Object`, use the
+ `object` key. For specific objects, prefer the resource name as the key
+ (e.g. `pod` for `v1.Pod` objects).
+
+- For non-Kubernetes objects, the `object` key may also be used, if you
+ accept a generic interface.
+
+- When logging a raw type, log it using the `type` key, with a value of
+ `fmt.Sprintf("%T", typ)`
+
+- If there's specific context around a type, the key may be more specific,
+ but should end with `type` -- for instance, `OwnerType` should be logged
+ as `owner` in the context of `log.Error(err, "Could not get ObjectKinds
+ for OwnerType", `owner type`, fmt.Sprintf("%T"))`. When possible, favor
+ communicating kind instead.
+
+### Multiple things
+
+- When logging multiple things, simply pluralize the key.
+
+### controller-runtime Specifics
+
+- Reconcile requests should be logged as `request`, although normal code
+ should favor logging the key.
+
+- Reconcile keys should be logged as with the same key as if you were
+ logging the object directly (e.g. `log.Info("reconciling pod", "pod",
+ req.NamespacedName)`). This ends up having a similar effect to logging
+ the object directly.
diff --git a/vendor/sigs.k8s.io/controller-runtime/VERSIONING.md b/vendor/sigs.k8s.io/controller-runtime/VERSIONING.md
new file mode 100644
index 000000000..18779000e
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/VERSIONING.md
@@ -0,0 +1,30 @@
+# Versioning and Branching in controller-runtime
+
+We follow the [common KubeBuilder versioning guidelines][guidelines], and
+use the corresponding tooling.
+
+For the purposes of the aforementioned guidelines, controller-runtime
+counts as a "library project", but otherwise follows the guidelines
+exactly.
+
+[guidelines]: https://sigs.k8s.io/kubebuilder-release-tools/VERSIONING.md
+
+## Compatiblity and Release Support
+
+For release branches, we generally tend to support backporting one (1)
+major release (`release-{X-1}` or `release-0.{Y-1}`), but may go back
+further if the need arises and is very pressing (e.g. security updates).
+
+### Dependency Support
+
+Note the [guidelines on dependency versions][dep-versions]. Particularly:
+
+- We **DO** guarantee Kubernetes REST API compability -- if a given
+ version of controller-runtime stops working with what should be
+ a supported version of Kubernetes, this is almost certainly a bug.
+
+- We **DO NOT** guarantee any particular compability matrix between
+ kubernetes library dependencies (client-go, apimachinery, etc); Such
+ compability is infeasible due to the way those libraries are versioned.
+
+[dep-versions]: https://sigs.k8s.io/kubebuilder-release-tools/VERSIONING.md#kubernetes-version-compatibility
diff --git a/vendor/sigs.k8s.io/controller-runtime/alias.go b/vendor/sigs.k8s.io/controller-runtime/alias.go
new file mode 100644
index 000000000..29f964dcb
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/alias.go
@@ -0,0 +1,150 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package controllerruntime
+
+import (
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "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"
+ "sigs.k8s.io/controller-runtime/pkg/manager/signals"
+ "sigs.k8s.io/controller-runtime/pkg/reconcile"
+ "sigs.k8s.io/controller-runtime/pkg/scheme"
+)
+
+// Builder builds an Application ControllerManagedBy (e.g. Operator) and returns a manager.Manager to start it.
+type Builder = builder.Builder
+
+// Request contains the information necessary to reconcile a Kubernetes object. This includes the
+// information to uniquely identify the object - its Name and Namespace. It does NOT contain information about
+// any specific Event or the object contents itself.
+type Request = reconcile.Request
+
+// Result contains the result of a Reconciler invocation.
+type Result = reconcile.Result
+
+// Manager initializes shared dependencies such as Caches and Clients, and provides them to Runnables.
+// A Manager is required to create Controllers.
+type Manager = manager.Manager
+
+// Options are the arguments for creating a new Manager.
+type Options = manager.Options
+
+// SchemeBuilder builds a new Scheme for mapping go types to Kubernetes GroupVersionKinds.
+type SchemeBuilder = scheme.Builder
+
+// GroupVersion contains the "group" and the "version", which uniquely identifies the API.
+type GroupVersion = schema.GroupVersion
+
+// GroupResource specifies a Group and a Resource, but does not force a version. This is useful for identifying
+// concepts during lookup stages without having partially valid types.
+type GroupResource = schema.GroupResource
+
+// TypeMeta describes an individual object in an API response or request
+// with strings representing the type of the object and its API schema version.
+// Structures that are versioned or persisted should inline TypeMeta.
+//
+// +k8s:deepcopy-gen=false
+type TypeMeta = metav1.TypeMeta
+
+// ObjectMeta is metadata that all persisted resources must have, which includes all objects
+// users must create.
+type ObjectMeta = metav1.ObjectMeta
+
+var (
+ // GetConfigOrDie creates a *rest.Config for talking to a Kubernetes apiserver.
+ // If --kubeconfig is set, will use the kubeconfig file at that location. Otherwise will assume running
+ // in cluster and use the cluster provided kubeconfig.
+ //
+ // Will log an error and exit if there is an error creating the rest.Config.
+ GetConfigOrDie = config.GetConfigOrDie
+
+ // GetConfig creates a *rest.Config for talking to a Kubernetes apiserver.
+ // If --kubeconfig is set, will use the kubeconfig file at that location. Otherwise will assume running
+ // in cluster and use the cluster provided kubeconfig.
+ //
+ // Config precedence
+ //
+ // * --kubeconfig flag pointing at a file
+ //
+ // * KUBECONFIG environment variable pointing at a file
+ //
+ // * In-cluster config if running in cluster
+ //
+ // * $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.
+ ConfigFile = cfg.File
+
+ // NewControllerManagedBy returns a new controller builder that will be started by the provided Manager.
+ NewControllerManagedBy = builder.ControllerManagedBy
+
+ // NewWebhookManagedBy returns a new webhook builder that will be started by the provided Manager.
+ NewWebhookManagedBy = builder.WebhookManagedBy
+
+ // NewManager returns a new Manager for creating Controllers.
+ NewManager = manager.New
+
+ // CreateOrUpdate creates or updates the given object obj in the Kubernetes
+ // cluster. The object's desired state should be reconciled with the existing
+ // state using the passed in ReconcileFn. obj must be a struct pointer so that
+ // obj can be updated with the content returned by the Server.
+ //
+ // It returns the executed operation and an error.
+ CreateOrUpdate = controllerutil.CreateOrUpdate
+
+ // SetControllerReference sets owner as a Controller OwnerReference on owned.
+ // This is used for garbage collection of the owned object and for
+ // reconciling the owner object on changes to owned (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.
+ 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
+ // is terminated with exit code 1.
+ SetupSignalHandler = signals.SetupSignalHandler
+
+ // Log is the base logger used by controller-runtime. It delegates
+ // to another logr.Logger. You *must* call SetLogger to
+ // get any actual logging.
+ Log = log.Log
+
+ // LoggerFrom returns a logger with predefined values from a context.Context.
+ // The logger, when used with controllers, can be expected to contain basic information about the object
+ // that's being reconciled like:
+ // - `reconciler group` and `reconciler kind` coming from the For(...) object passed in when building a controller.
+ // - `name` and `namespace` injected from the reconciliation request.
+ //
+ // This is meant to be used with the context supplied in a struct that satisfies the Reconciler interface.
+ LoggerFrom = log.FromContext
+
+ // LoggerInto takes a context and sets the logger as one of its keys.
+ //
+ // This is meant to be used in reconcilers to enrich the logger within a context with additional values.
+ LoggerInto = log.IntoContext
+
+ // SetLogger sets a concrete logging implementation for all deferred Loggers.
+ SetLogger = log.SetLogger
+)
diff --git a/vendor/sigs.k8s.io/controller-runtime/code-of-conduct.md b/vendor/sigs.k8s.io/controller-runtime/code-of-conduct.md
new file mode 100644
index 000000000..0d15c00cf
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/code-of-conduct.md
@@ -0,0 +1,3 @@
+# Kubernetes Community Code of Conduct
+
+Please refer to our [Kubernetes Community Code of Conduct](https://git.k8s.io/community/code-of-conduct.md)
diff --git a/vendor/sigs.k8s.io/controller-runtime/doc.go b/vendor/sigs.k8s.io/controller-runtime/doc.go
new file mode 100644
index 000000000..429eb129f
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/doc.go
@@ -0,0 +1,127 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Package controllerruntime provides tools to construct Kubernetes-style
+// controllers that manipulate both Kubernetes CRDs and aggregated/built-in
+// Kubernetes APIs.
+//
+// It defines easy helpers for the common use cases when building CRDs, built
+// on top of customizable layers of abstraction. Common cases should be easy,
+// and uncommon cases should be possible. In general, controller-runtime tries
+// to guide users towards Kubernetes controller best-practices.
+//
+// Getting Started
+//
+// The main entrypoint for controller-runtime is this root package, which
+// contains all of the common types needed to get started building controllers:
+// import (
+// ctrl "sigs.k8s.io/controller-runtime"
+// )
+//
+// The examples in this package walk through a basic controller setup. The
+// kubebuilder book (https://book.kubebuilder.io) has some more in-depth
+// walkthroughs.
+//
+// controller-runtime favors structs with sane defaults over constructors, so
+// it's fairly common to see structs being used directly in controller-runtime.
+//
+// Organization
+//
+// A brief-ish walkthrough of the layout of this library can be found below. Each
+// package contains more information about how to use it.
+//
+// Frequently asked questions about using controller-runtime and designing
+// controllers can be found at
+// https://github.com/kubernetes-sigs/controller-runtime/blob/master/FAQ.md.
+//
+// Managers
+//
+// Every controller and webhook is ultimately run by a Manager (pkg/manager). A
+// manager is responsible for running controllers and webhooks, and setting up
+// common dependencies (pkg/runtime/inject), like shared caches and clients, as
+// well as managing leader election (pkg/leaderelection). Managers are
+// generally configured to gracefully shut down controllers on pod termination
+// by wiring up a signal handler (pkg/manager/signals).
+//
+// Controllers
+//
+// Controllers (pkg/controller) use events (pkg/event) to eventually trigger
+// reconcile requests. They may be constructed manually, but are often
+// constructed with a Builder (pkg/builder), which eases the wiring of event
+// sources (pkg/source), like Kubernetes API object changes, to event handlers
+// (pkg/handler), like "enqueue a reconcile request for the object owner".
+// Predicates (pkg/predicate) can be used to filter which events actually
+// trigger reconciles. There are pre-written utilities for the common cases, and
+// interfaces and helpers for advanced cases.
+//
+// Reconcilers
+//
+// Controller logic is implemented in terms of Reconcilers (pkg/reconcile). A
+// Reconciler implements a function which takes a reconcile Request containing
+// the name and namespace of the object to reconcile, reconciles the object,
+// and returns a Response or an error indicating whether to requeue for a
+// second round of processing.
+//
+// Clients and Caches
+//
+// Reconcilers use Clients (pkg/client) to access API objects. The default
+// client provided by the manager reads from a local shared cache (pkg/cache)
+// and writes directly to the API server, but clients can be constructed that
+// only talk to the API server, without a cache. The Cache will auto-populate
+// with watched objects, as well as when other structured objects are
+// requested. The default split client does not promise to invalidate the cache
+// during writes (nor does it promise sequential create/get coherence), and code
+// should not assume a get immediately following a create/update will return
+// the updated resource. Caches may also have indexes, which can be created via
+// a FieldIndexer (pkg/client) obtained from the manager. Indexes can used to
+// quickly and easily look up all objects with certain fields set. Reconcilers
+// may retrieve event recorders (pkg/recorder) to emit events using the
+// manager.
+//
+// Schemes
+//
+// Clients, Caches, and many other things in Kubernetes use Schemes
+// (pkg/scheme) to associate Go types to Kubernetes API Kinds
+// (Group-Version-Kinds, to be specific).
+//
+// Webhooks
+//
+// Similarly, webhooks (pkg/webhook/admission) may be implemented directly, but
+// are often constructed using a builder (pkg/webhook/admission/builder). They
+// are run via a server (pkg/webhook) which is managed by a Manager.
+//
+// Logging and Metrics
+//
+// Logging (pkg/log) in controller-runtime is done via structured logs, using a
+// log set of interfaces called logr
+// (https://godoc.org/github.com/go-logr/logr). While controller-runtime
+// provides easy setup for using Zap (https://go.uber.org/zap, pkg/log/zap),
+// you can provide any implementation of logr as the base logger for
+// controller-runtime.
+//
+// Metrics (pkg/metrics) provided by controller-runtime are registered into a
+// controller-runtime-specific Prometheus metrics registry. The manager can
+// serve these by an HTTP endpoint, and additional metrics may be registered to
+// this Registry as normal.
+//
+// Testing
+//
+// You can easily build integration and unit tests for your controllers and
+// webhooks using the test Environment (pkg/envtest). This will automatically
+// stand up a copy of etcd and kube-apiserver, and provide the correct options
+// to connect to the API server. It's designed to work well with the Ginkgo
+// testing framework, but should work with any testing setup.
+package controllerruntime
diff --git a/vendor/sigs.k8s.io/controller-runtime/go.mod b/vendor/sigs.k8s.io/controller-runtime/go.mod
new file mode 100644
index 000000000..fabf9b8f6
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/go.mod
@@ -0,0 +1,28 @@
+module sigs.k8s.io/controller-runtime
+
+go 1.16
+
+require (
+ github.com/evanphx/json-patch v4.11.0+incompatible
+ github.com/fsnotify/fsnotify v1.4.9
+ github.com/go-logr/logr v0.4.0
+ github.com/go-logr/zapr v0.4.0
+ github.com/imdario/mergo v0.3.12 // indirect
+ github.com/onsi/ginkgo v1.16.4
+ github.com/onsi/gomega v1.15.0
+ github.com/prometheus/client_golang v1.11.0
+ github.com/prometheus/client_model v0.2.0
+ go.uber.org/goleak v1.1.10
+ go.uber.org/zap v1.19.0
+ golang.org/x/sys v0.0.0-20210817190340-bfb29a6856f2
+ golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac
+ gomodules.xyz/jsonpatch/v2 v2.2.0
+ google.golang.org/appengine v1.6.7 // indirect
+ k8s.io/api v0.22.2
+ k8s.io/apiextensions-apiserver v0.22.2
+ k8s.io/apimachinery v0.22.2
+ k8s.io/client-go v0.22.2
+ k8s.io/component-base v0.22.2
+ k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a
+ sigs.k8s.io/yaml v1.2.0
+)
diff --git a/vendor/sigs.k8s.io/controller-runtime/go.sum b/vendor/sigs.k8s.io/controller-runtime/go.sum
new file mode 100644
index 000000000..293f15d33
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/go.sum
@@ -0,0 +1,769 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
+cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
+cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
+cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
+cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
+cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
+cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
+cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
+cloud.google.com/go v0.54.0 h1:3ithwDMr7/3vpAMXiH+ZQnYbuIsh+OPhUPMFC9enmn0=
+cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
+cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
+cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
+cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
+cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
+cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
+cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
+cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
+cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
+cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
+cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
+cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
+cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
+dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
+github.com/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
+github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
+github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
+github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA=
+github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=
+github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
+github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
+github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
+github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
+github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
+github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
+github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
+github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
+github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
+github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
+github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
+github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
+github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
+github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
+github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
+github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
+github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
+github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
+github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
+github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
+github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
+github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
+github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
+github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
+github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
+github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
+github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
+github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
+github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
+github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
+github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
+github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
+github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
+github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
+github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo=
+github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA=
+github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
+github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
+github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
+github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
+github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
+github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
+github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
+github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
+github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
+github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
+github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
+github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
+github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
+github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ=
+github.com/evanphx/json-patch v4.11.0+incompatible h1:glyUF9yIYtMHzn8xaKw5rMhdWcwsYV8dZHIq5567/xs=
+github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
+github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
+github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
+github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
+github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
+github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
+github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
+github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
+github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
+github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
+github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
+github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
+github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
+github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
+github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc=
+github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
+github.com/go-logr/zapr v0.4.0 h1:uc1uML3hRYL9/ZZPdgHS/n8Nzo+eaYL/Efxkkamf7OM=
+github.com/go-logr/zapr v0.4.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk=
+github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
+github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
+github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
+github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
+github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
+github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
+github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
+github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
+github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
+github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
+github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
+github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
+github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
+github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
+github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
+github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
+github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
+github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
+github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
+github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
+github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
+github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
+github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
+github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
+github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
+github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU=
+github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw=
+github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA=
+github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
+github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
+github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
+github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
+github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
+github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
+github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
+github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
+github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
+github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
+github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
+github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
+github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
+github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
+github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
+github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
+github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
+github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
+github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
+github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
+github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
+github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
+github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
+github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
+github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
+github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
+github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
+github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
+github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
+github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ=
+github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
+github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
+github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
+github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
+github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
+github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
+github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
+github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
+github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
+github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
+github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
+github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=
+github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
+github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
+github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
+github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
+github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
+github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
+github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
+github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
+github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
+github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
+github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
+github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
+github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
+github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
+github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
+github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
+github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
+github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
+github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
+github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
+github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
+github.com/onsi/gomega v1.15.0 h1:WjP/FQ/sk43MRmnEcT+MlDw2TFvkrXlprrPST/IudjU=
+github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0=
+github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
+github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
+github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
+github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
+github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
+github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
+github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
+github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
+github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
+github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ=
+github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
+github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
+github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
+github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
+github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ=
+github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
+github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
+github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
+github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
+github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
+github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
+github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
+github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
+github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
+github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
+github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
+github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
+github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
+github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
+github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
+github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
+github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
+github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
+github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
+github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
+github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=
+github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
+github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
+github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
+github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
+github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
+github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
+go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
+go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
+go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
+go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
+go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0=
+go.etcd.io/etcd/pkg/v3 v3.5.0/go.mod h1:UzJGatBQ1lXChBkQF0AuAtkRQMYnHubxAEYIrC3MSsE=
+go.etcd.io/etcd/raft/v3 v3.5.0/go.mod h1:UFOHSIvO/nKwd4lhkwabrTD3cqW5yVyYYf/KlD00Szc=
+go.etcd.io/etcd/server/v3 v3.5.0/go.mod h1:3Ah5ruV+M+7RZr0+Y/5mNLwC+eQlni+mQmOVdCRJoS4=
+go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
+go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
+go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4=
+go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo=
+go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM=
+go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU=
+go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw=
+go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc=
+go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE=
+go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE=
+go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw=
+go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
+go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
+go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
+go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0=
+go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
+go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
+go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
+go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
+go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
+go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
+go.uber.org/zap v1.19.0 h1:mZQZefskPPCMIBCSEH0v2/iUqqLrYtaeqwD6FUGUnFE=
+go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
+golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
+golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
+golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
+golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
+golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
+golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
+golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
+golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug=
+golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
+golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
+golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
+golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
+golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
+golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
+golang.org/x/net v0.0.0-20210520170846-37e1c6afe023 h1:ADo5wSpq2gqaCGQWzk7S5vd//0iyyLeAratkEoG5dLE=
+golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
+golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210817190340-bfb29a6856f2 h1:c8PlLMqBbOHoqtjteWm5/kbe6rNY2pbRfbIMVnepueo=
+golang.org/x/sys v0.0.0-20210817190340-bfb29a6856f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE=
+golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs=
+golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
+golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.1.2 h1:kRBLX7v7Af8W7Gdbbc908OJcdgtK8bOz9Uaj8/F1ACA=
+golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY=
+gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY=
+google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
+google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
+google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
+google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
+google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
+google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
+google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
+google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
+google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
+google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
+google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
+google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
+google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
+google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
+google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
+google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
+google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
+google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
+google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
+google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
+google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
+google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
+google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
+gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
+gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
+gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
+gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
+gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
+gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
+gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
+honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
+k8s.io/api v0.22.2 h1:M8ZzAD0V6725Fjg53fKeTJxGsJvRbk4TEm/fexHMtfw=
+k8s.io/api v0.22.2/go.mod h1:y3ydYpLJAaDI+BbSe2xmGcqxiWHmWjkEeIbiwHvnPR8=
+k8s.io/apiextensions-apiserver v0.22.2 h1:zK7qI8Ery7j2CaN23UCFaC1hj7dMiI87n01+nKuewd4=
+k8s.io/apiextensions-apiserver v0.22.2/go.mod h1:2E0Ve/isxNl7tWLSUDgi6+cmwHi5fQRdwGVCxbC+KFA=
+k8s.io/apimachinery v0.22.2 h1:ejz6y/zNma8clPVfNDLnPbleBo6MpoFy/HBiBqCouVk=
+k8s.io/apimachinery v0.22.2/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0=
+k8s.io/apiserver v0.22.2/go.mod h1:vrpMmbyjWrgdyOvZTSpsusQq5iigKNWv9o9KlDAbBHI=
+k8s.io/client-go v0.22.2 h1:DaSQgs02aCC1QcwUdkKZWOeaVsQjYvWv8ZazcZ6JcHc=
+k8s.io/client-go v0.22.2/go.mod h1:sAlhrkVDf50ZHx6z4K0S40wISNTarf1r800F+RlCF6U=
+k8s.io/code-generator v0.22.2/go.mod h1:eV77Y09IopzeXOJzndrDyCI88UBok2h6WxAlBwpxa+o=
+k8s.io/component-base v0.22.2 h1:vNIvE0AIrLhjX8drH0BgCNJcR4QZxMXcJzBsDplDx9M=
+k8s.io/component-base v0.22.2/go.mod h1:5Br2QhI9OTe79p+TzPe9JKNQYvEKbq9rTJDWllunGug=
+k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
+k8s.io/gengo v0.0.0-20201214224949-b6c5ce23f027/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
+k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
+k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
+k8s.io/klog/v2 v2.9.0 h1:D7HV+n1V57XeZ0m6tdRkfknthUaM06VFbWldOFh8kzM=
+k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=
+k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM=
+k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
+k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a h1:8dYfu/Fc9Gz2rNJKB9IQRGgQOh2clmRzNIPPY1xLY5g=
+k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
+rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
+rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
+rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
+sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg=
+sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
+sigs.k8s.io/structured-merge-diff/v4 v4.1.2 h1:Hr/htKFmJEbtMgS/UD0N+gtgctAqz81t3nu+sPzynno=
+sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=
+sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
+sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/builder/controller.go b/vendor/sigs.k8s.io/controller-runtime/pkg/builder/controller.go
new file mode 100644
index 000000000..2cd4ce9de
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/builder/controller.go
@@ -0,0 +1,316 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package builder
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/go-logr/logr"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/client/apiutil"
+ "sigs.k8s.io/controller-runtime/pkg/controller"
+ "sigs.k8s.io/controller-runtime/pkg/handler"
+ "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 the we can use to
+// send/receive a given resource (metadata-only, unstructured, etc).
+type objectProjection int
+
+const (
+ // projectAsNormal doesn't change the object from the form given.
+ projectAsNormal objectProjection = iota
+ // projectAsMetadata turns this into an metadata-only watch.
+ projectAsMetadata
+)
+
+// Builder builds a Controller.
+type Builder struct {
+ forInput ForInput
+ ownsInput []OwnsInput
+ watchesInput []WatchesInput
+ mgr manager.Manager
+ globalPredicates []predicate.Predicate
+ ctrl controller.Controller
+ ctrlOptions controller.Options
+ name string
+}
+
+// ControllerManagedBy returns a new controller builder that will be started by the provided Manager.
+func ControllerManagedBy(m manager.Manager) *Builder {
+ return &Builder{mgr: m}
+}
+
+// ForInput represents the information set by For method.
+type ForInput struct {
+ object client.Object
+ predicates []predicate.Predicate
+ objectProjection objectProjection
+ err error
+}
+
+// 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 {
+ 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
+ }
+ input := ForInput{object: object}
+ for _, opt := range opts {
+ opt.ApplyToFor(&input)
+ }
+
+ blder.forInput = input
+ return blder
+}
+
+// OwnsInput represents the information set by Owns method.
+type OwnsInput struct {
+ object client.Object
+ predicates []predicate.Predicate
+ objectProjection objectProjection
+}
+
+// Owns defines types of Objects being *generated* by the ControllerManagedBy, and configures the ControllerManagedBy to respond to
+// create / delete / update events by *reconciling the owner object*. This is the equivalent of calling
+// Watches(&source.Kind{Type: <ForType-forInput>}, &handler.EnqueueRequestForOwner{OwnerType: apiType, IsController: true}).
+func (blder *Builder) Owns(object client.Object, opts ...OwnsOption) *Builder {
+ input := OwnsInput{object: object}
+ for _, opt := range opts {
+ opt.ApplyToOwns(&input)
+ }
+
+ blder.ownsInput = append(blder.ownsInput, input)
+ return blder
+}
+
+// WatchesInput represents the information set by Watches method.
+type WatchesInput struct {
+ src source.Source
+ eventhandler handler.EventHandler
+ predicates []predicate.Predicate
+ objectProjection objectProjection
+}
+
+// Watches exposes the lower-level ControllerManagedBy Watches functions through the builder. Consider using
+// Owns or For instead of Watches directly.
+// Specified predicates are registered only for given source.
+func (blder *Builder) Watches(src source.Source, eventhandler handler.EventHandler, opts ...WatchesOption) *Builder {
+ input := WatchesInput{src: src, eventhandler: eventhandler}
+ for _, opt := range opts {
+ opt.ApplyToWatches(&input)
+ }
+
+ 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.
+// Defaults to the empty list.
+func (blder *Builder) WithEventFilter(p predicate.Predicate) *Builder {
+ blder.globalPredicates = append(blder.globalPredicates, p)
+ return blder
+}
+
+// WithOptions overrides the controller options use in doController. Defaults to empty.
+func (blder *Builder) WithOptions(options controller.Options) *Builder {
+ blder.ctrlOptions = options
+ return blder
+}
+
+// WithLogger overrides the controller options's logger used.
+func (blder *Builder) WithLogger(log logr.Logger) *Builder {
+ blder.ctrlOptions.Log = log
+ return blder
+}
+
+// Named sets the name of the controller to the given name. The name shows up
+// in metrics, among other things, and thus should be a prometheus compatible name
+// (underscores and alphanumeric characters only).
+//
+// By default, controllers are named using the lowercase version of their kind.
+func (blder *Builder) Named(name string) *Builder {
+ blder.name = name
+ return blder
+}
+
+// Complete builds the Application Controller.
+func (blder *Builder) Complete(r reconcile.Reconciler) 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) {
+ if r == nil {
+ return nil, fmt.Errorf("must provide a non-nil Reconciler")
+ }
+ if blder.mgr == nil {
+ return nil, fmt.Errorf("must provide a non-nil Manager")
+ }
+ if blder.forInput.err != nil {
+ return nil, blder.forInput.err
+ }
+ // Checking the reconcile type exist or not
+ if blder.forInput.object == nil {
+ return nil, fmt.Errorf("must provide an object for reconciliation")
+ }
+
+ // Set the ControllerManagedBy
+ if err := blder.doController(r); err != nil {
+ return nil, err
+ }
+
+ // Set the Watch
+ if err := blder.doWatch(); err != nil {
+ return nil, err
+ }
+
+ return blder.ctrl, nil
+}
+
+func (blder *Builder) 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())
+ if err != nil {
+ return nil, fmt.Errorf("unable to determine GVK of %T for a metadata-only watch: %w", obj, err)
+ }
+ metaObj.SetGroupVersionKind(gvk)
+ return metaObj, nil
+ default:
+ panic(fmt.Sprintf("unexpected projection type %v on type %T, should not be possible since this is an internal field", proj, obj))
+ }
+}
+
+func (blder *Builder) doWatch() error {
+ // Reconcile type
+ typeForSrc, err := blder.project(blder.forInput.object, blder.forInput.objectProjection)
+ if err != nil {
+ return err
+ }
+ src := &source.Kind{Type: typeForSrc}
+ hdler := &handler.EnqueueRequestForObject{}
+ allPredicates := append(blder.globalPredicates, blder.forInput.predicates...)
+ if err := blder.ctrl.Watch(src, hdler, allPredicates...); err != nil {
+ return err
+ }
+
+ // Watches the managed types
+ for _, own := range blder.ownsInput {
+ typeForSrc, err := blder.project(own.object, own.objectProjection)
+ if err != nil {
+ return err
+ }
+ src := &source.Kind{Type: typeForSrc}
+ hdler := &handler.EnqueueRequestForOwner{
+ OwnerType: blder.forInput.object,
+ IsController: true,
+ }
+ allPredicates := append([]predicate.Predicate(nil), blder.globalPredicates...)
+ allPredicates = append(allPredicates, own.predicates...)
+ if err := blder.ctrl.Watch(src, hdler, allPredicates...); err != nil {
+ return err
+ }
+ }
+
+ // Do the watch requests
+ for _, w := range blder.watchesInput {
+ allPredicates := append([]predicate.Predicate(nil), blder.globalPredicates...)
+ allPredicates = append(allPredicates, w.predicates...)
+
+ // If the source of this watch is of type *source.Kind, project it.
+ if srckind, ok := w.src.(*source.Kind); ok {
+ typeForSrc, err := blder.project(srckind.Type, w.objectProjection)
+ if err != nil {
+ return err
+ }
+ srckind.Type = typeForSrc
+ }
+
+ if err := blder.ctrl.Watch(w.src, w.eventhandler, allPredicates...); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (blder *Builder) getControllerName(gvk schema.GroupVersionKind) string {
+ if blder.name != "" {
+ return blder.name
+ }
+ return strings.ToLower(gvk.Kind)
+}
+
+func (blder *Builder) doController(r reconcile.Reconciler) error {
+ globalOpts := blder.mgr.GetControllerOptions()
+
+ ctrlOptions := blder.ctrlOptions
+ if ctrlOptions.Reconciler == nil {
+ ctrlOptions.Reconciler = r
+ }
+
+ // Retrieve the GVK from the object we're reconciling
+ // to prepopulate logger information, and to optionally generate a default name.
+ gvk, err := getGvk(blder.forInput.object, blder.mgr.GetScheme())
+ if err != nil {
+ return err
+ }
+
+ // Setup concurrency.
+ if ctrlOptions.MaxConcurrentReconciles == 0 {
+ groupKind := gvk.GroupKind().String()
+
+ if concurrency, ok := globalOpts.GroupKindConcurrency[groupKind]; ok && concurrency > 0 {
+ ctrlOptions.MaxConcurrentReconciles = concurrency
+ }
+ }
+
+ // Setup cache sync timeout.
+ if ctrlOptions.CacheSyncTimeout == 0 && globalOpts.CacheSyncTimeout != nil {
+ ctrlOptions.CacheSyncTimeout = *globalOpts.CacheSyncTimeout
+ }
+
+ // Setup the logger.
+ if ctrlOptions.Log == nil {
+ ctrlOptions.Log = blder.mgr.GetLogger()
+ }
+ ctrlOptions.Log = ctrlOptions.Log.WithValues("reconciler group", gvk.Group, "reconciler kind", gvk.Kind)
+
+ // Build the controller and return.
+ blder.ctrl, err = newController(blder.getControllerName(gvk), blder.mgr, ctrlOptions)
+ return err
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/builder/doc.go b/vendor/sigs.k8s.io/controller-runtime/pkg/builder/doc.go
new file mode 100644
index 000000000..e4df1b709
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/builder/doc.go
@@ -0,0 +1,28 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Package builder wraps other controller-runtime libraries and exposes simple
+// patterns for building common Controllers.
+//
+// Projects built with the builder package can trivially be rebased on top of the underlying
+// packages if the project requires more customized behavior in the future.
+package builder
+
+import (
+ logf "sigs.k8s.io/controller-runtime/pkg/internal/log"
+)
+
+var log = logf.RuntimeLog.WithName("builder")
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/builder/options.go b/vendor/sigs.k8s.io/controller-runtime/pkg/builder/options.go
new file mode 100644
index 000000000..7bb427309
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/builder/options.go
@@ -0,0 +1,117 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package builder
+
+import (
+ "sigs.k8s.io/controller-runtime/pkg/predicate"
+)
+
+// {{{ "Functional" Option Interfaces
+
+// ForOption is some configuration that modifies options for a For request.
+type ForOption interface {
+ // ApplyToFor applies this configuration to the given for input.
+ ApplyToFor(*ForInput)
+}
+
+// OwnsOption is some configuration that modifies options for a owns request.
+type OwnsOption interface {
+ // ApplyToOwns applies this configuration to the given owns input.
+ ApplyToOwns(*OwnsInput)
+}
+
+// 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)
+}
+
+// }}}
+
+// {{{ Multi-Type Options
+
+// WithPredicates sets the given predicates list.
+func WithPredicates(predicates ...predicate.Predicate) Predicates {
+ return Predicates{
+ predicates: predicates,
+ }
+}
+
+// Predicates filters events before enqueuing the keys.
+type Predicates struct {
+ predicates []predicate.Predicate
+}
+
+// ApplyToFor applies this configuration to the given ForInput options.
+func (w Predicates) ApplyToFor(opts *ForInput) {
+ opts.predicates = w.predicates
+}
+
+// ApplyToOwns applies this configuration to the given OwnsInput options.
+func (w Predicates) ApplyToOwns(opts *OwnsInput) {
+ opts.predicates = w.predicates
+}
+
+// ApplyToWatches applies this configuration to the given WatchesInput options.
+func (w Predicates) ApplyToWatches(opts *WatchesInput) {
+ opts.predicates = w.predicates
+}
+
+var _ ForOption = &Predicates{}
+var _ OwnsOption = &Predicates{}
+var _ WatchesOption = &Predicates{}
+
+// }}}
+
+// {{{ For & Owns Dual-Type options
+
+// asProjection configures the projection (currently only metadata) on the input.
+// Currently only metadata is supported. We might want to expand
+// this to arbitrary non-special local projections in the future.
+type projectAs objectProjection
+
+// ApplyToFor applies this configuration to the given ForInput options.
+func (p projectAs) ApplyToFor(opts *ForInput) {
+ opts.objectProjection = objectProjection(p)
+}
+
+// ApplyToOwns applies this configuration to the given OwnsInput options.
+func (p projectAs) ApplyToOwns(opts *OwnsInput) {
+ opts.objectProjection = objectProjection(p)
+}
+
+// ApplyToWatches applies this configuration to the given WatchesInput options.
+func (p projectAs) ApplyToWatches(opts *WatchesInput) {
+ opts.objectProjection = objectProjection(p)
+}
+
+var (
+ // OnlyMetadata tells the controller to *only* cache metadata, and to watch
+ // the the API server in metadata-only form. This is useful when watching
+ // lots of objects, really big objects, or objects for which you only know
+ // the the GVK, but not the structure. You'll need to pass
+ // metav1.PartialObjectMetadata to the client when fetching objects in your
+ // reconciler, otherwise you'll end up with a duplicate structured or
+ // unstructured cache.
+ OnlyMetadata = projectAs(projectAsMetadata)
+
+ _ ForOption = OnlyMetadata
+ _ OwnsOption = OnlyMetadata
+ _ WatchesOption = OnlyMetadata
+)
+
+// }}}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/builder/webhook.go b/vendor/sigs.k8s.io/controller-runtime/pkg/builder/webhook.go
new file mode 100644
index 000000000..18feb1cd7
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/builder/webhook.go
@@ -0,0 +1,209 @@
+/*
+Copyright 2019 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package builder
+
+import (
+ "errors"
+ "net/http"
+ "net/url"
+ "strings"
+
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "k8s.io/client-go/rest"
+ "sigs.k8s.io/controller-runtime/pkg/client/apiutil"
+ "sigs.k8s.io/controller-runtime/pkg/manager"
+ "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
+ "sigs.k8s.io/controller-runtime/pkg/webhook/conversion"
+)
+
+// WebhookBuilder builds a Webhook.
+type WebhookBuilder struct {
+ apiType runtime.Object
+ withDefaulter admission.CustomDefaulter
+ withValidator admission.CustomValidator
+ gvk schema.GroupVersionKind
+ mgr manager.Manager
+ config *rest.Config
+}
+
+// WebhookManagedBy allows inform its manager.Manager.
+func WebhookManagedBy(m manager.Manager) *WebhookBuilder {
+ return &WebhookBuilder{mgr: m}
+}
+
+// TODO(droot): update the GoDoc for conversion.
+
+// For takes a runtime.Object which should be a CR.
+// 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 {
+ blder.apiType = apiType
+ return blder
+}
+
+// WithDefaulter takes a admission.WithDefaulter interface, a MutatingWebhook will be wired for this type.
+func (blder *WebhookBuilder) WithDefaulter(defaulter admission.CustomDefaulter) *WebhookBuilder {
+ blder.withDefaulter = defaulter
+ return blder
+}
+
+// WithValidator takes a admission.WithValidator interface, a ValidatingWebhook will be wired for this type.
+func (blder *WebhookBuilder) WithValidator(validator admission.CustomValidator) *WebhookBuilder {
+ blder.withValidator = validator
+ return blder
+}
+
+// Complete builds the webhook.
+func (blder *WebhookBuilder) Complete() error {
+ // Set the Config
+ blder.loadRestConfig()
+
+ // Set the Webhook if needed
+ return blder.registerWebhooks()
+}
+
+func (blder *WebhookBuilder) loadRestConfig() {
+ if blder.config == nil {
+ blder.config = blder.mgr.GetConfig()
+ }
+}
+
+func (blder *WebhookBuilder) registerWebhooks() error {
+ typ, err := blder.getType()
+ if err != nil {
+ return err
+ }
+
+ // Create webhook(s) for each type
+ blder.gvk, err = apiutil.GVKForObject(typ, blder.mgr.GetScheme())
+ if err != nil {
+ return err
+ }
+
+ blder.registerDefaultingWebhook()
+ blder.registerValidatingWebhook()
+
+ err = blder.registerConversionWebhook()
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+// registerDefaultingWebhook registers a defaulting webhook if th.
+func (blder *WebhookBuilder) registerDefaultingWebhook() {
+ mwh := blder.getDefaultingWebhook()
+ if mwh != nil {
+ path := generateMutatePath(blder.gvk)
+
+ // Checking if the path is already registered.
+ // If so, just skip it.
+ if !blder.isAlreadyHandled(path) {
+ log.Info("Registering a mutating webhook",
+ "GVK", blder.gvk,
+ "path", path)
+ blder.mgr.GetWebhookServer().Register(path, mwh)
+ }
+ }
+}
+
+func (blder *WebhookBuilder) getDefaultingWebhook() *admission.Webhook {
+ if defaulter := blder.withDefaulter; defaulter != nil {
+ return admission.WithCustomDefaulter(blder.apiType, defaulter)
+ }
+ if defaulter, ok := blder.apiType.(admission.Defaulter); ok {
+ return admission.DefaultingWebhookFor(defaulter)
+ }
+ log.Info(
+ "skip registering a mutating webhook, object does not implement admission.Defaulter or WithDefaulter wasn't called",
+ "GVK", blder.gvk)
+ return nil
+}
+
+func (blder *WebhookBuilder) registerValidatingWebhook() {
+ vwh := blder.getValidatingWebhook()
+ if vwh != nil {
+ path := generateValidatePath(blder.gvk)
+
+ // Checking if the path is already registered.
+ // If so, just skip it.
+ if !blder.isAlreadyHandled(path) {
+ log.Info("Registering a validating webhook",
+ "GVK", blder.gvk,
+ "path", path)
+ blder.mgr.GetWebhookServer().Register(path, vwh)
+ }
+ }
+}
+
+func (blder *WebhookBuilder) getValidatingWebhook() *admission.Webhook {
+ if validator := blder.withValidator; validator != nil {
+ return admission.WithCustomValidator(blder.apiType, validator)
+ }
+ if validator, ok := blder.apiType.(admission.Validator); ok {
+ return admission.ValidatingWebhookFor(validator)
+ }
+ log.Info(
+ "skip registering a validating webhook, object does not implement admission.Validator or WithValidator wasn't called",
+ "GVK", blder.gvk)
+ return nil
+}
+
+func (blder *WebhookBuilder) registerConversionWebhook() error {
+ ok, err := conversion.IsConvertible(blder.mgr.GetScheme(), blder.apiType)
+ if err != nil {
+ log.Error(err, "conversion check failed", "GVK", blder.gvk)
+ return err
+ }
+ if ok {
+ if !blder.isAlreadyHandled("/convert") {
+ blder.mgr.GetWebhookServer().Register("/convert", &conversion.Webhook{})
+ }
+ log.Info("Conversion webhook enabled", "GVK", blder.gvk)
+ }
+
+ return nil
+}
+
+func (blder *WebhookBuilder) getType() (runtime.Object, error) {
+ if blder.apiType != nil {
+ return blder.apiType, nil
+ }
+ return nil, errors.New("For() must be called with a valid object")
+}
+
+func (blder *WebhookBuilder) isAlreadyHandled(path string) bool {
+ if blder.mgr.GetWebhookServer().WebhookMux == nil {
+ return false
+ }
+ h, p := blder.mgr.GetWebhookServer().WebhookMux.Handler(&http.Request{URL: &url.URL{Path: path}})
+ if p == path && h != nil {
+ return true
+ }
+ return false
+}
+
+func generateMutatePath(gvk schema.GroupVersionKind) string {
+ return "/mutate-" + strings.ReplaceAll(gvk.Group, ".", "-") + "-" +
+ gvk.Version + "-" + strings.ToLower(gvk.Kind)
+}
+
+func generateValidatePath(gvk schema.GroupVersionKind) string {
+ return "/validate-" + strings.ReplaceAll(gvk.Group, ".", "-") + "-" +
+ gvk.Version + "-" + strings.ToLower(gvk.Kind)
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/cache/cache.go b/vendor/sigs.k8s.io/controller-runtime/pkg/cache/cache.go
new file mode 100644
index 000000000..f89800ca2
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/cache/cache.go
@@ -0,0 +1,231 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package cache
+
+import (
+ "context"
+ "fmt"
+ "time"
+
+ "k8s.io/apimachinery/pkg/api/meta"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "k8s.io/client-go/kubernetes/scheme"
+ "k8s.io/client-go/rest"
+ toolscache "k8s.io/client-go/tools/cache"
+ "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")
+
+// Cache knows how to load Kubernetes objects, fetch informers to request
+// to receive events for Kubernetes objects (at a low-level),
+// and add indices to fields on the objects stored in the cache.
+type Cache interface {
+ // Cache acts as a client to objects stored in the cache.
+ client.Reader
+
+ // Cache loads informers and adds field indices.
+ Informers
+}
+
+// Informers knows how to create or fetch informers for different
+// group-version-kinds, and add indices to those informers. It's safe to call
+// GetInformer from multiple threads.
+type Informers interface {
+ // GetInformer fetches or constructs an informer for the given object that corresponds to a single
+ // API kind and resource.
+ GetInformer(ctx context.Context, obj client.Object) (Informer, error)
+
+ // GetInformerForKind is similar to GetInformer, except that it takes a group-version-kind, instead
+ // of the underlying object.
+ GetInformerForKind(ctx context.Context, gvk schema.GroupVersionKind) (Informer, error)
+
+ // Start runs all the informers known to this cache until the context is closed.
+ // It blocks.
+ Start(ctx context.Context) error
+
+ // WaitForCacheSync waits for all the caches to sync. Returns false if it could not sync a cache.
+ WaitForCacheSync(ctx context.Context) bool
+
+ // Informers knows how to add indices to the caches (informers) that it manages.
+ client.FieldIndexer
+}
+
+// Informer - informer allows you interact with the underlying informer.
+type Informer interface {
+ // AddEventHandler adds an event handler to the shared informer using the shared informer's resync
+ // period. Events to a single handler are delivered sequentially, but there is no coordination
+ // between different handlers.
+ AddEventHandler(handler toolscache.ResourceEventHandler)
+ // AddEventHandlerWithResyncPeriod adds an event handler to the shared informer using the
+ // specified resync period. Events to a single handler are delivered sequentially, but there is
+ // no coordination between different handlers.
+ AddEventHandlerWithResyncPeriod(handler toolscache.ResourceEventHandler, resyncPeriod time.Duration)
+ // AddIndexers adds more indexers to this store. If you call this after you already have data
+ // in the store, the results are undefined.
+ AddIndexers(indexers toolscache.Indexers) error
+ // HasSynced return true if the informers underlying store has synced.
+ HasSynced() bool
+}
+
+// SelectorsByObject associate a client.Object's GVK to a field/label selector.
+type SelectorsByObject map[client.Object]internal.Selector
+
+// Options are the optional arguments for creating a new InformersMap object.
+type Options struct {
+ // Scheme is the scheme to use for mapping objects to GroupVersionKinds
+ Scheme *runtime.Scheme
+
+ // Mapper is the RESTMapper to use for mapping GroupVersionKinds to Resources
+ Mapper meta.RESTMapper
+
+ // Resync is the base frequency the informers are resynced.
+ // Defaults to defaultResyncTime.
+ // A 10 percent jitter will be added to the Resync period between informers
+ // So that all informers will not send list requests simultaneously.
+ Resync *time.Duration
+
+ // Namespace restricts the cache's ListWatch to the desired namespace
+ // Default watches all namespaces
+ Namespace string
+
+ // SelectorsByObject restricts the cache's ListWatch to the desired
+ // fields per GVK at the specified object, the map's value must implement
+ // Selector [1] using for example a Set [2]
+ // [1] https://pkg.go.dev/k8s.io/apimachinery/pkg/fields#Selector
+ // [2] https://pkg.go.dev/k8s.io/apimachinery/pkg/fields#Set
+ SelectorsByObject SelectorsByObject
+
+ // UnsafeDisableDeepCopyByObject indicates not to deep copy objects during get or
+ // list objects per GVK at the specified object.
+ // Be very careful with this, when enabled you must DeepCopy any object before mutating it,
+ // otherwise you will mutate the object in the cache.
+ UnsafeDisableDeepCopyByObject DisableDeepCopyByObject
+}
+
+var defaultResyncTime = 10 * time.Hour
+
+// New initializes and returns a new Cache.
+func New(config *rest.Config, opts Options) (Cache, error) {
+ opts, err := defaultOpts(config, opts)
+ if err != nil {
+ return nil, err
+ }
+ selectorsByGVK, err := convertToSelectorsByGVK(opts.SelectorsByObject, opts.Scheme)
+ if err != nil {
+ return nil, err
+ }
+ disableDeepCopyByGVK, err := convertToDisableDeepCopyByGVK(opts.UnsafeDisableDeepCopyByObject, opts.Scheme)
+ if err != nil {
+ return nil, err
+ }
+ im := internal.NewInformersMap(config, opts.Scheme, opts.Mapper, *opts.Resync, opts.Namespace, selectorsByGVK, disableDeepCopyByGVK)
+ return &informerCache{InformersMap: im}, nil
+}
+
+// BuilderWithOptions returns a Cache constructor that will build the a cache
+// honoring the options argument, this is useful to specify options like
+// SelectorsByObject
+// WARNING: if SelectorsByObject is specified. filtered out resources are not
+// returned.
+// WARNING: if UnsafeDisableDeepCopy is enabled, you must DeepCopy any object
+// returned from cache get/list before mutating it.
+func BuilderWithOptions(options Options) NewCacheFunc {
+ return func(config *rest.Config, opts Options) (Cache, error) {
+ if opts.Scheme == nil {
+ opts.Scheme = options.Scheme
+ }
+ if opts.Mapper == nil {
+ opts.Mapper = options.Mapper
+ }
+ if opts.Resync == nil {
+ opts.Resync = options.Resync
+ }
+ if opts.Namespace == "" {
+ opts.Namespace = options.Namespace
+ }
+ opts.SelectorsByObject = options.SelectorsByObject
+ opts.UnsafeDisableDeepCopyByObject = options.UnsafeDisableDeepCopyByObject
+ return New(config, opts)
+ }
+}
+
+func defaultOpts(config *rest.Config, opts Options) (Options, error) {
+ // Use the default Kubernetes Scheme if unset
+ if opts.Scheme == nil {
+ opts.Scheme = scheme.Scheme
+ }
+
+ // Construct a new Mapper if unset
+ if opts.Mapper == nil {
+ var err error
+ opts.Mapper, err = apiutil.NewDiscoveryRESTMapper(config)
+ if err != nil {
+ log.WithName("setup").Error(err, "Failed to get API Group-Resources")
+ return opts, fmt.Errorf("could not create RESTMapper from config")
+ }
+ }
+
+ // Default the resync period to 10 hours if unset
+ if opts.Resync == nil {
+ opts.Resync = &defaultResyncTime
+ }
+ return opts, nil
+}
+
+func convertToSelectorsByGVK(selectorsByObject SelectorsByObject, scheme *runtime.Scheme) (internal.SelectorsByGVK, error) {
+ selectorsByGVK := internal.SelectorsByGVK{}
+ for object, selector := range selectorsByObject {
+ gvk, err := apiutil.GVKForObject(object, scheme)
+ if err != nil {
+ return nil, err
+ }
+ selectorsByGVK[gvk] = selector
+ }
+ return selectorsByGVK, nil
+}
+
+// DisableDeepCopyByObject associate a client.Object's GVK to disable DeepCopy during get or list from cache.
+type DisableDeepCopyByObject map[client.Object]bool
+
+var _ client.Object = &ObjectAll{}
+
+// ObjectAll is the argument to represent all objects' types.
+type ObjectAll struct {
+ client.Object
+}
+
+func convertToDisableDeepCopyByGVK(disableDeepCopyByObject DisableDeepCopyByObject, scheme *runtime.Scheme) (internal.DisableDeepCopyByGVK, error) {
+ disableDeepCopyByGVK := internal.DisableDeepCopyByGVK{}
+ for obj, disable := range disableDeepCopyByObject {
+ switch obj.(type) {
+ case ObjectAll, *ObjectAll:
+ disableDeepCopyByGVK[internal.GroupVersionKindAll] = disable
+ default:
+ gvk, err := apiutil.GVKForObject(obj, scheme)
+ if err != nil {
+ return nil, err
+ }
+ disableDeepCopyByGVK[gvk] = disable
+ }
+ }
+ return disableDeepCopyByGVK, nil
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/cache/doc.go b/vendor/sigs.k8s.io/controller-runtime/pkg/cache/doc.go
new file mode 100644
index 000000000..e1742ac0f
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/cache/doc.go
@@ -0,0 +1,19 @@
+/*
+Copyright 2019 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Package cache provides object caches that act as caching client.Reader
+// instances and help drive Kubernetes-object-based event handlers.
+package cache
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/cache/informer_cache.go b/vendor/sigs.k8s.io/controller-runtime/pkg/cache/informer_cache.go
new file mode 100644
index 000000000..90647c8e3
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/cache/informer_cache.go
@@ -0,0 +1,217 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package cache
+
+import (
+ "context"
+ "fmt"
+ "reflect"
+ "strings"
+
+ apimeta "k8s.io/apimachinery/pkg/api/meta"
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "k8s.io/client-go/tools/cache"
+ "sigs.k8s.io/controller-runtime/pkg/cache/internal"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/client/apiutil"
+)
+
+var (
+ _ Informers = &informerCache{}
+ _ client.Reader = &informerCache{}
+ _ Cache = &informerCache{}
+)
+
+// ErrCacheNotStarted is returned when trying to read from the cache that wasn't started.
+type ErrCacheNotStarted struct{}
+
+func (*ErrCacheNotStarted) Error() string {
+ return "the cache is not started, can not read objects"
+}
+
+// informerCache is a Kubernetes Object cache populated from InformersMap. informerCache wraps an InformersMap.
+type informerCache struct {
+ *internal.InformersMap
+}
+
+// Get implements Reader.
+func (ip *informerCache) Get(ctx context.Context, key client.ObjectKey, out client.Object) error {
+ gvk, err := apiutil.GVKForObject(out, ip.Scheme)
+ if err != nil {
+ return err
+ }
+
+ started, cache, err := ip.InformersMap.Get(ctx, gvk, out)
+ if err != nil {
+ return err
+ }
+
+ if !started {
+ return &ErrCacheNotStarted{}
+ }
+ return cache.Reader.Get(ctx, key, out)
+}
+
+// List implements Reader.
+func (ip *informerCache) List(ctx context.Context, out client.ObjectList, opts ...client.ListOption) error {
+ gvk, cacheTypeObj, err := ip.objectTypeForListObject(out)
+ if err != nil {
+ return err
+ }
+
+ started, cache, err := ip.InformersMap.Get(ctx, *gvk, cacheTypeObj)
+ if err != nil {
+ return err
+ }
+
+ if !started {
+ return &ErrCacheNotStarted{}
+ }
+
+ return cache.Reader.List(ctx, out, opts...)
+}
+
+// objectTypeForListObject tries to find the runtime.Object and associated GVK
+// for a single object corresponding to the passed-in list type. We need them
+// because they are used as cache map key.
+func (ip *informerCache) objectTypeForListObject(list client.ObjectList) (*schema.GroupVersionKind, runtime.Object, error) {
+ gvk, err := apiutil.GVKForObject(list, ip.Scheme)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ if !strings.HasSuffix(gvk.Kind, "List") {
+ return nil, nil, fmt.Errorf("non-list type %T (kind %q) passed as output", list, gvk)
+ }
+ // we need the non-list GVK, so chop off the "List" from the end of the kind
+ gvk.Kind = gvk.Kind[:len(gvk.Kind)-4]
+ _, isUnstructured := list.(*unstructured.UnstructuredList)
+ var cacheTypeObj runtime.Object
+ if isUnstructured {
+ u := &unstructured.Unstructured{}
+ u.SetGroupVersionKind(gvk)
+ cacheTypeObj = u
+ } else {
+ itemsPtr, err := apimeta.GetItemsPtr(list)
+ if err != nil {
+ return nil, nil, err
+ }
+ // http://knowyourmeme.com/memes/this-is-fine
+ elemType := reflect.Indirect(reflect.ValueOf(itemsPtr)).Type().Elem()
+ if elemType.Kind() != reflect.Ptr {
+ elemType = reflect.PtrTo(elemType)
+ }
+
+ cacheTypeValue := reflect.Zero(elemType)
+ var ok bool
+ cacheTypeObj, ok = cacheTypeValue.Interface().(runtime.Object)
+ if !ok {
+ return nil, nil, fmt.Errorf("cannot get cache for %T, its element %T is not a runtime.Object", list, cacheTypeValue.Interface())
+ }
+ }
+
+ return &gvk, cacheTypeObj, nil
+}
+
+// GetInformerForKind returns the informer for the GroupVersionKind.
+func (ip *informerCache) GetInformerForKind(ctx context.Context, gvk schema.GroupVersionKind) (Informer, error) {
+ // Map the gvk to an object
+ obj, err := ip.Scheme.New(gvk)
+ if err != nil {
+ return nil, err
+ }
+
+ _, i, err := ip.InformersMap.Get(ctx, gvk, obj)
+ if err != nil {
+ return nil, err
+ }
+ return i.Informer, err
+}
+
+// GetInformer returns the informer for the obj.
+func (ip *informerCache) GetInformer(ctx context.Context, obj client.Object) (Informer, error) {
+ gvk, err := apiutil.GVKForObject(obj, ip.Scheme)
+ if err != nil {
+ return nil, err
+ }
+
+ _, i, err := ip.InformersMap.Get(ctx, gvk, obj)
+ if err != nil {
+ return nil, err
+ }
+ return i.Informer, err
+}
+
+// NeedLeaderElection implements the LeaderElectionRunnable interface
+// to indicate that this can be started without requiring the leader lock.
+func (ip *informerCache) NeedLeaderElection() bool {
+ return false
+}
+
+// IndexField adds an indexer to the underlying cache, using extraction function to get
+// value(s) from the given field. This index can then be used by passing a field selector
+// to List. For one-to-one compatibility with "normal" field selectors, only return one value.
+// The values may be anything. They will automatically be prefixed with the namespace of the
+// given object, if present. The objects passed are guaranteed to be objects of the correct type.
+func (ip *informerCache) IndexField(ctx context.Context, obj client.Object, field string, extractValue client.IndexerFunc) error {
+ informer, err := ip.GetInformer(ctx, obj)
+ if err != nil {
+ return err
+ }
+ return indexByField(informer, field, extractValue)
+}
+
+func indexByField(indexer Informer, field string, extractor client.IndexerFunc) error {
+ indexFunc := func(objRaw interface{}) ([]string, error) {
+ // TODO(directxman12): check if this is the correct type?
+ obj, isObj := objRaw.(client.Object)
+ if !isObj {
+ return nil, fmt.Errorf("object of type %T is not an Object", objRaw)
+ }
+ meta, err := apimeta.Accessor(obj)
+ if err != nil {
+ return nil, err
+ }
+ ns := meta.GetNamespace()
+
+ rawVals := extractor(obj)
+ var vals []string
+ if ns == "" {
+ // if we're not doubling the keys for the namespaced case, just re-use what was returned to us
+ vals = rawVals
+ } else {
+ // if we need to add non-namespaced versions too, double the length
+ vals = make([]string, len(rawVals)*2)
+ }
+ for i, rawVal := range rawVals {
+ // save a namespaced variant, so that we can ask
+ // "what are all the object matching a given index *in a given namespace*"
+ vals[i] = internal.KeyToNamespacedKey(ns, rawVal)
+ if ns != "" {
+ // if we have a namespace, also inject a special index key for listing
+ // regardless of the object namespace
+ vals[i+len(rawVals)] = internal.KeyToNamespacedKey("", rawVal)
+ }
+ }
+
+ return vals, nil
+ }
+
+ return indexer.AddIndexers(cache.Indexers{internal.FieldIndexName(field): indexFunc})
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/cache_reader.go b/vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/cache_reader.go
new file mode 100644
index 000000000..b95af18d7
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/cache_reader.go
@@ -0,0 +1,218 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package internal
+
+import (
+ "context"
+ "fmt"
+ "reflect"
+
+ apierrors "k8s.io/apimachinery/pkg/api/errors"
+ apimeta "k8s.io/apimachinery/pkg/api/meta"
+ "k8s.io/apimachinery/pkg/fields"
+ "k8s.io/apimachinery/pkg/labels"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "k8s.io/apimachinery/pkg/selection"
+ "k8s.io/client-go/tools/cache"
+
+ "sigs.k8s.io/controller-runtime/pkg/client"
+)
+
+// CacheReader is a client.Reader.
+var _ client.Reader = &CacheReader{}
+
+// CacheReader wraps a cache.Index to implement the client.CacheReader interface for a single type.
+type CacheReader struct {
+ // indexer is the underlying indexer wrapped by this cache.
+ indexer cache.Indexer
+
+ // groupVersionKind is the group-version-kind of the resource.
+ groupVersionKind schema.GroupVersionKind
+
+ // scopeName is the scope of the resource (namespaced or cluster-scoped).
+ scopeName apimeta.RESTScopeName
+
+ // disableDeepCopy indicates not to deep copy objects during get or list objects.
+ // Be very careful with this, when enabled you must DeepCopy any object before mutating it,
+ // otherwise you will mutate the object in the cache.
+ disableDeepCopy bool
+}
+
+// Get checks the indexer for the object and writes a copy of it if found.
+func (c *CacheReader) Get(_ context.Context, key client.ObjectKey, out client.Object) error {
+ if c.scopeName == apimeta.RESTScopeNameRoot {
+ key.Namespace = ""
+ }
+ storeKey := objectKeyToStoreKey(key)
+
+ // Lookup the object from the indexer cache
+ obj, exists, err := c.indexer.GetByKey(storeKey)
+ if err != nil {
+ return err
+ }
+
+ // Not found, return an error
+ if !exists {
+ // Resource gets transformed into Kind in the error anyway, so this is fine
+ return apierrors.NewNotFound(schema.GroupResource{
+ Group: c.groupVersionKind.Group,
+ Resource: c.groupVersionKind.Kind,
+ }, key.Name)
+ }
+
+ // Verify the result is a runtime.Object
+ if _, isObj := obj.(runtime.Object); !isObj {
+ // This should never happen
+ return fmt.Errorf("cache contained %T, which is not an Object", obj)
+ }
+
+ if c.disableDeepCopy {
+ // skip deep copy which might be unsafe
+ // you must DeepCopy any object before mutating it outside
+ } else {
+ // deep copy to avoid mutating cache
+ obj = obj.(runtime.Object).DeepCopyObject()
+ }
+
+ // Copy the value of the item in the cache to the returned value
+ // TODO(directxman12): this is a terrible hack, pls fix (we should have deepcopyinto)
+ outVal := reflect.ValueOf(out)
+ objVal := reflect.ValueOf(obj)
+ if !objVal.Type().AssignableTo(outVal.Type()) {
+ return fmt.Errorf("cache had type %s, but %s was asked for", objVal.Type(), outVal.Type())
+ }
+ reflect.Indirect(outVal).Set(reflect.Indirect(objVal))
+ if !c.disableDeepCopy {
+ out.GetObjectKind().SetGroupVersionKind(c.groupVersionKind)
+ }
+
+ return nil
+}
+
+// List lists items out of the indexer and writes them to out.
+func (c *CacheReader) List(_ context.Context, out client.ObjectList, opts ...client.ListOption) error {
+ var objs []interface{}
+ var err error
+
+ listOpts := client.ListOptions{}
+ listOpts.ApplyOptions(opts)
+
+ switch {
+ case listOpts.FieldSelector != nil:
+ // TODO(directxman12): support more complicated field selectors by
+ // combining multiple indices, GetIndexers, etc
+ field, val, requiresExact := requiresExactMatch(listOpts.FieldSelector)
+ if !requiresExact {
+ return fmt.Errorf("non-exact field matches are not supported by the cache")
+ }
+ // list all objects by the field selector. If this is namespaced and we have one, ask for the
+ // namespaced index key. Otherwise, ask for the non-namespaced variant by using the fake "all namespaces"
+ // namespace.
+ objs, err = c.indexer.ByIndex(FieldIndexName(field), KeyToNamespacedKey(listOpts.Namespace, val))
+ case listOpts.Namespace != "":
+ objs, err = c.indexer.ByIndex(cache.NamespaceIndex, listOpts.Namespace)
+ default:
+ objs = c.indexer.List()
+ }
+ if err != nil {
+ return err
+ }
+ var labelSel labels.Selector
+ if listOpts.LabelSelector != nil {
+ labelSel = listOpts.LabelSelector
+ }
+
+ limitSet := listOpts.Limit > 0
+
+ runtimeObjs := make([]runtime.Object, 0, len(objs))
+ for _, item := range objs {
+ // if the Limit option is set and the number of items
+ // listed exceeds this limit, then stop reading.
+ if limitSet && int64(len(runtimeObjs)) >= listOpts.Limit {
+ break
+ }
+ obj, isObj := item.(runtime.Object)
+ if !isObj {
+ return fmt.Errorf("cache contained %T, which is not an Object", obj)
+ }
+ meta, err := apimeta.Accessor(obj)
+ if err != nil {
+ return err
+ }
+ if labelSel != nil {
+ lbls := labels.Set(meta.GetLabels())
+ if !labelSel.Matches(lbls) {
+ continue
+ }
+ }
+
+ var outObj runtime.Object
+ if c.disableDeepCopy {
+ // skip deep copy which might be unsafe
+ // you must DeepCopy any object before mutating it outside
+ outObj = obj
+ } else {
+ outObj = obj.DeepCopyObject()
+ outObj.GetObjectKind().SetGroupVersionKind(c.groupVersionKind)
+ }
+ runtimeObjs = append(runtimeObjs, outObj)
+ }
+ return apimeta.SetList(out, runtimeObjs)
+}
+
+// objectKeyToStorageKey converts an object key to store key.
+// It's akin to MetaNamespaceKeyFunc. It's separate from
+// String to allow keeping the key format easily in sync with
+// MetaNamespaceKeyFunc.
+func objectKeyToStoreKey(k client.ObjectKey) string {
+ if k.Namespace == "" {
+ return k.Name
+ }
+ return k.Namespace + "/" + k.Name
+}
+
+// requiresExactMatch checks if the given field selector is of the form `k=v` or `k==v`.
+func requiresExactMatch(sel fields.Selector) (field, val string, required bool) {
+ reqs := sel.Requirements()
+ if len(reqs) != 1 {
+ return "", "", false
+ }
+ req := reqs[0]
+ if req.Operator != selection.Equals && req.Operator != selection.DoubleEquals {
+ return "", "", false
+ }
+ return req.Field, req.Value, true
+}
+
+// FieldIndexName constructs the name of the index over the given field,
+// for use with an indexer.
+func FieldIndexName(field string) string {
+ return "field:" + field
+}
+
+// noNamespaceNamespace is used as the "namespace" when we want to list across all namespaces.
+const allNamespacesNamespace = "__all_namespaces"
+
+// KeyToNamespacedKey prefixes the given index key with a namespace
+// for use in field selector indexes.
+func KeyToNamespacedKey(ns string, baseKey string) string {
+ if ns != "" {
+ return ns + "/" + baseKey
+ }
+ return allNamespacesNamespace + "/" + baseKey
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/deleg_map.go b/vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/deleg_map.go
new file mode 100644
index 000000000..9bfc8463f
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/deleg_map.go
@@ -0,0 +1,125 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package internal
+
+import (
+ "context"
+ "time"
+
+ "k8s.io/apimachinery/pkg/api/meta"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "k8s.io/client-go/rest"
+ "k8s.io/client-go/tools/cache"
+)
+
+// InformersMap create and caches Informers for (runtime.Object, schema.GroupVersionKind) pairs.
+// It uses a standard parameter codec constructed based on the given generated Scheme.
+type InformersMap struct {
+ // we abstract over the details of structured/unstructured/metadata with the specificInformerMaps
+ // TODO(directxman12): genericize this over different projections now that we have 3 different maps
+
+ structured *specificInformersMap
+ unstructured *specificInformersMap
+ metadata *specificInformersMap
+
+ // Scheme maps runtime.Objects to GroupVersionKinds
+ Scheme *runtime.Scheme
+}
+
+// NewInformersMap creates a new InformersMap that can create informers for
+// both structured and unstructured objects.
+func NewInformersMap(config *rest.Config,
+ scheme *runtime.Scheme,
+ mapper meta.RESTMapper,
+ resync time.Duration,
+ namespace string,
+ selectors SelectorsByGVK,
+ disableDeepCopy DisableDeepCopyByGVK,
+) *InformersMap {
+ return &InformersMap{
+ structured: newStructuredInformersMap(config, scheme, mapper, resync, namespace, selectors, disableDeepCopy),
+ unstructured: newUnstructuredInformersMap(config, scheme, mapper, resync, namespace, selectors, disableDeepCopy),
+ metadata: newMetadataInformersMap(config, scheme, mapper, resync, namespace, selectors, disableDeepCopy),
+
+ Scheme: scheme,
+ }
+}
+
+// Start calls Run on each of the informers and sets started to true. Blocks on the context.
+func (m *InformersMap) Start(ctx context.Context) error {
+ go m.structured.Start(ctx)
+ go m.unstructured.Start(ctx)
+ go m.metadata.Start(ctx)
+ <-ctx.Done()
+ return nil
+}
+
+// WaitForCacheSync waits until all the caches have been started and synced.
+func (m *InformersMap) WaitForCacheSync(ctx context.Context) bool {
+ syncedFuncs := append([]cache.InformerSynced(nil), m.structured.HasSyncedFuncs()...)
+ syncedFuncs = append(syncedFuncs, m.unstructured.HasSyncedFuncs()...)
+ syncedFuncs = append(syncedFuncs, m.metadata.HasSyncedFuncs()...)
+
+ if !m.structured.waitForStarted(ctx) {
+ return false
+ }
+ if !m.unstructured.waitForStarted(ctx) {
+ return false
+ }
+ if !m.metadata.waitForStarted(ctx) {
+ return false
+ }
+ return cache.WaitForCacheSync(ctx.Done(), syncedFuncs...)
+}
+
+// Get will create a new Informer and add it to the map of InformersMap if none exists. Returns
+// the Informer from the map.
+func (m *InformersMap) Get(ctx context.Context, gvk schema.GroupVersionKind, obj runtime.Object) (bool, *MapEntry, error) {
+ switch obj.(type) {
+ case *unstructured.Unstructured:
+ return m.unstructured.Get(ctx, gvk, obj)
+ case *unstructured.UnstructuredList:
+ return m.unstructured.Get(ctx, gvk, obj)
+ case *metav1.PartialObjectMetadata:
+ return m.metadata.Get(ctx, gvk, obj)
+ case *metav1.PartialObjectMetadataList:
+ return m.metadata.Get(ctx, gvk, obj)
+ default:
+ return m.structured.Get(ctx, gvk, obj)
+ }
+}
+
+// newStructuredInformersMap creates a new InformersMap for structured objects.
+func newStructuredInformersMap(config *rest.Config, scheme *runtime.Scheme, mapper meta.RESTMapper, resync time.Duration,
+ namespace string, selectors SelectorsByGVK, disableDeepCopy DisableDeepCopyByGVK) *specificInformersMap {
+ return newSpecificInformersMap(config, scheme, mapper, resync, namespace, selectors, disableDeepCopy, createStructuredListWatch)
+}
+
+// newUnstructuredInformersMap creates a new InformersMap for unstructured objects.
+func newUnstructuredInformersMap(config *rest.Config, scheme *runtime.Scheme, mapper meta.RESTMapper, resync time.Duration,
+ namespace string, selectors SelectorsByGVK, disableDeepCopy DisableDeepCopyByGVK) *specificInformersMap {
+ return newSpecificInformersMap(config, scheme, mapper, resync, namespace, selectors, disableDeepCopy, createUnstructuredListWatch)
+}
+
+// newMetadataInformersMap creates a new InformersMap for metadata-only objects.
+func newMetadataInformersMap(config *rest.Config, scheme *runtime.Scheme, mapper meta.RESTMapper, resync time.Duration,
+ namespace string, selectors SelectorsByGVK, disableDeepCopy DisableDeepCopyByGVK) *specificInformersMap {
+ return newSpecificInformersMap(config, scheme, mapper, resync, namespace, selectors, disableDeepCopy, createMetadataListWatch)
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/disabledeepcopy.go b/vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/disabledeepcopy.go
new file mode 100644
index 000000000..54bd7eec9
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/disabledeepcopy.go
@@ -0,0 +1,35 @@
+/*
+Copyright 2021 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package internal
+
+import "k8s.io/apimachinery/pkg/runtime/schema"
+
+// GroupVersionKindAll is the argument to represent all GroupVersionKind types.
+var GroupVersionKindAll = schema.GroupVersionKind{}
+
+// DisableDeepCopyByGVK associate a GroupVersionKind to disable DeepCopy during get or list from cache.
+type DisableDeepCopyByGVK map[schema.GroupVersionKind]bool
+
+// IsDisabled returns whether a GroupVersionKind is disabled DeepCopy.
+func (disableByGVK DisableDeepCopyByGVK) IsDisabled(gvk schema.GroupVersionKind) bool {
+ if d, ok := disableByGVK[gvk]; ok {
+ return d
+ } else if d, ok = disableByGVK[GroupVersionKindAll]; ok {
+ return d
+ }
+ return false
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/informers_map.go b/vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/informers_map.go
new file mode 100644
index 000000000..f8e957343
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/informers_map.go
@@ -0,0 +1,478 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package internal
+
+import (
+ "context"
+ "fmt"
+ "math/rand"
+ "sync"
+ "time"
+
+ apierrors "k8s.io/apimachinery/pkg/api/errors"
+ "k8s.io/apimachinery/pkg/api/meta"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "k8s.io/apimachinery/pkg/runtime/serializer"
+ "k8s.io/apimachinery/pkg/watch"
+ "k8s.io/client-go/dynamic"
+ "k8s.io/client-go/metadata"
+ "k8s.io/client-go/rest"
+ "k8s.io/client-go/tools/cache"
+
+ "sigs.k8s.io/controller-runtime/pkg/client/apiutil"
+)
+
+func init() {
+ rand.Seed(time.Now().UnixNano())
+}
+
+// clientListWatcherFunc knows how to create a ListWatcher.
+type createListWatcherFunc func(gvk schema.GroupVersionKind, ip *specificInformersMap) (*cache.ListWatch, error)
+
+// newSpecificInformersMap returns a new specificInformersMap (like
+// the generical InformersMap, except that it doesn't implement WaitForCacheSync).
+func newSpecificInformersMap(config *rest.Config,
+ scheme *runtime.Scheme,
+ mapper meta.RESTMapper,
+ resync time.Duration,
+ namespace string,
+ selectors SelectorsByGVK,
+ disableDeepCopy DisableDeepCopyByGVK,
+ createListWatcher createListWatcherFunc) *specificInformersMap {
+ ip := &specificInformersMap{
+ config: config,
+ Scheme: scheme,
+ mapper: mapper,
+ informersByGVK: make(map[schema.GroupVersionKind]*MapEntry),
+ codecs: serializer.NewCodecFactory(scheme),
+ paramCodec: runtime.NewParameterCodec(scheme),
+ resync: resync,
+ startWait: make(chan struct{}),
+ createListWatcher: createListWatcher,
+ namespace: namespace,
+ selectors: selectors,
+ disableDeepCopy: disableDeepCopy,
+ }
+ return ip
+}
+
+// MapEntry contains the cached data for an Informer.
+type MapEntry struct {
+ // Informer is the cached informer
+ Informer cache.SharedIndexInformer
+
+ // CacheReader wraps Informer and implements the CacheReader interface for a single type
+ Reader CacheReader
+}
+
+// specificInformersMap create and caches Informers for (runtime.Object, schema.GroupVersionKind) pairs.
+// It uses a standard parameter codec constructed based on the given generated Scheme.
+type specificInformersMap struct {
+ // Scheme maps runtime.Objects to GroupVersionKinds
+ Scheme *runtime.Scheme
+
+ // config is used to talk to the apiserver
+ config *rest.Config
+
+ // mapper maps GroupVersionKinds to Resources
+ mapper meta.RESTMapper
+
+ // informersByGVK is the cache of informers keyed by groupVersionKind
+ informersByGVK map[schema.GroupVersionKind]*MapEntry
+
+ // codecs is used to create a new REST client
+ codecs serializer.CodecFactory
+
+ // paramCodec is used by list and watch
+ paramCodec runtime.ParameterCodec
+
+ // stop is the stop channel to stop informers
+ stop <-chan struct{}
+
+ // resync is the base frequency the informers are resynced
+ // a 10 percent jitter will be added to the resync period between informers
+ // so that all informers will not send list requests simultaneously.
+ resync time.Duration
+
+ // mu guards access to the map
+ mu sync.RWMutex
+
+ // start is true if the informers have been started
+ started bool
+
+ // startWait is a channel that is closed after the
+ // informer has been started.
+ startWait chan struct{}
+
+ // createClient knows how to create a client and a list object,
+ // and allows for abstracting over the particulars of structured vs
+ // unstructured objects.
+ createListWatcher createListWatcherFunc
+
+ // namespace is the namespace that all ListWatches are restricted to
+ // default or empty string means all namespaces
+ namespace string
+
+ // selectors are the label or field selectors that will be added to the
+ // ListWatch ListOptions.
+ selectors SelectorsByGVK
+
+ // disableDeepCopy indicates not to deep copy objects during get or list objects.
+ disableDeepCopy DisableDeepCopyByGVK
+}
+
+// Start calls Run on each of the informers and sets started to true. Blocks on the context.
+// It doesn't return start because it can't return an error, and it's not a runnable directly.
+func (ip *specificInformersMap) Start(ctx context.Context) {
+ func() {
+ ip.mu.Lock()
+ defer ip.mu.Unlock()
+
+ // Set the stop channel so it can be passed to informers that are added later
+ ip.stop = ctx.Done()
+
+ // Start each informer
+ for _, informer := range ip.informersByGVK {
+ go informer.Informer.Run(ctx.Done())
+ }
+
+ // Set started to true so we immediately start any informers added later.
+ ip.started = true
+ close(ip.startWait)
+ }()
+ <-ctx.Done()
+}
+
+func (ip *specificInformersMap) waitForStarted(ctx context.Context) bool {
+ select {
+ case <-ip.startWait:
+ return true
+ case <-ctx.Done():
+ return false
+ }
+}
+
+// HasSyncedFuncs returns all the HasSynced functions for the informers in this map.
+func (ip *specificInformersMap) HasSyncedFuncs() []cache.InformerSynced {
+ ip.mu.RLock()
+ defer ip.mu.RUnlock()
+ syncedFuncs := make([]cache.InformerSynced, 0, len(ip.informersByGVK))
+ for _, informer := range ip.informersByGVK {
+ syncedFuncs = append(syncedFuncs, informer.Informer.HasSynced)
+ }
+ return syncedFuncs
+}
+
+// Get will create a new Informer and add it to the map of specificInformersMap if none exists. Returns
+// the Informer from the map.
+func (ip *specificInformersMap) Get(ctx context.Context, gvk schema.GroupVersionKind, obj runtime.Object) (bool, *MapEntry, error) {
+ // Return the informer if it is found
+ i, started, ok := func() (*MapEntry, bool, bool) {
+ ip.mu.RLock()
+ defer ip.mu.RUnlock()
+ i, ok := ip.informersByGVK[gvk]
+ return i, ip.started, ok
+ }()
+
+ if !ok {
+ var err error
+ if i, started, err = ip.addInformerToMap(gvk, obj); err != nil {
+ return started, nil, err
+ }
+ }
+
+ if started && !i.Informer.HasSynced() {
+ // Wait for it to sync before returning the Informer so that folks don't read from a stale cache.
+ if !cache.WaitForCacheSync(ctx.Done(), i.Informer.HasSynced) {
+ return started, nil, apierrors.NewTimeoutError(fmt.Sprintf("failed waiting for %T Informer to sync", obj), 0)
+ }
+ }
+
+ return started, i, nil
+}
+
+func (ip *specificInformersMap) addInformerToMap(gvk schema.GroupVersionKind, obj runtime.Object) (*MapEntry, bool, error) {
+ ip.mu.Lock()
+ defer ip.mu.Unlock()
+
+ // Check the cache to see if we already have an Informer. If we do, return the Informer.
+ // This is for the case where 2 routines tried to get the informer when it wasn't in the map
+ // so neither returned early, but the first one created it.
+ if i, ok := ip.informersByGVK[gvk]; ok {
+ return i, ip.started, nil
+ }
+
+ // Create a NewSharedIndexInformer and add it to the map.
+ var lw *cache.ListWatch
+ lw, err := ip.createListWatcher(gvk, ip)
+ if err != nil {
+ return nil, false, err
+ }
+ ni := cache.NewSharedIndexInformer(lw, obj, resyncPeriod(ip.resync)(), cache.Indexers{
+ cache.NamespaceIndex: cache.MetaNamespaceIndexFunc,
+ })
+ rm, err := ip.mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
+ if err != nil {
+ return nil, false, err
+ }
+
+ i := &MapEntry{
+ Informer: ni,
+ Reader: CacheReader{
+ indexer: ni.GetIndexer(),
+ groupVersionKind: gvk,
+ scopeName: rm.Scope.Name(),
+ disableDeepCopy: ip.disableDeepCopy.IsDisabled(gvk),
+ },
+ }
+ ip.informersByGVK[gvk] = i
+
+ // Start the Informer if need by
+ // TODO(seans): write thorough tests and document what happens here - can you add indexers?
+ // can you add eventhandlers?
+ if ip.started {
+ go i.Informer.Run(ip.stop)
+ }
+ return i, ip.started, nil
+}
+
+// newListWatch returns a new ListWatch object that can be used to create a SharedIndexInformer.
+func createStructuredListWatch(gvk schema.GroupVersionKind, ip *specificInformersMap) (*cache.ListWatch, error) {
+ // Kubernetes APIs work against Resources, not GroupVersionKinds. Map the
+ // groupVersionKind to the Resource API we will use.
+ mapping, err := ip.mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
+ if err != nil {
+ return nil, err
+ }
+
+ client, err := apiutil.RESTClientForGVK(gvk, false, ip.config, ip.codecs)
+ if err != nil {
+ return nil, err
+ }
+ listGVK := gvk.GroupVersion().WithKind(gvk.Kind + "List")
+ listObj, err := ip.Scheme.New(listGVK)
+ if err != nil {
+ return nil, err
+ }
+
+ // TODO: the functions that make use of this ListWatch should be adapted to
+ // pass in their own contexts instead of relying on this fixed one here.
+ ctx := context.TODO()
+ // Create a new ListWatch for the obj
+ return &cache.ListWatch{
+ ListFunc: func(opts metav1.ListOptions) (runtime.Object, error) {
+ ip.selectors[gvk].ApplyToList(&opts)
+ res := listObj.DeepCopyObject()
+ namespace := restrictNamespaceBySelector(ip.namespace, ip.selectors[gvk])
+ isNamespaceScoped := namespace != "" && mapping.Scope.Name() != meta.RESTScopeNameRoot
+ err := client.Get().NamespaceIfScoped(namespace, isNamespaceScoped).Resource(mapping.Resource.Resource).VersionedParams(&opts, ip.paramCodec).Do(ctx).Into(res)
+ return res, err
+ },
+ // Setup the watch function
+ WatchFunc: func(opts metav1.ListOptions) (watch.Interface, error) {
+ ip.selectors[gvk].ApplyToList(&opts)
+ // Watch needs to be set to true separately
+ opts.Watch = true
+ namespace := restrictNamespaceBySelector(ip.namespace, ip.selectors[gvk])
+ isNamespaceScoped := namespace != "" && mapping.Scope.Name() != meta.RESTScopeNameRoot
+ return client.Get().NamespaceIfScoped(namespace, isNamespaceScoped).Resource(mapping.Resource.Resource).VersionedParams(&opts, ip.paramCodec).Watch(ctx)
+ },
+ }, nil
+}
+
+func createUnstructuredListWatch(gvk schema.GroupVersionKind, ip *specificInformersMap) (*cache.ListWatch, error) {
+ // Kubernetes APIs work against Resources, not GroupVersionKinds. Map the
+ // groupVersionKind to the Resource API we will use.
+ mapping, err := ip.mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
+ if err != nil {
+ return nil, err
+ }
+
+ // If the rest configuration has a negotiated serializer passed in,
+ // we should remove it and use the one that the dynamic client sets for us.
+ cfg := rest.CopyConfig(ip.config)
+ cfg.NegotiatedSerializer = nil
+ dynamicClient, err := dynamic.NewForConfig(cfg)
+ if err != nil {
+ return nil, err
+ }
+
+ // TODO: the functions that make use of this ListWatch should be adapted to
+ // pass in their own contexts instead of relying on this fixed one here.
+ ctx := context.TODO()
+ // Create a new ListWatch for the obj
+ return &cache.ListWatch{
+ ListFunc: func(opts metav1.ListOptions) (runtime.Object, error) {
+ ip.selectors[gvk].ApplyToList(&opts)
+ namespace := restrictNamespaceBySelector(ip.namespace, ip.selectors[gvk])
+ if namespace != "" && mapping.Scope.Name() != meta.RESTScopeNameRoot {
+ return dynamicClient.Resource(mapping.Resource).Namespace(namespace).List(ctx, opts)
+ }
+ return dynamicClient.Resource(mapping.Resource).List(ctx, opts)
+ },
+ // Setup the watch function
+ WatchFunc: func(opts metav1.ListOptions) (watch.Interface, error) {
+ ip.selectors[gvk].ApplyToList(&opts)
+ // Watch needs to be set to true separately
+ opts.Watch = true
+ namespace := restrictNamespaceBySelector(ip.namespace, ip.selectors[gvk])
+ if namespace != "" && mapping.Scope.Name() != meta.RESTScopeNameRoot {
+ return dynamicClient.Resource(mapping.Resource).Namespace(namespace).Watch(ctx, opts)
+ }
+ return dynamicClient.Resource(mapping.Resource).Watch(ctx, opts)
+ },
+ }, nil
+}
+
+func createMetadataListWatch(gvk schema.GroupVersionKind, ip *specificInformersMap) (*cache.ListWatch, error) {
+ // Kubernetes APIs work against Resources, not GroupVersionKinds. Map the
+ // groupVersionKind to the Resource API we will use.
+ mapping, err := ip.mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
+ if err != nil {
+ return nil, err
+ }
+
+ // Always clear the negotiated serializer and use the one
+ // set from the metadata client.
+ cfg := rest.CopyConfig(ip.config)
+ cfg.NegotiatedSerializer = nil
+
+ // grab the metadata client
+ client, err := metadata.NewForConfig(cfg)
+ if err != nil {
+ return nil, err
+ }
+
+ // TODO: the functions that make use of this ListWatch should be adapted to
+ // pass in their own contexts instead of relying on this fixed one here.
+ ctx := context.TODO()
+
+ // create the relevant listwatch
+ return &cache.ListWatch{
+ ListFunc: func(opts metav1.ListOptions) (runtime.Object, error) {
+ ip.selectors[gvk].ApplyToList(&opts)
+
+ var (
+ list *metav1.PartialObjectMetadataList
+ err error
+ )
+ namespace := restrictNamespaceBySelector(ip.namespace, ip.selectors[gvk])
+ if namespace != "" && mapping.Scope.Name() != meta.RESTScopeNameRoot {
+ list, err = client.Resource(mapping.Resource).Namespace(namespace).List(ctx, opts)
+ } else {
+ list, err = client.Resource(mapping.Resource).List(ctx, opts)
+ }
+ if list != nil {
+ for i := range list.Items {
+ list.Items[i].SetGroupVersionKind(gvk)
+ }
+ }
+ return list, err
+ },
+ // Setup the watch function
+ WatchFunc: func(opts metav1.ListOptions) (watch.Interface, error) {
+ ip.selectors[gvk].ApplyToList(&opts)
+ // Watch needs to be set to true separately
+ opts.Watch = true
+
+ var (
+ watcher watch.Interface
+ err error
+ )
+ namespace := restrictNamespaceBySelector(ip.namespace, ip.selectors[gvk])
+ if namespace != "" && mapping.Scope.Name() != meta.RESTScopeNameRoot {
+ watcher, err = client.Resource(mapping.Resource).Namespace(namespace).Watch(ctx, opts)
+ } else {
+ watcher, err = client.Resource(mapping.Resource).Watch(ctx, opts)
+ }
+ if watcher != nil {
+ watcher = newGVKFixupWatcher(gvk, watcher)
+ }
+ return watcher, err
+ },
+ }, nil
+}
+
+type gvkFixupWatcher struct {
+ watcher watch.Interface
+ ch chan watch.Event
+ gvk schema.GroupVersionKind
+ wg sync.WaitGroup
+}
+
+func newGVKFixupWatcher(gvk schema.GroupVersionKind, watcher watch.Interface) watch.Interface {
+ ch := make(chan watch.Event)
+ w := &gvkFixupWatcher{
+ gvk: gvk,
+ watcher: watcher,
+ ch: ch,
+ }
+ w.wg.Add(1)
+ go w.run()
+ return w
+}
+
+func (w *gvkFixupWatcher) run() {
+ for e := range w.watcher.ResultChan() {
+ e.Object.GetObjectKind().SetGroupVersionKind(w.gvk)
+ w.ch <- e
+ }
+ w.wg.Done()
+}
+
+func (w *gvkFixupWatcher) Stop() {
+ w.watcher.Stop()
+ w.wg.Wait()
+ close(w.ch)
+}
+
+func (w *gvkFixupWatcher) ResultChan() <-chan watch.Event {
+ return w.ch
+}
+
+// resyncPeriod returns a function which generates a duration each time it is
+// invoked; this is so that multiple controllers don't get into lock-step and all
+// hammer the apiserver with list requests simultaneously.
+func resyncPeriod(resync time.Duration) func() time.Duration {
+ return func() time.Duration {
+ // the factor will fall into [0.9, 1.1)
+ factor := rand.Float64()/5.0 + 0.9 //nolint:gosec
+ return time.Duration(float64(resync.Nanoseconds()) * factor)
+ }
+}
+
+// restrictNamespaceBySelector returns either a global restriction for all ListWatches
+// if not default/empty, or the namespace that a ListWatch for the specific resource
+// is restricted to, based on a specified field selector for metadata.namespace field.
+func restrictNamespaceBySelector(namespaceOpt string, s Selector) string {
+ if namespaceOpt != "" {
+ // namespace is already restricted
+ return namespaceOpt
+ }
+ fieldSelector := s.Field
+ if fieldSelector == nil || fieldSelector.Empty() {
+ return ""
+ }
+ // check whether a selector includes the namespace field
+ value, found := fieldSelector.RequiresExactMatch("metadata.namespace")
+ if found {
+ return value
+ }
+ return ""
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/selector.go b/vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/selector.go
new file mode 100644
index 000000000..cd9c58000
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/selector.go
@@ -0,0 +1,43 @@
+/*
+Copyright 2021 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package internal
+
+import (
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/fields"
+ "k8s.io/apimachinery/pkg/labels"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+)
+
+// SelectorsByGVK associate a GroupVersionKind to a field/label selector.
+type SelectorsByGVK map[schema.GroupVersionKind]Selector
+
+// Selector specify the label/field selector to fill in ListOptions.
+type Selector struct {
+ Label labels.Selector
+ Field fields.Selector
+}
+
+// ApplyToList fill in ListOptions LabelSelector and FieldSelector if needed.
+func (s Selector) ApplyToList(listOpts *metav1.ListOptions) {
+ if s.Label != nil {
+ listOpts.LabelSelector = s.Label.String()
+ }
+ if s.Field != nil {
+ listOpts.FieldSelector = s.Field.String()
+ }
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/cache/multi_namespace_cache.go b/vendor/sigs.k8s.io/controller-runtime/pkg/cache/multi_namespace_cache.go
new file mode 100644
index 000000000..dc29651b0
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/cache/multi_namespace_cache.go
@@ -0,0 +1,331 @@
+/*
+Copyright 2019 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package cache
+
+import (
+ "context"
+ "fmt"
+ "time"
+
+ corev1 "k8s.io/api/core/v1"
+ apimeta "k8s.io/apimachinery/pkg/api/meta"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "k8s.io/client-go/rest"
+ toolscache "k8s.io/client-go/tools/cache"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/internal/objectutil"
+)
+
+// NewCacheFunc - Function for creating a new cache from the options and a rest config.
+type NewCacheFunc func(config *rest.Config, opts Options) (Cache, error)
+
+// a new global namespaced cache to handle cluster scoped resources.
+const globalCache = "_cluster-scope"
+
+// MultiNamespacedCacheBuilder - Builder function to create a new multi-namespaced cache.
+// This will scope the cache to a list of namespaces. Listing for all namespaces
+// will list for all the namespaces that this knows about. By default this will create
+// a global cache for cluster scoped resource. Note that this is not intended
+// to be used for excluding namespaces, this is better done via a Predicate. Also note that
+// you may face performance issues when using this with a high number of namespaces.
+func MultiNamespacedCacheBuilder(namespaces []string) NewCacheFunc {
+ return func(config *rest.Config, opts Options) (Cache, error) {
+ opts, err := defaultOpts(config, opts)
+ if err != nil {
+ return nil, err
+ }
+
+ caches := map[string]Cache{}
+
+ // create a cache for cluster scoped resources
+ gCache, err := New(config, opts)
+ if err != nil {
+ return nil, fmt.Errorf("error creating global cache %v", err)
+ }
+
+ for _, ns := range namespaces {
+ opts.Namespace = ns
+ c, err := New(config, opts)
+ if err != nil {
+ return nil, err
+ }
+ caches[ns] = c
+ }
+ return &multiNamespaceCache{namespaceToCache: caches, Scheme: opts.Scheme, RESTMapper: opts.Mapper, clusterCache: gCache}, nil
+ }
+}
+
+// multiNamespaceCache knows how to handle multiple namespaced caches
+// Use this feature when scoping permissions for your
+// operator to a list of namespaces instead of watching every namespace
+// in the cluster.
+type multiNamespaceCache struct {
+ namespaceToCache map[string]Cache
+ Scheme *runtime.Scheme
+ RESTMapper apimeta.RESTMapper
+ clusterCache Cache
+}
+
+var _ Cache = &multiNamespaceCache{}
+
+// Methods for multiNamespaceCache to conform to the Informers interface.
+func (c *multiNamespaceCache) GetInformer(ctx context.Context, obj client.Object) (Informer, error) {
+ informers := map[string]Informer{}
+
+ // If the object is clusterscoped, get the informer from clusterCache,
+ // if not use the namespaced caches.
+ isNamespaced, err := objectutil.IsAPINamespaced(obj, c.Scheme, c.RESTMapper)
+ if err != nil {
+ return nil, err
+ }
+ if !isNamespaced {
+ clusterCacheInf, err := c.clusterCache.GetInformer(ctx, obj)
+ if err != nil {
+ return nil, err
+ }
+ informers[globalCache] = clusterCacheInf
+
+ return &multiNamespaceInformer{namespaceToInformer: informers}, nil
+ }
+
+ for ns, cache := range c.namespaceToCache {
+ informer, err := cache.GetInformer(ctx, obj)
+ if err != nil {
+ return nil, err
+ }
+ informers[ns] = informer
+ }
+
+ return &multiNamespaceInformer{namespaceToInformer: informers}, nil
+}
+
+func (c *multiNamespaceCache) GetInformerForKind(ctx context.Context, gvk schema.GroupVersionKind) (Informer, error) {
+ informers := map[string]Informer{}
+
+ // If the object is clusterscoped, get the informer from clusterCache,
+ // if not use the namespaced caches.
+ isNamespaced, err := objectutil.IsAPINamespacedWithGVK(gvk, c.Scheme, c.RESTMapper)
+ if err != nil {
+ return nil, err
+ }
+ if !isNamespaced {
+ clusterCacheInf, err := c.clusterCache.GetInformerForKind(ctx, gvk)
+ if err != nil {
+ return nil, err
+ }
+ informers[globalCache] = clusterCacheInf
+
+ return &multiNamespaceInformer{namespaceToInformer: informers}, nil
+ }
+
+ for ns, cache := range c.namespaceToCache {
+ informer, err := cache.GetInformerForKind(ctx, gvk)
+ if err != nil {
+ return nil, err
+ }
+ informers[ns] = informer
+ }
+
+ return &multiNamespaceInformer{namespaceToInformer: informers}, nil
+}
+
+func (c *multiNamespaceCache) Start(ctx context.Context) error {
+ // start global cache
+ go func() {
+ err := c.clusterCache.Start(ctx)
+ if err != nil {
+ log.Error(err, "cluster scoped cache failed to start")
+ }
+ }()
+
+ // start namespaced caches
+ for ns, cache := range c.namespaceToCache {
+ go func(ns string, cache Cache) {
+ err := cache.Start(ctx)
+ if err != nil {
+ log.Error(err, "multinamespace cache failed to start namespaced informer", "namespace", ns)
+ }
+ }(ns, cache)
+ }
+
+ <-ctx.Done()
+ return nil
+}
+
+func (c *multiNamespaceCache) WaitForCacheSync(ctx context.Context) bool {
+ synced := true
+ for _, cache := range c.namespaceToCache {
+ if s := cache.WaitForCacheSync(ctx); !s {
+ synced = s
+ }
+ }
+
+ // check if cluster scoped cache has synced
+ if !c.clusterCache.WaitForCacheSync(ctx) {
+ synced = false
+ }
+ return synced
+}
+
+func (c *multiNamespaceCache) IndexField(ctx context.Context, obj client.Object, field string, extractValue client.IndexerFunc) error {
+ isNamespaced, err := objectutil.IsAPINamespaced(obj, c.Scheme, c.RESTMapper)
+ if err != nil {
+ return nil //nolint:nilerr
+ }
+
+ if !isNamespaced {
+ return c.clusterCache.IndexField(ctx, obj, field, extractValue)
+ }
+
+ for _, cache := range c.namespaceToCache {
+ if err := cache.IndexField(ctx, obj, field, extractValue); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (c *multiNamespaceCache) Get(ctx context.Context, key client.ObjectKey, obj client.Object) error {
+ isNamespaced, err := objectutil.IsAPINamespaced(obj, c.Scheme, c.RESTMapper)
+ if err != nil {
+ return err
+ }
+
+ if !isNamespaced {
+ // Look into the global cache to fetch the object
+ return c.clusterCache.Get(ctx, key, obj)
+ }
+
+ cache, ok := c.namespaceToCache[key.Namespace]
+ if !ok {
+ return fmt.Errorf("unable to get: %v because of unknown namespace for the cache", key)
+ }
+ return cache.Get(ctx, key, obj)
+}
+
+// List multi namespace cache will get all the objects in the namespaces that the cache is watching if asked for all namespaces.
+func (c *multiNamespaceCache) List(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error {
+ listOpts := client.ListOptions{}
+ listOpts.ApplyOptions(opts)
+
+ isNamespaced, err := objectutil.IsAPINamespaced(list, c.Scheme, c.RESTMapper)
+ if err != nil {
+ return err
+ }
+
+ if !isNamespaced {
+ // Look at the global cache to get the objects with the specified GVK
+ return c.clusterCache.List(ctx, list, opts...)
+ }
+
+ if listOpts.Namespace != corev1.NamespaceAll {
+ cache, ok := c.namespaceToCache[listOpts.Namespace]
+ if !ok {
+ return fmt.Errorf("unable to get: %v because of unknown namespace for the cache", listOpts.Namespace)
+ }
+ return cache.List(ctx, list, opts...)
+ }
+
+ listAccessor, err := apimeta.ListAccessor(list)
+ if err != nil {
+ return err
+ }
+
+ allItems, err := apimeta.ExtractList(list)
+ if err != nil {
+ return err
+ }
+
+ limitSet := listOpts.Limit > 0
+
+ var resourceVersion string
+ for _, cache := range c.namespaceToCache {
+ listObj := list.DeepCopyObject().(client.ObjectList)
+ err = cache.List(ctx, listObj, &listOpts)
+ if err != nil {
+ return err
+ }
+ items, err := apimeta.ExtractList(listObj)
+ if err != nil {
+ return err
+ }
+ accessor, err := apimeta.ListAccessor(listObj)
+ if err != nil {
+ return fmt.Errorf("object: %T must be a list type", list)
+ }
+ allItems = append(allItems, items...)
+ // The last list call should have the most correct resource version.
+ resourceVersion = accessor.GetResourceVersion()
+ if limitSet {
+ // decrement Limit by the number of items
+ // fetched from the current namespace.
+ listOpts.Limit -= int64(len(items))
+ // if a Limit was set and the number of
+ // items read has reached this set limit,
+ // then stop reading.
+ if listOpts.Limit == 0 {
+ break
+ }
+ }
+ }
+ listAccessor.SetResourceVersion(resourceVersion)
+
+ return apimeta.SetList(list, allItems)
+}
+
+// multiNamespaceInformer knows how to handle interacting with the underlying informer across multiple namespaces.
+type multiNamespaceInformer struct {
+ namespaceToInformer map[string]Informer
+}
+
+var _ Informer = &multiNamespaceInformer{}
+
+// AddEventHandler adds the handler to each namespaced informer.
+func (i *multiNamespaceInformer) AddEventHandler(handler toolscache.ResourceEventHandler) {
+ for _, informer := range i.namespaceToInformer {
+ informer.AddEventHandler(handler)
+ }
+}
+
+// AddEventHandlerWithResyncPeriod adds the handler with a resync period to each namespaced informer.
+func (i *multiNamespaceInformer) AddEventHandlerWithResyncPeriod(handler toolscache.ResourceEventHandler, resyncPeriod time.Duration) {
+ for _, informer := range i.namespaceToInformer {
+ informer.AddEventHandlerWithResyncPeriod(handler, resyncPeriod)
+ }
+}
+
+// AddIndexers adds the indexer for each namespaced informer.
+func (i *multiNamespaceInformer) AddIndexers(indexers toolscache.Indexers) error {
+ for _, informer := range i.namespaceToInformer {
+ err := informer.AddIndexers(indexers)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// HasSynced checks if each namespaced informer has synced.
+func (i *multiNamespaceInformer) HasSynced() bool {
+ for _, informer := range i.namespaceToInformer {
+ if ok := informer.HasSynced(); !ok {
+ return ok
+ }
+ }
+ return true
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/certwatcher/certwatcher.go b/vendor/sigs.k8s.io/controller-runtime/pkg/certwatcher/certwatcher.go
new file mode 100644
index 000000000..e8e0e17a2
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/certwatcher/certwatcher.go
@@ -0,0 +1,163 @@
+/*
+Copyright 2021 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package certwatcher
+
+import (
+ "context"
+ "crypto/tls"
+ "sync"
+
+ "github.com/fsnotify/fsnotify"
+ logf "sigs.k8s.io/controller-runtime/pkg/internal/log"
+)
+
+var log = logf.RuntimeLog.WithName("certwatcher")
+
+// CertWatcher watches certificate and key files for changes. When either file
+// changes, it reads and parses both and calls an optional callback with the new
+// certificate.
+type CertWatcher struct {
+ sync.RWMutex
+
+ currentCert *tls.Certificate
+ watcher *fsnotify.Watcher
+
+ certPath string
+ keyPath string
+}
+
+// New returns a new CertWatcher watching the given certificate and key.
+func New(certPath, keyPath string) (*CertWatcher, error) {
+ var err error
+
+ cw := &CertWatcher{
+ certPath: certPath,
+ keyPath: keyPath,
+ }
+
+ // Initial read of certificate and key.
+ if err := cw.ReadCertificate(); err != nil {
+ return nil, err
+ }
+
+ cw.watcher, err = fsnotify.NewWatcher()
+ if err != nil {
+ return nil, err
+ }
+
+ return cw, nil
+}
+
+// GetCertificate fetches the currently loaded certificate, which may be nil.
+func (cw *CertWatcher) GetCertificate(_ *tls.ClientHelloInfo) (*tls.Certificate, error) {
+ cw.RLock()
+ defer cw.RUnlock()
+ return cw.currentCert, nil
+}
+
+// Start starts the watch on the certificate and key files.
+func (cw *CertWatcher) Start(ctx context.Context) error {
+ files := []string{cw.certPath, cw.keyPath}
+
+ for _, f := range files {
+ if err := cw.watcher.Add(f); err != nil {
+ return err
+ }
+ }
+
+ go cw.Watch()
+
+ log.Info("Starting certificate watcher")
+
+ // Block until the context is done.
+ <-ctx.Done()
+
+ return cw.watcher.Close()
+}
+
+// Watch reads events from the watcher's channel and reacts to changes.
+func (cw *CertWatcher) Watch() {
+ for {
+ select {
+ case event, ok := <-cw.watcher.Events:
+ // Channel is closed.
+ if !ok {
+ return
+ }
+
+ cw.handleEvent(event)
+
+ case err, ok := <-cw.watcher.Errors:
+ // Channel is closed.
+ if !ok {
+ return
+ }
+
+ log.Error(err, "certificate watch error")
+ }
+ }
+}
+
+// ReadCertificate reads the certificate and key files from disk, parses them,
+// and updates the current certificate on the watcher. If a callback is set, it
+// is invoked with the new certificate.
+func (cw *CertWatcher) ReadCertificate() error {
+ cert, err := tls.LoadX509KeyPair(cw.certPath, cw.keyPath)
+ if err != nil {
+ return err
+ }
+
+ cw.Lock()
+ cw.currentCert = &cert
+ cw.Unlock()
+
+ log.Info("Updated current TLS certificate")
+
+ return nil
+}
+
+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)) {
+ return
+ }
+
+ log.V(1).Info("certificate event", "event", event)
+
+ // If the file was removed, re-add the watch.
+ if isRemove(event) {
+ if err := cw.watcher.Add(event.Name); err != nil {
+ log.Error(err, "error re-watching file")
+ }
+ }
+
+ if err := cw.ReadCertificate(); err != nil {
+ log.Error(err, "error re-reading certificate")
+ }
+}
+
+func isWrite(event fsnotify.Event) bool {
+ return event.Op&fsnotify.Write == fsnotify.Write
+}
+
+func isCreate(event fsnotify.Event) bool {
+ return event.Op&fsnotify.Create == fsnotify.Create
+}
+
+func isRemove(event fsnotify.Event) bool {
+ return event.Op&fsnotify.Remove == fsnotify.Remove
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/certwatcher/doc.go b/vendor/sigs.k8s.io/controller-runtime/pkg/certwatcher/doc.go
new file mode 100644
index 000000000..40c2fc0bf
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/certwatcher/doc.go
@@ -0,0 +1,23 @@
+/*
+Copyright 2021 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+/*
+Package certwatcher is a helper for reloading Certificates from disk to be used
+with tls servers. It provides a helper func `GetCertificate` which can be
+called from `tls.Config` and passed into your tls.Listener. For a detailed
+example server view pkg/webhook/server.go.
+*/
+package certwatcher
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/apimachinery.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/apimachinery.go
new file mode 100644
index 000000000..c92b0eaae
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/apimachinery.go
@@ -0,0 +1,196 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Package apiutil contains utilities for working with raw Kubernetes
+// API machinery, such as creating RESTMappers and raw REST clients,
+// and extracting the GVK of an object.
+package apiutil
+
+import (
+ "fmt"
+ "reflect"
+ "sync"
+
+ "k8s.io/apimachinery/pkg/api/meta"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "k8s.io/apimachinery/pkg/runtime/serializer"
+ "k8s.io/client-go/discovery"
+ clientgoscheme "k8s.io/client-go/kubernetes/scheme"
+ "k8s.io/client-go/rest"
+ "k8s.io/client-go/restmapper"
+)
+
+var (
+ protobufScheme = runtime.NewScheme()
+ protobufSchemeLock sync.RWMutex
+)
+
+func init() {
+ // Currently only enabled for built-in resources which are guaranteed to implement Protocol Buffers.
+ // For custom resources, CRDs can not support Protocol Buffers but Aggregated API can.
+ // See doc: https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/#advanced-features-and-flexibility
+ if err := clientgoscheme.AddToScheme(protobufScheme); err != nil {
+ panic(err)
+ }
+}
+
+// AddToProtobufScheme add the given SchemeBuilder into protobufScheme, which should
+// be additional types that do support protobuf.
+func AddToProtobufScheme(addToScheme func(*runtime.Scheme) error) error {
+ protobufSchemeLock.Lock()
+ defer protobufSchemeLock.Unlock()
+ return addToScheme(protobufScheme)
+}
+
+// NewDiscoveryRESTMapper constructs a new RESTMapper based on discovery
+// information fetched by a new client with the given config.
+func NewDiscoveryRESTMapper(c *rest.Config) (meta.RESTMapper, error) {
+ // Get a mapper
+ dc, err := discovery.NewDiscoveryClientForConfig(c)
+ if err != nil {
+ return nil, err
+ }
+ gr, err := restmapper.GetAPIGroupResources(dc)
+ if err != nil {
+ return nil, err
+ }
+ return restmapper.NewDiscoveryRESTMapper(gr), nil
+}
+
+// GVKForObject finds the GroupVersionKind associated with the given object, if there is only a single such GVK.
+func GVKForObject(obj runtime.Object, scheme *runtime.Scheme) (schema.GroupVersionKind, error) {
+ // TODO(directxman12): do we want to generalize this to arbitrary container types?
+ // I think we'd need a generalized form of scheme or something. It's a
+ // shame there's not a reliable "GetGVK" interface that works by default
+ // for unpopulated static types and populated "dynamic" types
+ // (unstructured, partial, etc)
+
+ // check for PartialObjectMetadata, which is analogous to unstructured, but isn't handled by ObjectKinds
+ _, isPartial := obj.(*metav1.PartialObjectMetadata) //nolint:ifshort
+ _, isPartialList := obj.(*metav1.PartialObjectMetadataList)
+ if isPartial || isPartialList {
+ // we require that the GVK be populated in order to recognize the object
+ gvk := obj.GetObjectKind().GroupVersionKind()
+ if len(gvk.Kind) == 0 {
+ return schema.GroupVersionKind{}, runtime.NewMissingKindErr("unstructured object has no kind")
+ }
+ if len(gvk.Version) == 0 {
+ return schema.GroupVersionKind{}, runtime.NewMissingVersionErr("unstructured object has no version")
+ }
+ return gvk, nil
+ }
+
+ gvks, isUnversioned, err := scheme.ObjectKinds(obj)
+ if err != nil {
+ return schema.GroupVersionKind{}, err
+ }
+ if isUnversioned {
+ return schema.GroupVersionKind{}, fmt.Errorf("cannot create group-version-kind for unversioned type %T", obj)
+ }
+
+ if len(gvks) < 1 {
+ return schema.GroupVersionKind{}, fmt.Errorf("no group-version-kinds associated with type %T", obj)
+ }
+ if len(gvks) > 1 {
+ // this should only trigger for things like metav1.XYZ --
+ // normal versioned types should be fine
+ return schema.GroupVersionKind{}, fmt.Errorf(
+ "multiple group-version-kinds associated with type %T, refusing to guess at one", obj)
+ }
+ return gvks[0], nil
+}
+
+// RESTClientForGVK constructs a new rest.Interface capable of accessing the resource associated
+// with the given GroupVersionKind. The REST client will be configured to use the negotiated serializer from
+// baseConfig, if set, otherwise a default serializer will be set.
+func RESTClientForGVK(gvk schema.GroupVersionKind, isUnstructured bool, baseConfig *rest.Config, codecs serializer.CodecFactory) (rest.Interface, error) {
+ return rest.RESTClientFor(createRestConfig(gvk, isUnstructured, baseConfig, codecs))
+}
+
+// serializerWithDecodedGVK is a CodecFactory that overrides the DecoderToVersion of a WithoutConversionCodecFactory
+// in order to avoid clearing the GVK from the decoded object.
+//
+// See https://github.com/kubernetes/kubernetes/issues/80609.
+type serializerWithDecodedGVK struct {
+ serializer.WithoutConversionCodecFactory
+}
+
+// DecoderToVersion returns an decoder that does not do conversion.
+func (f serializerWithDecodedGVK) DecoderToVersion(serializer runtime.Decoder, _ runtime.GroupVersioner) runtime.Decoder {
+ return serializer
+}
+
+// createRestConfig copies the base config and updates needed fields for a new rest config.
+func createRestConfig(gvk schema.GroupVersionKind, isUnstructured bool, baseConfig *rest.Config, codecs serializer.CodecFactory) *rest.Config {
+ gv := gvk.GroupVersion()
+
+ cfg := rest.CopyConfig(baseConfig)
+ cfg.GroupVersion = &gv
+ if gvk.Group == "" {
+ cfg.APIPath = "/api"
+ } else {
+ cfg.APIPath = "/apis"
+ }
+ if cfg.UserAgent == "" {
+ cfg.UserAgent = rest.DefaultKubernetesUserAgent()
+ }
+ // TODO(FillZpp): In the long run, we want to check discovery or something to make sure that this is actually true.
+ if cfg.ContentType == "" && !isUnstructured {
+ protobufSchemeLock.RLock()
+ if protobufScheme.Recognizes(gvk) {
+ cfg.ContentType = runtime.ContentTypeProtobuf
+ }
+ protobufSchemeLock.RUnlock()
+ }
+
+ if isUnstructured {
+ // If the object is unstructured, we need to preserve the GVK information.
+ // Use our own custom serializer.
+ cfg.NegotiatedSerializer = serializerWithDecodedGVK{serializer.WithoutConversionCodecFactory{CodecFactory: codecs}}
+ } else {
+ cfg.NegotiatedSerializer = serializerWithTargetZeroingDecode{NegotiatedSerializer: serializer.WithoutConversionCodecFactory{CodecFactory: codecs}}
+ }
+
+ return cfg
+}
+
+type serializerWithTargetZeroingDecode struct {
+ runtime.NegotiatedSerializer
+}
+
+func (s serializerWithTargetZeroingDecode) DecoderToVersion(serializer runtime.Decoder, r runtime.GroupVersioner) runtime.Decoder {
+ return targetZeroingDecoder{upstream: s.NegotiatedSerializer.DecoderToVersion(serializer, r)}
+}
+
+type targetZeroingDecoder struct {
+ upstream runtime.Decoder
+}
+
+func (t targetZeroingDecoder) Decode(data []byte, defaults *schema.GroupVersionKind, into runtime.Object) (runtime.Object, *schema.GroupVersionKind, error) {
+ zero(into)
+ return t.upstream.Decode(data, defaults, into)
+}
+
+// zero zeros the value of a pointer.
+func zero(x interface{}) {
+ if x == nil {
+ return
+ }
+ res := reflect.ValueOf(x).Elem()
+ res.Set(reflect.Zero(res.Type()))
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/dynamicrestmapper.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/dynamicrestmapper.go
new file mode 100644
index 000000000..56a00371f
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/dynamicrestmapper.go
@@ -0,0 +1,285 @@
+/*
+Copyright 2019 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package apiutil
+
+import (
+ "errors"
+ "sync"
+
+ "golang.org/x/time/rate"
+ "k8s.io/apimachinery/pkg/api/meta"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "k8s.io/client-go/discovery"
+ "k8s.io/client-go/rest"
+ "k8s.io/client-go/restmapper"
+)
+
+// dynamicRESTMapper is a RESTMapper that dynamically discovers resource
+// types at runtime.
+type dynamicRESTMapper struct {
+ mu sync.RWMutex // protects the following fields
+ staticMapper meta.RESTMapper
+ limiter *rate.Limiter
+ newMapper func() (meta.RESTMapper, error)
+
+ lazy bool
+ // Used for lazy init.
+ initOnce sync.Once
+}
+
+// DynamicRESTMapperOption is a functional option on the dynamicRESTMapper.
+type DynamicRESTMapperOption func(*dynamicRESTMapper) error
+
+// WithLimiter sets the RESTMapper's underlying limiter to lim.
+func WithLimiter(lim *rate.Limiter) DynamicRESTMapperOption {
+ return func(drm *dynamicRESTMapper) error {
+ drm.limiter = lim
+ return nil
+ }
+}
+
+// WithLazyDiscovery prevents the RESTMapper from discovering REST mappings
+// until an API call is made.
+var WithLazyDiscovery DynamicRESTMapperOption = func(drm *dynamicRESTMapper) error {
+ drm.lazy = true
+ return nil
+}
+
+// WithCustomMapper supports setting a custom RESTMapper refresher instead of
+// the default method, which uses a discovery client.
+//
+// This exists mainly for testing, but can be useful if you need tighter control
+// over how discovery is performed, which discovery endpoints are queried, etc.
+func WithCustomMapper(newMapper func() (meta.RESTMapper, error)) DynamicRESTMapperOption {
+ return func(drm *dynamicRESTMapper) error {
+ drm.newMapper = newMapper
+ return nil
+ }
+}
+
+// NewDynamicRESTMapper returns a dynamic RESTMapper for cfg. The dynamic
+// RESTMapper dynamically discovers resource types at runtime. opts
+// configure the RESTMapper.
+func NewDynamicRESTMapper(cfg *rest.Config, opts ...DynamicRESTMapperOption) (meta.RESTMapper, error) {
+ client, err := discovery.NewDiscoveryClientForConfig(cfg)
+ if err != nil {
+ return nil, err
+ }
+ drm := &dynamicRESTMapper{
+ limiter: rate.NewLimiter(rate.Limit(defaultRefillRate), defaultLimitSize),
+ newMapper: func() (meta.RESTMapper, error) {
+ groupResources, err := restmapper.GetAPIGroupResources(client)
+ if err != nil {
+ return nil, err
+ }
+ return restmapper.NewDiscoveryRESTMapper(groupResources), nil
+ },
+ }
+ for _, opt := range opts {
+ if err = opt(drm); err != nil {
+ return nil, err
+ }
+ }
+ if !drm.lazy {
+ if err := drm.setStaticMapper(); err != nil {
+ return nil, err
+ }
+ }
+ return drm, nil
+}
+
+var (
+ // defaultRefilRate is the default rate at which potential calls are
+ // added back to the "bucket" of allowed calls.
+ defaultRefillRate = 5
+ // defaultLimitSize is the default starting/max number of potential calls
+ // per second. Once a call is used, it's added back to the bucket at a rate
+ // of defaultRefillRate per second.
+ defaultLimitSize = 5
+)
+
+// setStaticMapper sets drm's staticMapper by querying its client, regardless
+// of reload backoff.
+func (drm *dynamicRESTMapper) setStaticMapper() error {
+ newMapper, err := drm.newMapper()
+ if err != nil {
+ return err
+ }
+ drm.staticMapper = newMapper
+ return nil
+}
+
+// init initializes drm only once if drm is lazy.
+func (drm *dynamicRESTMapper) init() (err error) {
+ drm.initOnce.Do(func() {
+ if drm.lazy {
+ err = drm.setStaticMapper()
+ }
+ })
+ return err
+}
+
+// checkAndReload attempts to call the given callback, which is assumed to be dependent
+// on the data in the restmapper.
+//
+// If the callback returns an error that matches the given error, it will attempt to reload
+// the RESTMapper's data and re-call the callback once that's occurred.
+// If the callback returns any other error, the function will return immediately regardless.
+//
+// It will take care of ensuring that reloads are rate-limited and that extraneous calls
+// aren't made. If a reload would exceed the limiters rate, it returns the error return by
+// the callback.
+// It's thread-safe, and worries about thread-safety for the callback (so the callback does
+// not need to attempt to lock the restmapper).
+func (drm *dynamicRESTMapper) checkAndReload(needsReloadErr error, checkNeedsReload func() error) error {
+ // first, check the common path -- data is fresh enough
+ // (use an IIFE for the lock's defer)
+ err := func() error {
+ drm.mu.RLock()
+ defer drm.mu.RUnlock()
+
+ return checkNeedsReload()
+ }()
+
+ // NB(directxman12): `Is` and `As` have a confusing relationship --
+ // `Is` is like `== or does this implement .Is`, whereas `As` says
+ // `can I type-assert into`
+ needsReload := errors.As(err, &needsReloadErr)
+ if !needsReload {
+ return err
+ }
+
+ // if the data wasn't fresh, we'll need to try and update it, so grab the lock...
+ drm.mu.Lock()
+ defer drm.mu.Unlock()
+
+ // ... and double-check that we didn't reload in the meantime
+ err = checkNeedsReload()
+ needsReload = errors.As(err, &needsReloadErr)
+ if !needsReload {
+ return err
+ }
+
+ // we're still stale, so grab a rate-limit token if we can...
+ if !drm.limiter.Allow() {
+ // return error from static mapper here, we have refreshed often enough (exceeding rate of provided limiter)
+ // so that client's can handle this the same way as a "normal" NoResourceMatchError / NoKindMatchError
+ return err
+ }
+
+ // ...reload...
+ if err := drm.setStaticMapper(); err != nil {
+ return err
+ }
+
+ // ...and return the results of the closure regardless
+ return checkNeedsReload()
+}
+
+// TODO: wrap reload errors on NoKindMatchError with go 1.13 errors.
+
+func (drm *dynamicRESTMapper) KindFor(resource schema.GroupVersionResource) (schema.GroupVersionKind, error) {
+ if err := drm.init(); err != nil {
+ return schema.GroupVersionKind{}, err
+ }
+ var gvk schema.GroupVersionKind
+ err := drm.checkAndReload(&meta.NoResourceMatchError{}, func() error {
+ var err error
+ gvk, err = drm.staticMapper.KindFor(resource)
+ return err
+ })
+ return gvk, err
+}
+
+func (drm *dynamicRESTMapper) KindsFor(resource schema.GroupVersionResource) ([]schema.GroupVersionKind, error) {
+ if err := drm.init(); err != nil {
+ return nil, err
+ }
+ var gvks []schema.GroupVersionKind
+ err := drm.checkAndReload(&meta.NoResourceMatchError{}, func() error {
+ var err error
+ gvks, err = drm.staticMapper.KindsFor(resource)
+ return err
+ })
+ return gvks, err
+}
+
+func (drm *dynamicRESTMapper) ResourceFor(input schema.GroupVersionResource) (schema.GroupVersionResource, error) {
+ if err := drm.init(); err != nil {
+ return schema.GroupVersionResource{}, err
+ }
+
+ var gvr schema.GroupVersionResource
+ err := drm.checkAndReload(&meta.NoResourceMatchError{}, func() error {
+ var err error
+ gvr, err = drm.staticMapper.ResourceFor(input)
+ return err
+ })
+ return gvr, err
+}
+
+func (drm *dynamicRESTMapper) ResourcesFor(input schema.GroupVersionResource) ([]schema.GroupVersionResource, error) {
+ if err := drm.init(); err != nil {
+ return nil, err
+ }
+ var gvrs []schema.GroupVersionResource
+ err := drm.checkAndReload(&meta.NoResourceMatchError{}, func() error {
+ var err error
+ gvrs, err = drm.staticMapper.ResourcesFor(input)
+ return err
+ })
+ return gvrs, err
+}
+
+func (drm *dynamicRESTMapper) RESTMapping(gk schema.GroupKind, versions ...string) (*meta.RESTMapping, error) {
+ if err := drm.init(); err != nil {
+ return nil, err
+ }
+ var mapping *meta.RESTMapping
+ err := drm.checkAndReload(&meta.NoKindMatchError{}, func() error {
+ var err error
+ mapping, err = drm.staticMapper.RESTMapping(gk, versions...)
+ return err
+ })
+ return mapping, err
+}
+
+func (drm *dynamicRESTMapper) RESTMappings(gk schema.GroupKind, versions ...string) ([]*meta.RESTMapping, error) {
+ if err := drm.init(); err != nil {
+ return nil, err
+ }
+ var mappings []*meta.RESTMapping
+ err := drm.checkAndReload(&meta.NoKindMatchError{}, func() error {
+ var err error
+ mappings, err = drm.staticMapper.RESTMappings(gk, versions...)
+ return err
+ })
+ return mappings, err
+}
+
+func (drm *dynamicRESTMapper) ResourceSingularizer(resource string) (string, error) {
+ if err := drm.init(); err != nil {
+ return "", err
+ }
+ var singular string
+ err := drm.checkAndReload(&meta.NoResourceMatchError{}, func() error {
+ var err error
+ singular, err = drm.staticMapper.ResourceSingularizer(resource)
+ return err
+ })
+ return singular, err
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/client.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/client.go
new file mode 100644
index 000000000..bbe36c467
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/client.go
@@ -0,0 +1,328 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package client
+
+import (
+ "context"
+ "fmt"
+ "strings"
+
+ "k8s.io/apimachinery/pkg/api/meta"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "k8s.io/apimachinery/pkg/runtime/serializer"
+ "k8s.io/client-go/kubernetes/scheme"
+ "k8s.io/client-go/metadata"
+ "k8s.io/client-go/rest"
+
+ "sigs.k8s.io/controller-runtime/pkg/client/apiutil"
+ "sigs.k8s.io/controller-runtime/pkg/log"
+)
+
+// 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 deuplication
+ AllowDuplicateLogs bool
+}
+
+// Options are creation options for a Client.
+type Options struct {
+ // Scheme, if provided, will be used to map go structs to GroupVersionKinds
+ Scheme *runtime.Scheme
+
+ // Mapper, if provided, will be used to map GroupVersionKinds to Resources
+ Mapper meta.RESTMapper
+
+ // Opts is used to configure the warning handler responsible for
+ // surfacing and handling warnings messages sent by the API server.
+ Opts WarningHandlerOptions
+}
+
+// New returns a new Client using the provided config and Options.
+// The returned client reads *and* writes directly from the server
+// (it doesn't use object caches). It understands how to work with
+// normal types (both custom resources and aggregated/built-in resources),
+// as well as unstructured types.
+//
+// In the case of normal types, the scheme will be used to look up the
+// corresponding group, version, and kind for the given type. In the
+// case of unstructured types, the group, version, and kind will be extracted
+// from the corresponding fields on the object.
+func New(config *rest.Config, options Options) (Client, error) {
+ return newClient(config, options)
+}
+
+func newClient(config *rest.Config, options Options) (*client, error) {
+ if config == nil {
+ return nil, fmt.Errorf("must provide non-nil rest.Config to client.New")
+ }
+
+ if !options.Opts.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.
+ rest.SetDefaultWarningHandler(
+ log.NewKubeAPIWarningLogger(
+ logger,
+ log.KubeAPIWarningLoggerOptions{
+ Deduplicate: !options.Opts.AllowDuplicateLogs,
+ },
+ ),
+ )
+ }
+
+ // Init a scheme if none provided
+ if options.Scheme == nil {
+ options.Scheme = scheme.Scheme
+ }
+
+ // Init a Mapper if none provided
+ if options.Mapper == nil {
+ var err error
+ options.Mapper, err = apiutil.NewDynamicRESTMapper(config)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ clientcache := &clientCache{
+ config: config,
+ scheme: options.Scheme,
+ mapper: options.Mapper,
+ codecs: serializer.NewCodecFactory(options.Scheme),
+
+ structuredResourceByType: make(map[schema.GroupVersionKind]*resourceMeta),
+ unstructuredResourceByType: make(map[schema.GroupVersionKind]*resourceMeta),
+ }
+
+ rawMetaClient, err := metadata.NewForConfig(config)
+ if err != nil {
+ return nil, fmt.Errorf("unable to construct metadata-only client for use as part of client: %w", err)
+ }
+
+ c := &client{
+ typedClient: typedClient{
+ cache: clientcache,
+ paramCodec: runtime.NewParameterCodec(options.Scheme),
+ },
+ unstructuredClient: unstructuredClient{
+ cache: clientcache,
+ paramCodec: noConversionParamCodec{},
+ },
+ metadataClient: metadataClient{
+ client: rawMetaClient,
+ restMapper: options.Mapper,
+ },
+ scheme: options.Scheme,
+ mapper: options.Mapper,
+ }
+
+ return c, nil
+}
+
+var _ Client = &client{}
+
+// client is a client.Client that reads and writes directly from/to an API server. It lazily initializes
+// new clients at the time they are used, and caches the client.
+type client struct {
+ typedClient typedClient
+ unstructuredClient unstructuredClient
+ metadataClient metadataClient
+ scheme *runtime.Scheme
+ mapper meta.RESTMapper
+}
+
+// resetGroupVersionKind is a helper function to restore and preserve GroupVersionKind on an object.
+func (c *client) resetGroupVersionKind(obj runtime.Object, gvk schema.GroupVersionKind) {
+ if gvk != schema.EmptyObjectKind.GroupVersionKind() {
+ if v, ok := obj.(schema.ObjectKind); ok {
+ v.SetGroupVersionKind(gvk)
+ }
+ }
+}
+
+// Scheme returns the scheme this client is using.
+func (c *client) Scheme() *runtime.Scheme {
+ return c.scheme
+}
+
+// RESTMapper returns the scheme this client is using.
+func (c *client) RESTMapper() meta.RESTMapper {
+ return c.mapper
+}
+
+// Create implements client.Client.
+func (c *client) Create(ctx context.Context, obj Object, opts ...CreateOption) error {
+ switch obj.(type) {
+ case *unstructured.Unstructured:
+ return c.unstructuredClient.Create(ctx, obj, opts...)
+ case *metav1.PartialObjectMetadata:
+ return fmt.Errorf("cannot create using only metadata")
+ default:
+ return c.typedClient.Create(ctx, obj, opts...)
+ }
+}
+
+// Update implements client.Client.
+func (c *client) Update(ctx context.Context, obj Object, opts ...UpdateOption) error {
+ defer c.resetGroupVersionKind(obj, obj.GetObjectKind().GroupVersionKind())
+ switch obj.(type) {
+ case *unstructured.Unstructured:
+ return c.unstructuredClient.Update(ctx, obj, opts...)
+ case *metav1.PartialObjectMetadata:
+ return fmt.Errorf("cannot update using only metadata -- did you mean to patch?")
+ default:
+ return c.typedClient.Update(ctx, obj, opts...)
+ }
+}
+
+// Delete implements client.Client.
+func (c *client) Delete(ctx context.Context, obj Object, opts ...DeleteOption) error {
+ switch obj.(type) {
+ case *unstructured.Unstructured:
+ return c.unstructuredClient.Delete(ctx, obj, opts...)
+ case *metav1.PartialObjectMetadata:
+ return c.metadataClient.Delete(ctx, obj, opts...)
+ default:
+ return c.typedClient.Delete(ctx, obj, opts...)
+ }
+}
+
+// DeleteAllOf implements client.Client.
+func (c *client) DeleteAllOf(ctx context.Context, obj Object, opts ...DeleteAllOfOption) error {
+ switch obj.(type) {
+ case *unstructured.Unstructured:
+ return c.unstructuredClient.DeleteAllOf(ctx, obj, opts...)
+ case *metav1.PartialObjectMetadata:
+ return c.metadataClient.DeleteAllOf(ctx, obj, opts...)
+ default:
+ return c.typedClient.DeleteAllOf(ctx, obj, opts...)
+ }
+}
+
+// Patch implements client.Client.
+func (c *client) Patch(ctx context.Context, obj Object, patch Patch, opts ...PatchOption) error {
+ defer c.resetGroupVersionKind(obj, obj.GetObjectKind().GroupVersionKind())
+ switch obj.(type) {
+ case *unstructured.Unstructured:
+ return c.unstructuredClient.Patch(ctx, obj, patch, opts...)
+ case *metav1.PartialObjectMetadata:
+ return c.metadataClient.Patch(ctx, obj, patch, opts...)
+ default:
+ return c.typedClient.Patch(ctx, obj, patch, opts...)
+ }
+}
+
+// Get implements client.Client.
+func (c *client) Get(ctx context.Context, key ObjectKey, obj Object) error {
+ switch obj.(type) {
+ case *unstructured.Unstructured:
+ return c.unstructuredClient.Get(ctx, key, obj)
+ case *metav1.PartialObjectMetadata:
+ // Metadata only object should always preserve the GVK coming in from the caller.
+ defer c.resetGroupVersionKind(obj, obj.GetObjectKind().GroupVersionKind())
+ return c.metadataClient.Get(ctx, key, obj)
+ default:
+ return c.typedClient.Get(ctx, key, obj)
+ }
+}
+
+// List implements client.Client.
+func (c *client) List(ctx context.Context, obj ObjectList, opts ...ListOption) error {
+ switch x := obj.(type) {
+ case *unstructured.UnstructuredList:
+ return c.unstructuredClient.List(ctx, obj, opts...)
+ case *metav1.PartialObjectMetadataList:
+ // Metadata only object should always preserve the GVK.
+ gvk := obj.GetObjectKind().GroupVersionKind()
+ defer c.resetGroupVersionKind(obj, gvk)
+
+ // Call the list client.
+ if err := c.metadataClient.List(ctx, obj, opts...); err != nil {
+ return err
+ }
+
+ // Restore the GVK for each item in the list.
+ itemGVK := schema.GroupVersionKind{
+ Group: gvk.Group,
+ Version: gvk.Version,
+ // TODO: this is producing unsafe guesses that don't actually work,
+ // but it matches ~99% of the cases out there.
+ Kind: strings.TrimSuffix(gvk.Kind, "List"),
+ }
+ for i := range x.Items {
+ item := &x.Items[i]
+ item.SetGroupVersionKind(itemGVK)
+ }
+
+ return nil
+ default:
+ return c.typedClient.List(ctx, obj, opts...)
+ }
+}
+
+// Status implements client.StatusClient.
+func (c *client) Status() StatusWriter {
+ return &statusWriter{client: c}
+}
+
+// statusWriter is client.StatusWriter that writes status subresource.
+type statusWriter struct {
+ client *client
+}
+
+// ensure statusWriter implements client.StatusWriter.
+var _ StatusWriter = &statusWriter{}
+
+// Update implements client.StatusWriter.
+func (sw *statusWriter) Update(ctx context.Context, obj Object, opts ...UpdateOption) error {
+ defer sw.client.resetGroupVersionKind(obj, obj.GetObjectKind().GroupVersionKind())
+ switch obj.(type) {
+ case *unstructured.Unstructured:
+ return sw.client.unstructuredClient.UpdateStatus(ctx, obj, opts...)
+ case *metav1.PartialObjectMetadata:
+ return fmt.Errorf("cannot update status using only metadata -- did you mean to patch?")
+ default:
+ return sw.client.typedClient.UpdateStatus(ctx, obj, opts...)
+ }
+}
+
+// Patch implements client.Client.
+func (sw *statusWriter) Patch(ctx context.Context, obj Object, patch Patch, opts ...PatchOption) error {
+ defer sw.client.resetGroupVersionKind(obj, obj.GetObjectKind().GroupVersionKind())
+ switch obj.(type) {
+ case *unstructured.Unstructured:
+ return sw.client.unstructuredClient.PatchStatus(ctx, obj, patch, opts...)
+ case *metav1.PartialObjectMetadata:
+ return sw.client.metadataClient.PatchStatus(ctx, obj, patch, opts...)
+ default:
+ return sw.client.typedClient.PatchStatus(ctx, obj, patch, opts...)
+ }
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/client_cache.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/client_cache.go
new file mode 100644
index 000000000..857a0b38a
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/client_cache.go
@@ -0,0 +1,150 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package client
+
+import (
+ "strings"
+ "sync"
+
+ "k8s.io/apimachinery/pkg/api/meta"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "k8s.io/apimachinery/pkg/runtime/serializer"
+ "k8s.io/client-go/rest"
+ "sigs.k8s.io/controller-runtime/pkg/client/apiutil"
+)
+
+// clientCache creates and caches rest clients and metadata for Kubernetes types.
+type clientCache struct {
+ // config is the rest.Config to talk to an apiserver
+ config *rest.Config
+
+ // scheme maps go structs to GroupVersionKinds
+ scheme *runtime.Scheme
+
+ // mapper maps GroupVersionKinds to Resources
+ mapper meta.RESTMapper
+
+ // codecs are used to create a REST client for a gvk
+ codecs serializer.CodecFactory
+
+ // structuredResourceByType caches structured type metadata
+ structuredResourceByType map[schema.GroupVersionKind]*resourceMeta
+ // unstructuredResourceByType caches unstructured type metadata
+ unstructuredResourceByType map[schema.GroupVersionKind]*resourceMeta
+ mu sync.RWMutex
+}
+
+// newResource maps obj to a Kubernetes Resource and constructs a client for that Resource.
+// If the object is a list, the resource represents the item's type instead.
+func (c *clientCache) newResource(gvk schema.GroupVersionKind, isList, isUnstructured bool) (*resourceMeta, error) {
+ if strings.HasSuffix(gvk.Kind, "List") && isList {
+ // if this was a list, treat it as a request for the item's resource
+ gvk.Kind = gvk.Kind[:len(gvk.Kind)-4]
+ }
+
+ client, err := apiutil.RESTClientForGVK(gvk, isUnstructured, c.config, c.codecs)
+ if err != nil {
+ return nil, err
+ }
+ mapping, err := c.mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
+ if err != nil {
+ return nil, err
+ }
+ return &resourceMeta{Interface: client, mapping: mapping, gvk: gvk}, nil
+}
+
+// getResource returns the resource meta information for the given type of object.
+// If the object is a list, the resource represents the item's type instead.
+func (c *clientCache) getResource(obj runtime.Object) (*resourceMeta, error) {
+ gvk, err := apiutil.GVKForObject(obj, c.scheme)
+ if err != nil {
+ return nil, err
+ }
+
+ _, isUnstructured := obj.(*unstructured.Unstructured)
+ _, isUnstructuredList := obj.(*unstructured.UnstructuredList)
+ isUnstructured = isUnstructured || isUnstructuredList
+
+ // It's better to do creation work twice than to not let multiple
+ // people make requests at once
+ c.mu.RLock()
+ resourceByType := c.structuredResourceByType
+ if isUnstructured {
+ resourceByType = c.unstructuredResourceByType
+ }
+ r, known := resourceByType[gvk]
+ c.mu.RUnlock()
+
+ if known {
+ return r, nil
+ }
+
+ // Initialize a new Client
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ r, err = c.newResource(gvk, meta.IsListType(obj), isUnstructured)
+ if err != nil {
+ return nil, err
+ }
+ resourceByType[gvk] = r
+ return r, err
+}
+
+// getObjMeta returns objMeta containing both type and object metadata and state.
+func (c *clientCache) getObjMeta(obj runtime.Object) (*objMeta, error) {
+ r, err := c.getResource(obj)
+ if err != nil {
+ return nil, err
+ }
+ m, err := meta.Accessor(obj)
+ if err != nil {
+ return nil, err
+ }
+ return &objMeta{resourceMeta: r, Object: m}, err
+}
+
+// resourceMeta caches state for a Kubernetes type.
+type resourceMeta struct {
+ // client is the rest client used to talk to the apiserver
+ rest.Interface
+ // gvk is the GroupVersionKind of the resourceMeta
+ gvk schema.GroupVersionKind
+ // mapping is the rest mapping
+ mapping *meta.RESTMapping
+}
+
+// isNamespaced returns true if the type is namespaced.
+func (r *resourceMeta) isNamespaced() bool {
+ return r.mapping.Scope.Name() != meta.RESTScopeNameRoot
+}
+
+// resource returns the resource name of the type.
+func (r *resourceMeta) resource() string {
+ return r.mapping.Resource.Resource
+}
+
+// objMeta stores type and object information about a Kubernetes type.
+type objMeta struct {
+ // resourceMeta contains type information for the object
+ *resourceMeta
+
+ // Object contains meta data for the object instance
+ metav1.Object
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/codec.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/codec.go
new file mode 100644
index 000000000..9c2923106
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/codec.go
@@ -0,0 +1,40 @@
+/*
+Copyright 2021 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package client
+
+import (
+ "errors"
+ "net/url"
+
+ "k8s.io/apimachinery/pkg/conversion/queryparams"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+)
+
+var _ runtime.ParameterCodec = noConversionParamCodec{}
+
+// noConversionParamCodec is a no-conversion codec for serializing parameters into URL query strings.
+// it's useful in scenarios with the unstructured client and arbitrary resources.
+type noConversionParamCodec struct{}
+
+func (noConversionParamCodec) EncodeParameters(obj runtime.Object, to schema.GroupVersion) (url.Values, error) {
+ return queryparams.Convert(obj)
+}
+
+func (noConversionParamCodec) DecodeParameters(parameters url.Values, from schema.GroupVersion, into runtime.Object) error {
+ return errors.New("DecodeParameters not implemented on noConversionParamCodec")
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/config/config.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/config/config.go
new file mode 100644
index 000000000..235a7e450
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/config/config.go
@@ -0,0 +1,157 @@
+/*
+Copyright 2017 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 (
+ "flag"
+ "fmt"
+ "os"
+ "os/user"
+ "path"
+
+ "k8s.io/client-go/rest"
+ "k8s.io/client-go/tools/clientcmd"
+ clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
+ logf "sigs.k8s.io/controller-runtime/pkg/internal/log"
+)
+
+var (
+ kubeconfig string
+ log = logf.RuntimeLog.WithName("client").WithName("config")
+)
+
+func init() {
+ // TODO: Fix this to allow double vendoring this library but still register flags on behalf of users
+ flag.StringVar(&kubeconfig, "kubeconfig", "",
+ "Paths to a kubeconfig. Only required if out-of-cluster.")
+}
+
+// GetConfig creates a *rest.Config for talking to a Kubernetes API server.
+// If --kubeconfig is set, will use the kubeconfig file at that location. Otherwise will assume running
+// in cluster and use the cluster provided kubeconfig.
+//
+// It also applies saner defaults for QPS and burst based on the Kubernetes
+// controller manager defaults (20 QPS, 30 burst)
+//
+// Config precedence
+//
+// * --kubeconfig flag pointing at a file
+//
+// * KUBECONFIG environment variable pointing at a file
+//
+// * In-cluster config if running in cluster
+//
+// * $HOME/.kube/config if exists.
+func GetConfig() (*rest.Config, error) {
+ return GetConfigWithContext("")
+}
+
+// GetConfigWithContext creates a *rest.Config for talking to a Kubernetes API server with a specific context.
+// If --kubeconfig is set, will use the kubeconfig file at that location. Otherwise will assume running
+// in cluster and use the cluster provided kubeconfig.
+//
+// It also applies saner defaults for QPS and burst based on the Kubernetes
+// controller manager defaults (20 QPS, 30 burst)
+//
+// Config precedence
+//
+// * --kubeconfig flag pointing at a file
+//
+// * KUBECONFIG environment variable pointing at a file
+//
+// * In-cluster config if running in cluster
+//
+// * $HOME/.kube/config if exists.
+func GetConfigWithContext(context string) (*rest.Config, error) {
+ cfg, err := loadConfig(context)
+ if err != nil {
+ return nil, err
+ }
+
+ if cfg.QPS == 0.0 {
+ cfg.QPS = 20.0
+ cfg.Burst = 30.0
+ }
+
+ return cfg, nil
+}
+
+// loadInClusterConfig is a function used to load the in-cluster
+// Kubernetes client config. This variable makes is possible to
+// test the precedence of loading the config.
+var loadInClusterConfig = rest.InClusterConfig
+
+// loadConfig loads a REST Config as per the rules specified in GetConfig.
+func loadConfig(context string) (*rest.Config, error) {
+ // If a flag is specified with the config location, use that
+ if len(kubeconfig) > 0 {
+ return loadConfigWithContext("", &clientcmd.ClientConfigLoadingRules{ExplicitPath: kubeconfig}, context)
+ }
+
+ // If the recommended kubeconfig env variable is not specified,
+ // try the in-cluster config.
+ kubeconfigPath := os.Getenv(clientcmd.RecommendedConfigPathEnvVar)
+ if len(kubeconfigPath) == 0 {
+ if c, err := loadInClusterConfig(); err == nil {
+ return c, nil
+ }
+ }
+
+ // If the recommended kubeconfig env variable is set, or there
+ // is no in-cluster config, try the default recommended locations.
+ //
+ // NOTE: For default config file locations, upstream only checks
+ // $HOME for the user's home directory, but we can also try
+ // os/user.HomeDir when $HOME is unset.
+ //
+ // TODO(jlanford): could this be done upstream?
+ loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
+ if _, ok := os.LookupEnv("HOME"); !ok {
+ u, err := user.Current()
+ if err != nil {
+ return nil, fmt.Errorf("could not get current user: %v", err)
+ }
+ loadingRules.Precedence = append(loadingRules.Precedence, path.Join(u.HomeDir, clientcmd.RecommendedHomeDir, clientcmd.RecommendedFileName))
+ }
+
+ return loadConfigWithContext("", loadingRules, context)
+}
+
+func loadConfigWithContext(apiServerURL string, loader clientcmd.ClientConfigLoader, context string) (*rest.Config, error) {
+ return clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
+ loader,
+ &clientcmd.ConfigOverrides{
+ ClusterInfo: clientcmdapi.Cluster{
+ Server: apiServerURL,
+ },
+ CurrentContext: context,
+ }).ClientConfig()
+}
+
+// GetConfigOrDie creates a *rest.Config for talking to a Kubernetes apiserver.
+// If --kubeconfig is set, will use the kubeconfig file at that location. Otherwise will assume running
+// in cluster and use the cluster provided kubeconfig.
+//
+// Will log an error and exit if there is an error creating the rest.Config.
+func GetConfigOrDie() *rest.Config {
+ config, err := GetConfig()
+ if err != nil {
+ log.Error(err, "unable to get kubeconfig")
+ os.Exit(1)
+ }
+ return config
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/config/doc.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/config/doc.go
new file mode 100644
index 000000000..796c9cf59
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/config/doc.go
@@ -0,0 +1,18 @@
+/*
+Copyright 2017 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 libraries for initializing REST configs for talking to the Kubernetes API
+package config
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/doc.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/doc.go
new file mode 100644
index 000000000..2965e5fa9
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/doc.go
@@ -0,0 +1,49 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Package client contains functionality for interacting with Kubernetes API
+// servers.
+//
+// Clients
+//
+// Clients are split into two interfaces -- Readers and Writers. Readers
+// get and list, while writers create, update, and delete.
+//
+// The New function can be used to create a new client that talks directly
+// to the API server.
+//
+// A common pattern in Kubernetes to read from a cache and write to the API
+// server. This pattern is covered by the DelegatingClient type, which can
+// be used to have a client whose Reader is different from the Writer.
+//
+// Options
+//
+// Many client operations in Kubernetes support options. These options are
+// represented as variadic arguments at the end of a given method call.
+// For instance, to use a label selector on list, you can call
+// err := someReader.List(context.Background(), &podList, client.MatchingLabels{"somelabel": "someval"})
+//
+// Indexing
+//
+// Indexes may be added to caches using a FieldIndexer. This allows you to easily
+// and efficiently look up objects with certain properties. You can then make
+// use of the index by specifying a field selector on calls to List on the Reader
+// corresponding to the given Cache.
+//
+// For instance, a Secret controller might have an index on the
+// `.spec.volumes.secret.secretName` field in Pod objects, so that it could
+// easily look up all pods that reference a given secret.
+package client
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/dryrun.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/dryrun.go
new file mode 100644
index 000000000..ea25ea253
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/dryrun.go
@@ -0,0 +1,106 @@
+/*
+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 client
+
+import (
+ "context"
+
+ "k8s.io/apimachinery/pkg/api/meta"
+ "k8s.io/apimachinery/pkg/runtime"
+)
+
+// NewDryRunClient wraps an existing client and enforces DryRun mode
+// on all mutating api calls.
+func NewDryRunClient(c Client) Client {
+ return &dryRunClient{client: c}
+}
+
+var _ Client = &dryRunClient{}
+
+// dryRunClient is a Client that wraps another Client in order to enforce DryRun mode.
+type dryRunClient struct {
+ client Client
+}
+
+// Scheme returns the scheme this client is using.
+func (c *dryRunClient) Scheme() *runtime.Scheme {
+ return c.client.Scheme()
+}
+
+// RESTMapper returns the rest mapper this client is using.
+func (c *dryRunClient) RESTMapper() meta.RESTMapper {
+ return c.client.RESTMapper()
+}
+
+// Create implements client.Client.
+func (c *dryRunClient) Create(ctx context.Context, obj Object, opts ...CreateOption) error {
+ return c.client.Create(ctx, obj, append(opts, DryRunAll)...)
+}
+
+// Update implements client.Client.
+func (c *dryRunClient) Update(ctx context.Context, obj Object, opts ...UpdateOption) error {
+ return c.client.Update(ctx, obj, append(opts, DryRunAll)...)
+}
+
+// Delete implements client.Client.
+func (c *dryRunClient) Delete(ctx context.Context, obj Object, opts ...DeleteOption) error {
+ return c.client.Delete(ctx, obj, append(opts, DryRunAll)...)
+}
+
+// DeleteAllOf implements client.Client.
+func (c *dryRunClient) DeleteAllOf(ctx context.Context, obj Object, opts ...DeleteAllOfOption) error {
+ return c.client.DeleteAllOf(ctx, obj, append(opts, DryRunAll)...)
+}
+
+// Patch implements client.Client.
+func (c *dryRunClient) Patch(ctx context.Context, obj Object, patch Patch, opts ...PatchOption) error {
+ return c.client.Patch(ctx, obj, patch, append(opts, DryRunAll)...)
+}
+
+// Get implements client.Client.
+func (c *dryRunClient) Get(ctx context.Context, key ObjectKey, obj Object) error {
+ return c.client.Get(ctx, key, obj)
+}
+
+// List implements client.Client.
+func (c *dryRunClient) List(ctx context.Context, obj ObjectList, opts ...ListOption) error {
+ return c.client.List(ctx, obj, opts...)
+}
+
+// Status implements client.StatusClient.
+func (c *dryRunClient) Status() StatusWriter {
+ return &dryRunStatusWriter{client: c.client.Status()}
+}
+
+// ensure dryRunStatusWriter implements client.StatusWriter.
+var _ StatusWriter = &dryRunStatusWriter{}
+
+// dryRunStatusWriter is client.StatusWriter that writes status subresource with dryRun mode
+// enforced.
+type dryRunStatusWriter struct {
+ client StatusWriter
+}
+
+// Update implements client.StatusWriter.
+func (sw *dryRunStatusWriter) Update(ctx context.Context, obj Object, opts ...UpdateOption) error {
+ return sw.client.Update(ctx, obj, append(opts, DryRunAll)...)
+}
+
+// Patch implements client.StatusWriter.
+func (sw *dryRunStatusWriter) Patch(ctx context.Context, obj Object, patch Patch, opts ...PatchOption) error {
+ return sw.client.Patch(ctx, obj, patch, append(opts, DryRunAll)...)
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/interfaces.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/interfaces.go
new file mode 100644
index 000000000..58c2ece15
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/interfaces.go
@@ -0,0 +1,145 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package client
+
+import (
+ "context"
+
+ apierrors "k8s.io/apimachinery/pkg/api/errors"
+
+ "k8s.io/apimachinery/pkg/api/meta"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/types"
+ "k8s.io/apimachinery/pkg/watch"
+)
+
+// ObjectKey identifies a Kubernetes Object.
+type ObjectKey = types.NamespacedName
+
+// ObjectKeyFromObject returns the ObjectKey given a runtime.Object.
+func ObjectKeyFromObject(obj Object) ObjectKey {
+ return ObjectKey{Namespace: obj.GetNamespace(), Name: obj.GetName()}
+}
+
+// Patch is a patch that can be applied to a Kubernetes object.
+type Patch interface {
+ // Type is the PatchType of the patch.
+ Type() types.PatchType
+ // Data is the raw data representing the patch.
+ Data(obj Object) ([]byte, error)
+}
+
+// TODO(directxman12): is there a sane way to deal with get/delete options?
+
+// Reader knows how to read and list Kubernetes objects.
+type Reader interface {
+ // Get retrieves an obj for the given object key from the Kubernetes Cluster.
+ // obj must be a struct pointer so that obj can be updated with the response
+ // returned by the Server.
+ Get(ctx context.Context, key ObjectKey, obj Object) error
+
+ // List retrieves list of objects for a given namespace and list options. On a
+ // successful call, Items field in the list will be populated with the
+ // result returned from the server.
+ List(ctx context.Context, list ObjectList, opts ...ListOption) error
+}
+
+// Writer knows how to create, delete, and update Kubernetes objects.
+type Writer interface {
+ // Create saves the object obj in the Kubernetes cluster.
+ Create(ctx context.Context, obj Object, opts ...CreateOption) error
+
+ // Delete deletes the given obj from Kubernetes cluster.
+ Delete(ctx context.Context, obj Object, opts ...DeleteOption) error
+
+ // Update updates the given obj in the Kubernetes cluster. obj must be a
+ // struct pointer so that obj can be updated with the content returned by the Server.
+ Update(ctx context.Context, obj Object, opts ...UpdateOption) error
+
+ // Patch patches the given obj in the Kubernetes cluster. obj must be a
+ // struct pointer so that obj can be updated with the content returned by the Server.
+ Patch(ctx context.Context, obj Object, patch Patch, opts ...PatchOption) error
+
+ // DeleteAllOf deletes all objects of the given type matching the given options.
+ DeleteAllOf(ctx context.Context, obj Object, opts ...DeleteAllOfOption) error
+}
+
+// StatusClient knows how to create a client which can update status subresource
+// for kubernetes objects.
+type StatusClient interface {
+ Status() StatusWriter
+}
+
+// StatusWriter knows how to update status subresource of a Kubernetes object.
+type StatusWriter interface {
+ // Update updates the fields corresponding to the status subresource for the
+ // given obj. obj must be a struct pointer so that obj can be updated
+ // with the content returned by the Server.
+ Update(ctx context.Context, obj Object, opts ...UpdateOption) error
+
+ // Patch patches the given object's subresource. obj must be a struct
+ // pointer so that obj can be updated with the content returned by the
+ // Server.
+ Patch(ctx context.Context, obj Object, patch Patch, opts ...PatchOption) error
+}
+
+// Client knows how to perform CRUD operations on Kubernetes objects.
+type Client interface {
+ Reader
+ Writer
+ StatusClient
+
+ // Scheme returns the scheme this client is using.
+ Scheme() *runtime.Scheme
+ // RESTMapper returns the rest this client is using.
+ RESTMapper() meta.RESTMapper
+}
+
+// WithWatch supports Watch on top of the CRUD operations supported by
+// the normal Client. Its intended use-case are CLI apps that need to wait for
+// events.
+type WithWatch interface {
+ Client
+ Watch(ctx context.Context, obj ObjectList, opts ...ListOption) (watch.Interface, error)
+}
+
+// IndexerFunc knows how to take an object and turn it into a series
+// of non-namespaced keys. Namespaced objects are automatically given
+// namespaced and non-spaced variants, so keys do not need to include namespace.
+type IndexerFunc func(Object) []string
+
+// FieldIndexer knows how to index over a particular "field" such that it
+// can later be used by a field selector.
+type FieldIndexer interface {
+ // IndexFields adds an index with the given field name on the given object type
+ // by using the given function to extract the value for that field. If you want
+ // compatibility with the Kubernetes API server, only return one key, and only use
+ // fields that the API server supports. Otherwise, you can return multiple keys,
+ // and "equality" in the field selector means that at least one key matches the value.
+ // The FieldIndexer will automatically take care of indexing over namespace
+ // and supporting efficient all-namespace queries.
+ IndexField(ctx context.Context, obj Object, field string, extractValue IndexerFunc) error
+}
+
+// IgnoreNotFound returns nil on NotFound errors.
+// All other values that are not NotFound errors or nil are returned unmodified.
+func IgnoreNotFound(err error) error {
+ if apierrors.IsNotFound(err) {
+ return nil
+ }
+ return err
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/metadata_client.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/metadata_client.go
new file mode 100644
index 000000000..59747463a
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/metadata_client.go
@@ -0,0 +1,195 @@
+/*
+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 client
+
+import (
+ "context"
+ "fmt"
+ "strings"
+
+ "k8s.io/apimachinery/pkg/api/meta"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "k8s.io/client-go/metadata"
+)
+
+// TODO(directxman12): we could rewrite this on top of the low-level REST
+// client to avoid the extra shallow copy at the end, but I'm not sure it's
+// worth it -- the metadata client deals with falling back to loading the whole
+// object on older API servers, etc, and we'd have to reproduce that.
+
+// metadataClient is a client that reads & writes metadata-only requests to/from the API server.
+type metadataClient struct {
+ client metadata.Interface
+ restMapper meta.RESTMapper
+}
+
+func (mc *metadataClient) getResourceInterface(gvk schema.GroupVersionKind, ns string) (metadata.ResourceInterface, error) {
+ mapping, err := mc.restMapper.RESTMapping(gvk.GroupKind(), gvk.Version)
+ if err != nil {
+ return nil, err
+ }
+ if mapping.Scope.Name() == meta.RESTScopeNameRoot {
+ return mc.client.Resource(mapping.Resource), nil
+ }
+ return mc.client.Resource(mapping.Resource).Namespace(ns), nil
+}
+
+// Delete implements client.Client.
+func (mc *metadataClient) Delete(ctx context.Context, obj Object, opts ...DeleteOption) error {
+ metadata, ok := obj.(*metav1.PartialObjectMetadata)
+ if !ok {
+ return fmt.Errorf("metadata client did not understand object: %T", obj)
+ }
+
+ resInt, err := mc.getResourceInterface(metadata.GroupVersionKind(), metadata.Namespace)
+ if err != nil {
+ return err
+ }
+
+ deleteOpts := DeleteOptions{}
+ deleteOpts.ApplyOptions(opts)
+
+ return resInt.Delete(ctx, metadata.Name, *deleteOpts.AsDeleteOptions())
+}
+
+// DeleteAllOf implements client.Client.
+func (mc *metadataClient) DeleteAllOf(ctx context.Context, obj Object, opts ...DeleteAllOfOption) error {
+ metadata, ok := obj.(*metav1.PartialObjectMetadata)
+ if !ok {
+ return fmt.Errorf("metadata client did not understand object: %T", obj)
+ }
+
+ deleteAllOfOpts := DeleteAllOfOptions{}
+ deleteAllOfOpts.ApplyOptions(opts)
+
+ resInt, err := mc.getResourceInterface(metadata.GroupVersionKind(), deleteAllOfOpts.ListOptions.Namespace)
+ if err != nil {
+ return err
+ }
+
+ return resInt.DeleteCollection(ctx, *deleteAllOfOpts.AsDeleteOptions(), *deleteAllOfOpts.AsListOptions())
+}
+
+// Patch implements client.Client.
+func (mc *metadataClient) Patch(ctx context.Context, obj Object, patch Patch, opts ...PatchOption) error {
+ metadata, ok := obj.(*metav1.PartialObjectMetadata)
+ if !ok {
+ return fmt.Errorf("metadata client did not understand object: %T", obj)
+ }
+
+ gvk := metadata.GroupVersionKind()
+ resInt, err := mc.getResourceInterface(gvk, metadata.Namespace)
+ if err != nil {
+ return err
+ }
+
+ data, err := patch.Data(obj)
+ if err != nil {
+ return err
+ }
+
+ patchOpts := &PatchOptions{}
+ patchOpts.ApplyOptions(opts)
+
+ res, err := resInt.Patch(ctx, metadata.Name, patch.Type(), data, *patchOpts.AsPatchOptions())
+ if err != nil {
+ return err
+ }
+ *metadata = *res
+ metadata.SetGroupVersionKind(gvk) // restore the GVK, which isn't set on metadata
+ return nil
+}
+
+// Get implements client.Client.
+func (mc *metadataClient) Get(ctx context.Context, key ObjectKey, obj Object) error {
+ metadata, ok := obj.(*metav1.PartialObjectMetadata)
+ if !ok {
+ return fmt.Errorf("metadata client did not understand object: %T", obj)
+ }
+
+ gvk := metadata.GroupVersionKind()
+
+ resInt, err := mc.getResourceInterface(gvk, key.Namespace)
+ if err != nil {
+ return err
+ }
+
+ res, err := resInt.Get(ctx, key.Name, metav1.GetOptions{})
+ if err != nil {
+ return err
+ }
+ *metadata = *res
+ metadata.SetGroupVersionKind(gvk) // restore the GVK, which isn't set on metadata
+ return nil
+}
+
+// List implements client.Client.
+func (mc *metadataClient) List(ctx context.Context, obj ObjectList, opts ...ListOption) error {
+ metadata, ok := obj.(*metav1.PartialObjectMetadataList)
+ if !ok {
+ return fmt.Errorf("metadata client did not understand object: %T", obj)
+ }
+
+ gvk := metadata.GroupVersionKind()
+ if strings.HasSuffix(gvk.Kind, "List") {
+ gvk.Kind = gvk.Kind[:len(gvk.Kind)-4]
+ }
+
+ listOpts := ListOptions{}
+ listOpts.ApplyOptions(opts)
+
+ resInt, err := mc.getResourceInterface(gvk, listOpts.Namespace)
+ if err != nil {
+ return err
+ }
+
+ res, err := resInt.List(ctx, *listOpts.AsListOptions())
+ if err != nil {
+ return err
+ }
+ *metadata = *res
+ metadata.SetGroupVersionKind(gvk) // restore the GVK, which isn't set on metadata
+ return nil
+}
+
+func (mc *metadataClient) PatchStatus(ctx context.Context, obj Object, patch Patch, opts ...PatchOption) error {
+ metadata, ok := obj.(*metav1.PartialObjectMetadata)
+ if !ok {
+ return fmt.Errorf("metadata client did not understand object: %T", obj)
+ }
+
+ gvk := metadata.GroupVersionKind()
+ resInt, err := mc.getResourceInterface(gvk, metadata.Namespace)
+ if err != nil {
+ return err
+ }
+
+ data, err := patch.Data(obj)
+ if err != nil {
+ return err
+ }
+
+ patchOpts := &PatchOptions{}
+ res, err := resInt.Patch(ctx, metadata.Name, patch.Type(), data, *patchOpts.AsPatchOptions(), "status")
+ if err != nil {
+ return err
+ }
+ *metadata = *res
+ metadata.SetGroupVersionKind(gvk) // restore the GVK, which isn't set on metadata
+ return nil
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/namespaced_client.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/namespaced_client.go
new file mode 100644
index 000000000..557598727
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/namespaced_client.go
@@ -0,0 +1,213 @@
+/*
+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 client
+
+import (
+ "context"
+ "fmt"
+
+ "k8s.io/apimachinery/pkg/api/meta"
+ "k8s.io/apimachinery/pkg/runtime"
+ "sigs.k8s.io/controller-runtime/pkg/internal/objectutil"
+)
+
+// NewNamespacedClient wraps an existing client enforcing the namespace value.
+// All functions using this client will have the same namespace declared here.
+func NewNamespacedClient(c Client, ns string) Client {
+ return &namespacedClient{
+ client: c,
+ namespace: ns,
+ }
+}
+
+var _ Client = &namespacedClient{}
+
+// namespacedClient is a Client that wraps another Client in order to enforce the specified namespace value.
+type namespacedClient struct {
+ namespace string
+ client Client
+}
+
+// Scheme returns the scheme this client is using.
+func (n *namespacedClient) Scheme() *runtime.Scheme {
+ return n.client.Scheme()
+}
+
+// RESTMapper returns the scheme this client is using.
+func (n *namespacedClient) RESTMapper() meta.RESTMapper {
+ return n.client.RESTMapper()
+}
+
+// Create implements clinet.Client.
+func (n *namespacedClient) Create(ctx context.Context, obj Object, opts ...CreateOption) error {
+ isNamespaceScoped, err := objectutil.IsAPINamespaced(obj, n.Scheme(), n.RESTMapper())
+ if err != nil {
+ return fmt.Errorf("error finding the scope of the object: %v", err)
+ }
+
+ objectNamespace := obj.GetNamespace()
+ if objectNamespace != n.namespace && objectNamespace != "" {
+ return fmt.Errorf("namespace %s of the object %s does not match the namespace %s on the client", objectNamespace, obj.GetName(), n.namespace)
+ }
+
+ if isNamespaceScoped && objectNamespace == "" {
+ obj.SetNamespace(n.namespace)
+ }
+ return n.client.Create(ctx, obj, opts...)
+}
+
+// Update implements client.Client.
+func (n *namespacedClient) Update(ctx context.Context, obj Object, opts ...UpdateOption) error {
+ isNamespaceScoped, err := objectutil.IsAPINamespaced(obj, n.Scheme(), n.RESTMapper())
+ if err != nil {
+ return fmt.Errorf("error finding the scope of the object: %v", err)
+ }
+
+ objectNamespace := obj.GetNamespace()
+ if objectNamespace != n.namespace && objectNamespace != "" {
+ return fmt.Errorf("namespace %s of the object %s does not match the namespace %s on the client", objectNamespace, obj.GetName(), n.namespace)
+ }
+
+ if isNamespaceScoped && objectNamespace == "" {
+ obj.SetNamespace(n.namespace)
+ }
+ return n.client.Update(ctx, obj, opts...)
+}
+
+// Delete implements client.Client.
+func (n *namespacedClient) Delete(ctx context.Context, obj Object, opts ...DeleteOption) error {
+ isNamespaceScoped, err := objectutil.IsAPINamespaced(obj, n.Scheme(), n.RESTMapper())
+ if err != nil {
+ return fmt.Errorf("error finding the scope of the object: %v", err)
+ }
+
+ objectNamespace := obj.GetNamespace()
+ if objectNamespace != n.namespace && objectNamespace != "" {
+ return fmt.Errorf("namespace %s of the object %s does not match the namespace %s on the client", objectNamespace, obj.GetName(), n.namespace)
+ }
+
+ if isNamespaceScoped && objectNamespace == "" {
+ obj.SetNamespace(n.namespace)
+ }
+ return n.client.Delete(ctx, obj, opts...)
+}
+
+// DeleteAllOf implements client.Client.
+func (n *namespacedClient) DeleteAllOf(ctx context.Context, obj Object, opts ...DeleteAllOfOption) error {
+ isNamespaceScoped, err := objectutil.IsAPINamespaced(obj, n.Scheme(), n.RESTMapper())
+ if err != nil {
+ return fmt.Errorf("error finding the scope of the object: %v", err)
+ }
+
+ if isNamespaceScoped {
+ opts = append(opts, InNamespace(n.namespace))
+ }
+ return n.client.DeleteAllOf(ctx, obj, opts...)
+}
+
+// Patch implements client.Client.
+func (n *namespacedClient) Patch(ctx context.Context, obj Object, patch Patch, opts ...PatchOption) error {
+ isNamespaceScoped, err := objectutil.IsAPINamespaced(obj, n.Scheme(), n.RESTMapper())
+ if err != nil {
+ return fmt.Errorf("error finding the scope of the object: %v", err)
+ }
+
+ objectNamespace := obj.GetNamespace()
+ if objectNamespace != n.namespace && objectNamespace != "" {
+ return fmt.Errorf("namespace %s of the object %s does not match the namespace %s on the client", objectNamespace, obj.GetName(), n.namespace)
+ }
+
+ if isNamespaceScoped && objectNamespace == "" {
+ obj.SetNamespace(n.namespace)
+ }
+ return n.client.Patch(ctx, obj, patch, opts...)
+}
+
+// Get implements client.Client.
+func (n *namespacedClient) Get(ctx context.Context, key ObjectKey, obj Object) error {
+ isNamespaceScoped, err := objectutil.IsAPINamespaced(obj, n.Scheme(), n.RESTMapper())
+ if err != nil {
+ return fmt.Errorf("error finding the scope of the object: %v", err)
+ }
+ if isNamespaceScoped {
+ if key.Namespace != "" && key.Namespace != n.namespace {
+ return fmt.Errorf("namespace %s provided for the object %s does not match the namesapce %s on the client", key.Namespace, obj.GetName(), n.namespace)
+ }
+ key.Namespace = n.namespace
+ }
+ return n.client.Get(ctx, key, obj)
+}
+
+// List implements client.Client.
+func (n *namespacedClient) List(ctx context.Context, obj ObjectList, opts ...ListOption) error {
+ if n.namespace != "" {
+ opts = append(opts, InNamespace(n.namespace))
+ }
+ return n.client.List(ctx, obj, opts...)
+}
+
+// Status implements client.StatusClient.
+func (n *namespacedClient) Status() StatusWriter {
+ return &namespacedClientStatusWriter{StatusClient: n.client.Status(), namespace: n.namespace, namespacedclient: n}
+}
+
+// ensure namespacedClientStatusWriter implements client.StatusWriter.
+var _ StatusWriter = &namespacedClientStatusWriter{}
+
+type namespacedClientStatusWriter struct {
+ StatusClient StatusWriter
+ namespace string
+ namespacedclient Client
+}
+
+// Update implements client.StatusWriter.
+func (nsw *namespacedClientStatusWriter) Update(ctx context.Context, obj Object, opts ...UpdateOption) error {
+ isNamespaceScoped, err := objectutil.IsAPINamespaced(obj, nsw.namespacedclient.Scheme(), nsw.namespacedclient.RESTMapper())
+
+ if err != nil {
+ return fmt.Errorf("error finding the scope of the object: %v", err)
+ }
+
+ objectNamespace := obj.GetNamespace()
+ if objectNamespace != nsw.namespace && objectNamespace != "" {
+ return fmt.Errorf("namespace %s of the object %s does not match the namespace %s on the client", objectNamespace, obj.GetName(), nsw.namespace)
+ }
+
+ if isNamespaceScoped && objectNamespace == "" {
+ obj.SetNamespace(nsw.namespace)
+ }
+ return nsw.StatusClient.Update(ctx, obj, opts...)
+}
+
+// Patch implements client.StatusWriter.
+func (nsw *namespacedClientStatusWriter) Patch(ctx context.Context, obj Object, patch Patch, opts ...PatchOption) error {
+ isNamespaceScoped, err := objectutil.IsAPINamespaced(obj, nsw.namespacedclient.Scheme(), nsw.namespacedclient.RESTMapper())
+
+ if err != nil {
+ return fmt.Errorf("error finding the scope of the object: %v", err)
+ }
+
+ objectNamespace := obj.GetNamespace()
+ if objectNamespace != nsw.namespace && objectNamespace != "" {
+ return fmt.Errorf("namespace %s of the object %s does not match the namespace %s on the client", objectNamespace, obj.GetName(), nsw.namespace)
+ }
+
+ if isNamespaceScoped && objectNamespace == "" {
+ obj.SetNamespace(nsw.namespace)
+ }
+ return nsw.StatusClient.Patch(ctx, obj, patch, opts...)
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/object.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/object.go
new file mode 100644
index 000000000..31e334d6c
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/object.go
@@ -0,0 +1,77 @@
+/*
+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 client
+
+import (
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/runtime"
+)
+
+// Object is a Kubernetes object, allows functions to work indistinctly with
+// any resource that implements both Object interfaces.
+//
+// Semantically, these are objects which are both serializable (runtime.Object)
+// and identifiable (metav1.Object) -- think any object which you could write
+// as YAML or JSON, and then `kubectl create`.
+//
+// Code-wise, this means that any object which embeds both ObjectMeta (which
+// provides metav1.Object) and TypeMeta (which provides half of runtime.Object)
+// and has a `DeepCopyObject` implementation (the other half of runtime.Object)
+// will implement this by default.
+//
+// For example, nearly all the built-in types are Objects, as well as all
+// KubeBuilder-generated CRDs (unless you do something real funky to them).
+//
+// By and large, most things that implement runtime.Object also implement
+// Object -- it's very rare to have *just* a runtime.Object implementation (the
+// cases tend to be funky built-in types like Webhook payloads that don't have
+// a `metadata` field).
+//
+// Notice that XYZList types are distinct: they implement ObjectList instead.
+type Object interface {
+ metav1.Object
+ runtime.Object
+}
+
+// ObjectList is a Kubernetes object list, allows functions to work
+// indistinctly with any resource that implements both runtime.Object and
+// metav1.ListInterface interfaces.
+//
+// Semantically, this is any object which may be serialized (ObjectMeta), and
+// is a kubernetes list wrapper (has items, pagination fields, etc) -- think
+// the wrapper used in a response from a `kubectl list --output yaml` call.
+//
+// Code-wise, this means that any object which embedds both ListMeta (which
+// provides metav1.ListInterface) and TypeMeta (which provides half of
+// runtime.Object) and has a `DeepCopyObject` implementation (the other half of
+// runtime.Object) will implement this by default.
+//
+// For example, nearly all the built-in XYZList types are ObjectLists, as well
+// as the XYZList types for all KubeBuilder-generated CRDs (unless you do
+// something real funky to them).
+//
+// By and large, most things that are XYZList and implement runtime.Object also
+// implement ObjectList -- it's very rare to have *just* a runtime.Object
+// implementation (the cases tend to be funky built-in types like Webhook
+// payloads that don't have a `metadata` field).
+//
+// This is similar to Object, which is almost always implemented by the items
+// in the list themselves.
+type ObjectList interface {
+ metav1.ListInterface
+ runtime.Object
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/options.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/options.go
new file mode 100644
index 000000000..aa2299eac
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/options.go
@@ -0,0 +1,697 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package client
+
+import (
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/fields"
+ "k8s.io/apimachinery/pkg/labels"
+ "k8s.io/apimachinery/pkg/selection"
+)
+
+// {{{ "Functional" Option Interfaces
+
+// CreateOption is some configuration that modifies options for a create request.
+type CreateOption interface {
+ // ApplyToCreate applies this configuration to the given create options.
+ ApplyToCreate(*CreateOptions)
+}
+
+// DeleteOption is some configuration that modifies options for a delete request.
+type DeleteOption interface {
+ // ApplyToDelete applies this configuration to the given delete options.
+ ApplyToDelete(*DeleteOptions)
+}
+
+// ListOption is some configuration that modifies options for a list request.
+type ListOption interface {
+ // ApplyToList applies this configuration to the given list options.
+ ApplyToList(*ListOptions)
+}
+
+// UpdateOption is some configuration that modifies options for a update request.
+type UpdateOption interface {
+ // ApplyToUpdate applies this configuration to the given update options.
+ ApplyToUpdate(*UpdateOptions)
+}
+
+// PatchOption is some configuration that modifies options for a patch request.
+type PatchOption interface {
+ // ApplyToPatch applies this configuration to the given patch options.
+ ApplyToPatch(*PatchOptions)
+}
+
+// DeleteAllOfOption is some configuration that modifies options for a delete request.
+type DeleteAllOfOption interface {
+ // ApplyToDeleteAllOf applies this configuration to the given deletecollection options.
+ ApplyToDeleteAllOf(*DeleteAllOfOptions)
+}
+
+// }}}
+
+// {{{ Multi-Type Options
+
+// DryRunAll sets the "dry run" option to "all", executing all
+// validation, etc without persisting the change to storage.
+var DryRunAll = dryRunAll{}
+
+type dryRunAll struct{}
+
+// ApplyToCreate applies this configuration to the given create options.
+func (dryRunAll) ApplyToCreate(opts *CreateOptions) {
+ opts.DryRun = []string{metav1.DryRunAll}
+}
+
+// ApplyToUpdate applies this configuration to the given update options.
+func (dryRunAll) ApplyToUpdate(opts *UpdateOptions) {
+ opts.DryRun = []string{metav1.DryRunAll}
+}
+
+// ApplyToPatch applies this configuration to the given patch options.
+func (dryRunAll) ApplyToPatch(opts *PatchOptions) {
+ opts.DryRun = []string{metav1.DryRunAll}
+}
+
+// ApplyToPatch applies this configuration to the given delete options.
+func (dryRunAll) ApplyToDelete(opts *DeleteOptions) {
+ opts.DryRun = []string{metav1.DryRunAll}
+}
+func (dryRunAll) ApplyToDeleteAllOf(opts *DeleteAllOfOptions) {
+ opts.DryRun = []string{metav1.DryRunAll}
+}
+
+// FieldOwner set the field manager name for the given server-side apply patch.
+type FieldOwner string
+
+// ApplyToPatch applies this configuration to the given patch options.
+func (f FieldOwner) ApplyToPatch(opts *PatchOptions) {
+ opts.FieldManager = string(f)
+}
+
+// ApplyToCreate applies this configuration to the given create options.
+func (f FieldOwner) ApplyToCreate(opts *CreateOptions) {
+ opts.FieldManager = string(f)
+}
+
+// ApplyToUpdate applies this configuration to the given update options.
+func (f FieldOwner) ApplyToUpdate(opts *UpdateOptions) {
+ opts.FieldManager = string(f)
+}
+
+// }}}
+
+// {{{ Create Options
+
+// CreateOptions contains options for create requests. It's generally a subset
+// of metav1.CreateOptions.
+type CreateOptions struct {
+ // When present, indicates that modifications should not be
+ // persisted. An invalid or unrecognized dryRun directive will
+ // result in an error response and no further processing of the
+ // request. Valid values are:
+ // - All: all dry run stages will be processed
+ DryRun []string
+
+ // FieldManager is the name of the user or component submitting
+ // this request. It must be set with server-side apply.
+ FieldManager string
+
+ // Raw represents raw CreateOptions, as passed to the API server.
+ Raw *metav1.CreateOptions
+}
+
+// AsCreateOptions returns these options as a metav1.CreateOptions.
+// This may mutate the Raw field.
+func (o *CreateOptions) AsCreateOptions() *metav1.CreateOptions {
+ if o == nil {
+ return &metav1.CreateOptions{}
+ }
+ if o.Raw == nil {
+ o.Raw = &metav1.CreateOptions{}
+ }
+
+ o.Raw.DryRun = o.DryRun
+ o.Raw.FieldManager = o.FieldManager
+ return o.Raw
+}
+
+// ApplyOptions applies the given create options on these options,
+// and then returns itself (for convenient chaining).
+func (o *CreateOptions) ApplyOptions(opts []CreateOption) *CreateOptions {
+ for _, opt := range opts {
+ opt.ApplyToCreate(o)
+ }
+ return o
+}
+
+// ApplyToCreate implements CreateOption.
+func (o *CreateOptions) ApplyToCreate(co *CreateOptions) {
+ if o.DryRun != nil {
+ co.DryRun = o.DryRun
+ }
+ if o.FieldManager != "" {
+ co.FieldManager = o.FieldManager
+ }
+ if o.Raw != nil {
+ co.Raw = o.Raw
+ }
+}
+
+var _ CreateOption = &CreateOptions{}
+
+// }}}
+
+// {{{ Delete Options
+
+// DeleteOptions contains options for delete requests. It's generally a subset
+// of metav1.DeleteOptions.
+type DeleteOptions struct {
+ // GracePeriodSeconds is the duration in seconds before the object should be
+ // deleted. Value must be non-negative integer. The value zero indicates
+ // delete immediately. If this value is nil, the default grace period for the
+ // specified type will be used.
+ GracePeriodSeconds *int64
+
+ // Preconditions must be fulfilled before a deletion is carried out. If not
+ // possible, a 409 Conflict status will be returned.
+ Preconditions *metav1.Preconditions
+
+ // PropagationPolicy determined whether and how garbage collection will be
+ // performed. Either this field or OrphanDependents may be set, but not both.
+ // The default policy is decided by the existing finalizer set in the
+ // metadata.finalizers and the resource-specific default policy.
+ // Acceptable values are: 'Orphan' - orphan the dependents; 'Background' -
+ // allow the garbage collector to delete the dependents in the background;
+ // 'Foreground' - a cascading policy that deletes all dependents in the
+ // foreground.
+ PropagationPolicy *metav1.DeletionPropagation
+
+ // Raw represents raw DeleteOptions, as passed to the API server.
+ Raw *metav1.DeleteOptions
+
+ // When present, indicates that modifications should not be
+ // persisted. An invalid or unrecognized dryRun directive will
+ // result in an error response and no further processing of the
+ // request. Valid values are:
+ // - All: all dry run stages will be processed
+ DryRun []string
+}
+
+// AsDeleteOptions returns these options as a metav1.DeleteOptions.
+// This may mutate the Raw field.
+func (o *DeleteOptions) AsDeleteOptions() *metav1.DeleteOptions {
+ if o == nil {
+ return &metav1.DeleteOptions{}
+ }
+ if o.Raw == nil {
+ o.Raw = &metav1.DeleteOptions{}
+ }
+
+ o.Raw.GracePeriodSeconds = o.GracePeriodSeconds
+ o.Raw.Preconditions = o.Preconditions
+ o.Raw.PropagationPolicy = o.PropagationPolicy
+ o.Raw.DryRun = o.DryRun
+ return o.Raw
+}
+
+// ApplyOptions applies the given delete options on these options,
+// and then returns itself (for convenient chaining).
+func (o *DeleteOptions) ApplyOptions(opts []DeleteOption) *DeleteOptions {
+ for _, opt := range opts {
+ opt.ApplyToDelete(o)
+ }
+ return o
+}
+
+var _ DeleteOption = &DeleteOptions{}
+
+// ApplyToDelete implements DeleteOption.
+func (o *DeleteOptions) ApplyToDelete(do *DeleteOptions) {
+ if o.GracePeriodSeconds != nil {
+ do.GracePeriodSeconds = o.GracePeriodSeconds
+ }
+ if o.Preconditions != nil {
+ do.Preconditions = o.Preconditions
+ }
+ if o.PropagationPolicy != nil {
+ do.PropagationPolicy = o.PropagationPolicy
+ }
+ if o.Raw != nil {
+ do.Raw = o.Raw
+ }
+ if o.DryRun != nil {
+ do.DryRun = o.DryRun
+ }
+}
+
+// GracePeriodSeconds sets the grace period for the deletion
+// to the given number of seconds.
+type GracePeriodSeconds int64
+
+// ApplyToDelete applies this configuration to the given delete options.
+func (s GracePeriodSeconds) ApplyToDelete(opts *DeleteOptions) {
+ secs := int64(s)
+ opts.GracePeriodSeconds = &secs
+}
+
+// ApplyToDeleteAllOf applies this configuration to the given an List options.
+func (s GracePeriodSeconds) ApplyToDeleteAllOf(opts *DeleteAllOfOptions) {
+ s.ApplyToDelete(&opts.DeleteOptions)
+}
+
+// Preconditions must be fulfilled before an operation (update, delete, etc.) is carried out.
+type Preconditions metav1.Preconditions
+
+// ApplyToDelete applies this configuration to the given delete options.
+func (p Preconditions) ApplyToDelete(opts *DeleteOptions) {
+ preconds := metav1.Preconditions(p)
+ opts.Preconditions = &preconds
+}
+
+// ApplyToDeleteAllOf applies this configuration to the given an List options.
+func (p Preconditions) ApplyToDeleteAllOf(opts *DeleteAllOfOptions) {
+ p.ApplyToDelete(&opts.DeleteOptions)
+}
+
+// PropagationPolicy determined whether and how garbage collection will be
+// performed. Either this field or OrphanDependents may be set, but not both.
+// The default policy is decided by the existing finalizer set in the
+// metadata.finalizers and the resource-specific default policy.
+// Acceptable values are: 'Orphan' - orphan the dependents; 'Background' -
+// allow the garbage collector to delete the dependents in the background;
+// 'Foreground' - a cascading policy that deletes all dependents in the
+// foreground.
+type PropagationPolicy metav1.DeletionPropagation
+
+// ApplyToDelete applies the given delete options on these options.
+// It will propagate to the dependents of the object to let the garbage collector handle it.
+func (p PropagationPolicy) ApplyToDelete(opts *DeleteOptions) {
+ policy := metav1.DeletionPropagation(p)
+ opts.PropagationPolicy = &policy
+}
+
+// ApplyToDeleteAllOf applies this configuration to the given an List options.
+func (p PropagationPolicy) ApplyToDeleteAllOf(opts *DeleteAllOfOptions) {
+ p.ApplyToDelete(&opts.DeleteOptions)
+}
+
+// }}}
+
+// {{{ List Options
+
+// ListOptions contains options for limiting or filtering results.
+// It's generally a subset of metav1.ListOptions, with support for
+// pre-parsed selectors (since generally, selectors will be executed
+// against the cache).
+type ListOptions struct {
+ // LabelSelector filters results by label. Use SetLabelSelector to
+ // set from raw string form.
+ LabelSelector labels.Selector
+ // FieldSelector filters results by a particular field. In order
+ // to use this with cache-based implementations, restrict usage to
+ // a single field-value pair that's been added to the indexers.
+ FieldSelector fields.Selector
+
+ // Namespace represents the namespace to list for, or empty for
+ // non-namespaced objects, or to list across all namespaces.
+ Namespace string
+
+ // Limit specifies the maximum number of results to return from the server. The server may
+ // not support this field on all resource types, but if it does and more results remain it
+ // will set the continue field on the returned list object. This field is not supported if watch
+ // is true in the Raw ListOptions.
+ Limit int64
+ // Continue is a token returned by the server that lets a client retrieve chunks of results
+ // from the server by specifying limit. The server may reject requests for continuation tokens
+ // it does not recognize and will return a 410 error if the token can no longer be used because
+ // it has expired. This field is not supported if watch is true in the Raw ListOptions.
+ Continue string
+
+ // Raw represents raw ListOptions, as passed to the API server. Note
+ // that these may not be respected by all implementations of interface,
+ // and the LabelSelector, FieldSelector, Limit and Continue fields are ignored.
+ Raw *metav1.ListOptions
+}
+
+var _ ListOption = &ListOptions{}
+
+// ApplyToList implements ListOption for ListOptions.
+func (o *ListOptions) ApplyToList(lo *ListOptions) {
+ if o.LabelSelector != nil {
+ lo.LabelSelector = o.LabelSelector
+ }
+ if o.FieldSelector != nil {
+ lo.FieldSelector = o.FieldSelector
+ }
+ if o.Namespace != "" {
+ lo.Namespace = o.Namespace
+ }
+ if o.Raw != nil {
+ lo.Raw = o.Raw
+ }
+ if o.Limit > 0 {
+ lo.Limit = o.Limit
+ }
+ if o.Continue != "" {
+ lo.Continue = o.Continue
+ }
+}
+
+// AsListOptions returns these options as a flattened metav1.ListOptions.
+// This may mutate the Raw field.
+func (o *ListOptions) AsListOptions() *metav1.ListOptions {
+ if o == nil {
+ return &metav1.ListOptions{}
+ }
+ if o.Raw == nil {
+ o.Raw = &metav1.ListOptions{}
+ }
+ if o.LabelSelector != nil {
+ o.Raw.LabelSelector = o.LabelSelector.String()
+ }
+ if o.FieldSelector != nil {
+ o.Raw.FieldSelector = o.FieldSelector.String()
+ }
+ if !o.Raw.Watch {
+ o.Raw.Limit = o.Limit
+ o.Raw.Continue = o.Continue
+ }
+ return o.Raw
+}
+
+// ApplyOptions applies the given list options on these options,
+// and then returns itself (for convenient chaining).
+func (o *ListOptions) ApplyOptions(opts []ListOption) *ListOptions {
+ for _, opt := range opts {
+ opt.ApplyToList(o)
+ }
+ return o
+}
+
+// MatchingLabels filters the list/delete operation on the given set of labels.
+type MatchingLabels map[string]string
+
+// ApplyToList applies this configuration to the given list options.
+func (m MatchingLabels) ApplyToList(opts *ListOptions) {
+ // TODO(directxman12): can we avoid reserializing this over and over?
+ sel := labels.SelectorFromValidatedSet(map[string]string(m))
+ opts.LabelSelector = sel
+}
+
+// ApplyToDeleteAllOf applies this configuration to the given an List options.
+func (m MatchingLabels) ApplyToDeleteAllOf(opts *DeleteAllOfOptions) {
+ m.ApplyToList(&opts.ListOptions)
+}
+
+// HasLabels filters the list/delete operation checking if the set of labels exists
+// without checking their values.
+type HasLabels []string
+
+// ApplyToList applies this configuration to the given list options.
+func (m HasLabels) ApplyToList(opts *ListOptions) {
+ sel := labels.NewSelector()
+ for _, label := range m {
+ r, err := labels.NewRequirement(label, selection.Exists, nil)
+ if err == nil {
+ sel = sel.Add(*r)
+ }
+ }
+ opts.LabelSelector = sel
+}
+
+// ApplyToDeleteAllOf applies this configuration to the given an List options.
+func (m HasLabels) ApplyToDeleteAllOf(opts *DeleteAllOfOptions) {
+ m.ApplyToList(&opts.ListOptions)
+}
+
+// MatchingLabelsSelector filters the list/delete operation on the given label
+// selector (or index in the case of cached lists). A struct is used because
+// labels.Selector is an interface, which cannot be aliased.
+type MatchingLabelsSelector struct {
+ labels.Selector
+}
+
+// ApplyToList applies this configuration to the given list options.
+func (m MatchingLabelsSelector) ApplyToList(opts *ListOptions) {
+ opts.LabelSelector = m
+}
+
+// ApplyToDeleteAllOf applies this configuration to the given an List options.
+func (m MatchingLabelsSelector) ApplyToDeleteAllOf(opts *DeleteAllOfOptions) {
+ m.ApplyToList(&opts.ListOptions)
+}
+
+// MatchingFields filters the list/delete operation on the given field Set
+// (or index in the case of cached lists).
+type MatchingFields fields.Set
+
+// ApplyToList applies this configuration to the given list options.
+func (m MatchingFields) ApplyToList(opts *ListOptions) {
+ // TODO(directxman12): can we avoid re-serializing this?
+ sel := fields.Set(m).AsSelector()
+ opts.FieldSelector = sel
+}
+
+// ApplyToDeleteAllOf applies this configuration to the given an List options.
+func (m MatchingFields) ApplyToDeleteAllOf(opts *DeleteAllOfOptions) {
+ m.ApplyToList(&opts.ListOptions)
+}
+
+// MatchingFieldsSelector filters the list/delete operation on the given field
+// selector (or index in the case of cached lists). A struct is used because
+// fields.Selector is an interface, which cannot be aliased.
+type MatchingFieldsSelector struct {
+ fields.Selector
+}
+
+// ApplyToList applies this configuration to the given list options.
+func (m MatchingFieldsSelector) ApplyToList(opts *ListOptions) {
+ opts.FieldSelector = m
+}
+
+// ApplyToDeleteAllOf applies this configuration to the given an List options.
+func (m MatchingFieldsSelector) ApplyToDeleteAllOf(opts *DeleteAllOfOptions) {
+ m.ApplyToList(&opts.ListOptions)
+}
+
+// InNamespace restricts the list/delete operation to the given namespace.
+type InNamespace string
+
+// ApplyToList applies this configuration to the given list options.
+func (n InNamespace) ApplyToList(opts *ListOptions) {
+ opts.Namespace = string(n)
+}
+
+// ApplyToDeleteAllOf applies this configuration to the given an List options.
+func (n InNamespace) ApplyToDeleteAllOf(opts *DeleteAllOfOptions) {
+ n.ApplyToList(&opts.ListOptions)
+}
+
+// Limit specifies the maximum number of results to return from the server.
+// Limit does not implement DeleteAllOfOption interface because the server
+// does not support setting it for deletecollection operations.
+type Limit int64
+
+// ApplyToList applies this configuration to the given an list options.
+func (l Limit) ApplyToList(opts *ListOptions) {
+ opts.Limit = int64(l)
+}
+
+// Continue sets a continuation token to retrieve chunks of results when using limit.
+// Continue does not implement DeleteAllOfOption interface because the server
+// does not support setting it for deletecollection operations.
+type Continue string
+
+// ApplyToList applies this configuration to the given an List options.
+func (c Continue) ApplyToList(opts *ListOptions) {
+ opts.Continue = string(c)
+}
+
+// }}}
+
+// {{{ Update Options
+
+// UpdateOptions contains options for create requests. It's generally a subset
+// of metav1.UpdateOptions.
+type UpdateOptions struct {
+ // When present, indicates that modifications should not be
+ // persisted. An invalid or unrecognized dryRun directive will
+ // result in an error response and no further processing of the
+ // request. Valid values are:
+ // - All: all dry run stages will be processed
+ DryRun []string
+
+ // FieldManager is the name of the user or component submitting
+ // this request. It must be set with server-side apply.
+ FieldManager string
+
+ // Raw represents raw UpdateOptions, as passed to the API server.
+ Raw *metav1.UpdateOptions
+}
+
+// AsUpdateOptions returns these options as a metav1.UpdateOptions.
+// This may mutate the Raw field.
+func (o *UpdateOptions) AsUpdateOptions() *metav1.UpdateOptions {
+ if o == nil {
+ return &metav1.UpdateOptions{}
+ }
+ if o.Raw == nil {
+ o.Raw = &metav1.UpdateOptions{}
+ }
+
+ o.Raw.DryRun = o.DryRun
+ o.Raw.FieldManager = o.FieldManager
+ return o.Raw
+}
+
+// ApplyOptions applies the given update options on these options,
+// and then returns itself (for convenient chaining).
+func (o *UpdateOptions) ApplyOptions(opts []UpdateOption) *UpdateOptions {
+ for _, opt := range opts {
+ opt.ApplyToUpdate(o)
+ }
+ return o
+}
+
+var _ UpdateOption = &UpdateOptions{}
+
+// ApplyToUpdate implements UpdateOption.
+func (o *UpdateOptions) ApplyToUpdate(uo *UpdateOptions) {
+ if o.DryRun != nil {
+ uo.DryRun = o.DryRun
+ }
+ if o.FieldManager != "" {
+ uo.FieldManager = o.FieldManager
+ }
+ if o.Raw != nil {
+ uo.Raw = o.Raw
+ }
+}
+
+// }}}
+
+// {{{ Patch Options
+
+// PatchOptions contains options for patch requests.
+type PatchOptions struct {
+ // When present, indicates that modifications should not be
+ // persisted. An invalid or unrecognized dryRun directive will
+ // result in an error response and no further processing of the
+ // request. Valid values are:
+ // - All: all dry run stages will be processed
+ DryRun []string
+
+ // Force is going to "force" Apply requests. It means user will
+ // re-acquire conflicting fields owned by other people. Force
+ // flag must be unset for non-apply patch requests.
+ // +optional
+ Force *bool
+
+ // FieldManager is the name of the user or component submitting
+ // this request. It must be set with server-side apply.
+ FieldManager string
+
+ // Raw represents raw PatchOptions, as passed to the API server.
+ Raw *metav1.PatchOptions
+}
+
+// ApplyOptions applies the given patch options on these options,
+// and then returns itself (for convenient chaining).
+func (o *PatchOptions) ApplyOptions(opts []PatchOption) *PatchOptions {
+ for _, opt := range opts {
+ opt.ApplyToPatch(o)
+ }
+ return o
+}
+
+// AsPatchOptions returns these options as a metav1.PatchOptions.
+// This may mutate the Raw field.
+func (o *PatchOptions) AsPatchOptions() *metav1.PatchOptions {
+ if o == nil {
+ return &metav1.PatchOptions{}
+ }
+ if o.Raw == nil {
+ o.Raw = &metav1.PatchOptions{}
+ }
+
+ o.Raw.DryRun = o.DryRun
+ o.Raw.Force = o.Force
+ o.Raw.FieldManager = o.FieldManager
+ return o.Raw
+}
+
+var _ PatchOption = &PatchOptions{}
+
+// ApplyToPatch implements PatchOptions.
+func (o *PatchOptions) ApplyToPatch(po *PatchOptions) {
+ if o.DryRun != nil {
+ po.DryRun = o.DryRun
+ }
+ if o.Force != nil {
+ po.Force = o.Force
+ }
+ if o.FieldManager != "" {
+ po.FieldManager = o.FieldManager
+ }
+ if o.Raw != nil {
+ po.Raw = o.Raw
+ }
+}
+
+// ForceOwnership indicates that in case of conflicts with server-side apply,
+// the client should acquire ownership of the conflicting field. Most
+// controllers should use this.
+var ForceOwnership = forceOwnership{}
+
+type forceOwnership struct{}
+
+func (forceOwnership) ApplyToPatch(opts *PatchOptions) {
+ definitelyTrue := true
+ opts.Force = &definitelyTrue
+}
+
+// }}}
+
+// {{{ DeleteAllOf Options
+
+// these are all just delete options and list options
+
+// DeleteAllOfOptions contains options for deletecollection (deleteallof) requests.
+// It's just list and delete options smooshed together.
+type DeleteAllOfOptions struct {
+ ListOptions
+ DeleteOptions
+}
+
+// ApplyOptions applies the given deleteallof options on these options,
+// and then returns itself (for convenient chaining).
+func (o *DeleteAllOfOptions) ApplyOptions(opts []DeleteAllOfOption) *DeleteAllOfOptions {
+ for _, opt := range opts {
+ opt.ApplyToDeleteAllOf(o)
+ }
+ return o
+}
+
+var _ DeleteAllOfOption = &DeleteAllOfOptions{}
+
+// ApplyToDeleteAllOf implements DeleteAllOfOption.
+func (o *DeleteAllOfOptions) ApplyToDeleteAllOf(do *DeleteAllOfOptions) {
+ o.ApplyToList(&do.ListOptions)
+ o.ApplyToDelete(&do.DeleteOptions)
+}
+
+// }}}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/patch.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/patch.go
new file mode 100644
index 000000000..10984c534
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/patch.go
@@ -0,0 +1,213 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package client
+
+import (
+ "fmt"
+
+ jsonpatch "github.com/evanphx/json-patch"
+ "k8s.io/apimachinery/pkg/types"
+ "k8s.io/apimachinery/pkg/util/json"
+ "k8s.io/apimachinery/pkg/util/strategicpatch"
+)
+
+var (
+ // Apply uses server-side apply to patch the given object.
+ Apply Patch = applyPatch{}
+
+ // Merge uses the raw object as a merge patch, without modifications.
+ // Use MergeFrom if you wish to compute a diff instead.
+ Merge Patch = mergePatch{}
+)
+
+type patch struct {
+ patchType types.PatchType
+ data []byte
+}
+
+// Type implements Patch.
+func (s *patch) Type() types.PatchType {
+ return s.patchType
+}
+
+// Data implements Patch.
+func (s *patch) Data(obj Object) ([]byte, error) {
+ return s.data, nil
+}
+
+// RawPatch constructs a new Patch with the given PatchType and data.
+func RawPatch(patchType types.PatchType, data []byte) Patch {
+ return &patch{patchType, data}
+}
+
+// MergeFromWithOptimisticLock can be used if clients want to make sure a patch
+// is being applied to the latest resource version of an object.
+//
+// The behavior is similar to what an Update would do, without the need to send the
+// whole object. Usually this method is useful if you might have multiple clients
+// acting on the same object and the same API version, but with different versions of the Go structs.
+//
+// For example, an "older" copy of a Widget that has fields A and B, and a "newer" copy with A, B, and C.
+// Sending an update using the older struct definition results in C being dropped, whereas using a patch does not.
+type MergeFromWithOptimisticLock struct{}
+
+// ApplyToMergeFrom applies this configuration to the given patch options.
+func (m MergeFromWithOptimisticLock) ApplyToMergeFrom(in *MergeFromOptions) {
+ in.OptimisticLock = true
+}
+
+// MergeFromOption is some configuration that modifies options for a merge-from patch data.
+type MergeFromOption interface {
+ // ApplyToMergeFrom applies this configuration to the given patch options.
+ ApplyToMergeFrom(*MergeFromOptions)
+}
+
+// MergeFromOptions contains options to generate a merge-from patch data.
+type MergeFromOptions struct {
+ // OptimisticLock, when true, includes `metadata.resourceVersion` into the final
+ // patch data. If the `resourceVersion` field doesn't match what's stored,
+ // the operation results in a conflict and clients will need to try again.
+ OptimisticLock bool
+}
+
+type mergeFromPatch struct {
+ patchType types.PatchType
+ createPatch func(originalJSON, modifiedJSON []byte, dataStruct interface{}) ([]byte, error)
+ from Object
+ opts MergeFromOptions
+}
+
+// Type implements Patch.
+func (s *mergeFromPatch) Type() types.PatchType {
+ return s.patchType
+}
+
+// Data implements Patch.
+func (s *mergeFromPatch) Data(obj Object) ([]byte, error) {
+ original := s.from
+ modified := obj
+
+ if s.opts.OptimisticLock {
+ version := original.GetResourceVersion()
+ if len(version) == 0 {
+ return nil, fmt.Errorf("cannot use OptimisticLock, object %q does not have any resource version we can use", original)
+ }
+
+ original = original.DeepCopyObject().(Object)
+ original.SetResourceVersion("")
+
+ modified = modified.DeepCopyObject().(Object)
+ modified.SetResourceVersion(version)
+ }
+
+ originalJSON, err := json.Marshal(original)
+ if err != nil {
+ return nil, err
+ }
+
+ modifiedJSON, err := json.Marshal(modified)
+ if err != nil {
+ return nil, err
+ }
+
+ data, err := s.createPatch(originalJSON, modifiedJSON, obj)
+ if err != nil {
+ return nil, err
+ }
+
+ return data, nil
+}
+
+func createMergePatch(originalJSON, modifiedJSON []byte, _ interface{}) ([]byte, error) {
+ return jsonpatch.CreateMergePatch(originalJSON, modifiedJSON)
+}
+
+func createStrategicMergePatch(originalJSON, modifiedJSON []byte, dataStruct interface{}) ([]byte, error) {
+ return strategicpatch.CreateTwoWayMergePatch(originalJSON, modifiedJSON, dataStruct)
+}
+
+// MergeFrom creates a Patch that patches using the merge-patch strategy with the given object as base.
+// The difference between MergeFrom and StrategicMergeFrom lays in the handling of modified list fields.
+// When using MergeFrom, existing lists will be completely replaced by new lists.
+// When using StrategicMergeFrom, the list field's `patchStrategy` is respected if specified in the API type,
+// e.g. the existing list is not replaced completely but rather merged with the new one using the list's `patchMergeKey`.
+// See https://kubernetes.io/docs/tasks/manage-kubernetes-objects/update-api-object-kubectl-patch/ for more details on
+// the difference between merge-patch and strategic-merge-patch.
+func MergeFrom(obj Object) Patch {
+ return &mergeFromPatch{patchType: types.MergePatchType, createPatch: createMergePatch, from: obj}
+}
+
+// MergeFromWithOptions creates a Patch that patches using the merge-patch strategy with the given object as base.
+// See MergeFrom for more details.
+func MergeFromWithOptions(obj Object, opts ...MergeFromOption) Patch {
+ options := &MergeFromOptions{}
+ for _, opt := range opts {
+ opt.ApplyToMergeFrom(options)
+ }
+ return &mergeFromPatch{patchType: types.MergePatchType, createPatch: createMergePatch, from: obj, opts: *options}
+}
+
+// StrategicMergeFrom creates a Patch that patches using the strategic-merge-patch strategy with the given object as base.
+// The difference between MergeFrom and StrategicMergeFrom lays in the handling of modified list fields.
+// When using MergeFrom, existing lists will be completely replaced by new lists.
+// When using StrategicMergeFrom, the list field's `patchStrategy` is respected if specified in the API type,
+// e.g. the existing list is not replaced completely but rather merged with the new one using the list's `patchMergeKey`.
+// See https://kubernetes.io/docs/tasks/manage-kubernetes-objects/update-api-object-kubectl-patch/ for more details on
+// the difference between merge-patch and strategic-merge-patch.
+// Please note, that CRDs don't support strategic-merge-patch, see
+// https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/#advanced-features-and-flexibility
+func StrategicMergeFrom(obj Object, opts ...MergeFromOption) Patch {
+ options := &MergeFromOptions{}
+ for _, opt := range opts {
+ opt.ApplyToMergeFrom(options)
+ }
+ return &mergeFromPatch{patchType: types.StrategicMergePatchType, createPatch: createStrategicMergePatch, from: obj, opts: *options}
+}
+
+// mergePatch uses a raw merge strategy to patch the object.
+type mergePatch struct{}
+
+// Type implements Patch.
+func (p mergePatch) Type() types.PatchType {
+ return types.MergePatchType
+}
+
+// Data implements Patch.
+func (p mergePatch) Data(obj Object) ([]byte, error) {
+ // NB(directxman12): we might technically want to be using an actual encoder
+ // here (in case some more performant encoder is introduced) but this is
+ // correct and sufficient for our uses (it's what the JSON serializer in
+ // client-go does, more-or-less).
+ return json.Marshal(obj)
+}
+
+// applyPatch uses server-side apply to patch the object.
+type applyPatch struct{}
+
+// Type implements Patch.
+func (p applyPatch) Type() types.PatchType {
+ return types.ApplyPatchType
+}
+
+// Data implements Patch.
+func (p applyPatch) Data(obj Object) ([]byte, error) {
+ // NB(directxman12): we might technically want to be using an actual encoder
+ // here (in case some more performant encoder is introduced) but this is
+ // correct and sufficient for our uses (it's what the JSON serializer in
+ // client-go does, more-or-less).
+ return json.Marshal(obj)
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/split.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/split.go
new file mode 100644
index 000000000..bf4b861f3
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/split.go
@@ -0,0 +1,141 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package client
+
+import (
+ "context"
+ "strings"
+
+ "k8s.io/apimachinery/pkg/api/meta"
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+
+ "sigs.k8s.io/controller-runtime/pkg/client/apiutil"
+)
+
+// NewDelegatingClientInput encapsulates the input parameters to create a new delegating client.
+type NewDelegatingClientInput struct {
+ CacheReader Reader
+ Client Client
+ UncachedObjects []Object
+ CacheUnstructured bool
+}
+
+// NewDelegatingClient creates a new delegating client.
+//
+// A delegating client forms a Client by composing separate reader, writer and
+// statusclient interfaces. This way, you can have an Client that reads from a
+// cache and writes to the API server.
+func NewDelegatingClient(in NewDelegatingClientInput) (Client, error) {
+ uncachedGVKs := map[schema.GroupVersionKind]struct{}{}
+ for _, obj := range in.UncachedObjects {
+ gvk, err := apiutil.GVKForObject(obj, in.Client.Scheme())
+ if err != nil {
+ return nil, err
+ }
+ uncachedGVKs[gvk] = struct{}{}
+ }
+
+ return &delegatingClient{
+ scheme: in.Client.Scheme(),
+ mapper: in.Client.RESTMapper(),
+ Reader: &delegatingReader{
+ CacheReader: in.CacheReader,
+ ClientReader: in.Client,
+ scheme: in.Client.Scheme(),
+ uncachedGVKs: uncachedGVKs,
+ cacheUnstructured: in.CacheUnstructured,
+ },
+ Writer: in.Client,
+ StatusClient: in.Client,
+ }, nil
+}
+
+type delegatingClient struct {
+ Reader
+ Writer
+ StatusClient
+
+ scheme *runtime.Scheme
+ mapper meta.RESTMapper
+}
+
+// Scheme returns the scheme this client is using.
+func (d *delegatingClient) Scheme() *runtime.Scheme {
+ return d.scheme
+}
+
+// RESTMapper returns the rest mapper this client is using.
+func (d *delegatingClient) RESTMapper() meta.RESTMapper {
+ return d.mapper
+}
+
+// delegatingReader forms a Reader that will cause Get and List requests for
+// unstructured types to use the ClientReader while requests for any other type
+// of object with use the CacheReader. This avoids accidentally caching the
+// entire cluster in the common case of loading arbitrary unstructured objects
+// (e.g. from OwnerReferences).
+type delegatingReader struct {
+ CacheReader Reader
+ ClientReader Reader
+
+ uncachedGVKs map[schema.GroupVersionKind]struct{}
+ scheme *runtime.Scheme
+ cacheUnstructured bool
+}
+
+func (d *delegatingReader) shouldBypassCache(obj runtime.Object) (bool, error) {
+ gvk, err := apiutil.GVKForObject(obj, d.scheme)
+ if err != nil {
+ return false, err
+ }
+ // TODO: this is producing unsafe guesses that don't actually work,
+ // but it matches ~99% of the cases out there.
+ if meta.IsListType(obj) {
+ gvk.Kind = strings.TrimSuffix(gvk.Kind, "List")
+ }
+ if _, isUncached := d.uncachedGVKs[gvk]; isUncached {
+ return true, nil
+ }
+ if !d.cacheUnstructured {
+ _, isUnstructured := obj.(*unstructured.Unstructured)
+ _, isUnstructuredList := obj.(*unstructured.UnstructuredList)
+ return isUnstructured || isUnstructuredList, nil
+ }
+ return false, nil
+}
+
+// Get retrieves an obj for a given object key from the Kubernetes Cluster.
+func (d *delegatingReader) Get(ctx context.Context, key ObjectKey, obj Object) error {
+ if isUncached, err := d.shouldBypassCache(obj); err != nil {
+ return err
+ } else if isUncached {
+ return d.ClientReader.Get(ctx, key, obj)
+ }
+ return d.CacheReader.Get(ctx, key, obj)
+}
+
+// List retrieves list of objects for a given namespace and list options.
+func (d *delegatingReader) List(ctx context.Context, list ObjectList, opts ...ListOption) error {
+ if isUncached, err := d.shouldBypassCache(list); err != nil {
+ return err
+ } else if isUncached {
+ return d.ClientReader.List(ctx, list, opts...)
+ }
+ return d.CacheReader.List(ctx, list, opts...)
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/typed_client.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/typed_client.go
new file mode 100644
index 000000000..dde7b21f2
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/typed_client.go
@@ -0,0 +1,205 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package client
+
+import (
+ "context"
+
+ "k8s.io/apimachinery/pkg/runtime"
+)
+
+var _ Reader = &typedClient{}
+var _ Writer = &typedClient{}
+var _ StatusWriter = &typedClient{}
+
+// client is a client.Client that reads and writes directly from/to an API server. It lazily initializes
+// new clients at the time they are used, and caches the client.
+type typedClient struct {
+ cache *clientCache
+ paramCodec runtime.ParameterCodec
+}
+
+// Create implements client.Client.
+func (c *typedClient) Create(ctx context.Context, obj Object, opts ...CreateOption) error {
+ o, err := c.cache.getObjMeta(obj)
+ if err != nil {
+ return err
+ }
+
+ createOpts := &CreateOptions{}
+ createOpts.ApplyOptions(opts)
+ return o.Post().
+ NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
+ Resource(o.resource()).
+ Body(obj).
+ VersionedParams(createOpts.AsCreateOptions(), c.paramCodec).
+ Do(ctx).
+ Into(obj)
+}
+
+// Update implements client.Client.
+func (c *typedClient) Update(ctx context.Context, obj Object, opts ...UpdateOption) error {
+ o, err := c.cache.getObjMeta(obj)
+ if err != nil {
+ return err
+ }
+
+ updateOpts := &UpdateOptions{}
+ updateOpts.ApplyOptions(opts)
+ return o.Put().
+ NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
+ Resource(o.resource()).
+ Name(o.GetName()).
+ Body(obj).
+ VersionedParams(updateOpts.AsUpdateOptions(), c.paramCodec).
+ Do(ctx).
+ Into(obj)
+}
+
+// Delete implements client.Client.
+func (c *typedClient) Delete(ctx context.Context, obj Object, opts ...DeleteOption) error {
+ o, err := c.cache.getObjMeta(obj)
+ if err != nil {
+ return err
+ }
+
+ deleteOpts := DeleteOptions{}
+ deleteOpts.ApplyOptions(opts)
+
+ return o.Delete().
+ NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
+ Resource(o.resource()).
+ Name(o.GetName()).
+ Body(deleteOpts.AsDeleteOptions()).
+ Do(ctx).
+ Error()
+}
+
+// DeleteAllOf implements client.Client.
+func (c *typedClient) DeleteAllOf(ctx context.Context, obj Object, opts ...DeleteAllOfOption) error {
+ o, err := c.cache.getObjMeta(obj)
+ if err != nil {
+ return err
+ }
+
+ deleteAllOfOpts := DeleteAllOfOptions{}
+ deleteAllOfOpts.ApplyOptions(opts)
+
+ return o.Delete().
+ NamespaceIfScoped(deleteAllOfOpts.ListOptions.Namespace, o.isNamespaced()).
+ Resource(o.resource()).
+ VersionedParams(deleteAllOfOpts.AsListOptions(), c.paramCodec).
+ Body(deleteAllOfOpts.AsDeleteOptions()).
+ Do(ctx).
+ Error()
+}
+
+// Patch implements client.Client.
+func (c *typedClient) Patch(ctx context.Context, obj Object, patch Patch, opts ...PatchOption) error {
+ o, err := c.cache.getObjMeta(obj)
+ if err != nil {
+ return err
+ }
+
+ data, err := patch.Data(obj)
+ if err != nil {
+ return err
+ }
+
+ patchOpts := &PatchOptions{}
+ return o.Patch(patch.Type()).
+ NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
+ Resource(o.resource()).
+ Name(o.GetName()).
+ VersionedParams(patchOpts.ApplyOptions(opts).AsPatchOptions(), c.paramCodec).
+ Body(data).
+ Do(ctx).
+ Into(obj)
+}
+
+// Get implements client.Client.
+func (c *typedClient) Get(ctx context.Context, key ObjectKey, obj Object) error {
+ r, err := c.cache.getResource(obj)
+ if err != nil {
+ return err
+ }
+ return r.Get().
+ NamespaceIfScoped(key.Namespace, r.isNamespaced()).
+ Resource(r.resource()).
+ Name(key.Name).Do(ctx).Into(obj)
+}
+
+// List implements client.Client.
+func (c *typedClient) List(ctx context.Context, obj ObjectList, opts ...ListOption) error {
+ r, err := c.cache.getResource(obj)
+ if err != nil {
+ return err
+ }
+ listOpts := ListOptions{}
+ listOpts.ApplyOptions(opts)
+ return r.Get().
+ NamespaceIfScoped(listOpts.Namespace, r.isNamespaced()).
+ Resource(r.resource()).
+ VersionedParams(listOpts.AsListOptions(), c.paramCodec).
+ Do(ctx).
+ Into(obj)
+}
+
+// UpdateStatus used by StatusWriter to write status.
+func (c *typedClient) UpdateStatus(ctx context.Context, obj Object, opts ...UpdateOption) error {
+ o, err := c.cache.getObjMeta(obj)
+ if err != nil {
+ return err
+ }
+ // TODO(droot): examine the returned error and check if it error needs to be
+ // wrapped to improve the UX ?
+ // It will be nice to receive an error saying the object doesn't implement
+ // status subresource and check CRD definition
+ return o.Put().
+ NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
+ Resource(o.resource()).
+ Name(o.GetName()).
+ SubResource("status").
+ Body(obj).
+ VersionedParams((&UpdateOptions{}).ApplyOptions(opts).AsUpdateOptions(), c.paramCodec).
+ Do(ctx).
+ Into(obj)
+}
+
+// PatchStatus used by StatusWriter to write status.
+func (c *typedClient) PatchStatus(ctx context.Context, obj Object, patch Patch, opts ...PatchOption) error {
+ o, err := c.cache.getObjMeta(obj)
+ if err != nil {
+ return err
+ }
+
+ data, err := patch.Data(obj)
+ if err != nil {
+ return err
+ }
+
+ patchOpts := &PatchOptions{}
+ return o.Patch(patch.Type()).
+ NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
+ Resource(o.resource()).
+ Name(o.GetName()).
+ SubResource("status").
+ Body(data).
+ VersionedParams(patchOpts.ApplyOptions(opts).AsPatchOptions(), c.paramCodec).
+ Do(ctx).
+ Into(obj)
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/unstructured_client.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/unstructured_client.go
new file mode 100644
index 000000000..dcf15be27
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/unstructured_client.go
@@ -0,0 +1,277 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package client
+
+import (
+ "context"
+ "fmt"
+ "strings"
+
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+ "k8s.io/apimachinery/pkg/runtime"
+)
+
+var _ Reader = &unstructuredClient{}
+var _ Writer = &unstructuredClient{}
+var _ StatusWriter = &unstructuredClient{}
+
+// client is a client.Client that reads and writes directly from/to an API server. It lazily initializes
+// new clients at the time they are used, and caches the client.
+type unstructuredClient struct {
+ cache *clientCache
+ paramCodec runtime.ParameterCodec
+}
+
+// Create implements client.Client.
+func (uc *unstructuredClient) Create(ctx context.Context, obj Object, opts ...CreateOption) error {
+ u, ok := obj.(*unstructured.Unstructured)
+ if !ok {
+ return fmt.Errorf("unstructured client did not understand object: %T", obj)
+ }
+
+ gvk := u.GroupVersionKind()
+
+ o, err := uc.cache.getObjMeta(obj)
+ if err != nil {
+ return err
+ }
+
+ createOpts := &CreateOptions{}
+ createOpts.ApplyOptions(opts)
+ result := o.Post().
+ NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
+ Resource(o.resource()).
+ Body(obj).
+ VersionedParams(createOpts.AsCreateOptions(), uc.paramCodec).
+ Do(ctx).
+ Into(obj)
+
+ u.SetGroupVersionKind(gvk)
+ return result
+}
+
+// Update implements client.Client.
+func (uc *unstructuredClient) Update(ctx context.Context, obj Object, opts ...UpdateOption) error {
+ u, ok := obj.(*unstructured.Unstructured)
+ if !ok {
+ return fmt.Errorf("unstructured client did not understand object: %T", obj)
+ }
+
+ gvk := u.GroupVersionKind()
+
+ o, err := uc.cache.getObjMeta(obj)
+ if err != nil {
+ return err
+ }
+
+ updateOpts := UpdateOptions{}
+ updateOpts.ApplyOptions(opts)
+ result := o.Put().
+ NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
+ Resource(o.resource()).
+ Name(o.GetName()).
+ Body(obj).
+ VersionedParams(updateOpts.AsUpdateOptions(), uc.paramCodec).
+ Do(ctx).
+ Into(obj)
+
+ u.SetGroupVersionKind(gvk)
+ return result
+}
+
+// Delete implements client.Client.
+func (uc *unstructuredClient) Delete(ctx context.Context, obj Object, opts ...DeleteOption) error {
+ _, ok := obj.(*unstructured.Unstructured)
+ if !ok {
+ return fmt.Errorf("unstructured client did not understand object: %T", obj)
+ }
+
+ o, err := uc.cache.getObjMeta(obj)
+ if err != nil {
+ return err
+ }
+
+ deleteOpts := DeleteOptions{}
+ deleteOpts.ApplyOptions(opts)
+ return o.Delete().
+ NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
+ Resource(o.resource()).
+ Name(o.GetName()).
+ Body(deleteOpts.AsDeleteOptions()).
+ Do(ctx).
+ Error()
+}
+
+// DeleteAllOf implements client.Client.
+func (uc *unstructuredClient) DeleteAllOf(ctx context.Context, obj Object, opts ...DeleteAllOfOption) error {
+ _, ok := obj.(*unstructured.Unstructured)
+ if !ok {
+ return fmt.Errorf("unstructured client did not understand object: %T", obj)
+ }
+
+ o, err := uc.cache.getObjMeta(obj)
+ if err != nil {
+ return err
+ }
+
+ deleteAllOfOpts := DeleteAllOfOptions{}
+ deleteAllOfOpts.ApplyOptions(opts)
+ return o.Delete().
+ NamespaceIfScoped(deleteAllOfOpts.ListOptions.Namespace, o.isNamespaced()).
+ Resource(o.resource()).
+ VersionedParams(deleteAllOfOpts.AsListOptions(), uc.paramCodec).
+ Body(deleteAllOfOpts.AsDeleteOptions()).
+ Do(ctx).
+ Error()
+}
+
+// Patch implements client.Client.
+func (uc *unstructuredClient) Patch(ctx context.Context, obj Object, patch Patch, opts ...PatchOption) error {
+ _, ok := obj.(*unstructured.Unstructured)
+ if !ok {
+ return fmt.Errorf("unstructured client did not understand object: %T", obj)
+ }
+
+ o, err := uc.cache.getObjMeta(obj)
+ if err != nil {
+ return err
+ }
+
+ data, err := patch.Data(obj)
+ if err != nil {
+ return err
+ }
+
+ patchOpts := &PatchOptions{}
+ return o.Patch(patch.Type()).
+ NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
+ Resource(o.resource()).
+ Name(o.GetName()).
+ VersionedParams(patchOpts.ApplyOptions(opts).AsPatchOptions(), uc.paramCodec).
+ Body(data).
+ Do(ctx).
+ Into(obj)
+}
+
+// Get implements client.Client.
+func (uc *unstructuredClient) Get(ctx context.Context, key ObjectKey, obj Object) error {
+ u, ok := obj.(*unstructured.Unstructured)
+ if !ok {
+ return fmt.Errorf("unstructured client did not understand object: %T", obj)
+ }
+
+ gvk := u.GroupVersionKind()
+
+ r, err := uc.cache.getResource(obj)
+ if err != nil {
+ return err
+ }
+
+ result := r.Get().
+ NamespaceIfScoped(key.Namespace, r.isNamespaced()).
+ Resource(r.resource()).
+ Name(key.Name).
+ Do(ctx).
+ Into(obj)
+
+ u.SetGroupVersionKind(gvk)
+
+ return result
+}
+
+// List implements client.Client.
+func (uc *unstructuredClient) List(ctx context.Context, obj ObjectList, opts ...ListOption) error {
+ u, ok := obj.(*unstructured.UnstructuredList)
+ if !ok {
+ return fmt.Errorf("unstructured client did not understand object: %T", obj)
+ }
+
+ gvk := u.GroupVersionKind()
+ if strings.HasSuffix(gvk.Kind, "List") {
+ gvk.Kind = gvk.Kind[:len(gvk.Kind)-4]
+ }
+
+ listOpts := ListOptions{}
+ listOpts.ApplyOptions(opts)
+
+ r, err := uc.cache.getResource(obj)
+ if err != nil {
+ return err
+ }
+
+ return r.Get().
+ NamespaceIfScoped(listOpts.Namespace, r.isNamespaced()).
+ Resource(r.resource()).
+ VersionedParams(listOpts.AsListOptions(), uc.paramCodec).
+ Do(ctx).
+ Into(obj)
+}
+
+func (uc *unstructuredClient) UpdateStatus(ctx context.Context, obj Object, opts ...UpdateOption) error {
+ _, ok := obj.(*unstructured.Unstructured)
+ if !ok {
+ return fmt.Errorf("unstructured client did not understand object: %T", obj)
+ }
+
+ o, err := uc.cache.getObjMeta(obj)
+ if err != nil {
+ return err
+ }
+
+ return o.Put().
+ NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
+ Resource(o.resource()).
+ Name(o.GetName()).
+ SubResource("status").
+ Body(obj).
+ VersionedParams((&UpdateOptions{}).ApplyOptions(opts).AsUpdateOptions(), uc.paramCodec).
+ Do(ctx).
+ Into(obj)
+}
+
+func (uc *unstructuredClient) PatchStatus(ctx context.Context, obj Object, patch Patch, opts ...PatchOption) error {
+ u, ok := obj.(*unstructured.Unstructured)
+ if !ok {
+ return fmt.Errorf("unstructured client did not understand object: %T", obj)
+ }
+
+ gvk := u.GroupVersionKind()
+
+ o, err := uc.cache.getObjMeta(obj)
+ if err != nil {
+ return err
+ }
+
+ data, err := patch.Data(obj)
+ if err != nil {
+ return err
+ }
+
+ patchOpts := &PatchOptions{}
+ result := o.Patch(patch.Type()).
+ NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
+ Resource(o.resource()).
+ Name(o.GetName()).
+ SubResource("status").
+ Body(data).
+ VersionedParams(patchOpts.ApplyOptions(opts).AsPatchOptions(), uc.paramCodec).
+ Do(ctx).
+ Into(u)
+
+ u.SetGroupVersionKind(gvk)
+ return result
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/watch.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/watch.go
new file mode 100644
index 000000000..765ca5daa
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/watch.go
@@ -0,0 +1,118 @@
+/*
+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 client
+
+import (
+ "context"
+ "strings"
+
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+ "k8s.io/apimachinery/pkg/watch"
+ "k8s.io/client-go/dynamic"
+ "k8s.io/client-go/rest"
+)
+
+// NewWithWatch returns a new WithWatch.
+func NewWithWatch(config *rest.Config, options Options) (WithWatch, error) {
+ client, err := newClient(config, options)
+ if err != nil {
+ return nil, err
+ }
+ dynamicClient, err := dynamic.NewForConfig(config)
+ if err != nil {
+ return nil, err
+ }
+ return &watchingClient{client: client, dynamic: dynamicClient}, nil
+}
+
+type watchingClient struct {
+ *client
+ dynamic dynamic.Interface
+}
+
+func (w *watchingClient) Watch(ctx context.Context, list ObjectList, opts ...ListOption) (watch.Interface, error) {
+ switch l := list.(type) {
+ case *unstructured.UnstructuredList:
+ return w.unstructuredWatch(ctx, l, opts...)
+ case *metav1.PartialObjectMetadataList:
+ return w.metadataWatch(ctx, l, opts...)
+ default:
+ return w.typedWatch(ctx, l, opts...)
+ }
+}
+
+func (w *watchingClient) listOpts(opts ...ListOption) ListOptions {
+ listOpts := ListOptions{}
+ listOpts.ApplyOptions(opts)
+ if listOpts.Raw == nil {
+ listOpts.Raw = &metav1.ListOptions{}
+ }
+ listOpts.Raw.Watch = true
+
+ return listOpts
+}
+
+func (w *watchingClient) metadataWatch(ctx context.Context, obj *metav1.PartialObjectMetadataList, opts ...ListOption) (watch.Interface, error) {
+ gvk := obj.GroupVersionKind()
+ if strings.HasSuffix(gvk.Kind, "List") {
+ gvk.Kind = gvk.Kind[:len(gvk.Kind)-4]
+ }
+
+ listOpts := w.listOpts(opts...)
+
+ resInt, err := w.client.metadataClient.getResourceInterface(gvk, listOpts.Namespace)
+ if err != nil {
+ return nil, err
+ }
+
+ return resInt.Watch(ctx, *listOpts.AsListOptions())
+}
+
+func (w *watchingClient) unstructuredWatch(ctx context.Context, obj *unstructured.UnstructuredList, opts ...ListOption) (watch.Interface, error) {
+ gvk := obj.GroupVersionKind()
+ if strings.HasSuffix(gvk.Kind, "List") {
+ gvk.Kind = gvk.Kind[:len(gvk.Kind)-4]
+ }
+
+ r, err := w.client.unstructuredClient.cache.getResource(obj)
+ if err != nil {
+ return nil, err
+ }
+
+ listOpts := w.listOpts(opts...)
+
+ if listOpts.Namespace != "" && r.isNamespaced() {
+ return w.dynamic.Resource(r.mapping.Resource).Namespace(listOpts.Namespace).Watch(ctx, *listOpts.AsListOptions())
+ }
+ return w.dynamic.Resource(r.mapping.Resource).Watch(ctx, *listOpts.AsListOptions())
+}
+
+func (w *watchingClient) typedWatch(ctx context.Context, obj ObjectList, opts ...ListOption) (watch.Interface, error) {
+ r, err := w.client.typedClient.cache.getResource(obj)
+ if err != nil {
+ return nil, err
+ }
+
+ listOpts := w.listOpts(opts...)
+
+ return r.Get().
+ NamespaceIfScoped(listOpts.Namespace, r.isNamespaced()).
+ Resource(r.resource()).
+ VersionedParams(listOpts.AsListOptions(), w.client.typedClient.paramCodec).
+ Watch(ctx)
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/cluster/cluster.go b/vendor/sigs.k8s.io/controller-runtime/pkg/cluster/cluster.go
new file mode 100644
index 000000000..dfd0fa9dd
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/cluster/cluster.go
@@ -0,0 +1,270 @@
+/*
+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 cluster
+
+import (
+ "context"
+ "errors"
+ "time"
+
+ "github.com/go-logr/logr"
+ "k8s.io/apimachinery/pkg/api/meta"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/client-go/kubernetes/scheme"
+ "k8s.io/client-go/rest"
+ "k8s.io/client-go/tools/record"
+ "sigs.k8s.io/controller-runtime/pkg/client/apiutil"
+ logf "sigs.k8s.io/controller-runtime/pkg/internal/log"
+
+ "sigs.k8s.io/controller-runtime/pkg/cache"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ intrec "sigs.k8s.io/controller-runtime/pkg/internal/recorder"
+)
+
+// Cluster provides various methods to interact with a cluster.
+type Cluster interface {
+ // SetFields will set any dependencies on an object for which the object has implemented the inject
+ // interface - e.g. inject.Client.
+ // Deprecated: use the equivalent Options field to set a field. This method will be removed in v0.10.
+ SetFields(interface{}) error
+
+ // GetConfig returns an initialized Config
+ GetConfig() *rest.Config
+
+ // GetScheme returns an initialized Scheme
+ GetScheme() *runtime.Scheme
+
+ // GetClient returns a client configured with the Config. This client may
+ // not be a fully "direct" client -- it may read from a cache, for
+ // instance. See Options.NewClient for more information on how the default
+ // implementation works.
+ GetClient() client.Client
+
+ // GetFieldIndexer returns a client.FieldIndexer configured with the client
+ GetFieldIndexer() client.FieldIndexer
+
+ // GetCache returns a cache.Cache
+ GetCache() cache.Cache
+
+ // GetEventRecorderFor returns a new EventRecorder for the provided name
+ GetEventRecorderFor(name string) record.EventRecorder
+
+ // GetRESTMapper returns a RESTMapper
+ GetRESTMapper() meta.RESTMapper
+
+ // GetAPIReader returns a reader that will be configured to use the API server.
+ // This should be used sparingly and only when the client does not fit your
+ // use case.
+ GetAPIReader() client.Reader
+
+ // Start starts the cluster
+ Start(ctx context.Context) error
+}
+
+// Options are the possible options that can be configured for a Cluster.
+type Options struct {
+ // Scheme is the scheme used to resolve runtime.Objects to GroupVersionKinds / Resources
+ // Defaults to the kubernetes/client-go scheme.Scheme, but it's almost always better
+ // idea to pass your own scheme in. See the documentation in pkg/scheme for more information.
+ Scheme *runtime.Scheme
+
+ // MapperProvider provides the rest mapper used to map go types to Kubernetes APIs
+ MapperProvider func(c *rest.Config) (meta.RESTMapper, error)
+
+ // Logger is the logger that should be used by this Cluster.
+ // If none is set, it defaults to log.Log global logger.
+ Logger logr.Logger
+
+ // 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.
+ SyncPeriod *time.Duration
+
+ // Namespace 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.
+ Namespace string
+
+ // NewCache is the function that will create the cache to be used
+ // by the manager. If not set this will use the default new cache function.
+ NewCache cache.NewCacheFunc
+
+ // NewClient is the func that creates the client to be used by the manager.
+ // If not set this will create the default DelegatingClient that will
+ // use the cache for reads and the client for writes.
+ NewClient NewClientFunc
+
+ // ClientDisableCacheFor tells the client that, if any cache is used, to bypass it
+ // for the given objects.
+ ClientDisableCacheFor []client.Object
+
+ // DryRunClient specifies whether the client should be configured to enforce
+ // dryRun mode.
+ DryRunClient bool
+
+ // EventBroadcaster records Events emitted by the manager and sends them to the Kubernetes API
+ // Use this to customize the event correlator and spam filter
+ //
+ // Deprecated: using this may cause goroutine leaks if the lifetime of your manager or controllers
+ // is shorter than the lifetime of your process.
+ EventBroadcaster record.EventBroadcaster
+
+ // makeBroadcaster allows deferring the creation of the broadcaster to
+ // avoid leaking goroutines if we never call Start on this manager. It also
+ // returns whether or not this is a "owned" broadcaster, and as such should be
+ // stopped with the manager.
+ makeBroadcaster intrec.EventBroadcasterProducer
+
+ // Dependency injection for testing
+ newRecorderProvider func(config *rest.Config, scheme *runtime.Scheme, logger logr.Logger, makeBroadcaster intrec.EventBroadcasterProducer) (*intrec.Provider, error)
+}
+
+// Option can be used to manipulate Options.
+type Option func(*Options)
+
+// New constructs a brand new cluster.
+func New(config *rest.Config, opts ...Option) (Cluster, error) {
+ if config == nil {
+ return nil, errors.New("must specify Config")
+ }
+
+ options := Options{}
+ for _, opt := range opts {
+ opt(&options)
+ }
+ options = setOptionsDefaults(options)
+
+ // Create the mapper provider
+ mapper, err := options.MapperProvider(config)
+ if err != nil {
+ options.Logger.Error(err, "Failed to get API Group-Resources")
+ return nil, err
+ }
+
+ // Create the cache for the cached read client and registering informers
+ cache, err := options.NewCache(config, cache.Options{Scheme: options.Scheme, Mapper: mapper, Resync: options.SyncPeriod, Namespace: options.Namespace})
+ if err != nil {
+ return nil, err
+ }
+
+ clientOptions := client.Options{Scheme: options.Scheme, Mapper: mapper}
+
+ apiReader, err := client.New(config, clientOptions)
+ if err != nil {
+ return nil, err
+ }
+
+ writeObj, err := options.NewClient(cache, config, clientOptions, options.ClientDisableCacheFor...)
+ if err != nil {
+ return nil, err
+ }
+
+ if options.DryRunClient {
+ writeObj = client.NewDryRunClient(writeObj)
+ }
+
+ // Create the recorder provider to inject event recorders for the components.
+ // TODO(directxman12): the log for the event provider should have a context (name, tags, etc) specific
+ // to the particular controller that it's being injected into, rather than a generic one like is here.
+ recorderProvider, err := options.newRecorderProvider(config, options.Scheme, options.Logger.WithName("events"), options.makeBroadcaster)
+ if err != nil {
+ return nil, err
+ }
+
+ return &cluster{
+ config: config,
+ scheme: options.Scheme,
+ cache: cache,
+ fieldIndexes: cache,
+ client: writeObj,
+ apiReader: apiReader,
+ recorderProvider: recorderProvider,
+ mapper: mapper,
+ logger: options.Logger,
+ }, nil
+}
+
+// setOptionsDefaults set default values for Options fields.
+func setOptionsDefaults(options Options) Options {
+ // Use the Kubernetes client-go scheme if none is specified
+ if options.Scheme == nil {
+ options.Scheme = scheme.Scheme
+ }
+
+ if options.MapperProvider == nil {
+ options.MapperProvider = func(c *rest.Config) (meta.RESTMapper, error) {
+ return apiutil.NewDynamicRESTMapper(c)
+ }
+ }
+
+ // Allow users to define how to create a new client
+ if options.NewClient == nil {
+ options.NewClient = DefaultNewClient
+ }
+
+ // Allow newCache to be mocked
+ if options.NewCache == nil {
+ options.NewCache = cache.New
+ }
+
+ // Allow newRecorderProvider to be mocked
+ if options.newRecorderProvider == nil {
+ options.newRecorderProvider = intrec.NewProvider
+ }
+
+ // This is duplicated with pkg/manager, we need it here to provide
+ // the user with an EventBroadcaster and there for the Leader election
+ if options.EventBroadcaster == nil {
+ // defer initialization to avoid leaking by default
+ options.makeBroadcaster = func() (record.EventBroadcaster, bool) {
+ return record.NewBroadcaster(), true
+ }
+ } else {
+ options.makeBroadcaster = func() (record.EventBroadcaster, bool) {
+ return options.EventBroadcaster, false
+ }
+ }
+
+ if options.Logger == nil {
+ options.Logger = logf.RuntimeLog.WithName("cluster")
+ }
+
+ return options
+}
+
+// NewClientFunc allows a user to define how to create a client.
+type NewClientFunc func(cache cache.Cache, config *rest.Config, options client.Options, uncachedObjects ...client.Object) (client.Client, error)
+
+// DefaultNewClient creates the default caching client.
+func DefaultNewClient(cache cache.Cache, config *rest.Config, options client.Options, uncachedObjects ...client.Object) (client.Client, error) {
+ c, err := client.New(config, options)
+ if err != nil {
+ return nil, err
+ }
+
+ return client.NewDelegatingClient(client.NewDelegatingClientInput{
+ CacheReader: cache,
+ Client: c,
+ UncachedObjects: uncachedObjects,
+ })
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/cluster/internal.go b/vendor/sigs.k8s.io/controller-runtime/pkg/cluster/internal.go
new file mode 100644
index 000000000..125e1d144
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/cluster/internal.go
@@ -0,0 +1,128 @@
+/*
+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 cluster
+
+import (
+ "context"
+
+ "github.com/go-logr/logr"
+ "k8s.io/apimachinery/pkg/api/meta"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/client-go/rest"
+ "k8s.io/client-go/tools/record"
+
+ "sigs.k8s.io/controller-runtime/pkg/cache"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ intrec "sigs.k8s.io/controller-runtime/pkg/internal/recorder"
+ "sigs.k8s.io/controller-runtime/pkg/runtime/inject"
+)
+
+type cluster struct {
+ // config is the rest.config used to talk to the apiserver. Required.
+ config *rest.Config
+
+ // scheme is the scheme injected into Controllers, EventHandlers, Sources and Predicates. Defaults
+ // to scheme.scheme.
+ scheme *runtime.Scheme
+
+ cache cache.Cache
+
+ // TODO(directxman12): Provide an escape hatch to get individual indexers
+ // client is the client injected into Controllers (and EventHandlers, Sources and Predicates).
+ client client.Client
+
+ // apiReader is the reader that will make requests to the api server and not the cache.
+ apiReader client.Reader
+
+ // fieldIndexes knows how to add field indexes over the Cache used by this controller,
+ // which can later be consumed via field selectors from the injected client.
+ fieldIndexes client.FieldIndexer
+
+ // recorderProvider is used to generate event recorders that will be injected into Controllers
+ // (and EventHandlers, Sources and Predicates).
+ recorderProvider *intrec.Provider
+
+ // mapper is used to map resources to kind, and map kind and version.
+ mapper meta.RESTMapper
+
+ // Logger is the logger that should be used by this manager.
+ // If none is set, it defaults to log.Log global logger.
+ logger logr.Logger
+}
+
+func (c *cluster) SetFields(i interface{}) error {
+ if _, err := inject.ConfigInto(c.config, i); err != nil {
+ return err
+ }
+ if _, err := inject.ClientInto(c.client, i); err != nil {
+ return err
+ }
+ if _, err := inject.APIReaderInto(c.apiReader, i); err != nil {
+ return err
+ }
+ if _, err := inject.SchemeInto(c.scheme, i); err != nil {
+ return err
+ }
+ if _, err := inject.CacheInto(c.cache, i); err != nil {
+ return err
+ }
+ if _, err := inject.MapperInto(c.mapper, i); err != nil {
+ return err
+ }
+ return nil
+}
+
+func (c *cluster) GetConfig() *rest.Config {
+ return c.config
+}
+
+func (c *cluster) GetClient() client.Client {
+ return c.client
+}
+
+func (c *cluster) GetScheme() *runtime.Scheme {
+ return c.scheme
+}
+
+func (c *cluster) GetFieldIndexer() client.FieldIndexer {
+ return c.fieldIndexes
+}
+
+func (c *cluster) GetCache() cache.Cache {
+ return c.cache
+}
+
+func (c *cluster) GetEventRecorderFor(name string) record.EventRecorder {
+ return c.recorderProvider.GetEventRecorderFor(name)
+}
+
+func (c *cluster) GetRESTMapper() meta.RESTMapper {
+ return c.mapper
+}
+
+func (c *cluster) GetAPIReader() client.Reader {
+ return c.apiReader
+}
+
+func (c *cluster) GetLogger() logr.Logger {
+ return c.logger
+}
+
+func (c *cluster) Start(ctx context.Context) error {
+ defer c.recorderProvider.Stop(ctx)
+ return c.cache.Start(ctx)
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/config/config.go b/vendor/sigs.k8s.io/controller-runtime/pkg/config/config.go
new file mode 100644
index 000000000..f23b02df0
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/config/config.go
@@ -0,0 +1,112 @@
+/*
+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"
+ ioutil "io/ioutil"
+ "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.
+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.
+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
+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
+}
+
+// InjectScheme will configure the scheme to be used for decoding the file.
+func (d *DeferredFileLoader) InjectScheme(scheme *runtime.Scheme) error {
+ d.scheme = scheme
+ return nil
+}
+
+// loadFile is used from the mutex.Once to load the file.
+func (d *DeferredFileLoader) loadFile() {
+ if d.scheme == nil {
+ d.err = fmt.Errorf("scheme not supplied to controller configuration loader")
+ return
+ }
+
+ content, err := ioutil.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")
+ }
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/config/doc.go b/vendor/sigs.k8s.io/controller-runtime/pkg/config/doc.go
new file mode 100644
index 000000000..ebd8243f3
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/config/doc.go
@@ -0,0 +1,25 @@
+/*
+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 ComponentConfig
+// files
+//
+// DeferredFileLoader
+//
+// This uses a deferred file decoding allowing you to chain your configuration
+// setup. You can pass this into manager.Options#File and it will load your
+// config.
+package config
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/config/v1alpha1/doc.go b/vendor/sigs.k8s.io/controller-runtime/pkg/config/v1alpha1/doc.go
new file mode 100644
index 000000000..1e3adbafb
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/config/v1alpha1/doc.go
@@ -0,0 +1,20 @@
+/*
+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
+package v1alpha1
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/config/v1alpha1/register.go b/vendor/sigs.k8s.io/controller-runtime/pkg/config/v1alpha1/register.go
new file mode 100644
index 000000000..9efdbc066
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/config/v1alpha1/register.go
@@ -0,0 +1,37 @@
+/*
+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.
+ GroupVersion = schema.GroupVersion{Group: "controller-runtime.sigs.k8s.io", Version: "v1alpha1"}
+
+ // SchemeBuilder is used to add go types to the GroupVersionKind scheme.
+ SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
+
+ // AddToScheme adds the types in this group-version to the given scheme.
+ AddToScheme = SchemeBuilder.AddToScheme
+)
+
+func init() {
+ SchemeBuilder.Register(&ControllerManagerConfiguration{})
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/config/v1alpha1/types.go b/vendor/sigs.k8s.io/controller-runtime/pkg/config/v1alpha1/types.go
new file mode 100644
index 000000000..e67b62e51
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/config/v1alpha1/types.go
@@ -0,0 +1,157 @@
+/*
+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.
+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 thw 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.
+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"`
+}
+
+// ControllerMetrics defines the metrics configs.
+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.
+type ControllerHealth struct {
+ // HealthProbeBindAddress is the TCP address that the controller should bind to
+ // for serving health probes
+ // +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.
+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.
+type ControllerManagerConfiguration struct {
+ metav1.TypeMeta `json:",inline"`
+
+ // ControllerManagerConfiguration returns the contfigurations for controllers
+ ControllerManagerConfigurationSpec `json:",inline"`
+}
+
+// Complete returns the configuration for controller-runtime.
+func (c *ControllerManagerConfigurationSpec) Complete() (ControllerManagerConfigurationSpec, error) {
+ return *c, nil
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/config/v1alpha1/zz_generated.deepcopy.go b/vendor/sigs.k8s.io/controller-runtime/pkg/config/v1alpha1/zz_generated.deepcopy.go
new file mode 100644
index 000000000..752fa9754
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/config/v1alpha1/zz_generated.deepcopy.go
@@ -0,0 +1,152 @@
+// +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
+ }
+}
+
+// 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
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/controller/controller.go b/vendor/sigs.k8s.io/controller-runtime/pkg/controller/controller.go
new file mode 100644
index 000000000..88ba78671
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/controller/controller.go
@@ -0,0 +1,141 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package controller
+
+import (
+ "context"
+ "fmt"
+ "time"
+
+ "github.com/go-logr/logr"
+ "k8s.io/client-go/util/workqueue"
+ "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 {
+ // MaxConcurrentReconciles is the maximum number of concurrent Reconciles which can be run. Defaults to 1.
+ MaxConcurrentReconciles int
+
+ // Reconciler reconciles an object
+ Reconciler reconcile.Reconciler
+
+ // 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
+
+ // Log is the logger used for this controller and passed to each reconciliation
+ // request via the context field.
+ Log logr.Logger
+
+ // CacheSyncTimeout refers to the time limit set to wait for syncing caches.
+ // Defaults to 2 minutes if not set.
+ CacheSyncTimeout time.Duration
+
+ // RecoverPanic indicates whether the panic caused by reconcile should be recovered.
+ RecoverPanic bool
+}
+
+// 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
+
+ // 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
+
+ // Start starts the controller. Start blocks until the context is closed or a
+ // controller has an error starting.
+ Start(ctx context.Context) error
+
+ // GetLogger returns this controller logger prefilled with basic information.
+ GetLogger() logr.Logger
+}
+
+// New returns a new Controller registered with the Manager. The Manager will ensure that shared Caches have
+// been synced before the Controller is Started.
+func New(name string, mgr manager.Manager, options Options) (Controller, error) {
+ c, err := NewUnmanaged(name, mgr, options)
+ if err != nil {
+ return nil, err
+ }
+
+ // Add the controller as a Manager components
+ return c, mgr.Add(c)
+}
+
+// NewUnmanaged returns a new controller without adding it to the manager. The
+// caller is responsible for starting the returned controller.
+func NewUnmanaged(name string, mgr manager.Manager, options Options) (Controller, error) {
+ if options.Reconciler == nil {
+ return nil, fmt.Errorf("must specify Reconciler")
+ }
+
+ if len(name) == 0 {
+ return nil, fmt.Errorf("must specify Name for Controller")
+ }
+
+ if options.Log == nil {
+ options.Log = mgr.GetLogger()
+ }
+
+ if options.MaxConcurrentReconciles <= 0 {
+ options.MaxConcurrentReconciles = 1
+ }
+
+ if options.CacheSyncTimeout == 0 {
+ options.CacheSyncTimeout = 2 * time.Minute
+ }
+
+ if options.RateLimiter == nil {
+ options.RateLimiter = workqueue.DefaultControllerRateLimiter()
+ }
+
+ // Inject dependencies into Reconciler
+ if err := mgr.SetFields(options.Reconciler); err != nil {
+ return nil, err
+ }
+
+ // Create controller with dependencies set
+ return &controller.Controller{
+ Do: options.Reconciler,
+ MakeQueue: func() workqueue.RateLimitingInterface {
+ return workqueue.NewNamedRateLimitingQueue(options.RateLimiter, name)
+ },
+ MaxConcurrentReconciles: options.MaxConcurrentReconciles,
+ CacheSyncTimeout: options.CacheSyncTimeout,
+ SetFields: mgr.SetFields,
+ Name: name,
+ Log: options.Log.WithName("controller").WithName(name),
+ RecoverPanic: options.RecoverPanic,
+ }, nil
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/controller/controllerutil/controllerutil.go b/vendor/sigs.k8s.io/controller-runtime/pkg/controller/controllerutil/controllerutil.go
new file mode 100644
index 000000000..13f14a7ed
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/controller/controllerutil/controllerutil.go
@@ -0,0 +1,389 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package controllerutil
+
+import (
+ "context"
+ "fmt"
+ "reflect"
+
+ "k8s.io/apimachinery/pkg/api/equality"
+ apierrors "k8s.io/apimachinery/pkg/api/errors"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "k8s.io/utils/pointer"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/client/apiutil"
+)
+
+// AlreadyOwnedError is an error returned if the object you are trying to assign
+// a controller reference is already owned by another controller Object is the
+// subject and Owner is the reference for the current owner.
+type AlreadyOwnedError struct {
+ Object metav1.Object
+ Owner metav1.OwnerReference
+}
+
+func (e *AlreadyOwnedError) Error() string {
+ return fmt.Sprintf("Object %s/%s is already owned by another %s controller %s", e.Object.GetNamespace(), e.Object.GetName(), e.Owner.Kind, e.Owner.Name)
+}
+
+func newAlreadyOwnedError(obj metav1.Object, owner metav1.OwnerReference) *AlreadyOwnedError {
+ return &AlreadyOwnedError{
+ Object: obj,
+ Owner: owner,
+ }
+}
+
+// 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 {
+ // Validate the owner.
+ ro, ok := owner.(runtime.Object)
+ if !ok {
+ return fmt.Errorf("%T is not a runtime.Object, cannot call SetControllerReference", owner)
+ }
+ if err := validateOwner(owner, controlled); err != nil {
+ return err
+ }
+
+ // Create a new controller ref.
+ gvk, err := apiutil.GVKForObject(ro, scheme)
+ if err != nil {
+ return err
+ }
+ ref := metav1.OwnerReference{
+ APIVersion: gvk.GroupVersion().String(),
+ Kind: gvk.Kind,
+ Name: owner.GetName(),
+ UID: owner.GetUID(),
+ BlockOwnerDeletion: pointer.BoolPtr(true),
+ Controller: pointer.BoolPtr(true),
+ }
+
+ // Return early with an error if the object is already controlled.
+ if existing := metav1.GetControllerOf(controlled); existing != nil && !referSameObject(*existing, ref) {
+ return newAlreadyOwnedError(controlled, *existing)
+ }
+
+ // Update owner references and return.
+ upsertOwnerRef(ref, controlled)
+ return nil
+}
+
+// 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 {
+ // Validate the owner.
+ ro, ok := owner.(runtime.Object)
+ if !ok {
+ return fmt.Errorf("%T is not a runtime.Object, cannot call SetOwnerReference", owner)
+ }
+ if err := validateOwner(owner, object); err != nil {
+ return err
+ }
+
+ // Create a new owner ref.
+ gvk, err := apiutil.GVKForObject(ro, scheme)
+ if err != nil {
+ return err
+ }
+ ref := metav1.OwnerReference{
+ APIVersion: gvk.GroupVersion().String(),
+ Kind: gvk.Kind,
+ UID: owner.GetUID(),
+ Name: owner.GetName(),
+ }
+
+ // Update owner references and return.
+ upsertOwnerRef(ref, object)
+ return nil
+}
+
+func upsertOwnerRef(ref metav1.OwnerReference, object metav1.Object) {
+ owners := object.GetOwnerReferences()
+ if idx := indexOwnerRef(owners, ref); idx == -1 {
+ owners = append(owners, ref)
+ } else {
+ owners[idx] = ref
+ }
+ object.SetOwnerReferences(owners)
+}
+
+// indexOwnerRef returns the index of the owner reference in the slice if found, or -1.
+func indexOwnerRef(ownerReferences []metav1.OwnerReference, ref metav1.OwnerReference) int {
+ for index, r := range ownerReferences {
+ if referSameObject(r, ref) {
+ return index
+ }
+ }
+ return -1
+}
+
+func validateOwner(owner, object metav1.Object) error {
+ ownerNs := owner.GetNamespace()
+ if ownerNs != "" {
+ objNs := object.GetNamespace()
+ if objNs == "" {
+ return fmt.Errorf("cluster-scoped resource must not have a namespace-scoped owner, owner's namespace %s", ownerNs)
+ }
+ if ownerNs != objNs {
+ return fmt.Errorf("cross-namespace owner references are disallowed, owner's namespace %s, obj's namespace %s", owner.GetNamespace(), object.GetNamespace())
+ }
+ }
+ return nil
+}
+
+// Returns true if a and b point to the same object.
+func referSameObject(a, b metav1.OwnerReference) bool {
+ aGV, err := schema.ParseGroupVersion(a.APIVersion)
+ if err != nil {
+ return false
+ }
+
+ bGV, err := schema.ParseGroupVersion(b.APIVersion)
+ if err != nil {
+ return false
+ }
+
+ return aGV.Group == bGV.Group && a.Kind == b.Kind && a.Name == b.Name
+}
+
+// OperationResult is the action result of a CreateOrUpdate call.
+type OperationResult string
+
+const ( // They should complete the sentence "Deployment default/foo has been ..."
+ // OperationResultNone means that the resource has not been changed.
+ OperationResultNone OperationResult = "unchanged"
+ // OperationResultCreated means that a new resource is created.
+ OperationResultCreated OperationResult = "created"
+ // OperationResultUpdated means that an existing resource is updated.
+ OperationResultUpdated OperationResult = "updated"
+ // OperationResultUpdatedStatus means that an existing resource and its status is updated.
+ OperationResultUpdatedStatus OperationResult = "updatedStatus"
+ // OperationResultUpdatedStatusOnly means that only an existing status is updated.
+ OperationResultUpdatedStatusOnly OperationResult = "updatedStatusOnly"
+)
+
+// CreateOrUpdate creates or updates the given object in the Kubernetes
+// cluster. The object's desired state must be reconciled with the existing
+// state inside the passed in callback MutateFn.
+//
+// The MutateFn is called regardless of creating or updating an object.
+//
+// It returns the executed operation and an error.
+func CreateOrUpdate(ctx context.Context, c client.Client, obj client.Object, f MutateFn) (OperationResult, error) {
+ key := client.ObjectKeyFromObject(obj)
+ if err := c.Get(ctx, key, obj); err != nil {
+ if !apierrors.IsNotFound(err) {
+ return OperationResultNone, err
+ }
+ if err := mutate(f, key, obj); err != nil {
+ return OperationResultNone, err
+ }
+ if err := c.Create(ctx, obj); err != nil {
+ return OperationResultNone, err
+ }
+ return OperationResultCreated, nil
+ }
+
+ existing := obj.DeepCopyObject() //nolint
+ if err := mutate(f, key, obj); err != nil {
+ return OperationResultNone, err
+ }
+
+ if equality.Semantic.DeepEqual(existing, obj) {
+ return OperationResultNone, nil
+ }
+
+ if err := c.Update(ctx, obj); err != nil {
+ return OperationResultNone, err
+ }
+ return OperationResultUpdated, nil
+}
+
+// CreateOrPatch creates or patches the given object in the Kubernetes
+// cluster. The object's desired state must be reconciled with the before
+// state inside the passed in callback MutateFn.
+//
+// The MutateFn is called regardless of creating or updating an object.
+//
+// It returns the executed operation and an error.
+func CreateOrPatch(ctx context.Context, c client.Client, obj client.Object, f MutateFn) (OperationResult, error) {
+ key := client.ObjectKeyFromObject(obj)
+ if err := c.Get(ctx, key, obj); err != nil {
+ if !apierrors.IsNotFound(err) {
+ return OperationResultNone, err
+ }
+ if f != nil {
+ if err := mutate(f, key, obj); err != nil {
+ return OperationResultNone, err
+ }
+ }
+ if err := c.Create(ctx, obj); err != nil {
+ return OperationResultNone, err
+ }
+ return OperationResultCreated, nil
+ }
+
+ // Create patches for the object and its possible status.
+ objPatch := client.MergeFrom(obj.DeepCopyObject().(client.Object))
+ statusPatch := client.MergeFrom(obj.DeepCopyObject().(client.Object))
+
+ // Create a copy of the original object as well as converting that copy to
+ // unstructured data.
+ before, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj.DeepCopyObject())
+ if err != nil {
+ return OperationResultNone, err
+ }
+
+ // Attempt to extract the status from the resource for easier comparison later
+ beforeStatus, hasBeforeStatus, err := unstructured.NestedFieldCopy(before, "status")
+ if err != nil {
+ return OperationResultNone, err
+ }
+
+ // If the resource contains a status then remove it from the unstructured
+ // copy to avoid unnecessary patching later.
+ if hasBeforeStatus {
+ unstructured.RemoveNestedField(before, "status")
+ }
+
+ // Mutate the original object.
+ if f != nil {
+ if err := mutate(f, key, obj); err != nil {
+ return OperationResultNone, err
+ }
+ }
+
+ // Convert the resource to unstructured to compare against our before copy.
+ after, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
+ if err != nil {
+ return OperationResultNone, err
+ }
+
+ // Attempt to extract the status from the resource for easier comparison later
+ afterStatus, hasAfterStatus, err := unstructured.NestedFieldCopy(after, "status")
+ if err != nil {
+ return OperationResultNone, err
+ }
+
+ // If the resource contains a status then remove it from the unstructured
+ // copy to avoid unnecessary patching later.
+ if hasAfterStatus {
+ unstructured.RemoveNestedField(after, "status")
+ }
+
+ result := OperationResultNone
+
+ if !reflect.DeepEqual(before, after) {
+ // Only issue a Patch if the before and after resources (minus status) differ
+ if err := c.Patch(ctx, obj, objPatch); err != nil {
+ return result, err
+ }
+ result = OperationResultUpdated
+ }
+
+ if (hasBeforeStatus || hasAfterStatus) && !reflect.DeepEqual(beforeStatus, afterStatus) {
+ // Only issue a Status Patch if the resource has a status and the beforeStatus
+ // and afterStatus copies differ
+ if result == OperationResultUpdated {
+ // If Status was replaced by Patch before, set it to afterStatus
+ objectAfterPatch, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
+ if err != nil {
+ return result, err
+ }
+ if err = unstructured.SetNestedField(objectAfterPatch, afterStatus, "status"); err != nil {
+ return result, err
+ }
+ // If Status was replaced by Patch before, restore patched structure to the obj
+ if err = runtime.DefaultUnstructuredConverter.FromUnstructured(objectAfterPatch, obj); err != nil {
+ return result, err
+ }
+ }
+ if err := c.Status().Patch(ctx, obj, statusPatch); err != nil {
+ return result, err
+ }
+ if result == OperationResultUpdated {
+ result = OperationResultUpdatedStatus
+ } else {
+ result = OperationResultUpdatedStatusOnly
+ }
+ }
+
+ return result, nil
+}
+
+// mutate wraps a MutateFn and applies validation to its result.
+func mutate(f MutateFn, key client.ObjectKey, obj client.Object) error {
+ if err := f(); err != nil {
+ return err
+ }
+ if newKey := client.ObjectKeyFromObject(obj); key != newKey {
+ return fmt.Errorf("MutateFn cannot mutate object name and/or object namespace")
+ }
+ return nil
+}
+
+// MutateFn is a function which mutates the existing object into it's desired state.
+type MutateFn func() error
+
+// AddFinalizer accepts an Object and adds the provided finalizer if not present.
+func AddFinalizer(o client.Object, finalizer string) {
+ f := o.GetFinalizers()
+ for _, e := range f {
+ if e == finalizer {
+ return
+ }
+ }
+ o.SetFinalizers(append(f, finalizer))
+}
+
+// RemoveFinalizer accepts an Object and removes the provided finalizer if present.
+func RemoveFinalizer(o client.Object, finalizer string) {
+ f := o.GetFinalizers()
+ for i := 0; i < len(f); i++ {
+ if f[i] == finalizer {
+ f = append(f[:i], f[i+1:]...)
+ i--
+ }
+ }
+ o.SetFinalizers(f)
+}
+
+// ContainsFinalizer checks an Object that the provided finalizer is present.
+func ContainsFinalizer(o client.Object, finalizer string) bool {
+ f := o.GetFinalizers()
+ for _, e := range f {
+ if e == finalizer {
+ return true
+ }
+ }
+ return false
+}
+
+// Object allows functions to work indistinctly with any resource that
+// implements both Object interfaces.
+//
+// Deprecated: Use client.Object instead.
+type Object = client.Object
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/controller/controllerutil/doc.go b/vendor/sigs.k8s.io/controller-runtime/pkg/controller/controllerutil/doc.go
new file mode 100644
index 000000000..ab386b29c
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/controller/controllerutil/doc.go
@@ -0,0 +1,20 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+/*
+Package controllerutil contains utility functions for working with and implementing Controllers.
+*/
+package controllerutil
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/controller/doc.go b/vendor/sigs.k8s.io/controller-runtime/pkg/controller/doc.go
new file mode 100644
index 000000000..667b14fdd
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/controller/doc.go
@@ -0,0 +1,25 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+/*
+Package controller provides types and functions for building Controllers. Controllers implement Kubernetes APIs.
+
+Creation
+
+To create a new Controller, first create a manager.Manager and pass it to the controller.New function.
+The Controller MUST be started by calling Manager.Start.
+*/
+package controller
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/conversion/conversion.go b/vendor/sigs.k8s.io/controller-runtime/pkg/conversion/conversion.go
new file mode 100644
index 000000000..da32ab48e
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/conversion/conversion.go
@@ -0,0 +1,40 @@
+/*
+Copyright 2019 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+/*
+Package conversion provides interface definitions that an API Type needs to
+implement for it to be supported by the generic conversion webhook handler
+defined under pkg/webhook/conversion.
+*/
+package conversion
+
+import "k8s.io/apimachinery/pkg/runtime"
+
+// Convertible defines capability of a type to convertible i.e. it can be converted to/from a hub type.
+type Convertible interface {
+ runtime.Object
+ ConvertTo(dst Hub) error
+ ConvertFrom(src Hub) error
+}
+
+// Hub marks that a given type is the hub type for conversion. This means that
+// all conversions will first convert to the hub type, then convert from the hub
+// type to the destination type. All types besides the hub type should implement
+// Convertible.
+type Hub interface {
+ runtime.Object
+ Hub()
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/event/doc.go b/vendor/sigs.k8s.io/controller-runtime/pkg/event/doc.go
new file mode 100644
index 000000000..adba3bbc1
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/event/doc.go
@@ -0,0 +1,28 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+/*
+Package event contains the definitions for the Event types produced by source.Sources and transformed into
+reconcile.Requests by handler.EventHandler.
+
+You should rarely need to work with these directly -- instead, use Controller.Watch with
+source.Sources and handler.EventHandlers.
+
+Events generally contain both a full runtime.Object that caused the event, as well
+as a direct handle to that object's metadata. This saves a lot of typecasting in
+code that works with Events.
+*/
+package event
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/event/event.go b/vendor/sigs.k8s.io/controller-runtime/pkg/event/event.go
new file mode 100644
index 000000000..271b3c00f
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/event/event.go
@@ -0,0 +1,55 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package event
+
+import "sigs.k8s.io/controller-runtime/pkg/client"
+
+// 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 an handler.EventHandler.
+type CreateEvent struct {
+ // Object is the object from the event
+ Object 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 UpdateEvent struct {
+ // ObjectOld is the object from the event
+ ObjectOld client.Object
+
+ // ObjectNew is the object from the event
+ ObjectNew 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 struct {
+ // Object is the object from the event
+ Object client.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 {
+ // Object is the object from the event
+ Object client.Object
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/handler/doc.go b/vendor/sigs.k8s.io/controller-runtime/pkg/handler/doc.go
new file mode 100644
index 000000000..3b5b79048
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/handler/doc.go
@@ -0,0 +1,38 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+/*
+Package handler defines EventHandlers that enqueue reconcile.Requests in response to Create, Update, Deletion Events
+observed from Watching Kubernetes APIs. Users should provide a source.Source and handler.EventHandler to
+Controller.Watch in order to generate and enqueue reconcile.Request work items.
+
+Generally, following premade event handlers should be sufficient for most use cases:
+
+EventHandlers
+
+EnqueueRequestForObject - Enqueues a reconcile.Request containing the Name and Namespace of the object in the Event. This will
+cause the object that was the source of the Event (e.g. the created / deleted / updated object) to be
+reconciled.
+
+EnqueueRequestForOwner - Enqueues a reconcile.Request containing the Name and Namespace of the Owner of the object in the Event.
+This will cause owner of the object that was the source of the Event (e.g. the owner object that created the object)
+to be reconciled.
+
+EnqueueRequestsFromMapFunc - Enqueues reconcile.Requests resulting from a user provided transformation function run against the
+object in the Event. This will cause an arbitrary collection of objects (defined from a transformation of the
+source object) to be reconciled.
+*/
+package handler
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/handler/enqueue.go b/vendor/sigs.k8s.io/controller-runtime/pkg/handler/enqueue.go
new file mode 100644
index 000000000..e6d3a4eaa
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/handler/enqueue.go
@@ -0,0 +1,90 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package handler
+
+import (
+ "k8s.io/apimachinery/pkg/types"
+ "k8s.io/client-go/util/workqueue"
+ "sigs.k8s.io/controller-runtime/pkg/event"
+ logf "sigs.k8s.io/controller-runtime/pkg/internal/log"
+ "sigs.k8s.io/controller-runtime/pkg/reconcile"
+)
+
+var enqueueLog = logf.RuntimeLog.WithName("eventhandler").WithName("EnqueueRequestForObject")
+
+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
+// Controllers that have associated Resources (e.g. CRDs) to reconcile the associated Resource.
+type EnqueueRequestForObject struct{}
+
+// Create implements EventHandler.
+func (e *EnqueueRequestForObject) Create(evt event.CreateEvent, q workqueue.RateLimitingInterface) {
+ if evt.Object == nil {
+ enqueueLog.Error(nil, "CreateEvent received with no metadata", "event", evt)
+ return
+ }
+ q.Add(reconcile.Request{NamespacedName: types.NamespacedName{
+ Name: evt.Object.GetName(),
+ Namespace: evt.Object.GetNamespace(),
+ }})
+}
+
+// Update implements EventHandler.
+func (e *EnqueueRequestForObject) Update(evt event.UpdateEvent, q workqueue.RateLimitingInterface) {
+ switch {
+ case evt.ObjectNew != nil:
+ q.Add(reconcile.Request{NamespacedName: types.NamespacedName{
+ Name: evt.ObjectNew.GetName(),
+ Namespace: evt.ObjectNew.GetNamespace(),
+ }})
+ case evt.ObjectOld != nil:
+ q.Add(reconcile.Request{NamespacedName: types.NamespacedName{
+ Name: evt.ObjectOld.GetName(),
+ Namespace: evt.ObjectOld.GetNamespace(),
+ }})
+ default:
+ enqueueLog.Error(nil, "UpdateEvent received with no metadata", "event", evt)
+ }
+}
+
+// Delete implements EventHandler.
+func (e *EnqueueRequestForObject) Delete(evt event.DeleteEvent, q workqueue.RateLimitingInterface) {
+ if evt.Object == nil {
+ enqueueLog.Error(nil, "DeleteEvent received with no metadata", "event", evt)
+ return
+ }
+ q.Add(reconcile.Request{NamespacedName: types.NamespacedName{
+ Name: evt.Object.GetName(),
+ Namespace: evt.Object.GetNamespace(),
+ }})
+}
+
+// Generic implements EventHandler.
+func (e *EnqueueRequestForObject) Generic(evt event.GenericEvent, q workqueue.RateLimitingInterface) {
+ if evt.Object == nil {
+ enqueueLog.Error(nil, "GenericEvent received with no metadata", "event", evt)
+ return
+ }
+ q.Add(reconcile.Request{NamespacedName: types.NamespacedName{
+ Name: evt.Object.GetName(),
+ Namespace: evt.Object.GetNamespace(),
+ }})
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/handler/enqueue_mapped.go b/vendor/sigs.k8s.io/controller-runtime/pkg/handler/enqueue_mapped.go
new file mode 100644
index 000000000..17401b1fd
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/handler/enqueue_mapped.go
@@ -0,0 +1,97 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package handler
+
+import (
+ "k8s.io/client-go/util/workqueue"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/event"
+ "sigs.k8s.io/controller-runtime/pkg/reconcile"
+ "sigs.k8s.io/controller-runtime/pkg/runtime/inject"
+)
+
+// MapFunc is the signature required for enqueueing requests from a generic function.
+// This type is usually used with EnqueueRequestsFromMapFunc when registering an event handler.
+type MapFunc func(client.Object) []reconcile.Request
+
+// 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
+// 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)
+//
+// EnqueueRequestsFromMapFunc is frequently used to fan-out updates from one object to one or more other
+// objects of a differing type.
+//
+// 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{
+ toRequests: fn,
+ }
+}
+
+var _ EventHandler = &enqueueRequestsFromMapFunc{}
+
+type enqueueRequestsFromMapFunc struct {
+ // Mapper transforms the argument into a slice of keys to be reconciled
+ toRequests MapFunc
+}
+
+// Create implements EventHandler.
+func (e *enqueueRequestsFromMapFunc) Create(evt event.CreateEvent, q workqueue.RateLimitingInterface) {
+ reqs := map[reconcile.Request]empty{}
+ e.mapAndEnqueue(q, evt.Object, reqs)
+}
+
+// Update implements EventHandler.
+func (e *enqueueRequestsFromMapFunc) Update(evt event.UpdateEvent, q workqueue.RateLimitingInterface) {
+ reqs := map[reconcile.Request]empty{}
+ e.mapAndEnqueue(q, evt.ObjectOld, reqs)
+ e.mapAndEnqueue(q, evt.ObjectNew, reqs)
+}
+
+// Delete implements EventHandler.
+func (e *enqueueRequestsFromMapFunc) Delete(evt event.DeleteEvent, q workqueue.RateLimitingInterface) {
+ reqs := map[reconcile.Request]empty{}
+ e.mapAndEnqueue(q, evt.Object, reqs)
+}
+
+// Generic implements EventHandler.
+func (e *enqueueRequestsFromMapFunc) Generic(evt event.GenericEvent, q workqueue.RateLimitingInterface) {
+ reqs := map[reconcile.Request]empty{}
+ e.mapAndEnqueue(q, evt.Object, reqs)
+}
+
+func (e *enqueueRequestsFromMapFunc) mapAndEnqueue(q workqueue.RateLimitingInterface, object client.Object, reqs map[reconcile.Request]empty) {
+ for _, req := range e.toRequests(object) {
+ _, ok := reqs[req]
+ if !ok {
+ q.Add(req)
+ reqs[req] = empty{}
+ }
+ }
+}
+
+// EnqueueRequestsFromMapFunc can inject fields into the mapper.
+
+// InjectFunc implements inject.Injector.
+func (e *enqueueRequestsFromMapFunc) InjectFunc(f inject.Func) error {
+ if f == nil {
+ return nil
+ }
+ return f(e.toRequests)
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/handler/enqueue_owner.go b/vendor/sigs.k8s.io/controller-runtime/pkg/handler/enqueue_owner.go
new file mode 100644
index 000000000..63699893f
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/handler/enqueue_owner.go
@@ -0,0 +1,189 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package handler
+
+import (
+ "fmt"
+
+ "k8s.io/apimachinery/pkg/api/meta"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "k8s.io/apimachinery/pkg/types"
+ "k8s.io/client-go/util/workqueue"
+ "sigs.k8s.io/controller-runtime/pkg/event"
+ logf "sigs.k8s.io/controller-runtime/pkg/internal/log"
+ "sigs.k8s.io/controller-runtime/pkg/reconcile"
+ "sigs.k8s.io/controller-runtime/pkg/runtime/inject"
+)
+
+var _ EventHandler = &EnqueueRequestForOwner{}
+
+var log = logf.RuntimeLog.WithName("eventhandler").WithName("EnqueueRequestForOwner")
+
+// EnqueueRequestForOwner enqueues Requests for the Owners of an object. E.g. the object that created
+// the object that was the source of the Event.
+//
+// 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.EnqueueRequestForOwner EventHandler with an OwnerType of ReplicaSet and IsController set to true.
+type EnqueueRequestForOwner struct {
+ // OwnerType is the type of the Owner object to look for in OwnerReferences. Only Group and Kind are compared.
+ OwnerType runtime.Object
+
+ // IsController if set will only look at the first OwnerReference with Controller: true.
+ IsController bool
+
+ // groupKind is the cached Group and Kind from OwnerType
+ groupKind schema.GroupKind
+
+ // mapper maps GroupVersionKinds to Resources
+ mapper meta.RESTMapper
+}
+
+// Create implements EventHandler.
+func (e *EnqueueRequestForOwner) Create(evt event.CreateEvent, q workqueue.RateLimitingInterface) {
+ reqs := map[reconcile.Request]empty{}
+ e.getOwnerReconcileRequest(evt.Object, reqs)
+ for req := range reqs {
+ q.Add(req)
+ }
+}
+
+// Update implements EventHandler.
+func (e *EnqueueRequestForOwner) Update(evt event.UpdateEvent, q workqueue.RateLimitingInterface) {
+ reqs := map[reconcile.Request]empty{}
+ e.getOwnerReconcileRequest(evt.ObjectOld, reqs)
+ e.getOwnerReconcileRequest(evt.ObjectNew, reqs)
+ for req := range reqs {
+ q.Add(req)
+ }
+}
+
+// Delete implements EventHandler.
+func (e *EnqueueRequestForOwner) Delete(evt event.DeleteEvent, q workqueue.RateLimitingInterface) {
+ reqs := map[reconcile.Request]empty{}
+ e.getOwnerReconcileRequest(evt.Object, reqs)
+ for req := range reqs {
+ q.Add(req)
+ }
+}
+
+// Generic implements EventHandler.
+func (e *EnqueueRequestForOwner) Generic(evt event.GenericEvent, q workqueue.RateLimitingInterface) {
+ reqs := map[reconcile.Request]empty{}
+ e.getOwnerReconcileRequest(evt.Object, reqs)
+ for req := range reqs {
+ q.Add(req)
+ }
+}
+
+// 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 {
+ // Get the kinds of the type
+ kinds, _, err := scheme.ObjectKinds(e.OwnerType)
+ if err != nil {
+ log.Error(err, "Could not get ObjectKinds for OwnerType", "owner type", fmt.Sprintf("%T", e.OwnerType))
+ return err
+ }
+ // Expect only 1 kind. If there is more than one kind this is probably an edge case such as ListOptions.
+ if len(kinds) != 1 {
+ err := fmt.Errorf("expected exactly 1 kind for OwnerType %T, but found %s kinds", e.OwnerType, kinds)
+ log.Error(nil, "expected exactly 1 kind for OwnerType", "owner type", fmt.Sprintf("%T", e.OwnerType), "kinds", kinds)
+ return err
+ }
+ // Cache the Group and Kind for the OwnerType
+ e.groupKind = schema.GroupKind{Group: kinds[0].Group, Kind: kinds[0].Kind}
+ return nil
+}
+
+// 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) {
+ // 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) {
+ // 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 {
+ log.Error(err, "Could not parse OwnerReference APIVersion",
+ "api version", ref.APIVersion)
+ return
+ }
+
+ // Compare the OwnerReference Group and Kind against the OwnerType Group and Kind specified by the user.
+ // If the two match, create a Request for the objected referred to by
+ // the OwnerReference. Use the Name from the OwnerReference and the Namespace from the
+ // object in the event.
+ if ref.Kind == e.groupKind.Kind && refGV.Group == e.groupKind.Group {
+ // Match found - add a Request for the object referred to in the OwnerReference
+ request := reconcile.Request{NamespacedName: types.NamespacedName{
+ Name: ref.Name,
+ }}
+
+ // if owner is not namespaced then we should set the namespace to the empty
+ mapping, err := e.mapper.RESTMapping(e.groupKind, refGV.Version)
+ if err != nil {
+ log.Error(err, "Could not retrieve rest mapping", "kind", e.groupKind)
+ return
+ }
+ if mapping.Scope.Name() != meta.RESTScopeNameRoot {
+ request.Namespace = object.GetNamespace()
+ }
+
+ result[request] = empty{}
+ }
+ }
+}
+
+// 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 {
+ return nil
+ }
+
+ // If not filtered as Controller only, then use all the OwnerReferences
+ if !e.IsController {
+ return object.GetOwnerReferences()
+ }
+ // If filtered to a Controller, only take the Controller OwnerReference
+ if ownerRef := metav1.GetControllerOf(object); ownerRef != nil {
+ return []metav1.OwnerReference{*ownerRef}
+ }
+ // No Controller OwnerReference found
+ return nil
+}
+
+var _ inject.Scheme = &EnqueueRequestForOwner{}
+
+// InjectScheme is called by the Controller to provide a singleton scheme to the EnqueueRequestForOwner.
+func (e *EnqueueRequestForOwner) InjectScheme(s *runtime.Scheme) error {
+ return e.parseOwnerTypeGroupKind(s)
+}
+
+var _ inject.Mapper = &EnqueueRequestForOwner{}
+
+// InjectMapper is called by the Controller to provide the rest mapper used by the manager.
+func (e *EnqueueRequestForOwner) InjectMapper(m meta.RESTMapper) error {
+ e.mapper = m
+ return nil
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/handler/eventhandler.go b/vendor/sigs.k8s.io/controller-runtime/pkg/handler/eventhandler.go
new file mode 100644
index 000000000..8652d22d7
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/handler/eventhandler.go
@@ -0,0 +1,104 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package handler
+
+import (
+ "k8s.io/client-go/util/workqueue"
+ "sigs.k8s.io/controller-runtime/pkg/event"
+)
+
+// 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.
+//
+// Identical reconcile.Requests will be batched together through the queuing mechanism before reconcile is called.
+//
+// * Use EnqueueRequestForObject 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 EnqueueRequestForOwner 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 EnqueueRequestsFromMapFunc 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 EventHandler, you can ignore the functions on the EventHandler interface.
+// Most users shouldn't need to implement their own EventHandler.
+type EventHandler interface {
+ // Create is called in response to an create event - e.g. Pod Creation.
+ Create(event.CreateEvent, workqueue.RateLimitingInterface)
+
+ // Update is called in response to an update event - e.g. Pod Updated.
+ Update(event.UpdateEvent, workqueue.RateLimitingInterface)
+
+ // Delete is called in response to a delete event - e.g. Pod Deleted.
+ Delete(event.DeleteEvent, workqueue.RateLimitingInterface)
+
+ // Generic is called in response to an event of an unknown type or a synthetic event triggered as a cron or
+ // external trigger request - e.g. reconcile Autoscaling, or a Webhook.
+ Generic(event.GenericEvent, workqueue.RateLimitingInterface)
+}
+
+var _ EventHandler = Funcs{}
+
+// Funcs implements EventHandler.
+type Funcs struct {
+ // Create is called in response to an add event. Defaults to no-op.
+ // RateLimitingInterface is used to enqueue reconcile.Requests.
+ CreateFunc func(event.CreateEvent, workqueue.RateLimitingInterface)
+
+ // Update is called in response to an update event. Defaults to no-op.
+ // RateLimitingInterface is used to enqueue reconcile.Requests.
+ UpdateFunc func(event.UpdateEvent, workqueue.RateLimitingInterface)
+
+ // Delete is called in response to a delete event. Defaults to no-op.
+ // RateLimitingInterface is used to enqueue reconcile.Requests.
+ DeleteFunc func(event.DeleteEvent, workqueue.RateLimitingInterface)
+
+ // GenericFunc is called in response to a generic event. Defaults to no-op.
+ // RateLimitingInterface is used to enqueue reconcile.Requests.
+ GenericFunc func(event.GenericEvent, workqueue.RateLimitingInterface)
+}
+
+// Create implements EventHandler.
+func (h Funcs) Create(e event.CreateEvent, q workqueue.RateLimitingInterface) {
+ if h.CreateFunc != nil {
+ h.CreateFunc(e, q)
+ }
+}
+
+// Delete implements EventHandler.
+func (h Funcs) Delete(e event.DeleteEvent, q workqueue.RateLimitingInterface) {
+ if h.DeleteFunc != nil {
+ h.DeleteFunc(e, q)
+ }
+}
+
+// Update implements EventHandler.
+func (h Funcs) Update(e event.UpdateEvent, q workqueue.RateLimitingInterface) {
+ if h.UpdateFunc != nil {
+ h.UpdateFunc(e, q)
+ }
+}
+
+// Generic implements EventHandler.
+func (h Funcs) Generic(e event.GenericEvent, q workqueue.RateLimitingInterface) {
+ if h.GenericFunc != nil {
+ h.GenericFunc(e, q)
+ }
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/healthz/doc.go b/vendor/sigs.k8s.io/controller-runtime/pkg/healthz/doc.go
new file mode 100644
index 000000000..9827eeafe
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/healthz/doc.go
@@ -0,0 +1,32 @@
+/*
+Copyright 2014 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 healthz contains helpers from supporting liveness and readiness endpoints.
+// (often referred to as healthz and readyz, respectively).
+//
+// This package draws heavily from the apiserver's healthz package
+// ( https://github.com/kubernetes/apiserver/tree/master/pkg/server/healthz )
+// but has some changes to bring it in line with controller-runtime's style.
+//
+// The main entrypoint is the Handler -- this serves both aggregated health status
+// and individual health check endpoints.
+package healthz
+
+import (
+ logf "sigs.k8s.io/controller-runtime/pkg/internal/log"
+)
+
+var log = logf.RuntimeLog.WithName("healthz")
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/healthz/healthz.go b/vendor/sigs.k8s.io/controller-runtime/pkg/healthz/healthz.go
new file mode 100644
index 000000000..bd1cc151a
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/healthz/healthz.go
@@ -0,0 +1,206 @@
+/*
+Copyright 2014 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 healthz
+
+import (
+ "fmt"
+ "net/http"
+ "path"
+ "sort"
+ "strings"
+
+ "k8s.io/apimachinery/pkg/util/sets"
+)
+
+// Handler is an http.Handler that aggregates the results of the given
+// checkers to the root path, and supports calling individual checkers on
+// subpaths of the name of the checker.
+//
+// Adding checks on the fly is *not* threadsafe -- use a wrapper.
+type Handler struct {
+ Checks map[string]Checker
+}
+
+// checkStatus holds the output of a particular check.
+type checkStatus struct {
+ name string
+ healthy bool
+ excluded bool
+}
+
+func (h *Handler) serveAggregated(resp http.ResponseWriter, req *http.Request) {
+ failed := false
+ excluded := getExcludedChecks(req)
+
+ parts := make([]checkStatus, 0, len(h.Checks))
+
+ // calculate the results...
+ for checkName, check := range h.Checks {
+ // no-op the check if we've specified we want to exclude the check
+ if excluded.Has(checkName) {
+ excluded.Delete(checkName)
+ parts = append(parts, checkStatus{name: checkName, healthy: true, excluded: true})
+ continue
+ }
+ if err := check(req); err != nil {
+ log.V(1).Info("healthz check failed", "checker", checkName, "error", err)
+ parts = append(parts, checkStatus{name: checkName, healthy: false})
+ failed = true
+ } else {
+ parts = append(parts, checkStatus{name: checkName, healthy: true})
+ }
+ }
+
+ // ...default a check if none is present...
+ if len(h.Checks) == 0 {
+ parts = append(parts, checkStatus{name: "ping", healthy: true})
+ }
+
+ for _, c := range excluded.List() {
+ log.V(1).Info("cannot exclude health check, no matches for it", "checker", c)
+ }
+
+ // ...sort to be consistent...
+ sort.Slice(parts, func(i, j int) bool { return parts[i].name < parts[j].name })
+
+ // ...and write out the result
+ // TODO(directxman12): this should also accept a request for JSON content (via a accept header)
+ _, forceVerbose := req.URL.Query()["verbose"]
+ writeStatusesAsText(resp, parts, excluded, failed, forceVerbose)
+}
+
+// writeStatusAsText writes out the given check statuses in some semi-arbitrary
+// bespoke text format that we copied from Kubernetes. unknownExcludes lists
+// any checks that the user requested to have excluded, but weren't actually
+// known checks. writeStatusAsText is always verbose on failure, and can be
+// forced to be verbose on success using the given argument.
+func writeStatusesAsText(resp http.ResponseWriter, parts []checkStatus, unknownExcludes sets.String, failed, forceVerbose bool) {
+ resp.Header().Set("Content-Type", "text/plain; charset=utf-8")
+ resp.Header().Set("X-Content-Type-Options", "nosniff")
+
+ // always write status code first
+ if failed {
+ resp.WriteHeader(http.StatusInternalServerError)
+ } else {
+ resp.WriteHeader(http.StatusOK)
+ }
+
+ // shortcut for easy non-verbose success
+ if !failed && !forceVerbose {
+ fmt.Fprint(resp, "ok")
+ return
+ }
+
+ // we're always verbose on failure, so from this point on we're guaranteed to be verbose
+
+ for _, checkOut := range parts {
+ switch {
+ case checkOut.excluded:
+ fmt.Fprintf(resp, "[+]%s excluded: ok\n", checkOut.name)
+ case checkOut.healthy:
+ fmt.Fprintf(resp, "[+]%s ok\n", checkOut.name)
+ default:
+ // don't include the error since this endpoint is public. If someone wants more detail
+ // they should have explicit permission to the detailed checks.
+ fmt.Fprintf(resp, "[-]%s failed: reason withheld\n", checkOut.name)
+ }
+ }
+
+ if unknownExcludes.Len() > 0 {
+ fmt.Fprintf(resp, "warn: some health checks cannot be excluded: no matches for %s\n", formatQuoted(unknownExcludes.List()...))
+ }
+
+ if failed {
+ log.Info("healthz check failed", "statuses", parts)
+ fmt.Fprintf(resp, "healthz check failed\n")
+ } else {
+ fmt.Fprint(resp, "healthz check passed\n")
+ }
+}
+
+func (h *Handler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
+ // clean up the request (duplicating the internal logic of http.ServeMux a bit)
+ // clean up the path a bit
+ reqPath := req.URL.Path
+ if reqPath == "" || reqPath[0] != '/' {
+ reqPath = "/" + reqPath
+ }
+ // path.Clean removes the trailing slash except for root for us
+ // (which is fine, since we're only serving one layer of sub-paths)
+ reqPath = path.Clean(reqPath)
+
+ // either serve the root endpoint...
+ if reqPath == "/" {
+ h.serveAggregated(resp, req)
+ return
+ }
+
+ // ...the default check (if nothing else is present)...
+ if len(h.Checks) == 0 && reqPath[1:] == "ping" {
+ CheckHandler{Checker: Ping}.ServeHTTP(resp, req)
+ return
+ }
+
+ // ...or an individual checker
+ checkName := reqPath[1:] // ignore the leading slash
+ checker, known := h.Checks[checkName]
+ if !known {
+ http.NotFoundHandler().ServeHTTP(resp, req)
+ return
+ }
+
+ CheckHandler{Checker: checker}.ServeHTTP(resp, req)
+}
+
+// CheckHandler is an http.Handler that serves a health check endpoint at the root path,
+// based on its checker.
+type CheckHandler struct {
+ Checker
+}
+
+func (h CheckHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
+ if err := h.Checker(req); err != nil {
+ http.Error(resp, fmt.Sprintf("internal server error: %v", err), http.StatusInternalServerError)
+ } else {
+ fmt.Fprint(resp, "ok")
+ }
+}
+
+// Checker knows how to perform a health check.
+type Checker func(req *http.Request) error
+
+// Ping returns true automatically when checked.
+var Ping Checker = func(_ *http.Request) error { return nil }
+
+// getExcludedChecks extracts the health check names to be excluded from the query param.
+func getExcludedChecks(r *http.Request) sets.String {
+ checks, found := r.URL.Query()["exclude"]
+ if found {
+ return sets.NewString(checks...)
+ }
+ return sets.NewString()
+}
+
+// formatQuoted returns a formatted string of the health check names,
+// preserving the order passed in.
+func formatQuoted(names ...string) string {
+ quoted := make([]string, 0, len(names))
+ for _, name := range names {
+ quoted = append(quoted, fmt.Sprintf("%q", name))
+ }
+ return strings.Join(quoted, ",")
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/controller.go b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/controller.go
new file mode 100644
index 000000000..1f4712d8b
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/controller.go
@@ -0,0 +1,351 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package controller
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "sync"
+ "time"
+
+ "github.com/go-logr/logr"
+ utilruntime "k8s.io/apimachinery/pkg/util/runtime"
+ "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/runtime/inject"
+ "sigs.k8s.io/controller-runtime/pkg/source"
+)
+
+var _ inject.Injector = &Controller{}
+
+// Controller implements controller.Controller.
+type Controller struct {
+ // Name is used to uniquely identify a Controller in tracing, logging and monitoring. Name is required.
+ Name string
+
+ // MaxConcurrentReconciles is the maximum number of concurrent Reconciles which can be run. Defaults to 1.
+ MaxConcurrentReconciles int
+
+ // 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
+
+ // 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
+ // leads to goroutine leaks if something calls controller.New repeatedly.
+ MakeQueue func() workqueue.RateLimitingInterface
+
+ // Queue is an listeningQueue that listens for events from Informers and adds object keys to
+ // the Queue for processing
+ Queue workqueue.RateLimitingInterface
+
+ // SetFields is used to inject dependencies into other objects such as Sources, EventHandlers and Predicates
+ // Deprecated: the caller should handle injected fields itself.
+ SetFields func(i interface{}) error
+
+ // mu is used to synchronize Controller setup
+ mu sync.Mutex
+
+ // Started is true if the Controller has been Started
+ Started bool
+
+ // ctx is the context that was passed to Start() and used when starting watches.
+ //
+ // According to the docs, contexts should not be stored in a struct: https://golang.org/pkg/context,
+ // while we usually always strive to follow best practices, we consider this a legacy case and it should
+ // undergo a major refactoring and redesign to allow for context to not be stored in a struct.
+ ctx context.Context
+
+ // CacheSyncTimeout refers to the time limit set on waiting for cache to sync
+ // Defaults to 2 minutes if not set.
+ CacheSyncTimeout time.Duration
+
+ // startWatches maintains a list of sources, handlers, and predicates to start when the controller is started.
+ startWatches []watchDescription
+
+ // Log is used to log messages to users during reconciliation, or for example when a watch is started.
+ Log logr.Logger
+
+ // RecoverPanic indicates whether the panic caused by reconcile should be recovered.
+ RecoverPanic 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) {
+ if c.RecoverPanic {
+ defer func() {
+ if r := recover(); r != nil {
+ for _, fn := range utilruntime.PanicHandlers {
+ fn(r)
+ }
+ err = fmt.Errorf("panic: %v [recovered]", r)
+ }
+ }()
+ }
+ log := c.Log.WithValues("name", req.Name, "namespace", req.Namespace)
+ ctx = logf.IntoContext(ctx, log)
+ return c.Do.Reconcile(ctx, req)
+}
+
+// Watch implements controller.Controller.
+func (c *Controller) Watch(src source.Source, evthdler handler.EventHandler, prct ...predicate.Predicate) error {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+
+ // Inject Cache into arguments
+ if err := c.SetFields(src); err != nil {
+ return err
+ }
+ if err := c.SetFields(evthdler); err != nil {
+ return err
+ }
+ for _, pr := range prct {
+ if err := c.SetFields(pr); err != nil {
+ return err
+ }
+ }
+
+ // Controller hasn't started yet, store the watches locally and return.
+ //
+ // These watches are going to be held on the controller struct until the manager or user calls Start(...).
+ if !c.Started {
+ c.startWatches = append(c.startWatches, watchDescription{src: src, handler: evthdler, predicates: prct})
+ return nil
+ }
+
+ c.Log.Info("Starting EventSource", "source", src)
+ return src.Start(c.ctx, evthdler, c.Queue, prct...)
+}
+
+// Start implements controller.Controller.
+func (c *Controller) 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()
+ if c.Started {
+ return errors.New("controller was started more than once. This is likely to be caused by being added to a manager multiple times")
+ }
+
+ c.initMetrics()
+
+ // Set the internal context.
+ c.ctx = ctx
+
+ c.Queue = c.MakeQueue()
+ go func() {
+ <-ctx.Done()
+ c.Queue.ShutDown()
+ }()
+
+ wg := &sync.WaitGroup{}
+ err := func() error {
+ defer c.mu.Unlock()
+
+ // TODO(pwittrock): Reconsider HandleCrash
+ defer utilruntime.HandleCrash()
+
+ // NB(directxman12): launch the sources *before* trying to wait for the
+ // caches to sync so that they have a chance to register their intendeded
+ // caches.
+ for _, watch := range c.startWatches {
+ c.Log.Info("Starting EventSource", "source", fmt.Sprintf("%s", watch.src))
+
+ if err := watch.src.Start(ctx, watch.handler, c.Queue, watch.predicates...); err != nil {
+ return err
+ }
+ }
+
+ // Start the SharedIndexInformer factories to begin populating the SharedIndexInformer caches
+ c.Log.Info("Starting Controller")
+
+ for _, watch := range c.startWatches {
+ syncingSource, ok := watch.src.(source.SyncingSource)
+ if !ok {
+ continue
+ }
+
+ if err := func() error {
+ // use a context with timeout for launching sources and syncing caches.
+ sourceStartCtx, cancel := context.WithTimeout(ctx, c.CacheSyncTimeout)
+ defer cancel()
+
+ // WaitForSync waits for a definitive timeout, and returns if there
+ // is an error or a timeout
+ if err := syncingSource.WaitForSync(sourceStartCtx); err != nil {
+ err := fmt.Errorf("failed to wait for %s caches to sync: %w", c.Name, err)
+ c.Log.Error(err, "Could not wait for Cache to sync")
+ return err
+ }
+
+ return nil
+ }(); err != nil {
+ return err
+ }
+ }
+
+ // All the watches have been started, we can reset the local slice.
+ //
+ // We should never hold watches more than necessary, each watch source can hold a backing cache,
+ // which won't be garbage collected if we hold a reference to it.
+ c.startWatches = nil
+
+ // Launch workers to process resources
+ c.Log.Info("Starting workers", "worker count", c.MaxConcurrentReconciles)
+ wg.Add(c.MaxConcurrentReconciles)
+ for i := 0; i < c.MaxConcurrentReconciles; i++ {
+ go func() {
+ defer wg.Done()
+ // Run a worker thread that just dequeues items, processes them, and marks them done.
+ // It enforces that the reconcileHandler is never invoked concurrently with the same object.
+ for c.processNextWorkItem(ctx) {
+ }
+ }()
+ }
+
+ c.Started = true
+ return nil
+ }()
+ if err != nil {
+ return err
+ }
+
+ <-ctx.Done()
+ c.Log.Info("Shutdown signal received, waiting for all workers to finish")
+ wg.Wait()
+ c.Log.Info("All workers finished")
+ return nil
+}
+
+// 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 {
+ obj, shutdown := c.Queue.Get()
+ if shutdown {
+ // Stop working
+ return false
+ }
+
+ // We call Done here so the workqueue knows we have finished
+ // processing this item. We also must remember to call Forget if we
+ // do not want this work item being re-queued. For example, we do
+ // not call Forget if a transient error occurs, instead the item is
+ // put back on the workqueue and attempted again after a back-off
+ // period.
+ defer c.Queue.Done(obj)
+
+ ctrlmetrics.ActiveWorkers.WithLabelValues(c.Name).Add(1)
+ defer ctrlmetrics.ActiveWorkers.WithLabelValues(c.Name).Add(-1)
+
+ c.reconcileHandler(ctx, obj)
+ return true
+}
+
+const (
+ labelError = "error"
+ labelRequeueAfter = "requeue_after"
+ labelRequeue = "requeue"
+ labelSuccess = "success"
+)
+
+func (c *Controller) initMetrics() {
+ ctrlmetrics.ActiveWorkers.WithLabelValues(c.Name).Set(0)
+ ctrlmetrics.ReconcileErrors.WithLabelValues(c.Name).Add(0)
+ 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.WorkerCount.WithLabelValues(c.Name).Set(float64(c.MaxConcurrentReconciles))
+}
+
+func (c *Controller) reconcileHandler(ctx context.Context, obj interface{}) {
+ // Update metrics after processing each item
+ reconcileStartTS := time.Now()
+ defer func() {
+ c.updateMetrics(time.Since(reconcileStartTS))
+ }()
+
+ // Make sure that the 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.Log.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.Log.WithValues("name", req.Name, "namespace", req.Namespace)
+ ctx = logf.IntoContext(ctx, log)
+
+ // RunInformersAndControllers the syncHandler, passing it the Namespace/Name string of the
+ // resource to be synced.
+ result, err := c.Reconcile(ctx, req)
+ switch {
+ case err != nil:
+ c.Queue.AddRateLimited(req)
+ ctrlmetrics.ReconcileErrors.WithLabelValues(c.Name).Inc()
+ ctrlmetrics.ReconcileTotal.WithLabelValues(c.Name, labelError).Inc()
+ log.Error(err, "Reconciler error")
+ case result.RequeueAfter > 0:
+ // The result.RequeueAfter request will be lost, if it is returned
+ // 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.AddAfter(req, result.RequeueAfter)
+ ctrlmetrics.ReconcileTotal.WithLabelValues(c.Name, labelRequeueAfter).Inc()
+ case result.Requeue:
+ c.Queue.AddRateLimited(req)
+ ctrlmetrics.ReconcileTotal.WithLabelValues(c.Name, labelRequeue).Inc()
+ default:
+ // Finally, if no error occurs we Forget this item so it does not
+ // get queued again until another change happens.
+ c.Queue.Forget(obj)
+ ctrlmetrics.ReconcileTotal.WithLabelValues(c.Name, labelSuccess).Inc()
+ }
+}
+
+// GetLogger returns this controller's logger.
+func (c *Controller) GetLogger() logr.Logger {
+ return c.Log
+}
+
+// InjectFunc implement SetFields.Injector.
+func (c *Controller) InjectFunc(f inject.Func) error {
+ c.SetFields = f
+ return nil
+}
+
+// updateMetrics updates prometheus metrics within the controller.
+func (c *Controller) updateMetrics(reconcileTime time.Duration) {
+ ctrlmetrics.ReconcileTime.WithLabelValues(c.Name).Observe(reconcileTime.Seconds())
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/metrics/metrics.go b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/metrics/metrics.go
new file mode 100644
index 000000000..baec66927
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/metrics/metrics.go
@@ -0,0 +1,78 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package metrics
+
+import (
+ "github.com/prometheus/client_golang/prometheus"
+ "github.com/prometheus/client_golang/prometheus/collectors"
+ "sigs.k8s.io/controller-runtime/pkg/metrics"
+)
+
+var (
+ // ReconcileTotal is a prometheus counter metrics which holds the total
+ // number of reconciliations per controller. It has two labels. controller label refers
+ // to the controller name and result label refers to the reconcile result i.e
+ // success, error, requeue, requeue_after.
+ ReconcileTotal = prometheus.NewCounterVec(prometheus.CounterOpts{
+ Name: "controller_runtime_reconcile_total",
+ Help: "Total number of reconciliations per controller",
+ }, []string{"controller", "result"})
+
+ // ReconcileErrors is a prometheus counter metrics which holds the total
+ // number of errors from the Reconciler.
+ ReconcileErrors = prometheus.NewCounterVec(prometheus.CounterOpts{
+ Name: "controller_runtime_reconcile_errors_total",
+ Help: "Total number of reconciliation errors per controller",
+ }, []string{"controller"})
+
+ // ReconcileTime is a prometheus metric which keeps track of the duration
+ // of reconciliations.
+ ReconcileTime = prometheus.NewHistogramVec(prometheus.HistogramOpts{
+ Name: "controller_runtime_reconcile_time_seconds",
+ Help: "Length of time per reconciliation per controller",
+ Buckets: []float64{0.005, 0.01, 0.025, 0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0,
+ 1.25, 1.5, 1.75, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5, 6, 7, 8, 9, 10, 15, 20, 25, 30, 40, 50, 60},
+ }, []string{"controller"})
+
+ // WorkerCount is a prometheus metric which holds the number of
+ // concurrent reconciles per controller.
+ WorkerCount = prometheus.NewGaugeVec(prometheus.GaugeOpts{
+ Name: "controller_runtime_max_concurrent_reconciles",
+ Help: "Maximum number of concurrent reconciles per controller",
+ }, []string{"controller"})
+
+ // ActiveWorkers is a prometheus metric which holds the number
+ // of active workers per controller.
+ ActiveWorkers = prometheus.NewGaugeVec(prometheus.GaugeOpts{
+ Name: "controller_runtime_active_workers",
+ Help: "Number of currently used workers per controller",
+ }, []string{"controller"})
+)
+
+func init() {
+ metrics.Registry.MustRegister(
+ ReconcileTotal,
+ ReconcileErrors,
+ ReconcileTime,
+ WorkerCount,
+ ActiveWorkers,
+ // expose process metrics like CPU, Memory, file descriptor usage etc.
+ collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}),
+ // expose Go runtime metrics like GC stats, memory stats etc.
+ collectors.NewGoCollector(),
+ )
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/internal/log/log.go b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/log/log.go
new file mode 100644
index 000000000..d91a0ca50
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/log/log.go
@@ -0,0 +1,32 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package log
+
+import (
+ "github.com/go-logr/logr"
+
+ "sigs.k8s.io/controller-runtime/pkg/log"
+)
+
+var (
+ // RuntimeLog is a base parent logger for use inside controller-runtime.
+ RuntimeLog logr.Logger
+)
+
+func init() {
+ RuntimeLog = log.Log.WithName("controller-runtime")
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/internal/objectutil/objectutil.go b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/objectutil/objectutil.go
new file mode 100644
index 000000000..7057f3dbe
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/objectutil/objectutil.go
@@ -0,0 +1,78 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package objectutil
+
+import (
+ "errors"
+ "fmt"
+
+ apimeta "k8s.io/apimachinery/pkg/api/meta"
+ "k8s.io/apimachinery/pkg/labels"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "sigs.k8s.io/controller-runtime/pkg/client/apiutil"
+)
+
+// FilterWithLabels returns a copy of the items in objs matching labelSel.
+func FilterWithLabels(objs []runtime.Object, labelSel labels.Selector) ([]runtime.Object, error) {
+ outItems := make([]runtime.Object, 0, len(objs))
+ for _, obj := range objs {
+ meta, err := apimeta.Accessor(obj)
+ if err != nil {
+ return nil, err
+ }
+ if labelSel != nil {
+ lbls := labels.Set(meta.GetLabels())
+ if !labelSel.Matches(lbls) {
+ continue
+ }
+ }
+ outItems = append(outItems, obj.DeepCopyObject())
+ }
+ return outItems, nil
+}
+
+// IsAPINamespaced returns true if the object is namespace scoped.
+// For unstructured objects the gvk is found from the object itself.
+func IsAPINamespaced(obj runtime.Object, scheme *runtime.Scheme, restmapper apimeta.RESTMapper) (bool, error) {
+ gvk, err := apiutil.GVKForObject(obj, scheme)
+ if err != nil {
+ return false, err
+ }
+
+ return IsAPINamespacedWithGVK(gvk, scheme, restmapper)
+}
+
+// IsAPINamespacedWithGVK returns true if the object having the provided
+// GVK is namespace scoped.
+func IsAPINamespacedWithGVK(gk schema.GroupVersionKind, scheme *runtime.Scheme, restmapper apimeta.RESTMapper) (bool, error) {
+ restmapping, err := restmapper.RESTMapping(schema.GroupKind{Group: gk.Group, Kind: gk.Kind})
+ if err != nil {
+ return false, fmt.Errorf("failed to get restmapping: %w", err)
+ }
+
+ scope := restmapping.Scope.Name()
+
+ if scope == "" {
+ return false, errors.New("scope cannot be identified, empty scope returned")
+ }
+
+ if scope != apimeta.RESTScopeNameRoot {
+ return true, nil
+ }
+ return false, nil
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/internal/recorder/recorder.go b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/recorder/recorder.go
new file mode 100644
index 000000000..46cc1714b
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/recorder/recorder.go
@@ -0,0 +1,176 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package recorder
+
+import (
+ "context"
+ "fmt"
+ "sync"
+
+ "github.com/go-logr/logr"
+ corev1 "k8s.io/api/core/v1"
+ "k8s.io/apimachinery/pkg/runtime"
+ corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
+ "k8s.io/client-go/rest"
+ "k8s.io/client-go/tools/record"
+)
+
+// EventBroadcasterProducer makes an event broadcaster, returning
+// whether or not the broadcaster should be stopped with the Provider,
+// or not (e.g. if it's shared, it shouldn't be stopped with the Provider).
+type EventBroadcasterProducer func() (caster record.EventBroadcaster, stopWithProvider bool)
+
+// Provider is a recorder.Provider that records events to the k8s API server
+// and to a logr Logger.
+type Provider struct {
+ lock sync.RWMutex
+ stopped bool
+
+ // scheme to specify when creating a recorder
+ scheme *runtime.Scheme
+ // logger is the logger to use when logging diagnostic event info
+ logger logr.Logger
+ evtClient corev1client.EventInterface
+ makeBroadcaster EventBroadcasterProducer
+
+ broadcasterOnce sync.Once
+ broadcaster record.EventBroadcaster
+ stopBroadcaster bool
+}
+
+// NB(directxman12): this manually implements Stop instead of Being a runnable because we need to
+// stop it *after* everything else shuts down, otherwise we'll cause panics as the leader election
+// code finishes up and tries to continue emitting events.
+
+// Stop attempts to stop this provider, stopping the underlying broadcaster
+// if the broadcaster asked to be stopped. It kinda tries to honor the given
+// context, but the underlying broadcaster has an indefinite wait that doesn't
+// return until all queued events are flushed, so this may end up just returning
+// before the underlying wait has finished instead of cancelling the wait.
+// This is Very Frustratingâ„¢.
+func (p *Provider) Stop(shutdownCtx context.Context) {
+ doneCh := make(chan struct{})
+
+ go func() {
+ // technically, this could start the broadcaster, but practically, it's
+ // almost certainly already been started (e.g. by leader election). We
+ // need to invoke this to ensure that we don't inadvertently race with
+ // an invocation of getBroadcaster.
+ broadcaster := p.getBroadcaster()
+ if p.stopBroadcaster {
+ p.lock.Lock()
+ broadcaster.Shutdown()
+ p.stopped = true
+ p.lock.Unlock()
+ }
+ close(doneCh)
+ }()
+
+ select {
+ case <-shutdownCtx.Done():
+ case <-doneCh:
+ }
+}
+
+// getBroadcaster ensures that a broadcaster is started for this
+// provider, and returns it. It's threadsafe.
+func (p *Provider) getBroadcaster() record.EventBroadcaster {
+ // NB(directxman12): this can technically still leak if something calls
+ // "getBroadcaster" (i.e. Emits an Event) but never calls Start, but if we
+ // create the broadcaster in start, we could race with other things that
+ // are started at the same time & want to emit events. The alternative is
+ // silently swallowing events and more locking, but that seems suboptimal.
+
+ p.broadcasterOnce.Do(func() {
+ broadcaster, stop := p.makeBroadcaster()
+ broadcaster.StartRecordingToSink(&corev1client.EventSinkImpl{Interface: p.evtClient})
+ broadcaster.StartEventWatcher(
+ func(e *corev1.Event) {
+ p.logger.V(1).Info(e.Type, "object", e.InvolvedObject, "reason", e.Reason, "message", e.Message)
+ })
+ p.broadcaster = broadcaster
+ p.stopBroadcaster = stop
+ })
+
+ return p.broadcaster
+}
+
+// NewProvider create a new Provider instance.
+func NewProvider(config *rest.Config, scheme *runtime.Scheme, logger logr.Logger, makeBroadcaster EventBroadcasterProducer) (*Provider, error) {
+ corev1Client, err := corev1client.NewForConfig(config)
+ if err != nil {
+ return nil, fmt.Errorf("failed to init client: %w", err)
+ }
+
+ p := &Provider{scheme: scheme, logger: logger, makeBroadcaster: makeBroadcaster, evtClient: corev1Client.Events("")}
+ return p, nil
+}
+
+// GetEventRecorderFor returns an event recorder that broadcasts to this provider's
+// broadcaster. All events will be associated with a component of the given name.
+func (p *Provider) GetEventRecorderFor(name string) record.EventRecorder {
+ return &lazyRecorder{
+ prov: p,
+ name: name,
+ }
+}
+
+// lazyRecorder is a recorder that doesn't actually instantiate any underlying
+// recorder until the first event is emitted.
+type lazyRecorder struct {
+ prov *Provider
+ name string
+
+ recOnce sync.Once
+ rec record.EventRecorder
+}
+
+// ensureRecording ensures that a concrete recorder is populated for this recorder.
+func (l *lazyRecorder) ensureRecording() {
+ l.recOnce.Do(func() {
+ broadcaster := l.prov.getBroadcaster()
+ l.rec = broadcaster.NewRecorder(l.prov.scheme, corev1.EventSource{Component: l.name})
+ })
+}
+
+func (l *lazyRecorder) Event(object runtime.Object, eventtype, reason, message string) {
+ l.ensureRecording()
+
+ l.prov.lock.RLock()
+ if !l.prov.stopped {
+ l.rec.Event(object, eventtype, reason, message)
+ }
+ l.prov.lock.RUnlock()
+}
+func (l *lazyRecorder) Eventf(object runtime.Object, eventtype, reason, messageFmt string, args ...interface{}) {
+ l.ensureRecording()
+
+ l.prov.lock.RLock()
+ if !l.prov.stopped {
+ l.rec.Eventf(object, eventtype, reason, messageFmt, args...)
+ }
+ l.prov.lock.RUnlock()
+}
+func (l *lazyRecorder) AnnotatedEventf(object runtime.Object, annotations map[string]string, eventtype, reason, messageFmt string, args ...interface{}) {
+ l.ensureRecording()
+
+ l.prov.lock.RLock()
+ if !l.prov.stopped {
+ l.rec.AnnotatedEventf(object, annotations, eventtype, reason, messageFmt, args...)
+ }
+ l.prov.lock.RUnlock()
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/leaderelection/doc.go b/vendor/sigs.k8s.io/controller-runtime/pkg/leaderelection/doc.go
new file mode 100644
index 000000000..37a9aefab
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/leaderelection/doc.go
@@ -0,0 +1,24 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+/*
+Package leaderelection contains a constructor for a leader election resource lock.
+This is used to ensure that multiple copies of a controller manager can be run with
+only one active set of controllers, for active-passive HA.
+
+It uses built-in Kubernetes leader election APIs.
+*/
+package leaderelection
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/leaderelection/leader_election.go b/vendor/sigs.k8s.io/controller-runtime/pkg/leaderelection/leader_election.go
new file mode 100644
index 000000000..3dedd462f
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/leaderelection/leader_election.go
@@ -0,0 +1,126 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package leaderelection
+
+import (
+ "errors"
+ "fmt"
+ "io/ioutil"
+ "os"
+
+ "k8s.io/apimachinery/pkg/util/uuid"
+ coordinationv1client "k8s.io/client-go/kubernetes/typed/coordination/v1"
+ corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
+ "k8s.io/client-go/rest"
+ "k8s.io/client-go/tools/leaderelection/resourcelock"
+ "sigs.k8s.io/controller-runtime/pkg/recorder"
+)
+
+const inClusterNamespacePath = "/var/run/secrets/kubernetes.io/serviceaccount/namespace"
+
+// Options provides the required configuration to create a new resource lock.
+type Options struct {
+ // LeaderElection determines whether or not to use leader election when
+ // starting the manager.
+ LeaderElection bool
+
+ // LeaderElectionResourceLock determines which resource lock to use for leader election,
+ // defaults to "configmapsleases".
+ LeaderElectionResourceLock string
+
+ // LeaderElectionNamespace determines the namespace in which the leader
+ // election resource will be created.
+ LeaderElectionNamespace string
+
+ // LeaderElectionID determines the name of the resource that leader election
+ // will use for holding the leader lock.
+ LeaderElectionID string
+}
+
+// NewResourceLock creates a new resource lock for use in a leader election loop.
+func NewResourceLock(config *rest.Config, recorderProvider recorder.Provider, options Options) (resourcelock.Interface, error) {
+ if !options.LeaderElection {
+ return nil, nil
+ }
+
+ // Default resource lock to "configmapsleases". We must keep this default until we are sure all controller-runtime
+ // users have upgraded from the original default ConfigMap lock to a controller-runtime version that has this new
+ // default. Many users of controller-runtime skip versions, so we should be extremely conservative here.
+ if options.LeaderElectionResourceLock == "" {
+ options.LeaderElectionResourceLock = resourcelock.ConfigMapsLeasesResourceLock
+ }
+
+ // LeaderElectionID must be provided to prevent clashes
+ if options.LeaderElectionID == "" {
+ return nil, errors.New("LeaderElectionID must be configured")
+ }
+
+ // Default the namespace (if running in cluster)
+ if options.LeaderElectionNamespace == "" {
+ var err error
+ options.LeaderElectionNamespace, err = getInClusterNamespace()
+ if err != nil {
+ return nil, fmt.Errorf("unable to find leader election namespace: %w", err)
+ }
+ }
+
+ // Leader id, needs to be unique
+ id, err := os.Hostname()
+ if err != nil {
+ return nil, err
+ }
+ id = id + "_" + string(uuid.NewUUID())
+
+ // Construct clients for leader election
+ rest.AddUserAgent(config, "leader-election")
+ corev1Client, err := corev1client.NewForConfig(config)
+ if err != nil {
+ return nil, err
+ }
+
+ coordinationClient, err := coordinationv1client.NewForConfig(config)
+ if err != nil {
+ return nil, err
+ }
+
+ return resourcelock.New(options.LeaderElectionResourceLock,
+ options.LeaderElectionNamespace,
+ options.LeaderElectionID,
+ corev1Client,
+ coordinationClient,
+ resourcelock.ResourceLockConfig{
+ Identity: id,
+ EventRecorder: recorderProvider.GetEventRecorderFor(id),
+ })
+}
+
+func getInClusterNamespace() (string, error) {
+ // Check whether the namespace file exists.
+ // If not, we are not running in cluster so can't guess the namespace.
+ if _, err := os.Stat(inClusterNamespacePath); os.IsNotExist(err) {
+ return "", fmt.Errorf("not running in-cluster, please specify LeaderElectionNamespace")
+ } else if err != nil {
+ return "", fmt.Errorf("error checking namespace file: %w", err)
+ }
+
+ // Load the namespace file and return its content
+ namespace, err := ioutil.ReadFile(inClusterNamespacePath)
+ if err != nil {
+ return "", fmt.Errorf("error reading namespace file: %w", err)
+ }
+ return string(namespace), nil
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/log/deleg.go b/vendor/sigs.k8s.io/controller-runtime/pkg/log/deleg.go
new file mode 100644
index 000000000..9d73947da
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/log/deleg.go
@@ -0,0 +1,216 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package log
+
+import (
+ "sync"
+
+ "github.com/go-logr/logr"
+)
+
+// loggerPromise knows how to populate a concrete logr.Logger
+// with options, given an actual base logger later on down the line.
+type loggerPromise struct {
+ logger *DelegatingLogger
+ childPromises []*loggerPromise
+ promisesLock sync.Mutex
+
+ name *string
+ tags []interface{}
+ level int
+}
+
+func (p *loggerPromise) WithName(l *DelegatingLogger, name string) *loggerPromise {
+ res := &loggerPromise{
+ logger: l,
+ name: &name,
+ promisesLock: sync.Mutex{},
+ }
+
+ p.promisesLock.Lock()
+ defer p.promisesLock.Unlock()
+ p.childPromises = append(p.childPromises, res)
+ return res
+}
+
+// WithValues provides a new Logger with the tags appended.
+func (p *loggerPromise) WithValues(l *DelegatingLogger, tags ...interface{}) *loggerPromise {
+ res := &loggerPromise{
+ logger: l,
+ tags: tags,
+ promisesLock: sync.Mutex{},
+ }
+
+ p.promisesLock.Lock()
+ defer p.promisesLock.Unlock()
+ p.childPromises = append(p.childPromises, res)
+ return res
+}
+
+func (p *loggerPromise) V(l *DelegatingLogger, level int) *loggerPromise {
+ res := &loggerPromise{
+ logger: l,
+ level: level,
+ promisesLock: sync.Mutex{},
+ }
+
+ p.promisesLock.Lock()
+ defer p.promisesLock.Unlock()
+ p.childPromises = append(p.childPromises, res)
+ return res
+}
+
+// Fulfill instantiates the Logger with the provided logger.
+func (p *loggerPromise) Fulfill(parentLogger logr.Logger) {
+ logger := logr.WithCallDepth(parentLogger, 1)
+ if p.name != nil {
+ logger = logger.WithName(*p.name)
+ }
+
+ if p.tags != nil {
+ logger = logger.WithValues(p.tags...)
+ }
+ if p.level != 0 {
+ logger = logger.V(p.level)
+ }
+
+ p.logger.lock.Lock()
+ p.logger.logger = logger
+ p.logger.promise = nil
+ p.logger.lock.Unlock()
+
+ for _, childPromise := range p.childPromises {
+ childPromise.Fulfill(logger)
+ }
+}
+
+// DelegatingLogger is a logr.Logger that delegates to another logr.Logger.
+// If the underlying promise is not nil, it registers calls to sub-loggers with
+// the logging factory to be populated later, and returns a new delegating
+// logger. It expects to have *some* logr.Logger set at all times (generally
+// a no-op logger before the promises are fulfilled).
+type DelegatingLogger struct {
+ lock sync.RWMutex
+ logger logr.Logger
+ promise *loggerPromise
+}
+
+// Enabled tests whether this Logger is enabled. For example, commandline
+// flags might be used to set the logging verbosity and disable some info
+// logs.
+func (l *DelegatingLogger) Enabled() bool {
+ l.lock.RLock()
+ defer l.lock.RUnlock()
+ return l.logger.Enabled()
+}
+
+// Info logs a non-error message with the given key/value pairs as context.
+//
+// The msg argument should be used to add some constant description to
+// the log line. The key/value pairs can then be used to add additional
+// variable information. The key/value pairs should alternate string
+// keys and arbitrary values.
+func (l *DelegatingLogger) Info(msg string, keysAndValues ...interface{}) {
+ l.lock.RLock()
+ defer l.lock.RUnlock()
+ l.logger.Info(msg, keysAndValues...)
+}
+
+// Error logs an error, with the given message and key/value pairs as context.
+// It functions similarly to calling Info with the "error" named value, but may
+// have unique behavior, and should be preferred for logging errors (see the
+// package documentations for more information).
+//
+// The msg field should be used to add context to any underlying error,
+// while the err field should be used to attach the actual error that
+// triggered this log line, if present.
+func (l *DelegatingLogger) Error(err error, msg string, keysAndValues ...interface{}) {
+ l.lock.RLock()
+ defer l.lock.RUnlock()
+ l.logger.Error(err, msg, keysAndValues...)
+}
+
+// V returns an Logger value for a specific verbosity level, relative to
+// this Logger. In other words, V values are additive. V higher verbosity
+// level means a log message is less important. It's illegal to pass a log
+// level less than zero.
+func (l *DelegatingLogger) V(level int) logr.Logger {
+ l.lock.RLock()
+ defer l.lock.RUnlock()
+
+ if l.promise == nil {
+ return l.logger.V(level)
+ }
+
+ res := &DelegatingLogger{logger: l.logger}
+ promise := l.promise.V(res, level)
+ res.promise = promise
+
+ return res
+}
+
+// WithName provides a new Logger with the name appended.
+func (l *DelegatingLogger) WithName(name string) logr.Logger {
+ l.lock.RLock()
+ defer l.lock.RUnlock()
+
+ if l.promise == nil {
+ return l.logger.WithName(name)
+ }
+
+ res := &DelegatingLogger{logger: l.logger}
+ promise := l.promise.WithName(res, name)
+ res.promise = promise
+
+ return res
+}
+
+// WithValues provides a new Logger with the tags appended.
+func (l *DelegatingLogger) WithValues(tags ...interface{}) logr.Logger {
+ l.lock.RLock()
+ defer l.lock.RUnlock()
+
+ if l.promise == nil {
+ return l.logger.WithValues(tags...)
+ }
+
+ res := &DelegatingLogger{logger: l.logger}
+ promise := l.promise.WithValues(res, tags...)
+ res.promise = promise
+
+ return res
+}
+
+// Fulfill switches the logger over to use the actual logger
+// provided, instead of the temporary initial one, if this method
+// has not been previously called.
+func (l *DelegatingLogger) Fulfill(actual logr.Logger) {
+ if l.promise != nil {
+ l.promise.Fulfill(actual)
+ }
+}
+
+// NewDelegatingLogger constructs a new DelegatingLogger which uses
+// the given logger before it's promise is fulfilled.
+func NewDelegatingLogger(initial logr.Logger) *DelegatingLogger {
+ l := &DelegatingLogger{
+ logger: initial,
+ promise: &loggerPromise{promisesLock: sync.Mutex{}},
+ }
+ l.promise.logger = l
+ return l
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/log/log.go b/vendor/sigs.k8s.io/controller-runtime/pkg/log/log.go
new file mode 100644
index 000000000..229ac7ec3
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/log/log.go
@@ -0,0 +1,99 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Package log contains utilities for fetching a new logger
+// when one is not already available.
+//
+// The Log Handle
+//
+// This package contains a root logr.Logger Log. It may be used to
+// get a handle to whatever the root logging implementation is. By
+// default, no implementation exists, and the handle returns "promises"
+// to loggers. When the implementation is set using SetLogger, these
+// "promises" will be converted over to real loggers.
+//
+// Logr
+//
+// All logging in controller-runtime is structured, using a set of interfaces
+// defined by a package called logr
+// (https://godoc.org/github.com/go-logr/logr). The sub-package zap provides
+// helpers for setting up logr backed by Zap (go.uber.org/zap).
+package log
+
+import (
+ "context"
+ "sync"
+ "time"
+
+ "github.com/go-logr/logr"
+)
+
+// SetLogger sets a concrete logging implementation for all deferred Loggers.
+func SetLogger(l logr.Logger) {
+ loggerWasSetLock.Lock()
+ defer loggerWasSetLock.Unlock()
+
+ loggerWasSet = true
+ Log.Fulfill(l)
+}
+
+// It is safe to assume that if this wasn't set within the first 30 seconds of a binaries
+// lifetime, it will never get set. The DelegatingLogger causes a high number of memory
+// allocations when not given an actual Logger, so we set a NullLogger to avoid that.
+//
+// We need to keep the DelegatingLogger because we have various inits() that get a logger from
+// here. They will always get executed before any code that imports controller-runtime
+// has a chance to run and hence to set an actual logger.
+func init() {
+ // Init is blocking, so start a new goroutine
+ go func() {
+ time.Sleep(30 * time.Second)
+ loggerWasSetLock.Lock()
+ defer loggerWasSetLock.Unlock()
+ if !loggerWasSet {
+ Log.Fulfill(NullLogger{})
+ }
+ }()
+}
+
+var (
+ loggerWasSetLock sync.Mutex
+ loggerWasSet bool
+)
+
+// Log is the base logger used by kubebuilder. It delegates
+// to another logr.Logger. You *must* call SetLogger to
+// get any actual logging. If SetLogger is not called within
+// the first 30 seconds of a binaries lifetime, it will get
+// set to a NullLogger.
+var Log = NewDelegatingLogger(NullLogger{})
+
+// FromContext returns a logger with predefined values from a context.Context.
+func FromContext(ctx context.Context, keysAndValues ...interface{}) logr.Logger {
+ var log logr.Logger = Log
+ if ctx != nil {
+ if logger := logr.FromContext(ctx); logger != nil {
+ log = logger
+ }
+ }
+ return log.WithValues(keysAndValues...)
+}
+
+// IntoContext takes a context and sets the logger as one of its values.
+// Use FromContext function to retrieve the logger.
+func IntoContext(ctx context.Context, log logr.Logger) context.Context {
+ return logr.NewContext(ctx, log)
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/log/null.go b/vendor/sigs.k8s.io/controller-runtime/pkg/log/null.go
new file mode 100644
index 000000000..09a5a02eb
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/log/null.go
@@ -0,0 +1,60 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package log
+
+import (
+ "github.com/go-logr/logr"
+)
+
+// NB: this is the same as the null logger logr/testing,
+// but avoids accidentally adding the testing flags to
+// all binaries.
+
+// NullLogger is a logr.Logger that does nothing.
+type NullLogger struct{}
+
+var _ logr.Logger = NullLogger{}
+
+// Info implements logr.InfoLogger.
+func (NullLogger) Info(_ string, _ ...interface{}) {
+ // Do nothing.
+}
+
+// Enabled implements logr.InfoLogger.
+func (NullLogger) Enabled() bool {
+ return false
+}
+
+// Error implements logr.Logger.
+func (NullLogger) Error(_ error, _ string, _ ...interface{}) {
+ // Do nothing.
+}
+
+// V implements logr.Logger.
+func (log NullLogger) V(_ int) logr.Logger {
+ return log
+}
+
+// WithName implements logr.Logger.
+func (log NullLogger) WithName(_ string) logr.Logger {
+ return log
+}
+
+// WithValues implements logr.Logger.
+func (log NullLogger) WithValues(_ ...interface{}) logr.Logger {
+ return log
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/log/warning_handler.go b/vendor/sigs.k8s.io/controller-runtime/pkg/log/warning_handler.go
new file mode 100644
index 000000000..3012fdd41
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/log/warning_handler.go
@@ -0,0 +1,76 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package log
+
+import (
+ "sync"
+
+ "github.com/go-logr/logr"
+)
+
+// KubeAPIWarningLoggerOptions controls the behavior
+// of a rest.WarningHandler constructed using NewKubeAPIWarningLogger().
+type KubeAPIWarningLoggerOptions struct {
+ // Deduplicate indicates a given warning message should only be written once.
+ // Setting this to true in a long-running process handling many warnings can
+ // result in increased memory use.
+ Deduplicate bool
+}
+
+// KubeAPIWarningLogger is a wrapper around
+// a provided logr.Logger that implements the
+// rest.WarningHandler interface.
+type KubeAPIWarningLogger struct {
+ // logger is used to log responses with the warning header
+ logger logr.Logger
+ // opts contain options controlling warning output
+ opts KubeAPIWarningLoggerOptions
+ // writtenLock gurads written
+ writtenLock sync.Mutex
+ // used to keep track of already logged messages
+ // and help in de-duplication.
+ written map[string]struct{}
+}
+
+// HandleWarningHeader handles logging for responses from API server that are
+// warnings with code being 299 and uses a logr.Logger for it's logging purposes.
+func (l *KubeAPIWarningLogger) HandleWarningHeader(code int, agent string, message string) {
+ if code != 299 || len(message) == 0 {
+ return
+ }
+
+ if l.opts.Deduplicate {
+ l.writtenLock.Lock()
+ defer l.writtenLock.Unlock()
+
+ if _, alreadyLogged := l.written[message]; alreadyLogged {
+ return
+ }
+ l.written[message] = struct{}{}
+ }
+ l.logger.Info(message)
+}
+
+// NewKubeAPIWarningLogger returns an implementation of rest.WarningHandler that logs warnings
+// with code = 299 to the provided logr.Logger.
+func NewKubeAPIWarningLogger(l logr.Logger, opts KubeAPIWarningLoggerOptions) *KubeAPIWarningLogger {
+ h := &KubeAPIWarningLogger{logger: l, opts: opts}
+ if opts.Deduplicate {
+ h.written = map[string]struct{}{}
+ }
+ return h
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/manager/doc.go b/vendor/sigs.k8s.io/controller-runtime/pkg/manager/doc.go
new file mode 100644
index 000000000..f2976c7f7
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/manager/doc.go
@@ -0,0 +1,21 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+/*
+Package manager is required to create Controllers and provides shared dependencies such as clients, caches, schemes,
+etc. Controllers must be started by calling Manager.Start.
+*/
+package manager
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/manager/internal.go b/vendor/sigs.k8s.io/controller-runtime/pkg/manager/internal.go
new file mode 100644
index 000000000..59794d962
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/manager/internal.go
@@ -0,0 +1,709 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package manager
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "net"
+ "net/http"
+ "sync"
+ "time"
+
+ "github.com/go-logr/logr"
+ "github.com/prometheus/client_golang/prometheus/promhttp"
+ "k8s.io/apimachinery/pkg/api/meta"
+ "k8s.io/apimachinery/pkg/runtime"
+ kerrors "k8s.io/apimachinery/pkg/util/errors"
+ "k8s.io/client-go/rest"
+ "k8s.io/client-go/tools/leaderelection"
+ "k8s.io/client-go/tools/leaderelection/resourcelock"
+ "k8s.io/client-go/tools/record"
+
+ "sigs.k8s.io/controller-runtime/pkg/cache"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/cluster"
+ "sigs.k8s.io/controller-runtime/pkg/config/v1alpha1"
+ "sigs.k8s.io/controller-runtime/pkg/healthz"
+ intrec "sigs.k8s.io/controller-runtime/pkg/internal/recorder"
+ "sigs.k8s.io/controller-runtime/pkg/metrics"
+ "sigs.k8s.io/controller-runtime/pkg/runtime/inject"
+ "sigs.k8s.io/controller-runtime/pkg/webhook"
+)
+
+const (
+ // Values taken from: https://github.com/kubernetes/component-base/blob/master/config/v1alpha1/defaults.go
+ defaultLeaseDuration = 15 * time.Second
+ defaultRenewDeadline = 10 * time.Second
+ defaultRetryPeriod = 2 * time.Second
+ defaultGracefulShutdownPeriod = 30 * time.Second
+
+ defaultReadinessEndpoint = "/readyz"
+ defaultLivenessEndpoint = "/healthz"
+ defaultMetricsEndpoint = "/metrics"
+)
+
+var _ Runnable = &controllerManager{}
+
+type controllerManager struct {
+ // cluster holds a variety of methods to interact with a cluster. Required.
+ cluster cluster.Cluster
+
+ // leaderElectionRunnables is the set of Controllers that the controllerManager injects deps into and Starts.
+ // These Runnables are managed by lead election.
+ leaderElectionRunnables []Runnable
+
+ // nonLeaderElectionRunnables is the set of webhook servers that the controllerManager injects deps into and Starts.
+ // These Runnables will not be blocked by lead election.
+ nonLeaderElectionRunnables []Runnable
+
+ // recorderProvider is used to generate event recorders that will be injected into Controllers
+ // (and EventHandlers, Sources and Predicates).
+ recorderProvider *intrec.Provider
+
+ // resourceLock forms the basis for leader election
+ resourceLock resourcelock.Interface
+
+ // leaderElectionReleaseOnCancel defines if the manager should step back from the leader lease
+ // on shutdown
+ leaderElectionReleaseOnCancel bool
+
+ // metricsListener is used to serve prometheus metrics
+ metricsListener net.Listener
+
+ // metricsExtraHandlers contains extra handlers to register on http server that serves metrics.
+ metricsExtraHandlers map[string]http.Handler
+
+ // healthProbeListener is used to serve liveness probe
+ healthProbeListener net.Listener
+
+ // Readiness probe endpoint name
+ readinessEndpointName string
+
+ // Liveness probe endpoint name
+ livenessEndpointName string
+
+ // Readyz probe handler
+ readyzHandler *healthz.Handler
+
+ // Healthz probe handler
+ healthzHandler *healthz.Handler
+
+ mu sync.Mutex
+ started bool
+ startedLeader bool
+ healthzStarted bool
+ errChan chan error
+
+ // controllerOptions are the global controller options.
+ controllerOptions v1alpha1.ControllerConfigurationSpec
+
+ // Logger is the logger that should be used by this manager.
+ // If none is set, it defaults to log.Log global logger.
+ logger logr.Logger
+
+ // leaderElectionCancel is used to cancel the leader election. It is distinct from internalStopper,
+ // because for safety reasons we need to os.Exit() when we lose the leader election, meaning that
+ // it must be deferred until after gracefulShutdown is done.
+ leaderElectionCancel context.CancelFunc
+
+ // leaderElectionStopped is an internal channel used to signal the stopping procedure that the
+ // LeaderElection.Run(...) function has returned and the shutdown can proceed.
+ leaderElectionStopped chan struct{}
+
+ // stop procedure engaged. In other words, we should not add anything else to the manager
+ stopProcedureEngaged bool
+
+ // elected is closed when this manager becomes the leader of a group of
+ // managers, either because it won a leader election or because no leader
+ // election was configured.
+ elected chan struct{}
+
+ caches []hasCache
+
+ // port is the port that the webhook server serves at.
+ port int
+ // host is the hostname that the webhook server binds to.
+ host string
+ // CertDir is the directory that contains the server key and certificate.
+ // if not set, webhook server would look up the server key and certificate in
+ // {TempDir}/k8s-webhook-server/serving-certs
+ certDir string
+
+ webhookServer *webhook.Server
+ // webhookServerOnce will be called in GetWebhookServer() to optionally initialize
+ // webhookServer if unset, and Add() it to controllerManager.
+ webhookServerOnce sync.Once
+
+ // leaseDuration is the duration that non-leader candidates will
+ // wait to force acquire leadership.
+ leaseDuration time.Duration
+ // renewDeadline is the duration that the acting controlplane will retry
+ // refreshing leadership before giving up.
+ renewDeadline time.Duration
+ // retryPeriod is the duration the LeaderElector clients should wait
+ // between tries of actions.
+ retryPeriod time.Duration
+
+ // waitForRunnable is holding the number of runnables currently running so that
+ // we can wait for them to exit before quitting the manager
+ waitForRunnable sync.WaitGroup
+
+ // gracefulShutdownTimeout is the duration given to runnable to stop
+ // before the manager actually returns on stop.
+ gracefulShutdownTimeout time.Duration
+
+ // onStoppedLeading is callled when the leader election lease is lost.
+ // It can be overridden for tests.
+ onStoppedLeading func()
+
+ // shutdownCtx is the context that can be used during shutdown. It will be cancelled
+ // after the gracefulShutdownTimeout ended. It must not be accessed before internalStop
+ // is closed because it will be nil.
+ shutdownCtx context.Context
+
+ internalCtx context.Context
+ internalCancel context.CancelFunc
+
+ // internalProceduresStop channel is used internally to the manager when coordinating
+ // the proper shutdown of servers. This channel is also used for dependency injection.
+ internalProceduresStop chan struct{}
+}
+
+type hasCache interface {
+ Runnable
+ GetCache() cache.Cache
+}
+
+// Add sets dependencies on i, and adds it to the list of Runnables to start.
+func (cm *controllerManager) Add(r Runnable) error {
+ cm.mu.Lock()
+ defer cm.mu.Unlock()
+ if cm.stopProcedureEngaged {
+ return errors.New("can't accept new runnable as stop procedure is already engaged")
+ }
+
+ // Set dependencies on the object
+ if err := cm.SetFields(r); err != nil {
+ return err
+ }
+
+ var shouldStart bool
+
+ // Add the runnable to the leader election or the non-leaderelection list
+ if leRunnable, ok := r.(LeaderElectionRunnable); ok && !leRunnable.NeedLeaderElection() {
+ shouldStart = cm.started
+ cm.nonLeaderElectionRunnables = append(cm.nonLeaderElectionRunnables, r)
+ } else if hasCache, ok := r.(hasCache); ok {
+ cm.caches = append(cm.caches, hasCache)
+ } else {
+ shouldStart = cm.startedLeader
+ cm.leaderElectionRunnables = append(cm.leaderElectionRunnables, r)
+ }
+
+ if shouldStart {
+ // If already started, start the controller
+ cm.startRunnable(r)
+ }
+
+ return nil
+}
+
+// Deprecated: use the equivalent Options field to set a field. This method will be removed in v0.10.
+func (cm *controllerManager) SetFields(i interface{}) error {
+ if err := cm.cluster.SetFields(i); err != nil {
+ return err
+ }
+ if _, err := inject.InjectorInto(cm.SetFields, i); err != nil {
+ return err
+ }
+ if _, err := inject.StopChannelInto(cm.internalProceduresStop, i); err != nil {
+ return err
+ }
+ if _, err := inject.LoggerInto(cm.logger, i); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// AddMetricsExtraHandler adds extra handler served on path to the http server that serves metrics.
+func (cm *controllerManager) AddMetricsExtraHandler(path string, handler http.Handler) error {
+ if path == defaultMetricsEndpoint {
+ return fmt.Errorf("overriding builtin %s endpoint is not allowed", defaultMetricsEndpoint)
+ }
+
+ cm.mu.Lock()
+ defer cm.mu.Unlock()
+
+ if _, found := cm.metricsExtraHandlers[path]; found {
+ return fmt.Errorf("can't register extra handler by duplicate path %q on metrics http server", path)
+ }
+
+ cm.metricsExtraHandlers[path] = handler
+ 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.mu.Lock()
+ defer cm.mu.Unlock()
+
+ if cm.stopProcedureEngaged {
+ return errors.New("can't accept new healthCheck as stop procedure is already engaged")
+ }
+
+ if cm.healthzStarted {
+ return fmt.Errorf("unable to add new checker because healthz endpoint has already been created")
+ }
+
+ if cm.healthzHandler == nil {
+ cm.healthzHandler = &healthz.Handler{Checks: map[string]healthz.Checker{}}
+ }
+
+ cm.healthzHandler.Checks[name] = check
+ return nil
+}
+
+// AddReadyzCheck allows you to add Readyz checker.
+func (cm *controllerManager) AddReadyzCheck(name string, check healthz.Checker) error {
+ cm.mu.Lock()
+ defer cm.mu.Unlock()
+
+ if cm.stopProcedureEngaged {
+ return errors.New("can't accept new ready check as stop procedure is already engaged")
+ }
+
+ if cm.healthzStarted {
+ return fmt.Errorf("unable to add new checker because readyz endpoint has already been created")
+ }
+
+ if cm.readyzHandler == nil {
+ cm.readyzHandler = &healthz.Handler{Checks: map[string]healthz.Checker{}}
+ }
+
+ cm.readyzHandler.Checks[name] = check
+ return nil
+}
+
+func (cm *controllerManager) GetConfig() *rest.Config {
+ return cm.cluster.GetConfig()
+}
+
+func (cm *controllerManager) GetClient() client.Client {
+ return cm.cluster.GetClient()
+}
+
+func (cm *controllerManager) GetScheme() *runtime.Scheme {
+ return cm.cluster.GetScheme()
+}
+
+func (cm *controllerManager) GetFieldIndexer() client.FieldIndexer {
+ return cm.cluster.GetFieldIndexer()
+}
+
+func (cm *controllerManager) GetCache() cache.Cache {
+ return cm.cluster.GetCache()
+}
+
+func (cm *controllerManager) GetEventRecorderFor(name string) record.EventRecorder {
+ return cm.cluster.GetEventRecorderFor(name)
+}
+
+func (cm *controllerManager) GetRESTMapper() meta.RESTMapper {
+ return cm.cluster.GetRESTMapper()
+}
+
+func (cm *controllerManager) GetAPIReader() client.Reader {
+ return cm.cluster.GetAPIReader()
+}
+
+func (cm *controllerManager) GetWebhookServer() *webhook.Server {
+ cm.webhookServerOnce.Do(func() {
+ if cm.webhookServer == nil {
+ cm.webhookServer = &webhook.Server{
+ Port: cm.port,
+ Host: cm.host,
+ CertDir: cm.certDir,
+ }
+ }
+ if err := cm.Add(cm.webhookServer); err != nil {
+ panic("unable to add webhook server to the controller manager")
+ }
+ })
+ return cm.webhookServer
+}
+
+func (cm *controllerManager) GetLogger() logr.Logger {
+ return cm.logger
+}
+
+func (cm *controllerManager) GetControllerOptions() v1alpha1.ControllerConfigurationSpec {
+ return cm.controllerOptions
+}
+
+func (cm *controllerManager) serveMetrics() {
+ handler := promhttp.HandlerFor(metrics.Registry, promhttp.HandlerOpts{
+ ErrorHandling: promhttp.HTTPErrorOnError,
+ })
+ // TODO(JoelSpeed): Use existing Kubernetes machinery for serving metrics
+ mux := http.NewServeMux()
+ mux.Handle(defaultMetricsEndpoint, handler)
+
+ func() {
+ cm.mu.Lock()
+ defer cm.mu.Unlock()
+
+ for path, extraHandler := range cm.metricsExtraHandlers {
+ mux.Handle(path, extraHandler)
+ }
+ }()
+
+ server := http.Server{
+ Handler: mux,
+ }
+ // Run the server
+ cm.startRunnable(RunnableFunc(func(_ context.Context) error {
+ cm.logger.Info("Starting metrics server", "path", defaultMetricsEndpoint)
+ if err := server.Serve(cm.metricsListener); err != nil && err != http.ErrServerClosed {
+ return err
+ }
+ return nil
+ }))
+
+ // Shutdown the server when stop is closed
+ <-cm.internalProceduresStop
+ if err := server.Shutdown(cm.shutdownCtx); err != nil {
+ cm.errChan <- err
+ }
+}
+
+func (cm *controllerManager) serveHealthProbes() {
+ mux := http.NewServeMux()
+ server := http.Server{
+ Handler: mux,
+ }
+
+ func() {
+ cm.mu.Lock()
+ defer cm.mu.Unlock()
+
+ if cm.readyzHandler != nil {
+ mux.Handle(cm.readinessEndpointName, http.StripPrefix(cm.readinessEndpointName, cm.readyzHandler))
+ // Append '/' suffix to handle subpaths
+ mux.Handle(cm.readinessEndpointName+"/", http.StripPrefix(cm.readinessEndpointName, cm.readyzHandler))
+ }
+ if cm.healthzHandler != nil {
+ mux.Handle(cm.livenessEndpointName, http.StripPrefix(cm.livenessEndpointName, cm.healthzHandler))
+ // Append '/' suffix to handle subpaths
+ mux.Handle(cm.livenessEndpointName+"/", http.StripPrefix(cm.livenessEndpointName, cm.healthzHandler))
+ }
+
+ // Run server
+ cm.startRunnable(RunnableFunc(func(_ context.Context) error {
+ if err := server.Serve(cm.healthProbeListener); err != nil && err != http.ErrServerClosed {
+ return err
+ }
+ return nil
+ }))
+ cm.healthzStarted = true
+ }()
+
+ go func() {
+ // Shutdown the server when stop is closed
+ <-cm.internalProceduresStop
+ if err := server.Shutdown(cm.shutdownCtx); err != nil {
+ cm.errChan <- err
+ }
+ }()
+}
+
+func (cm *controllerManager) Start(ctx context.Context) (err error) {
+ if err := cm.Add(cm.cluster); err != nil {
+ return fmt.Errorf("failed to add cluster to runnables: %w", err)
+ }
+ cm.internalCtx, cm.internalCancel = context.WithCancel(ctx)
+
+ // 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)
+ // This must be deferred after closing stopComplete, otherwise we deadlock.
+ defer func() {
+ // https://hips.hearstapps.com/hmg-prod.s3.amazonaws.com/images/gettyimages-459889618-1533579787.jpg
+ stopErr := cm.engageStopProcedure(stopComplete)
+ if stopErr != nil {
+ if err != nil {
+ // Utilerrors.Aggregate allows to use errors.Is for all contained errors
+ // whereas fmt.Errorf allows wrapping at most one error which means the
+ // other one can not be found anymore.
+ err = kerrors.NewAggregate([]error{err, stopErr})
+ } else {
+ err = stopErr
+ }
+ }
+ }()
+
+ // initialize this here so that we reset the signal channel state on every start
+ // Everything that might write into this channel must be started in a new goroutine,
+ // because otherwise we might block this routine trying to write into the full channel
+ // and will not be able to enter the deferred cm.engageStopProcedure() which drains
+ // it.
+ cm.errChan = make(chan error)
+
+ // Metrics should be served whether the controller is leader or not.
+ // (If we don't serve metrics for non-leaders, prometheus will still scrape
+ // the pod but will get a connection refused)
+ if cm.metricsListener != nil {
+ go cm.serveMetrics()
+ }
+
+ // Serve health probes
+ if cm.healthProbeListener != nil {
+ cm.serveHealthProbes()
+ }
+
+ // Webhooks MUST start before any cache is populated, otherwise there is a race condition
+ // between conversion webhooks and the cache sync (usually initial list) which causes the webhooks
+ // to never start because no cache can be populated.
+ cm.startWebhookRunnables()
+
+ go cm.startNonLeaderElectionRunnables()
+
+ go func() {
+ if cm.resourceLock != nil {
+ err := cm.startLeaderElection()
+ if err != nil {
+ cm.errChan <- err
+ }
+ } else {
+ // Treat not having leader election enabled the same as being elected.
+ cm.startLeaderElectionRunnables()
+ close(cm.elected)
+ }
+ }()
+
+ select {
+ case <-ctx.Done():
+ // We are done
+ return nil
+ case err := <-cm.errChan:
+ // Error starting or running a runnable
+ return err
+ }
+}
+
+// engageStopProcedure signals all runnables to stop, reads potential errors
+// from the errChan and waits for them to end. It must not be called more than once.
+func (cm *controllerManager) engageStopProcedure(stopComplete <-chan struct{}) error {
+ // Populate the shutdown context.
+ var shutdownCancel context.CancelFunc
+ if cm.gracefulShutdownTimeout > 0 {
+ cm.shutdownCtx, shutdownCancel = context.WithTimeout(context.Background(), cm.gracefulShutdownTimeout)
+ } else {
+ cm.shutdownCtx, shutdownCancel = context.WithCancel(context.Background())
+ }
+ defer shutdownCancel()
+
+ // Cancel the internal stop channel and wait for the procedures to stop and complete.
+ close(cm.internalProceduresStop)
+ cm.internalCancel()
+
+ // Start draining the errors before acquiring the lock to make sure we don't deadlock
+ // if something that has the lock is blocked on trying to write into the unbuffered
+ // channel after something else already wrote into it.
+ go func() {
+ for {
+ select {
+ case err, ok := <-cm.errChan:
+ if ok {
+ cm.logger.Error(err, "error received after stop sequence was engaged")
+ }
+ case <-stopComplete:
+ return
+ }
+ }
+ }()
+ if cm.gracefulShutdownTimeout == 0 {
+ return nil
+ }
+ cm.mu.Lock()
+ defer cm.mu.Unlock()
+ cm.stopProcedureEngaged = true
+
+ // we want to close this after the other runnables stop, because we don't
+ // want things like leader election to try and emit events on a closed
+ // channel
+ defer cm.recorderProvider.Stop(cm.shutdownCtx)
+ return cm.waitForRunnableToEnd(shutdownCancel)
+}
+
+// waitForRunnableToEnd blocks until all runnables ended or the
+// tearDownTimeout was reached. In the latter case, an error is returned.
+func (cm *controllerManager) waitForRunnableToEnd(shutdownCancel context.CancelFunc) (retErr error) {
+ // Cancel leader election only after we waited. It will os.Exit() the app for safety.
+ defer func() {
+ if retErr == nil && cm.leaderElectionCancel != nil {
+ // After asking the context to be cancelled, make sure
+ // we wait for the leader stopped channel to be closed, otherwise
+ // we might encounter race conditions between this code
+ // and the event recorder, which is used within leader election code.
+ cm.leaderElectionCancel()
+ <-cm.leaderElectionStopped
+ }
+ }()
+
+ go func() {
+ cm.waitForRunnable.Wait()
+ shutdownCancel()
+ }()
+
+ <-cm.shutdownCtx.Done()
+ if err := cm.shutdownCtx.Err(); err != nil && err != context.Canceled {
+ return fmt.Errorf("failed waiting for all runnables to end within grace period of %s: %w", cm.gracefulShutdownTimeout, err)
+ }
+ return nil
+}
+
+func (cm *controllerManager) startWebhookRunnables() {
+ cm.mu.Lock()
+ defer cm.mu.Unlock()
+
+ // WARNING: Webhooks MUST start before any cache is populated, otherwise there is a race condition
+ // between conversion webhooks and the cache sync (usually initial list) which causes the webhooks
+ // to never start because no cache can be populated.
+ for _, c := range cm.nonLeaderElectionRunnables {
+ if _, ok := c.(*webhook.Server); ok {
+ cm.startRunnable(c)
+ }
+ }
+}
+
+func (cm *controllerManager) startNonLeaderElectionRunnables() {
+ cm.mu.Lock()
+ defer cm.mu.Unlock()
+
+ // Start and wait for caches.
+ cm.waitForCache(cm.internalCtx)
+
+ // Start the non-leaderelection Runnables after the cache has synced
+ for _, c := range cm.nonLeaderElectionRunnables {
+ if _, ok := c.(*webhook.Server); ok {
+ continue
+ }
+
+ // Controllers block, but we want to return an error if any have an error starting.
+ // Write any Start errors to a channel so we can return them
+ cm.startRunnable(c)
+ }
+}
+
+func (cm *controllerManager) startLeaderElectionRunnables() {
+ cm.mu.Lock()
+ defer cm.mu.Unlock()
+
+ cm.waitForCache(cm.internalCtx)
+
+ // Start the leader election Runnables after the cache has synced
+ for _, c := range cm.leaderElectionRunnables {
+ // Controllers block, but we want to return an error if any have an error starting.
+ // Write any Start errors to a channel so we can return them
+ cm.startRunnable(c)
+ }
+
+ cm.startedLeader = true
+}
+
+func (cm *controllerManager) waitForCache(ctx context.Context) {
+ if cm.started {
+ return
+ }
+
+ for _, cache := range cm.caches {
+ cm.startRunnable(cache)
+ }
+
+ // Wait for the caches to sync.
+ // TODO(community): Check the return value and write a test
+ for _, cache := range cm.caches {
+ cache.GetCache().WaitForCacheSync(ctx)
+ }
+ // TODO: This should be the return value of cm.cache.WaitForCacheSync but we abuse
+ // cm.started as check if we already started the cache so it must always become true.
+ // Making sure that the cache doesn't get started twice is needed to not get a "close
+ // of closed channel" panic
+ cm.started = true
+}
+
+func (cm *controllerManager) startLeaderElection() (err error) {
+ ctx, cancel := context.WithCancel(context.Background())
+ cm.mu.Lock()
+ cm.leaderElectionCancel = cancel
+ cm.mu.Unlock()
+
+ if cm.onStoppedLeading == nil {
+ cm.onStoppedLeading = func() {
+ // Make sure graceful shutdown is skipped if we lost the leader lock without
+ // intending to.
+ cm.gracefulShutdownTimeout = time.Duration(0)
+ // Most implementations of leader election log.Fatal() here.
+ // Since Start is wrapped in log.Fatal when called, we can just return
+ // an error here which will cause the program to exit.
+ cm.errChan <- errors.New("leader election lost")
+ }
+ }
+ l, err := leaderelection.NewLeaderElector(leaderelection.LeaderElectionConfig{
+ Lock: cm.resourceLock,
+ LeaseDuration: cm.leaseDuration,
+ RenewDeadline: cm.renewDeadline,
+ RetryPeriod: cm.retryPeriod,
+ Callbacks: leaderelection.LeaderCallbacks{
+ OnStartedLeading: func(_ context.Context) {
+ cm.startLeaderElectionRunnables()
+ close(cm.elected)
+ },
+ OnStoppedLeading: cm.onStoppedLeading,
+ },
+ ReleaseOnCancel: cm.leaderElectionReleaseOnCancel,
+ })
+ if err != nil {
+ return err
+ }
+
+ // Start the leader elector process
+ go func() {
+ l.Run(ctx)
+ <-ctx.Done()
+ close(cm.leaderElectionStopped)
+ }()
+ return nil
+}
+
+func (cm *controllerManager) Elected() <-chan struct{} {
+ return cm.elected
+}
+
+func (cm *controllerManager) startRunnable(r Runnable) {
+ cm.waitForRunnable.Add(1)
+ go func() {
+ defer cm.waitForRunnable.Done()
+ if err := r.Start(cm.internalCtx); err != nil {
+ cm.errChan <- err
+ }
+ }()
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/manager/manager.go b/vendor/sigs.k8s.io/controller-runtime/pkg/manager/manager.go
new file mode 100644
index 000000000..2d2733f0a
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/manager/manager.go
@@ -0,0 +1,579 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package manager
+
+import (
+ "context"
+ "fmt"
+ "net"
+ "net/http"
+ "reflect"
+ "time"
+
+ "github.com/go-logr/logr"
+ "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"
+ "k8s.io/client-go/tools/record"
+ "sigs.k8s.io/controller-runtime/pkg/cache"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/cluster"
+ "sigs.k8s.io/controller-runtime/pkg/config"
+ "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"
+ "sigs.k8s.io/controller-runtime/pkg/log"
+ "sigs.k8s.io/controller-runtime/pkg/metrics"
+ "sigs.k8s.io/controller-runtime/pkg/recorder"
+ "sigs.k8s.io/controller-runtime/pkg/runtime/inject"
+ "sigs.k8s.io/controller-runtime/pkg/webhook"
+)
+
+// Manager initializes shared dependencies such as Caches and Clients, and provides them to Runnables.
+// A Manager is required to create Controllers.
+type Manager interface {
+ // Cluster holds a variety of methods to interact with a cluster.
+ cluster.Cluster
+
+ // Add will set requested dependencies on the component, and cause the component to be
+ // started when Start is called. Add will inject any dependencies for which the argument
+ // implements the inject interface - e.g. inject.Client.
+ // Depending on if a Runnable implements LeaderElectionRunnable interface, a Runnable can be run in either
+ // non-leaderelection mode (always running) or leader election mode (managed by leader election if enabled).
+ Add(Runnable) error
+
+ // Elected is closed when this manager is elected leader of a group of
+ // managers, either because it won a leader election or because no leader
+ // election was configured.
+ Elected() <-chan struct{}
+
+ // AddMetricsExtraHandler 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 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.
+ AddMetricsExtraHandler(path string, handler http.Handler) error
+
+ // AddHealthzCheck allows you to add Healthz checker
+ AddHealthzCheck(name string, check healthz.Checker) error
+
+ // AddReadyzCheck allows you to add Readyz checker
+ AddReadyzCheck(name string, check healthz.Checker) error
+
+ // Start starts all registered Controllers and blocks until the context is cancelled.
+ // Returns an error if there is an error starting any controller.
+ //
+ // If LeaderElection is used, the binary must be exited immediately after this returns,
+ // otherwise components that need leader election might continue to run after the leader
+ // lock was lost.
+ Start(ctx context.Context) error
+
+ // GetWebhookServer returns a webhook.Server
+ GetWebhookServer() *webhook.Server
+
+ // GetLogger returns this manager's logger.
+ GetLogger() logr.Logger
+
+ // GetControllerOptions returns controller global configuration options.
+ GetControllerOptions() v1alpha1.ControllerConfigurationSpec
+}
+
+// Options are the arguments for creating a new Manager.
+type Options struct {
+ // Scheme is the scheme used to resolve runtime.Objects to GroupVersionKinds / Resources
+ // Defaults to the kubernetes/client-go scheme.Scheme, but it's almost always better
+ // idea to pass your own scheme in. See the documentation in pkg/scheme for more information.
+ Scheme *runtime.Scheme
+
+ // MapperProvider provides the rest mapper used to map go types to Kubernetes APIs
+ MapperProvider func(c *rest.Config) (meta.RESTMapper, error)
+
+ // SyncPeriod determines the minimum frequency at which watched resources are
+ // reconciled. A lower period will correct entropy more quickly, but reduce
+ // responsiveness to change if there are many watched resources. Change this
+ // value only if you know what you are doing. Defaults to 10 hours if unset.
+ // there will a 10 percent jitter between the SyncPeriod of all controllers
+ // so that all controllers will not send list requests simultaneously.
+ //
+ // This applies to all controllers.
+ //
+ // A period sync happens for two reasons:
+ // 1. To insure against a bug in the controller that causes an object to not
+ // be requeued, when it otherwise should be requeued.
+ // 2. To insure against an unknown bug in controller-runtime, or its dependencies,
+ // that causes an object to not be requeued, when it otherwise should be
+ // requeued, or to be removed from the queue, when it otherwise should not
+ // be removed.
+ //
+ // If you want
+ // 1. to insure against missed watch events, or
+ // 2. to poll services that cannot be watched,
+ // then we recommend that, instead of changing the default period, the
+ // controller requeue, with a constant duration `t`, whenever the controller
+ // is "done" with an object, and would otherwise not requeue it, i.e., we
+ // recommend the `Reconcile` function return `reconcile.Result{RequeueAfter: t}`,
+ // instead of `reconcile.Result{}`.
+ SyncPeriod *time.Duration
+
+ // Logger is the logger that should be used by this manager.
+ // If none is set, it defaults to log.Log global logger.
+ Logger logr.Logger
+
+ // LeaderElection determines whether or not to use leader election when
+ // starting the manager.
+ LeaderElection bool
+
+ // LeaderElectionResourceLock determines which resource lock to use for leader election,
+ // defaults to "configmapsleases". Change this value only if you know what you are doing.
+ // Otherwise, users of your controller might end up with multiple running instances that
+ // each acquired leadership through different resource locks during upgrades and thus
+ // act on the same resources concurrently.
+ // If you want to migrate to the "leases" resource lock, you might do so by migrating to the
+ // respective multilock first ("configmapsleases" or "endpointsleases"), which will acquire a
+ // leader lock on both resources. After all your users have migrated to the multilock, you can
+ // go ahead and migrate to "leases". Please also keep in mind, that users might skip versions
+ // of your controller.
+ //
+ // Note: before controller-runtime version v0.7, the resource lock was set to "configmaps".
+ // Please keep this in mind, when planning a proper migration path for your controller.
+ LeaderElectionResourceLock string
+
+ // LeaderElectionNamespace determines the namespace in which the leader
+ // election resource will be created.
+ LeaderElectionNamespace string
+
+ // LeaderElectionID determines the name of the resource that leader election
+ // will use for holding the leader lock.
+ LeaderElectionID string
+
+ // LeaderElectionConfig can be specified to override the default configuration
+ // that is used to build the leader election client.
+ LeaderElectionConfig *rest.Config
+
+ // LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily
+ // when the Manager ends. This requires the binary to immediately end when the
+ // Manager is stopped, otherwise this setting is unsafe. Setting this significantly
+ // speeds up voluntary leader transitions as the new leader doesn't have to wait
+ // LeaseDuration time first.
+ LeaderElectionReleaseOnCancel bool
+
+ // LeaseDuration is the duration that non-leader candidates will
+ // wait to force acquire leadership. This is measured against time of
+ // last observed ack. Default is 15 seconds.
+ LeaseDuration *time.Duration
+ // RenewDeadline is the duration that the acting controlplane will retry
+ // refreshing leadership before giving up. Default is 10 seconds.
+ RenewDeadline *time.Duration
+ // RetryPeriod is the duration the LeaderElector clients should wait
+ // between tries of actions. Default is 2 seconds.
+ RetryPeriod *time.Duration
+
+ // Namespace 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.
+ Namespace string
+
+ // MetricsBindAddress 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.
+ MetricsBindAddress string
+
+ // HealthProbeBindAddress is the TCP address that the controller should bind to
+ // for serving health probes
+ HealthProbeBindAddress string
+
+ // Readiness probe endpoint name, defaults to "readyz"
+ ReadinessEndpointName string
+
+ // Liveness probe endpoint name, defaults to "healthz"
+ LivenessEndpointName string
+
+ // Port is the port that the webhook server serves at.
+ // It is used to set webhook.Server.Port if WebhookServer is not set.
+ Port int
+ // Host is the hostname that the webhook server binds to.
+ // It is used to set webhook.Server.Host if WebhookServer is not set.
+ Host string
+
+ // CertDir is the directory that contains the server key and certificate.
+ // If not set, webhook server would look up the server key and certificate in
+ // {TempDir}/k8s-webhook-server/serving-certs. The server key and certificate
+ // must be named tls.key and tls.crt, respectively.
+ // It is used to set webhook.Server.CertDir if WebhookServer is not set.
+ CertDir string
+
+ // WebhookServer is an externally configured webhook.Server. By default,
+ // a Manager will create a default server using Port, Host, and CertDir;
+ // if this is set, the Manager will use this server instead.
+ WebhookServer *webhook.Server
+
+ // Functions to all for a user to customize the values that will be injected.
+
+ // NewCache is the function that will create the cache to be used
+ // by the manager. If not set this will use the default new cache function.
+ NewCache cache.NewCacheFunc
+
+ // NewClient is the func that creates the client to be used by the manager.
+ // If not set this will create the default DelegatingClient that will
+ // use the cache for reads and the client for writes.
+ NewClient cluster.NewClientFunc
+
+ // ClientDisableCacheFor tells the client that, if any cache is used, to bypass it
+ // for the given objects.
+ ClientDisableCacheFor []client.Object
+
+ // DryRunClient specifies whether the client should be configured to enforce
+ // dryRun mode.
+ DryRunClient bool
+
+ // EventBroadcaster records Events emitted by the manager and sends them to the Kubernetes API
+ // Use this to customize the event correlator and spam filter
+ //
+ // Deprecated: using this may cause goroutine leaks if the lifetime of your manager or controllers
+ // is shorter than the lifetime of your process.
+ EventBroadcaster record.EventBroadcaster
+
+ // 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 *time.Duration
+
+ // Controller contains global configuration options for controllers
+ // registered within this manager.
+ // +optional
+ Controller v1alpha1.ControllerConfigurationSpec
+
+ // makeBroadcaster allows deferring the creation of the broadcaster to
+ // avoid leaking goroutines if we never call Start on this manager. It also
+ // returns whether or not this is a "owned" broadcaster, and as such should be
+ // stopped with the manager.
+ makeBroadcaster intrec.EventBroadcasterProducer
+
+ // Dependency injection for testing
+ newRecorderProvider func(config *rest.Config, scheme *runtime.Scheme, logger logr.Logger, makeBroadcaster intrec.EventBroadcasterProducer) (*intrec.Provider, error)
+ newResourceLock func(config *rest.Config, recorderProvider recorder.Provider, options leaderelection.Options) (resourcelock.Interface, error)
+ newMetricsListener func(addr string) (net.Listener, error)
+ newHealthProbeListener func(addr string) (net.Listener, error)
+}
+
+// Runnable allows a component to be started.
+// It's very important that Start blocks until
+// it's done running.
+type Runnable interface {
+ // Start starts running the component. The component will stop running
+ // when the context is closed. Start blocks until the context is closed or
+ // an error occurs.
+ Start(context.Context) error
+}
+
+// RunnableFunc implements Runnable using a function.
+// It's very important that the given function block
+// until it's done running.
+type RunnableFunc func(context.Context) error
+
+// Start implements Runnable.
+func (r RunnableFunc) Start(ctx context.Context) error {
+ return r(ctx)
+}
+
+// LeaderElectionRunnable knows if a Runnable needs to be run in the leader election mode.
+type LeaderElectionRunnable interface {
+ // NeedLeaderElection returns true if the Runnable needs to be run in the leader election mode.
+ // e.g. controllers need to be run in leader election mode, while webhook server doesn't.
+ NeedLeaderElection() bool
+}
+
+// New returns a new Manager for creating Controllers.
+func New(config *rest.Config, options Options) (Manager, error) {
+ // Set default values for options fields
+ options = setOptionsDefaults(options)
+
+ cluster, err := cluster.New(config, func(clusterOptions *cluster.Options) {
+ clusterOptions.Scheme = options.Scheme
+ clusterOptions.MapperProvider = options.MapperProvider
+ clusterOptions.Logger = options.Logger
+ clusterOptions.SyncPeriod = options.SyncPeriod
+ clusterOptions.Namespace = options.Namespace
+ clusterOptions.NewCache = options.NewCache
+ clusterOptions.NewClient = options.NewClient
+ clusterOptions.ClientDisableCacheFor = options.ClientDisableCacheFor
+ clusterOptions.DryRunClient = options.DryRunClient
+ clusterOptions.EventBroadcaster = options.EventBroadcaster //nolint:staticcheck
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ // Create the recorder provider to inject event recorders for the components.
+ // TODO(directxman12): the log for the event provider should have a context (name, tags, etc) specific
+ // to the particular controller that it's being injected into, rather than a generic one like is here.
+ recorderProvider, err := options.newRecorderProvider(config, cluster.GetScheme(), options.Logger.WithName("events"), options.makeBroadcaster)
+ if err != nil {
+ return nil, err
+ }
+
+ // Create the resource lock to enable leader election)
+ leaderConfig := options.LeaderElectionConfig
+ if leaderConfig == nil {
+ leaderConfig = rest.CopyConfig(config)
+ }
+ resourceLock, err := options.newResourceLock(leaderConfig, recorderProvider, leaderelection.Options{
+ LeaderElection: options.LeaderElection,
+ LeaderElectionResourceLock: options.LeaderElectionResourceLock,
+ LeaderElectionID: options.LeaderElectionID,
+ LeaderElectionNamespace: options.LeaderElectionNamespace,
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ // Create the metrics listener. This will throw an error if the metrics bind
+ // address is invalid or already in use.
+ metricsListener, err := options.newMetricsListener(options.MetricsBindAddress)
+ if err != nil {
+ return nil, err
+ }
+
+ // By default we have no extra endpoints to expose on metrics http server.
+ metricsExtraHandlers := make(map[string]http.Handler)
+
+ // Create health probes listener. This will throw an error if the bind
+ // address is invalid or already in use.
+ healthProbeListener, err := options.newHealthProbeListener(options.HealthProbeBindAddress)
+ if err != nil {
+ return nil, err
+ }
+
+ return &controllerManager{
+ cluster: cluster,
+ recorderProvider: recorderProvider,
+ resourceLock: resourceLock,
+ metricsListener: metricsListener,
+ metricsExtraHandlers: metricsExtraHandlers,
+ controllerOptions: options.Controller,
+ logger: options.Logger,
+ elected: make(chan struct{}),
+ port: options.Port,
+ host: options.Host,
+ certDir: options.CertDir,
+ webhookServer: options.WebhookServer,
+ leaseDuration: *options.LeaseDuration,
+ renewDeadline: *options.RenewDeadline,
+ retryPeriod: *options.RetryPeriod,
+ healthProbeListener: healthProbeListener,
+ readinessEndpointName: options.ReadinessEndpointName,
+ livenessEndpointName: options.LivenessEndpointName,
+ gracefulShutdownTimeout: *options.GracefulShutdownTimeout,
+ internalProceduresStop: make(chan struct{}),
+ leaderElectionStopped: make(chan struct{}),
+ leaderElectionReleaseOnCancel: options.LeaderElectionReleaseOnCancel,
+ }, 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.
+func (o Options) AndFrom(loader config.ControllerManagerConfiguration) (Options, error) {
+ if inj, wantsScheme := loader.(inject.Scheme); wantsScheme {
+ err := inj.InjectScheme(o.Scheme)
+ if err != nil {
+ return o, err
+ }
+ }
+
+ newObj, err := loader.Complete()
+ if err != nil {
+ return o, err
+ }
+
+ o = o.setLeaderElectionConfig(newObj)
+
+ if o.SyncPeriod == nil && newObj.SyncPeriod != nil {
+ o.SyncPeriod = &newObj.SyncPeriod.Duration
+ }
+
+ if o.Namespace == "" && newObj.CacheNamespace != "" {
+ o.Namespace = newObj.CacheNamespace
+ }
+
+ if o.MetricsBindAddress == "" && newObj.Metrics.BindAddress != "" {
+ o.MetricsBindAddress = 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.Port == 0 && newObj.Webhook.Port != nil {
+ o.Port = *newObj.Webhook.Port
+ }
+
+ if o.Host == "" && newObj.Webhook.Host != "" {
+ o.Host = newObj.Webhook.Host
+ }
+
+ if o.CertDir == "" && newObj.Webhook.CertDir != "" {
+ o.CertDir = newObj.Webhook.CertDir
+ }
+
+ if newObj.Controller != nil {
+ if o.Controller.CacheSyncTimeout == nil && 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.
+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 !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" {
+ return nil, nil
+ }
+
+ ln, err := net.Listen("tcp", addr)
+ if err != nil {
+ return nil, fmt.Errorf("error listening on %s: %v", addr, err)
+ }
+ return ln, nil
+}
+
+// setOptionsDefaults set default values for Options fields.
+func setOptionsDefaults(options Options) Options {
+ // Allow newResourceLock to be mocked
+ if options.newResourceLock == nil {
+ options.newResourceLock = leaderelection.NewResourceLock
+ }
+
+ // Allow newRecorderProvider to be mocked
+ if options.newRecorderProvider == nil {
+ options.newRecorderProvider = intrec.NewProvider
+ }
+
+ // This is duplicated with pkg/cluster, we need it here
+ // for the leader election and there to provide the user with
+ // an EventBroadcaster
+ if options.EventBroadcaster == nil {
+ // defer initialization to avoid leaking by default
+ options.makeBroadcaster = func() (record.EventBroadcaster, bool) {
+ return record.NewBroadcaster(), true
+ }
+ } else {
+ options.makeBroadcaster = func() (record.EventBroadcaster, bool) {
+ return options.EventBroadcaster, false
+ }
+ }
+
+ if options.newMetricsListener == nil {
+ options.newMetricsListener = metrics.NewListener
+ }
+ leaseDuration, renewDeadline, retryPeriod := defaultLeaseDuration, defaultRenewDeadline, defaultRetryPeriod
+ if options.LeaseDuration == nil {
+ options.LeaseDuration = &leaseDuration
+ }
+
+ if options.RenewDeadline == nil {
+ options.RenewDeadline = &renewDeadline
+ }
+
+ if options.RetryPeriod == nil {
+ options.RetryPeriod = &retryPeriod
+ }
+
+ if options.ReadinessEndpointName == "" {
+ options.ReadinessEndpointName = defaultReadinessEndpoint
+ }
+
+ if options.LivenessEndpointName == "" {
+ options.LivenessEndpointName = defaultLivenessEndpoint
+ }
+
+ if options.newHealthProbeListener == nil {
+ options.newHealthProbeListener = defaultHealthProbeListener
+ }
+
+ if options.GracefulShutdownTimeout == nil {
+ gracefulShutdownTimeout := defaultGracefulShutdownPeriod
+ options.GracefulShutdownTimeout = &gracefulShutdownTimeout
+ }
+
+ if options.Logger == nil {
+ options.Logger = log.Log
+ }
+
+ return options
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/manager/signals/doc.go b/vendor/sigs.k8s.io/controller-runtime/pkg/manager/signals/doc.go
new file mode 100644
index 000000000..737cc7eff
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/manager/signals/doc.go
@@ -0,0 +1,20 @@
+/*
+Copyright 2017 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 signals contains libraries for handling signals to gracefully
+// shutdown the manager in combination with Kubernetes pod graceful termination
+// policy.
+package signals
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/manager/signals/signal.go b/vendor/sigs.k8s.io/controller-runtime/pkg/manager/signals/signal.go
new file mode 100644
index 000000000..9a85558f8
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/manager/signals/signal.go
@@ -0,0 +1,45 @@
+/*
+Copyright 2017 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 signals
+
+import (
+ "context"
+ "os"
+ "os/signal"
+)
+
+var onlyOneSignalHandler = make(chan struct{})
+
+// SetupSignalHandler registers 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
+// is terminated with exit code 1.
+func SetupSignalHandler() context.Context {
+ close(onlyOneSignalHandler) // panics when called twice
+
+ ctx, cancel := context.WithCancel(context.Background())
+
+ c := make(chan os.Signal, 2)
+ signal.Notify(c, shutdownSignals...)
+ go func() {
+ <-c
+ cancel()
+ <-c
+ os.Exit(1) // second signal. Exit directly.
+ }()
+
+ return ctx
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/manager/signals/signal_posix.go b/vendor/sigs.k8s.io/controller-runtime/pkg/manager/signals/signal_posix.go
new file mode 100644
index 000000000..9bdb4e741
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/manager/signals/signal_posix.go
@@ -0,0 +1,26 @@
+// +build !windows
+
+/*
+Copyright 2017 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 signals
+
+import (
+ "os"
+ "syscall"
+)
+
+var shutdownSignals = []os.Signal{os.Interrupt, syscall.SIGTERM}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/manager/signals/signal_windows.go b/vendor/sigs.k8s.io/controller-runtime/pkg/manager/signals/signal_windows.go
new file mode 100644
index 000000000..4907d573f
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/manager/signals/signal_windows.go
@@ -0,0 +1,23 @@
+/*
+Copyright 2017 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 signals
+
+import (
+ "os"
+)
+
+var shutdownSignals = []os.Signal{os.Interrupt}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/metrics/client_go_adapter.go b/vendor/sigs.k8s.io/controller-runtime/pkg/metrics/client_go_adapter.go
new file mode 100644
index 000000000..d32ce2534
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/metrics/client_go_adapter.go
@@ -0,0 +1,231 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package metrics
+
+import (
+ "context"
+ "net/url"
+ "time"
+
+ "github.com/prometheus/client_golang/prometheus"
+ reflectormetrics "k8s.io/client-go/tools/cache"
+ clientmetrics "k8s.io/client-go/tools/metrics"
+)
+
+// this file contains setup logic to initialize the myriad of places
+// that client-go registers metrics. We copy the names and formats
+// from Kubernetes so that we match the core controllers.
+
+// Metrics subsystem and all of the keys used by the rest client.
+const (
+ RestClientSubsystem = "rest_client"
+ LatencyKey = "request_latency_seconds"
+ ResultKey = "requests_total"
+)
+
+// Metrics subsystem and all keys used by the reflectors.
+const (
+ ReflectorSubsystem = "reflector"
+ ListsTotalKey = "lists_total"
+ ListsDurationKey = "list_duration_seconds"
+ ItemsPerListKey = "items_per_list"
+ WatchesTotalKey = "watches_total"
+ ShortWatchesTotalKey = "short_watches_total"
+ WatchDurationKey = "watch_duration_seconds"
+ ItemsPerWatchKey = "items_per_watch"
+ LastResourceVersionKey = "last_resource_version"
+)
+
+var (
+ // client metrics.
+
+ // RequestLatency reports the request latency in seconds per verb/URL.
+ // Deprecated: This metric is deprecated for removal in a future release: using the URL as a
+ // dimension results in cardinality explosion for some consumers. It was deprecated upstream
+ // in k8s v1.14 and hidden in v1.17 via https://github.com/kubernetes/kubernetes/pull/83836.
+ // It is not registered by default. To register:
+ // import (
+ // clientmetrics "k8s.io/client-go/tools/metrics"
+ // clmetrics "sigs.k8s.io/controller-runtime/metrics"
+ // )
+ //
+ // func init() {
+ // clmetrics.Registry.MustRegister(clmetrics.RequestLatency)
+ // clientmetrics.Register(clientmetrics.RegisterOpts{
+ // RequestLatency: clmetrics.LatencyAdapter
+ // })
+ // }
+ RequestLatency = prometheus.NewHistogramVec(prometheus.HistogramOpts{
+ Subsystem: RestClientSubsystem,
+ Name: LatencyKey,
+ Help: "Request latency in seconds. Broken down by verb and URL.",
+ Buckets: prometheus.ExponentialBuckets(0.001, 2, 10),
+ }, []string{"verb", "url"})
+
+ requestResult = prometheus.NewCounterVec(prometheus.CounterOpts{
+ Subsystem: RestClientSubsystem,
+ Name: ResultKey,
+ Help: "Number of HTTP requests, partitioned by status code, method, and host.",
+ }, []string{"code", "method", "host"})
+
+ // reflector metrics.
+
+ // TODO(directxman12): update these to be histograms once the metrics overhaul KEP
+ // PRs start landing.
+
+ listsTotal = prometheus.NewCounterVec(prometheus.CounterOpts{
+ Subsystem: ReflectorSubsystem,
+ Name: ListsTotalKey,
+ Help: "Total number of API lists done by the reflectors",
+ }, []string{"name"})
+
+ listsDuration = prometheus.NewSummaryVec(prometheus.SummaryOpts{
+ Subsystem: ReflectorSubsystem,
+ Name: ListsDurationKey,
+ Help: "How long an API list takes to return and decode for the reflectors",
+ }, []string{"name"})
+
+ itemsPerList = prometheus.NewSummaryVec(prometheus.SummaryOpts{
+ Subsystem: ReflectorSubsystem,
+ Name: ItemsPerListKey,
+ Help: "How many items an API list returns to the reflectors",
+ }, []string{"name"})
+
+ watchesTotal = prometheus.NewCounterVec(prometheus.CounterOpts{
+ Subsystem: ReflectorSubsystem,
+ Name: WatchesTotalKey,
+ Help: "Total number of API watches done by the reflectors",
+ }, []string{"name"})
+
+ shortWatchesTotal = prometheus.NewCounterVec(prometheus.CounterOpts{
+ Subsystem: ReflectorSubsystem,
+ Name: ShortWatchesTotalKey,
+ Help: "Total number of short API watches done by the reflectors",
+ }, []string{"name"})
+
+ watchDuration = prometheus.NewSummaryVec(prometheus.SummaryOpts{
+ Subsystem: ReflectorSubsystem,
+ Name: WatchDurationKey,
+ Help: "How long an API watch takes to return and decode for the reflectors",
+ }, []string{"name"})
+
+ itemsPerWatch = prometheus.NewSummaryVec(prometheus.SummaryOpts{
+ Subsystem: ReflectorSubsystem,
+ Name: ItemsPerWatchKey,
+ Help: "How many items an API watch returns to the reflectors",
+ }, []string{"name"})
+
+ lastResourceVersion = prometheus.NewGaugeVec(prometheus.GaugeOpts{
+ Subsystem: ReflectorSubsystem,
+ Name: LastResourceVersionKey,
+ Help: "Last resource version seen for the reflectors",
+ }, []string{"name"})
+)
+
+func init() {
+ registerClientMetrics()
+ registerReflectorMetrics()
+}
+
+// registerClientMetrics sets up the client latency metrics from client-go.
+func registerClientMetrics() {
+ // register the metrics with our registry
+ Registry.MustRegister(requestResult)
+
+ // register the metrics with client-go
+ clientmetrics.Register(clientmetrics.RegisterOpts{
+ RequestResult: &resultAdapter{metric: requestResult},
+ })
+}
+
+// registerReflectorMetrics sets up reflector (reconcile) loop metrics.
+func registerReflectorMetrics() {
+ Registry.MustRegister(listsTotal)
+ Registry.MustRegister(listsDuration)
+ Registry.MustRegister(itemsPerList)
+ Registry.MustRegister(watchesTotal)
+ Registry.MustRegister(shortWatchesTotal)
+ Registry.MustRegister(watchDuration)
+ Registry.MustRegister(itemsPerWatch)
+ Registry.MustRegister(lastResourceVersion)
+
+ reflectormetrics.SetReflectorMetricsProvider(reflectorMetricsProvider{})
+}
+
+// this section contains adapters, implementations, and other sundry organic, artisanally
+// hand-crafted syntax trees required to convince client-go that it actually wants to let
+// someone use its metrics.
+
+// Client metrics adapters (method #1 for client-go metrics),
+// copied (more-or-less directly) from k8s.io/kubernetes setup code
+// (which isn't anywhere in an easily-importable place).
+
+// LatencyAdapter implements LatencyMetric.
+type LatencyAdapter struct {
+ metric *prometheus.HistogramVec
+}
+
+// Observe increments the request latency metric for the given verb/URL.
+func (l *LatencyAdapter) Observe(_ context.Context, verb string, u url.URL, latency time.Duration) {
+ l.metric.WithLabelValues(verb, u.String()).Observe(latency.Seconds())
+}
+
+type resultAdapter struct {
+ metric *prometheus.CounterVec
+}
+
+func (r *resultAdapter) Increment(_ context.Context, code, method, host string) {
+ r.metric.WithLabelValues(code, method, host).Inc()
+}
+
+// Reflector metrics provider (method #2 for client-go metrics),
+// copied (more-or-less directly) from k8s.io/kubernetes setup code
+// (which isn't anywhere in an easily-importable place).
+
+type reflectorMetricsProvider struct{}
+
+func (reflectorMetricsProvider) NewListsMetric(name string) reflectormetrics.CounterMetric {
+ return listsTotal.WithLabelValues(name)
+}
+
+func (reflectorMetricsProvider) NewListDurationMetric(name string) reflectormetrics.SummaryMetric {
+ return listsDuration.WithLabelValues(name)
+}
+
+func (reflectorMetricsProvider) NewItemsInListMetric(name string) reflectormetrics.SummaryMetric {
+ return itemsPerList.WithLabelValues(name)
+}
+
+func (reflectorMetricsProvider) NewWatchesMetric(name string) reflectormetrics.CounterMetric {
+ return watchesTotal.WithLabelValues(name)
+}
+
+func (reflectorMetricsProvider) NewShortWatchesMetric(name string) reflectormetrics.CounterMetric {
+ return shortWatchesTotal.WithLabelValues(name)
+}
+
+func (reflectorMetricsProvider) NewWatchDurationMetric(name string) reflectormetrics.SummaryMetric {
+ return watchDuration.WithLabelValues(name)
+}
+
+func (reflectorMetricsProvider) NewItemsInWatchMetric(name string) reflectormetrics.SummaryMetric {
+ return itemsPerWatch.WithLabelValues(name)
+}
+
+func (reflectorMetricsProvider) NewLastResourceVersionMetric(name string) reflectormetrics.GaugeMetric {
+ return lastResourceVersion.WithLabelValues(name)
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/metrics/doc.go b/vendor/sigs.k8s.io/controller-runtime/pkg/metrics/doc.go
new file mode 100644
index 000000000..6ed9df951
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/metrics/doc.go
@@ -0,0 +1,20 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+/*
+Package metrics contains controller related metrics utilities
+*/
+package metrics
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/metrics/listener.go b/vendor/sigs.k8s.io/controller-runtime/pkg/metrics/listener.go
new file mode 100644
index 000000000..123d8c15f
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/metrics/listener.go
@@ -0,0 +1,52 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package metrics
+
+import (
+ "fmt"
+ "net"
+
+ logf "sigs.k8s.io/controller-runtime/pkg/internal/log"
+)
+
+var log = logf.RuntimeLog.WithName("metrics")
+
+// DefaultBindAddress sets the default bind address for the metrics listener
+// The metrics is on by default.
+var DefaultBindAddress = ":8080"
+
+// NewListener creates a new TCP listener bound to the given address.
+func NewListener(addr string) (net.Listener, error) {
+ if addr == "" {
+ // If the metrics bind address is empty, default to ":8080"
+ addr = DefaultBindAddress
+ }
+
+ // Add a case to disable metrics altogether
+ if addr == "0" {
+ return nil, nil
+ }
+
+ log.Info("Metrics server is starting to listen", "addr", addr)
+ ln, err := net.Listen("tcp", addr)
+ if err != nil {
+ er := fmt.Errorf("error listening on %s: %w", addr, err)
+ log.Error(er, "metrics server failed to listen. You may want to disable the metrics server or use another port if it is due to conflicts")
+ return nil, er
+ }
+ return ln, nil
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/metrics/registry.go b/vendor/sigs.k8s.io/controller-runtime/pkg/metrics/registry.go
new file mode 100644
index 000000000..ce17124d5
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/metrics/registry.go
@@ -0,0 +1,30 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package metrics
+
+import "github.com/prometheus/client_golang/prometheus"
+
+// RegistererGatherer combines both parts of the API of a Prometheus
+// registry, both the Registerer and the Gatherer interfaces.
+type RegistererGatherer interface {
+ prometheus.Registerer
+ prometheus.Gatherer
+}
+
+// Registry is a prometheus registry for storing metrics within the
+// controller-runtime.
+var Registry RegistererGatherer = prometheus.NewRegistry()
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/metrics/workqueue.go b/vendor/sigs.k8s.io/controller-runtime/pkg/metrics/workqueue.go
new file mode 100644
index 000000000..8ca47235d
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/metrics/workqueue.go
@@ -0,0 +1,130 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package metrics
+
+import (
+ "github.com/prometheus/client_golang/prometheus"
+ "k8s.io/client-go/util/workqueue"
+)
+
+// This file is copied and adapted from k8s.io/kubernetes/pkg/util/workqueue/prometheus
+// which registers metrics to the default prometheus Registry. We require very
+// similar functionality, but must register metrics to a different Registry.
+
+// Metrics subsystem and all keys used by the workqueue.
+const (
+ WorkQueueSubsystem = "workqueue"
+ DepthKey = "depth"
+ AddsKey = "adds_total"
+ QueueLatencyKey = "queue_duration_seconds"
+ WorkDurationKey = "work_duration_seconds"
+ UnfinishedWorkKey = "unfinished_work_seconds"
+ LongestRunningProcessorKey = "longest_running_processor_seconds"
+ RetriesKey = "retries_total"
+)
+
+var (
+ depth = prometheus.NewGaugeVec(prometheus.GaugeOpts{
+ Subsystem: WorkQueueSubsystem,
+ Name: DepthKey,
+ Help: "Current depth of workqueue",
+ }, []string{"name"})
+
+ adds = prometheus.NewCounterVec(prometheus.CounterOpts{
+ Subsystem: WorkQueueSubsystem,
+ Name: AddsKey,
+ Help: "Total number of adds handled by workqueue",
+ }, []string{"name"})
+
+ 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, 10),
+ }, []string{"name"})
+
+ 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, 10),
+ }, []string{"name"})
+
+ unfinished = prometheus.NewGaugeVec(prometheus.GaugeOpts{
+ Subsystem: WorkQueueSubsystem,
+ Name: UnfinishedWorkKey,
+ Help: "How many seconds of work has been done that " +
+ "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"})
+
+ longestRunningProcessor = prometheus.NewGaugeVec(prometheus.GaugeOpts{
+ Subsystem: WorkQueueSubsystem,
+ Name: LongestRunningProcessorKey,
+ Help: "How many seconds has the longest running " +
+ "processor for workqueue been running.",
+ }, []string{"name"})
+
+ retries = prometheus.NewCounterVec(prometheus.CounterOpts{
+ Subsystem: WorkQueueSubsystem,
+ Name: RetriesKey,
+ Help: "Total number of retries handled by workqueue",
+ }, []string{"name"})
+)
+
+func init() {
+ Registry.MustRegister(depth)
+ Registry.MustRegister(adds)
+ Registry.MustRegister(latency)
+ Registry.MustRegister(workDuration)
+ Registry.MustRegister(unfinished)
+ Registry.MustRegister(longestRunningProcessor)
+ Registry.MustRegister(retries)
+
+ workqueue.SetProvider(workqueueMetricsProvider{})
+}
+
+type workqueueMetricsProvider struct{}
+
+func (workqueueMetricsProvider) NewDepthMetric(name string) workqueue.GaugeMetric {
+ return depth.WithLabelValues(name)
+}
+
+func (workqueueMetricsProvider) NewAddsMetric(name string) workqueue.CounterMetric {
+ return adds.WithLabelValues(name)
+}
+
+func (workqueueMetricsProvider) NewLatencyMetric(name string) workqueue.HistogramMetric {
+ return latency.WithLabelValues(name)
+}
+
+func (workqueueMetricsProvider) NewWorkDurationMetric(name string) workqueue.HistogramMetric {
+ return workDuration.WithLabelValues(name)
+}
+
+func (workqueueMetricsProvider) NewUnfinishedWorkSecondsMetric(name string) workqueue.SettableGaugeMetric {
+ return unfinished.WithLabelValues(name)
+}
+
+func (workqueueMetricsProvider) NewLongestRunningProcessorSecondsMetric(name string) workqueue.SettableGaugeMetric {
+ return longestRunningProcessor.WithLabelValues(name)
+}
+
+func (workqueueMetricsProvider) NewRetriesMetric(name string) workqueue.CounterMetric {
+ return retries.WithLabelValues(name)
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/predicate/doc.go b/vendor/sigs.k8s.io/controller-runtime/pkg/predicate/doc.go
new file mode 100644
index 000000000..e498107ef
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/predicate/doc.go
@@ -0,0 +1,20 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+/*
+Package predicate defines Predicates used by Controllers to filter Events before they are provided to EventHandlers.
+*/
+package predicate
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/predicate/predicate.go b/vendor/sigs.k8s.io/controller-runtime/pkg/predicate/predicate.go
new file mode 100644
index 000000000..fc59d89ba
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/predicate/predicate.go
@@ -0,0 +1,333 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package predicate
+
+import (
+ "reflect"
+
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/labels"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/event"
+ logf "sigs.k8s.io/controller-runtime/pkg/internal/log"
+)
+
+var log = logf.RuntimeLog.WithName("predicate").WithName("eventFilters")
+
+// Predicate filters events before enqueuing the keys.
+type Predicate interface {
+ // Create returns true if the Create event should be processed
+ Create(event.CreateEvent) bool
+
+ // Delete returns true if the Delete event should be processed
+ Delete(event.DeleteEvent) bool
+
+ // Update returns true if the Update event should be processed
+ Update(event.UpdateEvent) bool
+
+ // Generic returns true if the Generic event should be processed
+ Generic(event.GenericEvent) bool
+}
+
+var _ Predicate = Funcs{}
+var _ Predicate = ResourceVersionChangedPredicate{}
+var _ Predicate = GenerationChangedPredicate{}
+var _ Predicate = AnnotationChangedPredicate{}
+var _ Predicate = or{}
+var _ Predicate = and{}
+
+// Funcs is a function that implements Predicate.
+type Funcs struct {
+ // Create returns true if the Create event should be processed
+ CreateFunc func(event.CreateEvent) bool
+
+ // Delete returns true if the Delete event should be processed
+ DeleteFunc func(event.DeleteEvent) bool
+
+ // Update returns true if the Update event should be processed
+ UpdateFunc func(event.UpdateEvent) bool
+
+ // Generic returns true if the Generic event should be processed
+ GenericFunc func(event.GenericEvent) bool
+}
+
+// Create implements Predicate.
+func (p Funcs) Create(e event.CreateEvent) bool {
+ if p.CreateFunc != nil {
+ return p.CreateFunc(e)
+ }
+ return true
+}
+
+// Delete implements Predicate.
+func (p Funcs) Delete(e event.DeleteEvent) bool {
+ if p.DeleteFunc != nil {
+ return p.DeleteFunc(e)
+ }
+ return true
+}
+
+// Update implements Predicate.
+func (p Funcs) Update(e event.UpdateEvent) bool {
+ if p.UpdateFunc != nil {
+ return p.UpdateFunc(e)
+ }
+ return true
+}
+
+// Generic implements Predicate.
+func (p Funcs) Generic(e event.GenericEvent) bool {
+ if p.GenericFunc != nil {
+ return p.GenericFunc(e)
+ }
+ return true
+}
+
+// NewPredicateFuncs 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 NewPredicateFuncs(filter func(object client.Object) bool) Funcs {
+ return Funcs{
+ CreateFunc: func(e event.CreateEvent) bool {
+ return filter(e.Object)
+ },
+ UpdateFunc: func(e event.UpdateEvent) bool {
+ return filter(e.ObjectNew)
+ },
+ DeleteFunc: func(e event.DeleteEvent) bool {
+ return filter(e.Object)
+ },
+ GenericFunc: func(e event.GenericEvent) bool {
+ return filter(e.Object)
+ },
+ }
+}
+
+// ResourceVersionChangedPredicate implements a default update predicate function on resource version change.
+type ResourceVersionChangedPredicate struct {
+ Funcs
+}
+
+// Update implements default UpdateEvent filter for validating resource version change.
+func (ResourceVersionChangedPredicate) Update(e event.UpdateEvent) bool {
+ if e.ObjectOld == nil {
+ log.Error(nil, "Update event has no old object to update", "event", e)
+ return false
+ }
+ if e.ObjectNew == nil {
+ log.Error(nil, "Update event has no new object to update", "event", e)
+ return false
+ }
+
+ return e.ObjectNew.GetResourceVersion() != e.ObjectOld.GetResourceVersion()
+}
+
+// GenerationChangedPredicate 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 GenerationChangedPredicate struct {
+ Funcs
+}
+
+// Update implements default UpdateEvent filter for validating generation change.
+func (GenerationChangedPredicate) Update(e event.UpdateEvent) bool {
+ if e.ObjectOld == nil {
+ log.Error(nil, "Update event has no old object to update", "event", e)
+ return false
+ }
+ if e.ObjectNew == nil {
+ log.Error(nil, "Update event has no new object for update", "event", e)
+ return false
+ }
+
+ return e.ObjectNew.GetGeneration() != e.ObjectOld.GetGeneration()
+}
+
+// AnnotationChangedPredicate implements a default update predicate function on annotation change.
+//
+// This predicate will skip update events that have no change in the object's annotation.
+// It is intended to be used in conjunction with the GenerationChangedPredicate, as in the following example:
+//
+// Controller.Watch(
+// &source.Kind{Type: v1.MyCustomKind},
+// &handler.EnqueueRequestForObject{},
+// predicate.Or(predicate.GenerationChangedPredicate{}, predicate.AnnotationChangedPredicate{}))
+//
+// 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
+}
+
+// Update implements default UpdateEvent filter for validating annotation change.
+func (AnnotationChangedPredicate) Update(e event.UpdateEvent) bool {
+ if e.ObjectOld == nil {
+ log.Error(nil, "Update event has no old object to update", "event", e)
+ return false
+ }
+ if e.ObjectNew == nil {
+ log.Error(nil, "Update event has no new object for update", "event", e)
+ return false
+ }
+
+ return !reflect.DeepEqual(e.ObjectNew.GetAnnotations(), e.ObjectOld.GetAnnotations())
+}
+
+// LabelChangedPredicate implements a default update predicate function on label change.
+//
+// This predicate will skip update events that have no change in the object's label.
+// It is intended to be used in conjunction with the GenerationChangedPredicate, as in the following example:
+//
+// Controller.Watch(
+// &source.Kind{Type: v1.MyCustomKind},
+// &handler.EnqueueRequestForObject{},
+// predicate.Or(predicate.GenerationChangedPredicate{}, predicate.LabelChangedPredicate{}))
+//
+// 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
+}
+
+// Update implements default UpdateEvent filter for checking label change.
+func (LabelChangedPredicate) Update(e event.UpdateEvent) bool {
+ if e.ObjectOld == nil {
+ log.Error(nil, "Update event has no old object to update", "event", e)
+ return false
+ }
+ if e.ObjectNew == nil {
+ log.Error(nil, "Update event has no new object for update", "event", e)
+ return false
+ }
+
+ return !reflect.DeepEqual(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}
+}
+
+type and struct {
+ predicates []Predicate
+}
+
+func (a and) Create(e event.CreateEvent) bool {
+ for _, p := range a.predicates {
+ if !p.Create(e) {
+ return false
+ }
+ }
+ return true
+}
+
+func (a and) Update(e event.UpdateEvent) bool {
+ for _, p := range a.predicates {
+ if !p.Update(e) {
+ return false
+ }
+ }
+ return true
+}
+
+func (a and) Delete(e event.DeleteEvent) bool {
+ for _, p := range a.predicates {
+ if !p.Delete(e) {
+ return false
+ }
+ }
+ return true
+}
+
+func (a and) Generic(e event.GenericEvent) bool {
+ for _, p := range a.predicates {
+ if !p.Generic(e) {
+ return false
+ }
+ }
+ return true
+}
+
+// Or returns a composite predicate that implements a logical OR of the predicates passed to it.
+func Or(predicates ...Predicate) Predicate {
+ return or{predicates}
+}
+
+type or struct {
+ predicates []Predicate
+}
+
+func (o or) Create(e event.CreateEvent) bool {
+ for _, p := range o.predicates {
+ if p.Create(e) {
+ return true
+ }
+ }
+ return false
+}
+
+func (o or) Update(e event.UpdateEvent) bool {
+ for _, p := range o.predicates {
+ if p.Update(e) {
+ return true
+ }
+ }
+ return false
+}
+
+func (o or) Delete(e event.DeleteEvent) bool {
+ for _, p := range o.predicates {
+ if p.Delete(e) {
+ return true
+ }
+ }
+ return false
+}
+
+func (o or) Generic(e event.GenericEvent) bool {
+ for _, p := range o.predicates {
+ if p.Generic(e) {
+ return true
+ }
+ }
+ return false
+}
+
+// LabelSelectorPredicate constructs a Predicate from a LabelSelector.
+// Only objects matching the LabelSelector will be admitted.
+func LabelSelectorPredicate(s metav1.LabelSelector) (Predicate, error) {
+ selector, err := metav1.LabelSelectorAsSelector(&s)
+ if err != nil {
+ return Funcs{}, err
+ }
+ return NewPredicateFuncs(func(o client.Object) bool {
+ return selector.Matches(labels.Set(o.GetLabels()))
+ }), nil
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/ratelimiter/doc.go b/vendor/sigs.k8s.io/controller-runtime/pkg/ratelimiter/doc.go
new file mode 100644
index 000000000..a01d603fe
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/ratelimiter/doc.go
@@ -0,0 +1,22 @@
+/*
+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 defines rate limiters used by Controllers to limit how frequently requests may be queued.
+
+Typical rate limiters that can be used are implemented in client-go's workqueue package.
+*/
+package ratelimiter
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/ratelimiter/ratelimiter.go b/vendor/sigs.k8s.io/controller-runtime/pkg/ratelimiter/ratelimiter.go
new file mode 100644
index 000000000..565a3a227
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/ratelimiter/ratelimiter.go
@@ -0,0 +1,30 @@
+/*
+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
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/reconcile/doc.go b/vendor/sigs.k8s.io/controller-runtime/pkg/reconcile/doc.go
new file mode 100644
index 000000000..d221dd7b3
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/reconcile/doc.go
@@ -0,0 +1,21 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+/*
+Package reconcile defines the Reconciler interface to implement Kubernetes APIs. Reconciler is provided
+to Controllers at creation time as the API implementation.
+*/
+package reconcile
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/reconcile/reconcile.go b/vendor/sigs.k8s.io/controller-runtime/pkg/reconcile/reconcile.go
new file mode 100644
index 000000000..b2159c531
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/reconcile/reconcile.go
@@ -0,0 +1,102 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package reconcile
+
+import (
+ "context"
+ "time"
+
+ "k8s.io/apimachinery/pkg/types"
+)
+
+// Result contains the result of a Reconciler invocation.
+type Result struct {
+ // Requeue tells the Controller to requeue the reconcile key. Defaults to false.
+ Requeue bool
+
+ // RequeueAfter if greater than 0, tells the Controller to requeue the reconcile key after the Duration.
+ // Implies that Requeue is true, there is no need to set Requeue to true at the same time as RequeueAfter.
+ RequeueAfter time.Duration
+}
+
+// IsZero returns true if this result is empty.
+func (r *Result) IsZero() bool {
+ if r == nil {
+ return true
+ }
+ return *r == Result{}
+}
+
+// Request contains the information necessary to reconcile a Kubernetes object. This includes the
+// information to uniquely identify the object - its Name and Namespace. It does NOT contain information about
+// any specific Event or the object contents itself.
+type Request struct {
+ // NamespacedName is the name and namespace of the object to reconcile.
+ types.NamespacedName
+}
+
+/*
+Reconciler implements a Kubernetes 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).
+
+reconcile implementations compare the state specified in an object by a user against the actual cluster state,
+and then perform operations to make the actual cluster state reflect the state specified by the user.
+
+Typically, reconcile is triggered by a Controller in response to cluster Events (e.g. Creating, Updating,
+Deleting Kubernetes objects) or external Events (GitHub Webhooks, polling external sources, etc).
+
+Example reconcile Logic:
+
+ * Read an object and all the Pods it owns.
+ * Observe that the object spec specifies 5 replicas but actual cluster contains only 1 Pod replica.
+ * Create 4 Pods and set their OwnerReferences to the object.
+
+reconcile may be implemented as either a type:
+
+ type reconcile struct {}
+
+ func (reconcile) reconcile(controller.Request) (controller.Result, error) {
+ // Implement business logic of reading and writing objects here
+ return controller.Result{}, nil
+ }
+
+Or as a function:
+
+ controller.Func(func(o controller.Request) (controller.Result, error) {
+ // Implement business logic of reading and writing objects here
+ return controller.Result{}, nil
+ })
+
+Reconciliation is level-based, meaning action isn't driven off changes in individual Events, but instead is
+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 {
+ // Reconciler performs a full reconciliation for the object referred to by the Request.
+ // The Controller will requeue the Request to be processed again if an error is non-nil or
+ // Result.Requeue is true, otherwise upon completion it will remove the work from the queue.
+ Reconcile(context.Context, Request) (Result, error)
+}
+
+// Func is a function that implements the reconcile interface.
+type Func 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) }
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/recorder/recorder.go b/vendor/sigs.k8s.io/controller-runtime/pkg/recorder/recorder.go
new file mode 100644
index 000000000..f093f0a72
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/recorder/recorder.go
@@ -0,0 +1,31 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Package recorder defines interfaces for working with Kubernetes event recorders.
+//
+// You can use these to emit Kubernetes events associated with a particular Kubernetes
+// object.
+package recorder
+
+import (
+ "k8s.io/client-go/tools/record"
+)
+
+// Provider knows how to generate new event recorders with given name.
+type Provider interface {
+ // NewRecorder returns an EventRecorder with given name.
+ GetEventRecorderFor(name string) record.EventRecorder
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/runtime/inject/doc.go b/vendor/sigs.k8s.io/controller-runtime/pkg/runtime/inject/doc.go
new file mode 100644
index 000000000..17c60895f
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/runtime/inject/doc.go
@@ -0,0 +1,22 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+/*
+Package inject defines interfaces and functions for propagating dependencies from a ControllerManager to
+the components registered with it. Dependencies are propagated to Reconciler, Source, EventHandler and Predicate
+objects which implement the Injectable interfaces.
+*/
+package inject
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/runtime/inject/inject.go b/vendor/sigs.k8s.io/controller-runtime/pkg/runtime/inject/inject.go
new file mode 100644
index 000000000..c8c56ba81
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/runtime/inject/inject.go
@@ -0,0 +1,164 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Package inject is used by a Manager to inject types into Sources, EventHandlers, Predicates, and Reconciles.
+// Deprecated: Use manager.Options fields directly. This package will be removed in v0.10.
+package inject
+
+import (
+ "github.com/go-logr/logr"
+ "k8s.io/apimachinery/pkg/api/meta"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/client-go/rest"
+
+ "sigs.k8s.io/controller-runtime/pkg/cache"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+)
+
+// Cache is used by the ControllerManager to inject Cache into Sources, EventHandlers, Predicates, and
+// Reconciles.
+type Cache interface {
+ InjectCache(cache cache.Cache) error
+}
+
+// CacheInto will set informers on i and return the result if it implements Cache. Returns
+// false if i does not implement Cache.
+func CacheInto(c cache.Cache, i interface{}) (bool, error) {
+ if s, ok := i.(Cache); ok {
+ return true, s.InjectCache(c)
+ }
+ return false, nil
+}
+
+// APIReader is used by the Manager to inject the APIReader into necessary types.
+type APIReader interface {
+ InjectAPIReader(client.Reader) error
+}
+
+// APIReaderInto will set APIReader on i and return the result if it implements APIReaderInto.
+// Returns false if i does not implement APIReader.
+func APIReaderInto(reader client.Reader, i interface{}) (bool, error) {
+ if s, ok := i.(APIReader); ok {
+ return true, s.InjectAPIReader(reader)
+ }
+ return false, nil
+}
+
+// Config is used by the ControllerManager to inject Config into Sources, EventHandlers, Predicates, and
+// Reconciles.
+type Config interface {
+ InjectConfig(*rest.Config) error
+}
+
+// ConfigInto will set config on i and return the result if it implements Config. Returns
+// false if i does not implement Config.
+func ConfigInto(config *rest.Config, i interface{}) (bool, error) {
+ if s, ok := i.(Config); ok {
+ return true, s.InjectConfig(config)
+ }
+ return false, nil
+}
+
+// Client is used by the ControllerManager to inject client into Sources, EventHandlers, Predicates, and
+// Reconciles.
+type Client interface {
+ InjectClient(client.Client) error
+}
+
+// ClientInto will set client on i and return the result if it implements Client. Returns
+// false if i does not implement Client.
+func ClientInto(client client.Client, i interface{}) (bool, error) {
+ if s, ok := i.(Client); ok {
+ return true, s.InjectClient(client)
+ }
+ return false, nil
+}
+
+// Scheme is used by the ControllerManager to inject Scheme into Sources, EventHandlers, Predicates, and
+// Reconciles.
+type Scheme interface {
+ InjectScheme(scheme *runtime.Scheme) error
+}
+
+// SchemeInto will set scheme and return the result on i if it implements Scheme. Returns
+// false if i does not implement Scheme.
+func SchemeInto(scheme *runtime.Scheme, i interface{}) (bool, error) {
+ if is, ok := i.(Scheme); ok {
+ return true, is.InjectScheme(scheme)
+ }
+ return false, nil
+}
+
+// Stoppable is used by the ControllerManager to inject stop channel into Sources,
+// EventHandlers, Predicates, and Reconciles.
+type Stoppable interface {
+ InjectStopChannel(<-chan struct{}) error
+}
+
+// StopChannelInto will set stop channel on i and return the result if it implements Stoppable.
+// Returns false if i does not implement Stoppable.
+func StopChannelInto(stop <-chan struct{}, i interface{}) (bool, error) {
+ if s, ok := i.(Stoppable); ok {
+ return true, s.InjectStopChannel(stop)
+ }
+ return false, nil
+}
+
+// Mapper is used to inject the rest mapper to components that may need it.
+type Mapper interface {
+ InjectMapper(meta.RESTMapper) error
+}
+
+// MapperInto will set the rest mapper on i and return the result if it implements Mapper.
+// Returns false if i does not implement Mapper.
+func MapperInto(mapper meta.RESTMapper, i interface{}) (bool, error) {
+ if m, ok := i.(Mapper); ok {
+ return true, m.InjectMapper(mapper)
+ }
+ return false, nil
+}
+
+// Func injects dependencies into i.
+type Func func(i interface{}) error
+
+// Injector is used by the ControllerManager to inject Func into Controllers.
+type Injector interface {
+ InjectFunc(f Func) error
+}
+
+// InjectorInto will set f and return the result on i if it implements Injector. Returns
+// false if i does not implement Injector.
+func InjectorInto(f Func, i interface{}) (bool, error) {
+ if ii, ok := i.(Injector); ok {
+ return true, ii.InjectFunc(f)
+ }
+ return false, nil
+}
+
+// Logger is used to inject Loggers into components that need them
+// and don't otherwise have opinions.
+type Logger interface {
+ InjectLogger(l logr.Logger) error
+}
+
+// LoggerInto will set the logger on the given object if it implements inject.Logger,
+// returning true if a InjectLogger was called, and false otherwise.
+func LoggerInto(l logr.Logger, i interface{}) (bool, error) {
+ if injectable, wantsLogger := i.(Logger); wantsLogger {
+ return true, injectable.InjectLogger(l)
+ }
+ return false, nil
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/scheme/scheme.go b/vendor/sigs.k8s.io/controller-runtime/pkg/scheme/scheme.go
new file mode 100644
index 000000000..9dc93a9b2
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/scheme/scheme.go
@@ -0,0 +1,94 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Package scheme contains utilities for gradually building Schemes,
+// which contain information associating Go types with Kubernetes
+// groups, versions, and kinds.
+//
+// Each API group should define a utility function
+// called AddToScheme for adding its types to a Scheme:
+//
+// // in package myapigroupv1...
+// var (
+// SchemeGroupVersion = schema.GroupVersion{Group: "my.api.group", Version: "v1"}
+// SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion}
+// AddToScheme = SchemeBuilder.AddToScheme
+// )
+//
+// func init() {
+// SchemeBuilder.Register(&MyType{}, &MyTypeList)
+// }
+// var (
+// scheme *runtime.Scheme = runtime.NewScheme()
+// )
+//
+// This also true of the built-in Kubernetes types. Then, in the entrypoint for
+// your manager, assemble the scheme containing exactly the types you need,
+// panicing if scheme registration failed. For instance, if our controller needs
+// types from the core/v1 API group (e.g. Pod), plus types from my.api.group/v1:
+//
+// func init() {
+// utilruntime.Must(myapigroupv1.AddToScheme(scheme))
+// utilruntime.Must(kubernetesscheme.AddToScheme(scheme))
+// }
+//
+// func main() {
+// mgr := controllers.NewManager(context.Background(), controllers.GetConfigOrDie(), manager.Options{
+// Scheme: scheme,
+// })
+// // ...
+// }
+//
+package scheme
+
+import (
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+)
+
+// Builder builds a new Scheme for mapping go types to Kubernetes GroupVersionKinds.
+type Builder struct {
+ GroupVersion schema.GroupVersion
+ runtime.SchemeBuilder
+}
+
+// Register adds one or more objects to the SchemeBuilder so they can be added to a Scheme. Register mutates bld.
+func (bld *Builder) Register(object ...runtime.Object) *Builder {
+ bld.SchemeBuilder.Register(func(scheme *runtime.Scheme) error {
+ scheme.AddKnownTypes(bld.GroupVersion, object...)
+ metav1.AddToGroupVersion(scheme, bld.GroupVersion)
+ return nil
+ })
+ return bld
+}
+
+// RegisterAll registers all types from the Builder argument. RegisterAll mutates bld.
+func (bld *Builder) RegisterAll(b *Builder) *Builder {
+ bld.SchemeBuilder = append(bld.SchemeBuilder, b.SchemeBuilder...)
+ return bld
+}
+
+// AddToScheme adds all registered types to s.
+func (bld *Builder) AddToScheme(s *runtime.Scheme) error {
+ return bld.SchemeBuilder.AddToScheme(s)
+}
+
+// Build returns a new Scheme containing the registered types.
+func (bld *Builder) Build() (*runtime.Scheme, error) {
+ s := runtime.NewScheme()
+ return s, bld.AddToScheme(s)
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/source/doc.go b/vendor/sigs.k8s.io/controller-runtime/pkg/source/doc.go
new file mode 100644
index 000000000..31935c83c
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/source/doc.go
@@ -0,0 +1,22 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+/*
+Package source provides event streams to hook up to Controllers with Controller.Watch. Events are
+used with handler.EventHandlers to enqueue reconcile.Requests and trigger Reconciles for Kubernetes
+objects.
+*/
+package source
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/source/internal/eventsource.go b/vendor/sigs.k8s.io/controller-runtime/pkg/source/internal/eventsource.go
new file mode 100644
index 000000000..f0cfe212e
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/source/internal/eventsource.go
@@ -0,0 +1,138 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package internal
+
+import (
+ "fmt"
+
+ "k8s.io/client-go/tools/cache"
+ "k8s.io/client-go/util/workqueue"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/event"
+ "sigs.k8s.io/controller-runtime/pkg/handler"
+ logf "sigs.k8s.io/controller-runtime/pkg/internal/log"
+
+ "sigs.k8s.io/controller-runtime/pkg/predicate"
+)
+
+var log = logf.RuntimeLog.WithName("source").WithName("EventHandler")
+
+var _ cache.ResourceEventHandler = EventHandler{}
+
+// EventHandler adapts a handler.EventHandler interface to a cache.ResourceEventHandler interface.
+type EventHandler struct {
+ EventHandler handler.EventHandler
+ Queue workqueue.RateLimitingInterface
+ Predicates []predicate.Predicate
+}
+
+// OnAdd creates CreateEvent and calls Create on EventHandler.
+func (e EventHandler) OnAdd(obj interface{}) {
+ c := event.CreateEvent{}
+
+ // Pull Object out of the object
+ if o, ok := obj.(client.Object); ok {
+ c.Object = o
+ } else {
+ log.Error(nil, "OnAdd missing Object",
+ "object", obj, "type", fmt.Sprintf("%T", obj))
+ return
+ }
+
+ for _, p := range e.Predicates {
+ if !p.Create(c) {
+ return
+ }
+ }
+
+ // Invoke create handler
+ e.EventHandler.Create(c, e.Queue)
+}
+
+// OnUpdate creates UpdateEvent and calls Update on EventHandler.
+func (e EventHandler) OnUpdate(oldObj, newObj interface{}) {
+ u := event.UpdateEvent{}
+
+ if o, ok := oldObj.(client.Object); ok {
+ u.ObjectOld = o
+ } else {
+ log.Error(nil, "OnUpdate missing ObjectOld",
+ "object", oldObj, "type", fmt.Sprintf("%T", oldObj))
+ return
+ }
+
+ // Pull Object out of the object
+ if o, ok := newObj.(client.Object); ok {
+ u.ObjectNew = o
+ } else {
+ log.Error(nil, "OnUpdate missing ObjectNew",
+ "object", newObj, "type", fmt.Sprintf("%T", newObj))
+ return
+ }
+
+ for _, p := range e.Predicates {
+ if !p.Update(u) {
+ return
+ }
+ }
+
+ // Invoke update handler
+ e.EventHandler.Update(u, e.Queue)
+}
+
+// OnDelete creates DeleteEvent and calls Delete on EventHandler.
+func (e EventHandler) OnDelete(obj interface{}) {
+ d := event.DeleteEvent{}
+
+ // Deal with tombstone events by pulling the object out. Tombstone events wrap the object in a
+ // DeleteFinalStateUnknown struct, so the object needs to be pulled out.
+ // Copied from sample-controller
+ // This should never happen if we aren't missing events, which we have concluded that we are not
+ // and made decisions off of this belief. Maybe this shouldn't be here?
+ var ok bool
+ if _, ok = obj.(client.Object); !ok {
+ // If the object doesn't have Metadata, assume it is a tombstone object of type DeletedFinalStateUnknown
+ tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
+ if !ok {
+ log.Error(nil, "Error decoding objects. Expected cache.DeletedFinalStateUnknown",
+ "type", fmt.Sprintf("%T", obj),
+ "object", obj)
+ return
+ }
+
+ // Set obj to the tombstone obj
+ obj = tombstone.Obj
+ }
+
+ // Pull Object out of the object
+ if o, ok := obj.(client.Object); ok {
+ d.Object = o
+ } else {
+ log.Error(nil, "OnDelete missing Object",
+ "object", obj, "type", fmt.Sprintf("%T", obj))
+ return
+ }
+
+ for _, p := range e.Predicates {
+ if !p.Delete(d) {
+ return
+ }
+ }
+
+ // Invoke delete handler
+ e.EventHandler.Delete(d, e.Queue)
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/source/source.go b/vendor/sigs.k8s.io/controller-runtime/pkg/source/source.go
new file mode 100644
index 000000000..8f649eaac
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/source/source.go
@@ -0,0 +1,366 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package source
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "sync"
+ "time"
+
+ "k8s.io/apimachinery/pkg/api/meta"
+ "k8s.io/apimachinery/pkg/util/wait"
+ "k8s.io/client-go/util/workqueue"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/event"
+ "sigs.k8s.io/controller-runtime/pkg/handler"
+ logf "sigs.k8s.io/controller-runtime/pkg/internal/log"
+ "sigs.k8s.io/controller-runtime/pkg/runtime/inject"
+ "sigs.k8s.io/controller-runtime/pkg/source/internal"
+
+ "sigs.k8s.io/controller-runtime/pkg/cache"
+ "sigs.k8s.io/controller-runtime/pkg/predicate"
+)
+
+var log = logf.RuntimeLog.WithName("source")
+
+const (
+ // defaultBufferSize is the default number of event notifications that can be buffered.
+ defaultBufferSize = 1024
+)
+
+// Source is a source of events (eh.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).
+//
+// Users may build their own Source implementations. If their implementations implement any of the inject package
+// interfaces, the dependencies will be injected by the Controller when Watch is called.
+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
+}
+
+// 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
+ WaitForSync(ctx context.Context) error
+}
+
+// NewKindWithCache creates a Source without InjectCache, so that it is assured that the given cache is used
+// and not overwritten. It can be used to watch objects in a different cluster by passing the cache
+// from that other cluster.
+func NewKindWithCache(object client.Object, cache cache.Cache) SyncingSource {
+ return &kindWithCache{kind: Kind{Type: object, cache: cache}}
+}
+
+type kindWithCache struct {
+ kind Kind
+}
+
+func (ks *kindWithCache) Start(ctx context.Context, handler handler.EventHandler, queue workqueue.RateLimitingInterface,
+ prct ...predicate.Predicate) error {
+ return ks.kind.Start(ctx, handler, queue, prct...)
+}
+
+func (ks *kindWithCache) WaitForSync(ctx context.Context) error {
+ return ks.kind.WaitForSync(ctx)
+}
+
+// Kind is used to provide a source of events originating inside the cluster from Watches (e.g. Pod Create).
+type Kind struct {
+ // Type is the type of object to watch. e.g. &v1.Pod{}
+ Type client.Object
+
+ // cache used to watch APIs
+ cache cache.Cache
+
+ // started may contain an error if one was encountered during startup. If its closed and does not
+ // contain an error, startup and syncing finished.
+ started chan error
+ startCancel func()
+}
+
+var _ SyncingSource = &Kind{}
+
+// Start is internal and should be called only by the Controller to register an EventHandler with the Informer
+// to enqueue reconcile.Requests.
+func (ks *Kind) Start(ctx context.Context, handler handler.EventHandler, queue workqueue.RateLimitingInterface,
+ prct ...predicate.Predicate) error {
+ // Type should have been specified by the user.
+ if ks.Type == nil {
+ return fmt.Errorf("must specify Kind.Type")
+ }
+
+ // cache should have been injected before Start was called
+ if ks.cache == nil {
+ return fmt.Errorf("must call CacheInto on Kind before calling Start")
+ }
+
+ // cache.GetInformer will block until its context is cancelled if the cache was already started and it can not
+ // sync that informer (most commonly due to RBAC issues).
+ ctx, ks.startCancel = context.WithCancel(ctx)
+ ks.started = make(chan error)
+ go func() {
+ var (
+ i cache.Informer
+ lastErr error
+ )
+
+ // Tries to get an informer until it returns true,
+ // an error or the specified context is cancelled or expired.
+ if err := wait.PollImmediateUntilWithContext(ctx, 10*time.Second, func(ctx context.Context) (bool, error) {
+ // Lookup the Informer from the Cache and add an EventHandler which populates the Queue
+ i, lastErr = ks.cache.GetInformer(ctx, ks.Type)
+ if lastErr != nil {
+ kindMatchErr := &meta.NoKindMatchError{}
+ if errors.As(lastErr, &kindMatchErr) {
+ log.Error(lastErr, "if kind is a CRD, it should be installed before calling Start",
+ "kind", kindMatchErr.GroupKind)
+ }
+ return false, nil // Retry.
+ }
+ return true, nil
+ }); err != nil {
+ if lastErr != nil {
+ ks.started <- fmt.Errorf("failed to get informer from cache: %w", lastErr)
+ return
+ }
+ ks.started <- err
+ return
+ }
+
+ i.AddEventHandler(internal.EventHandler{Queue: queue, EventHandler: handler, Predicates: prct})
+ if !ks.cache.WaitForCacheSync(ctx) {
+ // Would be great to return something more informative here
+ ks.started <- errors.New("cache did not sync")
+ }
+ close(ks.started)
+ }()
+
+ return nil
+}
+
+func (ks *Kind) String() string {
+ if ks.Type != nil {
+ return fmt.Sprintf("kind source: %T", ks.Type)
+ }
+ return "kind source: unknown type"
+}
+
+// WaitForSync implements SyncingSource to allow controllers to wait with starting
+// workers until the cache is synced.
+func (ks *Kind) WaitForSync(ctx context.Context) error {
+ select {
+ case err := <-ks.started:
+ return err
+ case <-ctx.Done():
+ ks.startCancel()
+ return errors.New("timed out waiting for cache to be synced")
+ }
+}
+
+var _ inject.Cache = &Kind{}
+
+// InjectCache is internal should be called only by the Controller. InjectCache is used to inject
+// the Cache dependency initialized by the ControllerManager.
+func (ks *Kind) InjectCache(c cache.Cache) error {
+ if ks.cache == nil {
+ ks.cache = c
+ }
+ return nil
+}
+
+var _ Source = &Channel{}
+
+// 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 {
+ // 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
+
+ // stop is to end ongoing goroutine, and close the channels
+ stop <-chan struct{}
+
+ // dest is the destination channels of the added event handlers
+ dest []chan event.GenericEvent
+
+ // DestBufferSize is the specified buffer size of dest channels.
+ // Default to 1024 if not specified.
+ DestBufferSize int
+
+ // destLock is to ensure the destination channels are safely added/removed
+ destLock sync.Mutex
+}
+
+func (cs *Channel) String() string {
+ return fmt.Sprintf("channel source: %p", cs)
+}
+
+var _ inject.Stoppable = &Channel{}
+
+// InjectStopChannel is internal should be called only by the Controller.
+// It is used to inject the stop channel initialized by the ControllerManager.
+func (cs *Channel) InjectStopChannel(stop <-chan struct{}) error {
+ if cs.stop == nil {
+ cs.stop = stop
+ }
+
+ return nil
+}
+
+// Start implements Source and should only be called by the Controller.
+func (cs *Channel) Start(
+ ctx context.Context,
+ handler handler.EventHandler,
+ queue workqueue.RateLimitingInterface,
+ prct ...predicate.Predicate) error {
+ // Source should have been specified by the user.
+ if cs.Source == nil {
+ return fmt.Errorf("must specify Channel.Source")
+ }
+
+ // stop should have been injected before Start was called
+ if cs.stop == nil {
+ return fmt.Errorf("must call InjectStop on Channel before calling Start")
+ }
+
+ // use default value if DestBufferSize not specified
+ if cs.DestBufferSize == 0 {
+ cs.DestBufferSize = defaultBufferSize
+ }
+
+ dst := make(chan event.GenericEvent, cs.DestBufferSize)
+
+ cs.destLock.Lock()
+ cs.dest = append(cs.dest, dst)
+ cs.destLock.Unlock()
+
+ cs.once.Do(func() {
+ // Distribute GenericEvents to all EventHandler / Queue pairs Watching this source
+ go cs.syncLoop(ctx)
+ })
+
+ go func() {
+ for evt := range dst {
+ shouldHandle := true
+ for _, p := range prct {
+ if !p.Generic(evt) {
+ shouldHandle = false
+ break
+ }
+ }
+
+ if shouldHandle {
+ handler.Generic(evt, queue)
+ }
+ }
+ }()
+
+ return nil
+}
+
+func (cs *Channel) doStop() {
+ cs.destLock.Lock()
+ defer cs.destLock.Unlock()
+
+ for _, dst := range cs.dest {
+ close(dst)
+ }
+}
+
+func (cs *Channel) distribute(evt event.GenericEvent) {
+ cs.destLock.Lock()
+ defer cs.destLock.Unlock()
+
+ for _, dst := range cs.dest {
+ // We cannot make it under goroutine here, or we'll meet the
+ // race condition of writing message to closed channels.
+ // To avoid blocking, the dest channels are expected to be of
+ // proper buffer size. If we still see it blocked, then
+ // the controller is thought to be in an abnormal state.
+ dst <- evt
+ }
+}
+
+func (cs *Channel) syncLoop(ctx context.Context) {
+ for {
+ select {
+ case <-ctx.Done():
+ // Close destination channels
+ cs.doStop()
+ return
+ 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
+ cs.doStop()
+ return
+ }
+ cs.distribute(evt)
+ }
+ }
+}
+
+// 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
+}
+
+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 {
+ // Informer should have been specified by the user.
+ if is.Informer == nil {
+ return fmt.Errorf("must specify Informer.Informer")
+ }
+
+ is.Informer.AddEventHandler(internal.EventHandler{Queue: queue, EventHandler: handler, Predicates: prct})
+ return nil
+}
+
+func (is *Informer) String() string {
+ return fmt.Sprintf("informer source: %p", is.Informer)
+}
+
+var _ Source = Func(nil)
+
+// Func is a function that implements Source.
+type Func func(context.Context, handler.EventHandler, workqueue.RateLimitingInterface, ...predicate.Predicate) 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 Func) String() string {
+ return fmt.Sprintf("func source: %p", f)
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/decode.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/decode.go
new file mode 100644
index 000000000..c7cb71b75
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/decode.go
@@ -0,0 +1,72 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package admission
+
+import (
+ "fmt"
+
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/serializer"
+ "k8s.io/apimachinery/pkg/util/json"
+)
+
+// 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, error) {
+ return &Decoder{codecs: serializer.NewCodecFactory(scheme)}, nil
+}
+
+// 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 {
+ // we error out if rawObj is an empty object.
+ if len(req.Object.Raw) == 0 {
+ return fmt.Errorf("there is no content to decode")
+ }
+ return d.DecodeRaw(req.Object, into)
+}
+
+// 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 {
+ // 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
+ // also means we can't pass the unstructured directly in, since it'll try
+ // and call unstructured's special Unmarshal implementation, which calls
+ // back into that same decoder :-/
+ // See kubernetes/kubernetes#74373.
+
+ // we error out if rawObj is an empty object.
+ if len(rawObj.Raw) == 0 {
+ return fmt.Errorf("there is no content to decode")
+ }
+ if unstructuredInto, isUnstructured := into.(*unstructured.Unstructured); isUnstructured {
+ // unmarshal into unstructured's underlying object to avoid calling the decoder
+ return json.Unmarshal(rawObj.Raw, &unstructuredInto.Object)
+ }
+
+ deserializer := d.codecs.UniversalDeserializer()
+ return runtime.DecodeInto(deserializer, rawObj.Raw, into)
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/defaulter.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/defaulter.go
new file mode 100644
index 000000000..0d9aa7a83
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/defaulter.go
@@ -0,0 +1,74 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package admission
+
+import (
+ "context"
+ "encoding/json"
+ "net/http"
+
+ "k8s.io/apimachinery/pkg/runtime"
+)
+
+// Defaulter defines functions for setting defaults on resources.
+type Defaulter interface {
+ runtime.Object
+ Default()
+}
+
+// DefaultingWebhookFor creates a new Webhook for Defaulting the provided type.
+func DefaultingWebhookFor(defaulter Defaulter) *Webhook {
+ return &Webhook{
+ Handler: &mutatingHandler{defaulter: defaulter},
+ }
+}
+
+type mutatingHandler struct {
+ defaulter Defaulter
+ decoder *Decoder
+}
+
+var _ DecoderInjector = &mutatingHandler{}
+
+// InjectDecoder injects the decoder into a mutatingHandler.
+func (h *mutatingHandler) InjectDecoder(d *Decoder) error {
+ h.decoder = d
+ return nil
+}
+
+// Handle handles admission requests.
+func (h *mutatingHandler) Handle(ctx context.Context, req Request) Response {
+ if h.defaulter == nil {
+ panic("defaulter should never be nil")
+ }
+
+ // Get the object in the request
+ obj := h.defaulter.DeepCopyObject().(Defaulter)
+ if err := h.decoder.Decode(req, obj); err != nil {
+ return Errored(http.StatusBadRequest, err)
+ }
+
+ // Default the object
+ obj.Default()
+ marshalled, err := json.Marshal(obj)
+ if err != nil {
+ return Errored(http.StatusInternalServerError, err)
+ }
+
+ // Create the patch
+ return PatchResponseFromRaw(req.Object.Raw, marshalled)
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/defaulter_custom.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/defaulter_custom.go
new file mode 100644
index 000000000..a012784e4
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/defaulter_custom.go
@@ -0,0 +1,85 @@
+/*
+Copyright 2021 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package admission
+
+import (
+ "context"
+ "encoding/json"
+
+ "errors"
+ "net/http"
+
+ apierrors "k8s.io/apimachinery/pkg/api/errors"
+ "k8s.io/apimachinery/pkg/runtime"
+)
+
+// CustomDefaulter defines functions for setting defaults on resources.
+type CustomDefaulter interface {
+ Default(ctx context.Context, obj runtime.Object) error
+}
+
+// WithCustomDefaulter creates a new Webhook for a CustomDefaulter interface.
+func WithCustomDefaulter(obj runtime.Object, defaulter CustomDefaulter) *Webhook {
+ return &Webhook{
+ Handler: &defaulterForType{object: obj, defaulter: defaulter},
+ }
+}
+
+type defaulterForType struct {
+ defaulter CustomDefaulter
+ object runtime.Object
+ decoder *Decoder
+}
+
+var _ DecoderInjector = &defaulterForType{}
+
+func (h *defaulterForType) InjectDecoder(d *Decoder) error {
+ h.decoder = d
+ return nil
+}
+
+// Handle handles admission requests.
+func (h *defaulterForType) Handle(ctx context.Context, req Request) Response {
+ if h.defaulter == nil {
+ panic("defaulter should never be nil")
+ }
+ if h.object == nil {
+ panic("object should never be nil")
+ }
+
+ // Get the object in the request
+ obj := h.object.DeepCopyObject()
+ if err := h.decoder.Decode(req, obj); err != nil {
+ return Errored(http.StatusBadRequest, err)
+ }
+
+ // Default the object
+ if err := h.defaulter.Default(ctx, obj); err != nil {
+ var apiStatus apierrors.APIStatus
+ if errors.As(err, &apiStatus) {
+ return validationResponseFromStatus(false, apiStatus.Status())
+ }
+ return Denied(err.Error())
+ }
+
+ // Create the patch
+ marshalled, err := json.Marshal(obj)
+ if err != nil {
+ return Errored(http.StatusInternalServerError, err)
+ }
+ return PatchResponseFromRaw(req.Object.Raw, marshalled)
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/doc.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/doc.go
new file mode 100644
index 000000000..0b274dd02
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/doc.go
@@ -0,0 +1,28 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+/*
+Package admission provides implementation for admission webhook and methods to implement admission webhook handlers.
+
+See examples/mutatingwebhook.go and examples/validatingwebhook.go for examples of admission webhooks.
+*/
+package admission
+
+import (
+ logf "sigs.k8s.io/controller-runtime/pkg/internal/log"
+)
+
+var log = logf.RuntimeLog.WithName("admission")
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/http.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/http.go
new file mode 100644
index 000000000..3fa8872ff
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/http.go
@@ -0,0 +1,146 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package admission
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net/http"
+
+ v1 "k8s.io/api/admission/v1"
+ "k8s.io/api/admission/v1beta1"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "k8s.io/apimachinery/pkg/runtime/serializer"
+ utilruntime "k8s.io/apimachinery/pkg/util/runtime"
+)
+
+var admissionScheme = runtime.NewScheme()
+var admissionCodecs = serializer.NewCodecFactory(admissionScheme)
+
+func init() {
+ utilruntime.Must(v1.AddToScheme(admissionScheme))
+ utilruntime.Must(v1beta1.AddToScheme(admissionScheme))
+}
+
+var _ http.Handler = &Webhook{}
+
+func (wh *Webhook) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ var body []byte
+ var err error
+ ctx := r.Context()
+ if wh.WithContextFunc != nil {
+ ctx = wh.WithContextFunc(ctx, r)
+ }
+
+ var reviewResponse Response
+ if r.Body == nil {
+ err = errors.New("request body is empty")
+ wh.log.Error(err, "bad request")
+ reviewResponse = Errored(http.StatusBadRequest, err)
+ wh.writeResponse(w, reviewResponse)
+ return
+ }
+
+ defer r.Body.Close()
+ if body, err = ioutil.ReadAll(r.Body); err != nil {
+ wh.log.Error(err, "unable to read the body from the incoming request")
+ reviewResponse = Errored(http.StatusBadRequest, err)
+ wh.writeResponse(w, reviewResponse)
+ return
+ }
+
+ // verify the content type is accurate
+ if contentType := r.Header.Get("Content-Type"); contentType != "application/json" {
+ err = fmt.Errorf("contentType=%s, expected application/json", contentType)
+ wh.log.Error(err, "unable to process a request with an unknown content type", "content type", contentType)
+ reviewResponse = Errored(http.StatusBadRequest, err)
+ wh.writeResponse(w, reviewResponse)
+ return
+ }
+
+ // Both v1 and v1beta1 AdmissionReview types are exactly the same, so the v1beta1 type can
+ // be decoded into the v1 type. However the runtime codec's decoder guesses which type to
+ // decode into by type name if an Object's TypeMeta isn't set. By setting TypeMeta of an
+ // unregistered type to the v1 GVK, the decoder will coerce a v1beta1 AdmissionReview to v1.
+ // The actual AdmissionReview GVK will be used to write a typed response in case the
+ // webhook config permits multiple versions, otherwise this response will fail.
+ req := Request{}
+ ar := unversionedAdmissionReview{}
+ // avoid an extra copy
+ ar.Request = &req.AdmissionRequest
+ ar.SetGroupVersionKind(v1.SchemeGroupVersion.WithKind("AdmissionReview"))
+ _, actualAdmRevGVK, err := admissionCodecs.UniversalDeserializer().Decode(body, nil, &ar)
+ if err != nil {
+ wh.log.Error(err, "unable to decode the request")
+ reviewResponse = Errored(http.StatusBadRequest, err)
+ wh.writeResponse(w, reviewResponse)
+ return
+ }
+ wh.log.V(1).Info("received request", "UID", req.UID, "kind", req.Kind, "resource", req.Resource)
+
+ reviewResponse = wh.Handle(ctx, req)
+ wh.writeResponseTyped(w, reviewResponse, actualAdmRevGVK)
+}
+
+// writeResponse writes response to w generically, i.e. without encoding GVK information.
+func (wh *Webhook) writeResponse(w io.Writer, response Response) {
+ wh.writeAdmissionResponse(w, v1.AdmissionReview{Response: &response.AdmissionResponse})
+}
+
+// writeResponseTyped writes response to w with GVK set to admRevGVK, which is necessary
+// if multiple AdmissionReview versions are permitted by the webhook.
+func (wh *Webhook) writeResponseTyped(w io.Writer, response Response, admRevGVK *schema.GroupVersionKind) {
+ ar := v1.AdmissionReview{
+ Response: &response.AdmissionResponse,
+ }
+ // Default to a v1 AdmissionReview, otherwise the API server may not recognize the request
+ // if multiple AdmissionReview versions are permitted by the webhook config.
+ // TODO(estroz): this should be configurable since older API servers won't know about v1.
+ if admRevGVK == nil || *admRevGVK == (schema.GroupVersionKind{}) {
+ ar.SetGroupVersionKind(v1.SchemeGroupVersion.WithKind("AdmissionReview"))
+ } else {
+ ar.SetGroupVersionKind(*admRevGVK)
+ }
+ wh.writeAdmissionResponse(w, ar)
+}
+
+// writeAdmissionResponse writes ar to w.
+func (wh *Webhook) writeAdmissionResponse(w io.Writer, ar v1.AdmissionReview) {
+ if err := json.NewEncoder(w).Encode(ar); err != nil {
+ wh.log.Error(err, "unable to encode the response")
+ wh.writeResponse(w, Errored(http.StatusInternalServerError, err))
+ } else {
+ res := ar.Response
+ if log := wh.log; log.V(1).Enabled() {
+ if res.Result != nil {
+ log = log.WithValues("code", res.Result.Code, "reason", res.Result.Reason)
+ }
+ log.V(1).Info("wrote response", "UID", res.UID, "allowed", res.Allowed)
+ }
+ }
+}
+
+// unversionedAdmissionReview is used to decode both v1 and v1beta1 AdmissionReview types.
+type unversionedAdmissionReview struct {
+ v1.AdmissionReview
+}
+
+var _ runtime.Object = &unversionedAdmissionReview{}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/inject.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/inject.go
new file mode 100644
index 000000000..d5af0d598
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/inject.go
@@ -0,0 +1,31 @@
+/*
+Copyright 2019 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package admission
+
+// DecoderInjector is used by the ControllerManager to inject decoder into webhook handlers.
+type DecoderInjector interface {
+ InjectDecoder(*Decoder) error
+}
+
+// InjectDecoderInto will set decoder on i and return the result if it implements Decoder. Returns
+// false if i does not implement Decoder.
+func InjectDecoderInto(decoder *Decoder, i interface{}) (bool, error) {
+ if s, ok := i.(DecoderInjector); ok {
+ return true, s.InjectDecoder(decoder)
+ }
+ return false, nil
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/multi.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/multi.go
new file mode 100644
index 000000000..26900cf2e
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/multi.go
@@ -0,0 +1,147 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package admission
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "net/http"
+
+ jsonpatch "gomodules.xyz/jsonpatch/v2"
+ admissionv1 "k8s.io/api/admission/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+ "sigs.k8s.io/controller-runtime/pkg/runtime/inject"
+)
+
+type multiMutating []Handler
+
+func (hs multiMutating) Handle(ctx context.Context, req Request) Response {
+ patches := []jsonpatch.JsonPatchOperation{}
+ for _, handler := range hs {
+ resp := handler.Handle(ctx, req)
+ if !resp.Allowed {
+ return resp
+ }
+ if resp.PatchType != nil && *resp.PatchType != admissionv1.PatchTypeJSONPatch {
+ return Errored(http.StatusInternalServerError,
+ fmt.Errorf("unexpected patch type returned by the handler: %v, only allow: %v",
+ resp.PatchType, admissionv1.PatchTypeJSONPatch))
+ }
+ patches = append(patches, resp.Patches...)
+ }
+ var err error
+ marshaledPatch, err := json.Marshal(patches)
+ if err != nil {
+ return Errored(http.StatusBadRequest, fmt.Errorf("error when marshaling the patch: %w", err))
+ }
+ return Response{
+ AdmissionResponse: admissionv1.AdmissionResponse{
+ Allowed: true,
+ Result: &metav1.Status{
+ Code: http.StatusOK,
+ },
+ Patch: marshaledPatch,
+ PatchType: func() *admissionv1.PatchType { pt := admissionv1.PatchTypeJSONPatch; return &pt }(),
+ },
+ }
+}
+
+// InjectFunc injects the field setter into the handlers.
+func (hs multiMutating) InjectFunc(f inject.Func) error {
+ // inject directly into the handlers. It would be more correct
+ // to do this in a sync.Once in Handle (since we don't have some
+ // other start/finalize-type method), but it's more efficient to
+ // do it here, presumably.
+ for _, handler := range hs {
+ if err := f(handler); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+// InjectDecoder injects the decoder into the handlers.
+func (hs multiMutating) InjectDecoder(d *Decoder) error {
+ for _, handler := range hs {
+ if _, err := InjectDecoderInto(d, handler); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// MultiMutatingHandler combines multiple mutating webhook handlers into a single
+// mutating webhook handler. Handlers are called in sequential order, and the first
+// `allowed: false` response may short-circuit the rest. Users must take care to
+// ensure patches are disjoint.
+func MultiMutatingHandler(handlers ...Handler) Handler {
+ return multiMutating(handlers)
+}
+
+type multiValidating []Handler
+
+func (hs multiValidating) Handle(ctx context.Context, req Request) Response {
+ for _, handler := range hs {
+ resp := handler.Handle(ctx, req)
+ if !resp.Allowed {
+ return resp
+ }
+ }
+ return Response{
+ AdmissionResponse: admissionv1.AdmissionResponse{
+ Allowed: true,
+ Result: &metav1.Status{
+ Code: http.StatusOK,
+ },
+ },
+ }
+}
+
+// MultiValidatingHandler combines multiple validating webhook handlers into a single
+// validating webhook handler. Handlers are called in sequential order, and the first
+// `allowed: false` response may short-circuit the rest.
+func MultiValidatingHandler(handlers ...Handler) Handler {
+ return multiValidating(handlers)
+}
+
+// InjectFunc injects the field setter into the handlers.
+func (hs multiValidating) InjectFunc(f inject.Func) error {
+ // inject directly into the handlers. It would be more correct
+ // to do this in a sync.Once in Handle (since we don't have some
+ // other start/finalize-type method), but it's more efficient to
+ // do it here, presumably.
+ for _, handler := range hs {
+ if err := f(handler); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+// InjectDecoder injects the decoder into the handlers.
+func (hs multiValidating) InjectDecoder(d *Decoder) error {
+ for _, handler := range hs {
+ if _, err := InjectDecoderInto(d, handler); err != nil {
+ return err
+ }
+ }
+ return nil
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/response.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/response.go
new file mode 100644
index 000000000..24ff1dee3
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/response.go
@@ -0,0 +1,121 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package admission
+
+import (
+ "net/http"
+
+ jsonpatch "gomodules.xyz/jsonpatch/v2"
+ admissionv1 "k8s.io/api/admission/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+// Allowed constructs a response indicating that the given operation
+// is allowed (without any patches).
+func Allowed(reason string) Response {
+ return ValidationResponse(true, reason)
+}
+
+// Denied constructs a response indicating that the given operation
+// is not allowed.
+func Denied(reason string) Response {
+ return ValidationResponse(false, reason)
+}
+
+// Patched constructs a response indicating that the given operation is
+// allowed, and that the target object should be modified by the given
+// JSONPatch operations.
+func Patched(reason string, patches ...jsonpatch.JsonPatchOperation) Response {
+ resp := Allowed(reason)
+ resp.Patches = patches
+
+ return resp
+}
+
+// Errored creates a new Response for error-handling a request.
+func Errored(code int32, err error) Response {
+ return Response{
+ AdmissionResponse: admissionv1.AdmissionResponse{
+ Allowed: false,
+ Result: &metav1.Status{
+ Code: code,
+ Message: err.Error(),
+ },
+ },
+ }
+}
+
+// ValidationResponse returns a response for admitting a request.
+func ValidationResponse(allowed bool, reason string) Response {
+ code := http.StatusForbidden
+ if allowed {
+ code = http.StatusOK
+ }
+ resp := Response{
+ AdmissionResponse: admissionv1.AdmissionResponse{
+ Allowed: allowed,
+ Result: &metav1.Status{
+ Code: int32(code),
+ },
+ },
+ }
+ if len(reason) > 0 {
+ resp.Result.Reason = metav1.StatusReason(reason)
+ }
+ return resp
+}
+
+// PatchResponseFromRaw takes 2 byte arrays and returns a new response with json patch.
+// The original object should be passed in as raw bytes to avoid the roundtripping problem
+// described in https://github.com/kubernetes-sigs/kubebuilder/issues/510.
+func PatchResponseFromRaw(original, current []byte) Response {
+ patches, err := jsonpatch.CreatePatch(original, current)
+ if err != nil {
+ return Errored(http.StatusInternalServerError, err)
+ }
+ return Response{
+ Patches: patches,
+ AdmissionResponse: admissionv1.AdmissionResponse{
+ Allowed: true,
+ PatchType: func() *admissionv1.PatchType {
+ if len(patches) == 0 {
+ return nil
+ }
+ pt := admissionv1.PatchTypeJSONPatch
+ return &pt
+ }(),
+ },
+ }
+}
+
+// validationResponseFromStatus returns a response for admitting a request with provided Status object.
+func validationResponseFromStatus(allowed bool, status metav1.Status) Response {
+ resp := Response{
+ AdmissionResponse: admissionv1.AdmissionResponse{
+ Allowed: allowed,
+ Result: &status,
+ },
+ }
+ return resp
+}
+
+// WithWarnings adds the given warnings to the Response.
+// If any warnings were already given, they will not be overwritten.
+func (r Response) WithWarnings(warnings ...string) Response {
+ r.AdmissionResponse.Warnings = append(r.AdmissionResponse.Warnings, warnings...)
+ return r
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/validator.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/validator.go
new file mode 100644
index 000000000..4b27e75ed
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/validator.go
@@ -0,0 +1,122 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package admission
+
+import (
+ "context"
+ goerrors "errors"
+ "net/http"
+
+ v1 "k8s.io/api/admission/v1"
+ apierrors "k8s.io/apimachinery/pkg/api/errors"
+ "k8s.io/apimachinery/pkg/runtime"
+)
+
+// Validator defines functions for validating an operation.
+type Validator interface {
+ runtime.Object
+ ValidateCreate() error
+ ValidateUpdate(old runtime.Object) error
+ ValidateDelete() error
+}
+
+// ValidatingWebhookFor creates a new Webhook for validating the provided type.
+func ValidatingWebhookFor(validator Validator) *Webhook {
+ return &Webhook{
+ Handler: &validatingHandler{validator: validator},
+ }
+}
+
+type validatingHandler struct {
+ validator Validator
+ decoder *Decoder
+}
+
+var _ DecoderInjector = &validatingHandler{}
+
+// InjectDecoder injects the decoder into a validatingHandler.
+func (h *validatingHandler) InjectDecoder(d *Decoder) error {
+ h.decoder = d
+ return nil
+}
+
+// Handle handles admission requests.
+func (h *validatingHandler) Handle(ctx context.Context, req Request) Response {
+ if h.validator == nil {
+ panic("validator should never be nil")
+ }
+
+ // Get the object in the request
+ obj := h.validator.DeepCopyObject().(Validator)
+ if req.Operation == v1.Create {
+ err := h.decoder.Decode(req, obj)
+ if err != nil {
+ return Errored(http.StatusBadRequest, err)
+ }
+
+ err = obj.ValidateCreate()
+ if err != nil {
+ var apiStatus apierrors.APIStatus
+ if goerrors.As(err, &apiStatus) {
+ return validationResponseFromStatus(false, apiStatus.Status())
+ }
+ return Denied(err.Error())
+ }
+ }
+
+ if req.Operation == v1.Update {
+ oldObj := obj.DeepCopyObject()
+
+ err := h.decoder.DecodeRaw(req.Object, obj)
+ if err != nil {
+ return Errored(http.StatusBadRequest, err)
+ }
+ err = h.decoder.DecodeRaw(req.OldObject, oldObj)
+ if err != nil {
+ return Errored(http.StatusBadRequest, err)
+ }
+
+ err = obj.ValidateUpdate(oldObj)
+ if err != nil {
+ var apiStatus apierrors.APIStatus
+ if goerrors.As(err, &apiStatus) {
+ return validationResponseFromStatus(false, apiStatus.Status())
+ }
+ return Denied(err.Error())
+ }
+ }
+
+ if req.Operation == v1.Delete {
+ // In reference to PR: https://github.com/kubernetes/kubernetes/pull/76346
+ // OldObject contains the object being deleted
+ err := h.decoder.DecodeRaw(req.OldObject, obj)
+ if err != nil {
+ return Errored(http.StatusBadRequest, err)
+ }
+
+ err = obj.ValidateDelete()
+ if err != nil {
+ var apiStatus apierrors.APIStatus
+ if goerrors.As(err, &apiStatus) {
+ return validationResponseFromStatus(false, apiStatus.Status())
+ }
+ return Denied(err.Error())
+ }
+ }
+
+ return Allowed("")
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/validator_custom.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/validator_custom.go
new file mode 100644
index 000000000..38d556511
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/validator_custom.go
@@ -0,0 +1,111 @@
+/*
+Copyright 2021 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package admission
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "net/http"
+
+ v1 "k8s.io/api/admission/v1"
+ apierrors "k8s.io/apimachinery/pkg/api/errors"
+ "k8s.io/apimachinery/pkg/runtime"
+)
+
+// CustomValidator defines functions for validating an operation.
+type CustomValidator interface {
+ ValidateCreate(ctx context.Context, obj runtime.Object) error
+ ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) error
+ ValidateDelete(ctx context.Context, obj runtime.Object) error
+}
+
+// WithCustomValidator creates a new Webhook for validating the provided type.
+func WithCustomValidator(obj runtime.Object, validator CustomValidator) *Webhook {
+ return &Webhook{
+ Handler: &validatorForType{object: obj, validator: validator},
+ }
+}
+
+type validatorForType struct {
+ validator CustomValidator
+ object runtime.Object
+ decoder *Decoder
+}
+
+var _ DecoderInjector = &validatorForType{}
+
+// InjectDecoder injects the decoder into a validatingHandler.
+func (h *validatorForType) InjectDecoder(d *Decoder) error {
+ h.decoder = d
+ return nil
+}
+
+// Handle handles admission requests.
+func (h *validatorForType) Handle(ctx context.Context, req Request) Response {
+ if h.validator == nil {
+ panic("validator should never be nil")
+ }
+ if h.object == nil {
+ panic("object should never be nil")
+ }
+
+ // Get the object in the request
+ obj := h.object.DeepCopyObject()
+
+ var err error
+ switch req.Operation {
+ case v1.Create:
+ if err := h.decoder.Decode(req, obj); err != nil {
+ return Errored(http.StatusBadRequest, err)
+ }
+
+ err = h.validator.ValidateCreate(ctx, obj)
+ case v1.Update:
+ oldObj := obj.DeepCopyObject()
+ if err := h.decoder.DecodeRaw(req.Object, obj); err != nil {
+ return Errored(http.StatusBadRequest, err)
+ }
+ if err := h.decoder.DecodeRaw(req.OldObject, oldObj); err != nil {
+ return Errored(http.StatusBadRequest, err)
+ }
+
+ err = h.validator.ValidateUpdate(ctx, oldObj, obj)
+ case v1.Delete:
+ // In reference to PR: https://github.com/kubernetes/kubernetes/pull/76346
+ // OldObject contains the object being deleted
+ if err := h.decoder.DecodeRaw(req.OldObject, obj); err != nil {
+ return Errored(http.StatusBadRequest, err)
+ }
+
+ err = h.validator.ValidateDelete(ctx, obj)
+ default:
+ return Errored(http.StatusBadRequest, fmt.Errorf("unknown operation request %q", req.Operation))
+ }
+
+ // Check the error message first.
+ if err != nil {
+ var apiStatus apierrors.APIStatus
+ if errors.As(err, &apiStatus) {
+ return validationResponseFromStatus(false, apiStatus.Status())
+ }
+ return Denied(err.Error())
+ }
+
+ // Return allowed if everything succeeded.
+ return Allowed("")
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/webhook.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/webhook.go
new file mode 100644
index 000000000..cf7dbcf68
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/webhook.go
@@ -0,0 +1,255 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package admission
+
+import (
+ "context"
+ "errors"
+ "net/http"
+
+ "github.com/go-logr/logr"
+ jsonpatch "gomodules.xyz/jsonpatch/v2"
+ admissionv1 "k8s.io/api/admission/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/util/json"
+ "k8s.io/client-go/kubernetes/scheme"
+
+ logf "sigs.k8s.io/controller-runtime/pkg/internal/log"
+ "sigs.k8s.io/controller-runtime/pkg/runtime/inject"
+ "sigs.k8s.io/controller-runtime/pkg/webhook/internal/metrics"
+)
+
+var (
+ errUnableToEncodeResponse = errors.New("unable to encode response")
+)
+
+// Request defines the input for an admission handler.
+// It contains information to identify the object in
+// question (group, version, kind, resource, subresource,
+// name, namespace), as well as the operation in question
+// (e.g. Get, Create, etc), and the object itself.
+type Request struct {
+ admissionv1.AdmissionRequest
+}
+
+// Response is the output of an admission handler.
+// It contains a response indicating if a given
+// operation is allowed, as well as a set of patches
+// to mutate the object in the case of a mutating admission handler.
+type Response struct {
+ // Patches are the JSON patches for mutating webhooks.
+ // Using this instead of setting Response.Patch to minimize
+ // overhead of serialization and deserialization.
+ // Patches set here will override any patches in the response,
+ // so leave this empty if you want to set the patch response directly.
+ Patches []jsonpatch.JsonPatchOperation
+ // AdmissionResponse is the raw admission response.
+ // The Patch field in it will be overwritten by the listed patches.
+ admissionv1.AdmissionResponse
+}
+
+// Complete populates any fields that are yet to be set in
+// the underlying AdmissionResponse, It mutates the response.
+func (r *Response) Complete(req Request) error {
+ r.UID = req.UID
+
+ // ensure that we have a valid status code
+ if r.Result == nil {
+ r.Result = &metav1.Status{}
+ }
+ if r.Result.Code == 0 {
+ r.Result.Code = http.StatusOK
+ }
+ // TODO(directxman12): do we need to populate this further, and/or
+ // is code actually necessary (the same webhook doesn't use it)
+
+ if len(r.Patches) == 0 {
+ return nil
+ }
+
+ var err error
+ r.Patch, err = json.Marshal(r.Patches)
+ if err != nil {
+ return err
+ }
+ patchType := admissionv1.PatchTypeJSONPatch
+ r.PatchType = &patchType
+
+ return nil
+}
+
+// Handler can handle an AdmissionRequest.
+type Handler interface {
+ // Handle yields a response to an AdmissionRequest.
+ //
+ // The supplied context is extracted from the received http.Request, allowing wrapping
+ // http.Handlers to inject values into and control cancelation of downstream request processing.
+ Handle(context.Context, Request) Response
+}
+
+// HandlerFunc implements Handler interface using a single function.
+type HandlerFunc func(context.Context, Request) Response
+
+var _ Handler = HandlerFunc(nil)
+
+// Handle process the AdmissionRequest by invoking the underlying function.
+func (f HandlerFunc) Handle(ctx context.Context, req Request) Response {
+ return f(ctx, req)
+}
+
+// Webhook represents each individual webhook.
+//
+// It must be registered with a webhook.Server or
+// populated by StandaloneWebhook to be ran on an arbitrary HTTP server.
+type Webhook struct {
+ // Handler actually processes an admission request returning whether it was allowed or denied,
+ // and potentially patches to apply to the handler.
+ Handler Handler
+
+ // WithContextFunc will allow you to take the http.Request.Context() and
+ // add any additional information such as passing the request path or
+ // headers thus allowing you to read them from within the handler
+ WithContextFunc func(context.Context, *http.Request) context.Context
+
+ // decoder is constructed on receiving a scheme and passed down to then handler
+ decoder *Decoder
+
+ log logr.Logger
+}
+
+// InjectLogger gets a handle to a logging instance, hopefully with more info about this particular webhook.
+func (wh *Webhook) InjectLogger(l logr.Logger) error {
+ wh.log = l
+ return nil
+}
+
+// Handle processes AdmissionRequest.
+// If the webhook is mutating type, it delegates the AdmissionRequest to each handler and merge the patches.
+// 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 {
+ resp := wh.Handler.Handle(ctx, req)
+ if err := resp.Complete(req); err != nil {
+ wh.log.Error(err, "unable to encode response")
+ return Errored(http.StatusInternalServerError, errUnableToEncodeResponse)
+ }
+
+ return resp
+}
+
+// InjectScheme injects a scheme into the webhook, in order to construct a Decoder.
+func (wh *Webhook) InjectScheme(s *runtime.Scheme) error {
+ // TODO(directxman12): we should have a better way to pass this down
+
+ var err error
+ wh.decoder, err = NewDecoder(s)
+ if err != nil {
+ return err
+ }
+
+ // inject the decoder here too, just in case the order of calling this is not
+ // scheme first, then inject func
+ if wh.Handler != nil {
+ if _, err := InjectDecoderInto(wh.GetDecoder(), wh.Handler); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+// GetDecoder returns a decoder to decode the objects embedded in admission requests.
+// It may be nil if we haven't received a scheme to use to determine object types yet.
+func (wh *Webhook) GetDecoder() *Decoder {
+ return wh.decoder
+}
+
+// InjectFunc injects the field setter into the webhook.
+func (wh *Webhook) InjectFunc(f inject.Func) error {
+ // inject directly into the handlers. It would be more correct
+ // to do this in a sync.Once in Handle (since we don't have some
+ // other start/finalize-type method), but it's more efficient to
+ // do it here, presumably.
+
+ // also inject a decoder, and wrap this so that we get a setFields
+ // that injects a decoder (hopefully things don't ignore the duplicate
+ // InjectorInto call).
+
+ var setFields inject.Func
+ setFields = func(target interface{}) error {
+ if err := f(target); err != nil {
+ return err
+ }
+
+ if _, err := inject.InjectorInto(setFields, target); err != nil {
+ return err
+ }
+
+ if _, err := InjectDecoderInto(wh.GetDecoder(), target); err != nil {
+ return err
+ }
+
+ return nil
+ }
+
+ return setFields(wh.Handler)
+}
+
+// StandaloneOptions let you configure a StandaloneWebhook.
+type StandaloneOptions struct {
+ // Scheme is the scheme used to resolve runtime.Objects to GroupVersionKinds / Resources
+ // Defaults to the kubernetes/client-go scheme.Scheme, but it's almost always better
+ // idea to pass your own scheme in. See the documentation in pkg/scheme for more information.
+ Scheme *runtime.Scheme
+ // Logger to be used by the webhook.
+ // If none is set, it defaults to log.Log global logger.
+ Logger logr.Logger
+ // MetricsPath is used for labelling prometheus metrics
+ // by the path is served on.
+ // If none is set, prometheus metrics will not be generated.
+ MetricsPath string
+}
+
+// StandaloneWebhook prepares a webhook for use without a webhook.Server,
+// passing in the information normally populated by webhook.Server
+// and instrumenting the webhook with metrics.
+//
+// Use this to attach your webhook to an arbitrary HTTP server or mux.
+//
+// Note that you are responsible for terminating TLS if you use StandaloneWebhook
+// in your own server/mux. In order to be accessed by a kubernetes cluster,
+// all webhook servers require TLS.
+func StandaloneWebhook(hook *Webhook, opts StandaloneOptions) (http.Handler, error) {
+ if opts.Scheme == nil {
+ opts.Scheme = scheme.Scheme
+ }
+
+ if err := hook.InjectScheme(opts.Scheme); err != nil {
+ return nil, err
+ }
+
+ if opts.Logger == nil {
+ opts.Logger = logf.RuntimeLog.WithName("webhook")
+ }
+ hook.log = opts.Logger
+
+ if opts.MetricsPath == "" {
+ return hook, nil
+ }
+ return metrics.InstrumentedHook(opts.MetricsPath, hook), nil
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/alias.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/alias.go
new file mode 100644
index 000000000..293137db4
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/alias.go
@@ -0,0 +1,79 @@
+/*
+Copyright 2019 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package webhook
+
+import (
+ "gomodules.xyz/jsonpatch/v2"
+ "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
+)
+
+// define some aliases for common bits of the webhook functionality
+
+// Defaulter defines functions for setting defaults on resources.
+type Defaulter = admission.Defaulter
+
+// Validator defines functions for validating an operation.
+type Validator = admission.Validator
+
+// CustomDefaulter defines functions for setting defaults on resources.
+type CustomDefaulter = admission.CustomDefaulter
+
+// CustomValidator defines functions for validating an operation.
+type CustomValidator = admission.CustomValidator
+
+// AdmissionRequest defines the input for an admission handler.
+// It contains information to identify the object in
+// question (group, version, kind, resource, subresource,
+// name, namespace), as well as the operation in question
+// (e.g. Get, Create, etc), and the object itself.
+type AdmissionRequest = admission.Request
+
+// AdmissionResponse is the output of an admission handler.
+// It contains a response indicating if a given
+// operation is allowed, as well as a set of patches
+// to mutate the object in the case of a mutating admission handler.
+type AdmissionResponse = admission.Response
+
+// Admission is webhook suitable for registration with the server
+// an admission webhook that validates API operations and potentially
+// mutates their contents.
+type Admission = admission.Webhook
+
+// AdmissionHandler knows how to process admission requests, validating them,
+// and potentially mutating the objects they contain.
+type AdmissionHandler = admission.Handler
+
+// AdmissionDecoder knows how to decode objects from admission requests.
+type AdmissionDecoder = admission.Decoder
+
+// JSONPatchOp represents a single JSONPatch patch operation.
+type JSONPatchOp = jsonpatch.Operation
+
+var (
+ // Allowed indicates that the admission request should be allowed for the given reason.
+ Allowed = admission.Allowed
+
+ // Denied indicates that the admission request should be denied for the given reason.
+ Denied = admission.Denied
+
+ // Patched indicates that the admission request should be allowed for the given reason,
+ // and that the contained object should be mutated using the given patches.
+ Patched = admission.Patched
+
+ // Errored indicates that an error occurred in the admission request.
+ Errored = admission.Errored
+)
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/conversion/conversion.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/conversion/conversion.go
new file mode 100644
index 000000000..a5b7a282c
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/conversion/conversion.go
@@ -0,0 +1,345 @@
+/*
+Copyright 2019 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+/*
+Package conversion provides implementation for CRD conversion webhook that implements handler for version conversion requests for types that are convertible.
+
+See pkg/conversion for interface definitions required to ensure an API Type is convertible.
+*/
+package conversion
+
+import (
+ "encoding/json"
+ "fmt"
+ "net/http"
+
+ apix "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
+ "k8s.io/apimachinery/pkg/api/meta"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "sigs.k8s.io/controller-runtime/pkg/conversion"
+ logf "sigs.k8s.io/controller-runtime/pkg/log"
+)
+
+var (
+ log = logf.Log.WithName("conversion-webhook")
+)
+
+// Webhook implements a CRD conversion webhook HTTP handler.
+type Webhook struct {
+ scheme *runtime.Scheme
+ decoder *Decoder
+}
+
+// InjectScheme injects a scheme into the webhook, in order to construct a Decoder.
+func (wh *Webhook) InjectScheme(s *runtime.Scheme) error {
+ var err error
+ wh.scheme = s
+ wh.decoder, err = NewDecoder(s)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// ensure Webhook implements http.Handler
+var _ http.Handler = &Webhook{}
+
+func (wh *Webhook) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ convertReview := &apix.ConversionReview{}
+ err := json.NewDecoder(r.Body).Decode(convertReview)
+ if err != nil {
+ log.Error(err, "failed to read conversion request")
+ w.WriteHeader(http.StatusBadRequest)
+ return
+ }
+
+ // TODO(droot): may be move the conversion logic to a separate module to
+ // decouple it from the http layer ?
+ resp, err := wh.handleConvertRequest(convertReview.Request)
+ if err != nil {
+ log.Error(err, "failed to convert", "request", convertReview.Request.UID)
+ convertReview.Response = errored(err)
+ } else {
+ convertReview.Response = resp
+ }
+ convertReview.Response.UID = convertReview.Request.UID
+ convertReview.Request = nil
+
+ err = json.NewEncoder(w).Encode(convertReview)
+ if err != nil {
+ log.Error(err, "failed to write response")
+ return
+ }
+}
+
+// handles a version conversion request.
+func (wh *Webhook) handleConvertRequest(req *apix.ConversionRequest) (*apix.ConversionResponse, error) {
+ if req == nil {
+ return nil, fmt.Errorf("conversion request is nil")
+ }
+ var objects []runtime.RawExtension
+
+ for _, obj := range req.Objects {
+ src, gvk, err := wh.decoder.Decode(obj.Raw)
+ if err != nil {
+ return nil, err
+ }
+ dst, err := wh.allocateDstObject(req.DesiredAPIVersion, gvk.Kind)
+ if err != nil {
+ return nil, err
+ }
+ err = wh.convertObject(src, dst)
+ if err != nil {
+ return nil, err
+ }
+ objects = append(objects, runtime.RawExtension{Object: dst})
+ }
+ return &apix.ConversionResponse{
+ UID: req.UID,
+ ConvertedObjects: objects,
+ Result: metav1.Status{
+ Status: metav1.StatusSuccess,
+ },
+ }, nil
+}
+
+// convertObject will convert given a src object to dst object.
+// Note(droot): couldn't find a way to reduce the cyclomatic complexity under 10
+// without compromising readability, so disabling gocyclo linter
+func (wh *Webhook) convertObject(src, dst runtime.Object) error {
+ srcGVK := src.GetObjectKind().GroupVersionKind()
+ dstGVK := dst.GetObjectKind().GroupVersionKind()
+
+ if srcGVK.GroupKind() != dstGVK.GroupKind() {
+ return fmt.Errorf("src %T and dst %T does not belong to same API Group", src, dst)
+ }
+
+ if srcGVK == dstGVK {
+ return fmt.Errorf("conversion is not allowed between same type %T", src)
+ }
+
+ srcIsHub, dstIsHub := isHub(src), isHub(dst)
+ srcIsConvertible, dstIsConvertible := isConvertible(src), isConvertible(dst)
+
+ switch {
+ case srcIsHub && dstIsConvertible:
+ return dst.(conversion.Convertible).ConvertFrom(src.(conversion.Hub))
+ case dstIsHub && srcIsConvertible:
+ return src.(conversion.Convertible).ConvertTo(dst.(conversion.Hub))
+ case srcIsConvertible && dstIsConvertible:
+ return wh.convertViaHub(src.(conversion.Convertible), dst.(conversion.Convertible))
+ default:
+ return fmt.Errorf("%T is not convertible to %T", src, dst)
+ }
+}
+
+func (wh *Webhook) convertViaHub(src, dst conversion.Convertible) error {
+ hub, err := wh.getHub(src)
+ if err != nil {
+ return err
+ }
+
+ if hub == nil {
+ return fmt.Errorf("%s does not have any Hub defined", src)
+ }
+
+ err = src.ConvertTo(hub)
+ if err != nil {
+ return fmt.Errorf("%T failed to convert to hub version %T : %w", src, hub, err)
+ }
+
+ err = dst.ConvertFrom(hub)
+ if err != nil {
+ return fmt.Errorf("%T failed to convert from hub version %T : %w", dst, hub, err)
+ }
+
+ return nil
+}
+
+// getHub returns an instance of the Hub for passed-in object's group/kind.
+func (wh *Webhook) getHub(obj runtime.Object) (conversion.Hub, error) {
+ gvks, err := objectGVKs(wh.scheme, obj)
+ if err != nil {
+ return nil, err
+ }
+ if len(gvks) == 0 {
+ return nil, fmt.Errorf("error retrieving gvks for object : %v", obj)
+ }
+
+ var hub conversion.Hub
+ var hubFoundAlready bool
+ for _, gvk := range gvks {
+ instance, err := wh.scheme.New(gvk)
+ if err != nil {
+ return nil, fmt.Errorf("failed to allocate an instance for gvk %v: %w", gvk, err)
+ }
+ if val, isHub := instance.(conversion.Hub); isHub {
+ if hubFoundAlready {
+ return nil, fmt.Errorf("multiple hub version defined for %T", obj)
+ }
+ hubFoundAlready = true
+ hub = val
+ }
+ }
+ return hub, nil
+}
+
+// allocateDstObject returns an instance for a given GVK.
+func (wh *Webhook) allocateDstObject(apiVersion, kind string) (runtime.Object, error) {
+ gvk := schema.FromAPIVersionAndKind(apiVersion, kind)
+
+ obj, err := wh.scheme.New(gvk)
+ if err != nil {
+ return obj, err
+ }
+
+ t, err := meta.TypeAccessor(obj)
+ if err != nil {
+ return obj, err
+ }
+
+ t.SetAPIVersion(apiVersion)
+ t.SetKind(kind)
+
+ return obj, nil
+}
+
+// IsConvertible determines if given type is convertible or not. For a type
+// to be convertible, the group-kind needs to have a Hub type defined and all
+// non-hub types must be able to convert to/from Hub.
+func IsConvertible(scheme *runtime.Scheme, obj runtime.Object) (bool, error) {
+ var hubs, spokes, nonSpokes []runtime.Object
+
+ gvks, err := objectGVKs(scheme, obj)
+ if err != nil {
+ return false, err
+ }
+ if len(gvks) == 0 {
+ return false, fmt.Errorf("error retrieving gvks for object : %v", obj)
+ }
+
+ for _, gvk := range gvks {
+ instance, err := scheme.New(gvk)
+ if err != nil {
+ return false, fmt.Errorf("failed to allocate an instance for gvk %v: %w", gvk, err)
+ }
+
+ if isHub(instance) {
+ hubs = append(hubs, instance)
+ continue
+ }
+
+ if !isConvertible(instance) {
+ nonSpokes = append(nonSpokes, instance)
+ continue
+ }
+
+ spokes = append(spokes, instance)
+ }
+
+ if len(gvks) == 1 {
+ return false, nil // single version
+ }
+
+ if len(hubs) == 0 && len(spokes) == 0 {
+ // multiple version detected with no conversion implementation. This is
+ // true for multi-version built-in types.
+ return false, nil
+ }
+
+ if len(hubs) == 1 && len(nonSpokes) == 0 { // convertible
+ return true, nil
+ }
+
+ return false, PartialImplementationError{
+ hubs: hubs,
+ nonSpokes: nonSpokes,
+ spokes: spokes,
+ }
+}
+
+// objectGVKs returns all (Group,Version,Kind) for the Group/Kind of given object.
+func objectGVKs(scheme *runtime.Scheme, obj runtime.Object) ([]schema.GroupVersionKind, error) {
+ // NB: we should not use `obj.GetObjectKind().GroupVersionKind()` to get the
+ // GVK here, since it is parsed from apiVersion and kind fields and it may
+ // return empty GVK if obj is an uninitialized object.
+ objGVKs, _, err := scheme.ObjectKinds(obj)
+ if err != nil {
+ return nil, err
+ }
+ if len(objGVKs) != 1 {
+ return nil, fmt.Errorf("expect to get only one GVK for %v", obj)
+ }
+ objGVK := objGVKs[0]
+ knownTypes := scheme.AllKnownTypes()
+
+ var gvks []schema.GroupVersionKind
+ for gvk := range knownTypes {
+ if objGVK.GroupKind() == gvk.GroupKind() {
+ gvks = append(gvks, gvk)
+ }
+ }
+ return gvks, nil
+}
+
+// PartialImplementationError represents an error due to partial conversion
+// implementation such as hub without spokes, multiple hubs or spokes without hub.
+type PartialImplementationError struct {
+ gvk schema.GroupVersionKind
+ hubs []runtime.Object
+ nonSpokes []runtime.Object
+ spokes []runtime.Object
+}
+
+func (e PartialImplementationError) Error() string {
+ if len(e.hubs) == 0 {
+ return fmt.Sprintf("no hub defined for gvk %s", e.gvk)
+ }
+ if len(e.hubs) > 1 {
+ return fmt.Sprintf("multiple(%d) hubs defined for group-kind '%s' ",
+ len(e.hubs), e.gvk.GroupKind())
+ }
+ if len(e.nonSpokes) > 0 {
+ return fmt.Sprintf("%d inconvertible types detected for group-kind '%s'",
+ len(e.nonSpokes), e.gvk.GroupKind())
+ }
+ return ""
+}
+
+// isHub determines if passed-in object is a Hub or not.
+func isHub(obj runtime.Object) bool {
+ _, yes := obj.(conversion.Hub)
+ return yes
+}
+
+// isConvertible determines if passed-in object is a convertible.
+func isConvertible(obj runtime.Object) bool {
+ _, yes := obj.(conversion.Convertible)
+ return yes
+}
+
+// helper to construct error response.
+func errored(err error) *apix.ConversionResponse {
+ return &apix.ConversionResponse{
+ Result: metav1.Status{
+ Status: metav1.StatusFailure,
+ Message: err.Error(),
+ },
+ }
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/conversion/decoder.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/conversion/decoder.go
new file mode 100644
index 000000000..6a9e9c236
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/conversion/decoder.go
@@ -0,0 +1,47 @@
+/*
+Copyright 2021 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package conversion
+
+import (
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "k8s.io/apimachinery/pkg/runtime/serializer"
+)
+
+// Decoder knows how to decode the contents of a CRD version conversion
+// request into a concrete object.
+// TODO(droot): consider reusing decoder from admission pkg for this.
+type Decoder struct {
+ codecs serializer.CodecFactory
+}
+
+// NewDecoder creates a Decoder given the runtime.Scheme
+func NewDecoder(scheme *runtime.Scheme) (*Decoder, error) {
+ return &Decoder{codecs: serializer.NewCodecFactory(scheme)}, nil
+}
+
+// Decode decodes the inlined object.
+func (d *Decoder) Decode(content []byte) (runtime.Object, *schema.GroupVersionKind, error) {
+ deserializer := d.codecs.UniversalDeserializer()
+ return deserializer.Decode(content, nil, nil)
+}
+
+// DecodeInto decodes the inlined object in the into the passed-in runtime.Object.
+func (d *Decoder) DecodeInto(content []byte, into runtime.Object) error {
+ deserializer := d.codecs.UniversalDeserializer()
+ return runtime.DecodeInto(deserializer, content, into)
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/doc.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/doc.go
new file mode 100644
index 000000000..2c93f0d99
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/doc.go
@@ -0,0 +1,28 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+/*
+Package webhook provides methods to build and bootstrap a webhook server.
+
+Currently, it only supports admission webhooks. It will support CRD conversion webhooks in the near future.
+*/
+package webhook
+
+import (
+ logf "sigs.k8s.io/controller-runtime/pkg/internal/log"
+)
+
+var log = logf.RuntimeLog.WithName("webhook")
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/metrics/metrics.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/metrics/metrics.go
new file mode 100644
index 000000000..557004908
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/metrics/metrics.go
@@ -0,0 +1,85 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package metrics
+
+import (
+ "net/http"
+
+ "github.com/prometheus/client_golang/prometheus"
+ "github.com/prometheus/client_golang/prometheus/promhttp"
+
+ "sigs.k8s.io/controller-runtime/pkg/metrics"
+)
+
+var (
+ // RequestLatency is a prometheus metric which is a histogram of the latency
+ // of processing admission requests.
+ RequestLatency = prometheus.NewHistogramVec(
+ prometheus.HistogramOpts{
+ Name: "controller_runtime_webhook_latency_seconds",
+ Help: "Histogram of the latency of processing admission requests",
+ },
+ []string{"webhook"},
+ )
+
+ // RequestTotal is a prometheus metric which is a counter of the total processed admission requests.
+ RequestTotal = func() *prometheus.CounterVec {
+ return prometheus.NewCounterVec(
+ prometheus.CounterOpts{
+ Name: "controller_runtime_webhook_requests_total",
+ Help: "Total number of admission requests by HTTP status code.",
+ },
+ []string{"webhook", "code"},
+ )
+ }()
+
+ // RequestInFlight is a prometheus metric which is a gauge of the in-flight admission requests.
+ RequestInFlight = func() *prometheus.GaugeVec {
+ return prometheus.NewGaugeVec(
+ prometheus.GaugeOpts{
+ Name: "controller_runtime_webhook_requests_in_flight",
+ Help: "Current number of admission requests being served.",
+ },
+ []string{"webhook"},
+ )
+ }()
+)
+
+func init() {
+ metrics.Registry.MustRegister(RequestLatency, RequestTotal, RequestInFlight)
+}
+
+// InstrumentedHook adds some instrumentation on top of the given webhook.
+func InstrumentedHook(path string, hookRaw http.Handler) http.Handler {
+ lbl := prometheus.Labels{"webhook": path}
+
+ lat := RequestLatency.MustCurryWith(lbl)
+ cnt := RequestTotal.MustCurryWith(lbl)
+ gge := RequestInFlight.With(lbl)
+
+ // Initialize the most likely HTTP status codes.
+ cnt.WithLabelValues("200")
+ cnt.WithLabelValues("500")
+
+ return promhttp.InstrumentHandlerDuration(
+ lat,
+ promhttp.InstrumentHandlerCounter(
+ cnt,
+ promhttp.InstrumentHandlerInFlight(gge, hookRaw),
+ ),
+ )
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/server.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/server.go
new file mode 100644
index 000000000..1db38113f
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/server.go
@@ -0,0 +1,339 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package webhook
+
+import (
+ "context"
+ "crypto/tls"
+ "crypto/x509"
+ "fmt"
+ "io/ioutil"
+ "net"
+ "net/http"
+ "os"
+ "path/filepath"
+ "strconv"
+ "sync"
+ "time"
+
+ "k8s.io/apimachinery/pkg/runtime"
+ kscheme "k8s.io/client-go/kubernetes/scheme"
+ "sigs.k8s.io/controller-runtime/pkg/certwatcher"
+ "sigs.k8s.io/controller-runtime/pkg/healthz"
+ "sigs.k8s.io/controller-runtime/pkg/runtime/inject"
+ "sigs.k8s.io/controller-runtime/pkg/webhook/internal/metrics"
+)
+
+// DefaultPort is the default port that the webhook server serves.
+var DefaultPort = 9443
+
+// Server is an admission webhook server that can serve traffic and
+// generates related k8s resources for deploying.
+//
+// TLS is required for a webhook to be accessed by kubernetes, so
+// you must provide a CertName and KeyName or have valid cert/key
+// at the default locations (tls.crt and tls.key). If you do not
+// want to configure TLS (i.e for testing purposes) run an
+// admission.StandaloneWebhook in your own server.
+type Server struct {
+ // Host is the address that the server will listen on.
+ // Defaults to "" - all addresses.
+ Host string
+
+ // Port is the port number that the server will serve.
+ // It will be defaulted to 9443 if unspecified.
+ Port int
+
+ // CertDir is the directory that contains the server key and certificate. The
+ // server key and certificate.
+ CertDir string
+
+ // CertName is the server certificate name. Defaults to tls.crt.
+ CertName string
+
+ // KeyName is the server key name. Defaults to tls.key.
+ KeyName string
+
+ // ClientCAName is the CA certificate name which server used to verify remote(client)'s certificate.
+ // Defaults to "", which means server does not verify client's certificate.
+ ClientCAName string
+
+ // TLSVersion is the minimum version of TLS supported. Accepts
+ // "", "1.0", "1.1", "1.2" and "1.3" only ("" is equivalent to "1.0" for backwards compatibility)
+ TLSMinVersion string
+
+ // WebhookMux is the multiplexer that handles different webhooks.
+ WebhookMux *http.ServeMux
+
+ // webhooks keep track of all registered webhooks for dependency injection,
+ // and to provide better panic messages on duplicate webhook registration.
+ webhooks map[string]http.Handler
+
+ // setFields allows injecting dependencies from an external source
+ setFields inject.Func
+
+ // defaultingOnce ensures that the default fields are only ever set once.
+ defaultingOnce sync.Once
+
+ // started is set to true immediately before the server is started
+ // and thus can be used to check if the server has been started
+ started bool
+
+ // mu protects access to the webhook map & setFields for Start, Register, etc
+ mu sync.Mutex
+}
+
+// setDefaults does defaulting for the Server.
+func (s *Server) setDefaults() {
+ s.webhooks = map[string]http.Handler{}
+ if s.WebhookMux == nil {
+ s.WebhookMux = http.NewServeMux()
+ }
+
+ if s.Port <= 0 {
+ s.Port = DefaultPort
+ }
+
+ if len(s.CertDir) == 0 {
+ s.CertDir = filepath.Join(os.TempDir(), "k8s-webhook-server", "serving-certs")
+ }
+
+ if len(s.CertName) == 0 {
+ s.CertName = "tls.crt"
+ }
+
+ if len(s.KeyName) == 0 {
+ s.KeyName = "tls.key"
+ }
+}
+
+// NeedLeaderElection implements the LeaderElectionRunnable interface, which indicates
+// the webhook server doesn't need leader election.
+func (*Server) NeedLeaderElection() bool {
+ return false
+}
+
+// Register marks the given webhook as being served at the given path.
+// It panics if two hooks are registered on the same path.
+func (s *Server) Register(path string, hook http.Handler) {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ s.defaultingOnce.Do(s.setDefaults)
+ if _, found := s.webhooks[path]; found {
+ panic(fmt.Errorf("can't register duplicate path: %v", path))
+ }
+ // TODO(directxman12): call setfields if we've already started the server
+ s.webhooks[path] = hook
+ s.WebhookMux.Handle(path, metrics.InstrumentedHook(path, hook))
+
+ regLog := log.WithValues("path", path)
+ regLog.Info("Registering webhook")
+
+ // we've already been "started", inject dependencies here.
+ // Otherwise, InjectFunc will do this for us later.
+ if s.setFields != nil {
+ if err := s.setFields(hook); err != nil {
+ // TODO(directxman12): swallowing this error isn't great, but we'd have to
+ // change the signature to fix that
+ regLog.Error(err, "unable to inject fields into webhook during registration")
+ }
+
+ baseHookLog := log.WithName("webhooks")
+
+ // NB(directxman12): we don't propagate this further by wrapping setFields because it's
+ // unclear if this is how we want to deal with log propagation. In this specific instance,
+ // we want to be able to pass a logger to webhooks because they don't know their own path.
+ if _, err := inject.LoggerInto(baseHookLog.WithValues("webhook", path), hook); err != nil {
+ regLog.Error(err, "unable to logger into webhook during registration")
+ }
+ }
+}
+
+// StartStandalone runs a webhook server without
+// a controller manager.
+func (s *Server) StartStandalone(ctx context.Context, scheme *runtime.Scheme) error {
+ // Use the Kubernetes client-go scheme if none is specified
+ if scheme == nil {
+ scheme = kscheme.Scheme
+ }
+
+ if err := s.InjectFunc(func(i interface{}) error {
+ if _, err := inject.SchemeInto(scheme, i); err != nil {
+ return err
+ }
+ return nil
+ }); err != nil {
+ return err
+ }
+
+ return s.Start(ctx)
+}
+
+// tlsVersion converts from human-readable TLS version (for example "1.1")
+// to the values accepted by tls.Config (for example 0x301).
+func tlsVersion(version string) (uint16, error) {
+ switch version {
+ // default is previous behaviour
+ case "":
+ return tls.VersionTLS10, nil
+ case "1.0":
+ return tls.VersionTLS10, nil
+ case "1.1":
+ return tls.VersionTLS11, nil
+ case "1.2":
+ return tls.VersionTLS12, nil
+ case "1.3":
+ return tls.VersionTLS13, nil
+ default:
+ return 0, fmt.Errorf("invalid TLSMinVersion %v: expects 1.0, 1.1, 1.2, 1.3 or empty", version)
+ }
+}
+
+// Start runs the server.
+// It will install the webhook related resources depend on the server configuration.
+func (s *Server) Start(ctx context.Context) error {
+ s.defaultingOnce.Do(s.setDefaults)
+
+ baseHookLog := log.WithName("webhooks")
+ baseHookLog.Info("Starting webhook server")
+
+ certPath := filepath.Join(s.CertDir, s.CertName)
+ keyPath := filepath.Join(s.CertDir, s.KeyName)
+
+ certWatcher, err := certwatcher.New(certPath, keyPath)
+ if err != nil {
+ return err
+ }
+
+ go func() {
+ if err := certWatcher.Start(ctx); err != nil {
+ log.Error(err, "certificate watcher error")
+ }
+ }()
+
+ tlsMinVersion, err := tlsVersion(s.TLSMinVersion)
+ if err != nil {
+ return err
+ }
+
+ cfg := &tls.Config{ //nolint:gosec
+ NextProtos: []string{"h2"},
+ GetCertificate: certWatcher.GetCertificate,
+ MinVersion: tlsMinVersion,
+ }
+
+ // load CA to verify client certificate
+ if s.ClientCAName != "" {
+ certPool := x509.NewCertPool()
+ clientCABytes, err := ioutil.ReadFile(filepath.Join(s.CertDir, s.ClientCAName))
+ if err != nil {
+ return fmt.Errorf("failed to read client CA cert: %v", err)
+ }
+
+ ok := certPool.AppendCertsFromPEM(clientCABytes)
+ if !ok {
+ return fmt.Errorf("failed to append client CA cert to CA pool")
+ }
+
+ cfg.ClientCAs = certPool
+ cfg.ClientAuth = tls.RequireAndVerifyClientCert
+ }
+
+ listener, err := tls.Listen("tcp", net.JoinHostPort(s.Host, strconv.Itoa(s.Port)), cfg)
+ if err != nil {
+ return err
+ }
+
+ log.Info("Serving webhook server", "host", s.Host, "port", s.Port)
+
+ srv := &http.Server{
+ Handler: s.WebhookMux,
+ }
+
+ idleConnsClosed := make(chan struct{})
+ go func() {
+ <-ctx.Done()
+ log.Info("shutting down webhook server")
+
+ // TODO: use a context with reasonable timeout
+ if err := srv.Shutdown(context.Background()); err != nil {
+ // Error from closing listeners, or context timeout
+ log.Error(err, "error shutting down the HTTP server")
+ }
+ close(idleConnsClosed)
+ }()
+
+ s.mu.Lock()
+ s.started = true
+ s.mu.Unlock()
+ if err := srv.Serve(listener); err != nil && err != http.ErrServerClosed {
+ return err
+ }
+
+ <-idleConnsClosed
+ return nil
+}
+
+// StartedChecker returns an healthz.Checker which is healthy after the
+// server has been started.
+func (s *Server) StartedChecker() healthz.Checker {
+ config := &tls.Config{
+ InsecureSkipVerify: true, // nolint:gosec // config is used to connect to our own webhook port.
+ }
+ return func(req *http.Request) error {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ if !s.started {
+ return fmt.Errorf("webhook server has not been started yet")
+ }
+
+ d := &net.Dialer{Timeout: 10 * time.Second}
+ conn, err := tls.DialWithDialer(d, "tcp", net.JoinHostPort(s.Host, strconv.Itoa(s.Port)), config)
+ if err != nil {
+ return fmt.Errorf("webhook server is not reachable: %v", err)
+ }
+
+ if err := conn.Close(); err != nil {
+ return fmt.Errorf("webhook server is not reachable: closing connection: %v", err)
+ }
+
+ return nil
+ }
+}
+
+// InjectFunc injects the field setter into the server.
+func (s *Server) InjectFunc(f inject.Func) error {
+ s.setFields = f
+
+ // inject fields here that weren't injected in Register because we didn't have setFields yet.
+ baseHookLog := log.WithName("webhooks")
+ for hookPath, webhook := range s.webhooks {
+ if err := s.setFields(webhook); err != nil {
+ return err
+ }
+
+ // NB(directxman12): we don't propagate this further by wrapping setFields because it's
+ // unclear if this is how we want to deal with log propagation. In this specific instance,
+ // we want to be able to pass a logger to webhooks because they don't know their own path.
+ if _, err := inject.LoggerInto(baseHookLog.WithValues("webhook", hookPath), webhook); err != nil {
+ return err
+ }
+ }
+ return nil
+}