admission: complete plumbing of validation admission

This commit is contained in:
Dr. Stefan Schimanski 2017-10-24 14:08:34 +02:00
parent d4f48c9313
commit 74b4223ab8
11 changed files with 103 additions and 58 deletions

View File

@ -38,6 +38,11 @@ func (AlwaysAdmit) Admit(a admission.Attributes) (err error) {
return nil return nil
} }
// ValidatingAdmit makes an admission decision based on the request attributes. It is NOT allowed to mutate
func (AlwaysAdmit) ValidatingAdmit(a admission.Attributes) (err error) {
return nil
}
// Handles returns true if this admission controller can handle the given operation // Handles returns true if this admission controller can handle the given operation
// where operation can be one of CREATE, UPDATE, DELETE, or CONNECT // where operation can be one of CREATE, UPDATE, DELETE, or CONNECT
func (AlwaysAdmit) Handles(operation admission.Operation) bool { func (AlwaysAdmit) Handles(operation admission.Operation) bool {

View File

@ -67,6 +67,8 @@ type MutationInterface interface {
// ValidationInterface is an abstract, pluggable interface for Admission Control decisions. // ValidationInterface is an abstract, pluggable interface for Admission Control decisions.
type ValidationInterface interface { type ValidationInterface interface {
Interface
// ValidatingAdmit makes an admission decision based on the request attributes. It is NOT allowed to mutate // ValidatingAdmit makes an admission decision based on the request attributes. It is NOT allowed to mutate
ValidatingAdmit(a Attributes) (err error) ValidatingAdmit(a Attributes) (err error)
} }

View File

@ -34,7 +34,7 @@ import (
utiltrace "k8s.io/apiserver/pkg/util/trace" utiltrace "k8s.io/apiserver/pkg/util/trace"
) )
func createHandler(r rest.NamedCreater, scope RequestScope, typer runtime.ObjectTyper, mutatingAdmission admission.Interface, validatingAdmission admission.ValidationInterface, includeName bool) http.HandlerFunc { func createHandler(r rest.NamedCreater, scope RequestScope, typer runtime.ObjectTyper, admit admission.Interface, includeName bool) http.HandlerFunc {
return func(w http.ResponseWriter, req *http.Request) { return func(w http.ResponseWriter, req *http.Request) {
// For performance tracking purposes. // For performance tracking purposes.
trace := utiltrace.New("Create " + req.URL.Path) trace := utiltrace.New("Create " + req.URL.Path)
@ -95,7 +95,7 @@ func createHandler(r rest.NamedCreater, scope RequestScope, typer runtime.Object
userInfo, _ := request.UserFrom(ctx) userInfo, _ := request.UserFrom(ctx)
admissionAttributes := admission.NewAttributesRecord(obj, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Create, userInfo) admissionAttributes := admission.NewAttributesRecord(obj, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Create, userInfo)
if mutatingAdmission != nil && mutatingAdmission.Handles(admission.Create) { if mutatingAdmission, ok := admit.(admission.MutationInterface); ok && mutatingAdmission.Handles(admission.Create) {
err = mutatingAdmission.Admit(admissionAttributes) err = mutatingAdmission.Admit(admissionAttributes)
if err != nil { if err != nil {
scope.err(err, w, req) scope.err(err, w, req)
@ -112,7 +112,7 @@ func createHandler(r rest.NamedCreater, scope RequestScope, typer runtime.Object
ctx, ctx,
name, name,
obj, obj,
rest.AdmissionToValidateObjectFunc(validatingAdmission, admissionAttributes), rest.AdmissionToValidateObjectFunc(admit, admissionAttributes),
includeUninitialized, includeUninitialized,
) )
}) })
@ -150,13 +150,13 @@ func createHandler(r rest.NamedCreater, scope RequestScope, typer runtime.Object
} }
// CreateNamedResource returns a function that will handle a resource creation with name. // CreateNamedResource returns a function that will handle a resource creation with name.
func CreateNamedResource(r rest.NamedCreater, scope RequestScope, typer runtime.ObjectTyper, mutatingAdmission admission.Interface, validatingAdmission admission.ValidationInterface) http.HandlerFunc { func CreateNamedResource(r rest.NamedCreater, scope RequestScope, typer runtime.ObjectTyper, admission admission.Interface) http.HandlerFunc {
return createHandler(r, scope, typer, mutatingAdmission, validatingAdmission, true) return createHandler(r, scope, typer, admission, true)
} }
// CreateResource returns a function that will handle a resource creation. // CreateResource returns a function that will handle a resource creation.
func CreateResource(r rest.Creater, scope RequestScope, typer runtime.ObjectTyper, mutatingAdmission admission.Interface, validatingAdmission admission.ValidationInterface) http.HandlerFunc { func CreateResource(r rest.Creater, scope RequestScope, typer runtime.ObjectTyper, admission admission.Interface) http.HandlerFunc {
return createHandler(&namedCreaterAdapter{r}, scope, typer, mutatingAdmission, validatingAdmission, false) return createHandler(&namedCreaterAdapter{r}, scope, typer, admission, false)
} }
type namedCreaterAdapter struct { type namedCreaterAdapter struct {

View File

@ -35,7 +35,7 @@ import (
// DeleteResource returns a function that will handle a resource deletion // DeleteResource returns a function that will handle a resource deletion
// TODO admission here becomes solely validating admission // TODO admission here becomes solely validating admission
func DeleteResource(r rest.GracefulDeleter, allowsOptions bool, scope RequestScope, mutatingAdmission admission.Interface, validatingAdmission admission.ValidationInterface) http.HandlerFunc { func DeleteResource(r rest.GracefulDeleter, allowsOptions bool, scope RequestScope, admit admission.Interface) http.HandlerFunc {
return func(w http.ResponseWriter, req *http.Request) { return func(w http.ResponseWriter, req *http.Request) {
// For performance tracking purposes. // For performance tracking purposes.
trace := utiltrace.New("Delete " + req.URL.Path) trace := utiltrace.New("Delete " + req.URL.Path)
@ -94,7 +94,7 @@ func DeleteResource(r rest.GracefulDeleter, allowsOptions bool, scope RequestSco
} }
trace.Step("About to check admission control") trace.Step("About to check admission control")
if mutatingAdmission != nil && mutatingAdmission.Handles(admission.Delete) { if mutatingAdmission, ok := admit.(admission.MutationInterface); ok && mutatingAdmission.Handles(admission.Delete) {
userInfo, _ := request.UserFrom(ctx) userInfo, _ := request.UserFrom(ctx)
err = mutatingAdmission.Admit(admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Delete, userInfo)) err = mutatingAdmission.Admit(admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Delete, userInfo))
@ -103,7 +103,8 @@ func DeleteResource(r rest.GracefulDeleter, allowsOptions bool, scope RequestSco
return return
} }
} }
if validatingAdmission != nil && validatingAdmission.Handles(admission.Delete) { // TODO: avoid calling Handles twice
if validatingAdmission, ok := admit.(admission.ValidationInterface); ok && validatingAdmission.Handles(admission.Delete) {
userInfo, _ := request.UserFrom(ctx) userInfo, _ := request.UserFrom(ctx)
err = validatingAdmission.ValidatingAdmit(admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Delete, userInfo)) err = validatingAdmission.ValidatingAdmit(admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Delete, userInfo))
@ -167,7 +168,7 @@ func DeleteResource(r rest.GracefulDeleter, allowsOptions bool, scope RequestSco
} }
// DeleteCollection returns a function that will handle a collection deletion // DeleteCollection returns a function that will handle a collection deletion
func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope RequestScope, mutatingAdmission admission.Interface, validatingAdmission admission.ValidationInterface) http.HandlerFunc { func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope RequestScope, admit admission.Interface) http.HandlerFunc {
return func(w http.ResponseWriter, req *http.Request) { return func(w http.ResponseWriter, req *http.Request) {
// TODO: we either want to remove timeout or document it (if we document, move timeout out of this function and declare it in api_installer) // TODO: we either want to remove timeout or document it (if we document, move timeout out of this function and declare it in api_installer)
timeout := parseTimeout(req.URL.Query().Get("timeout")) timeout := parseTimeout(req.URL.Query().Get("timeout"))
@ -181,7 +182,7 @@ func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope RequestSco
ctx := scope.ContextFunc(req) ctx := scope.ContextFunc(req)
ctx = request.WithNamespace(ctx, namespace) ctx = request.WithNamespace(ctx, namespace)
if mutatingAdmission != nil && mutatingAdmission.Handles(admission.Delete) { if mutatingAdmission, ok := admit.(admission.MutationInterface); ok && mutatingAdmission.Handles(admission.Delete) {
userInfo, _ := request.UserFrom(ctx) userInfo, _ := request.UserFrom(ctx)
err = mutatingAdmission.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))
@ -190,7 +191,8 @@ func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope RequestSco
return return
} }
} }
if validatingAdmission != nil && validatingAdmission.Handles(admission.Delete) { // TODO: avoid calling Handles twice
if validatingAdmission, ok := admit.(admission.ValidationInterface); ok && validatingAdmission.Handles(admission.Delete) {
userInfo, _ := request.UserFrom(ctx) userInfo, _ := request.UserFrom(ctx)
err = validatingAdmission.ValidatingAdmit(admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, "", scope.Resource, scope.Subresource, admission.Delete, userInfo)) err = validatingAdmission.ValidatingAdmit(admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, "", scope.Resource, scope.Subresource, admission.Delete, userInfo))

View File

@ -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}), scope.Serializer.DecoderToVersion(s.Serializer, schema.GroupVersion{Group: gv.Group, Version: runtime.APIVersionInternal}),
) )
updateAdmit := func(updatedObject runtime.Object, currentObject runtime.Object) error { userInfo, _ := request.UserFrom(ctx)
if admit != nil && admit.Handles(admission.Update) { staticAdmissionAttributes := admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Update, userInfo)
userInfo, _ := request.UserFrom(ctx) updateMutation := func(updatedObject runtime.Object, currentObject runtime.Object) error {
return admit.Admit(admission.NewAttributesRecord(updatedObject, currentObject, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Update, userInfo)) 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 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) scope.Namer, scope.Creater, scope.Defaulter, scope.UnsafeConvertor, scope.Kind, scope.Resource, codec)
if err != nil { if err != nil {
scope.err(err, w, req) 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 // patchResource divides PatchResource for easier unit testing
func patchResource( func patchResource(
ctx request.Context, ctx request.Context,
admit updateAdmissionFunc, updateMutation mutateObjectUpdateFunc,
createValidation rest.ValidateObjectFunc,
updateValidation rest.ValidateObjectUpdateFunc,
timeout time.Duration, timeout time.Duration,
versionedObj runtime.Object, versionedObj runtime.Object,
patcher rest.Patcher, patcher rest.Patcher,
@ -341,16 +352,15 @@ func patchResource(
// applyAdmission is called every time GuaranteedUpdate asks for the updated object, // applyAdmission is called every time GuaranteedUpdate asks for the updated object,
// and is given the currently persisted object and the patched object as input. // 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) { 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) updatedObjectInfo := rest.DefaultUpdatedObjectInfo(nil, applyPatch, applyAdmission)
return finishRequest(timeout, func() (runtime.Object, error) { 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++ { for i := 0; i < MaxRetryWhenPatchConflicts && (errors.IsConflict(updateErr)); i++ {
lastConflictErr = updateErr lastConflictErr = updateErr
updateObject, _, updateErr = patcher.Update(ctx, name, updatedObjectInfo) updateObject, _, updateErr = patcher.Update(ctx, name, updatedObjectInfo, createValidation, updateValidation)
} }
return updateObject, updateErr return updateObject, updateErr
}) })

View File

@ -117,11 +117,20 @@ func ConnectResource(connecter rest.Connecter, scope RequestScope, admit admissi
ResourcePath: restPath, ResourcePath: restPath,
} }
userInfo, _ := request.UserFrom(ctx) userInfo, _ := request.UserFrom(ctx)
// TODO: remove the mutating admission here as soon as we have ported all plugin that handle CONNECT
err = admit.Admit(admission.NewAttributesRecord(connectRequest, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Connect, userInfo)) if mutatingAdmit, ok := admit.(admission.MutationInterface); ok {
if err != nil { err = mutatingAdmit.Admit(admission.NewAttributesRecord(connectRequest, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Connect, userInfo))
scope.err(err, w, req) if err != nil {
return 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) requestInfo, _ := request.RequestInfoFrom(ctx)

View File

@ -137,7 +137,7 @@ func (p *testPatcher) New() runtime.Object {
return &example.Pod{} 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 currentPod := p.startingPod
if p.numUpdates > 0 { if p.numUpdates > 0 {
currentPod = p.updatePod currentPod = p.updatePod
@ -202,7 +202,8 @@ type patchTestCase struct {
name string name string
// admission chain to use, nil is fine // 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 is used as the starting point for the first Update
startingPod *example.Pod startingPod *example.Pod
@ -224,9 +225,16 @@ func (tc *patchTestCase) Run(t *testing.T) {
name := tc.startingPod.Name name := tc.startingPod.Name
codec := codecs.LegacyCodec(examplev1.SchemeGroupVersion) codec := codecs.LegacyCodec(examplev1.SchemeGroupVersion)
admit := tc.admit
if admit == nil { admissionMutation := tc.admissionMutation
admit = func(updatedObject runtime.Object, currentObject runtime.Object) error { 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 return nil
} }
} }
@ -288,7 +296,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 len(tc.expectedError) != 0 {
if err == nil || err.Error() != tc.expectedError { if err == nil || err.Error() != tc.expectedError {
t.Errorf("%s: expected error %v, but got %v", tc.name, tc.expectedError, err) t.Errorf("%s: expected error %v, but got %v", tc.name, tc.expectedError, err)
@ -511,7 +530,7 @@ func TestPatchWithAdmissionRejection(t *testing.T) {
tc := &patchTestCase{ tc := &patchTestCase{
name: "TestPatchWithAdmissionRejection", 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") return errors.New("admission failure")
}, },
@ -550,7 +569,7 @@ func TestPatchWithVersionConflictThenAdmissionFailure(t *testing.T) {
tc := &patchTestCase{ tc := &patchTestCase{
name: "TestPatchWithVersionConflictThenAdmissionFailure", name: "TestPatchWithVersionConflictThenAdmissionFailure",
admit: func(updatedObject runtime.Object, currentObject runtime.Object) error { admissionMutation: func(updatedObject runtime.Object, currentObject runtime.Object) error {
if seen { if seen {
return errors.New("admission failure") return errors.New("admission failure")
} }

View File

@ -33,7 +33,7 @@ import (
) )
// UpdateResource returns a function that will handle a resource update // UpdateResource returns a function that will handle a resource update
func UpdateResource(r rest.Updater, scope RequestScope, typer runtime.ObjectTyper, mutatingAdmission admission.Interface, validatingAdmission admission.ValidationInterface) http.HandlerFunc { func UpdateResource(r rest.Updater, scope RequestScope, typer runtime.ObjectTyper, admit admission.Interface) http.HandlerFunc {
return func(w http.ResponseWriter, req *http.Request) { return func(w http.ResponseWriter, req *http.Request) {
// For performance tracking purposes. // For performance tracking purposes.
trace := utiltrace.New("Update " + req.URL.Path) trace := utiltrace.New("Update " + req.URL.Path)
@ -89,7 +89,7 @@ func UpdateResource(r rest.Updater, scope RequestScope, typer runtime.ObjectType
userInfo, _ := request.UserFrom(ctx) userInfo, _ := request.UserFrom(ctx)
staticAdmissionAttributes := admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Update, userInfo) staticAdmissionAttributes := admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Update, userInfo)
var transformers []rest.TransformFunc var transformers []rest.TransformFunc
if mutatingAdmission != nil && mutatingAdmission.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) { transformers = append(transformers, func(ctx request.Context, newObj, oldObj runtime.Object) (runtime.Object, error) {
return newObj, mutatingAdmission.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))
}) })
@ -102,8 +102,8 @@ func UpdateResource(r rest.Updater, scope RequestScope, typer runtime.ObjectType
ctx, ctx,
name, name,
rest.DefaultUpdatedObjectInfo(obj, transformers...), rest.DefaultUpdatedObjectInfo(obj, transformers...),
rest.AdmissionToValidateObjectFunc(validatingAdmission, staticAdmissionAttributes), rest.AdmissionToValidateObjectFunc(admit, staticAdmissionAttributes),
rest.AdmissionToValidateObjectUpdateFunc(validatingAdmission, staticAdmissionAttributes), rest.AdmissionToValidateObjectUpdateFunc(admit, staticAdmissionAttributes),
) )
wasCreated = created wasCreated = created
return obj, err return obj, err

View File

@ -52,16 +52,6 @@ import (
// object. // object.
type ObjectFunc func(obj runtime.Object) error type ObjectFunc func(obj runtime.Object) 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
// 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
// GenericStore interface can be used for type assertions when we need to access the underlying strategies. // GenericStore interface can be used for type assertions when we need to access the underlying strategies.
type GenericStore interface { type GenericStore interface {
GetCreateStrategy() rest.RESTCreateStrategy GetCreateStrategy() rest.RESTCreateStrategy
@ -315,7 +305,7 @@ func (e *Store) ListPredicate(ctx genericapirequest.Context, p storage.Selection
} }
// Create inserts a new item according to the unique key from the object. // Create inserts a new item according to the unique key from the object.
func (e *Store) Create(ctx genericapirequest.Context, obj runtime.Object, createValidation ValidateObjectFunc, 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 { if err := rest.BeforeCreate(e.CreateStrategy, ctx, obj); err != nil {
return nil, err return nil, err
} }
@ -514,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 // 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. // 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. // 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, createValidation ValidateObjectFunc, updateValidation ValidateObjectUpdateFunc) (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) key, err := e.KeyFunc(ctx, name)
if err != nil { if err != nil {
return nil, false, err return nil, false, err

View File

@ -154,7 +154,11 @@ type NamespaceScopedStrategy interface {
} }
// AdmissionToValidateObjectFunc converts validating admission to a rest validate object func // AdmissionToValidateObjectFunc converts validating admission to a rest validate object func
func AdmissionToValidateObjectFunc(validatingAdmission admission.ValidationInterface, staticAttributes admission.Attributes) ValidateObjectFunc { 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 { return func(obj runtime.Object) error {
finalAttributes := admission.NewAttributesRecord( finalAttributes := admission.NewAttributesRecord(
obj, obj,
@ -170,6 +174,6 @@ func AdmissionToValidateObjectFunc(validatingAdmission admission.ValidationInter
if !validatingAdmission.Handles(finalAttributes.GetOperation()) { if !validatingAdmission.Handles(finalAttributes.GetOperation()) {
return nil return nil
} }
return validatingAdmission.Validate(finalAttributes) return validatingAdmission.ValidatingAdmit(finalAttributes)
} }
} }

View File

@ -232,7 +232,11 @@ func (i *wrappedUpdatedObjectInfo) UpdatedObject(ctx genericapirequest.Context,
} }
// AdmissionToValidateObjectUpdateFunc converts validating admission to a rest validate object update func // AdmissionToValidateObjectUpdateFunc converts validating admission to a rest validate object update func
func AdmissionToValidateObjectUpdateFunc(validatingAdmission admission.ValidationInterface, staticAttributes admission.Attributes) ValidateObjectUpdateFunc { 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 { return func(obj, old runtime.Object) error {
finalAttributes := admission.NewAttributesRecord( finalAttributes := admission.NewAttributesRecord(
obj, obj,
@ -248,6 +252,6 @@ func AdmissionToValidateObjectUpdateFunc(validatingAdmission admission.Validatio
if !validatingAdmission.Handles(finalAttributes.GetOperation()) { if !validatingAdmission.Handles(finalAttributes.GetOperation()) {
return nil return nil
} }
return validatingAdmission.Validate(finalAttributes) return validatingAdmission.ValidatingAdmit(finalAttributes)
} }
} }