Merge pull request #67798 from mbohlool/crd_refactoring

Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions here: https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md.

Refactor admission webhook client code to a apiserver/pkg/util package

As part of #67006 This refactoring enable us to share code between admission webhooks and CRD conversion webhooks.

@deads2k @lavalamp @sttts @kubernetes/sig-api-machinery-misc

Kubernetes-commit: 14eb029fba5717e2dfe3434a682f9aec4752c44d
This commit is contained in:
Kubernetes Publisher 2018-08-31 06:16:28 -07:00
commit 36e49471e7
15 changed files with 182 additions and 124 deletions

106
Godeps/Godeps.json generated
View File

@ -984,215 +984,215 @@
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/api/apitesting", "ImportPath": "k8s.io/apimachinery/pkg/api/apitesting",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/api/apitesting/fuzzer", "ImportPath": "k8s.io/apimachinery/pkg/api/apitesting/fuzzer",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/api/apitesting/roundtrip", "ImportPath": "k8s.io/apimachinery/pkg/api/apitesting/roundtrip",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/api/equality", "ImportPath": "k8s.io/apimachinery/pkg/api/equality",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/api/errors", "ImportPath": "k8s.io/apimachinery/pkg/api/errors",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/api/meta", "ImportPath": "k8s.io/apimachinery/pkg/api/meta",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/api/resource", "ImportPath": "k8s.io/apimachinery/pkg/api/resource",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/api/validation", "ImportPath": "k8s.io/apimachinery/pkg/api/validation",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/api/validation/path", "ImportPath": "k8s.io/apimachinery/pkg/api/validation/path",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/fuzzer", "ImportPath": "k8s.io/apimachinery/pkg/apis/meta/fuzzer",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/internalversion", "ImportPath": "k8s.io/apimachinery/pkg/apis/meta/internalversion",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1", "ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured", "ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1/validation", "ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1/validation",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1beta1", "ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1beta1",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/conversion", "ImportPath": "k8s.io/apimachinery/pkg/conversion",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/conversion/queryparams", "ImportPath": "k8s.io/apimachinery/pkg/conversion/queryparams",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/fields", "ImportPath": "k8s.io/apimachinery/pkg/fields",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/labels", "ImportPath": "k8s.io/apimachinery/pkg/labels",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/runtime", "ImportPath": "k8s.io/apimachinery/pkg/runtime",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/runtime/schema", "ImportPath": "k8s.io/apimachinery/pkg/runtime/schema",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer", "ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/json", "ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/json",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/protobuf", "ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/protobuf",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/recognizer", "ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/recognizer",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/streaming", "ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/streaming",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/versioning", "ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/versioning",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/selection", "ImportPath": "k8s.io/apimachinery/pkg/selection",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/types", "ImportPath": "k8s.io/apimachinery/pkg/types",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/cache", "ImportPath": "k8s.io/apimachinery/pkg/util/cache",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/clock", "ImportPath": "k8s.io/apimachinery/pkg/util/clock",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/diff", "ImportPath": "k8s.io/apimachinery/pkg/util/diff",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/errors", "ImportPath": "k8s.io/apimachinery/pkg/util/errors",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/framer", "ImportPath": "k8s.io/apimachinery/pkg/util/framer",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/intstr", "ImportPath": "k8s.io/apimachinery/pkg/util/intstr",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/json", "ImportPath": "k8s.io/apimachinery/pkg/util/json",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/mergepatch", "ImportPath": "k8s.io/apimachinery/pkg/util/mergepatch",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/naming", "ImportPath": "k8s.io/apimachinery/pkg/util/naming",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/net", "ImportPath": "k8s.io/apimachinery/pkg/util/net",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/rand", "ImportPath": "k8s.io/apimachinery/pkg/util/rand",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/runtime", "ImportPath": "k8s.io/apimachinery/pkg/util/runtime",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/sets", "ImportPath": "k8s.io/apimachinery/pkg/util/sets",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/strategicpatch", "ImportPath": "k8s.io/apimachinery/pkg/util/strategicpatch",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/uuid", "ImportPath": "k8s.io/apimachinery/pkg/util/uuid",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/validation", "ImportPath": "k8s.io/apimachinery/pkg/util/validation",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/validation/field", "ImportPath": "k8s.io/apimachinery/pkg/util/validation/field",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/wait", "ImportPath": "k8s.io/apimachinery/pkg/util/wait",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/waitgroup", "ImportPath": "k8s.io/apimachinery/pkg/util/waitgroup",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/yaml", "ImportPath": "k8s.io/apimachinery/pkg/util/yaml",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/version", "ImportPath": "k8s.io/apimachinery/pkg/version",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/watch", "ImportPath": "k8s.io/apimachinery/pkg/watch",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/third_party/forked/golang/json", "ImportPath": "k8s.io/apimachinery/third_party/forked/golang/json",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/third_party/forked/golang/reflect", "ImportPath": "k8s.io/apimachinery/third_party/forked/golang/reflect",
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30" "Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
}, },
{ {
"ImportPath": "k8s.io/client-go/discovery", "ImportPath": "k8s.io/client-go/discovery",

View File

@ -21,6 +21,7 @@ import (
"fmt" "fmt"
"io" "io"
admissionv1beta1 "k8s.io/api/admission/v1beta1"
"k8s.io/api/admissionregistration/v1beta1" "k8s.io/api/admissionregistration/v1beta1"
apierrors "k8s.io/apimachinery/pkg/api/errors" apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
@ -29,6 +30,7 @@ import (
"k8s.io/apiserver/pkg/admission/plugin/webhook/config" "k8s.io/apiserver/pkg/admission/plugin/webhook/config"
"k8s.io/apiserver/pkg/admission/plugin/webhook/namespace" "k8s.io/apiserver/pkg/admission/plugin/webhook/namespace"
"k8s.io/apiserver/pkg/admission/plugin/webhook/rules" "k8s.io/apiserver/pkg/admission/plugin/webhook/rules"
"k8s.io/apiserver/pkg/util/webhook"
"k8s.io/client-go/informers" "k8s.io/client-go/informers"
clientset "k8s.io/client-go/kubernetes" clientset "k8s.io/client-go/kubernetes"
) )
@ -40,7 +42,7 @@ type Webhook struct {
sourceFactory sourceFactory sourceFactory sourceFactory
hookSource Source hookSource Source
clientManager *config.ClientManager clientManager *webhook.ClientManager
convertor *convertor convertor *convertor
namespaceMatcher *namespace.Matcher namespaceMatcher *namespace.Matcher
dispatcher Dispatcher dispatcher Dispatcher
@ -52,7 +54,7 @@ var (
) )
type sourceFactory func(f informers.SharedInformerFactory) Source type sourceFactory func(f informers.SharedInformerFactory) Source
type dispatcherFactory func(cm *config.ClientManager) Dispatcher type dispatcherFactory func(cm *webhook.ClientManager) Dispatcher
// NewWebhook creates a new generic admission webhook. // NewWebhook creates a new generic admission webhook.
func NewWebhook(handler *admission.Handler, configFile io.Reader, sourceFactory sourceFactory, dispatcherFactory dispatcherFactory) (*Webhook, error) { func NewWebhook(handler *admission.Handler, configFile io.Reader, sourceFactory sourceFactory, dispatcherFactory dispatcherFactory) (*Webhook, error) {
@ -61,17 +63,17 @@ func NewWebhook(handler *admission.Handler, configFile io.Reader, sourceFactory
return nil, err return nil, err
} }
cm, err := config.NewClientManager() cm, err := webhook.NewClientManager(admissionv1beta1.SchemeGroupVersion, admissionv1beta1.AddToScheme)
if err != nil { if err != nil {
return nil, err return nil, err
} }
authInfoResolver, err := config.NewDefaultAuthenticationInfoResolver(kubeconfigFile) authInfoResolver, err := webhook.NewDefaultAuthenticationInfoResolver(kubeconfigFile)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Set defaults which may be overridden later. // Set defaults which may be overridden later.
cm.SetAuthenticationInfoResolver(authInfoResolver) cm.SetAuthenticationInfoResolver(authInfoResolver)
cm.SetServiceResolver(config.NewDefaultServiceResolver()) cm.SetServiceResolver(webhook.NewDefaultServiceResolver())
return &Webhook{ return &Webhook{
Handler: handler, Handler: handler,
@ -86,13 +88,13 @@ func NewWebhook(handler *admission.Handler, configFile io.Reader, sourceFactory
// SetAuthenticationInfoResolverWrapper sets the // SetAuthenticationInfoResolverWrapper sets the
// AuthenticationInfoResolverWrapper. // AuthenticationInfoResolverWrapper.
// TODO find a better way wire this, but keep this pull small for now. // TODO find a better way wire this, but keep this pull small for now.
func (a *Webhook) SetAuthenticationInfoResolverWrapper(wrapper config.AuthenticationInfoResolverWrapper) { func (a *Webhook) SetAuthenticationInfoResolverWrapper(wrapper webhook.AuthenticationInfoResolverWrapper) {
a.clientManager.SetAuthenticationInfoResolverWrapper(wrapper) a.clientManager.SetAuthenticationInfoResolverWrapper(wrapper)
} }
// SetServiceResolver sets a service resolver for the webhook admission plugin. // SetServiceResolver sets a service resolver for the webhook admission plugin.
// Passing a nil resolver does not have an effect, instead a default one will be used. // Passing a nil resolver does not have an effect, instead a default one will be used.
func (a *Webhook) SetServiceResolver(sr config.ServiceResolver) { func (a *Webhook) SetServiceResolver(sr webhook.ServiceResolver) {
a.clientManager.SetServiceResolver(sr) a.clientManager.SetServiceResolver(sr)
} }

View File

@ -20,13 +20,13 @@ import (
"net/url" "net/url"
"k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/admission"
webhookconfig "k8s.io/apiserver/pkg/admission/plugin/webhook/config" "k8s.io/apiserver/pkg/util/webhook"
) )
// WantsServiceResolver defines a function that accepts a ServiceResolver for // WantsServiceResolver defines a function that accepts a ServiceResolver for
// admission plugins that need to make calls to services. // admission plugins that need to make calls to services.
type WantsServiceResolver interface { type WantsServiceResolver interface {
SetServiceResolver(webhookconfig.ServiceResolver) SetServiceResolver(webhook.ServiceResolver)
} }
// ServiceResolver knows how to convert a service reference into an actual // ServiceResolver knows how to convert a service reference into an actual
@ -38,22 +38,22 @@ type ServiceResolver interface {
// WantsAuthenticationInfoResolverWrapper defines a function that wraps the standard AuthenticationInfoResolver // WantsAuthenticationInfoResolverWrapper defines a function that wraps the standard AuthenticationInfoResolver
// to allow the apiserver to control what is returned as auth info // to allow the apiserver to control what is returned as auth info
type WantsAuthenticationInfoResolverWrapper interface { type WantsAuthenticationInfoResolverWrapper interface {
SetAuthenticationInfoResolverWrapper(webhookconfig.AuthenticationInfoResolverWrapper) SetAuthenticationInfoResolverWrapper(wrapper webhook.AuthenticationInfoResolverWrapper)
admission.InitializationValidator admission.InitializationValidator
} }
// PluginInitializer is used for initialization of the webhook admission plugin. // PluginInitializer is used for initialization of the webhook admission plugin.
type PluginInitializer struct { type PluginInitializer struct {
serviceResolver webhookconfig.ServiceResolver serviceResolver webhook.ServiceResolver
authenticationInfoResolverWrapper webhookconfig.AuthenticationInfoResolverWrapper authenticationInfoResolverWrapper webhook.AuthenticationInfoResolverWrapper
} }
var _ admission.PluginInitializer = &PluginInitializer{} var _ admission.PluginInitializer = &PluginInitializer{}
// NewPluginInitializer constructs new instance of PluginInitializer // NewPluginInitializer constructs new instance of PluginInitializer
func NewPluginInitializer( func NewPluginInitializer(
authenticationInfoResolverWrapper webhookconfig.AuthenticationInfoResolverWrapper, authenticationInfoResolverWrapper webhook.AuthenticationInfoResolverWrapper,
serviceResolver webhookconfig.ServiceResolver, serviceResolver webhook.ServiceResolver,
) *PluginInitializer { ) *PluginInitializer {
return &PluginInitializer{ return &PluginInitializer{
authenticationInfoResolverWrapper: authenticationInfoResolverWrapper, authenticationInfoResolverWrapper: authenticationInfoResolverWrapper,

View File

@ -21,7 +21,7 @@ import (
"testing" "testing"
"k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/admission/plugin/webhook/config" "k8s.io/apiserver/pkg/util/webhook"
) )
type doNothingAdmission struct{} type doNothingAdmission struct{}
@ -39,7 +39,7 @@ type serviceWanter struct {
got ServiceResolver got ServiceResolver
} }
func (s *serviceWanter) SetServiceResolver(sr config.ServiceResolver) { s.got = sr } func (s *serviceWanter) SetServiceResolver(sr webhook.ServiceResolver) { s.got = sr }
func TestWantsServiceResolver(t *testing.T) { func TestWantsServiceResolver(t *testing.T) {
sw := &serviceWanter{} sw := &serviceWanter{}

View File

@ -33,19 +33,20 @@ import (
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
utilruntime "k8s.io/apimachinery/pkg/util/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime"
admissionmetrics "k8s.io/apiserver/pkg/admission/metrics" admissionmetrics "k8s.io/apiserver/pkg/admission/metrics"
"k8s.io/apiserver/pkg/admission/plugin/webhook/config"
webhookerrors "k8s.io/apiserver/pkg/admission/plugin/webhook/errors" webhookerrors "k8s.io/apiserver/pkg/admission/plugin/webhook/errors"
"k8s.io/apiserver/pkg/admission/plugin/webhook/generic" "k8s.io/apiserver/pkg/admission/plugin/webhook/generic"
"k8s.io/apiserver/pkg/admission/plugin/webhook/request" "k8s.io/apiserver/pkg/admission/plugin/webhook/request"
"k8s.io/apiserver/pkg/admission/plugin/webhook/util"
"k8s.io/apiserver/pkg/util/webhook"
) )
type mutatingDispatcher struct { type mutatingDispatcher struct {
cm *config.ClientManager cm *webhook.ClientManager
plugin *Plugin plugin *Plugin
} }
func newMutatingDispatcher(p *Plugin) func(cm *config.ClientManager) generic.Dispatcher { func newMutatingDispatcher(p *Plugin) func(cm *webhook.ClientManager) generic.Dispatcher {
return func(cm *config.ClientManager) generic.Dispatcher { return func(cm *webhook.ClientManager) generic.Dispatcher {
return &mutatingDispatcher{cm, p} return &mutatingDispatcher{cm, p}
} }
} }
@ -62,7 +63,7 @@ func (a *mutatingDispatcher) Dispatch(ctx context.Context, attr *generic.Version
} }
ignoreClientCallFailures := hook.FailurePolicy != nil && *hook.FailurePolicy == v1beta1.Ignore ignoreClientCallFailures := hook.FailurePolicy != nil && *hook.FailurePolicy == v1beta1.Ignore
if callErr, ok := err.(*webhookerrors.ErrCallingWebhook); ok { if callErr, ok := err.(*webhook.ErrCallingWebhook); ok {
if ignoreClientCallFailures { if ignoreClientCallFailures {
glog.Warningf("Failed calling webhook, failing open %v: %v", hook.Name, callErr) glog.Warningf("Failed calling webhook, failing open %v: %v", hook.Name, callErr)
utilruntime.HandleError(callErr) utilruntime.HandleError(callErr)
@ -84,7 +85,7 @@ func (a *mutatingDispatcher) Dispatch(ctx context.Context, attr *generic.Version
func (a *mutatingDispatcher) callAttrMutatingHook(ctx context.Context, h *v1beta1.Webhook, attr *generic.VersionedAttributes) error { func (a *mutatingDispatcher) callAttrMutatingHook(ctx context.Context, h *v1beta1.Webhook, attr *generic.VersionedAttributes) error {
if attr.IsDryRun() { if attr.IsDryRun() {
if h.SideEffects == nil { if h.SideEffects == nil {
return &webhookerrors.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("Webhook SideEffects is nil")} return &webhook.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("Webhook SideEffects is nil")}
} }
if !(*h.SideEffects == v1beta1.SideEffectClassNone || *h.SideEffects == v1beta1.SideEffectClassNoneOnDryRun) { if !(*h.SideEffects == v1beta1.SideEffectClassNone || *h.SideEffects == v1beta1.SideEffectClassNoneOnDryRun) {
return webhookerrors.NewDryRunUnsupportedErr(h.Name) return webhookerrors.NewDryRunUnsupportedErr(h.Name)
@ -93,17 +94,17 @@ func (a *mutatingDispatcher) callAttrMutatingHook(ctx context.Context, h *v1beta
// Make the webhook request // Make the webhook request
request := request.CreateAdmissionReview(attr) request := request.CreateAdmissionReview(attr)
client, err := a.cm.HookClient(h) client, err := a.cm.HookClient(util.HookClientConfigForWebhook(h))
if err != nil { if err != nil {
return &webhookerrors.ErrCallingWebhook{WebhookName: h.Name, Reason: err} return &webhook.ErrCallingWebhook{WebhookName: h.Name, Reason: err}
} }
response := &admissionv1beta1.AdmissionReview{} response := &admissionv1beta1.AdmissionReview{}
if err := client.Post().Context(ctx).Body(&request).Do().Into(response); err != nil { if err := client.Post().Context(ctx).Body(&request).Do().Into(response); err != nil {
return &webhookerrors.ErrCallingWebhook{WebhookName: h.Name, Reason: err} return &webhook.ErrCallingWebhook{WebhookName: h.Name, Reason: err}
} }
if response.Response == nil { if response.Response == nil {
return &webhookerrors.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("Webhook response was absent")} return &webhook.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("Webhook response was absent")}
} }
for k, v := range response.Response.AuditAnnotations { for k, v := range response.Response.AuditAnnotations {

View File

@ -19,22 +19,22 @@ package testing
import ( import (
"sync/atomic" "sync/atomic"
"k8s.io/apiserver/pkg/admission/plugin/webhook/config"
"k8s.io/apiserver/pkg/admission/plugin/webhook/testcerts" "k8s.io/apiserver/pkg/admission/plugin/webhook/testcerts"
"k8s.io/apiserver/pkg/util/webhook"
"k8s.io/client-go/rest" "k8s.io/client-go/rest"
) )
// Wrapper turns an AuthenticationInfoResolver into a AuthenticationInfoResolverWrapper that unconditionally // Wrapper turns an AuthenticationInfoResolver into a AuthenticationInfoResolverWrapper that unconditionally
// returns the given AuthenticationInfoResolver. // returns the given AuthenticationInfoResolver.
func Wrapper(r config.AuthenticationInfoResolver) func(config.AuthenticationInfoResolver) config.AuthenticationInfoResolver { func Wrapper(r webhook.AuthenticationInfoResolver) func(webhook.AuthenticationInfoResolver) webhook.AuthenticationInfoResolver {
return func(config.AuthenticationInfoResolver) config.AuthenticationInfoResolver { return func(webhook.AuthenticationInfoResolver) webhook.AuthenticationInfoResolver {
return r return r
} }
} }
// NewAuthenticationInfoResolver creates a fake AuthenticationInfoResolver that counts cache misses on // NewAuthenticationInfoResolver creates a fake AuthenticationInfoResolver that counts cache misses on
// every call to its methods. // every call to its methods.
func NewAuthenticationInfoResolver(cacheMisses *int32) config.AuthenticationInfoResolver { func NewAuthenticationInfoResolver(cacheMisses *int32) webhook.AuthenticationInfoResolver {
return &authenticationInfoResolver{ return &authenticationInfoResolver{
restConfig: &rest.Config{ restConfig: &rest.Config{
TLSClientConfig: rest.TLSClientConfig{ TLSClientConfig: rest.TLSClientConfig{

View File

@ -20,7 +20,7 @@ import (
"fmt" "fmt"
"net/url" "net/url"
"k8s.io/apiserver/pkg/admission/plugin/webhook/config" "k8s.io/apiserver/pkg/util/webhook"
) )
type serviceResolver struct { type serviceResolver struct {
@ -29,7 +29,7 @@ type serviceResolver struct {
// NewServiceResolver returns a static service resolve that return the given URL or // NewServiceResolver returns a static service resolve that return the given URL or
// an error for the failResolve namespace. // an error for the failResolve namespace.
func NewServiceResolver(base url.URL) config.ServiceResolver { func NewServiceResolver(base url.URL) webhook.ServiceResolver {
return &serviceResolver{base} return &serviceResolver{base}
} }

View File

@ -0,0 +1,42 @@
/*
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 util
import (
"k8s.io/api/admissionregistration/v1beta1"
"k8s.io/apiserver/pkg/util/webhook"
)
// HookClientConfigForWebhook construct a webhook.ClientConfig using a v1beta1.Webhook API object.
// webhook.ClientConfig is used to create a HookClient and the purpose of the config struct is to
// share that with other packages that need to create a HookClient.
func HookClientConfigForWebhook(w *v1beta1.Webhook) webhook.ClientConfig {
ret := webhook.ClientConfig{Name: w.Name, CABundle: w.ClientConfig.CABundle}
if w.ClientConfig.URL != nil {
ret.URL = *w.ClientConfig.URL
}
if w.ClientConfig.Service != nil {
ret.Service = &webhook.ClientConfigService{
Name: w.ClientConfig.Service.Name,
Namespace: w.ClientConfig.Service.Namespace,
}
if w.ClientConfig.Service.Path != nil {
ret.Service.Path = *w.ClientConfig.Service.Path
}
}
return ret
}

View File

@ -29,17 +29,18 @@ import (
apierrors "k8s.io/apimachinery/pkg/api/errors" apierrors "k8s.io/apimachinery/pkg/api/errors"
utilruntime "k8s.io/apimachinery/pkg/util/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime"
admissionmetrics "k8s.io/apiserver/pkg/admission/metrics" admissionmetrics "k8s.io/apiserver/pkg/admission/metrics"
"k8s.io/apiserver/pkg/admission/plugin/webhook/config"
webhookerrors "k8s.io/apiserver/pkg/admission/plugin/webhook/errors" webhookerrors "k8s.io/apiserver/pkg/admission/plugin/webhook/errors"
"k8s.io/apiserver/pkg/admission/plugin/webhook/generic" "k8s.io/apiserver/pkg/admission/plugin/webhook/generic"
"k8s.io/apiserver/pkg/admission/plugin/webhook/request" "k8s.io/apiserver/pkg/admission/plugin/webhook/request"
"k8s.io/apiserver/pkg/admission/plugin/webhook/util"
"k8s.io/apiserver/pkg/util/webhook"
) )
type validatingDispatcher struct { type validatingDispatcher struct {
cm *config.ClientManager cm *webhook.ClientManager
} }
func newValidatingDispatcher(cm *config.ClientManager) generic.Dispatcher { func newValidatingDispatcher(cm *webhook.ClientManager) generic.Dispatcher {
return &validatingDispatcher{cm} return &validatingDispatcher{cm}
} }
@ -61,7 +62,7 @@ func (d *validatingDispatcher) Dispatch(ctx context.Context, attr *generic.Versi
} }
ignoreClientCallFailures := hook.FailurePolicy != nil && *hook.FailurePolicy == v1beta1.Ignore ignoreClientCallFailures := hook.FailurePolicy != nil && *hook.FailurePolicy == v1beta1.Ignore
if callErr, ok := err.(*webhookerrors.ErrCallingWebhook); ok { if callErr, ok := err.(*webhook.ErrCallingWebhook); ok {
if ignoreClientCallFailures { if ignoreClientCallFailures {
glog.Warningf("Failed calling webhook, failing open %v: %v", hook.Name, callErr) glog.Warningf("Failed calling webhook, failing open %v: %v", hook.Name, callErr)
utilruntime.HandleError(callErr) utilruntime.HandleError(callErr)
@ -99,7 +100,7 @@ func (d *validatingDispatcher) Dispatch(ctx context.Context, attr *generic.Versi
func (d *validatingDispatcher) callHook(ctx context.Context, h *v1beta1.Webhook, attr *generic.VersionedAttributes) error { func (d *validatingDispatcher) callHook(ctx context.Context, h *v1beta1.Webhook, attr *generic.VersionedAttributes) error {
if attr.IsDryRun() { if attr.IsDryRun() {
if h.SideEffects == nil { if h.SideEffects == nil {
return &webhookerrors.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("Webhook SideEffects is nil")} return &webhook.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("Webhook SideEffects is nil")}
} }
if !(*h.SideEffects == v1beta1.SideEffectClassNone || *h.SideEffects == v1beta1.SideEffectClassNoneOnDryRun) { if !(*h.SideEffects == v1beta1.SideEffectClassNone || *h.SideEffects == v1beta1.SideEffectClassNoneOnDryRun) {
return webhookerrors.NewDryRunUnsupportedErr(h.Name) return webhookerrors.NewDryRunUnsupportedErr(h.Name)
@ -108,17 +109,17 @@ func (d *validatingDispatcher) callHook(ctx context.Context, h *v1beta1.Webhook,
// Make the webhook request // Make the webhook request
request := request.CreateAdmissionReview(attr) request := request.CreateAdmissionReview(attr)
client, err := d.cm.HookClient(h) client, err := d.cm.HookClient(util.HookClientConfigForWebhook(h))
if err != nil { if err != nil {
return &webhookerrors.ErrCallingWebhook{WebhookName: h.Name, Reason: err} return &webhook.ErrCallingWebhook{WebhookName: h.Name, Reason: err}
} }
response := &admissionv1beta1.AdmissionReview{} response := &admissionv1beta1.AdmissionReview{}
if err := client.Post().Context(ctx).Body(&request).Do().Into(response); err != nil { if err := client.Post().Context(ctx).Body(&request).Do().Into(response); err != nil {
return &webhookerrors.ErrCallingWebhook{WebhookName: h.Name, Reason: err} return &webhook.ErrCallingWebhook{WebhookName: h.Name, Reason: err}
} }
if response.Response == nil { if response.Response == nil {
return &webhookerrors.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("Webhook response was absent")} return &webhook.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("Webhook response was absent")}
} }
for k, v := range response.Response.AuditAnnotations { for k, v := range response.Response.AuditAnnotations {
key := h.Name + "/" + k key := h.Name + "/" + k

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package config package webhook
import ( import (
"fmt" "fmt"
@ -47,10 +47,12 @@ type AuthenticationInfoResolverDelegator struct {
ClientConfigForServiceFunc func(serviceName, serviceNamespace string) (*rest.Config, error) ClientConfigForServiceFunc func(serviceName, serviceNamespace string) (*rest.Config, error)
} }
// ClientConfigFor returns client config for given server.
func (a *AuthenticationInfoResolverDelegator) ClientConfigFor(server string) (*rest.Config, error) { func (a *AuthenticationInfoResolverDelegator) ClientConfigFor(server string) (*rest.Config, error) {
return a.ClientConfigForFunc(server) return a.ClientConfigForFunc(server)
} }
// ClientConfigForService returns client config for given service.
func (a *AuthenticationInfoResolverDelegator) ClientConfigForService(serviceName, serviceNamespace string) (*rest.Config, error) { func (a *AuthenticationInfoResolverDelegator) ClientConfigForService(serviceName, serviceNamespace string) (*rest.Config, error) {
return a.ClientConfigForServiceFunc(serviceName, serviceNamespace) return a.ClientConfigForServiceFunc(serviceName, serviceNamespace)
} }

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package config package webhook
import ( import (
"testing" "testing"

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package config package webhook
import ( import (
"context" "context"
@ -24,13 +24,11 @@ import (
"net" "net"
"net/url" "net/url"
lru "github.com/hashicorp/golang-lru" "github.com/hashicorp/golang-lru"
admissionv1beta1 "k8s.io/api/admission/v1beta1"
"k8s.io/api/admissionregistration/v1beta1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer" "k8s.io/apimachinery/pkg/runtime/serializer"
utilerrors "k8s.io/apimachinery/pkg/util/errors" utilerrors "k8s.io/apimachinery/pkg/util/errors"
webhookerrors "k8s.io/apiserver/pkg/admission/plugin/webhook/errors"
"k8s.io/client-go/rest" "k8s.io/client-go/rest"
) )
@ -38,9 +36,20 @@ const (
defaultCacheSize = 200 defaultCacheSize = 200
) )
var ( // ClientConfig defines parameters required for creating a hook client.
ErrNeedServiceOrURL = errors.New("webhook configuration must have either service or URL") type ClientConfig struct {
) Name string
URL string
CABundle []byte
Service *ClientConfigService
}
// ClientConfigService defines service discovery parameters of the webhook.
type ClientConfigService struct {
Name string
Namespace string
Path string
}
// ClientManager builds REST clients to talk to webhooks. It caches the clients // ClientManager builds REST clients to talk to webhooks. It caches the clients
// to avoid duplicate creation. // to avoid duplicate creation.
@ -52,19 +61,19 @@ type ClientManager struct {
} }
// NewClientManager creates a clientManager. // NewClientManager creates a clientManager.
func NewClientManager() (ClientManager, error) { func NewClientManager(gv schema.GroupVersion, addToSchemaFunc func(s *runtime.Scheme) error) (ClientManager, error) {
cache, err := lru.New(defaultCacheSize) cache, err := lru.New(defaultCacheSize)
if err != nil { if err != nil {
return ClientManager{}, err return ClientManager{}, err
} }
admissionScheme := runtime.NewScheme() admissionScheme := runtime.NewScheme()
if err := admissionv1beta1.AddToScheme(admissionScheme); err != nil { if err := addToSchemaFunc(admissionScheme); err != nil {
return ClientManager{}, err return ClientManager{}, err
} }
return ClientManager{ return ClientManager{
cache: cache, cache: cache,
negotiatedSerializer: serializer.NegotiatedSerializerWrapper(runtime.SerializerInfo{ negotiatedSerializer: serializer.NegotiatedSerializerWrapper(runtime.SerializerInfo{
Serializer: serializer.NewCodecFactory(admissionScheme).LegacyCodec(admissionv1beta1.SchemeGroupVersion), Serializer: serializer.NewCodecFactory(admissionScheme).LegacyCodec(gv),
}), }),
}, nil }, nil
} }
@ -106,8 +115,10 @@ func (cm *ClientManager) Validate() error {
// HookClient get a RESTClient from the cache, or constructs one based on the // HookClient get a RESTClient from the cache, or constructs one based on the
// webhook configuration. // webhook configuration.
func (cm *ClientManager) HookClient(h *v1beta1.Webhook) (*rest.RESTClient, error) { func (cm *ClientManager) HookClient(cc ClientConfig) (*rest.RESTClient, error) {
cacheKey, err := json.Marshal(h.ClientConfig) ccWithNoName := cc
ccWithNoName.Name = ""
cacheKey, err := json.Marshal(ccWithNoName)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -120,7 +131,7 @@ func (cm *ClientManager) HookClient(h *v1beta1.Webhook) (*rest.RESTClient, error
if len(cfg.TLSClientConfig.CAData) > 0 { if len(cfg.TLSClientConfig.CAData) > 0 {
cfg.TLSClientConfig.CAData = append(cfg.TLSClientConfig.CAData, '\n') cfg.TLSClientConfig.CAData = append(cfg.TLSClientConfig.CAData, '\n')
} }
cfg.TLSClientConfig.CAData = append(cfg.TLSClientConfig.CAData, h.ClientConfig.CABundle...) cfg.TLSClientConfig.CAData = append(cfg.TLSClientConfig.CAData, cc.CABundle...)
cfg.ContentConfig.NegotiatedSerializer = cm.negotiatedSerializer cfg.ContentConfig.NegotiatedSerializer = cm.negotiatedSerializer
cfg.ContentConfig.ContentType = runtime.ContentTypeJSON cfg.ContentConfig.ContentType = runtime.ContentTypeJSON
@ -131,18 +142,16 @@ func (cm *ClientManager) HookClient(h *v1beta1.Webhook) (*rest.RESTClient, error
return client, err return client, err
} }
if svc := h.ClientConfig.Service; svc != nil { if cc.Service != nil {
restConfig, err := cm.authInfoResolver.ClientConfigForService(svc.Name, svc.Namespace) restConfig, err := cm.authInfoResolver.ClientConfigForService(cc.Service.Name, cc.Service.Namespace)
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg := rest.CopyConfig(restConfig) cfg := rest.CopyConfig(restConfig)
serverName := svc.Name + "." + svc.Namespace + ".svc" serverName := cc.Service.Name + "." + cc.Service.Namespace + ".svc"
host := serverName + ":443" host := serverName + ":443"
cfg.Host = "https://" + host cfg.Host = "https://" + host
if svc.Path != nil { cfg.APIPath = cc.Service.Path
cfg.APIPath = *svc.Path
}
// Set the server name if not already set // Set the server name if not already set
if len(cfg.TLSClientConfig.ServerName) == 0 { if len(cfg.TLSClientConfig.ServerName) == 0 {
cfg.TLSClientConfig.ServerName = serverName cfg.TLSClientConfig.ServerName = serverName
@ -155,7 +164,7 @@ func (cm *ClientManager) HookClient(h *v1beta1.Webhook) (*rest.RESTClient, error
} }
cfg.Dial = func(ctx context.Context, network, addr string) (net.Conn, error) { cfg.Dial = func(ctx context.Context, network, addr string) (net.Conn, error) {
if addr == host { if addr == host {
u, err := cm.serviceResolver.ResolveEndpoint(svc.Namespace, svc.Name) u, err := cm.serviceResolver.ResolveEndpoint(cc.Service.Namespace, cc.Service.Name)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -167,13 +176,13 @@ func (cm *ClientManager) HookClient(h *v1beta1.Webhook) (*rest.RESTClient, error
return complete(cfg) return complete(cfg)
} }
if h.ClientConfig.URL == nil { if cc.URL == "" {
return nil, &webhookerrors.ErrCallingWebhook{WebhookName: h.Name, Reason: ErrNeedServiceOrURL} return nil, &ErrCallingWebhook{WebhookName: cc.Name, Reason: errors.New("webhook configuration must have either service or URL")}
} }
u, err := url.Parse(*h.ClientConfig.URL) u, err := url.Parse(cc.URL)
if err != nil { if err != nil {
return nil, &webhookerrors.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("Unparsable URL: %v", err)} return nil, &ErrCallingWebhook{WebhookName: cc.Name, Reason: fmt.Errorf("Unparsable URL: %v", err)}
} }
restConfig, err := cm.authInfoResolver.ClientConfigFor(u.Host) restConfig, err := cm.authInfoResolver.ClientConfigFor(u.Host)

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package errors package webhook
import "fmt" import "fmt"

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package config package webhook
import ( import (
"errors" "errors"
@ -29,6 +29,7 @@ type ServiceResolver interface {
type defaultServiceResolver struct{} type defaultServiceResolver struct{}
// NewDefaultServiceResolver creates a new default server resolver.
func NewDefaultServiceResolver() ServiceResolver { func NewDefaultServiceResolver() ServiceResolver {
return &defaultServiceResolver{} return &defaultServiceResolver{}
} }

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package config package webhook
import ( import (
"fmt" "fmt"