diff --git a/pkg/kubeapiserver/admission/initializer.go b/pkg/kubeapiserver/admission/initializer.go index fd6bf4cefa4..0a93b8ae1f1 100644 --- a/pkg/kubeapiserver/admission/initializer.go +++ b/pkg/kubeapiserver/admission/initializer.go @@ -34,13 +34,13 @@ import ( // WantsInternalKubeClientSet defines a function which sets ClientSet for admission plugins that need it type WantsInternalKubeClientSet interface { SetInternalKubeClientSet(internalclientset.Interface) - admission.Validator + admission.InitializationValidator } // WantsInternalKubeInformerFactory defines a function which sets InformerFactory for admission plugins that need it type WantsInternalKubeInformerFactory interface { SetInternalKubeInformerFactory(informers.SharedInformerFactory) - admission.Validator + admission.InitializationValidator } // WantsCloudConfig defines a function which sets CloudConfig for admission plugins that need it. @@ -56,7 +56,7 @@ type WantsRESTMapper interface { // WantsQuotaConfiguration defines a function which sets quota configuration for admission plugins that need it. type WantsQuotaConfiguration interface { SetQuotaConfiguration(quota.Configuration) - admission.Validator + admission.InitializationValidator } // WantsServiceResolver defines a fuction that accepts a ServiceResolver for @@ -75,7 +75,7 @@ type ServiceResolver interface { // to allow the apiserver to control what is returned as auth info type WantsAuthenticationInfoResolverWrapper interface { SetAuthenticationInfoResolverWrapper(webhook.AuthenticationInfoResolverWrapper) - admission.Validator + admission.InitializationValidator } type PluginInitializer struct { diff --git a/pkg/master/reconcilers/BUILD b/pkg/master/reconcilers/BUILD index ae48372d4f0..20ca7b24555 100644 --- a/pkg/master/reconcilers/BUILD +++ b/pkg/master/reconcilers/BUILD @@ -21,6 +21,7 @@ go_library( "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library", + "//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library", "//vendor/k8s.io/apiserver/pkg/storage:go_default_library", "//vendor/k8s.io/client-go/util/retry:go_default_library", ], diff --git a/pkg/master/reconcilers/lease.go b/pkg/master/reconcilers/lease.go index e3c3f46240b..fbb4d2da809 100644 --- a/pkg/master/reconcilers/lease.go +++ b/pkg/master/reconcilers/lease.go @@ -33,6 +33,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" kruntime "k8s.io/apimachinery/pkg/runtime" apirequest "k8s.io/apiserver/pkg/endpoints/request" + "k8s.io/apiserver/pkg/registry/rest" "k8s.io/apiserver/pkg/storage" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/endpoints" @@ -220,7 +221,7 @@ func (r *leaseEndpointReconciler) doReconcile(serviceName string, endpointPorts } glog.Warningf("Resetting endpoints for master service %q to %v", serviceName, masterIPs) - return r.endpointRegistry.UpdateEndpoints(ctx, e) + return r.endpointRegistry.UpdateEndpoints(ctx, e, rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc) } // checkEndpointSubsetFormatWithLease determines if the endpoint is in the diff --git a/pkg/registry/apps/statefulset/registry.go b/pkg/registry/apps/statefulset/registry.go index beef2d75365..eb33b6e1ced 100644 --- a/pkg/registry/apps/statefulset/registry.go +++ b/pkg/registry/apps/statefulset/registry.go @@ -33,8 +33,8 @@ type Registry interface { ListStatefulSets(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (*apps.StatefulSetList, error) WatchStatefulSets(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (watch.Interface, error) GetStatefulSet(ctx genericapirequest.Context, statefulSetID string, options *metav1.GetOptions) (*apps.StatefulSet, error) - CreateStatefulSet(ctx genericapirequest.Context, statefulSet *apps.StatefulSet) (*apps.StatefulSet, error) - UpdateStatefulSet(ctx genericapirequest.Context, statefulSet *apps.StatefulSet) (*apps.StatefulSet, error) + CreateStatefulSet(ctx genericapirequest.Context, statefulSet *apps.StatefulSet, createValidation rest.ValidateObjectFunc) (*apps.StatefulSet, error) + UpdateStatefulSet(ctx genericapirequest.Context, statefulSet *apps.StatefulSet, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (*apps.StatefulSet, error) DeleteStatefulSet(ctx genericapirequest.Context, statefulSetID string) error } @@ -72,16 +72,16 @@ func (s *storage) GetStatefulSet(ctx genericapirequest.Context, statefulSetID st return obj.(*apps.StatefulSet), nil } -func (s *storage) CreateStatefulSet(ctx genericapirequest.Context, statefulSet *apps.StatefulSet) (*apps.StatefulSet, error) { - obj, err := s.Create(ctx, statefulSet, false) +func (s *storage) CreateStatefulSet(ctx genericapirequest.Context, statefulSet *apps.StatefulSet, createValidation rest.ValidateObjectFunc) (*apps.StatefulSet, error) { + obj, err := s.Create(ctx, statefulSet, rest.ValidateAllObjectFunc, false) if err != nil { return nil, err } return obj.(*apps.StatefulSet), nil } -func (s *storage) UpdateStatefulSet(ctx genericapirequest.Context, statefulSet *apps.StatefulSet) (*apps.StatefulSet, error) { - obj, _, err := s.Update(ctx, statefulSet.Name, rest.DefaultUpdatedObjectInfo(statefulSet)) +func (s *storage) UpdateStatefulSet(ctx genericapirequest.Context, statefulSet *apps.StatefulSet, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (*apps.StatefulSet, error) { + obj, _, err := s.Update(ctx, statefulSet.Name, rest.DefaultUpdatedObjectInfo(statefulSet), createValidation, updateValidation) if err != nil { return nil, err } diff --git a/pkg/registry/apps/statefulset/storage/storage.go b/pkg/registry/apps/statefulset/storage/storage.go index 1eba35293a5..6adce290dc4 100644 --- a/pkg/registry/apps/statefulset/storage/storage.go +++ b/pkg/registry/apps/statefulset/storage/storage.go @@ -108,8 +108,8 @@ func (r *StatusREST) Get(ctx genericapirequest.Context, name string, options *me } // Update alters the status subset of an object. -func (r *StatusREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { - return r.store.Update(ctx, name, objInfo) +func (r *StatusREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (runtime.Object, bool, error) { + return r.store.Update(ctx, name, objInfo, createValidation, updateValidation) } // Implement ShortNamesProvider @@ -156,7 +156,7 @@ func (r *ScaleREST) Get(ctx genericapirequest.Context, name string, options *met return scale, err } -func (r *ScaleREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { +func (r *ScaleREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (runtime.Object, bool, error) { ss, err := r.registry.GetStatefulSet(ctx, name, &metav1.GetOptions{}) if err != nil { return nil, false, err @@ -185,7 +185,7 @@ func (r *ScaleREST) Update(ctx genericapirequest.Context, name string, objInfo r ss.Spec.Replicas = scale.Spec.Replicas ss.ResourceVersion = scale.ResourceVersion - ss, err = r.registry.UpdateStatefulSet(ctx, ss) + ss, err = r.registry.UpdateStatefulSet(ctx, ss, createValidation, updateValidation) if err != nil { return nil, false, err } diff --git a/pkg/registry/apps/statefulset/storage/storage_test.go b/pkg/registry/apps/statefulset/storage/storage_test.go index 82961bbd1eb..75761d01e5b 100644 --- a/pkg/registry/apps/statefulset/storage/storage_test.go +++ b/pkg/registry/apps/statefulset/storage/storage_test.go @@ -46,7 +46,7 @@ func newStorage(t *testing.T) (StatefulSetStorage, *etcdtesting.EtcdTestServer) // createStatefulSet is a helper function that returns a StatefulSet with the updated resource version. func createStatefulSet(storage *REST, ps apps.StatefulSet, t *testing.T) (apps.StatefulSet, error) { ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), ps.Namespace) - obj, err := storage.Create(ctx, &ps, false) + obj, err := storage.Create(ctx, &ps, rest.ValidateAllObjectFunc, false) if err != nil { t.Errorf("Failed to create StatefulSet, %v", err) } @@ -125,7 +125,7 @@ func TestStatusUpdate(t *testing.T) { }, } - if _, _, err := storage.Status.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update)); err != nil { + if _, _, err := storage.Status.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc); err != nil { t.Fatalf("unexpected error: %v", err) } obj, err := storage.StatefulSet.Get(ctx, "foo", &metav1.GetOptions{}) @@ -274,7 +274,7 @@ func TestScaleUpdate(t *testing.T) { }, } - if _, _, err := storage.Scale.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update)); err != nil { + if _, _, err := storage.Scale.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc); err != nil { t.Fatalf("error updating scale %v: %v", update, err) } @@ -290,7 +290,7 @@ func TestScaleUpdate(t *testing.T) { update.ResourceVersion = sts.ResourceVersion update.Spec.Replicas = 15 - if _, _, err = storage.Scale.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update)); err != nil && !errors.IsConflict(err) { + if _, _, err = storage.Scale.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc); err != nil && !errors.IsConflict(err) { t.Fatalf("unexpected error, expecting an update conflict but got %v", err) } } diff --git a/pkg/registry/authentication/tokenreview/BUILD b/pkg/registry/authentication/tokenreview/BUILD index 4644d795041..26eba385a93 100644 --- a/pkg/registry/authentication/tokenreview/BUILD +++ b/pkg/registry/authentication/tokenreview/BUILD @@ -15,6 +15,7 @@ go_library( "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apiserver/pkg/authentication/authenticator:go_default_library", "//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library", + "//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library", ], ) diff --git a/pkg/registry/authentication/tokenreview/storage.go b/pkg/registry/authentication/tokenreview/storage.go index f512907e6e5..28c1da7c8d9 100644 --- a/pkg/registry/authentication/tokenreview/storage.go +++ b/pkg/registry/authentication/tokenreview/storage.go @@ -24,6 +24,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apiserver/pkg/authentication/authenticator" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" + "k8s.io/apiserver/pkg/registry/rest" "k8s.io/kubernetes/pkg/apis/authentication" ) @@ -39,7 +40,7 @@ func (r *REST) New() runtime.Object { return &authentication.TokenReview{} } -func (r *REST) Create(ctx genericapirequest.Context, obj runtime.Object, includeUninitialized bool) (runtime.Object, error) { +func (r *REST) Create(ctx genericapirequest.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, includeUninitialized bool) (runtime.Object, error) { tokenReview, ok := obj.(*authentication.TokenReview) if !ok { return nil, apierrors.NewBadRequest(fmt.Sprintf("not a TokenReview: %#v", obj)) diff --git a/pkg/registry/authorization/localsubjectaccessreview/BUILD b/pkg/registry/authorization/localsubjectaccessreview/BUILD index b2ba4af182f..6ed963cd2aa 100644 --- a/pkg/registry/authorization/localsubjectaccessreview/BUILD +++ b/pkg/registry/authorization/localsubjectaccessreview/BUILD @@ -17,6 +17,7 @@ go_library( "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library", "//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library", + "//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library", ], ) diff --git a/pkg/registry/authorization/localsubjectaccessreview/rest.go b/pkg/registry/authorization/localsubjectaccessreview/rest.go index d36f17582e1..53487b310aa 100644 --- a/pkg/registry/authorization/localsubjectaccessreview/rest.go +++ b/pkg/registry/authorization/localsubjectaccessreview/rest.go @@ -23,6 +23,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apiserver/pkg/authorization/authorizer" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" + "k8s.io/apiserver/pkg/registry/rest" authorizationapi "k8s.io/kubernetes/pkg/apis/authorization" authorizationvalidation "k8s.io/kubernetes/pkg/apis/authorization/validation" authorizationutil "k8s.io/kubernetes/pkg/registry/authorization/util" @@ -40,7 +41,7 @@ func (r *REST) New() runtime.Object { return &authorizationapi.LocalSubjectAccessReview{} } -func (r *REST) Create(ctx genericapirequest.Context, obj runtime.Object, includeUninitialized bool) (runtime.Object, error) { +func (r *REST) Create(ctx genericapirequest.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, includeUninitialized bool) (runtime.Object, error) { localSubjectAccessReview, ok := obj.(*authorizationapi.LocalSubjectAccessReview) if !ok { return nil, kapierrors.NewBadRequest(fmt.Sprintf("not a LocaLocalSubjectAccessReview: %#v", obj)) diff --git a/pkg/registry/authorization/selfsubjectaccessreview/BUILD b/pkg/registry/authorization/selfsubjectaccessreview/BUILD index fa7f914485b..06b8845dbc4 100644 --- a/pkg/registry/authorization/selfsubjectaccessreview/BUILD +++ b/pkg/registry/authorization/selfsubjectaccessreview/BUILD @@ -17,6 +17,7 @@ go_library( "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library", "//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library", + "//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library", ], ) diff --git a/pkg/registry/authorization/selfsubjectaccessreview/rest.go b/pkg/registry/authorization/selfsubjectaccessreview/rest.go index 4498b5f5a70..5041dddb7cc 100644 --- a/pkg/registry/authorization/selfsubjectaccessreview/rest.go +++ b/pkg/registry/authorization/selfsubjectaccessreview/rest.go @@ -23,6 +23,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apiserver/pkg/authorization/authorizer" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" + "k8s.io/apiserver/pkg/registry/rest" authorizationapi "k8s.io/kubernetes/pkg/apis/authorization" authorizationvalidation "k8s.io/kubernetes/pkg/apis/authorization/validation" authorizationutil "k8s.io/kubernetes/pkg/registry/authorization/util" @@ -40,7 +41,7 @@ func (r *REST) New() runtime.Object { return &authorizationapi.SelfSubjectAccessReview{} } -func (r *REST) Create(ctx genericapirequest.Context, obj runtime.Object, includeUninitialized bool) (runtime.Object, error) { +func (r *REST) Create(ctx genericapirequest.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, includeUninitialized bool) (runtime.Object, error) { selfSAR, ok := obj.(*authorizationapi.SelfSubjectAccessReview) if !ok { return nil, apierrors.NewBadRequest(fmt.Sprintf("not a SelfSubjectAccessReview: %#v", obj)) diff --git a/pkg/registry/authorization/selfsubjectrulesreview/BUILD b/pkg/registry/authorization/selfsubjectrulesreview/BUILD index 6bc1c631b8a..2afdc2381bf 100644 --- a/pkg/registry/authorization/selfsubjectrulesreview/BUILD +++ b/pkg/registry/authorization/selfsubjectrulesreview/BUILD @@ -11,6 +11,7 @@ go_library( "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library", "//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library", + "//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library", ], ) diff --git a/pkg/registry/authorization/selfsubjectrulesreview/rest.go b/pkg/registry/authorization/selfsubjectrulesreview/rest.go index 062829faba5..2093f324ede 100644 --- a/pkg/registry/authorization/selfsubjectrulesreview/rest.go +++ b/pkg/registry/authorization/selfsubjectrulesreview/rest.go @@ -23,6 +23,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apiserver/pkg/authorization/authorizer" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" + "k8s.io/apiserver/pkg/registry/rest" authorizationapi "k8s.io/kubernetes/pkg/apis/authorization" ) @@ -42,7 +43,7 @@ func (r *REST) New() runtime.Object { } // Create attempts to get self subject rules in specific namespace. -func (r *REST) Create(ctx genericapirequest.Context, obj runtime.Object, includeUninitialized bool) (runtime.Object, error) { +func (r *REST) Create(ctx genericapirequest.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, includeUninitialized bool) (runtime.Object, error) { selfSRR, ok := obj.(*authorizationapi.SelfSubjectRulesReview) if !ok { return nil, apierrors.NewBadRequest(fmt.Sprintf("not a SelfSubjectRulesReview: %#v", obj)) diff --git a/pkg/registry/authorization/subjectaccessreview/BUILD b/pkg/registry/authorization/subjectaccessreview/BUILD index ebc67716823..fb4474b4398 100644 --- a/pkg/registry/authorization/subjectaccessreview/BUILD +++ b/pkg/registry/authorization/subjectaccessreview/BUILD @@ -18,6 +18,7 @@ go_library( "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library", "//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library", + "//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library", ], ) @@ -44,5 +45,6 @@ go_test( "//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library", "//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library", "//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library", + "//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library", ], ) diff --git a/pkg/registry/authorization/subjectaccessreview/rest.go b/pkg/registry/authorization/subjectaccessreview/rest.go index 1583a32ac04..1252a41ca48 100644 --- a/pkg/registry/authorization/subjectaccessreview/rest.go +++ b/pkg/registry/authorization/subjectaccessreview/rest.go @@ -23,6 +23,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apiserver/pkg/authorization/authorizer" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" + "k8s.io/apiserver/pkg/registry/rest" authorizationapi "k8s.io/kubernetes/pkg/apis/authorization" authorizationvalidation "k8s.io/kubernetes/pkg/apis/authorization/validation" authorizationutil "k8s.io/kubernetes/pkg/registry/authorization/util" @@ -40,7 +41,7 @@ func (r *REST) New() runtime.Object { return &authorizationapi.SubjectAccessReview{} } -func (r *REST) Create(ctx genericapirequest.Context, obj runtime.Object, includeUninitialized bool) (runtime.Object, error) { +func (r *REST) Create(ctx genericapirequest.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, includeUninitialized bool) (runtime.Object, error) { subjectAccessReview, ok := obj.(*authorizationapi.SubjectAccessReview) if !ok { return nil, kapierrors.NewBadRequest(fmt.Sprintf("not a SubjectAccessReview: %#v", obj)) diff --git a/pkg/registry/authorization/subjectaccessreview/rest_test.go b/pkg/registry/authorization/subjectaccessreview/rest_test.go index b278538249b..697441f42fd 100644 --- a/pkg/registry/authorization/subjectaccessreview/rest_test.go +++ b/pkg/registry/authorization/subjectaccessreview/rest_test.go @@ -26,6 +26,7 @@ import ( "k8s.io/apiserver/pkg/authentication/user" "k8s.io/apiserver/pkg/authorization/authorizer" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" + "k8s.io/apiserver/pkg/registry/rest" authorizationapi "k8s.io/kubernetes/pkg/apis/authorization" ) @@ -174,9 +175,9 @@ func TestCreate(t *testing.T) { reason: tc.reason, err: tc.err, } - rest := NewREST(auth) + storage := NewREST(auth) - result, err := rest.Create(genericapirequest.NewContext(), &authorizationapi.SubjectAccessReview{Spec: tc.spec}, false) + result, err := storage.Create(genericapirequest.NewContext(), &authorizationapi.SubjectAccessReview{Spec: tc.spec}, rest.ValidateAllObjectFunc, false) if err != nil { if tc.expectedErr != "" { if !strings.Contains(err.Error(), tc.expectedErr) { diff --git a/pkg/registry/autoscaling/horizontalpodautoscaler/storage/storage.go b/pkg/registry/autoscaling/horizontalpodautoscaler/storage/storage.go index 2988ebb4ef5..5d6602f5dd0 100644 --- a/pkg/registry/autoscaling/horizontalpodautoscaler/storage/storage.go +++ b/pkg/registry/autoscaling/horizontalpodautoscaler/storage/storage.go @@ -83,6 +83,6 @@ func (r *StatusREST) Get(ctx genericapirequest.Context, name string, options *me } // Update alters the status subset of an object. -func (r *StatusREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { - return r.store.Update(ctx, name, objInfo) +func (r *StatusREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (runtime.Object, bool, error) { + return r.store.Update(ctx, name, objInfo, createValidation, updateValidation) } diff --git a/pkg/registry/batch/cronjob/storage/storage.go b/pkg/registry/batch/cronjob/storage/storage.go index 407a3ef40cb..d8ae47f3890 100644 --- a/pkg/registry/batch/cronjob/storage/storage.go +++ b/pkg/registry/batch/cronjob/storage/storage.go @@ -81,6 +81,6 @@ func (r *StatusREST) Get(ctx genericapirequest.Context, name string, options *me } // Update alters the status subset of an object. -func (r *StatusREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { - return r.store.Update(ctx, name, objInfo) +func (r *StatusREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (runtime.Object, bool, error) { + return r.store.Update(ctx, name, objInfo, createValidation, updateValidation) } diff --git a/pkg/registry/batch/job/storage/storage.go b/pkg/registry/batch/job/storage/storage.go index 4a0ed987417..580cc5a7531 100644 --- a/pkg/registry/batch/job/storage/storage.go +++ b/pkg/registry/batch/job/storage/storage.go @@ -98,6 +98,6 @@ func (r *StatusREST) Get(ctx genericapirequest.Context, name string, options *me } // Update alters the status subset of an object. -func (r *StatusREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { - return r.store.Update(ctx, name, objInfo) +func (r *StatusREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (runtime.Object, bool, error) { + return r.store.Update(ctx, name, objInfo, createValidation, updateValidation) } diff --git a/pkg/registry/certificates/certificates/registry.go b/pkg/registry/certificates/certificates/registry.go index 4dfd9611452..d3eea018bce 100644 --- a/pkg/registry/certificates/certificates/registry.go +++ b/pkg/registry/certificates/certificates/registry.go @@ -28,8 +28,8 @@ import ( // Registry is an interface for things that know how to store CSRs. type Registry interface { ListCSRs(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (*certificates.CertificateSigningRequestList, error) - CreateCSR(ctx genericapirequest.Context, csr *certificates.CertificateSigningRequest) error - UpdateCSR(ctx genericapirequest.Context, csr *certificates.CertificateSigningRequest) error + CreateCSR(ctx genericapirequest.Context, csr *certificates.CertificateSigningRequest, createValidation rest.ValidateObjectFunc) error + UpdateCSR(ctx genericapirequest.Context, csr *certificates.CertificateSigningRequest, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) error GetCSR(ctx genericapirequest.Context, csrID string, options *metav1.GetOptions) (*certificates.CertificateSigningRequest, error) DeleteCSR(ctx genericapirequest.Context, csrID string) error WatchCSRs(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (watch.Interface, error) @@ -55,13 +55,13 @@ func (s *storage) ListCSRs(ctx genericapirequest.Context, options *metainternalv return obj.(*certificates.CertificateSigningRequestList), nil } -func (s *storage) CreateCSR(ctx genericapirequest.Context, csr *certificates.CertificateSigningRequest) error { - _, err := s.Create(ctx, csr, false) +func (s *storage) CreateCSR(ctx genericapirequest.Context, csr *certificates.CertificateSigningRequest, createValidation rest.ValidateObjectFunc) error { + _, err := s.Create(ctx, csr, createValidation, false) return err } -func (s *storage) UpdateCSR(ctx genericapirequest.Context, csr *certificates.CertificateSigningRequest) error { - _, _, err := s.Update(ctx, csr.Name, rest.DefaultUpdatedObjectInfo(csr)) +func (s *storage) UpdateCSR(ctx genericapirequest.Context, csr *certificates.CertificateSigningRequest, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) error { + _, _, err := s.Update(ctx, csr.Name, rest.DefaultUpdatedObjectInfo(csr), createValidation, updateValidation) return err } diff --git a/pkg/registry/certificates/certificates/storage/storage.go b/pkg/registry/certificates/certificates/storage/storage.go index 52bf2dbb791..4f69ac91189 100644 --- a/pkg/registry/certificates/certificates/storage/storage.go +++ b/pkg/registry/certificates/certificates/storage/storage.go @@ -78,8 +78,8 @@ func (r *StatusREST) New() runtime.Object { } // Update alters the status subset of an object. -func (r *StatusREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { - return r.store.Update(ctx, name, objInfo) +func (r *StatusREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (runtime.Object, bool, error) { + return r.store.Update(ctx, name, objInfo, createValidation, updateValidation) } // ApprovalREST implements the REST endpoint for changing the approval state of a CSR. @@ -92,6 +92,6 @@ func (r *ApprovalREST) New() runtime.Object { } // Update alters the approval subset of an object. -func (r *ApprovalREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { - return r.store.Update(ctx, name, objInfo) +func (r *ApprovalREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (runtime.Object, bool, error) { + return r.store.Update(ctx, name, objInfo, createValidation, updateValidation) } diff --git a/pkg/registry/core/configmap/registry.go b/pkg/registry/core/configmap/registry.go index b26e6141e18..0006f870953 100644 --- a/pkg/registry/core/configmap/registry.go +++ b/pkg/registry/core/configmap/registry.go @@ -30,8 +30,8 @@ type Registry interface { ListConfigMaps(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (*api.ConfigMapList, error) WatchConfigMaps(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (watch.Interface, error) GetConfigMap(ctx genericapirequest.Context, name string, options *metav1.GetOptions) (*api.ConfigMap, error) - CreateConfigMap(ctx genericapirequest.Context, cfg *api.ConfigMap) (*api.ConfigMap, error) - UpdateConfigMap(ctx genericapirequest.Context, cfg *api.ConfigMap) (*api.ConfigMap, error) + CreateConfigMap(ctx genericapirequest.Context, cfg *api.ConfigMap, createValidation rest.ValidateObjectFunc) (*api.ConfigMap, error) + UpdateConfigMap(ctx genericapirequest.Context, cfg *api.ConfigMap, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (*api.ConfigMap, error) DeleteConfigMap(ctx genericapirequest.Context, name string) error } @@ -68,8 +68,8 @@ func (s *storage) GetConfigMap(ctx genericapirequest.Context, name string, optio return obj.(*api.ConfigMap), nil } -func (s *storage) CreateConfigMap(ctx genericapirequest.Context, cfg *api.ConfigMap) (*api.ConfigMap, error) { - obj, err := s.Create(ctx, cfg, false) +func (s *storage) CreateConfigMap(ctx genericapirequest.Context, cfg *api.ConfigMap, createValidation rest.ValidateObjectFunc) (*api.ConfigMap, error) { + obj, err := s.Create(ctx, cfg, createValidation, false) if err != nil { return nil, err } @@ -77,8 +77,8 @@ func (s *storage) CreateConfigMap(ctx genericapirequest.Context, cfg *api.Config return obj.(*api.ConfigMap), nil } -func (s *storage) UpdateConfigMap(ctx genericapirequest.Context, cfg *api.ConfigMap) (*api.ConfigMap, error) { - obj, _, err := s.Update(ctx, cfg.Name, rest.DefaultUpdatedObjectInfo(cfg)) +func (s *storage) UpdateConfigMap(ctx genericapirequest.Context, cfg *api.ConfigMap, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (*api.ConfigMap, error) { + obj, _, err := s.Update(ctx, cfg.Name, rest.DefaultUpdatedObjectInfo(cfg), createValidation, updateValidation) if err != nil { return nil, err } diff --git a/pkg/registry/core/endpoint/registry.go b/pkg/registry/core/endpoint/registry.go index 1202cec9c16..cc2f3e44484 100644 --- a/pkg/registry/core/endpoint/registry.go +++ b/pkg/registry/core/endpoint/registry.go @@ -30,7 +30,7 @@ type Registry interface { ListEndpoints(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (*api.EndpointsList, error) GetEndpoints(ctx genericapirequest.Context, name string, options *metav1.GetOptions) (*api.Endpoints, error) WatchEndpoints(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (watch.Interface, error) - UpdateEndpoints(ctx genericapirequest.Context, e *api.Endpoints) error + UpdateEndpoints(ctx genericapirequest.Context, e *api.Endpoints, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) error DeleteEndpoints(ctx genericapirequest.Context, name string) error } @@ -65,8 +65,8 @@ func (s *storage) GetEndpoints(ctx genericapirequest.Context, name string, optio return obj.(*api.Endpoints), nil } -func (s *storage) UpdateEndpoints(ctx genericapirequest.Context, endpoints *api.Endpoints) error { - _, _, err := s.Update(ctx, endpoints.Name, rest.DefaultUpdatedObjectInfo(endpoints)) +func (s *storage) UpdateEndpoints(ctx genericapirequest.Context, endpoints *api.Endpoints, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) error { + _, _, err := s.Update(ctx, endpoints.Name, rest.DefaultUpdatedObjectInfo(endpoints), createValidation, updateValidation) return err } diff --git a/pkg/registry/core/namespace/registry.go b/pkg/registry/core/namespace/registry.go index 78bb723216a..9cc0957216b 100644 --- a/pkg/registry/core/namespace/registry.go +++ b/pkg/registry/core/namespace/registry.go @@ -30,8 +30,8 @@ type Registry interface { ListNamespaces(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (*api.NamespaceList, error) WatchNamespaces(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (watch.Interface, error) GetNamespace(ctx genericapirequest.Context, namespaceID string, options *metav1.GetOptions) (*api.Namespace, error) - CreateNamespace(ctx genericapirequest.Context, namespace *api.Namespace) error - UpdateNamespace(ctx genericapirequest.Context, namespace *api.Namespace) error + CreateNamespace(ctx genericapirequest.Context, namespace *api.Namespace, createValidation rest.ValidateObjectFunc) error + UpdateNamespace(ctx genericapirequest.Context, namespace *api.Namespace, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) error DeleteNamespace(ctx genericapirequest.Context, namespaceID string) error } @@ -66,13 +66,13 @@ func (s *storage) GetNamespace(ctx genericapirequest.Context, namespaceName stri return obj.(*api.Namespace), nil } -func (s *storage) CreateNamespace(ctx genericapirequest.Context, namespace *api.Namespace) error { - _, err := s.Create(ctx, namespace, false) +func (s *storage) CreateNamespace(ctx genericapirequest.Context, namespace *api.Namespace, createValidation rest.ValidateObjectFunc) error { + _, err := s.Create(ctx, namespace, createValidation, false) return err } -func (s *storage) UpdateNamespace(ctx genericapirequest.Context, namespace *api.Namespace) error { - _, _, err := s.Update(ctx, namespace.Name, rest.DefaultUpdatedObjectInfo(namespace)) +func (s *storage) UpdateNamespace(ctx genericapirequest.Context, namespace *api.Namespace, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) error { + _, _, err := s.Update(ctx, namespace.Name, rest.DefaultUpdatedObjectInfo(namespace), createValidation, updateValidation) return err } diff --git a/pkg/registry/core/namespace/storage/BUILD b/pkg/registry/core/namespace/storage/BUILD index b21c6fa531c..16fcc0e35c7 100644 --- a/pkg/registry/core/namespace/storage/BUILD +++ b/pkg/registry/core/namespace/storage/BUILD @@ -19,6 +19,7 @@ go_test( "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library", "//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library", + "//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library", "//vendor/k8s.io/apiserver/pkg/storage/etcd/testing:go_default_library", ], ) diff --git a/pkg/registry/core/namespace/storage/storage.go b/pkg/registry/core/namespace/storage/storage.go index 05c6e180151..b457f9959d8 100644 --- a/pkg/registry/core/namespace/storage/storage.go +++ b/pkg/registry/core/namespace/storage/storage.go @@ -89,12 +89,12 @@ func (r *REST) List(ctx genericapirequest.Context, options *metainternalversion. return r.store.List(ctx, options) } -func (r *REST) Create(ctx genericapirequest.Context, obj runtime.Object, includeUninitialized bool) (runtime.Object, error) { - return r.store.Create(ctx, obj, includeUninitialized) +func (r *REST) Create(ctx genericapirequest.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, includeUninitialized bool) (runtime.Object, error) { + return r.store.Create(ctx, obj, createValidation, includeUninitialized) } -func (r *REST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { - return r.store.Update(ctx, name, objInfo) +func (r *REST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (runtime.Object, bool, error) { + return r.store.Update(ctx, name, objInfo, createValidation, updateValidation) } func (r *REST) Get(ctx genericapirequest.Context, name string, options *metav1.GetOptions) (runtime.Object, error) { @@ -219,8 +219,8 @@ func (r *StatusREST) Get(ctx genericapirequest.Context, name string, options *me } // Update alters the status subset of an object. -func (r *StatusREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { - return r.store.Update(ctx, name, objInfo) +func (r *StatusREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (runtime.Object, bool, error) { + return r.store.Update(ctx, name, objInfo, createValidation, updateValidation) } func (r *FinalizeREST) New() runtime.Object { @@ -228,6 +228,6 @@ func (r *FinalizeREST) New() runtime.Object { } // Update alters the status finalizers subset of an object. -func (r *FinalizeREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { - return r.store.Update(ctx, name, objInfo) +func (r *FinalizeREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (runtime.Object, bool, error) { + return r.store.Update(ctx, name, objInfo, createValidation, updateValidation) } diff --git a/pkg/registry/core/namespace/storage/storage_test.go b/pkg/registry/core/namespace/storage/storage_test.go index fdc57efe2b8..26183d3b4a2 100644 --- a/pkg/registry/core/namespace/storage/storage_test.go +++ b/pkg/registry/core/namespace/storage/storage_test.go @@ -24,6 +24,7 @@ import ( "k8s.io/apimachinery/pkg/labels" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" "k8s.io/apiserver/pkg/registry/generic" + "k8s.io/apiserver/pkg/registry/rest" etcdtesting "k8s.io/apiserver/pkg/storage/etcd/testing" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/registry/registrytest" @@ -67,7 +68,7 @@ func TestCreateSetsFields(t *testing.T) { defer storage.store.DestroyFunc() namespace := validNewNamespace() ctx := genericapirequest.NewContext() - _, err := storage.Create(ctx, namespace, false) + _, err := storage.Create(ctx, namespace, rest.ValidateAllObjectFunc, false) if err != nil { t.Fatalf("unexpected error: %v", err) } diff --git a/pkg/registry/core/node/registry.go b/pkg/registry/core/node/registry.go index 165cb6476c5..4640af58af8 100644 --- a/pkg/registry/core/node/registry.go +++ b/pkg/registry/core/node/registry.go @@ -28,8 +28,8 @@ import ( // Registry is an interface for things that know how to store node. type Registry interface { ListNodes(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (*api.NodeList, error) - CreateNode(ctx genericapirequest.Context, node *api.Node) error - UpdateNode(ctx genericapirequest.Context, node *api.Node) error + CreateNode(ctx genericapirequest.Context, node *api.Node, createValidation rest.ValidateObjectFunc) error + UpdateNode(ctx genericapirequest.Context, node *api.Node, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) error GetNode(ctx genericapirequest.Context, nodeID string, options *metav1.GetOptions) (*api.Node, error) DeleteNode(ctx genericapirequest.Context, nodeID string) error WatchNodes(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (watch.Interface, error) @@ -55,13 +55,13 @@ func (s *storage) ListNodes(ctx genericapirequest.Context, options *metainternal return obj.(*api.NodeList), nil } -func (s *storage) CreateNode(ctx genericapirequest.Context, node *api.Node) error { - _, err := s.Create(ctx, node, false) +func (s *storage) CreateNode(ctx genericapirequest.Context, node *api.Node, createValidation rest.ValidateObjectFunc) error { + _, err := s.Create(ctx, node, createValidation, false) return err } -func (s *storage) UpdateNode(ctx genericapirequest.Context, node *api.Node) error { - _, _, err := s.Update(ctx, node.Name, rest.DefaultUpdatedObjectInfo(node)) +func (s *storage) UpdateNode(ctx genericapirequest.Context, node *api.Node, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) error { + _, _, err := s.Update(ctx, node.Name, rest.DefaultUpdatedObjectInfo(node), createValidation, updateValidation) return err } diff --git a/pkg/registry/core/node/storage/storage.go b/pkg/registry/core/node/storage/storage.go index fcf5d237a61..ad47a8f2a09 100644 --- a/pkg/registry/core/node/storage/storage.go +++ b/pkg/registry/core/node/storage/storage.go @@ -68,8 +68,8 @@ func (r *StatusREST) Get(ctx genericapirequest.Context, name string, options *me } // Update alters the status subset of an object. -func (r *StatusREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { - return r.store.Update(ctx, name, objInfo) +func (r *StatusREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (runtime.Object, bool, error) { + return r.store.Update(ctx, name, objInfo, createValidation, updateValidation) } // NewStorage returns a NodeStorage object that will work against nodes. diff --git a/pkg/registry/core/persistentvolume/storage/storage.go b/pkg/registry/core/persistentvolume/storage/storage.go index 84fd2577910..7eb5dd44526 100644 --- a/pkg/registry/core/persistentvolume/storage/storage.go +++ b/pkg/registry/core/persistentvolume/storage/storage.go @@ -78,6 +78,6 @@ func (r *StatusREST) Get(ctx genericapirequest.Context, name string, options *me } // Update alters the status subset of an object. -func (r *StatusREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { - return r.store.Update(ctx, name, objInfo) +func (r *StatusREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (runtime.Object, bool, error) { + return r.store.Update(ctx, name, objInfo, createValidation, updateValidation) } diff --git a/pkg/registry/core/persistentvolume/storage/storage_test.go b/pkg/registry/core/persistentvolume/storage/storage_test.go index bc98bbe09ba..215f633d9e5 100644 --- a/pkg/registry/core/persistentvolume/storage/storage_test.go +++ b/pkg/registry/core/persistentvolume/storage/storage_test.go @@ -187,7 +187,7 @@ func TestUpdateStatus(t *testing.T) { }, } - _, _, err = statusStorage.Update(ctx, pvIn.Name, rest.DefaultUpdatedObjectInfo(pvIn)) + _, _, err = statusStorage.Update(ctx, pvIn.Name, rest.DefaultUpdatedObjectInfo(pvIn), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc) if err != nil { t.Fatalf("Unexpected error: %v", err) } diff --git a/pkg/registry/core/persistentvolumeclaim/storage/storage.go b/pkg/registry/core/persistentvolumeclaim/storage/storage.go index 544ac1cf694..f27a624f834 100644 --- a/pkg/registry/core/persistentvolumeclaim/storage/storage.go +++ b/pkg/registry/core/persistentvolumeclaim/storage/storage.go @@ -78,6 +78,6 @@ func (r *StatusREST) Get(ctx genericapirequest.Context, name string, options *me } // Update alters the status subset of an object. -func (r *StatusREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { - return r.store.Update(ctx, name, objInfo) +func (r *StatusREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (runtime.Object, bool, error) { + return r.store.Update(ctx, name, objInfo, createValidation, updateValidation) } diff --git a/pkg/registry/core/persistentvolumeclaim/storage/storage_test.go b/pkg/registry/core/persistentvolumeclaim/storage/storage_test.go index 916042829f4..5ac282eaa69 100644 --- a/pkg/registry/core/persistentvolumeclaim/storage/storage_test.go +++ b/pkg/registry/core/persistentvolumeclaim/storage/storage_test.go @@ -178,7 +178,7 @@ func TestUpdateStatus(t *testing.T) { }, } - _, _, err = statusStorage.Update(ctx, pvc.Name, rest.DefaultUpdatedObjectInfo(pvc)) + _, _, err = statusStorage.Update(ctx, pvc.Name, rest.DefaultUpdatedObjectInfo(pvc), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc) if err != nil { t.Fatalf("Unexpected error: %v", err) } diff --git a/pkg/registry/core/pod/storage/eviction.go b/pkg/registry/core/pod/storage/eviction.go index 405d1b8666e..d7d9e152d15 100644 --- a/pkg/registry/core/pod/storage/eviction.go +++ b/pkg/registry/core/pod/storage/eviction.go @@ -78,7 +78,7 @@ func (r *EvictionREST) New() runtime.Object { } // Create attempts to create a new eviction. That is, it tries to evict a pod. -func (r *EvictionREST) Create(ctx genericapirequest.Context, obj runtime.Object, includeUninitialized bool) (runtime.Object, error) { +func (r *EvictionREST) Create(ctx genericapirequest.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, includeUninitialized bool) (runtime.Object, error) { eviction := obj.(*policy.Eviction) obj, err := r.store.Get(ctx, eviction.Name, &metav1.GetOptions{}) diff --git a/pkg/registry/core/pod/storage/storage.go b/pkg/registry/core/pod/storage/storage.go index 1416e50b3ec..7a71ba4b76f 100644 --- a/pkg/registry/core/pod/storage/storage.go +++ b/pkg/registry/core/pod/storage/storage.go @@ -135,7 +135,7 @@ func (r *BindingREST) New() runtime.Object { var _ = rest.Creater(&BindingREST{}) // Create ensures a pod is bound to a specific host. -func (r *BindingREST) Create(ctx genericapirequest.Context, obj runtime.Object, includeUninitialized bool) (out runtime.Object, err error) { +func (r *BindingREST) Create(ctx genericapirequest.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, includeUninitialized bool) (out runtime.Object, err error) { binding := obj.(*api.Binding) // TODO: move me to a binding strategy @@ -212,6 +212,6 @@ func (r *StatusREST) Get(ctx genericapirequest.Context, name string, options *me } // Update alters the status subset of an object. -func (r *StatusREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { - return r.store.Update(ctx, name, objInfo) +func (r *StatusREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (runtime.Object, bool, error) { + return r.store.Update(ctx, name, objInfo, createValidation, updateValidation) } diff --git a/pkg/registry/core/pod/storage/storage_test.go b/pkg/registry/core/pod/storage/storage_test.go index e76275c3191..1a98bc7d38b 100644 --- a/pkg/registry/core/pod/storage/storage_test.go +++ b/pkg/registry/core/pod/storage/storage_test.go @@ -188,7 +188,7 @@ func TestIgnoreDeleteNotFound(t *testing.T) { } // create pod - _, err = registry.Create(testContext, pod, false) + _, err = registry.Create(testContext, pod, rest.ValidateAllObjectFunc, false) if err != nil { t.Errorf("Unexpected error: %v", err) } @@ -225,7 +225,7 @@ func TestCreateSetsFields(t *testing.T) { defer server.Terminate(t) defer storage.Store.DestroyFunc() pod := validNewPod() - _, err := storage.Create(genericapirequest.NewDefaultContext(), pod, false) + _, err := storage.Create(genericapirequest.NewDefaultContext(), pod, rest.ValidateAllObjectFunc, false) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -489,7 +489,7 @@ func TestEtcdCreate(t *testing.T) { defer server.Terminate(t) defer storage.Store.DestroyFunc() ctx := genericapirequest.NewDefaultContext() - _, err := storage.Create(ctx, validNewPod(), false) + _, err := storage.Create(ctx, validNewPod(), rest.ValidateAllObjectFunc, false) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -498,7 +498,7 @@ func TestEtcdCreate(t *testing.T) { _, err = bindingStorage.Create(ctx, &api.Binding{ ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceDefault, Name: "foo"}, Target: api.ObjectReference{Name: "machine"}, - }, false) + }, rest.ValidateAllObjectFunc, false) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -524,7 +524,7 @@ func TestEtcdCreateBindingNoPod(t *testing.T) { _, err := bindingStorage.Create(ctx, &api.Binding{ ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceDefault, Name: "foo"}, Target: api.ObjectReference{Name: "machine"}, - }, false) + }, rest.ValidateAllObjectFunc, false) if err == nil { t.Fatalf("Expected not-found-error but got nothing") } @@ -547,7 +547,7 @@ func TestEtcdCreateFailsWithoutNamespace(t *testing.T) { defer storage.Store.DestroyFunc() pod := validNewPod() pod.Namespace = "" - _, err := storage.Create(genericapirequest.NewContext(), pod, false) + _, err := storage.Create(genericapirequest.NewContext(), pod, rest.ValidateAllObjectFunc, false) // Accept "namespace" or "Namespace". if err == nil || !strings.Contains(err.Error(), "amespace") { t.Fatalf("expected error that namespace was missing from context, got: %v", err) @@ -559,7 +559,7 @@ func TestEtcdCreateWithContainersNotFound(t *testing.T) { defer server.Terminate(t) defer storage.Store.DestroyFunc() ctx := genericapirequest.NewDefaultContext() - _, err := storage.Create(ctx, validNewPod(), false) + _, err := storage.Create(ctx, validNewPod(), rest.ValidateAllObjectFunc, false) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -572,7 +572,7 @@ func TestEtcdCreateWithContainersNotFound(t *testing.T) { Annotations: map[string]string{"label1": "value1"}, }, Target: api.ObjectReference{Name: "machine"}, - }, false) + }, rest.ValidateAllObjectFunc, false) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -594,7 +594,7 @@ func TestEtcdCreateWithConflict(t *testing.T) { defer storage.Store.DestroyFunc() ctx := genericapirequest.NewDefaultContext() - _, err := storage.Create(ctx, validNewPod(), false) + _, err := storage.Create(ctx, validNewPod(), rest.ValidateAllObjectFunc, false) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -608,12 +608,12 @@ func TestEtcdCreateWithConflict(t *testing.T) { }, Target: api.ObjectReference{Name: "machine"}, } - _, err = bindingStorage.Create(ctx, &binding, false) + _, err = bindingStorage.Create(ctx, &binding, rest.ValidateAllObjectFunc, false) if err != nil { t.Fatalf("unexpected error: %v", err) } - _, err = bindingStorage.Create(ctx, &binding, false) + _, err = bindingStorage.Create(ctx, &binding, rest.ValidateAllObjectFunc, false) if err == nil || !errors.IsConflict(err) { t.Fatalf("expected resource conflict error, not: %v", err) } @@ -624,7 +624,7 @@ func TestEtcdCreateWithExistingContainers(t *testing.T) { defer server.Terminate(t) defer storage.Store.DestroyFunc() ctx := genericapirequest.NewDefaultContext() - _, err := storage.Create(ctx, validNewPod(), false) + _, err := storage.Create(ctx, validNewPod(), rest.ValidateAllObjectFunc, false) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -633,7 +633,7 @@ func TestEtcdCreateWithExistingContainers(t *testing.T) { _, err = bindingStorage.Create(ctx, &api.Binding{ ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceDefault, Name: "foo"}, Target: api.ObjectReference{Name: "machine"}, - }, false) + }, rest.ValidateAllObjectFunc, false) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -683,10 +683,10 @@ func TestEtcdCreateBinding(t *testing.T) { for k, test := range testCases { storage, bindingStorage, _, server := newStorage(t) - if _, err := storage.Create(ctx, validNewPod(), false); err != nil { + if _, err := storage.Create(ctx, validNewPod(), rest.ValidateAllObjectFunc, false); err != nil { t.Fatalf("%s: unexpected error: %v", k, err) } - if _, err := bindingStorage.Create(ctx, &test.binding, false); !test.errOK(err) { + if _, err := bindingStorage.Create(ctx, &test.binding, rest.ValidateAllObjectFunc, false); !test.errOK(err) { t.Errorf("%s: unexpected error: %v", k, err) } else if err == nil { // If bind succeeded, verify Host field in pod's Spec. @@ -712,7 +712,7 @@ func TestEtcdUpdateUninitialized(t *testing.T) { pod := validNewPod() // add pending initializers to the pod pod.ObjectMeta.Initializers = &metav1.Initializers{Pending: []metav1.Initializer{{Name: "init.k8s.io"}}} - if _, err := storage.Create(ctx, pod, true); err != nil { + if _, err := storage.Create(ctx, pod, rest.ValidateAllObjectFunc, true); err != nil { t.Fatalf("unexpected error: %v", err) } podIn := *pod @@ -727,7 +727,7 @@ func TestEtcdUpdateUninitialized(t *testing.T) { }) podIn.ObjectMeta.Initializers = nil - _, _, err := storage.Update(ctx, podIn.Name, rest.DefaultUpdatedObjectInfo(&podIn)) + _, _, err := storage.Update(ctx, podIn.Name, rest.DefaultUpdatedObjectInfo(&podIn), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc) if err != nil { t.Errorf("Unexpected error: %v", err) } @@ -754,7 +754,7 @@ func TestEtcdStatusUpdateUninitialized(t *testing.T) { pod := validNewPod() // add pending initializers to the pod pod.ObjectMeta.Initializers = &metav1.Initializers{Pending: []metav1.Initializer{{Name: "init.k8s.io"}}} - if _, err := storage.Create(ctx, pod, true); err != nil { + if _, err := storage.Create(ctx, pod, rest.ValidateAllObjectFunc, true); err != nil { t.Fatalf("unexpected error: %v", err) } podIn := *pod @@ -762,7 +762,7 @@ func TestEtcdStatusUpdateUninitialized(t *testing.T) { podIn.Status.Phase = api.PodRunning podIn.ObjectMeta.Initializers = nil - _, _, err := statusStorage.Update(ctx, podIn.Name, rest.DefaultUpdatedObjectInfo(&podIn)) + _, _, err := statusStorage.Update(ctx, podIn.Name, rest.DefaultUpdatedObjectInfo(&podIn), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc) expected := "Forbidden: must not update status when the object is uninitialized" if err == nil { t.Fatalf("Unexpected no err, expected %q", expected) @@ -778,12 +778,12 @@ func TestEtcdUpdateNotScheduled(t *testing.T) { defer storage.Store.DestroyFunc() ctx := genericapirequest.NewDefaultContext() - if _, err := storage.Create(ctx, validNewPod(), false); err != nil { + if _, err := storage.Create(ctx, validNewPod(), rest.ValidateAllObjectFunc, false); err != nil { t.Fatalf("unexpected error: %v", err) } podIn := validChangedPod() - _, _, err := storage.Update(ctx, podIn.Name, rest.DefaultUpdatedObjectInfo(podIn)) + _, _, err := storage.Update(ctx, podIn.Name, rest.DefaultUpdatedObjectInfo(podIn), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc) if err != nil { t.Errorf("Unexpected error: %v", err) } @@ -853,7 +853,7 @@ func TestEtcdUpdateScheduled(t *testing.T) { SchedulerName: api.DefaultSchedulerName, }, } - _, _, err = storage.Update(ctx, podIn.Name, rest.DefaultUpdatedObjectInfo(&podIn)) + _, _, err = storage.Update(ctx, podIn.Name, rest.DefaultUpdatedObjectInfo(&podIn), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc) if err != nil { t.Errorf("Unexpected error: %v", err) } @@ -937,7 +937,7 @@ func TestEtcdUpdateStatus(t *testing.T) { expected.Labels = podIn.Labels expected.Status = podIn.Status - _, _, err = statusStorage.Update(ctx, podIn.Name, rest.DefaultUpdatedObjectInfo(&podIn)) + _, _, err = statusStorage.Update(ctx, podIn.Name, rest.DefaultUpdatedObjectInfo(&podIn), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc) if err != nil { t.Fatalf("Unexpected error: %v", err) } diff --git a/pkg/registry/core/replicationcontroller/registry.go b/pkg/registry/core/replicationcontroller/registry.go index 41a438c4e8b..90f6c791f71 100644 --- a/pkg/registry/core/replicationcontroller/registry.go +++ b/pkg/registry/core/replicationcontroller/registry.go @@ -34,8 +34,8 @@ type Registry interface { ListControllers(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (*api.ReplicationControllerList, error) WatchControllers(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (watch.Interface, error) GetController(ctx genericapirequest.Context, controllerID string, options *metav1.GetOptions) (*api.ReplicationController, error) - CreateController(ctx genericapirequest.Context, controller *api.ReplicationController) (*api.ReplicationController, error) - UpdateController(ctx genericapirequest.Context, controller *api.ReplicationController) (*api.ReplicationController, error) + CreateController(ctx genericapirequest.Context, controller *api.ReplicationController, createValidation rest.ValidateObjectFunc) (*api.ReplicationController, error) + UpdateController(ctx genericapirequest.Context, controller *api.ReplicationController, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (*api.ReplicationController, error) DeleteController(ctx genericapirequest.Context, controllerID string) error } @@ -73,16 +73,16 @@ func (s *storage) GetController(ctx genericapirequest.Context, controllerID stri return obj.(*api.ReplicationController), nil } -func (s *storage) CreateController(ctx genericapirequest.Context, controller *api.ReplicationController) (*api.ReplicationController, error) { - obj, err := s.Create(ctx, controller, false) +func (s *storage) CreateController(ctx genericapirequest.Context, controller *api.ReplicationController, createValidation rest.ValidateObjectFunc) (*api.ReplicationController, error) { + obj, err := s.Create(ctx, controller, createValidation, false) if err != nil { return nil, err } return obj.(*api.ReplicationController), nil } -func (s *storage) UpdateController(ctx genericapirequest.Context, controller *api.ReplicationController) (*api.ReplicationController, error) { - obj, _, err := s.Update(ctx, controller.Name, rest.DefaultUpdatedObjectInfo(controller)) +func (s *storage) UpdateController(ctx genericapirequest.Context, controller *api.ReplicationController, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (*api.ReplicationController, error) { + obj, _, err := s.Update(ctx, controller.Name, rest.DefaultUpdatedObjectInfo(controller), createValidation, updateValidation) if err != nil { return nil, err } diff --git a/pkg/registry/core/replicationcontroller/storage/storage.go b/pkg/registry/core/replicationcontroller/storage/storage.go index 686b9448755..f5a57af80b4 100644 --- a/pkg/registry/core/replicationcontroller/storage/storage.go +++ b/pkg/registry/core/replicationcontroller/storage/storage.go @@ -119,8 +119,8 @@ func (r *StatusREST) Get(ctx genericapirequest.Context, name string, options *me } // Update alters the status subset of an object. -func (r *StatusREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { - return r.store.Update(ctx, name, objInfo) +func (r *StatusREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (runtime.Object, bool, error) { + return r.store.Update(ctx, name, objInfo, createValidation, updateValidation) } type ScaleREST struct { @@ -153,13 +153,14 @@ func (r *ScaleREST) Get(ctx genericapirequest.Context, name string, options *met return scaleFromRC(rc), nil } -func (r *ScaleREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { +func (r *ScaleREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (runtime.Object, bool, error) { rc, err := r.registry.GetController(ctx, name, &metav1.GetOptions{}) if err != nil { return nil, false, errors.NewNotFound(autoscaling.Resource("replicationcontrollers/scale"), name) } oldScale := scaleFromRC(rc) + // TODO: should this pass validation? obj, err := objInfo.UpdatedObject(ctx, oldScale) if err != nil { return nil, false, err @@ -179,7 +180,7 @@ func (r *ScaleREST) Update(ctx genericapirequest.Context, name string, objInfo r rc.Spec.Replicas = scale.Spec.Replicas rc.ResourceVersion = scale.ResourceVersion - rc, err = r.registry.UpdateController(ctx, rc) + rc, err = r.registry.UpdateController(ctx, rc, createValidation, updateValidation) if err != nil { return nil, false, err } diff --git a/pkg/registry/core/replicationcontroller/storage/storage_test.go b/pkg/registry/core/replicationcontroller/storage/storage_test.go index c09e47055c2..4a63ee79a12 100644 --- a/pkg/registry/core/replicationcontroller/storage/storage_test.go +++ b/pkg/registry/core/replicationcontroller/storage/storage_test.go @@ -55,7 +55,7 @@ func newStorage(t *testing.T) (ControllerStorage, *etcdtesting.EtcdTestServer) { // createController is a helper function that returns a controller with the updated resource version. func createController(storage *REST, rc api.ReplicationController, t *testing.T) (api.ReplicationController, error) { ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), rc.Namespace) - obj, err := storage.Create(ctx, &rc, false) + obj, err := storage.Create(ctx, &rc, rest.ValidateAllObjectFunc, false) if err != nil { t.Errorf("Failed to create controller, %v", err) } @@ -173,7 +173,7 @@ func TestGenerationNumber(t *testing.T) { // Updates to spec should increment the generation number controller.Spec.Replicas += 1 - storage.Controller.Update(ctx, controller.Name, rest.DefaultUpdatedObjectInfo(controller)) + storage.Controller.Update(ctx, controller.Name, rest.DefaultUpdatedObjectInfo(controller), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -188,7 +188,7 @@ func TestGenerationNumber(t *testing.T) { // Updates to status should not increment either spec or status generation numbers controller.Status.Replicas += 1 - storage.Controller.Update(ctx, controller.Name, rest.DefaultUpdatedObjectInfo(controller)) + storage.Controller.Update(ctx, controller.Name, rest.DefaultUpdatedObjectInfo(controller), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -308,7 +308,7 @@ func TestScaleUpdate(t *testing.T) { }, } - if _, _, err := storage.Scale.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update)); err != nil { + if _, _, err := storage.Scale.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc); err != nil { t.Fatalf("error updating scale %v: %v", update, err) } obj, err := storage.Scale.Get(ctx, name, &metav1.GetOptions{}) @@ -323,7 +323,7 @@ func TestScaleUpdate(t *testing.T) { update.ResourceVersion = rc.ResourceVersion update.Spec.Replicas = 15 - if _, _, err = storage.Scale.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update)); err != nil && !errors.IsConflict(err) { + if _, _, err = storage.Scale.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc); err != nil && !errors.IsConflict(err) { t.Fatalf("unexpected error, expecting an update conflict but got %v", err) } } diff --git a/pkg/registry/core/resourcequota/storage/storage.go b/pkg/registry/core/resourcequota/storage/storage.go index 76f8a8f6a7a..daf6fa2c07c 100644 --- a/pkg/registry/core/resourcequota/storage/storage.go +++ b/pkg/registry/core/resourcequota/storage/storage.go @@ -77,6 +77,6 @@ func (r *StatusREST) Get(ctx genericapirequest.Context, name string, options *me } // Update alters the status subset of an object. -func (r *StatusREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { - return r.store.Update(ctx, name, objInfo) +func (r *StatusREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (runtime.Object, bool, error) { + return r.store.Update(ctx, name, objInfo, createValidation, updateValidation) } diff --git a/pkg/registry/core/resourcequota/storage/storage_test.go b/pkg/registry/core/resourcequota/storage/storage_test.go index 13e5d44cb89..6249687c493 100644 --- a/pkg/registry/core/resourcequota/storage/storage_test.go +++ b/pkg/registry/core/resourcequota/storage/storage_test.go @@ -87,7 +87,7 @@ func TestCreateSetsFields(t *testing.T) { defer storage.Store.DestroyFunc() ctx := genericapirequest.NewDefaultContext() resourcequota := validNewResourceQuota() - _, err := storage.Create(genericapirequest.NewDefaultContext(), resourcequota, false) + _, err := storage.Create(genericapirequest.NewDefaultContext(), resourcequota, rest.ValidateAllObjectFunc, false) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -191,7 +191,7 @@ func TestUpdateStatus(t *testing.T) { }, } - _, _, err = status.Update(ctx, resourcequotaIn.Name, rest.DefaultUpdatedObjectInfo(resourcequotaIn)) + _, _, err = status.Update(ctx, resourcequotaIn.Name, rest.DefaultUpdatedObjectInfo(resourcequotaIn), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc) if err != nil { t.Fatalf("Unexpected error: %v", err) } diff --git a/pkg/registry/core/secret/registry.go b/pkg/registry/core/secret/registry.go index a6d99683e00..73e1b5e2c1a 100644 --- a/pkg/registry/core/secret/registry.go +++ b/pkg/registry/core/secret/registry.go @@ -30,8 +30,8 @@ type Registry interface { ListSecrets(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (*api.SecretList, error) WatchSecrets(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (watch.Interface, error) GetSecret(ctx genericapirequest.Context, name string, options *metav1.GetOptions) (*api.Secret, error) - CreateSecret(ctx genericapirequest.Context, Secret *api.Secret) (*api.Secret, error) - UpdateSecret(ctx genericapirequest.Context, Secret *api.Secret) (*api.Secret, error) + CreateSecret(ctx genericapirequest.Context, Secret *api.Secret, createValidation rest.ValidateObjectFunc) (*api.Secret, error) + UpdateSecret(ctx genericapirequest.Context, Secret *api.Secret, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (*api.Secret, error) DeleteSecret(ctx genericapirequest.Context, name string) error } @@ -66,13 +66,13 @@ func (s *storage) GetSecret(ctx genericapirequest.Context, name string, options return obj.(*api.Secret), nil } -func (s *storage) CreateSecret(ctx genericapirequest.Context, secret *api.Secret) (*api.Secret, error) { - obj, err := s.Create(ctx, secret, false) +func (s *storage) CreateSecret(ctx genericapirequest.Context, secret *api.Secret, createValidation rest.ValidateObjectFunc) (*api.Secret, error) { + obj, err := s.Create(ctx, secret, createValidation, false) return obj.(*api.Secret), err } -func (s *storage) UpdateSecret(ctx genericapirequest.Context, secret *api.Secret) (*api.Secret, error) { - obj, _, err := s.Update(ctx, secret.Name, rest.DefaultUpdatedObjectInfo(secret)) +func (s *storage) UpdateSecret(ctx genericapirequest.Context, secret *api.Secret, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (*api.Secret, error) { + obj, _, err := s.Update(ctx, secret.Name, rest.DefaultUpdatedObjectInfo(secret), createValidation, updateValidation) return obj.(*api.Secret), err } diff --git a/pkg/registry/core/service/registry.go b/pkg/registry/core/service/registry.go index d63922f1e03..1660535525c 100644 --- a/pkg/registry/core/service/registry.go +++ b/pkg/registry/core/service/registry.go @@ -30,10 +30,10 @@ import ( // Registry is an interface for things that know how to store services. type Registry interface { ListServices(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (*api.ServiceList, error) - CreateService(ctx genericapirequest.Context, svc *api.Service) (*api.Service, error) + CreateService(ctx genericapirequest.Context, svc *api.Service, createValidation rest.ValidateObjectFunc) (*api.Service, error) GetService(ctx genericapirequest.Context, name string, options *metav1.GetOptions) (*api.Service, error) DeleteService(ctx genericapirequest.Context, name string) error - UpdateService(ctx genericapirequest.Context, svc *api.Service) (*api.Service, error) + UpdateService(ctx genericapirequest.Context, svc *api.Service, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (*api.Service, error) WatchServices(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (watch.Interface, error) ExportService(ctx genericapirequest.Context, name string, options metav1.ExportOptions) (*api.Service, error) } @@ -57,8 +57,8 @@ func (s *storage) ListServices(ctx genericapirequest.Context, options *metainter return obj.(*api.ServiceList), nil } -func (s *storage) CreateService(ctx genericapirequest.Context, svc *api.Service) (*api.Service, error) { - obj, err := s.Create(ctx, svc, false) +func (s *storage) CreateService(ctx genericapirequest.Context, svc *api.Service, createValidation rest.ValidateObjectFunc) (*api.Service, error) { + obj, err := s.Create(ctx, svc, createValidation, false) if err != nil { return nil, err } @@ -78,8 +78,8 @@ func (s *storage) DeleteService(ctx genericapirequest.Context, name string) erro return err } -func (s *storage) UpdateService(ctx genericapirequest.Context, svc *api.Service) (*api.Service, error) { - obj, _, err := s.Update(ctx, svc.Name, rest.DefaultUpdatedObjectInfo(svc)) +func (s *storage) UpdateService(ctx genericapirequest.Context, svc *api.Service, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (*api.Service, error) { + obj, _, err := s.Update(ctx, svc.Name, rest.DefaultUpdatedObjectInfo(svc), createValidation, updateValidation) if err != nil { return nil, err } diff --git a/pkg/registry/core/service/rest.go b/pkg/registry/core/service/rest.go index 3a353c34495..a36d0983466 100644 --- a/pkg/registry/core/service/rest.go +++ b/pkg/registry/core/service/rest.go @@ -88,7 +88,7 @@ func (rs *REST) Categories() []string { } // TODO: implement includeUninitialized by refactoring this to move to store -func (rs *REST) Create(ctx genericapirequest.Context, obj runtime.Object, includeUninitialized bool) (runtime.Object, error) { +func (rs *REST) Create(ctx genericapirequest.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, includeUninitialized bool) (runtime.Object, error) { service := obj.(*api.Service) if err := rest.BeforeCreate(Strategy, ctx, obj); err != nil { @@ -133,7 +133,7 @@ func (rs *REST) Create(ctx genericapirequest.Context, obj runtime.Object, includ } } - out, err := rs.registry.CreateService(ctx, service) + out, err := rs.registry.CreateService(ctx, service, createValidation) if err != nil { err = rest.CheckGeneratedNameError(Strategy, err, service) } @@ -284,7 +284,7 @@ func (rs *REST) healthCheckNodePortUpdate(oldService, service *api.Service, node return true, nil } -func (rs *REST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { +func (rs *REST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (runtime.Object, bool, error) { oldService, err := rs.registry.GetService(ctx, name, &metav1.GetOptions{}) if err != nil { return nil, false, err @@ -360,7 +360,7 @@ func (rs *REST) Update(ctx genericapirequest.Context, name string, objInfo rest. } } - out, err := rs.registry.UpdateService(ctx, service) + out, err := rs.registry.UpdateService(ctx, service, createValidation, updateValidation) if err == nil { el := nodePortOp.Commit() if el != nil { diff --git a/pkg/registry/core/service/rest_test.go b/pkg/registry/core/service/rest_test.go index 810c026b38a..ad171e9441e 100644 --- a/pkg/registry/core/service/rest_test.go +++ b/pkg/registry/core/service/rest_test.go @@ -111,7 +111,7 @@ func TestServiceRegistryCreate(t *testing.T) { }, } ctx := genericapirequest.NewDefaultContext() - created_svc, err := storage.Create(ctx, svc, false) + created_svc, err := storage.Create(ctx, svc, rest.ValidateAllObjectFunc, false) if err != nil { t.Fatalf("Unexpected error: %v", err) } @@ -235,7 +235,7 @@ func TestServiceRegistryCreateMultiNodePortsService(t *testing.T) { ctx := genericapirequest.NewDefaultContext() for _, test := range testCases { - created_svc, err := storage.Create(ctx, test.svc, false) + created_svc, err := storage.Create(ctx, test.svc, rest.ValidateAllObjectFunc, false) if err != nil { t.Fatalf("Unexpected error: %v", err) } @@ -311,7 +311,7 @@ func TestServiceStorageValidatesCreate(t *testing.T) { } ctx := genericapirequest.NewDefaultContext() for _, failureCase := range failureCases { - c, err := storage.Create(ctx, &failureCase, false) + c, err := storage.Create(ctx, &failureCase, rest.ValidateAllObjectFunc, false) if c != nil { t.Errorf("Expected nil object") } @@ -334,7 +334,7 @@ func TestServiceRegistryUpdate(t *testing.T) { TargetPort: intstr.FromInt(6502), }}, }, - }) + }, rest.ValidateAllObjectFunc) if err != nil { t.Fatalf("Expected no error: %v", err) @@ -353,7 +353,7 @@ func TestServiceRegistryUpdate(t *testing.T) { TargetPort: intstr.FromInt(6502), }}, }, - })) + }), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc) if err != nil { t.Fatalf("Expected no error: %v", err) } @@ -384,7 +384,7 @@ func TestServiceStorageValidatesUpdate(t *testing.T) { Protocol: api.ProtocolTCP, }}, }, - }) + }, rest.ValidateAllObjectFunc) failureCases := map[string]api.Service{ "empty ID": { ObjectMeta: metav1.ObjectMeta{Name: ""}, @@ -414,7 +414,7 @@ func TestServiceStorageValidatesUpdate(t *testing.T) { }, } for _, failureCase := range failureCases { - c, created, err := storage.Update(ctx, failureCase.Name, rest.DefaultUpdatedObjectInfo(&failureCase)) + c, created, err := storage.Update(ctx, failureCase.Name, rest.DefaultUpdatedObjectInfo(&failureCase), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc) if c != nil || created { t.Errorf("Expected nil object or created false") } @@ -440,7 +440,7 @@ func TestServiceRegistryExternalService(t *testing.T) { }}, }, } - _, err := storage.Create(ctx, svc, false) + _, err := storage.Create(ctx, svc, rest.ValidateAllObjectFunc, false) if err != nil { t.Errorf("Failed to create service: %#v", err) } @@ -477,7 +477,7 @@ func TestServiceRegistryDelete(t *testing.T) { }}, }, } - registry.CreateService(ctx, svc) + registry.CreateService(ctx, svc, rest.ValidateAllObjectFunc) storage.Delete(ctx, svc.Name) if e, a := "foo", registry.DeletedID; e != a { t.Errorf("Expected %v, but got %v", e, a) @@ -499,7 +499,7 @@ func TestServiceRegistryDeleteExternal(t *testing.T) { }}, }, } - registry.CreateService(ctx, svc) + registry.CreateService(ctx, svc, rest.ValidateAllObjectFunc) storage.Delete(ctx, svc.Name) if e, a := "foo", registry.DeletedID; e != a { t.Errorf("Expected %v, but got %v", e, a) @@ -524,14 +524,14 @@ func TestServiceRegistryUpdateExternalService(t *testing.T) { }}, }, } - if _, err := storage.Create(ctx, svc1, false); err != nil { + if _, err := storage.Create(ctx, svc1, rest.ValidateAllObjectFunc, false); err != nil { t.Fatalf("Unexpected error: %v", err) } // Modify load balancer to be external. svc2 := svc1.DeepCopy() svc2.Spec.Type = api.ServiceTypeLoadBalancer - if _, _, err := storage.Update(ctx, svc2.Name, rest.DefaultUpdatedObjectInfo(svc2)); err != nil { + if _, _, err := storage.Update(ctx, svc2.Name, rest.DefaultUpdatedObjectInfo(svc2), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc); err != nil { t.Fatalf("Unexpected error: %v", err) } defer releaseServiceNodePorts(t, ctx, svc2.Name, storage, registry) @@ -539,7 +539,7 @@ func TestServiceRegistryUpdateExternalService(t *testing.T) { // Change port. svc3 := svc2.DeepCopy() svc3.Spec.Ports[0].Port = 6504 - if _, _, err := storage.Update(ctx, svc3.Name, rest.DefaultUpdatedObjectInfo(svc3)); err != nil { + if _, _, err := storage.Update(ctx, svc3.Name, rest.DefaultUpdatedObjectInfo(svc3), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc); err != nil { t.Fatalf("Unexpected error: %v", err) } } @@ -568,7 +568,7 @@ func TestServiceRegistryUpdateMultiPortExternalService(t *testing.T) { }}, }, } - if _, err := storage.Create(ctx, svc1, false); err != nil { + if _, err := storage.Create(ctx, svc1, rest.ValidateAllObjectFunc, false); err != nil { t.Fatalf("Unexpected error: %v", err) } defer releaseServiceNodePorts(t, ctx, svc1.Name, storage, registry) @@ -576,7 +576,7 @@ func TestServiceRegistryUpdateMultiPortExternalService(t *testing.T) { // Modify ports svc2 := svc1.DeepCopy() svc2.Spec.Ports[1].Port = 8088 - if _, _, err := storage.Update(ctx, svc2.Name, rest.DefaultUpdatedObjectInfo(svc2)); err != nil { + if _, _, err := storage.Update(ctx, svc2.Name, rest.DefaultUpdatedObjectInfo(svc2), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc); err != nil { t.Fatalf("Unexpected error: %v", err) } } @@ -589,7 +589,7 @@ func TestServiceRegistryGet(t *testing.T) { Spec: api.ServiceSpec{ Selector: map[string]string{"bar": "baz"}, }, - }) + }, rest.ValidateAllObjectFunc) storage.Get(ctx, "foo", &metav1.GetOptions{}) if e, a := "foo", registry.GottenID; e != a { t.Errorf("Expected %v, but got %v", e, a) @@ -642,7 +642,7 @@ func TestServiceRegistryResourceLocation(t *testing.T) { {Name: "", Port: 93, TargetPort: intstr.FromInt(80)}, }, }, - }) + }, rest.ValidateAllObjectFunc) redirector := rest.Redirector(storage) // Test a simple id. @@ -725,13 +725,13 @@ func TestServiceRegistryList(t *testing.T) { Spec: api.ServiceSpec{ Selector: map[string]string{"bar": "baz"}, }, - }) + }, rest.ValidateAllObjectFunc) registry.CreateService(ctx, &api.Service{ ObjectMeta: metav1.ObjectMeta{Name: "foo2", Namespace: metav1.NamespaceDefault}, Spec: api.ServiceSpec{ Selector: map[string]string{"bar2": "baz2"}, }, - }) + }, rest.ValidateAllObjectFunc) registry.List.ResourceVersion = "1" s, _ := storage.List(ctx, nil) sl := s.(*api.ServiceList) @@ -766,7 +766,7 @@ func TestServiceRegistryIPAllocation(t *testing.T) { }, } ctx := genericapirequest.NewDefaultContext() - created_svc1, _ := storage.Create(ctx, svc1, false) + created_svc1, _ := storage.Create(ctx, svc1, rest.ValidateAllObjectFunc, false) created_service_1 := created_svc1.(*api.Service) if created_service_1.Name != "foo" { t.Errorf("Expected foo, but got %v", created_service_1.Name) @@ -788,7 +788,7 @@ func TestServiceRegistryIPAllocation(t *testing.T) { }}, }} ctx = genericapirequest.NewDefaultContext() - created_svc2, _ := storage.Create(ctx, svc2, false) + created_svc2, _ := storage.Create(ctx, svc2, rest.ValidateAllObjectFunc, false) created_service_2 := created_svc2.(*api.Service) if created_service_2.Name != "bar" { t.Errorf("Expected bar, but got %v", created_service_2.Name) @@ -821,7 +821,7 @@ func TestServiceRegistryIPAllocation(t *testing.T) { }, } ctx = genericapirequest.NewDefaultContext() - created_svc3, err := storage.Create(ctx, svc3, false) + created_svc3, err := storage.Create(ctx, svc3, rest.ValidateAllObjectFunc, false) if err != nil { t.Fatal(err) } @@ -848,7 +848,7 @@ func TestServiceRegistryIPReallocation(t *testing.T) { }, } ctx := genericapirequest.NewDefaultContext() - created_svc1, _ := storage.Create(ctx, svc1, false) + created_svc1, _ := storage.Create(ctx, svc1, rest.ValidateAllObjectFunc, false) created_service_1 := created_svc1.(*api.Service) if created_service_1.Name != "foo" { t.Errorf("Expected foo, but got %v", created_service_1.Name) @@ -876,7 +876,7 @@ func TestServiceRegistryIPReallocation(t *testing.T) { }, } ctx = genericapirequest.NewDefaultContext() - created_svc2, _ := storage.Create(ctx, svc2, false) + created_svc2, _ := storage.Create(ctx, svc2, rest.ValidateAllObjectFunc, false) created_service_2 := created_svc2.(*api.Service) if created_service_2.Name != "bar" { t.Errorf("Expected bar, but got %v", created_service_2.Name) @@ -903,7 +903,7 @@ func TestServiceRegistryIPUpdate(t *testing.T) { }, } ctx := genericapirequest.NewDefaultContext() - created_svc, _ := storage.Create(ctx, svc, false) + created_svc, _ := storage.Create(ctx, svc, rest.ValidateAllObjectFunc, false) created_service := created_svc.(*api.Service) if created_service.Spec.Ports[0].Port != 6502 { t.Errorf("Expected port 6502, but got %v", created_service.Spec.Ports[0].Port) @@ -915,7 +915,7 @@ func TestServiceRegistryIPUpdate(t *testing.T) { update := created_service.DeepCopy() update.Spec.Ports[0].Port = 6503 - updated_svc, _, _ := storage.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(update)) + updated_svc, _, _ := storage.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(update), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc) updated_service := updated_svc.(*api.Service) if updated_service.Spec.Ports[0].Port != 6503 { t.Errorf("Expected port 6503, but got %v", updated_service.Spec.Ports[0].Port) @@ -934,7 +934,7 @@ func TestServiceRegistryIPUpdate(t *testing.T) { update.Spec.Ports[0].Port = 6503 update.Spec.ClusterIP = testIP // Error: Cluster IP is immutable - _, _, err := storage.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(update)) + _, _, err := storage.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(update), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc) if err == nil || !errors.IsInvalid(err) { t.Errorf("Unexpected error type: %v", err) } @@ -957,7 +957,7 @@ func TestServiceRegistryIPLoadBalancer(t *testing.T) { }, } ctx := genericapirequest.NewDefaultContext() - created_svc, err := storage.Create(ctx, svc, false) + created_svc, err := storage.Create(ctx, svc, rest.ValidateAllObjectFunc, false) if created_svc == nil || err != nil { t.Errorf("Unexpected failure creating service %v", err) } @@ -973,7 +973,7 @@ func TestServiceRegistryIPLoadBalancer(t *testing.T) { update := created_service.DeepCopy() - _, _, err = storage.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(update)) + _, _, err = storage.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(update), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc) if err != nil { t.Errorf("Unexpected error %v", err) } @@ -986,7 +986,7 @@ func TestUpdateServiceWithConflictingNamespace(t *testing.T) { } ctx := genericapirequest.NewDefaultContext() - obj, created, err := storage.Update(ctx, service.Name, rest.DefaultUpdatedObjectInfo(service)) + obj, created, err := storage.Update(ctx, service.Name, rest.DefaultUpdatedObjectInfo(service), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc) if obj != nil || created { t.Error("Expected a nil object, but we got a value or created was true") } @@ -1016,7 +1016,7 @@ func TestServiceRegistryExternalTrafficHealthCheckNodePortAllocation(t *testing. ExternalTrafficPolicy: api.ServiceExternalTrafficPolicyTypeLocal, }, } - created_svc, err := storage.Create(ctx, svc, false) + created_svc, err := storage.Create(ctx, svc, rest.ValidateAllObjectFunc, false) if created_svc == nil || err != nil { t.Errorf("Unexpected failure creating service %v", err) } @@ -1056,7 +1056,7 @@ func TestServiceRegistryExternalTrafficHealthCheckNodePortUserAllocation(t *test HealthCheckNodePort: randomNodePort, }, } - created_svc, err := storage.Create(ctx, svc, false) + created_svc, err := storage.Create(ctx, svc, rest.ValidateAllObjectFunc, false) if created_svc == nil || err != nil { t.Fatalf("Unexpected failure creating service :%v", err) } @@ -1098,7 +1098,7 @@ func TestServiceRegistryExternalTrafficHealthCheckNodePortNegative(t *testing.T) HealthCheckNodePort: int32(-1), }, } - created_svc, err := storage.Create(ctx, svc, false) + created_svc, err := storage.Create(ctx, svc, rest.ValidateAllObjectFunc, false) if created_svc == nil || err != nil { return } @@ -1123,7 +1123,7 @@ func TestServiceRegistryExternalTrafficGlobal(t *testing.T) { ExternalTrafficPolicy: api.ServiceExternalTrafficPolicyTypeCluster, }, } - created_svc, err := storage.Create(ctx, svc, false) + created_svc, err := storage.Create(ctx, svc, rest.ValidateAllObjectFunc, false) if created_svc == nil || err != nil { t.Errorf("Unexpected failure creating service %v", err) } diff --git a/pkg/registry/core/service/storage/storage.go b/pkg/registry/core/service/storage/storage.go index aaa0b52f601..eb0f8d052e5 100644 --- a/pkg/registry/core/service/storage/storage.go +++ b/pkg/registry/core/service/storage/storage.go @@ -89,6 +89,6 @@ func (r *StatusREST) Get(ctx genericapirequest.Context, name string, options *me } // Update alters the status subset of an object. -func (r *StatusREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { - return r.store.Update(ctx, name, objInfo) +func (r *StatusREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (runtime.Object, bool, error) { + return r.store.Update(ctx, name, objInfo, createValidation, updateValidation) } diff --git a/pkg/registry/core/serviceaccount/registry.go b/pkg/registry/core/serviceaccount/registry.go index 7979b29ac02..edb30e9de53 100644 --- a/pkg/registry/core/serviceaccount/registry.go +++ b/pkg/registry/core/serviceaccount/registry.go @@ -30,8 +30,8 @@ type Registry interface { ListServiceAccounts(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (*api.ServiceAccountList, error) WatchServiceAccounts(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (watch.Interface, error) GetServiceAccount(ctx genericapirequest.Context, name string, options *metav1.GetOptions) (*api.ServiceAccount, error) - CreateServiceAccount(ctx genericapirequest.Context, ServiceAccount *api.ServiceAccount) error - UpdateServiceAccount(ctx genericapirequest.Context, ServiceAccount *api.ServiceAccount) error + CreateServiceAccount(ctx genericapirequest.Context, ServiceAccount *api.ServiceAccount, createValidation rest.ValidateObjectFunc) error + UpdateServiceAccount(ctx genericapirequest.Context, ServiceAccount *api.ServiceAccount, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) error DeleteServiceAccount(ctx genericapirequest.Context, name string) error } @@ -66,13 +66,13 @@ func (s *storage) GetServiceAccount(ctx genericapirequest.Context, name string, return obj.(*api.ServiceAccount), nil } -func (s *storage) CreateServiceAccount(ctx genericapirequest.Context, serviceAccount *api.ServiceAccount) error { - _, err := s.Create(ctx, serviceAccount, false) +func (s *storage) CreateServiceAccount(ctx genericapirequest.Context, serviceAccount *api.ServiceAccount, createValidation rest.ValidateObjectFunc) error { + _, err := s.Create(ctx, serviceAccount, createValidation, false) return err } -func (s *storage) UpdateServiceAccount(ctx genericapirequest.Context, serviceAccount *api.ServiceAccount) error { - _, _, err := s.Update(ctx, serviceAccount.Name, rest.DefaultUpdatedObjectInfo(serviceAccount)) +func (s *storage) UpdateServiceAccount(ctx genericapirequest.Context, serviceAccount *api.ServiceAccount, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) error { + _, _, err := s.Update(ctx, serviceAccount.Name, rest.DefaultUpdatedObjectInfo(serviceAccount), createValidation, updateValidation) return err } diff --git a/pkg/registry/extensions/controller/storage/storage.go b/pkg/registry/extensions/controller/storage/storage.go index a3fa80c464b..d7e83e84b71 100644 --- a/pkg/registry/extensions/controller/storage/storage.go +++ b/pkg/registry/extensions/controller/storage/storage.go @@ -69,7 +69,7 @@ func (r *ScaleREST) Get(ctx genericapirequest.Context, name string, options *met return scaleFromRC(rc), nil } -func (r *ScaleREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { +func (r *ScaleREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (runtime.Object, bool, error) { rc, err := (*r.registry).GetController(ctx, name, &metav1.GetOptions{}) if err != nil { return nil, false, errors.NewNotFound(extensions.Resource("replicationcontrollers/scale"), name) @@ -92,7 +92,7 @@ func (r *ScaleREST) Update(ctx genericapirequest.Context, name string, objInfo r rc.Spec.Replicas = scale.Spec.Replicas rc.ResourceVersion = scale.ResourceVersion - rc, err = (*r.registry).UpdateController(ctx, rc) + rc, err = (*r.registry).UpdateController(ctx, rc, createValidation, updateValidation) if err != nil { return nil, false, errors.NewConflict(extensions.Resource("replicationcontrollers/scale"), scale.Name, err) } diff --git a/pkg/registry/extensions/controller/storage/storage_test.go b/pkg/registry/extensions/controller/storage/storage_test.go index 0d08f0d40ad..27bc76d727c 100644 --- a/pkg/registry/extensions/controller/storage/storage_test.go +++ b/pkg/registry/extensions/controller/storage/storage_test.go @@ -123,7 +123,7 @@ func TestUpdate(t *testing.T) { }, } - if _, _, err := storage.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update)); err != nil { + if _, _, err := storage.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc); err != nil { t.Fatalf("unexpected error: %v", err) } obj, err := storage.Get(ctx, "foo", &metav1.GetOptions{}) diff --git a/pkg/registry/extensions/daemonset/storage/storage.go b/pkg/registry/extensions/daemonset/storage/storage.go index c5318901dfd..461e5a97ff2 100644 --- a/pkg/registry/extensions/daemonset/storage/storage.go +++ b/pkg/registry/extensions/daemonset/storage/storage.go @@ -89,6 +89,6 @@ func (r *StatusREST) Get(ctx genericapirequest.Context, name string, options *me } // Update alters the status subset of an object. -func (r *StatusREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { - return r.store.Update(ctx, name, objInfo) +func (r *StatusREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (runtime.Object, bool, error) { + return r.store.Update(ctx, name, objInfo, createValidation, updateValidation) } diff --git a/pkg/registry/extensions/deployment/registry.go b/pkg/registry/extensions/deployment/registry.go index e5b03be6e36..951e86863fb 100644 --- a/pkg/registry/extensions/deployment/registry.go +++ b/pkg/registry/extensions/deployment/registry.go @@ -30,8 +30,8 @@ import ( type Registry interface { ListDeployments(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (*extensions.DeploymentList, error) GetDeployment(ctx genericapirequest.Context, deploymentID string, options *metav1.GetOptions) (*extensions.Deployment, error) - CreateDeployment(ctx genericapirequest.Context, deployment *extensions.Deployment) (*extensions.Deployment, error) - UpdateDeployment(ctx genericapirequest.Context, deployment *extensions.Deployment) (*extensions.Deployment, error) + CreateDeployment(ctx genericapirequest.Context, deployment *extensions.Deployment, createValidation rest.ValidateObjectFunc) (*extensions.Deployment, error) + UpdateDeployment(ctx genericapirequest.Context, deployment *extensions.Deployment, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (*extensions.Deployment, error) DeleteDeployment(ctx genericapirequest.Context, deploymentID string) error } @@ -64,16 +64,16 @@ func (s *storage) GetDeployment(ctx genericapirequest.Context, deploymentID stri return obj.(*extensions.Deployment), nil } -func (s *storage) CreateDeployment(ctx genericapirequest.Context, deployment *extensions.Deployment) (*extensions.Deployment, error) { - obj, err := s.Create(ctx, deployment, false) +func (s *storage) CreateDeployment(ctx genericapirequest.Context, deployment *extensions.Deployment, createValidation rest.ValidateObjectFunc) (*extensions.Deployment, error) { + obj, err := s.Create(ctx, deployment, createValidation, false) if err != nil { return nil, err } return obj.(*extensions.Deployment), nil } -func (s *storage) UpdateDeployment(ctx genericapirequest.Context, deployment *extensions.Deployment) (*extensions.Deployment, error) { - obj, _, err := s.Update(ctx, deployment.Name, rest.DefaultUpdatedObjectInfo(deployment)) +func (s *storage) UpdateDeployment(ctx genericapirequest.Context, deployment *extensions.Deployment, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (*extensions.Deployment, error) { + obj, _, err := s.Update(ctx, deployment.Name, rest.DefaultUpdatedObjectInfo(deployment), createValidation, updateValidation) if err != nil { return nil, err } diff --git a/pkg/registry/extensions/deployment/storage/storage.go b/pkg/registry/extensions/deployment/storage/storage.go index 23eb69bd329..aedccc58bf1 100644 --- a/pkg/registry/extensions/deployment/storage/storage.go +++ b/pkg/registry/extensions/deployment/storage/storage.go @@ -115,8 +115,8 @@ func (r *StatusREST) Get(ctx genericapirequest.Context, name string, options *me } // Update alters the status subset of an object. -func (r *StatusREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { - return r.store.Update(ctx, name, objInfo) +func (r *StatusREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (runtime.Object, bool, error) { + return r.store.Update(ctx, name, objInfo, createValidation, updateValidation) } // RollbackREST implements the REST endpoint for initiating the rollback of a deployment @@ -131,7 +131,7 @@ func (r *RollbackREST) New() runtime.Object { var _ = rest.Creater(&RollbackREST{}) -func (r *RollbackREST) Create(ctx genericapirequest.Context, obj runtime.Object, includeUninitialized bool) (runtime.Object, error) { +func (r *RollbackREST) Create(ctx genericapirequest.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, includeUninitialized bool) (runtime.Object, error) { rollback, ok := obj.(*extensions.DeploymentRollback) if !ok { return nil, errors.NewBadRequest(fmt.Sprintf("not a DeploymentRollback: %#v", obj)) @@ -227,7 +227,7 @@ func (r *ScaleREST) Get(ctx genericapirequest.Context, name string, options *met return scale, nil } -func (r *ScaleREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { +func (r *ScaleREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (runtime.Object, bool, error) { deployment, err := r.registry.GetDeployment(ctx, name, &metav1.GetOptions{}) if err != nil { return nil, false, errors.NewNotFound(extensions.Resource("deployments/scale"), name) @@ -256,7 +256,7 @@ func (r *ScaleREST) Update(ctx genericapirequest.Context, name string, objInfo r deployment.Spec.Replicas = scale.Spec.Replicas deployment.ResourceVersion = scale.ResourceVersion - deployment, err = r.registry.UpdateDeployment(ctx, deployment) + deployment, err = r.registry.UpdateDeployment(ctx, deployment, createValidation, updateValidation) if err != nil { return nil, false, err } diff --git a/pkg/registry/extensions/deployment/storage/storage_test.go b/pkg/registry/extensions/deployment/storage/storage_test.go index 09823f40c61..55fe3476f4c 100644 --- a/pkg/registry/extensions/deployment/storage/storage_test.go +++ b/pkg/registry/extensions/deployment/storage/storage_test.go @@ -251,7 +251,7 @@ func TestScaleUpdate(t *testing.T) { }, } - if _, _, err := storage.Scale.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update)); err != nil { + if _, _, err := storage.Scale.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc); err != nil { t.Fatalf("error updating scale %v: %v", update, err) } obj, err := storage.Scale.Get(ctx, name, &metav1.GetOptions{}) @@ -266,7 +266,7 @@ func TestScaleUpdate(t *testing.T) { update.ResourceVersion = deployment.ResourceVersion update.Spec.Replicas = 15 - if _, _, err = storage.Scale.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update)); err != nil && !errors.IsConflict(err) { + if _, _, err = storage.Scale.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc); err != nil && !errors.IsConflict(err) { t.Fatalf("unexpected error, expecting an update conflict but got %v", err) } } @@ -290,7 +290,7 @@ func TestStatusUpdate(t *testing.T) { }, } - if _, _, err := storage.Status.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update)); err != nil { + if _, _, err := storage.Status.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc); err != nil { t.Fatalf("unexpected error: %v", err) } obj, err := storage.Deployment.Get(ctx, name, &metav1.GetOptions{}) @@ -341,10 +341,10 @@ func TestEtcdCreateDeploymentRollback(t *testing.T) { storage, server := newStorage(t) rollbackStorage := storage.Rollback - if _, err := storage.Deployment.Create(ctx, validNewDeployment(), false); err != nil { + if _, err := storage.Deployment.Create(ctx, validNewDeployment(), rest.ValidateAllObjectFunc, false); err != nil { t.Fatalf("%s: unexpected error: %v", k, err) } - rollbackRespStatus, err := rollbackStorage.Create(ctx, &test.rollback, false) + rollbackRespStatus, err := rollbackStorage.Create(ctx, &test.rollback, rest.ValidateAllObjectFunc, false) if !test.errOK(err) { t.Errorf("%s: unexpected error: %v", k, err) } else if err == nil { @@ -381,7 +381,7 @@ func TestEtcdCreateDeploymentRollbackNoDeployment(t *testing.T) { Name: name, UpdatedAnnotations: map[string]string{}, RollbackTo: extensions.RollbackConfig{Revision: 1}, - }, false) + }, rest.ValidateAllObjectFunc, false) if err == nil { t.Fatalf("Expected not-found-error but got nothing") } diff --git a/pkg/registry/extensions/ingress/storage/storage.go b/pkg/registry/extensions/ingress/storage/storage.go index 9a8d6e253a1..0c7273d8578 100644 --- a/pkg/registry/extensions/ingress/storage/storage.go +++ b/pkg/registry/extensions/ingress/storage/storage.go @@ -81,6 +81,6 @@ func (r *StatusREST) Get(ctx genericapirequest.Context, name string, options *me } // Update alters the status subset of an object. -func (r *StatusREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { - return r.store.Update(ctx, name, objInfo) +func (r *StatusREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (runtime.Object, bool, error) { + return r.store.Update(ctx, name, objInfo, createValidation, updateValidation) } diff --git a/pkg/registry/extensions/replicaset/registry.go b/pkg/registry/extensions/replicaset/registry.go index 155083e1fbc..029631eb68e 100644 --- a/pkg/registry/extensions/replicaset/registry.go +++ b/pkg/registry/extensions/replicaset/registry.go @@ -34,8 +34,8 @@ type Registry interface { ListReplicaSets(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (*extensions.ReplicaSetList, error) WatchReplicaSets(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (watch.Interface, error) GetReplicaSet(ctx genericapirequest.Context, replicaSetID string, options *metav1.GetOptions) (*extensions.ReplicaSet, error) - CreateReplicaSet(ctx genericapirequest.Context, replicaSet *extensions.ReplicaSet) (*extensions.ReplicaSet, error) - UpdateReplicaSet(ctx genericapirequest.Context, replicaSet *extensions.ReplicaSet) (*extensions.ReplicaSet, error) + CreateReplicaSet(ctx genericapirequest.Context, replicaSet *extensions.ReplicaSet, createValidation rest.ValidateObjectFunc) (*extensions.ReplicaSet, error) + UpdateReplicaSet(ctx genericapirequest.Context, replicaSet *extensions.ReplicaSet, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (*extensions.ReplicaSet, error) DeleteReplicaSet(ctx genericapirequest.Context, replicaSetID string) error } @@ -73,16 +73,16 @@ func (s *storage) GetReplicaSet(ctx genericapirequest.Context, replicaSetID stri return obj.(*extensions.ReplicaSet), nil } -func (s *storage) CreateReplicaSet(ctx genericapirequest.Context, replicaSet *extensions.ReplicaSet) (*extensions.ReplicaSet, error) { - obj, err := s.Create(ctx, replicaSet, false) +func (s *storage) CreateReplicaSet(ctx genericapirequest.Context, replicaSet *extensions.ReplicaSet, createValidation rest.ValidateObjectFunc) (*extensions.ReplicaSet, error) { + obj, err := s.Create(ctx, replicaSet, createValidation, false) if err != nil { return nil, err } return obj.(*extensions.ReplicaSet), nil } -func (s *storage) UpdateReplicaSet(ctx genericapirequest.Context, replicaSet *extensions.ReplicaSet) (*extensions.ReplicaSet, error) { - obj, _, err := s.Update(ctx, replicaSet.Name, rest.DefaultUpdatedObjectInfo(replicaSet)) +func (s *storage) UpdateReplicaSet(ctx genericapirequest.Context, replicaSet *extensions.ReplicaSet, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (*extensions.ReplicaSet, error) { + obj, _, err := s.Update(ctx, replicaSet.Name, rest.DefaultUpdatedObjectInfo(replicaSet), createValidation, updateValidation) if err != nil { return nil, err } diff --git a/pkg/registry/extensions/replicaset/storage/storage.go b/pkg/registry/extensions/replicaset/storage/storage.go index 9eff35bcea7..de513a337a9 100644 --- a/pkg/registry/extensions/replicaset/storage/storage.go +++ b/pkg/registry/extensions/replicaset/storage/storage.go @@ -119,8 +119,8 @@ func (r *StatusREST) Get(ctx genericapirequest.Context, name string, options *me } // Update alters the status subset of an object. -func (r *StatusREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { - return r.store.Update(ctx, name, objInfo) +func (r *StatusREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (runtime.Object, bool, error) { + return r.store.Update(ctx, name, objInfo, createValidation, updateValidation) } type ScaleREST struct { @@ -161,7 +161,7 @@ func (r *ScaleREST) Get(ctx genericapirequest.Context, name string, options *met return scale, err } -func (r *ScaleREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { +func (r *ScaleREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (runtime.Object, bool, error) { rs, err := r.registry.GetReplicaSet(ctx, name, &metav1.GetOptions{}) if err != nil { return nil, false, errors.NewNotFound(extensions.Resource("replicasets/scale"), name) @@ -172,6 +172,7 @@ func (r *ScaleREST) Update(ctx genericapirequest.Context, name string, objInfo r return nil, false, err } + // TODO: should this pass admission? obj, err := objInfo.UpdatedObject(ctx, oldScale) if err != nil { return nil, false, err @@ -190,7 +191,7 @@ func (r *ScaleREST) Update(ctx genericapirequest.Context, name string, objInfo r rs.Spec.Replicas = scale.Spec.Replicas rs.ResourceVersion = scale.ResourceVersion - rs, err = r.registry.UpdateReplicaSet(ctx, rs) + rs, err = r.registry.UpdateReplicaSet(ctx, rs, createValidation, updateValidation) if err != nil { return nil, false, err } diff --git a/pkg/registry/extensions/replicaset/storage/storage_test.go b/pkg/registry/extensions/replicaset/storage/storage_test.go index ba8d4991288..8f58047564c 100644 --- a/pkg/registry/extensions/replicaset/storage/storage_test.go +++ b/pkg/registry/extensions/replicaset/storage/storage_test.go @@ -47,7 +47,7 @@ func newStorage(t *testing.T) (*ReplicaSetStorage, *etcdtesting.EtcdTestServer) // createReplicaSet is a helper function that returns a ReplicaSet with the updated resource version. func createReplicaSet(storage *REST, rs extensions.ReplicaSet, t *testing.T) (extensions.ReplicaSet, error) { ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), rs.Namespace) - obj, err := storage.Create(ctx, &rs, false) + obj, err := storage.Create(ctx, &rs, rest.ValidateAllObjectFunc, false) if err != nil { t.Errorf("Failed to create ReplicaSet, %v", err) } @@ -169,7 +169,7 @@ func TestGenerationNumber(t *testing.T) { // Updates to spec should increment the generation number storedRS.Spec.Replicas += 1 - storage.ReplicaSet.Update(ctx, storedRS.Name, rest.DefaultUpdatedObjectInfo(storedRS)) + storage.ReplicaSet.Update(ctx, storedRS.Name, rest.DefaultUpdatedObjectInfo(storedRS), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -184,7 +184,7 @@ func TestGenerationNumber(t *testing.T) { // Updates to status should not increment either spec or status generation numbers storedRS.Status.Replicas += 1 - storage.ReplicaSet.Update(ctx, storedRS.Name, rest.DefaultUpdatedObjectInfo(storedRS)) + storage.ReplicaSet.Update(ctx, storedRS.Name, rest.DefaultUpdatedObjectInfo(storedRS), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -311,7 +311,7 @@ func TestScaleUpdate(t *testing.T) { }, } - if _, _, err := storage.Scale.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update)); err != nil { + if _, _, err := storage.Scale.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc); err != nil { t.Fatalf("error updating scale %v: %v", update, err) } @@ -327,7 +327,7 @@ func TestScaleUpdate(t *testing.T) { update.ResourceVersion = rs.ResourceVersion update.Spec.Replicas = 15 - if _, _, err = storage.Scale.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update)); err != nil && !errors.IsConflict(err) { + if _, _, err = storage.Scale.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc); err != nil && !errors.IsConflict(err) { t.Fatalf("unexpected error, expecting an update conflict but got %v", err) } } @@ -352,7 +352,7 @@ func TestStatusUpdate(t *testing.T) { }, } - if _, _, err := storage.Status.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update)); err != nil { + if _, _, err := storage.Status.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc); err != nil { t.Fatalf("unexpected error: %v", err) } obj, err := storage.ReplicaSet.Get(ctx, "foo", &metav1.GetOptions{}) diff --git a/pkg/registry/networking/networkpolicy/registry.go b/pkg/registry/networking/networkpolicy/registry.go index 42956e112b7..1ddcd2139f2 100644 --- a/pkg/registry/networking/networkpolicy/registry.go +++ b/pkg/registry/networking/networkpolicy/registry.go @@ -28,8 +28,8 @@ import ( // Registry is an interface for things that know how to store NetworkPolicies. type Registry interface { ListNetworkPolicies(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (*networking.NetworkPolicyList, error) - CreateNetworkPolicy(ctx genericapirequest.Context, np *networking.NetworkPolicy) error - UpdateNetworkPolicy(ctx genericapirequest.Context, np *networking.NetworkPolicy) error + CreateNetworkPolicy(ctx genericapirequest.Context, np *networking.NetworkPolicy, createValidation rest.ValidateObjectFunc) error + UpdateNetworkPolicy(ctx genericapirequest.Context, np *networking.NetworkPolicy, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) error GetNetworkPolicy(ctx genericapirequest.Context, name string, options *metav1.GetOptions) (*networking.NetworkPolicy, error) DeleteNetworkPolicy(ctx genericapirequest.Context, name string) error WatchNetworkPolicies(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (watch.Interface, error) @@ -55,13 +55,13 @@ func (s *storage) ListNetworkPolicies(ctx genericapirequest.Context, options *me return obj.(*networking.NetworkPolicyList), nil } -func (s *storage) CreateNetworkPolicy(ctx genericapirequest.Context, np *networking.NetworkPolicy) error { - _, err := s.Create(ctx, np, false) +func (s *storage) CreateNetworkPolicy(ctx genericapirequest.Context, np *networking.NetworkPolicy, createValidation rest.ValidateObjectFunc) error { + _, err := s.Create(ctx, np, createValidation, false) return err } -func (s *storage) UpdateNetworkPolicy(ctx genericapirequest.Context, np *networking.NetworkPolicy) error { - _, _, err := s.Update(ctx, np.Name, rest.DefaultUpdatedObjectInfo(np)) +func (s *storage) UpdateNetworkPolicy(ctx genericapirequest.Context, np *networking.NetworkPolicy, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) error { + _, _, err := s.Update(ctx, np.Name, rest.DefaultUpdatedObjectInfo(np), createValidation, updateValidation) return err } diff --git a/pkg/registry/policy/poddisruptionbudget/storage/storage.go b/pkg/registry/policy/poddisruptionbudget/storage/storage.go index 9b3da8b8273..40aff589d1d 100644 --- a/pkg/registry/policy/poddisruptionbudget/storage/storage.go +++ b/pkg/registry/policy/poddisruptionbudget/storage/storage.go @@ -78,6 +78,6 @@ func (r *StatusREST) Get(ctx genericapirequest.Context, name string, options *me } // Update alters the status subset of an object. -func (r *StatusREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { - return r.store.Update(ctx, name, objInfo) +func (r *StatusREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (runtime.Object, bool, error) { + return r.store.Update(ctx, name, objInfo, createValidation, updateValidation) } diff --git a/pkg/registry/policy/poddisruptionbudget/storage/storage_test.go b/pkg/registry/policy/poddisruptionbudget/storage/storage_test.go index eb3acbdb924..9846cf86f56 100644 --- a/pkg/registry/policy/poddisruptionbudget/storage/storage_test.go +++ b/pkg/registry/policy/poddisruptionbudget/storage/storage_test.go @@ -41,7 +41,7 @@ func newStorage(t *testing.T) (*REST, *StatusREST, *etcdtesting.EtcdTestServer) // createPodDisruptionBudget is a helper function that returns a PodDisruptionBudget with the updated resource version. func createPodDisruptionBudget(storage *REST, pdb policy.PodDisruptionBudget, t *testing.T) (policy.PodDisruptionBudget, error) { ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), pdb.Namespace) - obj, err := storage.Create(ctx, &pdb, false) + obj, err := storage.Create(ctx, &pdb, rest.ValidateAllObjectFunc, false) if err != nil { t.Errorf("Failed to create PodDisruptionBudget, %v", err) } @@ -109,7 +109,7 @@ func TestStatusUpdate(t *testing.T) { }, } - if _, _, err := statusStorage.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update)); err != nil { + if _, _, err := statusStorage.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc); err != nil { t.Fatalf("unexpected error: %v", err) } obj, err = storage.Get(ctx, "foo", &metav1.GetOptions{}) diff --git a/pkg/registry/rbac/clusterrole/policybased/storage.go b/pkg/registry/rbac/clusterrole/policybased/storage.go index 36650cbb789..6a19e616347 100644 --- a/pkg/registry/rbac/clusterrole/policybased/storage.go +++ b/pkg/registry/rbac/clusterrole/policybased/storage.go @@ -40,9 +40,9 @@ func NewStorage(s rest.StandardStorage, ruleResolver rbacregistryvalidation.Auth return &Storage{s, ruleResolver} } -func (s *Storage) Create(ctx genericapirequest.Context, obj runtime.Object, includeUninitialized bool) (runtime.Object, error) { +func (s *Storage) Create(ctx genericapirequest.Context, obj runtime.Object, createValidatingAdmission rest.ValidateObjectFunc, includeUninitialized bool) (runtime.Object, error) { if rbacregistry.EscalationAllowed(ctx) { - return s.StandardStorage.Create(ctx, obj, includeUninitialized) + return s.StandardStorage.Create(ctx, obj, createValidatingAdmission, includeUninitialized) } clusterRole := obj.(*rbac.ClusterRole) @@ -50,12 +50,12 @@ func (s *Storage) Create(ctx genericapirequest.Context, obj runtime.Object, incl if err := rbacregistryvalidation.ConfirmNoEscalation(ctx, s.ruleResolver, rules); err != nil { return nil, errors.NewForbidden(groupResource, clusterRole.Name, err) } - return s.StandardStorage.Create(ctx, obj, includeUninitialized) + return s.StandardStorage.Create(ctx, obj, createValidatingAdmission, includeUninitialized) } -func (s *Storage) Update(ctx genericapirequest.Context, name string, obj rest.UpdatedObjectInfo) (runtime.Object, bool, error) { +func (s *Storage) Update(ctx genericapirequest.Context, name string, obj rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (runtime.Object, bool, error) { if rbacregistry.EscalationAllowed(ctx) { - return s.StandardStorage.Update(ctx, name, obj) + return s.StandardStorage.Update(ctx, name, obj, createValidation, updateValidation) } nonEscalatingInfo := rest.WrapUpdatedObjectInfo(obj, func(ctx genericapirequest.Context, obj runtime.Object, oldObj runtime.Object) (runtime.Object, error) { @@ -73,5 +73,5 @@ func (s *Storage) Update(ctx genericapirequest.Context, name string, obj rest.Up return obj, nil }) - return s.StandardStorage.Update(ctx, name, nonEscalatingInfo) + return s.StandardStorage.Update(ctx, name, nonEscalatingInfo, createValidation, updateValidation) } diff --git a/pkg/registry/rbac/clusterrole/registry.go b/pkg/registry/rbac/clusterrole/registry.go index 639c04cf03b..b8b1a21b54a 100644 --- a/pkg/registry/rbac/clusterrole/registry.go +++ b/pkg/registry/rbac/clusterrole/registry.go @@ -28,8 +28,8 @@ import ( // Registry is an interface for things that know how to store ClusterRoles. type Registry interface { ListClusterRoles(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (*rbac.ClusterRoleList, error) - CreateClusterRole(ctx genericapirequest.Context, clusterRole *rbac.ClusterRole) error - UpdateClusterRole(ctx genericapirequest.Context, clusterRole *rbac.ClusterRole) error + CreateClusterRole(ctx genericapirequest.Context, clusterRole *rbac.ClusterRole, createValidation rest.ValidateObjectFunc) error + UpdateClusterRole(ctx genericapirequest.Context, clusterRole *rbac.ClusterRole, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) error GetClusterRole(ctx genericapirequest.Context, name string, options *metav1.GetOptions) (*rbac.ClusterRole, error) DeleteClusterRole(ctx genericapirequest.Context, name string) error WatchClusterRoles(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (watch.Interface, error) @@ -55,13 +55,13 @@ func (s *storage) ListClusterRoles(ctx genericapirequest.Context, options *metai return obj.(*rbac.ClusterRoleList), nil } -func (s *storage) CreateClusterRole(ctx genericapirequest.Context, clusterRole *rbac.ClusterRole) error { - _, err := s.Create(ctx, clusterRole, false) +func (s *storage) CreateClusterRole(ctx genericapirequest.Context, clusterRole *rbac.ClusterRole, createValidation rest.ValidateObjectFunc) error { + _, err := s.Create(ctx, clusterRole, createValidation, false) return err } -func (s *storage) UpdateClusterRole(ctx genericapirequest.Context, clusterRole *rbac.ClusterRole) error { - _, _, err := s.Update(ctx, clusterRole.Name, rest.DefaultUpdatedObjectInfo(clusterRole)) +func (s *storage) UpdateClusterRole(ctx genericapirequest.Context, clusterRole *rbac.ClusterRole, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) error { + _, _, err := s.Update(ctx, clusterRole.Name, rest.DefaultUpdatedObjectInfo(clusterRole), createValidation, updateValidation) return err } diff --git a/pkg/registry/rbac/clusterrolebinding/policybased/storage.go b/pkg/registry/rbac/clusterrolebinding/policybased/storage.go index 95130b8c9c0..c6b54f92cc1 100644 --- a/pkg/registry/rbac/clusterrolebinding/policybased/storage.go +++ b/pkg/registry/rbac/clusterrolebinding/policybased/storage.go @@ -44,14 +44,14 @@ func NewStorage(s rest.StandardStorage, authorizer authorizer.Authorizer, ruleRe return &Storage{s, authorizer, ruleResolver} } -func (s *Storage) Create(ctx genericapirequest.Context, obj runtime.Object, includeUninitialized bool) (runtime.Object, error) { +func (s *Storage) Create(ctx genericapirequest.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, includeUninitialized bool) (runtime.Object, error) { if rbacregistry.EscalationAllowed(ctx) { - return s.StandardStorage.Create(ctx, obj, includeUninitialized) + return s.StandardStorage.Create(ctx, obj, createValidation, includeUninitialized) } clusterRoleBinding := obj.(*rbac.ClusterRoleBinding) if rbacregistry.BindingAuthorized(ctx, clusterRoleBinding.RoleRef, metav1.NamespaceNone, s.authorizer) { - return s.StandardStorage.Create(ctx, obj, includeUninitialized) + return s.StandardStorage.Create(ctx, obj, createValidation, includeUninitialized) } rules, err := s.ruleResolver.GetRoleReferenceRules(clusterRoleBinding.RoleRef, metav1.NamespaceNone) @@ -61,12 +61,12 @@ func (s *Storage) Create(ctx genericapirequest.Context, obj runtime.Object, incl if err := rbacregistryvalidation.ConfirmNoEscalation(ctx, s.ruleResolver, rules); err != nil { return nil, errors.NewForbidden(groupResource, clusterRoleBinding.Name, err) } - return s.StandardStorage.Create(ctx, obj, includeUninitialized) + return s.StandardStorage.Create(ctx, obj, createValidation, includeUninitialized) } -func (s *Storage) Update(ctx genericapirequest.Context, name string, obj rest.UpdatedObjectInfo) (runtime.Object, bool, error) { +func (s *Storage) Update(ctx genericapirequest.Context, name string, obj rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (runtime.Object, bool, error) { if rbacregistry.EscalationAllowed(ctx) { - return s.StandardStorage.Update(ctx, name, obj) + return s.StandardStorage.Update(ctx, name, obj, createValidation, updateValidation) } nonEscalatingInfo := rest.WrapUpdatedObjectInfo(obj, func(ctx genericapirequest.Context, obj runtime.Object, oldObj runtime.Object) (runtime.Object, error) { @@ -93,5 +93,5 @@ func (s *Storage) Update(ctx genericapirequest.Context, name string, obj rest.Up return obj, nil }) - return s.StandardStorage.Update(ctx, name, nonEscalatingInfo) + return s.StandardStorage.Update(ctx, name, nonEscalatingInfo, createValidation, updateValidation) } diff --git a/pkg/registry/rbac/clusterrolebinding/registry.go b/pkg/registry/rbac/clusterrolebinding/registry.go index 0a6b04895d1..2a3524e6ed3 100644 --- a/pkg/registry/rbac/clusterrolebinding/registry.go +++ b/pkg/registry/rbac/clusterrolebinding/registry.go @@ -28,8 +28,8 @@ import ( // Registry is an interface for things that know how to store ClusterRoleBindings. type Registry interface { ListClusterRoleBindings(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (*rbac.ClusterRoleBindingList, error) - CreateClusterRoleBinding(ctx genericapirequest.Context, clusterRoleBinding *rbac.ClusterRoleBinding) error - UpdateClusterRoleBinding(ctx genericapirequest.Context, clusterRoleBinding *rbac.ClusterRoleBinding) error + CreateClusterRoleBinding(ctx genericapirequest.Context, clusterRoleBinding *rbac.ClusterRoleBinding, createValidation rest.ValidateObjectFunc) error + UpdateClusterRoleBinding(ctx genericapirequest.Context, clusterRoleBinding *rbac.ClusterRoleBinding, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) error GetClusterRoleBinding(ctx genericapirequest.Context, name string, options *metav1.GetOptions) (*rbac.ClusterRoleBinding, error) DeleteClusterRoleBinding(ctx genericapirequest.Context, name string) error WatchClusterRoleBindings(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (watch.Interface, error) @@ -55,13 +55,13 @@ func (s *storage) ListClusterRoleBindings(ctx genericapirequest.Context, options return obj.(*rbac.ClusterRoleBindingList), nil } -func (s *storage) CreateClusterRoleBinding(ctx genericapirequest.Context, clusterRoleBinding *rbac.ClusterRoleBinding) error { - _, err := s.Create(ctx, clusterRoleBinding, false) +func (s *storage) CreateClusterRoleBinding(ctx genericapirequest.Context, clusterRoleBinding *rbac.ClusterRoleBinding, createValidation rest.ValidateObjectFunc) error { + _, err := s.Create(ctx, clusterRoleBinding, createValidation, false) return err } -func (s *storage) UpdateClusterRoleBinding(ctx genericapirequest.Context, clusterRoleBinding *rbac.ClusterRoleBinding) error { - _, _, err := s.Update(ctx, clusterRoleBinding.Name, rest.DefaultUpdatedObjectInfo(clusterRoleBinding)) +func (s *storage) UpdateClusterRoleBinding(ctx genericapirequest.Context, clusterRoleBinding *rbac.ClusterRoleBinding, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) error { + _, _, err := s.Update(ctx, clusterRoleBinding.Name, rest.DefaultUpdatedObjectInfo(clusterRoleBinding), createValidation, updateValidation) return err } diff --git a/pkg/registry/rbac/role/policybased/storage.go b/pkg/registry/rbac/role/policybased/storage.go index 1f2443d2b09..faf973731c8 100644 --- a/pkg/registry/rbac/role/policybased/storage.go +++ b/pkg/registry/rbac/role/policybased/storage.go @@ -40,9 +40,9 @@ func NewStorage(s rest.StandardStorage, ruleResolver rbacregistryvalidation.Auth return &Storage{s, ruleResolver} } -func (s *Storage) Create(ctx genericapirequest.Context, obj runtime.Object, includeUninitialized bool) (runtime.Object, error) { +func (s *Storage) Create(ctx genericapirequest.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, includeUninitialized bool) (runtime.Object, error) { if rbacregistry.EscalationAllowed(ctx) { - return s.StandardStorage.Create(ctx, obj, includeUninitialized) + return s.StandardStorage.Create(ctx, obj, createValidation, includeUninitialized) } role := obj.(*rbac.Role) @@ -50,12 +50,12 @@ func (s *Storage) Create(ctx genericapirequest.Context, obj runtime.Object, incl if err := rbacregistryvalidation.ConfirmNoEscalation(ctx, s.ruleResolver, rules); err != nil { return nil, errors.NewForbidden(groupResource, role.Name, err) } - return s.StandardStorage.Create(ctx, obj, includeUninitialized) + return s.StandardStorage.Create(ctx, obj, createValidation, includeUninitialized) } -func (s *Storage) Update(ctx genericapirequest.Context, name string, obj rest.UpdatedObjectInfo) (runtime.Object, bool, error) { +func (s *Storage) Update(ctx genericapirequest.Context, name string, obj rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (runtime.Object, bool, error) { if rbacregistry.EscalationAllowed(ctx) { - return s.StandardStorage.Update(ctx, name, obj) + return s.StandardStorage.Update(ctx, name, obj, createValidation, updateValidation) } nonEscalatingInfo := rest.WrapUpdatedObjectInfo(obj, func(ctx genericapirequest.Context, obj runtime.Object, oldObj runtime.Object) (runtime.Object, error) { @@ -73,5 +73,5 @@ func (s *Storage) Update(ctx genericapirequest.Context, name string, obj rest.Up return obj, nil }) - return s.StandardStorage.Update(ctx, name, nonEscalatingInfo) + return s.StandardStorage.Update(ctx, name, nonEscalatingInfo, createValidation, updateValidation) } diff --git a/pkg/registry/rbac/role/registry.go b/pkg/registry/rbac/role/registry.go index 7d7a47f8416..bbf26b5e23d 100644 --- a/pkg/registry/rbac/role/registry.go +++ b/pkg/registry/rbac/role/registry.go @@ -28,8 +28,8 @@ import ( // Registry is an interface for things that know how to store Roles. type Registry interface { ListRoles(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (*rbac.RoleList, error) - CreateRole(ctx genericapirequest.Context, role *rbac.Role) error - UpdateRole(ctx genericapirequest.Context, role *rbac.Role) error + CreateRole(ctx genericapirequest.Context, role *rbac.Role, createValidation rest.ValidateObjectFunc) error + UpdateRole(ctx genericapirequest.Context, role *rbac.Role, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) error GetRole(ctx genericapirequest.Context, name string, options *metav1.GetOptions) (*rbac.Role, error) DeleteRole(ctx genericapirequest.Context, name string) error WatchRoles(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (watch.Interface, error) @@ -55,13 +55,14 @@ func (s *storage) ListRoles(ctx genericapirequest.Context, options *metainternal return obj.(*rbac.RoleList), nil } -func (s *storage) CreateRole(ctx genericapirequest.Context, role *rbac.Role) error { - _, err := s.Create(ctx, role, false) +func (s *storage) CreateRole(ctx genericapirequest.Context, role *rbac.Role, createValidation rest.ValidateObjectFunc) error { + _, err := s.Create(ctx, role, createValidation, false) return err } -func (s *storage) UpdateRole(ctx genericapirequest.Context, role *rbac.Role) error { - _, _, err := s.Update(ctx, role.Name, rest.DefaultUpdatedObjectInfo(role)) +func (s *storage) UpdateRole(ctx genericapirequest.Context, role *rbac.Role, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) error { + // TODO: any admission? + _, _, err := s.Update(ctx, role.Name, rest.DefaultUpdatedObjectInfo(role), createValidation, updateValidation) return err } diff --git a/pkg/registry/rbac/rolebinding/policybased/storage.go b/pkg/registry/rbac/rolebinding/policybased/storage.go index 5f775d5c491..0a78ce3665e 100644 --- a/pkg/registry/rbac/rolebinding/policybased/storage.go +++ b/pkg/registry/rbac/rolebinding/policybased/storage.go @@ -43,9 +43,9 @@ func NewStorage(s rest.StandardStorage, authorizer authorizer.Authorizer, ruleRe return &Storage{s, authorizer, ruleResolver} } -func (s *Storage) Create(ctx genericapirequest.Context, obj runtime.Object, includeUninitialized bool) (runtime.Object, error) { +func (s *Storage) Create(ctx genericapirequest.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, includeUninitialized bool) (runtime.Object, error) { if rbacregistry.EscalationAllowed(ctx) { - return s.StandardStorage.Create(ctx, obj, includeUninitialized) + return s.StandardStorage.Create(ctx, obj, createValidation, includeUninitialized) } // Get the namespace from the context (populated from the URL). @@ -57,7 +57,7 @@ func (s *Storage) Create(ctx genericapirequest.Context, obj runtime.Object, incl roleBinding := obj.(*rbac.RoleBinding) if rbacregistry.BindingAuthorized(ctx, roleBinding.RoleRef, namespace, s.authorizer) { - return s.StandardStorage.Create(ctx, obj, includeUninitialized) + return s.StandardStorage.Create(ctx, obj, createValidation, includeUninitialized) } rules, err := s.ruleResolver.GetRoleReferenceRules(roleBinding.RoleRef, namespace) @@ -67,12 +67,12 @@ func (s *Storage) Create(ctx genericapirequest.Context, obj runtime.Object, incl if err := rbacregistryvalidation.ConfirmNoEscalation(ctx, s.ruleResolver, rules); err != nil { return nil, errors.NewForbidden(groupResource, roleBinding.Name, err) } - return s.StandardStorage.Create(ctx, obj, includeUninitialized) + return s.StandardStorage.Create(ctx, obj, createValidation, includeUninitialized) } -func (s *Storage) Update(ctx genericapirequest.Context, name string, obj rest.UpdatedObjectInfo) (runtime.Object, bool, error) { +func (s *Storage) Update(ctx genericapirequest.Context, name string, obj rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (runtime.Object, bool, error) { if rbacregistry.EscalationAllowed(ctx) { - return s.StandardStorage.Update(ctx, name, obj) + return s.StandardStorage.Update(ctx, name, obj, createValidation, updateValidation) } nonEscalatingInfo := rest.WrapUpdatedObjectInfo(obj, func(ctx genericapirequest.Context, obj runtime.Object, oldObj runtime.Object) (runtime.Object, error) { @@ -106,5 +106,5 @@ func (s *Storage) Update(ctx genericapirequest.Context, name string, obj rest.Up return obj, nil }) - return s.StandardStorage.Update(ctx, name, nonEscalatingInfo) + return s.StandardStorage.Update(ctx, name, nonEscalatingInfo, createValidation, updateValidation) } diff --git a/pkg/registry/rbac/rolebinding/registry.go b/pkg/registry/rbac/rolebinding/registry.go index 5c4a0270412..c708d088bb0 100644 --- a/pkg/registry/rbac/rolebinding/registry.go +++ b/pkg/registry/rbac/rolebinding/registry.go @@ -28,8 +28,8 @@ import ( // Registry is an interface for things that know how to store RoleBindings. type Registry interface { ListRoleBindings(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (*rbac.RoleBindingList, error) - CreateRoleBinding(ctx genericapirequest.Context, roleBinding *rbac.RoleBinding) error - UpdateRoleBinding(ctx genericapirequest.Context, roleBinding *rbac.RoleBinding) error + CreateRoleBinding(ctx genericapirequest.Context, roleBinding *rbac.RoleBinding, createValidation rest.ValidateObjectFunc) error + UpdateRoleBinding(ctx genericapirequest.Context, roleBinding *rbac.RoleBinding, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) error GetRoleBinding(ctx genericapirequest.Context, name string, options *metav1.GetOptions) (*rbac.RoleBinding, error) DeleteRoleBinding(ctx genericapirequest.Context, name string) error WatchRoleBindings(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (watch.Interface, error) @@ -55,14 +55,14 @@ func (s *storage) ListRoleBindings(ctx genericapirequest.Context, options *metai return obj.(*rbac.RoleBindingList), nil } -func (s *storage) CreateRoleBinding(ctx genericapirequest.Context, roleBinding *rbac.RoleBinding) error { +func (s *storage) CreateRoleBinding(ctx genericapirequest.Context, roleBinding *rbac.RoleBinding, createValidation rest.ValidateObjectFunc) error { // TODO(ericchiang): add additional validation - _, err := s.Create(ctx, roleBinding, false) + _, err := s.Create(ctx, roleBinding, createValidation, false) return err } -func (s *storage) UpdateRoleBinding(ctx genericapirequest.Context, roleBinding *rbac.RoleBinding) error { - _, _, err := s.Update(ctx, roleBinding.Name, rest.DefaultUpdatedObjectInfo(roleBinding)) +func (s *storage) UpdateRoleBinding(ctx genericapirequest.Context, roleBinding *rbac.RoleBinding, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) error { + _, _, err := s.Update(ctx, roleBinding.Name, rest.DefaultUpdatedObjectInfo(roleBinding), createValidation, updateValidation) return err } diff --git a/pkg/registry/registrytest/endpoint.go b/pkg/registry/registrytest/endpoint.go index cf0fc4877ad..6bf85b0c7d2 100644 --- a/pkg/registry/registrytest/endpoint.go +++ b/pkg/registry/registrytest/endpoint.go @@ -25,6 +25,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/watch" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" + "k8s.io/apiserver/pkg/registry/rest" "k8s.io/kubernetes/pkg/api" ) @@ -66,7 +67,7 @@ func (e *EndpointRegistry) WatchEndpoints(ctx genericapirequest.Context, options return nil, fmt.Errorf("unimplemented!") } -func (e *EndpointRegistry) UpdateEndpoints(ctx genericapirequest.Context, endpoints *api.Endpoints) error { +func (e *EndpointRegistry) UpdateEndpoints(ctx genericapirequest.Context, endpoints *api.Endpoints, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) error { // TODO: support namespaces in this mock e.lock.Lock() defer e.lock.Unlock() diff --git a/pkg/registry/registrytest/service.go b/pkg/registry/registrytest/service.go index 8b5313a03fa..38ba722281d 100644 --- a/pkg/registry/registrytest/service.go +++ b/pkg/registry/registrytest/service.go @@ -23,6 +23,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/watch" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" + "k8s.io/apiserver/pkg/registry/rest" "k8s.io/kubernetes/pkg/api" ) @@ -72,7 +73,7 @@ func (r *ServiceRegistry) ListServices(ctx genericapirequest.Context, options *m return res, r.Err } -func (r *ServiceRegistry) CreateService(ctx genericapirequest.Context, svc *api.Service) (*api.Service, error) { +func (r *ServiceRegistry) CreateService(ctx genericapirequest.Context, svc *api.Service, createValidation rest.ValidateObjectFunc) (*api.Service, error) { r.mu.Lock() defer r.mu.Unlock() @@ -99,7 +100,7 @@ func (r *ServiceRegistry) DeleteService(ctx genericapirequest.Context, id string return r.Err } -func (r *ServiceRegistry) UpdateService(ctx genericapirequest.Context, svc *api.Service) (*api.Service, error) { +func (r *ServiceRegistry) UpdateService(ctx genericapirequest.Context, svc *api.Service, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (*api.Service, error) { r.mu.Lock() defer r.mu.Unlock() diff --git a/pkg/registry/scheduling/priorityclass/registry.go b/pkg/registry/scheduling/priorityclass/registry.go index 9c98af93026..5e4c44dc161 100644 --- a/pkg/registry/scheduling/priorityclass/registry.go +++ b/pkg/registry/scheduling/priorityclass/registry.go @@ -28,8 +28,8 @@ import ( // Registry is an interface for things that know how to store PriorityClass. type Registry interface { ListPriorityClasses(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (*scheduling.PriorityClassList, error) - CreatePriorityClass(ctx genericapirequest.Context, pc *scheduling.PriorityClass) error - UpdatePriorityClass(ctx genericapirequest.Context, pc *scheduling.PriorityClass) error + CreatePriorityClass(ctx genericapirequest.Context, pc *scheduling.PriorityClass, createValidation rest.ValidateObjectFunc) error + UpdatePriorityClass(ctx genericapirequest.Context, pc *scheduling.PriorityClass, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) error GetPriorityClass(ctx genericapirequest.Context, name string, options *metav1.GetOptions) (*scheduling.PriorityClass, error) DeletePriorityClass(ctx genericapirequest.Context, name string) error WatchPriorityClasses(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (watch.Interface, error) @@ -55,13 +55,13 @@ func (s *storage) ListPriorityClasses(ctx genericapirequest.Context, options *me return obj.(*scheduling.PriorityClassList), nil } -func (s *storage) CreatePriorityClass(ctx genericapirequest.Context, pc *scheduling.PriorityClass) error { - _, err := s.Create(ctx, pc, false) +func (s *storage) CreatePriorityClass(ctx genericapirequest.Context, pc *scheduling.PriorityClass, createValidation rest.ValidateObjectFunc) error { + _, err := s.Create(ctx, pc, createValidation, false) return err } -func (s *storage) UpdatePriorityClass(ctx genericapirequest.Context, pc *scheduling.PriorityClass) error { - _, _, err := s.Update(ctx, pc.Name, rest.DefaultUpdatedObjectInfo(pc)) +func (s *storage) UpdatePriorityClass(ctx genericapirequest.Context, pc *scheduling.PriorityClass, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) error { + _, _, err := s.Update(ctx, pc.Name, rest.DefaultUpdatedObjectInfo(pc), createValidation, updateValidation) return err } diff --git a/pkg/registry/settings/podpreset/registry.go b/pkg/registry/settings/podpreset/registry.go index 5af80a3aa66..74b056d0e67 100644 --- a/pkg/registry/settings/podpreset/registry.go +++ b/pkg/registry/settings/podpreset/registry.go @@ -28,8 +28,8 @@ import ( // Registry is an interface for things that know how to store PodPresets. type Registry interface { ListPodPresets(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (*settings.PodPresetList, error) - CreatePodPreset(ctx genericapirequest.Context, pp *settings.PodPreset) error - UpdatePodPreset(ctx genericapirequest.Context, pp *settings.PodPreset) error + CreatePodPreset(ctx genericapirequest.Context, pp *settings.PodPreset, createValidation rest.ValidateObjectFunc) error + UpdatePodPreset(ctx genericapirequest.Context, pp *settings.PodPreset, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) error GetPodPreset(ctx genericapirequest.Context, ppID string, options *metav1.GetOptions) (*settings.PodPreset, error) DeletePodPreset(ctx genericapirequest.Context, ppID string) error WatchPodPresets(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (watch.Interface, error) @@ -55,13 +55,13 @@ func (s *storage) ListPodPresets(ctx genericapirequest.Context, options *metaint return obj.(*settings.PodPresetList), nil } -func (s *storage) CreatePodPreset(ctx genericapirequest.Context, pp *settings.PodPreset) error { - _, err := s.Create(ctx, pp, false) +func (s *storage) CreatePodPreset(ctx genericapirequest.Context, pp *settings.PodPreset, createValidation rest.ValidateObjectFunc) error { + _, err := s.Create(ctx, pp, createValidation, false) return err } -func (s *storage) UpdatePodPreset(ctx genericapirequest.Context, pp *settings.PodPreset) error { - _, _, err := s.Update(ctx, pp.Name, rest.DefaultUpdatedObjectInfo(pp)) +func (s *storage) UpdatePodPreset(ctx genericapirequest.Context, pp *settings.PodPreset, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) error { + _, _, err := s.Update(ctx, pp.Name, rest.DefaultUpdatedObjectInfo(pp), createValidation, updateValidation) return err } diff --git a/plugin/pkg/admission/admit/admission.go b/plugin/pkg/admission/admit/admission.go index 8605b9d2bf8..9c33855f52c 100644 --- a/plugin/pkg/admission/admit/admission.go +++ b/plugin/pkg/admission/admit/admission.go @@ -38,6 +38,11 @@ func (AlwaysAdmit) Admit(a admission.Attributes) (err error) { return nil } +// Validate makes an admission decision based on the request attributes. It is NOT allowed to mutate. +func (AlwaysAdmit) Validate(a admission.Attributes) (err error) { + return nil +} + // Handles returns true if this admission controller can handle the given operation // where operation can be one of CREATE, UPDATE, DELETE, or CONNECT func (AlwaysAdmit) Handles(operation admission.Operation) bool { diff --git a/plugin/pkg/admission/exec/admission.go b/plugin/pkg/admission/exec/admission.go index bde9e9db999..b5bea35de4a 100644 --- a/plugin/pkg/admission/exec/admission.go +++ b/plugin/pkg/admission/exec/admission.go @@ -135,8 +135,8 @@ func (d *DenyExec) SetInternalKubeClientSet(client internalclientset.Interface) d.client = client } -// Validate implements the Validator interface. -func (d *DenyExec) Validate() error { +// ValidateInitialization implements the InitializationValidator interface. +func (d *DenyExec) ValidateInitialization() error { if d.client == nil { return fmt.Errorf("missing client") } diff --git a/plugin/pkg/admission/exec/admission_test.go b/plugin/pkg/admission/exec/admission_test.go index f48ebcc7ae1..4a178f412fd 100644 --- a/plugin/pkg/admission/exec/admission_test.go +++ b/plugin/pkg/admission/exec/admission_test.go @@ -118,7 +118,7 @@ func testAdmission(t *testing.T, pod *api.Pod, handler *DenyExec, shouldAccept b }) handler.SetInternalKubeClientSet(mockClient) - admission.Validate(handler) + admission.ValidateInitialization(handler) // pods/exec { diff --git a/plugin/pkg/admission/gc/gc_admission.go b/plugin/pkg/admission/gc/gc_admission.go index 5c4287925b6..2963fad05d2 100644 --- a/plugin/pkg/admission/gc/gc_admission.go +++ b/plugin/pkg/admission/gc/gc_admission.go @@ -260,7 +260,7 @@ func (a *gcPermissionsEnforcement) SetRESTMapper(restMapper meta.RESTMapper) { a.restMapper = restMapper } -func (a *gcPermissionsEnforcement) Validate() error { +func (a *gcPermissionsEnforcement) ValidateInitialization() error { if a.authorizer == nil { return fmt.Errorf("missing authorizer") } diff --git a/plugin/pkg/admission/initialresources/admission_test.go b/plugin/pkg/admission/initialresources/admission_test.go index 0b710210181..408fbaab47d 100644 --- a/plugin/pkg/admission/initialresources/admission_test.go +++ b/plugin/pkg/admission/initialresources/admission_test.go @@ -110,7 +110,7 @@ func expectNoAnnotation(t *testing.T, pod *api.Pod) { } } -func admit(t *testing.T, ir admission.Interface, pods []*api.Pod) { +func admit(t *testing.T, ir admission.MutationInterface, pods []*api.Pod) { for i := range pods { p := pods[i] @@ -123,7 +123,7 @@ func admit(t *testing.T, ir admission.Interface, pods []*api.Pod) { } } -func testAdminScenarios(t *testing.T, ir admission.Interface, p *api.Pod) { +func testAdminScenarios(t *testing.T, ir admission.MutationInterface, p *api.Pod) { podKind := api.Kind("Pod").WithVersion("version") podRes := api.Resource("pods").WithVersion("version") @@ -151,7 +151,7 @@ func testAdminScenarios(t *testing.T, ir admission.Interface, p *api.Pod) { } } -func performTest(t *testing.T, ir admission.Interface) { +func performTest(t *testing.T, ir admission.MutationInterface) { pods := getPods() admit(t, ir, pods) testAdminScenarios(t, ir, pods[0]) diff --git a/plugin/pkg/admission/limitranger/admission.go b/plugin/pkg/admission/limitranger/admission.go index 356a53de289..676f459eb27 100644 --- a/plugin/pkg/admission/limitranger/admission.go +++ b/plugin/pkg/admission/limitranger/admission.go @@ -75,7 +75,7 @@ func (l *LimitRanger) SetInternalKubeInformerFactory(f informers.SharedInformerF l.lister = limitRangeInformer.Lister() } -func (l *LimitRanger) Validate() error { +func (l *LimitRanger) ValidateInitialization() error { if l.lister == nil { return fmt.Errorf("missing limitRange lister") } diff --git a/plugin/pkg/admission/limitranger/admission_test.go b/plugin/pkg/admission/limitranger/admission_test.go index dcd4c0db548..4ed29df8df2 100644 --- a/plugin/pkg/admission/limitranger/admission_test.go +++ b/plugin/pkg/admission/limitranger/admission_test.go @@ -746,7 +746,7 @@ func newHandlerForTest(c clientset.Interface) (*LimitRanger, informers.SharedInf } pluginInitializer := kubeadmission.NewPluginInitializer(c, f, nil, nil, nil, nil, nil) pluginInitializer.Initialize(handler) - err = admission.Validate(handler) + err = admission.ValidateInitialization(handler) return handler, f, err } diff --git a/plugin/pkg/admission/namespace/autoprovision/admission.go b/plugin/pkg/admission/namespace/autoprovision/admission.go index 7ee90198344..a963c06c43d 100644 --- a/plugin/pkg/admission/namespace/autoprovision/admission.go +++ b/plugin/pkg/admission/namespace/autoprovision/admission.go @@ -106,8 +106,8 @@ func (p *Provision) SetInternalKubeInformerFactory(f informers.SharedInformerFac p.SetReadyFunc(namespaceInformer.Informer().HasSynced) } -// Validate implements the Validator interface. -func (p *Provision) Validate() error { +// ValidateInitialization implements the InitializationValidator interface. +func (p *Provision) ValidateInitialization() error { if p.namespaceLister == nil { return fmt.Errorf("missing namespaceLister") } diff --git a/plugin/pkg/admission/namespace/autoprovision/admission_test.go b/plugin/pkg/admission/namespace/autoprovision/admission_test.go index d95a4be40e0..bbe8524dae3 100644 --- a/plugin/pkg/admission/namespace/autoprovision/admission_test.go +++ b/plugin/pkg/admission/namespace/autoprovision/admission_test.go @@ -35,12 +35,12 @@ import ( ) // newHandlerForTest returns the admission controller configured for testing. -func newHandlerForTest(c clientset.Interface) (admission.Interface, informers.SharedInformerFactory, error) { +func newHandlerForTest(c clientset.Interface) (admission.MutationInterface, informers.SharedInformerFactory, error) { f := informers.NewSharedInformerFactory(c, 5*time.Minute) handler := NewProvision() pluginInitializer := kubeadmission.NewPluginInitializer(c, f, nil, nil, nil, nil, nil) pluginInitializer.Initialize(handler) - err := admission.Validate(handler) + err := admission.ValidateInitialization(handler) return handler, f, err } diff --git a/plugin/pkg/admission/namespace/exists/admission.go b/plugin/pkg/admission/namespace/exists/admission.go index bed404a83e4..5a6c29751e3 100644 --- a/plugin/pkg/admission/namespace/exists/admission.go +++ b/plugin/pkg/admission/namespace/exists/admission.go @@ -101,8 +101,8 @@ func (e *Exists) SetInternalKubeInformerFactory(f informers.SharedInformerFactor e.SetReadyFunc(namespaceInformer.Informer().HasSynced) } -// Validate implements the Validator interface. -func (e *Exists) Validate() error { +// ValidateInitialization implements the InitializationValidator interface. +func (e *Exists) ValidateInitialization() error { if e.namespaceLister == nil { return fmt.Errorf("missing namespaceLister") } diff --git a/plugin/pkg/admission/namespace/exists/admission_test.go b/plugin/pkg/admission/namespace/exists/admission_test.go index f0655fb46d9..4b8d54c025a 100644 --- a/plugin/pkg/admission/namespace/exists/admission_test.go +++ b/plugin/pkg/admission/namespace/exists/admission_test.go @@ -34,12 +34,12 @@ import ( ) // newHandlerForTest returns the admission controller configured for testing. -func newHandlerForTest(c clientset.Interface) (admission.Interface, informers.SharedInformerFactory, error) { +func newHandlerForTest(c clientset.Interface) (admission.MutationInterface, informers.SharedInformerFactory, error) { f := informers.NewSharedInformerFactory(c, 5*time.Minute) handler := NewExists() pluginInitializer := kubeadmission.NewPluginInitializer(c, f, nil, nil, nil, nil, nil) pluginInitializer.Initialize(handler) - err := admission.Validate(handler) + err := admission.ValidateInitialization(handler) return handler, f, err } diff --git a/plugin/pkg/admission/noderestriction/admission.go b/plugin/pkg/admission/noderestriction/admission.go index f1f77a95569..86c90a58838 100644 --- a/plugin/pkg/admission/noderestriction/admission.go +++ b/plugin/pkg/admission/noderestriction/admission.go @@ -69,7 +69,7 @@ func (p *nodePlugin) SetInternalKubeClientSet(f internalclientset.Interface) { p.podsGetter = f.Core() } -func (p *nodePlugin) Validate() error { +func (p *nodePlugin) ValidateInitialization() error { if p.nodeIdentifier == nil { return fmt.Errorf("%s requires a node identifier", PluginName) } diff --git a/plugin/pkg/admission/persistentvolume/resize/admission.go b/plugin/pkg/admission/persistentvolume/resize/admission.go index eb7b11cdac0..4b3ea07d5b5 100644 --- a/plugin/pkg/admission/persistentvolume/resize/admission.go +++ b/plugin/pkg/admission/persistentvolume/resize/admission.go @@ -68,8 +68,8 @@ func (pvcr *persistentVolumeClaimResize) SetInternalKubeInformerFactory(f inform }) } -// Validate ensures lister is set. -func (pvcr *persistentVolumeClaimResize) Validate() error { +// ValidateInitialization ensures lister is set. +func (pvcr *persistentVolumeClaimResize) ValidateInitialization() error { if pvcr.pvLister == nil { return fmt.Errorf("missing persistent volume lister") } diff --git a/plugin/pkg/admission/persistentvolume/resize/admission_test.go b/plugin/pkg/admission/persistentvolume/resize/admission_test.go index 237b053822d..f30e53af917 100644 --- a/plugin/pkg/admission/persistentvolume/resize/admission_test.go +++ b/plugin/pkg/admission/persistentvolume/resize/admission_test.go @@ -272,7 +272,7 @@ func TestPVCResizeAdmission(t *testing.T) { ctrl := newPlugin() informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc()) ctrl.SetInternalKubeInformerFactory(informerFactory) - err := ctrl.Validate() + err := ctrl.ValidateInitialization() if err != nil { t.Fatalf("neither pv lister nor storageclass lister can be nil") } diff --git a/plugin/pkg/admission/podnodeselector/admission.go b/plugin/pkg/admission/podnodeselector/admission.go index 469286f1e9b..38763d5eac6 100644 --- a/plugin/pkg/admission/podnodeselector/admission.go +++ b/plugin/pkg/admission/podnodeselector/admission.go @@ -183,7 +183,7 @@ func (p *podNodeSelector) SetInternalKubeInformerFactory(f informers.SharedInfor p.SetReadyFunc(namespaceInformer.Informer().HasSynced) } -func (p *podNodeSelector) Validate() error { +func (p *podNodeSelector) ValidateInitialization() error { if p.namespaceLister == nil { return fmt.Errorf("missing namespaceLister") } diff --git a/plugin/pkg/admission/podnodeselector/admission_test.go b/plugin/pkg/admission/podnodeselector/admission_test.go index 6dc9b5a7029..331d824ee79 100644 --- a/plugin/pkg/admission/podnodeselector/admission_test.go +++ b/plugin/pkg/admission/podnodeselector/admission_test.go @@ -243,6 +243,6 @@ func newHandlerForTest(c clientset.Interface) (*podNodeSelector, informers.Share handler := NewPodNodeSelector(nil) pluginInitializer := kubeadmission.NewPluginInitializer(c, f, nil, nil, nil, nil, nil) pluginInitializer.Initialize(handler) - err := admission.Validate(handler) + err := admission.ValidateInitialization(handler) return handler, f, err } diff --git a/plugin/pkg/admission/podpreset/admission.go b/plugin/pkg/admission/podpreset/admission.go index 4bd33c7971c..60554fb7c1e 100644 --- a/plugin/pkg/admission/podpreset/admission.go +++ b/plugin/pkg/admission/podpreset/admission.go @@ -69,7 +69,7 @@ func NewPlugin() *podPresetPlugin { } } -func (plugin *podPresetPlugin) Validate() error { +func (plugin *podPresetPlugin) ValidateInitialization() error { if plugin.client == nil { return fmt.Errorf("%s requires a client", pluginName) } diff --git a/plugin/pkg/admission/podpreset/admission_test.go b/plugin/pkg/admission/podpreset/admission_test.go index bede2f6ca26..f7940986fda 100644 --- a/plugin/pkg/admission/podpreset/admission_test.go +++ b/plugin/pkg/admission/podpreset/admission_test.go @@ -393,7 +393,7 @@ func TestMergeVolumes(t *testing.T) { // NewTestAdmission provides an admission plugin with test implementations of internal structs. It uses // an authorizer that always returns true. -func NewTestAdmission(lister settingslisters.PodPresetLister, objects ...runtime.Object) kadmission.Interface { +func NewTestAdmission(lister settingslisters.PodPresetLister, objects ...runtime.Object) kadmission.MutationInterface { // Build a test client that the admission plugin can use to look up the service account missing from its cache client := fake.NewSimpleClientset(objects...) diff --git a/plugin/pkg/admission/podtolerationrestriction/admission.go b/plugin/pkg/admission/podtolerationrestriction/admission.go index ddd952b0494..15db790a46a 100644 --- a/plugin/pkg/admission/podtolerationrestriction/admission.go +++ b/plugin/pkg/admission/podtolerationrestriction/admission.go @@ -202,7 +202,7 @@ func (p *podTolerationsPlugin) SetInternalKubeInformerFactory(f informers.Shared } -func (p *podTolerationsPlugin) Validate() error { +func (p *podTolerationsPlugin) ValidateInitialization() error { if p.namespaceLister == nil { return fmt.Errorf("missing namespaceLister") } diff --git a/plugin/pkg/admission/podtolerationrestriction/admission_test.go b/plugin/pkg/admission/podtolerationrestriction/admission_test.go index fd7fcd2af9a..5cc9e3a1834 100644 --- a/plugin/pkg/admission/podtolerationrestriction/admission_test.go +++ b/plugin/pkg/admission/podtolerationrestriction/admission_test.go @@ -363,6 +363,6 @@ func newHandlerForTest(c clientset.Interface) (*podTolerationsPlugin, informers. handler := NewPodTolerationsPlugin(pluginConfig) pluginInitializer := kubeadmission.NewPluginInitializer(c, f, nil, nil, nil, nil, nil) pluginInitializer.Initialize(handler) - err = admission.Validate(handler) + err = admission.ValidateInitialization(handler) return handler, f, err } diff --git a/plugin/pkg/admission/priority/admission.go b/plugin/pkg/admission/priority/admission.go index 5105809e909..eddea6f446a 100644 --- a/plugin/pkg/admission/priority/admission.go +++ b/plugin/pkg/admission/priority/admission.go @@ -74,8 +74,8 @@ func NewPlugin() *PriorityPlugin { } } -// Validate implements the Validator interface. -func (p *PriorityPlugin) Validate() error { +// ValidateInitialization implements the InitializationValidator interface. +func (p *PriorityPlugin) ValidateInitialization() error { if p.client == nil { return fmt.Errorf("%s requires a client", pluginName) } diff --git a/plugin/pkg/admission/resourcequota/admission.go b/plugin/pkg/admission/resourcequota/admission.go index 26ddb02d1a5..0cffb684ffb 100644 --- a/plugin/pkg/admission/resourcequota/admission.go +++ b/plugin/pkg/admission/resourcequota/admission.go @@ -100,8 +100,8 @@ func (a *QuotaAdmission) SetQuotaConfiguration(c quota.Configuration) { a.evaluator = NewQuotaEvaluator(a.quotaAccessor, a.quotaConfiguration, nil, a.config, a.numEvaluators, a.stopCh) } -// Validate ensures an authorizer is set. -func (a *QuotaAdmission) Validate() error { +// ValidateInitialization ensures an authorizer is set. +func (a *QuotaAdmission) ValidateInitialization() error { if a.quotaAccessor == nil { return fmt.Errorf("missing quotaAccessor") } diff --git a/plugin/pkg/admission/security/podsecuritypolicy/admission.go b/plugin/pkg/admission/security/podsecuritypolicy/admission.go index 19de55b93e1..fbc1cc3761e 100644 --- a/plugin/pkg/admission/security/podsecuritypolicy/admission.go +++ b/plugin/pkg/admission/security/podsecuritypolicy/admission.go @@ -72,8 +72,8 @@ func (plugin *podSecurityPolicyPlugin) SetAuthorizer(authz authorizer.Authorizer plugin.authz = authz } -// Validate ensures an authorizer is set. -func (plugin *podSecurityPolicyPlugin) Validate() error { +// ValidateInitialization ensures an authorizer is set. +func (plugin *podSecurityPolicyPlugin) ValidateInitialization() error { if plugin.authz == nil { return fmt.Errorf("%s requires an authorizer", PluginName) } diff --git a/plugin/pkg/admission/security/podsecuritypolicy/admission_test.go b/plugin/pkg/admission/security/podsecuritypolicy/admission_test.go index 4d082ae98bb..e66e687becc 100644 --- a/plugin/pkg/admission/security/podsecuritypolicy/admission_test.go +++ b/plugin/pkg/admission/security/podsecuritypolicy/admission_test.go @@ -49,7 +49,7 @@ const defaultContainerName = "test-c" // NewTestAdmission provides an admission plugin with test implementations of internal structs. It uses // an authorizer that always returns true. -func NewTestAdmission(lister extensionslisters.PodSecurityPolicyLister) kadmission.Interface { +func NewTestAdmission(lister extensionslisters.PodSecurityPolicyLister) kadmission.MutationInterface { return &podSecurityPolicyPlugin{ Handler: kadmission.NewHandler(kadmission.Create, kadmission.Update), strategyFactory: kpsp.NewSimpleStrategyFactory(), diff --git a/plugin/pkg/admission/serviceaccount/admission.go b/plugin/pkg/admission/serviceaccount/admission.go index 9e319d26d6c..0876bb3b6cd 100644 --- a/plugin/pkg/admission/serviceaccount/admission.go +++ b/plugin/pkg/admission/serviceaccount/admission.go @@ -117,8 +117,8 @@ func (a *serviceAccount) SetInternalKubeInformerFactory(f informers.SharedInform }) } -// Validate ensures an authorizer is set. -func (a *serviceAccount) Validate() error { +// ValidateInitialization ensures an authorizer is set. +func (a *serviceAccount) ValidateInitialization() error { if a.client == nil { return fmt.Errorf("missing client") } diff --git a/plugin/pkg/admission/storageclass/setdefault/admission.go b/plugin/pkg/admission/storageclass/setdefault/admission.go index 2e218bb0eda..39aaaac00a0 100644 --- a/plugin/pkg/admission/storageclass/setdefault/admission.go +++ b/plugin/pkg/admission/storageclass/setdefault/admission.go @@ -69,8 +69,8 @@ func (a *claimDefaulterPlugin) SetInternalKubeInformerFactory(f informers.Shared a.SetReadyFunc(informer.Informer().HasSynced) } -// Validate ensures lister is set. -func (a *claimDefaulterPlugin) Validate() error { +// ValidateInitialization ensures lister is set. +func (a *claimDefaulterPlugin) ValidateInitialization() error { if a.lister == nil { return fmt.Errorf("missing lister") } diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresourcedefinition/etcd.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresourcedefinition/etcd.go index 92f42e1907b..85d981f1a3b 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresourcedefinition/etcd.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresourcedefinition/etcd.go @@ -167,6 +167,6 @@ func (r *StatusREST) New() runtime.Object { } // Update alters the status subset of an object. -func (r *StatusREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { - return r.store.Update(ctx, name, objInfo) +func (r *StatusREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (runtime.Object, bool, error) { + return r.store.Update(ctx, name, objInfo, createValidation, updateValidation) } diff --git a/staging/src/k8s.io/apiserver/pkg/admission/chain.go b/staging/src/k8s.io/apiserver/pkg/admission/chain.go index ba1813de15d..65c7a526187 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/chain.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/chain.go @@ -30,9 +30,27 @@ func (admissionHandler chainAdmissionHandler) Admit(a Attributes) error { if !handler.Handles(a.GetOperation()) { continue } - err := handler.Admit(a) - if err != nil { - return err + if mutator, ok := handler.(MutationInterface); ok { + err := mutator.Admit(a) + if err != nil { + return err + } + } + } + return nil +} + +// Validate performs an admission control check using a chain of handlers, and returns immediately on first error +func (admissionHandler chainAdmissionHandler) Validate(a Attributes) error { + for _, handler := range admissionHandler { + if !handler.Handles(a.GetOperation()) { + continue + } + if validator, ok := handler.(ValidationInterface); ok { + err := validator.Validate(a) + if err != nil { + return err + } } } return nil diff --git a/staging/src/k8s.io/apiserver/pkg/admission/chain_test.go b/staging/src/k8s.io/apiserver/pkg/admission/chain_test.go index 3a0dc7ce116..8bdd74ca475 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/chain_test.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/chain_test.go @@ -25,9 +25,9 @@ import ( type FakeHandler struct { *Handler - name string - admit bool - admitCalled bool + name string + admit, admitCalled bool + validate, validateCalled bool } func (h *FakeHandler) Admit(a Attributes) (err error) { @@ -38,6 +38,14 @@ func (h *FakeHandler) Admit(a Attributes) (err error) { return fmt.Errorf("Don't admit") } +func (h *FakeHandler) Validate(a Attributes) (err error) { + h.admitCalled = true + if h.admit { + return nil + } + return fmt.Errorf("Don't admit") +} + func makeHandler(name string, admit bool, ops ...Operation) Interface { return &FakeHandler{ name: name, diff --git a/staging/src/k8s.io/apiserver/pkg/admission/initializer/initializer_test.go b/staging/src/k8s.io/apiserver/pkg/admission/initializer/initializer_test.go index e046ee2d4a3..cbf0206a2fe 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/initializer/initializer_test.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/initializer/initializer_test.go @@ -99,7 +99,7 @@ func (self *WantExternalKubeInformerFactory) SetExternalKubeInformerFactory(sf i } func (self *WantExternalKubeInformerFactory) Admit(a admission.Attributes) error { return nil } func (self *WantExternalKubeInformerFactory) Handles(o admission.Operation) bool { return false } -func (self *WantExternalKubeInformerFactory) Validate() error { return nil } +func (self *WantExternalKubeInformerFactory) ValidateInitialization() error { return nil } var _ admission.Interface = &WantExternalKubeInformerFactory{} var _ initializer.WantsExternalKubeInformerFactory = &WantExternalKubeInformerFactory{} @@ -112,7 +112,7 @@ type WantExternalKubeClientSet struct { func (self *WantExternalKubeClientSet) SetExternalKubeClientSet(cs kubernetes.Interface) { self.cs = cs } func (self *WantExternalKubeClientSet) Admit(a admission.Attributes) error { return nil } func (self *WantExternalKubeClientSet) Handles(o admission.Operation) bool { return false } -func (self *WantExternalKubeClientSet) Validate() error { return nil } +func (self *WantExternalKubeClientSet) ValidateInitialization() error { return nil } var _ admission.Interface = &WantExternalKubeClientSet{} var _ initializer.WantsExternalKubeClientSet = &WantExternalKubeClientSet{} @@ -125,7 +125,7 @@ type WantAuthorizerAdmission struct { func (self *WantAuthorizerAdmission) SetAuthorizer(a authorizer.Authorizer) { self.auth = a } func (self *WantAuthorizerAdmission) Admit(a admission.Attributes) error { return nil } func (self *WantAuthorizerAdmission) Handles(o admission.Operation) bool { return false } -func (self *WantAuthorizerAdmission) Validate() error { return nil } +func (self *WantAuthorizerAdmission) ValidateInitialization() error { return nil } var _ admission.Interface = &WantAuthorizerAdmission{} var _ initializer.WantsAuthorizer = &WantAuthorizerAdmission{} @@ -145,7 +145,7 @@ type clientCertWanter struct { 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) Handles(o admission.Operation) bool { return false } -func (s *clientCertWanter) Validate() error { return nil } +func (s *clientCertWanter) ValidateInitialization() error { return nil } // WantSchemeAdmission is a test stub that fulfills the WantsScheme interface. type WantSchemeAdmission struct { @@ -155,7 +155,7 @@ type WantSchemeAdmission struct { func (self *WantSchemeAdmission) SetScheme(s *runtime.Scheme) { self.scheme = s } func (self *WantSchemeAdmission) Admit(a admission.Attributes) error { return nil } func (self *WantSchemeAdmission) Handles(o admission.Operation) bool { return false } -func (self *WantSchemeAdmission) Validate() error { return nil } +func (self *WantSchemeAdmission) ValidateInitialization() error { return nil } var _ admission.Interface = &WantSchemeAdmission{} var _ initializer.WantsScheme = &WantSchemeAdmission{} diff --git a/staging/src/k8s.io/apiserver/pkg/admission/initializer/interfaces.go b/staging/src/k8s.io/apiserver/pkg/admission/initializer/interfaces.go index 16131f9dc8d..98a07585406 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/initializer/interfaces.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/initializer/interfaces.go @@ -27,23 +27,23 @@ import ( // WantsExternalKubeClientSet defines a function which sets external ClientSet for admission plugins that need it type WantsExternalKubeClientSet interface { SetExternalKubeClientSet(kubernetes.Interface) - admission.Validator + admission.InitializationValidator } // WantsExternalKubeInformerFactory defines a function which sets InformerFactory for admission plugins that need it type WantsExternalKubeInformerFactory interface { SetExternalKubeInformerFactory(informers.SharedInformerFactory) - admission.Validator + admission.InitializationValidator } // WantsAuthorizer defines a function which sets Authorizer for admission plugins that need it. type WantsAuthorizer interface { SetAuthorizer(authorizer.Authorizer) - admission.Validator + admission.InitializationValidator } // WantsScheme defines a function that accepts runtime.Scheme for admission plugins that need it. type WantsScheme interface { SetScheme(*runtime.Scheme) - admission.Validator + admission.InitializationValidator } diff --git a/staging/src/k8s.io/apiserver/pkg/admission/interfaces.go b/staging/src/k8s.io/apiserver/pkg/admission/interfaces.go index a8e671db514..76d3864e275 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/interfaces.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/interfaces.go @@ -53,14 +53,26 @@ type Attributes interface { // Interface is an abstract, pluggable interface for Admission Control decisions. type Interface interface { - // Admit makes an admission decision based on the request attributes - Admit(a Attributes) (err error) - // Handles returns true if this admission controller can handle the given operation // where operation can be one of CREATE, UPDATE, DELETE, or CONNECT Handles(operation Operation) bool } +type MutationInterface interface { + Interface + + // Admit makes an admission decision based on the request attributes + Admit(a Attributes) (err error) +} + +// ValidationInterface is an abstract, pluggable interface for Admission Control decisions. +type ValidationInterface interface { + Interface + + // Validate makes an admission decision based on the request attributes. It is NOT allowed to mutate + Validate(a Attributes) (err error) +} + // Operation is the type of resource operation being checked for admission control type Operation string @@ -78,10 +90,10 @@ type PluginInitializer interface { Initialize(plugin Interface) } -// Validator holds Validate functions, which are responsible for validation of initialized shared resources -// and should be implemented on admission plugins -type Validator interface { - Validate() error +// InitializationValidator holds ValidateInitialization functions, which are responsible for validation of initialized +// shared resources and should be implemented on admission plugins +type InitializationValidator interface { + ValidateInitialization() error } // ConfigProvider provides a way to get configuration for an admission plugin based on its name diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/initialization/initialization.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/initialization/initialization.go index 5eebea997d7..e5adaf25047 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/initialization/initialization.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/initialization/initialization.go @@ -74,8 +74,8 @@ func NewInitializer() admission.Interface { return &initializer{} } -// Validate implements the Validator interface. -func (i *initializer) Validate() error { +// ValidateInitialization implements the InitializationValidator interface. +func (i *initializer) ValidateInitialization() error { if i.config == nil { return fmt.Errorf("the Initializer admission plugin requires a Kubernetes client to be provided") } diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle/admission.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle/admission.go index e053a4907c7..158f4164276 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle/admission.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle/admission.go @@ -57,9 +57,9 @@ func Register(plugins *admission.Plugins) { }) } -// lifecycle is an implementation of admission.Interface. +// Lifecycle is an implementation of admission.Interface. // It enforces life-cycle constraints around a Namespace depending on its Phase -type lifecycle struct { +type Lifecycle struct { *admission.Handler client kubernetes.Interface immortalNamespaces sets.String @@ -73,8 +73,8 @@ type forceLiveLookupEntry struct { expiry time.Time } -var _ = initializer.WantsExternalKubeInformerFactory(&lifecycle{}) -var _ = initializer.WantsExternalKubeClientSet(&lifecycle{}) +var _ = initializer.WantsExternalKubeInformerFactory(&Lifecycle{}) +var _ = initializer.WantsExternalKubeClientSet(&Lifecycle{}) func makeNamespaceKey(namespace string) *v1.Namespace { return &v1.Namespace{ @@ -85,7 +85,7 @@ func makeNamespaceKey(namespace string) *v1.Namespace { } } -func (l *lifecycle) Admit(a admission.Attributes) error { +func (l *Lifecycle) Admit(a admission.Attributes) error { // prevent deletion of immortal namespaces if a.GetOperation() == admission.Delete && a.GetKind().GroupKind() == v1.SchemeGroupVersion.WithKind("Namespace").GroupKind() && l.immortalNamespaces.Has(a.GetName()) { return errors.NewForbidden(a.GetResource().GroupResource(), a.GetName(), fmt.Errorf("this namespace may not be deleted")) @@ -188,14 +188,14 @@ func (l *lifecycle) Admit(a admission.Attributes) error { return nil } -// NewLifecycle creates a new namespace lifecycle admission control handler -func NewLifecycle(immortalNamespaces sets.String) (admission.Interface, error) { +// NewLifecycle creates a new namespace Lifecycle admission control handler +func NewLifecycle(immortalNamespaces sets.String) (*Lifecycle, error) { return newLifecycleWithClock(immortalNamespaces, clock.RealClock{}) } -func newLifecycleWithClock(immortalNamespaces sets.String, clock utilcache.Clock) (admission.Interface, error) { +func newLifecycleWithClock(immortalNamespaces sets.String, clock utilcache.Clock) (*Lifecycle, error) { forceLiveLookupCache := utilcache.NewLRUExpireCacheWithClock(100, clock) - return &lifecycle{ + return &Lifecycle{ Handler: admission.NewHandler(admission.Create, admission.Update, admission.Delete), immortalNamespaces: immortalNamespaces, forceLiveLookupCache: forceLiveLookupCache, @@ -203,19 +203,19 @@ func newLifecycleWithClock(immortalNamespaces sets.String, clock utilcache.Clock } // SetExternalKubeInformerFactory implements the WantsExternalKubeInformerFactory interface. -func (l *lifecycle) SetExternalKubeInformerFactory(f informers.SharedInformerFactory) { +func (l *Lifecycle) SetExternalKubeInformerFactory(f informers.SharedInformerFactory) { namespaceInformer := f.Core().V1().Namespaces() l.namespaceLister = namespaceInformer.Lister() l.SetReadyFunc(namespaceInformer.Informer().HasSynced) } // SetExternalKubeClientSet implements the WantsExternalKubeClientSet interface. -func (l *lifecycle) SetExternalKubeClientSet(client kubernetes.Interface) { +func (l *Lifecycle) SetExternalKubeClientSet(client kubernetes.Interface) { l.client = client } -// Validate implement the Validator interface. -func (l *lifecycle) Validate() error { +// ValidateInitialization implements the InitializationValidator interface. +func (l *Lifecycle) ValidateInitialization() error { if l.namespaceLister == nil { return fmt.Errorf("missing namespaceLister") } diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle/admission_test.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle/admission_test.go index 5937eb643b6..3c2c74e4d44 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle/admission_test.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle/admission_test.go @@ -37,12 +37,12 @@ import ( ) // newHandlerForTest returns a configured handler for testing. -func newHandlerForTest(c clientset.Interface) (admission.Interface, informers.SharedInformerFactory, error) { +func newHandlerForTest(c clientset.Interface) (*Lifecycle, informers.SharedInformerFactory, error) { return newHandlerForTestWithClock(c, clock.RealClock{}) } // newHandlerForTestWithClock returns a configured handler for testing. -func newHandlerForTestWithClock(c clientset.Interface, cacheClock clock.Clock) (admission.Interface, informers.SharedInformerFactory, error) { +func newHandlerForTestWithClock(c clientset.Interface, cacheClock clock.Clock) (*Lifecycle, informers.SharedInformerFactory, error) { f := informers.NewSharedInformerFactory(c, 5*time.Minute) handler, err := newLifecycleWithClock(sets.NewString(metav1.NamespaceDefault, metav1.NamespaceSystem), cacheClock) if err != nil { @@ -53,7 +53,7 @@ func newHandlerForTestWithClock(c clientset.Interface, cacheClock clock.Clock) ( return handler, f, err } pluginInitializer.Initialize(handler) - err = admission.Validate(handler) + err = admission.ValidateInitialization(handler) return handler, f, err } diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/admission.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/admission.go index 9a251f2d146..33b6dd0a23c 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/admission.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/admission.go @@ -156,9 +156,8 @@ func (a *GenericAdmissionWebhook) SetExternalKubeClientSet(client clientset.Inte a.hookSource = configuration.NewExternalAdmissionHookConfigurationManager(client.Admissionregistration().ExternalAdmissionHookConfigurations()) } -// Validator holds Validate functions, which are responsible for validation of initialized shared resources -// and should be implemented on admission plugins -func (a *GenericAdmissionWebhook) Validate() error { +// ValidateInitialization implements the InitializationValidator interface. +func (a *GenericAdmissionWebhook) ValidateInitialization() error { if a.hookSource == nil { return fmt.Errorf("the GenericAdmissionWebhook admission plugin requires a Kubernetes client to be provided") } diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugins.go b/staging/src/k8s.io/apiserver/pkg/admission/plugins.go index 5ddfc7e1f84..172ac337b34 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugins.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugins.go @@ -156,18 +156,18 @@ func (ps *Plugins) InitPlugin(name string, config io.Reader, pluginInitializer P pluginInitializer.Initialize(plugin) // ensure that plugins have been properly initialized - if err := Validate(plugin); err != nil { + if err := ValidateInitialization(plugin); err != nil { return nil, err } return plugin, nil } -// Validate will call the Validate function in each plugin if they implement -// the Validator interface. -func Validate(plugin Interface) error { - if validater, ok := plugin.(Validator); ok { - err := validater.Validate() +// ValidateInitialization will call the InitializationValidate function in each plugin if they implement +// the InitializationValidator interface. +func ValidateInitialization(plugin Interface) error { + if validater, ok := plugin.(InitializationValidator); ok { + err := validater.ValidateInitialization() if err != nil { return err } diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/apiserver_test.go b/staging/src/k8s.io/apiserver/pkg/endpoints/apiserver_test.go index 4dd05a8beea..458c4d2acda 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/apiserver_test.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/apiserver_test.go @@ -519,7 +519,7 @@ func (storage *SimpleRESTStorage) NewList() runtime.Object { return &genericapitesting.SimpleList{} } -func (storage *SimpleRESTStorage) Create(ctx request.Context, obj runtime.Object, includeUninitialized bool) (runtime.Object, error) { +func (storage *SimpleRESTStorage) Create(ctx request.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, includeUninitialized bool) (runtime.Object, error) { storage.checkContext(ctx) storage.created = obj.(*genericapitesting.Simple) if err := storage.errors["create"]; err != nil { @@ -532,7 +532,7 @@ func (storage *SimpleRESTStorage) Create(ctx request.Context, obj runtime.Object return obj, err } -func (storage *SimpleRESTStorage) Update(ctx request.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { +func (storage *SimpleRESTStorage) Update(ctx request.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (runtime.Object, bool, error) { storage.checkContext(ctx) obj, err := objInfo.UpdatedObject(ctx, &storage.item) if err != nil { @@ -714,7 +714,7 @@ type NamedCreaterRESTStorage struct { createdName string } -func (storage *NamedCreaterRESTStorage) Create(ctx request.Context, name string, obj runtime.Object, includeUninitialized bool) (runtime.Object, error) { +func (storage *NamedCreaterRESTStorage) Create(ctx request.Context, name string, obj runtime.Object, createValidation rest.ValidateObjectFunc, includeUninitialized bool) (runtime.Object, error) { storage.checkContext(ctx) storage.created = obj.(*genericapitesting.Simple) storage.createdName = name diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/create.go b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/create.go index 96c2da4b8bd..dc3560623ea 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/create.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/create.go @@ -93,10 +93,10 @@ func createHandler(r rest.NamedCreater, scope RequestScope, typer runtime.Object ae := request.AuditEventFrom(ctx) audit.LogRequestObject(ae, obj, scope.Resource, scope.Subresource, scope.Serializer) - if admit != nil && admit.Handles(admission.Create) { - userInfo, _ := request.UserFrom(ctx) - - err = admit.Admit(admission.NewAttributesRecord(obj, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Create, userInfo)) + userInfo, _ := request.UserFrom(ctx) + admissionAttributes := admission.NewAttributesRecord(obj, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Create, userInfo) + if mutatingAdmission, ok := admit.(admission.MutationInterface); ok && mutatingAdmission.Handles(admission.Create) { + err = mutatingAdmission.Admit(admissionAttributes) if err != nil { scope.err(err, w, req) return @@ -108,7 +108,13 @@ func createHandler(r rest.NamedCreater, scope RequestScope, typer runtime.Object trace.Step("About to store object in database") result, err := finishRequest(timeout, func() (runtime.Object, error) { - return r.Create(ctx, name, obj, includeUninitialized) + return r.Create( + ctx, + name, + obj, + rest.AdmissionToValidateObjectFunc(admit, admissionAttributes), + includeUninitialized, + ) }) if err != nil { scope.err(err, w, req) @@ -144,19 +150,19 @@ func createHandler(r rest.NamedCreater, scope RequestScope, typer runtime.Object } // CreateNamedResource returns a function that will handle a resource creation with name. -func CreateNamedResource(r rest.NamedCreater, scope RequestScope, typer runtime.ObjectTyper, admit admission.Interface) http.HandlerFunc { - return createHandler(r, scope, typer, admit, true) +func CreateNamedResource(r rest.NamedCreater, scope RequestScope, typer runtime.ObjectTyper, admission admission.Interface) http.HandlerFunc { + return createHandler(r, scope, typer, admission, true) } // CreateResource returns a function that will handle a resource creation. -func CreateResource(r rest.Creater, scope RequestScope, typer runtime.ObjectTyper, admit admission.Interface) http.HandlerFunc { - return createHandler(&namedCreaterAdapter{r}, scope, typer, admit, false) +func CreateResource(r rest.Creater, scope RequestScope, typer runtime.ObjectTyper, admission admission.Interface) http.HandlerFunc { + return createHandler(&namedCreaterAdapter{r}, scope, typer, admission, false) } type namedCreaterAdapter struct { rest.Creater } -func (c *namedCreaterAdapter) Create(ctx request.Context, name string, obj runtime.Object, includeUninitialized bool) (runtime.Object, error) { - return c.Creater.Create(ctx, obj, includeUninitialized) +func (c *namedCreaterAdapter) Create(ctx request.Context, name string, obj runtime.Object, createValidatingAdmission rest.ValidateObjectFunc, includeUninitialized bool) (runtime.Object, error) { + return c.Creater.Create(ctx, obj, createValidatingAdmission, includeUninitialized) } diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/delete.go b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/delete.go index 81fe41be198..0bc5a659b55 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/delete.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/delete.go @@ -34,6 +34,7 @@ import ( ) // DeleteResource returns a function that will handle a resource deletion +// TODO admission here becomes solely validating admission func DeleteResource(r rest.GracefulDeleter, allowsOptions bool, scope RequestScope, admit admission.Interface) http.HandlerFunc { return func(w http.ResponseWriter, req *http.Request) { // For performance tracking purposes. @@ -95,11 +96,18 @@ func DeleteResource(r rest.GracefulDeleter, allowsOptions bool, scope RequestSco trace.Step("About to check admission control") if admit != nil && admit.Handles(admission.Delete) { userInfo, _ := request.UserFrom(ctx) - - err = admit.Admit(admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Delete, userInfo)) - if err != nil { - scope.err(err, w, req) - return + attrs := admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Delete, userInfo) + if mutatingAdmission, ok := admit.(admission.MutationInterface); ok { + if err := mutatingAdmission.Admit(attrs); err != nil { + scope.err(err, w, req) + return + } + } + if validatingAdmission, ok := admit.(admission.ValidationInterface); ok { + if err := validatingAdmission.Validate(attrs); err != nil { + scope.err(err, w, req) + return + } } } @@ -171,10 +179,20 @@ func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope RequestSco ctx := scope.ContextFunc(req) ctx = request.WithNamespace(ctx, namespace) - if admit != nil && admit.Handles(admission.Delete) { + if mutatingAdmission, ok := admit.(admission.MutationInterface); ok && mutatingAdmission.Handles(admission.Delete) { userInfo, _ := request.UserFrom(ctx) - err = admit.Admit(admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, "", scope.Resource, scope.Subresource, admission.Delete, userInfo)) + err = mutatingAdmission.Admit(admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, "", scope.Resource, scope.Subresource, admission.Delete, userInfo)) + if err != nil { + scope.err(err, w, req) + return + } + } + // TODO: avoid calling Handles twice + if validatingAdmission, ok := admit.(admission.ValidationInterface); ok && validatingAdmission.Handles(admission.Delete) { + userInfo, _ := request.UserFrom(ctx) + + err = validatingAdmission.Validate(admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, "", scope.Resource, scope.Subresource, admission.Delete, userInfo)) if err != nil { scope.err(err, w, req) return diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/patch.go b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/patch.go index 7c01aafd2ad..8db9f40c93a 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/patch.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/patch.go @@ -92,16 +92,25 @@ func PatchResource(r rest.Patcher, scope RequestScope, admit admission.Interface scope.Serializer.DecoderToVersion(s.Serializer, schema.GroupVersion{Group: gv.Group, Version: runtime.APIVersionInternal}), ) - updateAdmit := func(updatedObject runtime.Object, currentObject runtime.Object) error { - if admit != nil && admit.Handles(admission.Update) { - userInfo, _ := request.UserFrom(ctx) - return admit.Admit(admission.NewAttributesRecord(updatedObject, currentObject, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Update, userInfo)) + userInfo, _ := request.UserFrom(ctx) + staticAdmissionAttributes := admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Update, userInfo) + updateMutation := func(updatedObject runtime.Object, currentObject runtime.Object) error { + if mutatingAdmission, ok := admit.(admission.MutationInterface); ok && admit.Handles(admission.Update) { + return mutatingAdmission.Admit(admission.NewAttributesRecord(updatedObject, currentObject, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Update, userInfo)) } - return nil } - result, err := patchResource(ctx, updateAdmit, timeout, versionedObj, r, name, patchType, patchJS, + result, err := patchResource( + ctx, + updateMutation, + rest.AdmissionToValidateObjectFunc(admit, staticAdmissionAttributes), + rest.AdmissionToValidateObjectUpdateFunc(admit, staticAdmissionAttributes), + timeout, versionedObj, + r, + name, + patchType, + patchJS, scope.Namer, scope.Creater, scope.Defaulter, scope.UnsafeConvertor, scope.Kind, scope.Resource, codec) if err != nil { scope.err(err, w, req) @@ -122,12 +131,14 @@ func PatchResource(r rest.Patcher, scope RequestScope, admit admission.Interface } } -type updateAdmissionFunc func(updatedObject runtime.Object, currentObject runtime.Object) error +type mutateObjectUpdateFunc func(obj, old runtime.Object) error // patchResource divides PatchResource for easier unit testing func patchResource( ctx request.Context, - admit updateAdmissionFunc, + updateMutation mutateObjectUpdateFunc, + createValidation rest.ValidateObjectFunc, + updateValidation rest.ValidateObjectUpdateFunc, timeout time.Duration, versionedObj runtime.Object, patcher rest.Patcher, @@ -341,16 +352,15 @@ func patchResource( // applyAdmission is called every time GuaranteedUpdate asks for the updated object, // and is given the currently persisted object and the patched object as input. applyAdmission := func(ctx request.Context, patchedObject runtime.Object, currentObject runtime.Object) (runtime.Object, error) { - return patchedObject, admit(patchedObject, currentObject) + return patchedObject, updateMutation(patchedObject, currentObject) } - updatedObjectInfo := rest.DefaultUpdatedObjectInfo(nil, applyPatch, applyAdmission) return finishRequest(timeout, func() (runtime.Object, error) { - updateObject, _, updateErr := patcher.Update(ctx, name, updatedObjectInfo) + updateObject, _, updateErr := patcher.Update(ctx, name, updatedObjectInfo, createValidation, updateValidation) for i := 0; i < MaxRetryWhenPatchConflicts && (errors.IsConflict(updateErr)); i++ { lastConflictErr = updateErr - updateObject, _, updateErr = patcher.Update(ctx, name, updatedObjectInfo) + updateObject, _, updateErr = patcher.Update(ctx, name, updatedObjectInfo, createValidation, updateValidation) } return updateObject, updateErr }) diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/rest.go b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/rest.go index cd4d7349e42..768005daa23 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/rest.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/rest.go @@ -117,11 +117,20 @@ func ConnectResource(connecter rest.Connecter, scope RequestScope, admit admissi ResourcePath: restPath, } userInfo, _ := request.UserFrom(ctx) - - err = admit.Admit(admission.NewAttributesRecord(connectRequest, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Connect, userInfo)) - if err != nil { - scope.err(err, w, req) - return + // TODO: remove the mutating admission here as soon as we have ported all plugin that handle CONNECT + if mutatingAdmit, ok := admit.(admission.MutationInterface); ok { + err = mutatingAdmit.Admit(admission.NewAttributesRecord(connectRequest, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Connect, userInfo)) + if err != nil { + scope.err(err, w, req) + return + } + } + if mutatingAdmit, ok := admit.(admission.ValidationInterface); ok { + err = mutatingAdmit.Validate(admission.NewAttributesRecord(connectRequest, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Connect, userInfo)) + if err != nil { + scope.err(err, w, req) + return + } } } requestInfo, _ := request.RequestInfoFrom(ctx) diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/rest_test.go b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/rest_test.go index 68e146422f6..65414ad5d36 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/rest_test.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/rest_test.go @@ -170,7 +170,7 @@ func (p *testPatcher) New() runtime.Object { return &example.Pod{} } -func (p *testPatcher) Update(ctx request.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { +func (p *testPatcher) Update(ctx request.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (runtime.Object, bool, error) { currentPod := p.startingPod if p.numUpdates > 0 { currentPod = p.updatePod @@ -235,7 +235,8 @@ type patchTestCase struct { name string // admission chain to use, nil is fine - admit updateAdmissionFunc + admissionMutation mutateObjectUpdateFunc + admissionValidation rest.ValidateObjectUpdateFunc // TODO: add test for this // startingPod is used as the starting point for the first Update startingPod *example.Pod @@ -257,9 +258,16 @@ func (tc *patchTestCase) Run(t *testing.T) { name := tc.startingPod.Name codec := codecs.LegacyCodec(examplev1.SchemeGroupVersion) - admit := tc.admit - if admit == nil { - admit = func(updatedObject runtime.Object, currentObject runtime.Object) error { + + admissionMutation := tc.admissionMutation + if admissionMutation == nil { + admissionMutation = func(updatedObject runtime.Object, currentObject runtime.Object) error { + return nil + } + } + admissionValidation := tc.admissionValidation + if admissionValidation == nil { + admissionValidation = func(updatedObject runtime.Object, currentObject runtime.Object) error { return nil } } @@ -321,7 +329,18 @@ func (tc *patchTestCase) Run(t *testing.T) { } - resultObj, err := patchResource(ctx, admit, 1*time.Second, versionedObj, testPatcher, name, patchType, patch, namer, creater, defaulter, convertor, kind, resource, codec) + resultObj, err := patchResource( + ctx, + admissionMutation, + rest.ValidateAllObjectFunc, + admissionValidation, + 1*time.Second, + versionedObj, + testPatcher, + name, + patchType, + patch, + namer, creater, defaulter, convertor, kind, resource, codec) if len(tc.expectedError) != 0 { if err == nil || err.Error() != tc.expectedError { t.Errorf("%s: expected error %v, but got %v", tc.name, tc.expectedError, err) @@ -544,7 +563,7 @@ func TestPatchWithAdmissionRejection(t *testing.T) { tc := &patchTestCase{ name: "TestPatchWithAdmissionRejection", - admit: func(updatedObject runtime.Object, currentObject runtime.Object) error { + admissionMutation: func(updatedObject runtime.Object, currentObject runtime.Object) error { return errors.New("admission failure") }, @@ -583,7 +602,7 @@ func TestPatchWithVersionConflictThenAdmissionFailure(t *testing.T) { tc := &patchTestCase{ name: "TestPatchWithVersionConflictThenAdmissionFailure", - admit: func(updatedObject runtime.Object, currentObject runtime.Object) error { + admissionMutation: func(updatedObject runtime.Object, currentObject runtime.Object) error { if seen { return errors.New("admission failure") } diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/update.go b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/update.go index 9743f4c7a6b..319bfd51b7c 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/update.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/update.go @@ -86,18 +86,25 @@ func UpdateResource(r rest.Updater, scope RequestScope, typer runtime.ObjectType return } + userInfo, _ := request.UserFrom(ctx) + staticAdmissionAttributes := admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Update, userInfo) var transformers []rest.TransformFunc - if admit != nil && admit.Handles(admission.Update) { + if mutatingAdmission, ok := admit.(admission.MutationInterface); ok && mutatingAdmission.Handles(admission.Update) { transformers = append(transformers, func(ctx request.Context, newObj, oldObj runtime.Object) (runtime.Object, error) { - userInfo, _ := request.UserFrom(ctx) - return newObj, admit.Admit(admission.NewAttributesRecord(newObj, oldObj, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Update, userInfo)) + return newObj, mutatingAdmission.Admit(admission.NewAttributesRecord(newObj, oldObj, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Update, userInfo)) }) } trace.Step("About to store object in database") wasCreated := false result, err := finishRequest(timeout, func() (runtime.Object, error) { - obj, created, err := r.Update(ctx, name, rest.DefaultUpdatedObjectInfo(obj, transformers...)) + obj, created, err := r.Update( + ctx, + name, + rest.DefaultUpdatedObjectInfo(obj, transformers...), + rest.AdmissionToValidateObjectFunc(admit, staticAdmissionAttributes), + rest.AdmissionToValidateObjectUpdateFunc(admit, staticAdmissionAttributes), + ) wasCreated = created return obj, err }) diff --git a/staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store.go b/staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store.go index c8a9fd6c857..8536d0de740 100644 --- a/staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store.go +++ b/staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store.go @@ -148,11 +148,13 @@ type Store struct { // AfterCreate implements a further operation to run after a resource is // created and before it is decorated, optional. AfterCreate ObjectFunc + // UpdateStrategy implements resource-specific behavior during updates. UpdateStrategy rest.RESTUpdateStrategy // AfterUpdate implements a further operation to run after a resource is // updated and before it is decorated, optional. AfterUpdate ObjectFunc + // DeleteStrategy implements resource-specific behavior during deletion. DeleteStrategy rest.RESTDeleteStrategy // AfterDelete implements a further operation to run after a resource is @@ -303,10 +305,18 @@ func (e *Store) ListPredicate(ctx genericapirequest.Context, p storage.Selection } // Create inserts a new item according to the unique key from the object. -func (e *Store) Create(ctx genericapirequest.Context, obj runtime.Object, includeUninitialized bool) (runtime.Object, error) { +func (e *Store) Create(ctx genericapirequest.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, includeUninitialized bool) (runtime.Object, error) { if err := rest.BeforeCreate(e.CreateStrategy, ctx, obj); err != nil { return nil, err } + // at this point we have a fully formed object. It is time to call the validators that the apiserver + // handling chain wants to enforce. + if createValidation != nil { + if err := createValidation(obj.DeepCopyObject()); err != nil { + return nil, err + } + } + name, err := e.ObjectNameFunc(obj) if err != nil { return nil, err @@ -494,7 +504,7 @@ func (e *Store) deleteWithoutFinalizers(ctx genericapirequest.Context, name, key // Update performs an atomic update and set of the object. Returns the result of the update // or an error. If the registry allows create-on-update, the create flow will be executed. // A bool is returned along with the object and any errors, to indicate object creation. -func (e *Store) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { +func (e *Store) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (runtime.Object, bool, error) { key, err := e.KeyFunc(ctx, name) if err != nil { return nil, false, err @@ -544,10 +554,18 @@ func (e *Store) Update(ctx genericapirequest.Context, name string, objInfo rest. if err := rest.BeforeCreate(e.CreateStrategy, ctx, obj); err != nil { return nil, nil, err } + // at this point we have a fully formed object. It is time to call the validators that the apiserver + // handling chain wants to enforce. + if createValidation != nil { + if err := createValidation(obj.DeepCopyObject()); err != nil { + return nil, nil, err + } + } ttl, err := e.calculateTTL(obj, 0, false) if err != nil { return nil, nil, err } + return obj, &ttl, nil } @@ -582,6 +600,13 @@ func (e *Store) Update(ctx genericapirequest.Context, name string, objInfo rest. if err := rest.BeforeUpdate(e.UpdateStrategy, ctx, obj, existing); err != nil { return nil, nil, err } + // at this point we have a fully formed object. It is time to call the validators that the apiserver + // handling chain wants to enforce. + if updateValidation != nil { + if err := updateValidation(obj.DeepCopyObject(), existing.DeepCopyObject()); err != nil { + return nil, nil, err + } + } if e.shouldDeleteDuringUpdate(ctx, key, obj, existing) { deleteObj = obj return nil, nil, errEmptiedFinalizers diff --git a/staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store_test.go b/staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store_test.go index f9b9fcb7e90..efa31cc3dcd 100644 --- a/staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store_test.go +++ b/staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store_test.go @@ -245,7 +245,7 @@ func TestStoreListResourceVersion(t *testing.T) { destroyFunc, registry := newTestGenericStoreRegistry(t, scheme, true) defer destroyFunc() - obj, err := registry.Create(ctx, fooPod, false) + obj, err := registry.Create(ctx, fooPod, rest.ValidateAllObjectFunc, false) if err != nil { t.Fatal(err) } @@ -275,7 +275,7 @@ func TestStoreListResourceVersion(t *testing.T) { t.Fatalf("expected waiting, but get %#v", l) } - if _, err := registry.Create(ctx, barPod, false); err != nil { + if _, err := registry.Create(ctx, barPod, rest.ValidateAllObjectFunc, false); err != nil { t.Fatal(err) } @@ -312,7 +312,7 @@ func TestStoreCreate(t *testing.T) { registry.DeleteStrategy = testGracefulStrategy{defaultDeleteStrategy} // create the object - objA, err := registry.Create(testContext, podA, false) + objA, err := registry.Create(testContext, podA, rest.ValidateAllObjectFunc, false) if err != nil { t.Errorf("Unexpected error: %v", err) } @@ -329,7 +329,7 @@ func TestStoreCreate(t *testing.T) { } // now try to create the second pod - _, err = registry.Create(testContext, podB, false) + _, err = registry.Create(testContext, podB, rest.ValidateAllObjectFunc, false) if !errors.IsAlreadyExists(err) { t.Errorf("Unexpected error: %v", err) } @@ -348,7 +348,7 @@ func TestStoreCreate(t *testing.T) { } // try to create before graceful deletion period is over - _, err = registry.Create(testContext, podA, false) + _, err = registry.Create(testContext, podA, rest.ValidateAllObjectFunc, false) if err == nil || !errors.IsAlreadyExists(err) { t.Fatalf("Expected 'already exists' error from storage, but got %v", err) } @@ -440,7 +440,7 @@ func TestStoreCreateInitialized(t *testing.T) { } pod.Initializers = nil - updated, _, err := registry.Update(ctx, podA.Name, rest.DefaultUpdatedObjectInfo(pod)) + updated, _, err := registry.Update(ctx, podA.Name, rest.DefaultUpdatedObjectInfo(pod), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc) if err != nil { t.Fatal(err) } @@ -476,7 +476,7 @@ func TestStoreCreateInitialized(t *testing.T) { }() // create the object - objA, err := registry.Create(ctx, podA, false) + objA, err := registry.Create(ctx, podA, rest.ValidateAllObjectFunc, false) if err != nil { t.Errorf("Unexpected error: %v", err) } @@ -533,7 +533,7 @@ func TestStoreCreateInitializedFailed(t *testing.T) { } pod.Initializers.Pending = nil pod.Initializers.Result = &metav1.Status{Status: metav1.StatusFailure, Code: 403, Reason: metav1.StatusReasonForbidden, Message: "induced failure"} - updated, _, err := registry.Update(ctx, podA.Name, rest.DefaultUpdatedObjectInfo(pod)) + updated, _, err := registry.Update(ctx, podA.Name, rest.DefaultUpdatedObjectInfo(pod), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc) if err != nil { t.Fatal(err) } @@ -556,7 +556,7 @@ func TestStoreCreateInitializedFailed(t *testing.T) { }() // create the object - _, err := registry.Create(ctx, podA, false) + _, err := registry.Create(ctx, podA, rest.ValidateAllObjectFunc, false) if !errors.IsForbidden(err) { t.Fatalf("unexpected error: %#v", err.(errors.APIStatus).Status()) } @@ -574,7 +574,7 @@ func TestStoreCreateInitializedFailed(t *testing.T) { } func updateAndVerify(t *testing.T, ctx genericapirequest.Context, registry *Store, pod *example.Pod) bool { - obj, _, err := registry.Update(ctx, pod.Name, rest.DefaultUpdatedObjectInfo(pod)) + obj, _, err := registry.Update(ctx, pod.Name, rest.DefaultUpdatedObjectInfo(pod), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc) if err != nil { t.Errorf("Unexpected error: %v", err) return false @@ -610,7 +610,7 @@ func TestStoreUpdate(t *testing.T) { defer destroyFunc() // Test1 try to update a non-existing node - _, _, err := registry.Update(testContext, podA.Name, rest.DefaultUpdatedObjectInfo(podA)) + _, _, err := registry.Update(testContext, podA.Name, rest.DefaultUpdatedObjectInfo(podA), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc) if !errors.IsNotFound(err) { t.Errorf("Unexpected error: %v", err) } @@ -623,7 +623,7 @@ func TestStoreUpdate(t *testing.T) { registry.UpdateStrategy.(*testRESTStrategy).allowCreateOnUpdate = false // Test3 outofDate - _, _, err = registry.Update(testContext, podAWithResourceVersion.Name, rest.DefaultUpdatedObjectInfo(podAWithResourceVersion)) + _, _, err = registry.Update(testContext, podAWithResourceVersion.Name, rest.DefaultUpdatedObjectInfo(podAWithResourceVersion), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc) if !errors.IsConflict(err) { t.Errorf("Unexpected error updating podAWithResourceVersion: %v", err) } @@ -660,7 +660,7 @@ func TestNoOpUpdates(t *testing.T) { var err error var createResult runtime.Object - if createResult, err = registry.Create(genericapirequest.NewDefaultContext(), newPod(), false); err != nil { + if createResult, err = registry.Create(genericapirequest.NewDefaultContext(), newPod(), rest.ValidateAllObjectFunc, false); err != nil { t.Fatalf("Unexpected error: %v", err) } @@ -671,7 +671,7 @@ func TestNoOpUpdates(t *testing.T) { var updateResult runtime.Object p := newPod() - if updateResult, _, err = registry.Update(genericapirequest.NewDefaultContext(), p.Name, rest.DefaultUpdatedObjectInfo(p)); err != nil { + if updateResult, _, err = registry.Update(genericapirequest.NewDefaultContext(), p.Name, rest.DefaultUpdatedObjectInfo(p), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc); err != nil { t.Fatalf("Unexpected error: %v", err) } @@ -835,7 +835,7 @@ func TestStoreDelete(t *testing.T) { } // create pod - _, err = registry.Create(testContext, podA, false) + _, err = registry.Create(testContext, podA, rest.ValidateAllObjectFunc, false) if err != nil { t.Errorf("Unexpected error: %v", err) } @@ -873,7 +873,7 @@ func TestStoreDeleteUninitialized(t *testing.T) { } // create pod - _, err = registry.Create(testContext, podA, true) + _, err = registry.Create(testContext, podA, rest.ValidateAllObjectFunc, true) if err != nil { t.Errorf("Unexpected error: %v", err) } @@ -950,7 +950,7 @@ func TestGracefulStoreHandleFinalizers(t *testing.T) { registry.EnableGarbageCollection = gcEnabled // create pod - _, err := registry.Create(testContext, podWithFinalizer, false) + _, err := registry.Create(testContext, podWithFinalizer, rest.ValidateAllObjectFunc, false) if err != nil { t.Errorf("Unexpected error: %v", err) } @@ -972,7 +972,7 @@ func TestGracefulStoreHandleFinalizers(t *testing.T) { ObjectMeta: metav1.ObjectMeta{Name: "foo", Finalizers: []string{"foo.com/x"}, ResourceVersion: podWithFinalizer.ObjectMeta.ResourceVersion}, Spec: example.PodSpec{NodeName: "machine"}, } - _, _, err = registry.Update(testContext, updatedPodWithFinalizer.ObjectMeta.Name, rest.DefaultUpdatedObjectInfo(updatedPodWithFinalizer)) + _, _, err = registry.Update(testContext, updatedPodWithFinalizer.ObjectMeta.Name, rest.DefaultUpdatedObjectInfo(updatedPodWithFinalizer), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc) if err != nil { t.Fatalf("Unexpected error: %v", err) } @@ -987,7 +987,7 @@ func TestGracefulStoreHandleFinalizers(t *testing.T) { ObjectMeta: metav1.ObjectMeta{Name: "foo", ResourceVersion: podWithFinalizer.ObjectMeta.ResourceVersion}, Spec: example.PodSpec{NodeName: "anothermachine"}, } - _, _, err = registry.Update(testContext, podWithFinalizer.ObjectMeta.Name, rest.DefaultUpdatedObjectInfo(podWithNoFinalizer)) + _, _, err = registry.Update(testContext, podWithFinalizer.ObjectMeta.Name, rest.DefaultUpdatedObjectInfo(podWithNoFinalizer), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc) if err != nil { t.Fatalf("Unexpected error: %v", err) } @@ -1016,7 +1016,7 @@ func TestFailedInitializationStoreUpdate(t *testing.T) { defer destroyFunc() // create pod, view initializing - obj, err := registry.Create(testContext, podInitializing, true) + obj, err := registry.Create(testContext, podInitializing, rest.ValidateAllObjectFunc, true) if err != nil { t.Errorf("Unexpected error: %v", err) } @@ -1024,7 +1024,7 @@ func TestFailedInitializationStoreUpdate(t *testing.T) { // update the pod with initialization failure, the pod should be deleted pod.Initializers.Result = &metav1.Status{Status: metav1.StatusFailure} - result, _, err := registry.Update(testContext, podInitializing.Name, rest.DefaultUpdatedObjectInfo(pod)) + result, _, err := registry.Update(testContext, podInitializing.Name, rest.DefaultUpdatedObjectInfo(pod), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc) if err != nil { t.Fatalf("Unexpected error: %v", err) } @@ -1055,7 +1055,7 @@ func TestNonGracefulStoreHandleFinalizers(t *testing.T) { registry.EnableGarbageCollection = gcEnabled // create pod - _, err := registry.Create(testContext, podWithFinalizer, false) + _, err := registry.Create(testContext, podWithFinalizer, rest.ValidateAllObjectFunc, false) if err != nil { t.Errorf("Unexpected error: %v", err) } @@ -1092,7 +1092,7 @@ func TestNonGracefulStoreHandleFinalizers(t *testing.T) { ObjectMeta: metav1.ObjectMeta{Name: "foo", Finalizers: []string{"foo.com/x"}, ResourceVersion: podWithFinalizer.ObjectMeta.ResourceVersion}, Spec: example.PodSpec{NodeName: "machine"}, } - _, _, err = registry.Update(testContext, updatedPodWithFinalizer.ObjectMeta.Name, rest.DefaultUpdatedObjectInfo(updatedPodWithFinalizer)) + _, _, err = registry.Update(testContext, updatedPodWithFinalizer.ObjectMeta.Name, rest.DefaultUpdatedObjectInfo(updatedPodWithFinalizer), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc) if err != nil { t.Errorf("Unexpected error: %v", err) } @@ -1111,7 +1111,7 @@ func TestNonGracefulStoreHandleFinalizers(t *testing.T) { ObjectMeta: metav1.ObjectMeta{Name: "foo", ResourceVersion: podWithFinalizer.ObjectMeta.ResourceVersion}, Spec: example.PodSpec{NodeName: "anothermachine"}, } - _, _, err = registry.Update(testContext, podWithFinalizer.ObjectMeta.Name, rest.DefaultUpdatedObjectInfo(podWithNoFinalizer)) + _, _, err = registry.Update(testContext, podWithFinalizer.ObjectMeta.Name, rest.DefaultUpdatedObjectInfo(podWithNoFinalizer), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc) if err != nil { t.Errorf("Unexpected error: %v", err) } @@ -1357,7 +1357,7 @@ func TestStoreDeleteWithOrphanDependents(t *testing.T) { for _, tc := range testcases { registry.DeleteStrategy = tc.strategy // create pod - _, err := registry.Create(testContext, tc.pod, false) + _, err := registry.Create(testContext, tc.pod, rest.ValidateAllObjectFunc, false) if err != nil { t.Fatalf("Unexpected error: %v", err) } @@ -1576,7 +1576,7 @@ func TestStoreDeletionPropagation(t *testing.T) { i++ pod := createPod(i, tc.existingFinalizers) // create pod - _, err := registry.Create(testContext, pod, false) + _, err := registry.Create(testContext, pod, rest.ValidateAllObjectFunc, false) if err != nil { t.Fatalf("Unexpected error: %v", err) } @@ -1628,13 +1628,13 @@ func TestStoreDeleteCollection(t *testing.T) { destroyFunc, registry := NewTestGenericStoreRegistry(t) defer destroyFunc() - if _, err := registry.Create(testContext, podA, false); err != nil { + if _, err := registry.Create(testContext, podA, rest.ValidateAllObjectFunc, false); err != nil { t.Errorf("Unexpected error: %v", err) } - if _, err := registry.Create(testContext, podB, false); err != nil { + if _, err := registry.Create(testContext, podB, rest.ValidateAllObjectFunc, false); err != nil { t.Errorf("Unexpected error: %v", err) } - if _, err := registry.Create(testContext, podC, true); err != nil { + if _, err := registry.Create(testContext, podC, rest.ValidateAllObjectFunc, true); err != nil { t.Errorf("Unexpected error: %v", err) } @@ -1670,10 +1670,10 @@ func TestStoreDeleteCollectionNotFound(t *testing.T) { for i := 0; i < 10; i++ { // Setup - if _, err := registry.Create(testContext, podA, false); err != nil { + if _, err := registry.Create(testContext, podA, rest.ValidateAllObjectFunc, false); err != nil { t.Errorf("Unexpected error: %v", err) } - if _, err := registry.Create(testContext, podB, false); err != nil { + if _, err := registry.Create(testContext, podB, rest.ValidateAllObjectFunc, false); err != nil { t.Errorf("Unexpected error: %v", err) } @@ -1709,7 +1709,7 @@ func TestStoreDeleteCollectionWithWatch(t *testing.T) { destroyFunc, registry := NewTestGenericStoreRegistry(t) defer destroyFunc() - objCreated, err := registry.Create(testContext, podA, false) + objCreated, err := registry.Create(testContext, podA, rest.ValidateAllObjectFunc, false) if err != nil { t.Fatalf("Unexpected error: %v", err) } @@ -1778,7 +1778,7 @@ func TestStoreWatch(t *testing.T) { if err != nil { t.Errorf("%v: unexpected error: %v", name, err) } else { - obj, err := registry.Create(testContext, podA, false) + obj, err := registry.Create(testContext, podA, rest.ValidateAllObjectFunc, false) if err != nil { got, open := <-wi.ResultChan() if !open { @@ -1922,7 +1922,7 @@ func TestQualifiedResource(t *testing.T) { defer destroyFunc() // update a non-exist object - _, _, err := registry.Update(testContext, podA.Name, rest.DefaultUpdatedObjectInfo(podA)) + _, _, err := registry.Update(testContext, podA.Name, rest.DefaultUpdatedObjectInfo(podA), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc) if !errors.IsNotFound(err) { t.Fatalf("Unexpected error: %v", err) } @@ -1954,13 +1954,13 @@ func TestQualifiedResource(t *testing.T) { } // create a non-exist object - _, err = registry.Create(testContext, podA, false) + _, err = registry.Create(testContext, podA, rest.ValidateAllObjectFunc, false) if err != nil { t.Fatal(err) } // create a exist object will fail - _, err = registry.Create(testContext, podA, false) + _, err = registry.Create(testContext, podA, rest.ValidateAllObjectFunc, false) if !errors.IsAlreadyExists(err) { t.Fatalf("Unexpected error: %v", err) } diff --git a/staging/src/k8s.io/apiserver/pkg/registry/rest/BUILD b/staging/src/k8s.io/apiserver/pkg/registry/rest/BUILD index 7b61aaeaef9..a9481dce300 100644 --- a/staging/src/k8s.io/apiserver/pkg/registry/rest/BUILD +++ b/staging/src/k8s.io/apiserver/pkg/registry/rest/BUILD @@ -49,6 +49,7 @@ go_library( "//vendor/k8s.io/apimachinery/pkg/util/uuid:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", "//vendor/k8s.io/apimachinery/pkg/watch:go_default_library", + "//vendor/k8s.io/apiserver/pkg/admission:go_default_library", "//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library", "//vendor/k8s.io/apiserver/pkg/features:go_default_library", "//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library", diff --git a/staging/src/k8s.io/apiserver/pkg/registry/rest/create.go b/staging/src/k8s.io/apiserver/pkg/registry/rest/create.go index 55b628f0308..1f998332e9c 100644 --- a/staging/src/k8s.io/apiserver/pkg/registry/rest/create.go +++ b/staging/src/k8s.io/apiserver/pkg/registry/rest/create.go @@ -25,6 +25,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/validation/field" + "k8s.io/apiserver/pkg/admission" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" "k8s.io/apiserver/pkg/features" "k8s.io/apiserver/pkg/storage/names" @@ -151,3 +152,28 @@ type NamespaceScopedStrategy interface { // NamespaceScoped returns if the object must be in a namespace. NamespaceScoped() bool } + +// AdmissionToValidateObjectFunc converts validating admission to a rest validate object func +func AdmissionToValidateObjectFunc(admit admission.Interface, staticAttributes admission.Attributes) ValidateObjectFunc { + validatingAdmission, ok := admit.(admission.ValidationInterface) + if !ok { + return func(obj runtime.Object) error { return nil } + } + return func(obj runtime.Object) error { + finalAttributes := admission.NewAttributesRecord( + obj, + staticAttributes.GetOldObject(), + staticAttributes.GetKind(), + staticAttributes.GetNamespace(), + staticAttributes.GetName(), + staticAttributes.GetResource(), + staticAttributes.GetSubresource(), + staticAttributes.GetOperation(), + staticAttributes.GetUserInfo(), + ) + if !validatingAdmission.Handles(finalAttributes.GetOperation()) { + return nil + } + return validatingAdmission.Validate(finalAttributes) + } +} diff --git a/staging/src/k8s.io/apiserver/pkg/registry/rest/rest.go b/staging/src/k8s.io/apiserver/pkg/registry/rest/rest.go index 21673110fd5..1155c8ade27 100644 --- a/staging/src/k8s.io/apiserver/pkg/registry/rest/rest.go +++ b/staging/src/k8s.io/apiserver/pkg/registry/rest/rest.go @@ -192,7 +192,7 @@ type Creater interface { // Create creates a new version of a resource. If includeUninitialized is set, the object may be returned // without completing initialization. - Create(ctx genericapirequest.Context, obj runtime.Object, includeUninitialized bool) (runtime.Object, error) + Create(ctx genericapirequest.Context, obj runtime.Object, createValidation ValidateObjectFunc, includeUninitialized bool) (runtime.Object, error) } // NamedCreater is an object that can create an instance of a RESTful object using a name parameter. @@ -205,7 +205,7 @@ type NamedCreater interface { // This is needed for create operations on subresources which include the name of the parent // resource in the path. If includeUninitialized is set, the object may be returned without // completing initialization. - Create(ctx genericapirequest.Context, name string, obj runtime.Object, includeUninitialized bool) (runtime.Object, error) + Create(ctx genericapirequest.Context, name string, obj runtime.Object, createValidation ValidateObjectFunc, includeUninitialized bool) (runtime.Object, error) } // UpdatedObjectInfo provides information about an updated object to an Updater. @@ -221,6 +221,26 @@ type UpdatedObjectInfo interface { UpdatedObject(ctx genericapirequest.Context, oldObj runtime.Object) (newObj runtime.Object, err error) } +// ValidateObjectFunc is a function to act on a given object. An error may be returned +// if the hook cannot be completed. An ObjectFunc may NOT transform the provided +// object. +type ValidateObjectFunc func(obj runtime.Object) error + +// ValidateAllObjectFunc is a "admit everything" instance of ValidateObjectFunc. +func ValidateAllObjectFunc(obj runtime.Object) error { + return nil +} + +// ValidateObjectUpdateFunc is a function to act on a given object and its predecessor. +// An error may be returned if the hook cannot be completed. An UpdateObjectFunc +// may NOT transform the provided object. +type ValidateObjectUpdateFunc func(obj, old runtime.Object) error + +// ValidateAllObjectUpdateFunc is a "admit everything" instance of ValidateObjectUpdateFunc. +func ValidateAllObjectUpdateFunc(obj, old runtime.Object) error { + return nil +} + // Updater is an object that can update an instance of a RESTful object. type Updater interface { // New returns an empty object that can be used with Update after request data has been put into it. @@ -230,14 +250,14 @@ type Updater interface { // Update finds a resource in the storage and updates it. Some implementations // may allow updates creates the object - they should set the created boolean // to true. - Update(ctx genericapirequest.Context, name string, objInfo UpdatedObjectInfo) (runtime.Object, bool, error) + Update(ctx genericapirequest.Context, name string, objInfo UpdatedObjectInfo, createValidation ValidateObjectFunc, updateValidation ValidateObjectUpdateFunc) (runtime.Object, bool, error) } // CreaterUpdater is a storage object that must support both create and update. // Go prevents embedded interfaces that implement the same method. type CreaterUpdater interface { Creater - Update(ctx genericapirequest.Context, name string, objInfo UpdatedObjectInfo) (runtime.Object, bool, error) + Update(ctx genericapirequest.Context, name string, objInfo UpdatedObjectInfo, createValidation ValidateObjectFunc, updateValidation ValidateObjectUpdateFunc) (runtime.Object, bool, error) } // CreaterUpdater must satisfy the Updater interface. diff --git a/staging/src/k8s.io/apiserver/pkg/registry/rest/resttest/resttest.go b/staging/src/k8s.io/apiserver/pkg/registry/rest/resttest/resttest.go index 1b19b6deaf7..11033454682 100644 --- a/staging/src/k8s.io/apiserver/pkg/registry/rest/resttest/resttest.go +++ b/staging/src/k8s.io/apiserver/pkg/registry/rest/resttest/resttest.go @@ -244,7 +244,7 @@ func (t *Tester) testCreateAlreadyExisting(obj runtime.Object, createFn CreateFu } defer t.delete(ctx, foo) - _, err := t.storage.(rest.Creater).Create(ctx, foo, false) + _, err := t.storage.(rest.Creater).Create(ctx, foo, rest.ValidateAllObjectFunc, false) if !errors.IsAlreadyExists(err) { t.Errorf("expected already exists err, got %v", err) } @@ -256,7 +256,7 @@ func (t *Tester) testCreateEquals(obj runtime.Object, getFn GetFunc) { foo := obj.DeepCopyObject() t.setObjectMeta(foo, t.namer(2)) - created, err := t.storage.(rest.Creater).Create(ctx, foo, false) + created, err := t.storage.(rest.Creater).Create(ctx, foo, rest.ValidateAllObjectFunc, false) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -284,7 +284,7 @@ func (t *Tester) testCreateDiscardsObjectNamespace(valid runtime.Object) { objectMeta.SetNamespace("not-default") // Ideally, we'd get an error back here, but at least verify the namespace wasn't persisted - created, err := t.storage.(rest.Creater).Create(t.TestContext(), valid.DeepCopyObject(), false) + created, err := t.storage.(rest.Creater).Create(t.TestContext(), valid.DeepCopyObject(), rest.ValidateAllObjectFunc, false) if err != nil { t.Fatalf("Unexpected error: %v", err) } @@ -300,7 +300,7 @@ func (t *Tester) testCreateGeneratesName(valid runtime.Object) { objectMeta.SetName("") objectMeta.SetGenerateName("test-") - created, err := t.storage.(rest.Creater).Create(t.TestContext(), valid, false) + created, err := t.storage.(rest.Creater).Create(t.TestContext(), valid, rest.ValidateAllObjectFunc, false) if err != nil { t.Fatalf("Unexpected error: %v", err) } @@ -315,7 +315,7 @@ func (t *Tester) testCreateHasMetadata(valid runtime.Object) { objectMeta.SetName(t.namer(1)) objectMeta.SetNamespace(t.TestNamespace()) - obj, err := t.storage.(rest.Creater).Create(t.TestContext(), valid, false) + obj, err := t.storage.(rest.Creater).Create(t.TestContext(), valid, rest.ValidateAllObjectFunc, false) if err != nil { t.Fatalf("Unexpected error: %v", err) } @@ -333,7 +333,7 @@ func (t *Tester) testCreateIgnoresContextNamespace(valid runtime.Object) { ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), "not-default2") // Ideally, we'd get an error back here, but at least verify the namespace wasn't persisted - created, err := t.storage.(rest.Creater).Create(ctx, valid.DeepCopyObject(), false) + created, err := t.storage.(rest.Creater).Create(ctx, valid.DeepCopyObject(), rest.ValidateAllObjectFunc, false) if err != nil { t.Fatalf("Unexpected error: %v", err) } @@ -352,7 +352,7 @@ func (t *Tester) testCreateIgnoresMismatchedNamespace(valid runtime.Object) { ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), "not-default2") // Ideally, we'd get an error back here, but at least verify the namespace wasn't persisted - created, err := t.storage.(rest.Creater).Create(ctx, valid.DeepCopyObject(), false) + created, err := t.storage.(rest.Creater).Create(ctx, valid.DeepCopyObject(), rest.ValidateAllObjectFunc, false) if err != nil { t.Fatalf("Unexpected error: %v", err) } @@ -370,7 +370,7 @@ func (t *Tester) testCreateValidatesNames(valid runtime.Object) { objCopyMeta.SetName(invalidName) ctx := t.TestContext() - _, err := t.storage.(rest.Creater).Create(ctx, objCopy, false) + _, err := t.storage.(rest.Creater).Create(ctx, objCopy, rest.ValidateAllObjectFunc, false) if !errors.IsInvalid(err) { t.Errorf("%s: Expected to get an invalid resource error, got '%v'", invalidName, err) } @@ -382,7 +382,7 @@ func (t *Tester) testCreateValidatesNames(valid runtime.Object) { objCopyMeta.SetName(objCopyMeta.GetName() + invalidSuffix) ctx := t.TestContext() - _, err := t.storage.(rest.Creater).Create(ctx, objCopy, false) + _, err := t.storage.(rest.Creater).Create(ctx, objCopy, rest.ValidateAllObjectFunc, false) if !errors.IsInvalid(err) { t.Errorf("%s: Expected to get an invalid resource error, got '%v'", invalidSuffix, err) } @@ -392,7 +392,7 @@ func (t *Tester) testCreateValidatesNames(valid runtime.Object) { func (t *Tester) testCreateInvokesValidation(invalid ...runtime.Object) { for i, obj := range invalid { ctx := t.TestContext() - _, err := t.storage.(rest.Creater).Create(ctx, obj, false) + _, err := t.storage.(rest.Creater).Create(ctx, obj, rest.ValidateAllObjectFunc, false) if !errors.IsInvalid(err) { t.Errorf("%d: Expected to get an invalid resource error, got %v", i, err) } @@ -403,7 +403,7 @@ func (t *Tester) testCreateRejectsMismatchedNamespace(valid runtime.Object) { objectMeta := t.getObjectMetaOrFail(valid) objectMeta.SetNamespace("not-default") - _, err := t.storage.(rest.Creater).Create(t.TestContext(), valid, false) + _, err := t.storage.(rest.Creater).Create(t.TestContext(), valid, rest.ValidateAllObjectFunc, false) if err == nil { t.Errorf("Expected an error, but we didn't get one") } else if !strings.Contains(err.Error(), "does not match the namespace sent on the request") { @@ -417,7 +417,7 @@ func (t *Tester) testCreateResetsUserData(valid runtime.Object) { objectMeta.SetUID("bad-uid") objectMeta.SetCreationTimestamp(now) - obj, err := t.storage.(rest.Creater).Create(t.TestContext(), valid, false) + obj, err := t.storage.(rest.Creater).Create(t.TestContext(), valid, rest.ValidateAllObjectFunc, false) if err != nil { t.Fatalf("Unexpected error: %v", err) } @@ -435,7 +435,7 @@ func (t *Tester) testCreateIgnoreClusterName(valid runtime.Object) { objectMeta.SetName(t.namer(3)) objectMeta.SetClusterName("clustername-to-ignore") - obj, err := t.storage.(rest.Creater).Create(t.TestContext(), valid.DeepCopyObject(), false) + obj, err := t.storage.(rest.Creater).Create(t.TestContext(), valid.DeepCopyObject(), rest.ValidateAllObjectFunc, false) if err != nil { t.Fatalf("Unexpected error: %v", err) } @@ -464,7 +464,7 @@ func (t *Tester) testUpdateEquals(obj runtime.Object, createFn CreateFunc, getFn } toUpdate = updateFn(toUpdate) toUpdateMeta := t.getObjectMetaOrFail(toUpdate) - updated, created, err := t.storage.(rest.Updater).Update(ctx, toUpdateMeta.GetName(), rest.DefaultUpdatedObjectInfo(toUpdate)) + updated, created, err := t.storage.(rest.Updater).Update(ctx, toUpdateMeta.GetName(), rest.DefaultUpdatedObjectInfo(toUpdate), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -504,7 +504,7 @@ func (t *Tester) testUpdateFailsOnVersionTooOld(obj runtime.Object, createFn Cre olderMeta := t.getObjectMetaOrFail(older) olderMeta.SetResourceVersion("1") - _, _, err = t.storage.(rest.Updater).Update(t.TestContext(), olderMeta.GetName(), rest.DefaultUpdatedObjectInfo(older)) + _, _, err = t.storage.(rest.Updater).Update(t.TestContext(), olderMeta.GetName(), rest.DefaultUpdatedObjectInfo(older), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc) if err == nil { t.Errorf("Expected an error, but we didn't get one") } else if !errors.IsConflict(err) { @@ -524,7 +524,7 @@ func (t *Tester) testUpdateInvokesValidation(obj runtime.Object, createFn Create for _, update := range invalidUpdateFn { toUpdate := update(foo.DeepCopyObject()) toUpdateMeta := t.getObjectMetaOrFail(toUpdate) - got, created, err := t.storage.(rest.Updater).Update(t.TestContext(), toUpdateMeta.GetName(), rest.DefaultUpdatedObjectInfo(toUpdate)) + got, created, err := t.storage.(rest.Updater).Update(t.TestContext(), toUpdateMeta.GetName(), rest.DefaultUpdatedObjectInfo(toUpdate), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc) if got != nil || created { t.Errorf("expected nil object and no creation for object: %v", toUpdate) } @@ -545,7 +545,7 @@ func (t *Tester) testUpdateWithWrongUID(obj runtime.Object, createFn CreateFunc, } objectMeta.SetUID(types.UID("UID1111")) - obj, created, err := t.storage.(rest.Updater).Update(ctx, objectMeta.GetName(), rest.DefaultUpdatedObjectInfo(foo)) + obj, created, err := t.storage.(rest.Updater).Update(ctx, objectMeta.GetName(), rest.DefaultUpdatedObjectInfo(foo), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc) if created || obj != nil { t.Errorf("expected nil object and no creation for object: %v", foo) } @@ -589,7 +589,7 @@ func (t *Tester) testUpdateRetrievesOldObject(obj runtime.Object, createFn Creat return updatedObject, nil } - updatedObj, created, err := t.storage.(rest.Updater).Update(ctx, objectMeta.GetName(), rest.DefaultUpdatedObjectInfo(storedFooWithUpdates, noopTransform)) + updatedObj, created, err := t.storage.(rest.Updater).Update(ctx, objectMeta.GetName(), rest.DefaultUpdatedObjectInfo(storedFooWithUpdates, noopTransform), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc) if err != nil { t.Errorf("unexpected error: %v", err) return @@ -624,7 +624,7 @@ func (t *Tester) testUpdatePropagatesUpdatedObjectError(obj runtime.Object, crea return nil, propagateErr } - _, _, err := t.storage.(rest.Updater).Update(ctx, name, rest.DefaultUpdatedObjectInfo(foo, noopTransform)) + _, _, err := t.storage.(rest.Updater).Update(ctx, name, rest.DefaultUpdatedObjectInfo(foo, noopTransform), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc) if err != propagateErr { t.Errorf("expected propagated error, got %#v", err) } @@ -650,7 +650,7 @@ func (t *Tester) testUpdateIgnoreGenerationUpdates(obj runtime.Object, createFn olderMeta := t.getObjectMetaOrFail(older) olderMeta.SetGeneration(2) - _, _, err = t.storage.(rest.Updater).Update(t.TestContext(), olderMeta.GetName(), rest.DefaultUpdatedObjectInfo(older)) + _, _, err = t.storage.(rest.Updater).Update(t.TestContext(), olderMeta.GetName(), rest.DefaultUpdatedObjectInfo(older), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc) if err != nil { t.Errorf("Unexpected error: %v", err) } @@ -666,7 +666,7 @@ func (t *Tester) testUpdateIgnoreGenerationUpdates(obj runtime.Object, createFn func (t *Tester) testUpdateOnNotFound(obj runtime.Object) { t.setObjectMeta(obj, t.namer(0)) - _, created, err := t.storage.(rest.Updater).Update(t.TestContext(), t.namer(0), rest.DefaultUpdatedObjectInfo(obj)) + _, created, err := t.storage.(rest.Updater).Update(t.TestContext(), t.namer(0), rest.DefaultUpdatedObjectInfo(obj), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc) if t.createOnUpdate { if err != nil { t.Errorf("creation allowed on updated, but got an error: %v", err) @@ -701,7 +701,7 @@ func (t *Tester) testUpdateRejectsMismatchedNamespace(obj runtime.Object, create objectMeta.SetName(t.namer(1)) objectMeta.SetNamespace("not-default") - obj, updated, err := t.storage.(rest.Updater).Update(t.TestContext(), "foo1", rest.DefaultUpdatedObjectInfo(storedFoo)) + obj, updated, err := t.storage.(rest.Updater).Update(t.TestContext(), "foo1", rest.DefaultUpdatedObjectInfo(storedFoo), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc) if obj != nil || updated { t.Errorf("expected nil object and not updated") } @@ -732,7 +732,7 @@ func (t *Tester) testUpdateIgnoreClusterName(obj runtime.Object, createFn Create olderMeta := t.getObjectMetaOrFail(older) olderMeta.SetClusterName("clustername-to-ignore") - _, _, err = t.storage.(rest.Updater).Update(t.TestContext(), olderMeta.GetName(), rest.DefaultUpdatedObjectInfo(older)) + _, _, err = t.storage.(rest.Updater).Update(t.TestContext(), olderMeta.GetName(), rest.DefaultUpdatedObjectInfo(older), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc) if err != nil { t.Errorf("Unexpected error: %v", err) } @@ -1064,14 +1064,14 @@ func (t *Tester) testGetDifferentNamespace(obj runtime.Object) { ctx1 := genericapirequest.WithNamespace(genericapirequest.NewContext(), "bar3") objMeta.SetNamespace(genericapirequest.NamespaceValue(ctx1)) - _, err := t.storage.(rest.Creater).Create(ctx1, obj, false) + _, err := t.storage.(rest.Creater).Create(ctx1, obj, rest.ValidateAllObjectFunc, false) if err != nil { t.Errorf("unexpected error: %v", err) } ctx2 := genericapirequest.WithNamespace(genericapirequest.NewContext(), "bar4") objMeta.SetNamespace(genericapirequest.NamespaceValue(ctx2)) - _, err = t.storage.(rest.Creater).Create(ctx2, obj, false) + _, err = t.storage.(rest.Creater).Create(ctx2, obj, rest.ValidateAllObjectFunc, false) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -1105,7 +1105,7 @@ func (t *Tester) testGetFound(obj runtime.Object) { ctx := t.TestContext() t.setObjectMeta(obj, t.namer(1)) - existing, err := t.storage.(rest.Creater).Create(ctx, obj, false) + existing, err := t.storage.(rest.Creater).Create(ctx, obj, rest.ValidateAllObjectFunc, false) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -1128,7 +1128,7 @@ func (t *Tester) testGetMimatchedNamespace(obj runtime.Object) { objMeta := t.getObjectMetaOrFail(obj) objMeta.SetName(t.namer(4)) objMeta.SetNamespace(genericapirequest.NamespaceValue(ctx1)) - _, err := t.storage.(rest.Creater).Create(ctx1, obj, false) + _, err := t.storage.(rest.Creater).Create(ctx1, obj, rest.ValidateAllObjectFunc, false) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -1147,7 +1147,7 @@ func (t *Tester) testGetMimatchedNamespace(obj runtime.Object) { func (t *Tester) testGetNotFound(obj runtime.Object) { ctx := t.TestContext() t.setObjectMeta(obj, t.namer(2)) - _, err := t.storage.(rest.Creater).Create(ctx, obj, false) + _, err := t.storage.(rest.Creater).Create(ctx, obj, rest.ValidateAllObjectFunc, false) if err != nil { t.Errorf("unexpected error: %v", err) } diff --git a/staging/src/k8s.io/apiserver/pkg/registry/rest/update.go b/staging/src/k8s.io/apiserver/pkg/registry/rest/update.go index 02b27544746..aaee5800c29 100644 --- a/staging/src/k8s.io/apiserver/pkg/registry/rest/update.go +++ b/staging/src/k8s.io/apiserver/pkg/registry/rest/update.go @@ -26,6 +26,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/validation/field" + "k8s.io/apiserver/pkg/admission" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" "k8s.io/apiserver/pkg/features" utilfeature "k8s.io/apiserver/pkg/util/feature" @@ -229,3 +230,28 @@ func (i *wrappedUpdatedObjectInfo) UpdatedObject(ctx genericapirequest.Context, return newObj, nil } + +// AdmissionToValidateObjectUpdateFunc converts validating admission to a rest validate object update func +func AdmissionToValidateObjectUpdateFunc(admit admission.Interface, staticAttributes admission.Attributes) ValidateObjectUpdateFunc { + validatingAdmission, ok := admit.(admission.ValidationInterface) + if !ok { + return func(obj, old runtime.Object) error { return nil } + } + return func(obj, old runtime.Object) error { + finalAttributes := admission.NewAttributesRecord( + obj, + old, + staticAttributes.GetKind(), + staticAttributes.GetNamespace(), + staticAttributes.GetName(), + staticAttributes.GetResource(), + staticAttributes.GetSubresource(), + staticAttributes.GetOperation(), + staticAttributes.GetUserInfo(), + ) + if !validatingAdmission.Handles(finalAttributes.GetOperation()) { + return nil + } + return validatingAdmission.Validate(finalAttributes) + } +} diff --git a/staging/src/k8s.io/kube-aggregator/pkg/registry/apiservice/etcd/etcd.go b/staging/src/k8s.io/kube-aggregator/pkg/registry/apiservice/etcd/etcd.go index 7a1ad78d097..b69f1a39d56 100644 --- a/staging/src/k8s.io/kube-aggregator/pkg/registry/apiservice/etcd/etcd.go +++ b/staging/src/k8s.io/kube-aggregator/pkg/registry/apiservice/etcd/etcd.go @@ -72,6 +72,6 @@ func (r *StatusREST) New() runtime.Object { } // Update alters the status subset of an object. -func (r *StatusREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { - return r.store.Update(ctx, name, objInfo) +func (r *StatusREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (runtime.Object, bool, error) { + return r.store.Update(ctx, name, objInfo, createValidation, updateValidation) } diff --git a/staging/src/k8s.io/sample-apiserver/pkg/admission/plugin/banflunder/admission.go b/staging/src/k8s.io/sample-apiserver/pkg/admission/plugin/banflunder/admission.go index 1b279763906..e0d1dc671c1 100644 --- a/staging/src/k8s.io/sample-apiserver/pkg/admission/plugin/banflunder/admission.go +++ b/staging/src/k8s.io/sample-apiserver/pkg/admission/plugin/banflunder/admission.go @@ -37,17 +37,17 @@ func Register(plugins *admission.Plugins) { }) } -type disallowFlunder struct { +type DisallowFlunder struct { *admission.Handler lister listers.FischerLister } -var _ = wardleinitializer.WantsInternalWardleInformerFactory(&disallowFlunder{}) +var _ = wardleinitializer.WantsInternalWardleInformerFactory(&DisallowFlunder{}) // Admit ensures that the object in-flight is of kind Flunder. // In addition checks that the Name is not on the banned list. // The list is stored in Fischers API objects. -func (d *disallowFlunder) Admit(a admission.Attributes) error { +func (d *DisallowFlunder) Admit(a admission.Attributes) error { // we are only interested in flunders if a.GetKind().GroupKind() != wardle.Kind("Flunder") { return nil @@ -80,12 +80,12 @@ func (d *disallowFlunder) Admit(a admission.Attributes) error { // SetInternalWardleInformerFactory gets Lister from SharedInformerFactory. // The lister knows how to lists Fischers. -func (d *disallowFlunder) SetInternalWardleInformerFactory(f informers.SharedInformerFactory) { +func (d *DisallowFlunder) SetInternalWardleInformerFactory(f informers.SharedInformerFactory) { d.lister = f.Wardle().InternalVersion().Fischers().Lister() } -// Validate checks whether the plugin was correctly initialized. -func (d *disallowFlunder) Validate() error { +// ValidaValidateInitializationte checks whether the plugin was correctly initialized. +func (d *DisallowFlunder) ValidateInitialization() error { if d.lister == nil { return fmt.Errorf("missing fischer lister") } @@ -93,8 +93,8 @@ func (d *disallowFlunder) Validate() error { } // New creates a new ban flunder admission plugin -func New() (admission.Interface, error) { - return &disallowFlunder{ +func New() (*DisallowFlunder, error) { + return &DisallowFlunder{ Handler: admission.NewHandler(admission.Create), }, nil } diff --git a/staging/src/k8s.io/sample-apiserver/pkg/admission/plugin/banflunder/admission_test.go b/staging/src/k8s.io/sample-apiserver/pkg/admission/plugin/banflunder/admission_test.go index 1b449abbb4a..5b08387b0e7 100644 --- a/staging/src/k8s.io/sample-apiserver/pkg/admission/plugin/banflunder/admission_test.go +++ b/staging/src/k8s.io/sample-apiserver/pkg/admission/plugin/banflunder/admission_test.go @@ -119,7 +119,7 @@ func TestBanflunderAdmissionPlugin(t *testing.T) { } targetInitializer.Initialize(target) - err = admission.Validate(target) + err = admission.ValidateInitialization(target) if err != nil { t.Fatalf("scenario %d: failed to initialize banflunder admission plugin due to =%v", index, err) } diff --git a/staging/src/k8s.io/sample-apiserver/pkg/admission/wardleinitializer/interfaces.go b/staging/src/k8s.io/sample-apiserver/pkg/admission/wardleinitializer/interfaces.go index 93a095b6f85..f5c6e41579b 100644 --- a/staging/src/k8s.io/sample-apiserver/pkg/admission/wardleinitializer/interfaces.go +++ b/staging/src/k8s.io/sample-apiserver/pkg/admission/wardleinitializer/interfaces.go @@ -24,5 +24,5 @@ import ( // WantsInternalWardleInformerFactory defines a function which sets InformerFactory for admission plugins that need it type WantsInternalWardleInformerFactory interface { SetInternalWardleInformerFactory(informers.SharedInformerFactory) - admission.Validator + admission.InitializationValidator } diff --git a/staging/src/k8s.io/sample-apiserver/pkg/admission/wardleinitializer/wardleinitializer_test.go b/staging/src/k8s.io/sample-apiserver/pkg/admission/wardleinitializer/wardleinitializer_test.go index 9c306e315eb..221876a617c 100644 --- a/staging/src/k8s.io/sample-apiserver/pkg/admission/wardleinitializer/wardleinitializer_test.go +++ b/staging/src/k8s.io/sample-apiserver/pkg/admission/wardleinitializer/wardleinitializer_test.go @@ -52,7 +52,7 @@ func (self *wantInternalWardleInformerFactory) SetInternalWardleInformerFactory( } func (self *wantInternalWardleInformerFactory) Admit(a admission.Attributes) error { return nil } func (self *wantInternalWardleInformerFactory) Handles(o admission.Operation) bool { return false } -func (self *wantInternalWardleInformerFactory) Validate() error { return nil } +func (self *wantInternalWardleInformerFactory) ValidateInitialization() error { return nil } var _ admission.Interface = &wantInternalWardleInformerFactory{} var _ wardleinitializer.WantsInternalWardleInformerFactory = &wantInternalWardleInformerFactory{} diff --git a/test/integration/auth/node_test.go b/test/integration/auth/node_test.go index 3ec65723b25..7bcb693c309 100644 --- a/test/integration/auth/node_test.go +++ b/test/integration/auth/node_test.go @@ -83,7 +83,7 @@ func TestNodeAuthorizer(t *testing.T) { // Set up NodeRestriction admission nodeRestrictionAdmission := noderestriction.NewPlugin(nodeidentifier.NewDefaultNodeIdentifier()) nodeRestrictionAdmission.SetInternalKubeClientSet(superuserClient) - if err := nodeRestrictionAdmission.Validate(); err != nil { + if err := nodeRestrictionAdmission.ValidateInitialization(); err != nil { t.Fatal(err) }