Add ObjectInterfaces to Admission and Validation

Kubernetes-commit: 513a87c7b25aa58f84fafe0dc170cee4c76e481b
This commit is contained in:
Mehdy Bohlool 2019-02-16 12:27:24 -08:00 committed by Kubernetes Publisher
parent cfef629361
commit 87b5ac0c06
26 changed files with 164 additions and 132 deletions

View File

@ -44,7 +44,7 @@ func WithAudit(i Interface, ae *auditinternal.Event) Interface {
return &auditHandler{i, ae} return &auditHandler{i, ae}
} }
func (handler auditHandler) Admit(a Attributes) error { func (handler auditHandler) Admit(a Attributes, o ObjectInterfaces) error {
if !handler.Interface.Handles(a.GetOperation()) { if !handler.Interface.Handles(a.GetOperation()) {
return nil return nil
} }
@ -53,13 +53,13 @@ func (handler auditHandler) Admit(a Attributes) error {
} }
var err error var err error
if mutator, ok := handler.Interface.(MutationInterface); ok { if mutator, ok := handler.Interface.(MutationInterface); ok {
err = mutator.Admit(a) err = mutator.Admit(a, o)
handler.logAnnotations(a) handler.logAnnotations(a)
} }
return err return err
} }
func (handler auditHandler) Validate(a Attributes) error { func (handler auditHandler) Validate(a Attributes, o ObjectInterfaces) error {
if !handler.Interface.Handles(a.GetOperation()) { if !handler.Interface.Handles(a.GetOperation()) {
return nil return nil
} }
@ -68,7 +68,7 @@ func (handler auditHandler) Validate(a Attributes) error {
} }
var err error var err error
if validator, ok := handler.Interface.(ValidationInterface); ok { if validator, ok := handler.Interface.(ValidationInterface); ok {
err = validator.Validate(a) err = validator.Validate(a, o)
handler.logAnnotations(a) handler.logAnnotations(a)
} }
return err return err

View File

@ -45,14 +45,14 @@ var _ Interface = &fakeHandler{}
var _ MutationInterface = &fakeHandler{} var _ MutationInterface = &fakeHandler{}
var _ ValidationInterface = &fakeHandler{} var _ ValidationInterface = &fakeHandler{}
func (h fakeHandler) Admit(a Attributes) error { func (h fakeHandler) Admit(a Attributes, o ObjectInterfaces) error {
for k, v := range h.admitAnnotations { for k, v := range h.admitAnnotations {
a.AddAnnotation(k, v) a.AddAnnotation(k, v)
} }
return h.admit return h.admit
} }
func (h fakeHandler) Validate(a Attributes) error { func (h fakeHandler) Validate(a Attributes, o ObjectInterfaces) error {
for k, v := range h.validateAnnotations { for k, v := range h.validateAnnotations {
a.AddAnnotation(k, v) a.AddAnnotation(k, v)
} }
@ -149,13 +149,13 @@ func TestWithAudit(t *testing.T) {
require.True(t, ok) require.True(t, ok)
auditMutator, ok := auditHandler.(MutationInterface) auditMutator, ok := auditHandler.(MutationInterface)
require.True(t, ok) require.True(t, ok)
assert.Equal(t, mutator.Admit(a), auditMutator.Admit(a), tcName+": WithAudit decorator should not effect the return value") assert.Equal(t, mutator.Admit(a, nil), auditMutator.Admit(a, nil), tcName+": WithAudit decorator should not effect the return value")
validator, ok := handler.(ValidationInterface) validator, ok := handler.(ValidationInterface)
require.True(t, ok) require.True(t, ok)
auditValidator, ok := auditHandler.(ValidationInterface) auditValidator, ok := auditHandler.(ValidationInterface)
require.True(t, ok) require.True(t, ok)
assert.Equal(t, validator.Validate(a), auditValidator.Validate(a), tcName+": WithAudit decorator should not effect the return value") assert.Equal(t, validator.Validate(a, nil), auditValidator.Validate(a, nil), tcName+": WithAudit decorator should not effect the return value")
annotations := make(map[string]string, len(tc.admitAnnotations)+len(tc.validateAnnotations)) annotations := make(map[string]string, len(tc.admitAnnotations)+len(tc.validateAnnotations))
for k, v := range tc.admitAnnotations { for k, v := range tc.admitAnnotations {

View File

@ -26,13 +26,13 @@ func NewChainHandler(handlers ...Interface) chainAdmissionHandler {
} }
// Admit performs an admission control check using a chain of handlers, and returns immediately on first error // Admit performs an admission control check using a chain of handlers, and returns immediately on first error
func (admissionHandler chainAdmissionHandler) Admit(a Attributes) error { func (admissionHandler chainAdmissionHandler) Admit(a Attributes, o ObjectInterfaces) error {
for _, handler := range admissionHandler { for _, handler := range admissionHandler {
if !handler.Handles(a.GetOperation()) { if !handler.Handles(a.GetOperation()) {
continue continue
} }
if mutator, ok := handler.(MutationInterface); ok { if mutator, ok := handler.(MutationInterface); ok {
err := mutator.Admit(a) err := mutator.Admit(a, o)
if err != nil { if err != nil {
return err return err
} }
@ -42,13 +42,13 @@ func (admissionHandler chainAdmissionHandler) Admit(a Attributes) error {
} }
// Validate performs an admission control check using a chain of handlers, and returns immediately on first error // Validate performs an admission control check using a chain of handlers, and returns immediately on first error
func (admissionHandler chainAdmissionHandler) Validate(a Attributes) error { func (admissionHandler chainAdmissionHandler) Validate(a Attributes, o ObjectInterfaces) error {
for _, handler := range admissionHandler { for _, handler := range admissionHandler {
if !handler.Handles(a.GetOperation()) { if !handler.Handles(a.GetOperation()) {
continue continue
} }
if validator, ok := handler.(ValidationInterface); ok { if validator, ok := handler.(ValidationInterface); ok {
err := validator.Validate(a) err := validator.Validate(a, o)
if err != nil { if err != nil {
return err return err
} }

View File

@ -31,7 +31,7 @@ type FakeHandler struct {
validate, validateCalled bool validate, validateCalled bool
} }
func (h *FakeHandler) Admit(a Attributes) (err error) { func (h *FakeHandler) Admit(a Attributes, o ObjectInterfaces) (err error) {
h.admitCalled = true h.admitCalled = true
if h.admit { if h.admit {
return nil return nil
@ -39,7 +39,7 @@ func (h *FakeHandler) Admit(a Attributes) (err error) {
return fmt.Errorf("Don't admit") return fmt.Errorf("Don't admit")
} }
func (h *FakeHandler) Validate(a Attributes) (err error) { func (h *FakeHandler) Validate(a Attributes, o ObjectInterfaces) (err error) {
h.validateCalled = true h.validateCalled = true
if h.validate { if h.validate {
return nil return nil
@ -119,7 +119,7 @@ func TestAdmitAndValidate(t *testing.T) {
for _, test := range tests { for _, test := range tests {
t.Logf("testcase = %s", test.name) t.Logf("testcase = %s", test.name)
// call admit and check that validate was not called at all // call admit and check that validate was not called at all
err := test.chain.Admit(NewAttributesRecord(nil, nil, schema.GroupVersionKind{}, test.ns, "", schema.GroupVersionResource{}, "", test.operation, false, nil)) err := test.chain.Admit(NewAttributesRecord(nil, nil, schema.GroupVersionKind{}, test.ns, "", schema.GroupVersionResource{}, "", test.operation, false, nil), nil)
accepted := (err == nil) accepted := (err == nil)
if accepted != test.accept { if accepted != test.accept {
t.Errorf("unexpected result of admit call: %v", accepted) t.Errorf("unexpected result of admit call: %v", accepted)
@ -140,7 +140,7 @@ func TestAdmitAndValidate(t *testing.T) {
} }
// call validate and check that admit was not called at all // call validate and check that admit was not called at all
err = test.chain.Validate(NewAttributesRecord(nil, nil, schema.GroupVersionKind{}, test.ns, "", schema.GroupVersionResource{}, "", test.operation, false, nil)) err = test.chain.Validate(NewAttributesRecord(nil, nil, schema.GroupVersionKind{}, test.ns, "", schema.GroupVersionResource{}, "", test.operation, false, nil), nil)
accepted = (err == nil) accepted = (err == nil)
if accepted != test.accept { if accepted != test.accept {
t.Errorf("unexpected result of validate call: %v\n", accepted) t.Errorf("unexpected result of validate call: %v\n", accepted)

View File

@ -85,7 +85,9 @@ type WantExternalKubeInformerFactory struct {
func (self *WantExternalKubeInformerFactory) SetExternalKubeInformerFactory(sf informers.SharedInformerFactory) { func (self *WantExternalKubeInformerFactory) SetExternalKubeInformerFactory(sf informers.SharedInformerFactory) {
self.sf = sf self.sf = sf
} }
func (self *WantExternalKubeInformerFactory) Admit(a admission.Attributes) error { return nil } func (self *WantExternalKubeInformerFactory) Admit(a admission.Attributes, o admission.ObjectInterfaces) error {
return nil
}
func (self *WantExternalKubeInformerFactory) Handles(o admission.Operation) bool { return false } func (self *WantExternalKubeInformerFactory) Handles(o admission.Operation) bool { return false }
func (self *WantExternalKubeInformerFactory) ValidateInitialization() error { return nil } func (self *WantExternalKubeInformerFactory) ValidateInitialization() error { return nil }
@ -98,7 +100,9 @@ type WantExternalKubeClientSet struct {
} }
func (self *WantExternalKubeClientSet) SetExternalKubeClientSet(cs kubernetes.Interface) { self.cs = cs } func (self *WantExternalKubeClientSet) SetExternalKubeClientSet(cs kubernetes.Interface) { self.cs = cs }
func (self *WantExternalKubeClientSet) Admit(a admission.Attributes) error { return nil } func (self *WantExternalKubeClientSet) Admit(a admission.Attributes, o admission.ObjectInterfaces) error {
return nil
}
func (self *WantExternalKubeClientSet) Handles(o admission.Operation) bool { return false } func (self *WantExternalKubeClientSet) Handles(o admission.Operation) bool { return false }
func (self *WantExternalKubeClientSet) ValidateInitialization() error { return nil } func (self *WantExternalKubeClientSet) ValidateInitialization() error { return nil }
@ -111,7 +115,9 @@ type WantAuthorizerAdmission struct {
} }
func (self *WantAuthorizerAdmission) SetAuthorizer(a authorizer.Authorizer) { self.auth = a } func (self *WantAuthorizerAdmission) SetAuthorizer(a authorizer.Authorizer) { self.auth = a }
func (self *WantAuthorizerAdmission) Admit(a admission.Attributes) error { return nil } func (self *WantAuthorizerAdmission) Admit(a admission.Attributes, o admission.ObjectInterfaces) error {
return nil
}
func (self *WantAuthorizerAdmission) Handles(o admission.Operation) bool { return false } func (self *WantAuthorizerAdmission) Handles(o admission.Operation) bool { return false }
func (self *WantAuthorizerAdmission) ValidateInitialization() error { return nil } func (self *WantAuthorizerAdmission) ValidateInitialization() error { return nil }
@ -131,7 +137,9 @@ type clientCertWanter struct {
} }
func (s *clientCertWanter) SetClientCert(cert, key []byte) { s.gotCert, s.gotKey = cert, key } func (s *clientCertWanter) SetClientCert(cert, key []byte) { s.gotCert, s.gotKey = cert, key }
func (s *clientCertWanter) Admit(a admission.Attributes) error { return nil } func (s *clientCertWanter) Admit(a admission.Attributes, o admission.ObjectInterfaces) error {
return nil
}
func (s *clientCertWanter) Handles(o admission.Operation) bool { return false } func (s *clientCertWanter) Handles(o admission.Operation) bool { return false }
func (s *clientCertWanter) ValidateInitialization() error { return nil } func (s *clientCertWanter) ValidateInitialization() error { return nil }
@ -141,7 +149,9 @@ type WantSchemeAdmission struct {
} }
func (self *WantSchemeAdmission) SetScheme(s *runtime.Scheme) { self.scheme = s } func (self *WantSchemeAdmission) SetScheme(s *runtime.Scheme) { self.scheme = s }
func (self *WantSchemeAdmission) Admit(a admission.Attributes) error { return nil } func (self *WantSchemeAdmission) Admit(a admission.Attributes, o admission.ObjectInterfaces) error {
return nil
}
func (self *WantSchemeAdmission) Handles(o admission.Operation) bool { return false } func (self *WantSchemeAdmission) Handles(o admission.Operation) bool { return false }
func (self *WantSchemeAdmission) ValidateInitialization() error { return nil } func (self *WantSchemeAdmission) ValidateInitialization() error { return nil }

View File

@ -62,6 +62,20 @@ type Attributes interface {
AddAnnotation(key, value string) error AddAnnotation(key, value string) error
} }
// ObjectInterfaces is an interface used by AdmissionController to get object interfaces
// such as Converter or Defaulter. These interfaces are normally coming from Request Scope
// to handle special cases like CRDs.
type ObjectInterfaces interface {
// GetObjectCreater is the ObjectCreator appropriate for the requested object.
GetObjectCreater() runtime.ObjectCreater
// GetObjectTyper is the ObjectTyper appropriate for the requested object.
GetObjectTyper() runtime.ObjectTyper
// GetObjectDefaulter is the ObjectDefaulter appropriate for the requested object.
GetObjectDefaulter() runtime.ObjectDefaulter
// GetObjectConvertor is the ObjectConvertor appropriate for the requested object.
GetObjectConvertor() runtime.ObjectConvertor
}
// privateAnnotationsGetter is a private interface which allows users to get annotations from Attributes. // privateAnnotationsGetter is a private interface which allows users to get annotations from Attributes.
type privateAnnotationsGetter interface { type privateAnnotationsGetter interface {
getAnnotations() map[string]string getAnnotations() map[string]string
@ -84,7 +98,7 @@ type MutationInterface interface {
Interface Interface
// Admit makes an admission decision based on the request attributes // Admit makes an admission decision based on the request attributes
Admit(a Attributes) (err error) Admit(a Attributes, o ObjectInterfaces) (err error)
} }
// ValidationInterface is an abstract, pluggable interface for Admission Control decisions. // ValidationInterface is an abstract, pluggable interface for Admission Control decisions.
@ -92,7 +106,7 @@ type ValidationInterface interface {
Interface Interface
// Validate makes an admission decision based on the request attributes. It is NOT allowed to mutate // Validate makes an admission decision based on the request attributes. It is NOT allowed to mutate
Validate(a Attributes) (err error) Validate(a Attributes, o ObjectInterfaces) (err error)
} }
// Operation is the type of resource operation being checked for admission control // Operation is the type of resource operation being checked for admission control

View File

@ -75,27 +75,27 @@ type pluginHandlerWithMetrics struct {
} }
// Admit performs a mutating admission control check and emit metrics. // Admit performs a mutating admission control check and emit metrics.
func (p pluginHandlerWithMetrics) Admit(a admission.Attributes) error { func (p pluginHandlerWithMetrics) Admit(a admission.Attributes, o admission.ObjectInterfaces) error {
mutatingHandler, ok := p.Interface.(admission.MutationInterface) mutatingHandler, ok := p.Interface.(admission.MutationInterface)
if !ok { if !ok {
return nil return nil
} }
start := time.Now() start := time.Now()
err := mutatingHandler.Admit(a) err := mutatingHandler.Admit(a, o)
p.observer(time.Since(start), err != nil, a, stepAdmit, p.extraLabels...) p.observer(time.Since(start), err != nil, a, stepAdmit, p.extraLabels...)
return err return err
} }
// Validate performs a non-mutating admission control check and emits metrics. // Validate performs a non-mutating admission control check and emits metrics.
func (p pluginHandlerWithMetrics) Validate(a admission.Attributes) error { func (p pluginHandlerWithMetrics) Validate(a admission.Attributes, o admission.ObjectInterfaces) error {
validatingHandler, ok := p.Interface.(admission.ValidationInterface) validatingHandler, ok := p.Interface.(admission.ValidationInterface)
if !ok { if !ok {
return nil return nil
} }
start := time.Now() start := time.Now()
err := validatingHandler.Validate(a) err := validatingHandler.Validate(a, o)
p.observer(time.Since(start), err != nil, a, stepValidate, p.extraLabels...) p.observer(time.Since(start), err != nil, a, stepValidate, p.extraLabels...)
return err return err
} }

View File

@ -34,8 +34,8 @@ var (
func TestObserveAdmissionStep(t *testing.T) { func TestObserveAdmissionStep(t *testing.T) {
Metrics.reset() Metrics.reset()
handler := WithStepMetrics(&mutatingAndValidatingFakeHandler{admission.NewHandler(admission.Create), true, true}) handler := WithStepMetrics(&mutatingAndValidatingFakeHandler{admission.NewHandler(admission.Create), true, true})
handler.(admission.MutationInterface).Admit(attr) handler.(admission.MutationInterface).Admit(attr, nil)
handler.(admission.ValidationInterface).Validate(attr) handler.(admission.ValidationInterface).Validate(attr, nil)
wantLabels := map[string]string{ wantLabels := map[string]string{
"operation": string(admission.Create), "operation": string(admission.Create),
"type": "admit", "type": "admit",
@ -52,8 +52,8 @@ func TestObserveAdmissionStep(t *testing.T) {
func TestObserveAdmissionController(t *testing.T) { func TestObserveAdmissionController(t *testing.T) {
Metrics.reset() Metrics.reset()
handler := WithControllerMetrics(&mutatingAndValidatingFakeHandler{admission.NewHandler(admission.Create), true, true}, "a") handler := WithControllerMetrics(&mutatingAndValidatingFakeHandler{admission.NewHandler(admission.Create), true, true}, "a")
handler.(admission.MutationInterface).Admit(attr) handler.(admission.MutationInterface).Admit(attr, nil)
handler.(admission.ValidationInterface).Validate(attr) handler.(admission.ValidationInterface).Validate(attr, nil)
wantLabels := map[string]string{ wantLabels := map[string]string{
"name": "a", "name": "a",
"operation": string(admission.Create), "operation": string(admission.Create),
@ -144,7 +144,7 @@ func TestWithMetrics(t *testing.T) {
h := WithMetrics(test.handler, Metrics.ObserveAdmissionController, test.name) h := WithMetrics(test.handler, Metrics.ObserveAdmissionController, test.name)
// test mutation // test mutation
err := h.(admission.MutationInterface).Admit(admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{}, test.ns, "", schema.GroupVersionResource{}, "", test.operation, false, nil)) err := h.(admission.MutationInterface).Admit(admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{}, test.ns, "", schema.GroupVersionResource{}, "", test.operation, false, nil), nil)
if test.admit && err != nil { if test.admit && err != nil {
t.Errorf("expected admit to succeed, but failed: %v", err) t.Errorf("expected admit to succeed, but failed: %v", err)
continue continue
@ -169,7 +169,7 @@ func TestWithMetrics(t *testing.T) {
} }
// test validation // test validation
err = h.(admission.ValidationInterface).Validate(admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{}, test.ns, "", schema.GroupVersionResource{}, "", test.operation, false, nil)) err = h.(admission.ValidationInterface).Validate(admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{}, test.ns, "", schema.GroupVersionResource{}, "", test.operation, false, nil), nil)
if test.validate && err != nil { if test.validate && err != nil {
t.Errorf("expected admit to succeed, but failed: %v", err) t.Errorf("expected admit to succeed, but failed: %v", err)
continue continue
@ -196,14 +196,14 @@ type mutatingAndValidatingFakeHandler struct {
validate bool validate bool
} }
func (h *mutatingAndValidatingFakeHandler) Admit(a admission.Attributes) (err error) { func (h *mutatingAndValidatingFakeHandler) Admit(a admission.Attributes, o admission.ObjectInterfaces) (err error) {
if h.admit { if h.admit {
return nil return nil
} }
return fmt.Errorf("don't admit") return fmt.Errorf("don't admit")
} }
func (h *mutatingAndValidatingFakeHandler) Validate(a admission.Attributes) (err error) { func (h *mutatingAndValidatingFakeHandler) Validate(a admission.Attributes, o admission.ObjectInterfaces) (err error) {
if h.validate { if h.validate {
return nil return nil
} }
@ -215,7 +215,7 @@ type validatingFakeHandler struct {
validate bool validate bool
} }
func (h *validatingFakeHandler) Validate(a admission.Attributes) (err error) { func (h *validatingFakeHandler) Validate(a admission.Attributes, o admission.ObjectInterfaces) (err error) {
if h.validate { if h.validate {
return nil return nil
} }
@ -227,7 +227,7 @@ type mutatingFakeHandler struct {
admit bool admit bool
} }
func (h *mutatingFakeHandler) Admit(a admission.Attributes) (err error) { func (h *mutatingFakeHandler) Admit(a admission.Attributes, o admission.ObjectInterfaces) (err error) {
if h.admit { if h.admit {
return nil return nil
} }

View File

@ -17,39 +17,25 @@ limitations under the License.
package generic package generic
import ( import (
"fmt"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/admission"
) )
// convertor converts objects to the desired version.
type convertor struct {
Scheme *runtime.Scheme
}
// ConvertToGVK converts object to the desired gvk. // ConvertToGVK converts object to the desired gvk.
func (c *convertor) ConvertToGVK(obj runtime.Object, gvk schema.GroupVersionKind) (runtime.Object, error) { func ConvertToGVK(obj runtime.Object, gvk schema.GroupVersionKind, o admission.ObjectInterfaces) (runtime.Object, error) {
// Unlike other resources, custom resources do not have internal version, so // Unlike other resources, custom resources do not have internal version, so
// if obj is a custom resource, it should not need conversion. // if obj is a custom resource, it should not need conversion.
if obj.GetObjectKind().GroupVersionKind() == gvk { if obj.GetObjectKind().GroupVersionKind() == gvk {
return obj, nil return obj, nil
} }
out, err := c.Scheme.New(gvk) out, err := o.GetObjectCreater().New(gvk)
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = c.Scheme.Convert(obj, out, nil) err = o.GetObjectConvertor().Convert(obj, out, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return out, nil return out, nil
} }
// Validate checks if the conversion has a scheme.
func (c *convertor) Validate() error {
if c.Scheme == nil {
return fmt.Errorf("the convertor requires a scheme")
}
return nil
}

View File

@ -26,6 +26,7 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/apis/example" "k8s.io/apiserver/pkg/apis/example"
examplev1 "k8s.io/apiserver/pkg/apis/example/v1" examplev1 "k8s.io/apiserver/pkg/apis/example/v1"
example2v1 "k8s.io/apiserver/pkg/apis/example2/v1" example2v1 "k8s.io/apiserver/pkg/apis/example2/v1"
@ -41,7 +42,7 @@ func initiateScheme(t *testing.T) *runtime.Scheme {
func TestConvertToGVK(t *testing.T) { func TestConvertToGVK(t *testing.T) {
scheme := initiateScheme(t) scheme := initiateScheme(t)
c := convertor{Scheme: scheme} o := &admission.SchemeBasedObjectInterfaces{scheme}
table := map[string]struct { table := map[string]struct {
obj runtime.Object obj runtime.Object
gvk schema.GroupVersionKind gvk schema.GroupVersionKind
@ -122,7 +123,7 @@ func TestConvertToGVK(t *testing.T) {
for name, test := range table { for name, test := range table {
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
actual, err := c.ConvertToGVK(test.obj, test.gvk) actual, err := ConvertToGVK(test.obj, test.gvk, o)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }

View File

@ -43,7 +43,6 @@ type Webhook struct {
hookSource Source hookSource Source
clientManager *webhook.ClientManager clientManager *webhook.ClientManager
convertor *convertor
namespaceMatcher *namespace.Matcher namespaceMatcher *namespace.Matcher
dispatcher Dispatcher dispatcher Dispatcher
} }
@ -79,7 +78,6 @@ func NewWebhook(handler *admission.Handler, configFile io.Reader, sourceFactory
Handler: handler, Handler: handler,
sourceFactory: sourceFactory, sourceFactory: sourceFactory,
clientManager: &cm, clientManager: &cm,
convertor: &convertor{},
namespaceMatcher: &namespace.Matcher{}, namespaceMatcher: &namespace.Matcher{},
dispatcher: dispatcherFactory(&cm), dispatcher: dispatcherFactory(&cm),
}, nil }, nil
@ -100,9 +98,6 @@ func (a *Webhook) SetServiceResolver(sr webhook.ServiceResolver) {
// SetScheme sets a serializer(NegotiatedSerializer) which is derived from the scheme // SetScheme sets a serializer(NegotiatedSerializer) which is derived from the scheme
func (a *Webhook) SetScheme(scheme *runtime.Scheme) { func (a *Webhook) SetScheme(scheme *runtime.Scheme) {
if scheme != nil {
a.convertor.Scheme = scheme
}
} }
// SetExternalKubeClientSet implements the WantsExternalKubeInformerFactory interface. // SetExternalKubeClientSet implements the WantsExternalKubeInformerFactory interface.
@ -132,9 +127,6 @@ func (a *Webhook) ValidateInitialization() error {
if err := a.clientManager.Validate(); err != nil { if err := a.clientManager.Validate(); err != nil {
return fmt.Errorf("clientManager is not properly setup: %v", err) return fmt.Errorf("clientManager is not properly setup: %v", err)
} }
if err := a.convertor.Validate(); err != nil {
return fmt.Errorf("convertor is not properly setup: %v", err)
}
return nil return nil
} }
@ -156,7 +148,7 @@ func (a *Webhook) ShouldCallHook(h *v1beta1.Webhook, attr admission.Attributes)
} }
// Dispatch is called by the downstream Validate or Admit methods. // Dispatch is called by the downstream Validate or Admit methods.
func (a *Webhook) Dispatch(attr admission.Attributes) error { func (a *Webhook) Dispatch(attr admission.Attributes, o admission.ObjectInterfaces) error {
if rules.IsWebhookConfigurationResource(attr) { if rules.IsWebhookConfigurationResource(attr) {
return nil return nil
} }
@ -188,14 +180,14 @@ func (a *Webhook) Dispatch(attr admission.Attributes) error {
Attributes: attr, Attributes: attr,
} }
if oldObj := attr.GetOldObject(); oldObj != nil { if oldObj := attr.GetOldObject(); oldObj != nil {
out, err := a.convertor.ConvertToGVK(oldObj, attr.GetKind()) out, err := ConvertToGVK(oldObj, attr.GetKind(), o)
if err != nil { if err != nil {
return apierrors.NewInternalError(err) return apierrors.NewInternalError(err)
} }
versionedAttr.VersionedOldObject = out versionedAttr.VersionedOldObject = out
} }
if obj := attr.GetObject(); obj != nil { if obj := attr.GetObject(); obj != nil {
out, err := a.convertor.ConvertToGVK(obj, attr.GetKind()) out, err := ConvertToGVK(obj, attr.GetKind(), o)
if err != nil { if err != nil {
return apierrors.NewInternalError(err) return apierrors.NewInternalError(err)
} }

View File

@ -91,6 +91,6 @@ func (a *Plugin) ValidateInitialization() error {
} }
// Admit makes an admission decision based on the request attributes. // Admit makes an admission decision based on the request attributes.
func (a *Plugin) Admit(attr admission.Attributes) error { func (a *Plugin) Admit(attr admission.Attributes, o admission.ObjectInterfaces) error {
return a.Webhook.Dispatch(attr) return a.Webhook.Dispatch(attr, o)
} }

View File

@ -23,23 +23,15 @@ import (
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"k8s.io/api/admission/v1beta1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/admission"
webhooktesting "k8s.io/apiserver/pkg/admission/plugin/webhook/testing" webhooktesting "k8s.io/apiserver/pkg/admission/plugin/webhook/testing"
) )
// TestAdmit tests that MutatingWebhook#Admit works as expected // TestAdmit tests that MutatingWebhook#Admit works as expected
func TestAdmit(t *testing.T) { func TestAdmit(t *testing.T) {
scheme := runtime.NewScheme()
require.NoError(t, v1beta1.AddToScheme(scheme))
require.NoError(t, corev1.AddToScheme(scheme))
testServer := webhooktesting.NewTestServer(t) testServer := webhooktesting.NewTestServer(t)
testServer.StartTLS() testServer.StartTLS()
defer testServer.Close() defer testServer.Close()
@ -48,6 +40,8 @@ func TestAdmit(t *testing.T) {
t.Fatalf("this should never happen? %v", err) t.Fatalf("this should never happen? %v", err)
} }
objectInterfaces := webhooktesting.NewObjectInterfacesForTest()
stopCh := make(chan struct{}) stopCh := make(chan struct{})
defer close(stopCh) defer close(stopCh)
@ -66,7 +60,6 @@ func TestAdmit(t *testing.T) {
wh.SetAuthenticationInfoResolverWrapper(webhooktesting.Wrapper(webhooktesting.NewAuthenticationInfoResolver(new(int32)))) wh.SetAuthenticationInfoResolverWrapper(webhooktesting.Wrapper(webhooktesting.NewAuthenticationInfoResolver(new(int32))))
wh.SetServiceResolver(webhooktesting.NewServiceResolver(*serverURL)) wh.SetServiceResolver(webhooktesting.NewServiceResolver(*serverURL))
wh.SetScheme(scheme)
wh.SetExternalKubeClientSet(client) wh.SetExternalKubeClientSet(client)
wh.SetExternalKubeInformerFactory(informer) wh.SetExternalKubeInformerFactory(informer)
@ -85,7 +78,7 @@ func TestAdmit(t *testing.T) {
attr = webhooktesting.NewAttribute(ns, tt.AdditionalLabels, tt.IsDryRun) attr = webhooktesting.NewAttribute(ns, tt.AdditionalLabels, tt.IsDryRun)
} }
err = wh.Admit(attr) err = wh.Admit(attr, objectInterfaces)
if tt.ExpectAllow != (err == nil) { if tt.ExpectAllow != (err == nil) {
t.Errorf("%s: expected allowed=%v, but got err=%v", tt.Name, tt.ExpectAllow, err) t.Errorf("%s: expected allowed=%v, but got err=%v", tt.Name, tt.ExpectAllow, err)
} }
@ -118,10 +111,6 @@ func TestAdmit(t *testing.T) {
// TestAdmitCachedClient tests that MutatingWebhook#Admit should cache restClient // TestAdmitCachedClient tests that MutatingWebhook#Admit should cache restClient
func TestAdmitCachedClient(t *testing.T) { func TestAdmitCachedClient(t *testing.T) {
scheme := runtime.NewScheme()
require.NoError(t, v1beta1.AddToScheme(scheme))
require.NoError(t, corev1.AddToScheme(scheme))
testServer := webhooktesting.NewTestServer(t) testServer := webhooktesting.NewTestServer(t)
testServer.StartTLS() testServer.StartTLS()
defer testServer.Close() defer testServer.Close()
@ -130,6 +119,8 @@ func TestAdmitCachedClient(t *testing.T) {
t.Fatalf("this should never happen? %v", err) t.Fatalf("this should never happen? %v", err)
} }
objectInterfaces := webhooktesting.NewObjectInterfacesForTest()
stopCh := make(chan struct{}) stopCh := make(chan struct{})
defer close(stopCh) defer close(stopCh)
@ -138,7 +129,6 @@ func TestAdmitCachedClient(t *testing.T) {
t.Fatalf("Failed to create mutating webhook: %v", err) t.Fatalf("Failed to create mutating webhook: %v", err)
} }
wh.SetServiceResolver(webhooktesting.NewServiceResolver(*serverURL)) wh.SetServiceResolver(webhooktesting.NewServiceResolver(*serverURL))
wh.SetScheme(scheme)
for _, tt := range webhooktesting.NewCachedClientTestcases(serverURL) { for _, tt := range webhooktesting.NewCachedClientTestcases(serverURL) {
ns := "webhook-test" ns := "webhook-test"
@ -158,7 +148,7 @@ func TestAdmitCachedClient(t *testing.T) {
continue continue
} }
err = wh.Admit(webhooktesting.NewAttribute(ns, nil, false)) err = wh.Admit(webhooktesting.NewAttribute(ns, nil, false), objectInterfaces)
if tt.ExpectAllow != (err == nil) { if tt.ExpectAllow != (err == nil) {
t.Errorf("%s: expected allowed=%v, but got err=%v", tt.Name, tt.ExpectAllow, err) t.Errorf("%s: expected allowed=%v, but got err=%v", tt.Name, tt.ExpectAllow, err)
} }

View File

@ -649,3 +649,10 @@ func newMatchEverythingRules() []registrationv1beta1.RuleWithOperations {
}, },
}} }}
} }
// NewObjectInterfacesForTest returns an ObjectInterfaces appropriate for test cases in this file.
func NewObjectInterfacesForTest() admission.ObjectInterfaces {
scheme := runtime.NewScheme()
corev1.AddToScheme(scheme)
return &admission.SchemeBasedObjectInterfaces{scheme}
}

View File

@ -59,6 +59,6 @@ func NewValidatingAdmissionWebhook(configFile io.Reader) (*Plugin, error) {
} }
// Validate makes an admission decision based on the request attributes. // Validate makes an admission decision based on the request attributes.
func (a *Plugin) Validate(attr admission.Attributes) error { func (a *Plugin) Validate(attr admission.Attributes, o admission.ObjectInterfaces) error {
return a.Webhook.Dispatch(attr) return a.Webhook.Dispatch(attr, o)
} }

View File

@ -22,25 +22,19 @@ import (
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"k8s.io/api/admission/v1beta1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
webhooktesting "k8s.io/apiserver/pkg/admission/plugin/webhook/testing" webhooktesting "k8s.io/apiserver/pkg/admission/plugin/webhook/testing"
) )
// TestValidate tests that ValidatingWebhook#Validate works as expected // TestValidate tests that ValidatingWebhook#Validate works as expected
func TestValidate(t *testing.T) { func TestValidate(t *testing.T) {
scheme := runtime.NewScheme()
require.NoError(t, v1beta1.AddToScheme(scheme))
require.NoError(t, corev1.AddToScheme(scheme))
testServer := webhooktesting.NewTestServer(t) testServer := webhooktesting.NewTestServer(t)
testServer.StartTLS() testServer.StartTLS()
defer testServer.Close() defer testServer.Close()
objectInterfaces := webhooktesting.NewObjectInterfacesForTest()
serverURL, err := url.ParseRequestURI(testServer.URL) serverURL, err := url.ParseRequestURI(testServer.URL)
if err != nil { if err != nil {
t.Fatalf("this should never happen? %v", err) t.Fatalf("this should never happen? %v", err)
@ -61,7 +55,6 @@ func TestValidate(t *testing.T) {
wh.SetAuthenticationInfoResolverWrapper(webhooktesting.Wrapper(webhooktesting.NewAuthenticationInfoResolver(new(int32)))) wh.SetAuthenticationInfoResolverWrapper(webhooktesting.Wrapper(webhooktesting.NewAuthenticationInfoResolver(new(int32))))
wh.SetServiceResolver(webhooktesting.NewServiceResolver(*serverURL)) wh.SetServiceResolver(webhooktesting.NewServiceResolver(*serverURL))
wh.SetScheme(scheme)
wh.SetExternalKubeClientSet(client) wh.SetExternalKubeClientSet(client)
wh.SetExternalKubeInformerFactory(informer) wh.SetExternalKubeInformerFactory(informer)
@ -74,7 +67,7 @@ func TestValidate(t *testing.T) {
} }
attr := webhooktesting.NewAttribute(ns, nil, tt.IsDryRun) attr := webhooktesting.NewAttribute(ns, nil, tt.IsDryRun)
err = wh.Validate(attr) err = wh.Validate(attr, objectInterfaces)
if tt.ExpectAllow != (err == nil) { if tt.ExpectAllow != (err == nil) {
t.Errorf("%s: expected allowed=%v, but got err=%v", tt.Name, tt.ExpectAllow, err) t.Errorf("%s: expected allowed=%v, but got err=%v", tt.Name, tt.ExpectAllow, err)
} }
@ -102,10 +95,6 @@ func TestValidate(t *testing.T) {
// TestValidateCachedClient tests that ValidatingWebhook#Validate should cache restClient // TestValidateCachedClient tests that ValidatingWebhook#Validate should cache restClient
func TestValidateCachedClient(t *testing.T) { func TestValidateCachedClient(t *testing.T) {
scheme := runtime.NewScheme()
require.NoError(t, v1beta1.AddToScheme(scheme))
require.NoError(t, corev1.AddToScheme(scheme))
testServer := webhooktesting.NewTestServer(t) testServer := webhooktesting.NewTestServer(t)
testServer.StartTLS() testServer.StartTLS()
defer testServer.Close() defer testServer.Close()
@ -114,6 +103,8 @@ func TestValidateCachedClient(t *testing.T) {
t.Fatalf("this should never happen? %v", err) t.Fatalf("this should never happen? %v", err)
} }
objectInterfaces := webhooktesting.NewObjectInterfacesForTest()
stopCh := make(chan struct{}) stopCh := make(chan struct{})
defer close(stopCh) defer close(stopCh)
@ -122,7 +113,6 @@ func TestValidateCachedClient(t *testing.T) {
t.Fatalf("Failed to create validating webhook: %v", err) t.Fatalf("Failed to create validating webhook: %v", err)
} }
wh.SetServiceResolver(webhooktesting.NewServiceResolver(*serverURL)) wh.SetServiceResolver(webhooktesting.NewServiceResolver(*serverURL))
wh.SetScheme(scheme)
for _, tt := range webhooktesting.NewCachedClientTestcases(serverURL) { for _, tt := range webhooktesting.NewCachedClientTestcases(serverURL) {
ns := "webhook-test" ns := "webhook-test"
@ -142,7 +132,7 @@ func TestValidateCachedClient(t *testing.T) {
continue continue
} }
err = wh.Validate(webhooktesting.NewAttribute(ns, nil, false)) err = wh.Validate(webhooktesting.NewAttribute(ns, nil, false), objectInterfaces)
if tt.ExpectAllow != (err == nil) { if tt.ExpectAllow != (err == nil) {
t.Errorf("%s: expected allowed=%v, but got err=%v", tt.Name, tt.ExpectAllow, err) t.Errorf("%s: expected allowed=%v, but got err=%v", tt.Name, tt.ExpectAllow, err)
} }

28
pkg/admission/util.go Normal file
View File

@ -0,0 +1,28 @@
/*
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
import "k8s.io/apimachinery/pkg/runtime"
type SchemeBasedObjectInterfaces struct {
Scheme *runtime.Scheme
}
func (r *SchemeBasedObjectInterfaces) GetObjectCreater() runtime.ObjectCreater { return r.Scheme }
func (r *SchemeBasedObjectInterfaces) GetObjectTyper() runtime.ObjectTyper { return r.Scheme }
func (r *SchemeBasedObjectInterfaces) GetObjectDefaulter() runtime.ObjectDefaulter { return r.Scheme }
func (r *SchemeBasedObjectInterfaces) GetObjectConvertor() runtime.ObjectConvertor { return r.Scheme }

View File

@ -76,7 +76,7 @@ import (
type alwaysMutatingDeny struct{} type alwaysMutatingDeny struct{}
func (alwaysMutatingDeny) Admit(a admission.Attributes) (err error) { func (alwaysMutatingDeny) Admit(a admission.Attributes, o admission.ObjectInterfaces) (err error) {
return admission.NewForbidden(a, errors.New("Mutating admission control is denying all modifications")) return admission.NewForbidden(a, errors.New("Mutating admission control is denying all modifications"))
} }
@ -86,7 +86,7 @@ func (alwaysMutatingDeny) Handles(operation admission.Operation) bool {
type alwaysValidatingDeny struct{} type alwaysValidatingDeny struct{}
func (alwaysValidatingDeny) Validate(a admission.Attributes) (err error) { func (alwaysValidatingDeny) Validate(a admission.Attributes, o admission.ObjectInterfaces) (err error) {
return admission.NewForbidden(a, errors.New("Validating admission control is denying all modifications")) return admission.NewForbidden(a, errors.New("Validating admission control is denying all modifications"))
} }

View File

@ -127,7 +127,7 @@ func createHandler(r rest.NamedCreater, scope RequestScope, admit admission.Inte
userInfo, _ := request.UserFrom(ctx) userInfo, _ := request.UserFrom(ctx)
admissionAttributes := admission.NewAttributesRecord(obj, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Create, dryrun.IsDryRun(options.DryRun), userInfo) admissionAttributes := admission.NewAttributesRecord(obj, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Create, dryrun.IsDryRun(options.DryRun), userInfo)
if mutatingAdmission, ok := admit.(admission.MutationInterface); ok && mutatingAdmission.Handles(admission.Create) { if mutatingAdmission, ok := admit.(admission.MutationInterface); ok && mutatingAdmission.Handles(admission.Create) {
err = mutatingAdmission.Admit(admissionAttributes) err = mutatingAdmission.Admit(admissionAttributes, &scope)
if err != nil { if err != nil {
scope.err(err, w, req) scope.err(err, w, req)
return return
@ -154,7 +154,7 @@ func createHandler(r rest.NamedCreater, scope RequestScope, admit admission.Inte
ctx, ctx,
name, name,
obj, obj,
rest.AdmissionToValidateObjectFunc(admit, admissionAttributes), rest.AdmissionToValidateObjectFunc(admit, admissionAttributes, &scope),
options, options,
) )
}) })

View File

@ -119,13 +119,13 @@ func DeleteResource(r rest.GracefulDeleter, allowsOptions bool, scope RequestSco
userInfo, _ := request.UserFrom(ctx) userInfo, _ := request.UserFrom(ctx)
attrs := admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Delete, dryrun.IsDryRun(options.DryRun), userInfo) attrs := admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Delete, dryrun.IsDryRun(options.DryRun), userInfo)
if mutatingAdmission, ok := admit.(admission.MutationInterface); ok { if mutatingAdmission, ok := admit.(admission.MutationInterface); ok {
if err := mutatingAdmission.Admit(attrs); err != nil { if err := mutatingAdmission.Admit(attrs, &scope); err != nil {
scope.err(err, w, req) scope.err(err, w, req)
return return
} }
} }
if validatingAdmission, ok := admit.(admission.ValidationInterface); ok { if validatingAdmission, ok := admit.(admission.ValidationInterface); ok {
if err := validatingAdmission.Validate(attrs); err != nil { if err := validatingAdmission.Validate(attrs, &scope); err != nil {
scope.err(err, w, req) scope.err(err, w, req)
return return
} }
@ -269,7 +269,7 @@ func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope RequestSco
userInfo, _ := request.UserFrom(ctx) userInfo, _ := request.UserFrom(ctx)
attrs := admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, "", scope.Resource, scope.Subresource, admission.Delete, dryrun.IsDryRun(options.DryRun), userInfo) attrs := admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, "", scope.Resource, scope.Subresource, admission.Delete, dryrun.IsDryRun(options.DryRun), userInfo)
if mutatingAdmission, ok := admit.(admission.MutationInterface); ok { if mutatingAdmission, ok := admit.(admission.MutationInterface); ok {
err = mutatingAdmission.Admit(attrs) err = mutatingAdmission.Admit(attrs, &scope)
if err != nil { if err != nil {
scope.err(err, w, req) scope.err(err, w, req)
return return
@ -277,7 +277,7 @@ func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope RequestSco
} }
if validatingAdmission, ok := admit.(admission.ValidationInterface); ok { if validatingAdmission, ok := admit.(admission.ValidationInterface); ok {
err = validatingAdmission.Validate(attrs) err = validatingAdmission.Validate(attrs, &scope)
if err != nil { if err != nil {
scope.err(err, w, req) scope.err(err, w, req)
return return

View File

@ -191,10 +191,12 @@ func PatchResource(r rest.Patcher, scope RequestScope, admit admission.Interface
subresource: scope.Subresource, subresource: scope.Subresource,
dryRun: dryrun.IsDryRun(options.DryRun), dryRun: dryrun.IsDryRun(options.DryRun),
objectInterfaces: &scope,
hubGroupVersion: scope.HubGroupVersion, hubGroupVersion: scope.HubGroupVersion,
createValidation: withAuthorization(rest.AdmissionToValidateObjectFunc(admit, staticCreateAttributes), scope.Authorizer, createAuthorizerAttributes), createValidation: withAuthorization(rest.AdmissionToValidateObjectFunc(admit, staticCreateAttributes, &scope), scope.Authorizer, createAuthorizerAttributes),
updateValidation: rest.AdmissionToValidateObjectUpdateFunc(admit, staticUpdateAttributes), updateValidation: rest.AdmissionToValidateObjectUpdateFunc(admit, staticUpdateAttributes, &scope),
admissionCheck: mutatingAdmission, admissionCheck: mutatingAdmission,
codec: codec, codec: codec,
@ -257,6 +259,8 @@ type patcher struct {
subresource string subresource string
dryRun bool dryRun bool
objectInterfaces admission.ObjectInterfaces
hubGroupVersion schema.GroupVersion hubGroupVersion schema.GroupVersion
// Validation functions // Validation functions
@ -507,7 +511,7 @@ func (p *patcher) applyAdmission(ctx context.Context, patchedObject runtime.Obje
} }
if p.admissionCheck != nil && p.admissionCheck.Handles(operation) { if p.admissionCheck != nil && p.admissionCheck.Handles(operation) {
attributes := p.admissionAttributes(ctx, patchedObject, currentObject, operation) attributes := p.admissionAttributes(ctx, patchedObject, currentObject, operation)
return patchedObject, p.admissionCheck.Admit(attributes) return patchedObject, p.admissionCheck.Admit(attributes, p.objectInterfaces)
} }
return patchedObject, nil return patchedObject, nil
} }

View File

@ -103,6 +103,13 @@ func (scope *RequestScope) AllowsStreamSchema(s string) bool {
return s == "watch" return s == "watch"
} }
var _ admission.ObjectInterfaces = &RequestScope{}
func (r *RequestScope) GetObjectCreater() runtime.ObjectCreater { return r.Creater }
func (r *RequestScope) GetObjectTyper() runtime.ObjectTyper { return r.Typer }
func (r *RequestScope) GetObjectDefaulter() runtime.ObjectDefaulter { return r.Defaulter }
func (r *RequestScope) GetObjectConvertor() runtime.ObjectConvertor { return r.Convertor }
// ConnectResource returns a function that handles a connect request on a rest.Storage object. // ConnectResource returns a function that handles a connect request on a rest.Storage object.
func ConnectResource(connecter rest.Connecter, scope RequestScope, admit admission.Interface, restPath string, isSubresource bool) http.HandlerFunc { func ConnectResource(connecter rest.Connecter, scope RequestScope, admit admission.Interface, restPath string, isSubresource bool) http.HandlerFunc {
return func(w http.ResponseWriter, req *http.Request) { return func(w http.ResponseWriter, req *http.Request) {
@ -131,14 +138,14 @@ func ConnectResource(connecter rest.Connecter, scope RequestScope, admit admissi
userInfo, _ := request.UserFrom(ctx) userInfo, _ := request.UserFrom(ctx)
// TODO: remove the mutating admission here as soon as we have ported all plugin that handle CONNECT // TODO: remove the mutating admission here as soon as we have ported all plugin that handle CONNECT
if mutatingAdmission, ok := admit.(admission.MutationInterface); ok { if mutatingAdmission, ok := admit.(admission.MutationInterface); ok {
err = mutatingAdmission.Admit(admission.NewAttributesRecord(opts, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Connect, false, userInfo)) err = mutatingAdmission.Admit(admission.NewAttributesRecord(opts, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Connect, false, userInfo), &scope)
if err != nil { if err != nil {
scope.err(err, w, req) scope.err(err, w, req)
return return
} }
} }
if validatingAdmission, ok := admit.(admission.ValidationInterface); ok { if validatingAdmission, ok := admit.(admission.ValidationInterface); ok {
err = validatingAdmission.Validate(admission.NewAttributesRecord(opts, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Connect, false, userInfo)) err = validatingAdmission.Validate(admission.NewAttributesRecord(opts, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Connect, false, userInfo), &scope)
if err != nil { if err != nil {
scope.err(err, w, req) scope.err(err, w, req)
return return

View File

@ -365,6 +365,7 @@ func (tc *patchTestCase) Run(t *testing.T) {
creater := runtime.ObjectCreater(scheme) creater := runtime.ObjectCreater(scheme)
defaulter := runtime.ObjectDefaulter(scheme) defaulter := runtime.ObjectDefaulter(scheme)
convertor := runtime.UnsafeObjectConvertor(scheme) convertor := runtime.UnsafeObjectConvertor(scheme)
objectInterfaces := &admission.SchemeBasedObjectInterfaces{scheme}
kind := examplev1.SchemeGroupVersion.WithKind("Pod") kind := examplev1.SchemeGroupVersion.WithKind("Pod")
resource := examplev1.SchemeGroupVersion.WithResource("pods") resource := examplev1.SchemeGroupVersion.WithResource("pods")
schemaReferenceObj := &examplev1.Pod{} schemaReferenceObj := &examplev1.Pod{}
@ -441,6 +442,8 @@ func (tc *patchTestCase) Run(t *testing.T) {
kind: kind, kind: kind,
resource: resource, resource: resource,
objectInterfaces: objectInterfaces,
hubGroupVersion: hubVersion, hubGroupVersion: hubVersion,
createValidation: rest.ValidateAllObjectFunc, createValidation: rest.ValidateAllObjectFunc,
@ -944,6 +947,6 @@ func (f mutateObjectUpdateFunc) Handles(operation admission.Operation) bool {
return true return true
} }
func (f mutateObjectUpdateFunc) Admit(a admission.Attributes) (err error) { func (f mutateObjectUpdateFunc) Admit(a admission.Attributes, o admission.ObjectInterfaces) (err error) {
return f(a.GetObject(), a.GetOldObject()) return f(a.GetObject(), a.GetOldObject())
} }

View File

@ -138,11 +138,11 @@ func UpdateResource(r rest.Updater, scope RequestScope, admit admission.Interfac
return nil, fmt.Errorf("unexpected error when extracting UID from oldObj: %v", err.Error()) return nil, fmt.Errorf("unexpected error when extracting UID from oldObj: %v", err.Error())
} else if !isNotZeroObject { } else if !isNotZeroObject {
if mutatingAdmission.Handles(admission.Create) { if mutatingAdmission.Handles(admission.Create) {
return newObj, mutatingAdmission.Admit(admission.NewAttributesRecord(newObj, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Create, dryrun.IsDryRun(options.DryRun), userInfo)) return newObj, mutatingAdmission.Admit(admission.NewAttributesRecord(newObj, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Create, dryrun.IsDryRun(options.DryRun), userInfo), &scope)
} }
} else { } else {
if mutatingAdmission.Handles(admission.Update) { if mutatingAdmission.Handles(admission.Update) {
return newObj, mutatingAdmission.Admit(admission.NewAttributesRecord(newObj, oldObj, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Update, dryrun.IsDryRun(options.DryRun), userInfo)) return newObj, mutatingAdmission.Admit(admission.NewAttributesRecord(newObj, oldObj, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Update, dryrun.IsDryRun(options.DryRun), userInfo), &scope)
} }
} }
return newObj, nil return newObj, nil
@ -172,11 +172,11 @@ func UpdateResource(r rest.Updater, scope RequestScope, admit admission.Interfac
rest.DefaultUpdatedObjectInfo(obj, transformers...), rest.DefaultUpdatedObjectInfo(obj, transformers...),
withAuthorization(rest.AdmissionToValidateObjectFunc( withAuthorization(rest.AdmissionToValidateObjectFunc(
admit, admit,
admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Create, dryrun.IsDryRun(options.DryRun), userInfo)), admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Create, dryrun.IsDryRun(options.DryRun), userInfo), &scope),
scope.Authorizer, createAuthorizerAttributes), scope.Authorizer, createAuthorizerAttributes),
rest.AdmissionToValidateObjectUpdateFunc( rest.AdmissionToValidateObjectUpdateFunc(
admit, admit,
admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Update, dryrun.IsDryRun(options.DryRun), userInfo)), admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Update, dryrun.IsDryRun(options.DryRun), userInfo), &scope),
false, false,
options, options,
) )

View File

@ -160,7 +160,7 @@ type NamespaceScopedStrategy interface {
} }
// AdmissionToValidateObjectFunc converts validating admission to a rest validate object func // AdmissionToValidateObjectFunc converts validating admission to a rest validate object func
func AdmissionToValidateObjectFunc(admit admission.Interface, staticAttributes admission.Attributes) ValidateObjectFunc { func AdmissionToValidateObjectFunc(admit admission.Interface, staticAttributes admission.Attributes, o admission.ObjectInterfaces) ValidateObjectFunc {
validatingAdmission, ok := admit.(admission.ValidationInterface) validatingAdmission, ok := admit.(admission.ValidationInterface)
if !ok { if !ok {
return func(obj runtime.Object) error { return nil } return func(obj runtime.Object) error { return nil }
@ -181,6 +181,6 @@ func AdmissionToValidateObjectFunc(admit admission.Interface, staticAttributes a
if !validatingAdmission.Handles(finalAttributes.GetOperation()) { if !validatingAdmission.Handles(finalAttributes.GetOperation()) {
return nil return nil
} }
return validatingAdmission.Validate(finalAttributes) return validatingAdmission.Validate(finalAttributes, o)
} }
} }

View File

@ -256,7 +256,7 @@ func (i *wrappedUpdatedObjectInfo) UpdatedObject(ctx context.Context, oldObj run
} }
// AdmissionToValidateObjectUpdateFunc converts validating admission to a rest validate object update func // AdmissionToValidateObjectUpdateFunc converts validating admission to a rest validate object update func
func AdmissionToValidateObjectUpdateFunc(admit admission.Interface, staticAttributes admission.Attributes) ValidateObjectUpdateFunc { func AdmissionToValidateObjectUpdateFunc(admit admission.Interface, staticAttributes admission.Attributes, o admission.ObjectInterfaces) ValidateObjectUpdateFunc {
validatingAdmission, ok := admit.(admission.ValidationInterface) validatingAdmission, ok := admit.(admission.ValidationInterface)
if !ok { if !ok {
return func(obj, old runtime.Object) error { return nil } return func(obj, old runtime.Object) error { return nil }
@ -277,6 +277,6 @@ func AdmissionToValidateObjectUpdateFunc(admit admission.Interface, staticAttrib
if !validatingAdmission.Handles(finalAttributes.GetOperation()) { if !validatingAdmission.Handles(finalAttributes.GetOperation()) {
return nil return nil
} }
return validatingAdmission.Validate(finalAttributes) return validatingAdmission.Validate(finalAttributes, o)
} }
} }