mirror of
https://github.com/1Password/onepassword-operator.git
synced 2025-10-23 07:58:04 +00:00
Update packages and add vendor directory
This commit is contained in:
333
vendor/sigs.k8s.io/controller-runtime/pkg/builder/controller.go
generated
vendored
Normal file
333
vendor/sigs.k8s.io/controller-runtime/pkg/builder/controller.go
generated
vendored
Normal file
@@ -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 builder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
"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
|
||||
}
|
||||
|
||||
// WithLogConstructor overrides the controller options's LogConstructor.
|
||||
func (blder *Builder) WithLogConstructor(logConstructor func(*reconcile.Request) logr.Logger) *Builder {
|
||||
blder.ctrlOptions.LogConstructor = logConstructor
|
||||
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
|
||||
}
|
||||
|
||||
controllerName := blder.getControllerName(gvk)
|
||||
|
||||
// Setup the logger.
|
||||
if ctrlOptions.LogConstructor == nil {
|
||||
log := blder.mgr.GetLogger().WithValues(
|
||||
"controller", controllerName,
|
||||
"controllerGroup", gvk.Group,
|
||||
"controllerKind", gvk.Kind,
|
||||
)
|
||||
|
||||
ctrlOptions.LogConstructor = func(req *reconcile.Request) logr.Logger {
|
||||
log := log
|
||||
if req != nil {
|
||||
log = log.WithValues(
|
||||
gvk.Kind, klog.KRef(req.Namespace, req.Name),
|
||||
"namespace", req.Namespace, "name", req.Name,
|
||||
)
|
||||
}
|
||||
return log
|
||||
}
|
||||
}
|
||||
|
||||
// Build the controller and return.
|
||||
blder.ctrl, err = newController(controllerName, blder.mgr, ctrlOptions)
|
||||
return err
|
||||
}
|
28
vendor/sigs.k8s.io/controller-runtime/pkg/builder/doc.go
generated
vendored
Normal file
28
vendor/sigs.k8s.io/controller-runtime/pkg/builder/doc.go
generated
vendored
Normal file
@@ -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")
|
140
vendor/sigs.k8s.io/controller-runtime/pkg/builder/options.go
generated
vendored
Normal file
140
vendor/sigs.k8s.io/controller-runtime/pkg/builder/options.go
generated
vendored
Normal file
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
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.
|
||||
//
|
||||
// When watching a resource with OnlyMetadata, for example the v1.Pod, you
|
||||
// should not Get and List using the v1.Pod type. Instead, you should use
|
||||
// the special metav1.PartialObjectMetadata type.
|
||||
//
|
||||
// ❌ Incorrect:
|
||||
//
|
||||
// pod := &v1.Pod{}
|
||||
// mgr.GetClient().Get(ctx, nsAndName, pod)
|
||||
//
|
||||
// ✅ Correct:
|
||||
//
|
||||
// pod := &metav1.PartialObjectMetadata{}
|
||||
// pod.SetGroupVersionKind(schema.GroupVersionKind{
|
||||
// Group: "",
|
||||
// Version: "v1",
|
||||
// Kind: "Pod",
|
||||
// })
|
||||
// mgr.GetClient().Get(ctx, nsAndName, pod)
|
||||
//
|
||||
// In the first case, controller-runtime will create another cache for the
|
||||
// concrete type on top of the metadata cache; this increases memory
|
||||
// consumption and leads to race conditions as caches are not in sync.
|
||||
OnlyMetadata = projectAs(projectAsMetadata)
|
||||
|
||||
_ ForOption = OnlyMetadata
|
||||
_ OwnsOption = OnlyMetadata
|
||||
_ WatchesOption = OnlyMetadata
|
||||
)
|
||||
|
||||
// }}}
|
216
vendor/sigs.k8s.io/controller-runtime/pkg/builder/webhook.go
generated
vendored
Normal file
216
vendor/sigs.k8s.io/controller-runtime/pkg/builder/webhook.go
generated
vendored
Normal file
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
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
|
||||
recoverPanic bool
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// RecoverPanic indicates whether the panic caused by webhook should be recovered.
|
||||
func (blder *WebhookBuilder) RecoverPanic() *WebhookBuilder {
|
||||
blder.recoverPanic = true
|
||||
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).WithRecoverPanic(blder.recoverPanic)
|
||||
}
|
||||
if defaulter, ok := blder.apiType.(admission.Defaulter); ok {
|
||||
return admission.DefaultingWebhookFor(defaulter).WithRecoverPanic(blder.recoverPanic)
|
||||
}
|
||||
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).WithRecoverPanic(blder.recoverPanic)
|
||||
}
|
||||
if validator, ok := blder.apiType.(admission.Validator); ok {
|
||||
return admission.ValidatingWebhookFor(validator).WithRecoverPanic(blder.recoverPanic)
|
||||
}
|
||||
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)
|
||||
}
|
Reference in New Issue
Block a user