From 29252acd1ad80ca492c800713d9bc329282e9ed8 Mon Sep 17 00:00:00 2001 From: Jordan Liggitt Date: Thu, 28 Apr 2016 21:21:35 -0400 Subject: [PATCH] Change rest storage Update interface to retrieve updated object Add OldObject to admission attributes Update resthandler Patch/Update admission plumbing --- cmd/integration/integration.go | 100 +++++++++++ federation/registry/cluster/etcd/etcd.go | 5 +- federation/registry/cluster/registry.go | 2 +- pkg/admission/attributes.go | 8 +- pkg/admission/chain_test.go | 2 +- pkg/admission/interfaces.go | 2 + pkg/api/rest/rest.go | 17 +- pkg/api/rest/resttest/resttest.go | 92 +++++++++- pkg/api/rest/update.go | 70 ++++++++ pkg/apiserver/api_installer.go | 1 + pkg/apiserver/apiserver.go | 1 + pkg/apiserver/apiserver_test.go | 14 +- pkg/apiserver/resthandler.go | 163 ++++++++++++------ pkg/apiserver/resthandler_test.go | 84 +++++++-- pkg/genericapiserver/genericapiserver.go | 1 + pkg/master/master.go | 1 + pkg/registry/configmap/registry.go | 2 +- pkg/registry/controller/etcd/etcd.go | 21 ++- pkg/registry/controller/etcd/etcd_test.go | 9 +- pkg/registry/controller/registry.go | 2 +- pkg/registry/daemonset/etcd/etcd.go | 5 +- pkg/registry/deployment/etcd/etcd.go | 26 ++- pkg/registry/deployment/etcd/etcd_test.go | 7 +- pkg/registry/deployment/registry.go | 2 +- pkg/registry/endpoint/registry.go | 2 +- .../experimental/controller/etcd/etcd.go | 14 +- .../experimental/controller/etcd/etcd_test.go | 3 +- pkg/registry/generic/registry/store.go | 60 +++---- pkg/registry/generic/registry/store_test.go | 15 +- .../horizontalpodautoscaler/etcd/etcd.go | 5 +- pkg/registry/ingress/etcd/etcd.go | 5 +- pkg/registry/job/etcd/etcd.go | 5 +- pkg/registry/namespace/etcd/etcd.go | 9 +- pkg/registry/namespace/registry.go | 2 +- pkg/registry/node/etcd/etcd.go | 4 +- pkg/registry/node/registry.go | 2 +- pkg/registry/persistentvolume/etcd/etcd.go | 5 +- .../persistentvolume/etcd/etcd_test.go | 3 +- .../persistentvolumeclaim/etcd/etcd.go | 5 +- .../persistentvolumeclaim/etcd/etcd_test.go | 3 +- pkg/registry/petset/etcd/etcd.go | 5 +- pkg/registry/petset/etcd/etcd_test.go | 3 +- pkg/registry/pod/etcd/etcd.go | 4 +- pkg/registry/pod/etcd/etcd_test.go | 16 +- pkg/registry/poddisruptionbudget/etcd/etcd.go | 5 +- .../poddisruptionbudget/etcd/etcd_test.go | 3 +- pkg/registry/replicaset/etcd/etcd.go | 24 ++- pkg/registry/replicaset/etcd/etcd_test.go | 11 +- pkg/registry/replicaset/registry.go | 2 +- pkg/registry/resourcequota/etcd/etcd.go | 5 +- pkg/registry/resourcequota/etcd/etcd_test.go | 3 +- pkg/registry/secret/registry.go | 2 +- pkg/registry/service/etcd/etcd.go | 5 +- pkg/registry/service/registry.go | 2 +- pkg/registry/service/rest.go | 17 +- pkg/registry/service/rest_test.go | 50 +++--- pkg/registry/serviceaccount/registry.go | 2 +- .../thirdpartyresourcedata/registry.go | 2 +- .../alwayspullimages/admission_test.go | 4 +- .../admission/antiaffinity/admission_test.go | 5 +- plugin/pkg/admission/deny/admission_test.go | 2 +- plugin/pkg/admission/exec/admission_test.go | 4 +- .../initialresources/admission_test.go | 2 +- .../admission/limitranger/admission_test.go | 16 +- .../namespace/autoprovision/admission_test.go | 8 +- .../namespace/lifecycle/admission_test.go | 18 +- .../persistentvolume/label/admission_test.go | 14 +- .../admission/resourcequota/admission_test.go | 24 +-- .../podsecuritypolicy/admission_test.go | 2 +- .../securitycontext/scdeny/admission_test.go | 6 +- .../serviceaccount/admission_test.go | 52 +++--- 71 files changed, 772 insertions(+), 325 deletions(-) diff --git a/cmd/integration/integration.go b/cmd/integration/integration.go index 1d6abfd488..431a151923 100644 --- a/cmd/integration/integration.go +++ b/cmd/integration/integration.go @@ -53,6 +53,7 @@ import ( "k8s.io/kubernetes/pkg/kubelet/dockertools" "k8s.io/kubernetes/pkg/labels" "k8s.io/kubernetes/pkg/master" + "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util" "k8s.io/kubernetes/pkg/util/flag" "k8s.io/kubernetes/pkg/util/flowcontrol" @@ -647,6 +648,105 @@ func runPatchTest(c *client.Client) { } } + // Test patch with a resource that allows create on update + endpointTemplate := &api.Endpoints{ + ObjectMeta: api.ObjectMeta{Name: "patchendpoint"}, + Subsets: []api.EndpointSubset{ + { + Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}}, + Ports: []api.EndpointPort{{Port: 80, Protocol: api.ProtocolTCP}}, + }, + }, + } + + patchEndpoint := func(json []byte) (runtime.Object, error) { + return c.Patch(api.MergePatchType).Resource("endpoints").Namespace(api.NamespaceDefault).Name("patchendpoint").Body(json).Do().Get() + } + + // Make sure patch doesn't get to CreateOnUpdate + { + endpointJSON, err := runtime.Encode(api.Codecs.LegacyCodec(v1.SchemeGroupVersion), endpointTemplate) + if err != nil { + glog.Fatalf("Failed creating endpoint JSON: %v", err) + } + if obj, err := patchEndpoint(endpointJSON); !apierrors.IsNotFound(err) { + glog.Fatalf("Expected notfound creating from patch, got error=%v and object: %#v", err, obj) + } + } + + // Create the endpoint (endpoints set AllowCreateOnUpdate=true) to get a UID and resource version + createdEndpoint, err := c.Endpoints(api.NamespaceDefault).Update(endpointTemplate) + if err != nil { + glog.Fatalf("Failed creating endpoint: %v", err) + } + + // Make sure identity patch is accepted + { + endpointJSON, err := runtime.Encode(api.Codecs.LegacyCodec(v1.SchemeGroupVersion), createdEndpoint) + if err != nil { + glog.Fatalf("Failed creating endpoint JSON: %v", err) + } + if _, err := patchEndpoint(endpointJSON); err != nil { + glog.Fatalf("Failed patching endpoint: %v", err) + } + } + + // Make sure patch complains about a mismatched resourceVersion + { + endpointTemplate.Name = "" + endpointTemplate.UID = "" + endpointTemplate.ResourceVersion = "1" + endpointJSON, err := runtime.Encode(api.Codecs.LegacyCodec(v1.SchemeGroupVersion), endpointTemplate) + if err != nil { + glog.Fatalf("Failed creating endpoint JSON: %v", err) + } + if _, err := patchEndpoint(endpointJSON); !apierrors.IsConflict(err) { + glog.Fatalf("Expected error, got %#v", err) + } + } + + // Make sure patch complains about mutating the UID + { + endpointTemplate.Name = "" + endpointTemplate.UID = "abc" + endpointTemplate.ResourceVersion = "" + endpointJSON, err := runtime.Encode(api.Codecs.LegacyCodec(v1.SchemeGroupVersion), endpointTemplate) + if err != nil { + glog.Fatalf("Failed creating endpoint JSON: %v", err) + } + if _, err := patchEndpoint(endpointJSON); !apierrors.IsInvalid(err) { + glog.Fatalf("Expected error, got %#v", err) + } + } + + // Make sure patch complains about a mismatched name + { + endpointTemplate.Name = "changedname" + endpointTemplate.UID = "" + endpointTemplate.ResourceVersion = "" + endpointJSON, err := runtime.Encode(api.Codecs.LegacyCodec(v1.SchemeGroupVersion), endpointTemplate) + if err != nil { + glog.Fatalf("Failed creating endpoint JSON: %v", err) + } + if _, err := patchEndpoint(endpointJSON); !apierrors.IsBadRequest(err) { + glog.Fatalf("Expected error, got %#v", err) + } + } + + // Make sure patch containing originally submitted JSON is accepted + { + endpointTemplate.Name = "" + endpointTemplate.UID = "" + endpointTemplate.ResourceVersion = "" + endpointJSON, err := runtime.Encode(api.Codecs.LegacyCodec(v1.SchemeGroupVersion), endpointTemplate) + if err != nil { + glog.Fatalf("Failed creating endpoint JSON: %v", err) + } + if _, err := patchEndpoint(endpointJSON); err != nil { + glog.Fatalf("Failed patching endpoint: %v", err) + } + } + glog.Info("PATCHs work.") } diff --git a/federation/registry/cluster/etcd/etcd.go b/federation/registry/cluster/etcd/etcd.go index 6b4cd179bc..99f3f9a2cd 100644 --- a/federation/registry/cluster/etcd/etcd.go +++ b/federation/registry/cluster/etcd/etcd.go @@ -20,6 +20,7 @@ import ( "k8s.io/kubernetes/federation/apis/federation" "k8s.io/kubernetes/federation/registry/cluster" "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/rest" "k8s.io/kubernetes/pkg/registry/generic" "k8s.io/kubernetes/pkg/registry/generic/registry" "k8s.io/kubernetes/pkg/runtime" @@ -38,8 +39,8 @@ func (r *StatusREST) New() runtime.Object { } // Update alters the status subset of an object. -func (r *StatusREST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) { - return r.store.Update(ctx, obj) +func (r *StatusREST) Update(ctx api.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { + return r.store.Update(ctx, name, objInfo) } // NewREST returns a RESTStorage object that will work against clusters. diff --git a/federation/registry/cluster/registry.go b/federation/registry/cluster/registry.go index 361ca91d14..99a41e63cc 100644 --- a/federation/registry/cluster/registry.go +++ b/federation/registry/cluster/registry.go @@ -71,7 +71,7 @@ func (s *storage) CreateCluster(ctx api.Context, cluster *federation.Cluster) er } func (s *storage) UpdateCluster(ctx api.Context, cluster *federation.Cluster) error { - _, _, err := s.Update(ctx, cluster) + _, _, err := s.Update(ctx, cluster.Name, rest.DefaultUpdatedObjectInfo(cluster, api.Scheme)) return err } diff --git a/pkg/admission/attributes.go b/pkg/admission/attributes.go index 4e53931a85..d3b8dd080c 100644 --- a/pkg/admission/attributes.go +++ b/pkg/admission/attributes.go @@ -30,10 +30,11 @@ type attributesRecord struct { subresource string operation Operation object runtime.Object + oldObject runtime.Object userInfo user.Info } -func NewAttributesRecord(object runtime.Object, kind unversioned.GroupVersionKind, namespace, name string, resource unversioned.GroupVersionResource, subresource string, operation Operation, userInfo user.Info) Attributes { +func NewAttributesRecord(object runtime.Object, oldObject runtime.Object, kind unversioned.GroupVersionKind, namespace, name string, resource unversioned.GroupVersionResource, subresource string, operation Operation, userInfo user.Info) Attributes { return &attributesRecord{ kind: kind, namespace: namespace, @@ -42,6 +43,7 @@ func NewAttributesRecord(object runtime.Object, kind unversioned.GroupVersionKin subresource: subresource, operation: operation, object: object, + oldObject: oldObject, userInfo: userInfo, } } @@ -74,6 +76,10 @@ func (record *attributesRecord) GetObject() runtime.Object { return record.object } +func (record *attributesRecord) GetOldObject() runtime.Object { + return record.oldObject +} + func (record *attributesRecord) GetUserInfo() user.Info { return record.userInfo } diff --git a/pkg/admission/chain_test.go b/pkg/admission/chain_test.go index 035182260e..3b138bbe03 100644 --- a/pkg/admission/chain_test.go +++ b/pkg/admission/chain_test.go @@ -100,7 +100,7 @@ func TestAdmit(t *testing.T) { }, } for _, test := range tests { - err := test.chain.Admit(NewAttributesRecord(nil, unversioned.GroupVersionKind{}, "", "", unversioned.GroupVersionResource{}, "", test.operation, nil)) + err := test.chain.Admit(NewAttributesRecord(nil, nil, unversioned.GroupVersionKind{}, "", "", unversioned.GroupVersionResource{}, "", test.operation, nil)) accepted := (err == nil) if accepted != test.accept { t.Errorf("%s: unexpected result of admit call: %v\n", test.name, accepted) diff --git a/pkg/admission/interfaces.go b/pkg/admission/interfaces.go index f006fbf167..6c3ef09760 100644 --- a/pkg/admission/interfaces.go +++ b/pkg/admission/interfaces.go @@ -41,6 +41,8 @@ type Attributes interface { GetOperation() Operation // GetObject is the object from the incoming request prior to default values being applied GetObject() runtime.Object + // GetOldObject is the existing object. Only populated for UPDATE requests. + GetOldObject() runtime.Object // GetKind is the type of object being manipulated. For example: Pod GetKind() unversioned.GroupVersionKind // GetUserInfo is information about the requesting user diff --git a/pkg/api/rest/rest.go b/pkg/api/rest/rest.go index 4565730f2f..4d5b5ba977 100644 --- a/pkg/api/rest/rest.go +++ b/pkg/api/rest/rest.go @@ -174,6 +174,19 @@ type NamedCreater interface { Create(ctx api.Context, name string, obj runtime.Object) (runtime.Object, error) } +// UpdatedObjectInfo provides information about an updated object to an Updater. +// It requires access to the old object in order to return the newly updated object. +type UpdatedObjectInfo interface { + // Returns preconditions built from the updated object, if applicable. + // May return nil, or a preconditions object containing nil fields, + // if no preconditions can be determined from the updated object. + Preconditions() *api.Preconditions + + // UpdatedObject returns the updated object, given a context and old object. + // The only time an empty oldObj should be passed in is if a "create on update" is occurring (there is no oldObj). + UpdatedObject(ctx api.Context, oldObj runtime.Object) (newObj runtime.Object, err error) +} + // 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. @@ -183,14 +196,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 api.Context, obj runtime.Object) (runtime.Object, bool, error) + Update(ctx api.Context, name string, objInfo UpdatedObjectInfo) (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 api.Context, obj runtime.Object) (runtime.Object, bool, error) + Update(ctx api.Context, name string, objInfo UpdatedObjectInfo) (runtime.Object, bool, error) } // CreaterUpdater must satisfy the Updater interface. diff --git a/pkg/api/rest/resttest/resttest.go b/pkg/api/rest/resttest/resttest.go index 8e92e2e3f6..40b33b46e2 100644 --- a/pkg/api/rest/resttest/resttest.go +++ b/pkg/api/rest/resttest/resttest.go @@ -170,6 +170,8 @@ func (t *Tester) TestUpdate(valid runtime.Object, createFn CreateFunc, getFn Get } t.testUpdateInvokesValidation(copyOrDie(valid), createFn, invalidUpdateFn...) t.testUpdateWithWrongUID(copyOrDie(valid), createFn, getFn) + t.testUpdateRetrievesOldObject(copyOrDie(valid), createFn, getFn) + t.testUpdatePropagatesUpdatedObjectError(copyOrDie(valid), createFn, getFn) } // Test deleting an object. @@ -442,7 +444,8 @@ func (t *Tester) testUpdateEquals(obj runtime.Object, createFn CreateFunc, getFn t.Errorf("unexpected error: %v", err) } toUpdate = updateFn(toUpdate) - updated, created, err := t.storage.(rest.Updater).Update(ctx, toUpdate) + toUpdateMeta := t.getObjectMetaOrFail(toUpdate) + updated, created, err := t.storage.(rest.Updater).Update(ctx, toUpdateMeta.Name, rest.DefaultUpdatedObjectInfo(toUpdate, api.Scheme)) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -482,7 +485,7 @@ func (t *Tester) testUpdateFailsOnVersionTooOld(obj runtime.Object, createFn Cre olderMeta := t.getObjectMetaOrFail(older) olderMeta.ResourceVersion = "1" - _, _, err = t.storage.(rest.Updater).Update(t.TestContext(), older) + _, _, err = t.storage.(rest.Updater).Update(t.TestContext(), olderMeta.Name, rest.DefaultUpdatedObjectInfo(older, api.Scheme)) if err == nil { t.Errorf("Expected an error, but we didn't get one") } else if !errors.IsConflict(err) { @@ -501,7 +504,8 @@ func (t *Tester) testUpdateInvokesValidation(obj runtime.Object, createFn Create for _, update := range invalidUpdateFn { toUpdate := update(copyOrDie(foo)) - got, created, err := t.storage.(rest.Updater).Update(t.TestContext(), toUpdate) + toUpdateMeta := t.getObjectMetaOrFail(toUpdate) + got, created, err := t.storage.(rest.Updater).Update(t.TestContext(), toUpdateMeta.Name, rest.DefaultUpdatedObjectInfo(toUpdate, api.Scheme)) if got != nil || created { t.Errorf("expected nil object and no creation for object: %v", toUpdate) } @@ -522,7 +526,7 @@ func (t *Tester) testUpdateWithWrongUID(obj runtime.Object, createFn CreateFunc, } objectMeta.UID = types.UID("UID1111") - obj, created, err := t.storage.(rest.Updater).Update(ctx, foo) + obj, created, err := t.storage.(rest.Updater).Update(ctx, objectMeta.Name, rest.DefaultUpdatedObjectInfo(foo, api.Scheme)) if created || obj != nil { t.Errorf("expected nil object and no creation for object: %v", foo) } @@ -531,9 +535,85 @@ func (t *Tester) testUpdateWithWrongUID(obj runtime.Object, createFn CreateFunc, } } +func (t *Tester) testUpdateRetrievesOldObject(obj runtime.Object, createFn CreateFunc, getFn GetFunc) { + ctx := t.TestContext() + foo := copyOrDie(obj) + t.setObjectMeta(foo, t.namer(6)) + objectMeta := t.getObjectMetaOrFail(foo) + objectMeta.Annotations = map[string]string{"A": "1"} + if err := createFn(ctx, foo); err != nil { + t.Errorf("unexpected error: %v", err) + return + } + + storedFoo, err := getFn(ctx, foo) + if err != nil { + t.Errorf("unexpected error: %v", err) + return + } + + storedFooWithUpdates := copyOrDie(storedFoo) + objectMeta = t.getObjectMetaOrFail(storedFooWithUpdates) + objectMeta.Annotations = map[string]string{"A": "2"} + + // Make sure a custom transform is called, and sees the expected updatedObject and oldObject + // This tests the mechanism used to pass the old and new object to admission + calledUpdatedObject := 0 + noopTransform := func(_ api.Context, updatedObject runtime.Object, oldObject runtime.Object) (runtime.Object, error) { + if !reflect.DeepEqual(storedFoo, oldObject) { + t.Errorf("Expected\n\t%#v\ngot\n\t%#v", storedFoo, oldObject) + } + if !reflect.DeepEqual(storedFooWithUpdates, updatedObject) { + t.Errorf("Expected\n\t%#v\ngot\n\t%#v", storedFooWithUpdates, updatedObject) + } + calledUpdatedObject++ + return updatedObject, nil + } + + updatedObj, created, err := t.storage.(rest.Updater).Update(ctx, objectMeta.Name, rest.DefaultUpdatedObjectInfo(storedFooWithUpdates, api.Scheme, noopTransform)) + if err != nil { + t.Errorf("unexpected error: %v", err) + return + } + if created { + t.Errorf("expected no creation for object") + return + } + if updatedObj == nil { + t.Errorf("expected non-nil object from update") + return + } + if calledUpdatedObject != 1 { + t.Errorf("expected UpdatedObject() to be called 1 time, was called %d", calledUpdatedObject) + return + } +} + +func (t *Tester) testUpdatePropagatesUpdatedObjectError(obj runtime.Object, createFn CreateFunc, getFn GetFunc) { + ctx := t.TestContext() + foo := copyOrDie(obj) + name := t.namer(7) + t.setObjectMeta(foo, name) + if err := createFn(ctx, foo); err != nil { + t.Errorf("unexpected error: %v", err) + return + } + + // Make sure our transform is called, and sees the expected updatedObject and oldObject + propagateErr := fmt.Errorf("custom updated object error for %v", foo) + noopTransform := func(_ api.Context, updatedObject runtime.Object, oldObject runtime.Object) (runtime.Object, error) { + return nil, propagateErr + } + + _, _, err := t.storage.(rest.Updater).Update(ctx, name, rest.DefaultUpdatedObjectInfo(foo, api.Scheme, noopTransform)) + if err != propagateErr { + t.Errorf("expected propagated error, got %#v", err) + } +} + func (t *Tester) testUpdateOnNotFound(obj runtime.Object) { t.setObjectMeta(obj, t.namer(0)) - _, created, err := t.storage.(rest.Updater).Update(t.TestContext(), obj) + _, created, err := t.storage.(rest.Updater).Update(t.TestContext(), t.namer(0), rest.DefaultUpdatedObjectInfo(obj, api.Scheme)) if t.createOnUpdate { if err != nil { t.Errorf("creation allowed on updated, but got an error: %v", err) @@ -563,7 +643,7 @@ func (t *Tester) testUpdateRejectsMismatchedNamespace(obj runtime.Object, create objectMeta.Name = t.namer(1) objectMeta.Namespace = "not-default" - obj, updated, err := t.storage.(rest.Updater).Update(t.TestContext(), obj) + obj, updated, err := t.storage.(rest.Updater).Update(t.TestContext(), "foo1", rest.DefaultUpdatedObjectInfo(obj, api.Scheme)) if obj != nil || updated { t.Errorf("expected nil object and not updated") } diff --git a/pkg/api/rest/update.go b/pkg/api/rest/update.go index 80ad14f866..bc5ed0c5f8 100644 --- a/pkg/api/rest/update.go +++ b/pkg/api/rest/update.go @@ -21,6 +21,7 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/errors" + "k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/api/validation" "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util/validation/field" @@ -103,3 +104,72 @@ func BeforeUpdate(strategy RESTUpdateStrategy, ctx api.Context, obj, old runtime return nil } + +// TransformFunc is a function to transform and return newObj +type TransformFunc func(ctx api.Context, newObj runtime.Object, oldObj runtime.Object) (transformedNewObj runtime.Object, err error) + +// defaultUpdatedObjectInfo implements UpdatedObjectInfo +type defaultUpdatedObjectInfo struct { + // obj is the updated object + obj runtime.Object + + // copier makes a copy of the object before returning it. + // this allows repeated calls to UpdatedObject() to return + // pristine data, even if the returned value is mutated. + copier runtime.ObjectCopier + + // transformers is an optional list of transforming functions that modify or + // replace obj using information from the context, old object, or other sources. + transformers []TransformFunc +} + +// DefaultUpdatedObjectInfo returns an UpdatedObjectInfo impl based on the specified object. +func DefaultUpdatedObjectInfo(obj runtime.Object, copier runtime.ObjectCopier, transformers ...TransformFunc) UpdatedObjectInfo { + return &defaultUpdatedObjectInfo{obj, copier, transformers} +} + +// Preconditions satisfies the UpdatedObjectInfo interface. +func (i *defaultUpdatedObjectInfo) Preconditions() *api.Preconditions { + // Attempt to get the UID out of the object + accessor, err := meta.Accessor(i.obj) + if err != nil { + // If no UID can be read, no preconditions are possible + return nil + } + + // If empty, no preconditions needed + uid := accessor.GetUID() + if len(uid) == 0 { + return nil + } + + return &api.Preconditions{UID: &uid} +} + +// UpdatedObject satisfies the UpdatedObjectInfo interface. +// It returns a copy of the held obj, passed through any configured transformers. +func (i *defaultUpdatedObjectInfo) UpdatedObject(ctx api.Context, oldObj runtime.Object) (runtime.Object, error) { + var err error + // Start with the configured object + newObj := i.obj + + // If the original is non-nil (might be nil if the first transformer builds the object from the oldObj), make a copy, + // so we don't return the original. BeforeUpdate can mutate the returned object, doing things like clearing ResourceVersion. + // If we're re-called, we need to be able to return the pristine version. + if newObj != nil { + newObj, err = i.copier.Copy(newObj) + if err != nil { + return nil, err + } + } + + // Allow any configured transformers to update the new object + for _, transformer := range i.transformers { + newObj, err = transformer(ctx, newObj, oldObj) + if err != nil { + return nil, err + } + } + + return newObj, nil +} diff --git a/pkg/apiserver/api_installer.go b/pkg/apiserver/api_installer.go index 423f09bfc4..75ab8f69e8 100644 --- a/pkg/apiserver/api_installer.go +++ b/pkg/apiserver/api_installer.go @@ -462,6 +462,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag ParameterCodec: a.group.ParameterCodec, Creater: a.group.Creater, Convertor: a.group.Convertor, + Copier: a.group.Copier, // TODO: This seems wrong for cross-group subresources. It makes an assumption that a subresource and its parent are in the same group version. Revisit this. Resource: a.group.GroupVersion.WithResource(resource), diff --git a/pkg/apiserver/apiserver.go b/pkg/apiserver/apiserver.go index e96369c297..a32af0a98e 100644 --- a/pkg/apiserver/apiserver.go +++ b/pkg/apiserver/apiserver.go @@ -91,6 +91,7 @@ type APIGroupVersion struct { Typer runtime.ObjectTyper Creater runtime.ObjectCreater Convertor runtime.ObjectConvertor + Copier runtime.ObjectCopier Linker runtime.SelfLinker Admit admission.Interface diff --git a/pkg/apiserver/apiserver_test.go b/pkg/apiserver/apiserver_test.go index 4408c16124..5e37bf0e0e 100644 --- a/pkg/apiserver/apiserver_test.go +++ b/pkg/apiserver/apiserver_test.go @@ -271,6 +271,7 @@ func handleInternal(storage map[string]rest.Storage, admissionControl admission. Creater: api.Scheme, Convertor: api.Scheme, + Copier: api.Scheme, Typer: api.Scheme, Linker: selfLinker, Mapper: namespaceMapper, @@ -502,13 +503,16 @@ func (storage *SimpleRESTStorage) Create(ctx api.Context, obj runtime.Object) (r return obj, err } -func (storage *SimpleRESTStorage) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) { +func (storage *SimpleRESTStorage) Update(ctx api.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { storage.checkContext(ctx) + obj, err := objInfo.UpdatedObject(ctx, &storage.item) + if err != nil { + return nil, false, err + } storage.updated = obj.(*apiservertesting.Simple) if err := storage.errors["update"]; err != nil { return nil, false, err } - var err error if storage.injectedFunction != nil { obj, err = storage.injectedFunction(obj) } @@ -2022,6 +2026,7 @@ func TestPatch(t *testing.T) { ObjectMeta: api.ObjectMeta{ Name: ID, Namespace: "", // update should allow the client to send an empty namespace + UID: "uid", }, Other: "bar", } @@ -2060,6 +2065,7 @@ func TestPatchRequiresMatchingName(t *testing.T) { ObjectMeta: api.ObjectMeta{ Name: ID, Namespace: "", // update should allow the client to send an empty namespace + UID: "uid", }, Other: "bar", } @@ -2399,6 +2405,7 @@ func TestUpdateREST(t *testing.T) { RequestInfoResolver: newTestRequestInfoResolver(), Creater: api.Scheme, Convertor: api.Scheme, + Copier: api.Scheme, Typer: api.Scheme, Linker: selfLinker, @@ -2483,6 +2490,7 @@ func TestParentResourceIsRequired(t *testing.T) { RequestInfoResolver: newTestRequestInfoResolver(), Creater: api.Scheme, Convertor: api.Scheme, + Copier: api.Scheme, Typer: api.Scheme, Linker: selfLinker, @@ -2514,6 +2522,7 @@ func TestParentResourceIsRequired(t *testing.T) { RequestInfoResolver: newTestRequestInfoResolver(), Creater: api.Scheme, Convertor: api.Scheme, + Copier: api.Scheme, Typer: api.Scheme, Linker: selfLinker, @@ -3211,6 +3220,7 @@ func TestXGSubresource(t *testing.T) { Creater: api.Scheme, Convertor: api.Scheme, + Copier: api.Scheme, Typer: api.Scheme, Linker: selfLinker, Mapper: namespaceMapper, diff --git a/pkg/apiserver/resthandler.go b/pkg/apiserver/resthandler.go index 4a1e970067..325b564191 100644 --- a/pkg/apiserver/resthandler.go +++ b/pkg/apiserver/resthandler.go @@ -77,6 +77,7 @@ type RequestScope struct { Creater runtime.ObjectCreater Convertor runtime.ObjectConvertor + Copier runtime.ObjectCopier Resource unversioned.GroupVersionResource Kind unversioned.GroupVersionKind @@ -200,7 +201,7 @@ func ConnectResource(connecter rest.Connecter, scope RequestScope, admit admissi } userInfo, _ := api.UserFrom(ctx) - err = admit.Admit(admission.NewAttributesRecord(connectRequest, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Connect, userInfo)) + err = admit.Admit(admission.NewAttributesRecord(connectRequest, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Connect, userInfo)) if err != nil { scope.err(err, res.ResponseWriter, req.Request) return @@ -391,7 +392,7 @@ func createHandler(r rest.NamedCreater, scope RequestScope, typer runtime.Object if admit != nil && admit.Handles(admission.Create) { userInfo, _ := api.UserFrom(ctx) - err = admit.Admit(admission.NewAttributesRecord(obj, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Create, userInfo)) + err = admit.Admit(admission.NewAttributesRecord(obj, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Create, userInfo)) if err != nil { scope.err(err, res.ResponseWriter, req.Request) return @@ -491,16 +492,16 @@ func PatchResource(r rest.Patcher, scope RequestScope, typer runtime.ObjectTyper scope.Serializer.DecoderToVersion(s, unversioned.GroupVersion{Group: gv.Group, Version: runtime.APIVersionInternal}), ) - updateAdmit := func(updatedObject runtime.Object) error { + updateAdmit := func(updatedObject runtime.Object, currentObject runtime.Object) error { if admit != nil && admit.Handles(admission.Update) { userInfo, _ := api.UserFrom(ctx) - return admit.Admit(admission.NewAttributesRecord(updatedObject, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Update, userInfo)) + return admit.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, scope.Namer, codec) + result, err := patchResource(ctx, updateAdmit, timeout, versionedObj, r, name, patchType, patchJS, scope.Namer, scope.Copier, scope.Resource, codec) if err != nil { scope.err(err, res.ResponseWriter, req.Request) return @@ -516,43 +517,69 @@ func PatchResource(r rest.Patcher, scope RequestScope, typer runtime.ObjectTyper } -type updateAdmissionFunc func(updatedObject runtime.Object) error +type updateAdmissionFunc func(updatedObject runtime.Object, currentObject runtime.Object) error // patchResource divides PatchResource for easier unit testing -func patchResource(ctx api.Context, admit updateAdmissionFunc, timeout time.Duration, versionedObj runtime.Object, patcher rest.Patcher, name string, patchType api.PatchType, patchJS []byte, namer ScopeNamer, codec runtime.Codec) (runtime.Object, error) { +func patchResource( + ctx api.Context, + admit updateAdmissionFunc, + timeout time.Duration, + versionedObj runtime.Object, + patcher rest.Patcher, + name string, + patchType api.PatchType, + patchJS []byte, + namer ScopeNamer, + copier runtime.ObjectCopier, + resource unversioned.GroupVersionResource, + codec runtime.Codec, +) (runtime.Object, error) { + namespace := api.NamespaceValue(ctx) - original, err := patcher.Get(ctx, name) - if err != nil { - return nil, err - } + var ( + originalObjJS []byte + originalPatchedObjJS []byte + lastConflictErr error + ) - originalObjJS, err := runtime.Encode(codec, original) - if err != nil { - return nil, err - } - originalPatchedObjJS, err := getPatchedJS(patchType, originalObjJS, patchJS, versionedObj) - if err != nil { - return nil, err - } - - objToUpdate := patcher.New() - if err := runtime.DecodeInto(codec, originalPatchedObjJS, objToUpdate); err != nil { - return nil, err - } - if err := checkName(objToUpdate, name, namespace, namer); err != nil { - return nil, err - } - - return finishRequest(timeout, func() (runtime.Object, error) { - if err := admit(objToUpdate); err != nil { + // applyPatch is called every time GuaranteedUpdate asks for the updated object, + // and is given the currently persisted object as input. + applyPatch := func(_ api.Context, _, currentObject runtime.Object) (runtime.Object, error) { + // Make sure we actually have a persisted currentObject + if hasUID, err := hasUID(currentObject); err != nil { return nil, err + } else if !hasUID { + return nil, errors.NewNotFound(resource.GroupResource(), name) } - // update should never create as previous get would fail - updateObject, _, updateErr := patcher.Update(ctx, objToUpdate) - for i := 0; i < MaxPatchConflicts && (errors.IsConflict(updateErr)); i++ { + switch { + case len(originalObjJS) == 0 || len(originalPatchedObjJS) == 0: + // first time through, + // 1. apply the patch + // 2. save the originalJS and patchedJS to detect whether there were conflicting changes on retries + if js, err := runtime.Encode(codec, currentObject); err != nil { + return nil, err + } else { + originalObjJS = js + } + if js, err := getPatchedJS(patchType, originalObjJS, patchJS, versionedObj); err != nil { + return nil, err + } else { + originalPatchedObjJS = js + } + + objToUpdate := patcher.New() + if err := runtime.DecodeInto(codec, originalPatchedObjJS, objToUpdate); err != nil { + return nil, err + } + if err := checkName(objToUpdate, name, namespace, namer); err != nil { + return nil, err + } + return objToUpdate, nil + + default: // on a conflict, // 1. build a strategic merge patch from originalJS and the patchedJS. Different patch types can // be specified, but a strategic merge patch should be expressive enough handle them. Build the @@ -560,16 +587,10 @@ func patchResource(ctx api.Context, admit updateAdmissionFunc, timeout time.Dura // 2. build a strategic merge patch from originalJS and the currentJS // 3. ensure no conflicts between the two patches // 4. apply the #1 patch to the currentJS object - // 5. retry the update - currentObject, err := patcher.Get(ctx, name) - if err != nil { - return nil, err - } currentObjectJS, err := runtime.Encode(codec, currentObject) if err != nil { return nil, err } - currentPatch, err := strategicpatch.CreateStrategicMergePatch(originalObjJS, currentObjectJS, versionedObj) if err != nil { return nil, err @@ -592,25 +613,41 @@ func patchResource(ctx api.Context, admit updateAdmissionFunc, timeout time.Dura return nil, err } if hasConflicts { - glog.V(4).Infof("patchResource failed for resource %s, becauase there is a meaningful conflict.\n diff1=%v\n, diff2=%v\n", name, diff1, diff2) - return updateObject, updateErr + glog.V(4).Infof("patchResource failed for resource %s, because there is a meaningful conflict.\n diff1=%v\n, diff2=%v\n", name, diff1, diff2) + // Return the last conflict error we got if we have one + if lastConflictErr != nil { + return nil, lastConflictErr + } + // Otherwise manufacture one of our own + return nil, errors.NewConflict(resource.GroupResource(), name, nil) } newlyPatchedObjJS, err := getPatchedJS(api.StrategicMergePatchType, currentObjectJS, originalPatch, versionedObj) if err != nil { return nil, err } + objToUpdate := patcher.New() if err := runtime.DecodeInto(codec, newlyPatchedObjJS, objToUpdate); err != nil { return nil, err } - - if err := admit(objToUpdate); err != nil { - return nil, err - } - - updateObject, _, updateErr = patcher.Update(ctx, objToUpdate) + return objToUpdate, nil } + } + // 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 api.Context, patchedObject runtime.Object, currentObject runtime.Object) (runtime.Object, error) { + return patchedObject, admit(patchedObject, currentObject) + } + + updatedObjectInfo := rest.DefaultUpdatedObjectInfo(nil, copier, applyPatch, applyAdmission) + + return finishRequest(timeout, func() (runtime.Object, error) { + updateObject, _, updateErr := patcher.Update(ctx, name, updatedObjectInfo) + for i := 0; i < MaxPatchConflicts && (errors.IsConflict(updateErr)); i++ { + lastConflictErr = updateErr + updateObject, _, updateErr = patcher.Update(ctx, name, updatedObjectInfo) + } return updateObject, updateErr }) } @@ -667,20 +704,18 @@ func UpdateResource(r rest.Updater, scope RequestScope, typer runtime.ObjectType return } + var transformers []rest.TransformFunc if admit != nil && admit.Handles(admission.Update) { - userInfo, _ := api.UserFrom(ctx) - - err = admit.Admit(admission.NewAttributesRecord(obj, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Update, userInfo)) - if err != nil { - scope.err(err, res.ResponseWriter, req.Request) - return - } + transformers = append(transformers, func(ctx api.Context, newObj, oldObj runtime.Object) (runtime.Object, error) { + userInfo, _ := api.UserFrom(ctx) + return newObj, admit.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, obj) + obj, created, err := r.Update(ctx, name, rest.DefaultUpdatedObjectInfo(obj, scope.Copier, transformers...)) wasCreated = created return obj, err }) @@ -753,7 +788,7 @@ func DeleteResource(r rest.GracefulDeleter, checkBody bool, scope RequestScope, if admit != nil && admit.Handles(admission.Delete) { userInfo, _ := api.UserFrom(ctx) - err = admit.Admit(admission.NewAttributesRecord(nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Delete, userInfo)) + err = admit.Admit(admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Delete, userInfo)) if err != nil { scope.err(err, res.ResponseWriter, req.Request) return @@ -814,7 +849,7 @@ func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope RequestSco if admit != nil && admit.Handles(admission.Delete) { userInfo, _ := api.UserFrom(ctx) - err = admit.Admit(admission.NewAttributesRecord(nil, scope.Kind, namespace, "", scope.Resource, scope.Subresource, admission.Delete, userInfo)) + err = admit.Admit(admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, "", scope.Resource, scope.Subresource, admission.Delete, userInfo)) if err != nil { scope.err(err, res.ResponseWriter, req.Request) return @@ -969,6 +1004,20 @@ func setSelfLink(obj runtime.Object, req *restful.Request, namer ScopeNamer) err return namer.SetSelfLink(obj, newURL.String()) } +func hasUID(obj runtime.Object) (bool, error) { + if obj == nil { + return false, nil + } + accessor, err := meta.Accessor(obj) + if err != nil { + return false, errors.NewInternalError(err) + } + if len(accessor.GetUID()) == 0 { + return false, nil + } + return true, nil +} + // checkName checks the provided name against the request func checkName(obj runtime.Object, name, namespace string, namer ScopeNamer) error { if objNamespace, objName, err := namer.ObjectName(obj); err == nil { diff --git a/pkg/apiserver/resthandler_test.go b/pkg/apiserver/resthandler_test.go index 7eb64f2917..ffb7349c5c 100644 --- a/pkg/apiserver/resthandler_test.go +++ b/pkg/apiserver/resthandler_test.go @@ -28,9 +28,11 @@ import ( "k8s.io/kubernetes/pkg/api" apierrors "k8s.io/kubernetes/pkg/api/errors" + "k8s.io/kubernetes/pkg/api/rest" "k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/types" "k8s.io/kubernetes/pkg/util/diff" "k8s.io/kubernetes/pkg/util/strategicpatch" ) @@ -64,20 +66,32 @@ func TestPatchAnonymousField(t *testing.T) { } type testPatcher struct { - // startingPod is used for the first Get + t *testing.T + + // startingPod is used for the first Update startingPod *api.Pod - // updatePod is the pod that is used for conflict comparison and returned for the SECOND Get + // updatePod is the pod that is used for conflict comparison and used for subsequent Update calls updatePod *api.Pod - numGets int + numUpdates int } func (p *testPatcher) New() runtime.Object { return &api.Pod{} } -func (p *testPatcher) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) { +func (p *testPatcher) Update(ctx api.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { + currentPod := p.startingPod + if p.numUpdates > 0 { + currentPod = p.updatePod + } + p.numUpdates++ + + obj, err := objInfo.UpdatedObject(ctx, currentPod) + if err != nil { + return nil, false, err + } inPod := obj.(*api.Pod) if inPod.ResourceVersion != p.updatePod.ResourceVersion { return nil, false, apierrors.NewConflict(api.Resource("pods"), inPod.Name, fmt.Errorf("existing %v, new %v", p.updatePod.ResourceVersion, inPod.ResourceVersion)) @@ -87,12 +101,8 @@ func (p *testPatcher) Update(ctx api.Context, obj runtime.Object) (runtime.Objec } func (p *testPatcher) Get(ctx api.Context, name string) (runtime.Object, error) { - if p.numGets > 0 { - return p.updatePod, nil - } - p.numGets++ - - return p.startingPod, nil + p.t.Fatal("Unexpected call to testPatcher.Get") + return nil, errors.New("Unexpected call to testPatcher.Get") } type testNamer struct { @@ -138,12 +148,12 @@ type patchTestCase struct { // admission chain to use, nil is fine admit updateAdmissionFunc - // startingPod is used for the first Get + // startingPod is used as the starting point for the first Update startingPod *api.Pod // changedPod is the "destination" pod for the patch. The test will create a patch from the startingPod to the changedPod // to use when calling the patch operation changedPod *api.Pod - // updatePod is the pod that is used for conflict comparison and returned for the SECOND Get + // updatePod is the pod that is used for conflict comparison and as the starting point for the second Update updatePod *api.Pod // expectedPod is the pod that you expect to get back after the patch is complete @@ -160,12 +170,13 @@ func (tc *patchTestCase) Run(t *testing.T) { codec := testapi.Default.Codec() admit := tc.admit if admit == nil { - admit = func(updatedObject runtime.Object) error { + admit = func(updatedObject runtime.Object, currentObject runtime.Object) error { return nil } } testPatcher := &testPatcher{} + testPatcher.t = t testPatcher.startingPod = tc.startingPod testPatcher.updatePod = tc.updatePod @@ -173,6 +184,8 @@ func (tc *patchTestCase) Run(t *testing.T) { ctx = api.WithNamespace(ctx, namespace) namer := &testNamer{namespace, name} + copier := runtime.ObjectCopier(api.Scheme) + resource := unversioned.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"} versionedObj, err := api.Scheme.ConvertToVersion(&api.Pod{}, unversioned.GroupVersion{Version: "v1"}) if err != nil { @@ -219,7 +232,7 @@ func (tc *patchTestCase) Run(t *testing.T) { } - resultObj, err := patchResource(ctx, admit, 1*time.Second, versionedObj, testPatcher, name, patchType, patch, namer, codec) + resultObj, err := patchResource(ctx, admit, 1*time.Second, versionedObj, testPatcher, name, patchType, patch, namer, copier, 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) @@ -265,6 +278,7 @@ func (tc *patchTestCase) Run(t *testing.T) { func TestPatchResourceWithVersionConflict(t *testing.T) { namespace := "bar" name := "foo" + uid := types.UID("uid") fifteen := int64(15) thirty := int64(30) @@ -280,18 +294,21 @@ func TestPatchResourceWithVersionConflict(t *testing.T) { tc.startingPod.Name = name tc.startingPod.Namespace = namespace + tc.startingPod.UID = uid tc.startingPod.ResourceVersion = "1" tc.startingPod.APIVersion = "v1" tc.startingPod.Spec.ActiveDeadlineSeconds = &fifteen tc.changedPod.Name = name tc.changedPod.Namespace = namespace + tc.changedPod.UID = uid tc.changedPod.ResourceVersion = "1" tc.changedPod.APIVersion = "v1" tc.changedPod.Spec.ActiveDeadlineSeconds = &thirty tc.updatePod.Name = name tc.updatePod.Namespace = namespace + tc.updatePod.UID = uid tc.updatePod.ResourceVersion = "2" tc.updatePod.APIVersion = "v1" tc.updatePod.Spec.ActiveDeadlineSeconds = &fifteen @@ -299,6 +316,7 @@ func TestPatchResourceWithVersionConflict(t *testing.T) { tc.expectedPod.Name = name tc.expectedPod.Namespace = namespace + tc.expectedPod.UID = uid tc.expectedPod.ResourceVersion = "2" tc.expectedPod.Spec.ActiveDeadlineSeconds = &thirty tc.expectedPod.Spec.NodeName = "anywhere" @@ -309,6 +327,7 @@ func TestPatchResourceWithVersionConflict(t *testing.T) { func TestPatchResourceWithConflict(t *testing.T) { namespace := "bar" name := "foo" + uid := types.UID("uid") tc := &patchTestCase{ name: "TestPatchResourceWithConflict", @@ -322,18 +341,21 @@ func TestPatchResourceWithConflict(t *testing.T) { tc.startingPod.Name = name tc.startingPod.Namespace = namespace + tc.startingPod.UID = uid tc.startingPod.ResourceVersion = "1" tc.startingPod.APIVersion = "v1" tc.startingPod.Spec.NodeName = "here" tc.changedPod.Name = name tc.changedPod.Namespace = namespace + tc.changedPod.UID = uid tc.changedPod.ResourceVersion = "1" tc.changedPod.APIVersion = "v1" tc.changedPod.Spec.NodeName = "there" tc.updatePod.Name = name tc.updatePod.Namespace = namespace + tc.updatePod.UID = uid tc.updatePod.ResourceVersion = "2" tc.updatePod.APIVersion = "v1" tc.updatePod.Spec.NodeName = "anywhere" @@ -344,13 +366,14 @@ func TestPatchResourceWithConflict(t *testing.T) { func TestPatchWithAdmissionRejection(t *testing.T) { namespace := "bar" name := "foo" + uid := types.UID("uid") fifteen := int64(15) thirty := int64(30) tc := &patchTestCase{ name: "TestPatchWithAdmissionRejection", - admit: func(updatedObject runtime.Object) error { + admit: func(updatedObject runtime.Object, currentObject runtime.Object) error { return errors.New("admission failure") }, @@ -363,12 +386,14 @@ func TestPatchWithAdmissionRejection(t *testing.T) { tc.startingPod.Name = name tc.startingPod.Namespace = namespace + tc.startingPod.UID = uid tc.startingPod.ResourceVersion = "1" tc.startingPod.APIVersion = "v1" tc.startingPod.Spec.ActiveDeadlineSeconds = &fifteen tc.changedPod.Name = name tc.changedPod.Namespace = namespace + tc.changedPod.UID = uid tc.changedPod.ResourceVersion = "1" tc.changedPod.APIVersion = "v1" tc.changedPod.Spec.ActiveDeadlineSeconds = &thirty @@ -379,6 +404,7 @@ func TestPatchWithAdmissionRejection(t *testing.T) { func TestPatchWithVersionConflictThenAdmissionFailure(t *testing.T) { namespace := "bar" name := "foo" + uid := types.UID("uid") fifteen := int64(15) thirty := int64(30) seen := false @@ -386,7 +412,7 @@ func TestPatchWithVersionConflictThenAdmissionFailure(t *testing.T) { tc := &patchTestCase{ name: "TestPatchWithVersionConflictThenAdmissionFailure", - admit: func(updatedObject runtime.Object) error { + admit: func(updatedObject runtime.Object, currentObject runtime.Object) error { if seen { return errors.New("admission failure") } @@ -404,18 +430,21 @@ func TestPatchWithVersionConflictThenAdmissionFailure(t *testing.T) { tc.startingPod.Name = name tc.startingPod.Namespace = namespace + tc.startingPod.UID = uid tc.startingPod.ResourceVersion = "1" tc.startingPod.APIVersion = "v1" tc.startingPod.Spec.ActiveDeadlineSeconds = &fifteen tc.changedPod.Name = name tc.changedPod.Namespace = namespace + tc.changedPod.UID = uid tc.changedPod.ResourceVersion = "1" tc.changedPod.APIVersion = "v1" tc.changedPod.Spec.ActiveDeadlineSeconds = &thirty tc.updatePod.Name = name tc.updatePod.Namespace = namespace + tc.updatePod.UID = uid tc.updatePod.ResourceVersion = "2" tc.updatePod.APIVersion = "v1" tc.updatePod.Spec.ActiveDeadlineSeconds = &fifteen @@ -423,3 +452,26 @@ func TestPatchWithVersionConflictThenAdmissionFailure(t *testing.T) { tc.Run(t) } + +func TestHasUID(t *testing.T) { + testcases := []struct { + obj runtime.Object + hasUID bool + }{ + {obj: nil, hasUID: false}, + {obj: &api.Pod{}, hasUID: false}, + {obj: nil, hasUID: false}, + {obj: runtime.Object(nil), hasUID: false}, + {obj: &api.Pod{ObjectMeta: api.ObjectMeta{UID: types.UID("A")}}, hasUID: true}, + } + for i, tc := range testcases { + actual, err := hasUID(tc.obj) + if err != nil { + t.Errorf("%d: unexpected error %v", i, err) + continue + } + if tc.hasUID != actual { + t.Errorf("%d: expected %v, got %v", i, tc.hasUID, actual) + } + } +} diff --git a/pkg/genericapiserver/genericapiserver.go b/pkg/genericapiserver/genericapiserver.go index 2930ac5c20..3e50b2b55f 100644 --- a/pkg/genericapiserver/genericapiserver.go +++ b/pkg/genericapiserver/genericapiserver.go @@ -862,6 +862,7 @@ func (s *GenericAPIServer) getAPIGroupVersion(apiGroupInfo *APIGroupInfo, groupV version.Serializer = apiGroupInfo.NegotiatedSerializer version.Creater = apiGroupInfo.Scheme version.Convertor = apiGroupInfo.Scheme + version.Copier = apiGroupInfo.Scheme version.Typer = apiGroupInfo.Scheme version.SubresourceGroupVersionKind = apiGroupInfo.SubresourceGroupVersionKind return version, err diff --git a/pkg/master/master.go b/pkg/master/master.go index 0b74b39cd1..ccc96b050d 100644 --- a/pkg/master/master.go +++ b/pkg/master/master.go @@ -749,6 +749,7 @@ func (m *Master) thirdpartyapi(group, kind, version string) *apiserver.APIGroupV Creater: thirdpartyresourcedata.NewObjectCreator(group, version, api.Scheme), Convertor: api.Scheme, + Copier: api.Scheme, Typer: api.Scheme, Mapper: thirdpartyresourcedata.NewMapper(registered.GroupOrDie(extensions.GroupName).RESTMapper, kind, version, group), diff --git a/pkg/registry/configmap/registry.go b/pkg/registry/configmap/registry.go index f94def472e..ff052e377d 100644 --- a/pkg/registry/configmap/registry.go +++ b/pkg/registry/configmap/registry.go @@ -75,7 +75,7 @@ func (s *storage) CreateConfigMap(ctx api.Context, cfg *api.ConfigMap) (*api.Con } func (s *storage) UpdateConfigMap(ctx api.Context, cfg *api.ConfigMap) (*api.ConfigMap, error) { - obj, _, err := s.Update(ctx, cfg) + obj, _, err := s.Update(ctx, cfg.Name, rest.DefaultUpdatedObjectInfo(cfg, api.Scheme)) if err != nil { return nil, err } diff --git a/pkg/registry/controller/etcd/etcd.go b/pkg/registry/controller/etcd/etcd.go index 05530402c8..8cd497ecaa 100644 --- a/pkg/registry/controller/etcd/etcd.go +++ b/pkg/registry/controller/etcd/etcd.go @@ -117,8 +117,8 @@ func (r *StatusREST) New() runtime.Object { } // Update alters the status subset of an object. -func (r *StatusREST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) { - return r.store.Update(ctx, obj) +func (r *StatusREST) Update(ctx api.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { + return r.store.Update(ctx, name, objInfo) } type ScaleREST struct { @@ -141,7 +141,18 @@ func (r *ScaleREST) Get(ctx api.Context, name string) (runtime.Object, error) { return scaleFromRC(rc), nil } -func (r *ScaleREST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) { +func (r *ScaleREST) Update(ctx api.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { + rc, err := r.registry.GetController(ctx, name) + if err != nil { + return nil, false, errors.NewNotFound(autoscaling.Resource("replicationcontrollers/scale"), name) + } + + oldScale := scaleFromRC(rc) + obj, err := objInfo.UpdatedObject(ctx, oldScale) + if err != nil { + return nil, false, err + } + if obj == nil { return nil, false, errors.NewBadRequest("nil update passed to Scale") } @@ -154,10 +165,6 @@ func (r *ScaleREST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, return nil, false, errors.NewInvalid(autoscaling.Kind("Scale"), scale.Name, errs) } - rc, err := r.registry.GetController(ctx, scale.Name) - if err != nil { - return nil, false, errors.NewNotFound(autoscaling.Resource("replicationcontrollers/scale"), scale.Name) - } rc.Spec.Replicas = scale.Spec.Replicas rc.ResourceVersion = scale.ResourceVersion rc, err = r.registry.UpdateController(ctx, rc) diff --git a/pkg/registry/controller/etcd/etcd_test.go b/pkg/registry/controller/etcd/etcd_test.go index 9a5e086c42..ea5debad2a 100644 --- a/pkg/registry/controller/etcd/etcd_test.go +++ b/pkg/registry/controller/etcd/etcd_test.go @@ -21,6 +21,7 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/errors" + "k8s.io/kubernetes/pkg/api/rest" "k8s.io/kubernetes/pkg/apis/autoscaling" "k8s.io/kubernetes/pkg/fields" "k8s.io/kubernetes/pkg/labels" @@ -159,7 +160,7 @@ func TestGenerationNumber(t *testing.T) { // Updates to spec should increment the generation number controller.Spec.Replicas += 1 - storage.Controller.Update(ctx, controller) + storage.Controller.Update(ctx, controller.Name, rest.DefaultUpdatedObjectInfo(controller, api.Scheme)) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -174,7 +175,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) + storage.Controller.Update(ctx, controller.Name, rest.DefaultUpdatedObjectInfo(controller, api.Scheme)) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -289,7 +290,7 @@ func TestScaleUpdate(t *testing.T) { }, } - if _, _, err := storage.Scale.Update(ctx, &update); err != nil { + if _, _, err := storage.Scale.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update, api.Scheme)); err != nil { t.Fatalf("error updating scale %v: %v", update, err) } obj, err := storage.Scale.Get(ctx, name) @@ -304,7 +305,7 @@ func TestScaleUpdate(t *testing.T) { update.ResourceVersion = rc.ResourceVersion update.Spec.Replicas = 15 - if _, _, err = storage.Scale.Update(ctx, &update); err != nil && !errors.IsConflict(err) { + if _, _, err = storage.Scale.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update, api.Scheme)); err != nil && !errors.IsConflict(err) { t.Fatalf("unexpected error, expecting an update conflict but got %v", err) } } diff --git a/pkg/registry/controller/registry.go b/pkg/registry/controller/registry.go index e3ecfa6a66..a8eea1dde3 100644 --- a/pkg/registry/controller/registry.go +++ b/pkg/registry/controller/registry.go @@ -79,7 +79,7 @@ func (s *storage) CreateController(ctx api.Context, controller *api.ReplicationC } func (s *storage) UpdateController(ctx api.Context, controller *api.ReplicationController) (*api.ReplicationController, error) { - obj, _, err := s.Update(ctx, controller) + obj, _, err := s.Update(ctx, controller.Name, rest.DefaultUpdatedObjectInfo(controller, api.Scheme)) if err != nil { return nil, err } diff --git a/pkg/registry/daemonset/etcd/etcd.go b/pkg/registry/daemonset/etcd/etcd.go index e5a77ad8cf..ab10ac2ec6 100644 --- a/pkg/registry/daemonset/etcd/etcd.go +++ b/pkg/registry/daemonset/etcd/etcd.go @@ -18,6 +18,7 @@ package etcd import ( "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/rest" "k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/fields" "k8s.io/kubernetes/pkg/labels" @@ -92,6 +93,6 @@ func (r *StatusREST) New() runtime.Object { } // Update alters the status subset of an object. -func (r *StatusREST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) { - return r.store.Update(ctx, obj) +func (r *StatusREST) Update(ctx api.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { + return r.store.Update(ctx, name, objInfo) } diff --git a/pkg/registry/deployment/etcd/etcd.go b/pkg/registry/deployment/etcd/etcd.go index 71dbd0c068..fd2846ebdb 100644 --- a/pkg/registry/deployment/etcd/etcd.go +++ b/pkg/registry/deployment/etcd/etcd.go @@ -116,8 +116,8 @@ func (r *StatusREST) New() runtime.Object { } // Update alters the status subset of an object. -func (r *StatusREST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) { - return r.store.Update(ctx, obj) +func (r *StatusREST) Update(ctx api.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { + return r.store.Update(ctx, name, objInfo) } // RollbackREST implements the REST endpoint for initiating the rollback of a deployment @@ -205,7 +205,21 @@ func (r *ScaleREST) Get(ctx api.Context, name string) (runtime.Object, error) { return scale, nil } -func (r *ScaleREST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) { +func (r *ScaleREST) Update(ctx api.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { + deployment, err := r.registry.GetDeployment(ctx, name) + if err != nil { + return nil, false, errors.NewNotFound(extensions.Resource("deployments/scale"), name) + } + + oldScale, err := scaleFromDeployment(deployment) + if err != nil { + return nil, false, err + } + + obj, err := objInfo.UpdatedObject(ctx, oldScale) + if err != nil { + return nil, false, err + } if obj == nil { return nil, false, errors.NewBadRequest(fmt.Sprintf("nil update passed to Scale")) } @@ -215,13 +229,9 @@ func (r *ScaleREST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, } if errs := extvalidation.ValidateScale(scale); len(errs) > 0 { - return nil, false, errors.NewInvalid(extensions.Kind("Scale"), scale.Name, errs) + return nil, false, errors.NewInvalid(extensions.Kind("Scale"), name, errs) } - deployment, err := r.registry.GetDeployment(ctx, scale.Name) - if err != nil { - return nil, false, errors.NewNotFound(extensions.Resource("deployments/scale"), scale.Name) - } deployment.Spec.Replicas = scale.Spec.Replicas deployment.ResourceVersion = scale.ResourceVersion deployment, err = r.registry.UpdateDeployment(ctx, deployment) diff --git a/pkg/registry/deployment/etcd/etcd_test.go b/pkg/registry/deployment/etcd/etcd_test.go index 67d9b6fc5f..d4503e1137 100644 --- a/pkg/registry/deployment/etcd/etcd_test.go +++ b/pkg/registry/deployment/etcd/etcd_test.go @@ -23,6 +23,7 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/errors" storeerr "k8s.io/kubernetes/pkg/api/errors/storage" + "k8s.io/kubernetes/pkg/api/rest" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/fields" @@ -233,7 +234,7 @@ func TestScaleUpdate(t *testing.T) { }, } - if _, _, err := storage.Scale.Update(ctx, &update); err != nil { + if _, _, err := storage.Scale.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update, api.Scheme)); err != nil { t.Fatalf("error updating scale %v: %v", update, err) } obj, err := storage.Scale.Get(ctx, name) @@ -248,7 +249,7 @@ func TestScaleUpdate(t *testing.T) { update.ResourceVersion = deployment.ResourceVersion update.Spec.Replicas = 15 - if _, _, err = storage.Scale.Update(ctx, &update); err != nil && !errors.IsConflict(err) { + if _, _, err = storage.Scale.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update, api.Scheme)); err != nil && !errors.IsConflict(err) { t.Fatalf("unexpected error, expecting an update conflict but got %v", err) } } @@ -272,7 +273,7 @@ func TestStatusUpdate(t *testing.T) { }, } - if _, _, err := storage.Status.Update(ctx, &update); err != nil { + if _, _, err := storage.Status.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update, api.Scheme)); err != nil { t.Fatalf("unexpected error: %v", err) } obj, err := storage.Deployment.Get(ctx, name) diff --git a/pkg/registry/deployment/registry.go b/pkg/registry/deployment/registry.go index ec967e93d9..f9ddf358c6 100644 --- a/pkg/registry/deployment/registry.go +++ b/pkg/registry/deployment/registry.go @@ -71,7 +71,7 @@ func (s *storage) CreateDeployment(ctx api.Context, deployment *extensions.Deplo } func (s *storage) UpdateDeployment(ctx api.Context, deployment *extensions.Deployment) (*extensions.Deployment, error) { - obj, _, err := s.Update(ctx, deployment) + obj, _, err := s.Update(ctx, deployment.Name, rest.DefaultUpdatedObjectInfo(deployment, api.Scheme)) if err != nil { return nil, err } diff --git a/pkg/registry/endpoint/registry.go b/pkg/registry/endpoint/registry.go index a034852fb6..8490718922 100644 --- a/pkg/registry/endpoint/registry.go +++ b/pkg/registry/endpoint/registry.go @@ -63,7 +63,7 @@ func (s *storage) GetEndpoints(ctx api.Context, name string) (*api.Endpoints, er } func (s *storage) UpdateEndpoints(ctx api.Context, endpoints *api.Endpoints) error { - _, _, err := s.Update(ctx, endpoints) + _, _, err := s.Update(ctx, endpoints.Name, rest.DefaultUpdatedObjectInfo(endpoints, api.Scheme)) return err } diff --git a/pkg/registry/experimental/controller/etcd/etcd.go b/pkg/registry/experimental/controller/etcd/etcd.go index 7bf63be2ed..58ce0af0e0 100644 --- a/pkg/registry/experimental/controller/etcd/etcd.go +++ b/pkg/registry/experimental/controller/etcd/etcd.go @@ -70,7 +70,15 @@ func (r *ScaleREST) Get(ctx api.Context, name string) (runtime.Object, error) { return scaleFromRC(rc), nil } -func (r *ScaleREST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) { +func (r *ScaleREST) Update(ctx api.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { + rc, err := (*r.registry).GetController(ctx, name) + if err != nil { + return nil, false, errors.NewNotFound(extensions.Resource("replicationcontrollers/scale"), name) + } + oldScale := scaleFromRC(rc) + + obj, err := objInfo.UpdatedObject(ctx, oldScale) + if obj == nil { return nil, false, errors.NewBadRequest(fmt.Sprintf("nil update passed to Scale")) } @@ -83,10 +91,6 @@ func (r *ScaleREST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, return nil, false, errors.NewInvalid(extensions.Kind("Scale"), scale.Name, errs) } - rc, err := (*r.registry).GetController(ctx, scale.Name) - if err != nil { - return nil, false, errors.NewNotFound(extensions.Resource("replicationcontrollers/scale"), scale.Name) - } rc.Spec.Replicas = scale.Spec.Replicas rc.ResourceVersion = scale.ResourceVersion rc, err = (*r.registry).UpdateController(ctx, rc) diff --git a/pkg/registry/experimental/controller/etcd/etcd_test.go b/pkg/registry/experimental/controller/etcd/etcd_test.go index 5119d88cbc..0cbb262be1 100644 --- a/pkg/registry/experimental/controller/etcd/etcd_test.go +++ b/pkg/registry/experimental/controller/etcd/etcd_test.go @@ -20,6 +20,7 @@ import ( "testing" "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/rest" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/registry/generic" @@ -116,7 +117,7 @@ func TestUpdate(t *testing.T) { }, } - if _, _, err := storage.Update(ctx, &update); err != nil { + if _, _, err := storage.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update, api.Scheme)); err != nil { t.Fatalf("unexpected error: %v", err) } obj, err := storage.Get(ctx, "foo") diff --git a/pkg/registry/generic/registry/store.go b/pkg/registry/generic/registry/store.go index 3e56d10b45..2a4979e7b7 100644 --- a/pkg/registry/generic/registry/store.go +++ b/pkg/registry/generic/registry/store.go @@ -238,46 +238,40 @@ func (e *Store) Create(ctx api.Context, obj runtime.Object) (runtime.Object, err // 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 api.Context, obj runtime.Object) (runtime.Object, bool, error) { - name, err := e.ObjectNameFunc(obj) - if err != nil { - return nil, false, err - } +func (e *Store) Update(ctx api.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { key, err := e.KeyFunc(ctx, name) if err != nil { return nil, false, err } - // If AllowUnconditionalUpdate() is true and the object specified by the user does not have a resource version, - // then we populate it with the latest version. - // Else, we check that the version specified by the user matches the version of latest storage object. - resourceVersion, err := e.Storage.Versioner().ObjectResourceVersion(obj) - if err != nil { - return nil, false, err + + var ( + creatingObj runtime.Object + creating = false + ) + + storagePreconditions := &storage.Preconditions{} + if preconditions := objInfo.Preconditions(); preconditions != nil { + storagePreconditions.UID = preconditions.UID } - doUnconditionalUpdate := resourceVersion == 0 && e.UpdateStrategy.AllowUnconditionalUpdate() - // TODO: expose TTL - creating := false + out := e.NewFunc() - meta, err := api.ObjectMetaFor(obj) - if err != nil { - return nil, false, kubeerr.NewInternalError(err) - } - var preconditions *storage.Preconditions - // If the UID of the new object is specified, we use it as an Update precondition. - if len(meta.UID) != 0 { - UIDCopy := meta.UID - preconditions = &storage.Preconditions{UID: &UIDCopy} - } - err = e.Storage.GuaranteedUpdate(ctx, key, out, true, preconditions, func(existing runtime.Object, res storage.ResponseMeta) (runtime.Object, *uint64, error) { - // Since we return 'obj' from this function and it can be modified outside this - // function, we are resetting resourceVersion to the initial value here. - // - // TODO: In fact, we should probably return a DeepCopy of obj in all places. - err := e.Storage.Versioner().UpdateObject(obj, resourceVersion) + + err = e.Storage.GuaranteedUpdate(ctx, key, out, true, storagePreconditions, func(existing runtime.Object, res storage.ResponseMeta) (runtime.Object, *uint64, error) { + // Given the existing object, get the new object + obj, err := objInfo.UpdatedObject(ctx, existing) if err != nil { return nil, nil, err } + // If AllowUnconditionalUpdate() is true and the object specified by the user does not have a resource version, + // then we populate it with the latest version. + // Else, we check that the version specified by the user matches the version of latest storage object. + resourceVersion, err := e.Storage.Versioner().ObjectResourceVersion(obj) + if err != nil { + return nil, nil, err + } + doUnconditionalUpdate := resourceVersion == 0 && e.UpdateStrategy.AllowUnconditionalUpdate() + version, err := e.Storage.Versioner().ObjectResourceVersion(existing) if err != nil { return nil, nil, err @@ -287,6 +281,7 @@ func (e *Store) Update(ctx api.Context, obj runtime.Object) (runtime.Object, boo return nil, nil, kubeerr.NewNotFound(e.QualifiedResource, name) } creating = true + creatingObj = obj if err := rest.BeforeCreate(e.CreateStrategy, ctx, obj); err != nil { return nil, nil, err } @@ -298,6 +293,7 @@ func (e *Store) Update(ctx api.Context, obj runtime.Object) (runtime.Object, boo } creating = false + creatingObj = nil if doUnconditionalUpdate { // Update the object's resource version to match the latest storage object's resource version. err = e.Storage.Versioner().UpdateObject(obj, res.ResourceVersion) @@ -338,7 +334,7 @@ func (e *Store) Update(ctx api.Context, obj runtime.Object) (runtime.Object, boo if err != nil { if creating { err = storeerr.InterpretCreateError(err, e.QualifiedResource, name) - err = rest.CheckGeneratedNameError(e.CreateStrategy, err, obj) + err = rest.CheckGeneratedNameError(e.CreateStrategy, err, creatingObj) } else { err = storeerr.InterpretUpdateError(err, e.QualifiedResource, name) } @@ -358,7 +354,7 @@ func (e *Store) Update(ctx api.Context, obj runtime.Object) (runtime.Object, boo } } if e.Decorator != nil { - if err := e.Decorator(obj); err != nil { + if err := e.Decorator(out); err != nil { return nil, false, err } } diff --git a/pkg/registry/generic/registry/store_test.go b/pkg/registry/generic/registry/store_test.go index a89eca3e60..67a62b1320 100644 --- a/pkg/registry/generic/registry/store_test.go +++ b/pkg/registry/generic/registry/store_test.go @@ -28,6 +28,7 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/errors" "k8s.io/kubernetes/pkg/api/meta" + "k8s.io/kubernetes/pkg/api/rest" "k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/fields" @@ -273,7 +274,7 @@ func TestStoreCreate(t *testing.T) { } func updateAndVerify(t *testing.T, ctx api.Context, registry *Store, pod *api.Pod) bool { - obj, _, err := registry.Update(ctx, pod) + obj, _, err := registry.Update(ctx, pod.Name, rest.DefaultUpdatedObjectInfo(pod, api.Scheme)) if err != nil { t.Errorf("Unexpected error: %v", err) return false @@ -309,7 +310,7 @@ func TestStoreUpdate(t *testing.T) { defer server.Terminate(t) // Test1 try to update a non-existing node - _, _, err := registry.Update(testContext, podA) + _, _, err := registry.Update(testContext, podA.Name, rest.DefaultUpdatedObjectInfo(podA, api.Scheme)) if !errors.IsNotFound(err) { t.Errorf("Unexpected error: %v", err) } @@ -322,7 +323,7 @@ func TestStoreUpdate(t *testing.T) { registry.UpdateStrategy.(*testRESTStrategy).allowCreateOnUpdate = false // Test3 outofDate - _, _, err = registry.Update(testContext, podAWithResourceVersion) + _, _, err = registry.Update(testContext, podAWithResourceVersion.Name, rest.DefaultUpdatedObjectInfo(podAWithResourceVersion, api.Scheme)) if !errors.IsConflict(err) { t.Errorf("Unexpected error updating podAWithResourceVersion: %v", err) } @@ -369,7 +370,8 @@ func TestNoOpUpdates(t *testing.T) { } var updateResult runtime.Object - if updateResult, _, err = registry.Update(api.NewDefaultContext(), newPod()); err != nil { + p := newPod() + if updateResult, _, err = registry.Update(api.NewDefaultContext(), p.Name, rest.DefaultUpdatedObjectInfo(p, api.Scheme)); err != nil { t.Fatalf("Unexpected error: %v", err) } @@ -446,8 +448,12 @@ func TestStoreCustomExport(t *testing.T) { if exportedPod.Labels["exact"] != "false" { t.Errorf("expected: exact->false, found: %s", exportedPod.Labels["exact"]) } + if exportedPod.Labels["prepare_create"] != "true" { + t.Errorf("expected: prepare_create->true, found: %s", exportedPod.Labels["prepare_create"]) + } delete(exportedPod.Labels, "exported") delete(exportedPod.Labels, "exact") + delete(exportedPod.Labels, "prepare_create") exportObjectMeta(&podA.ObjectMeta, false) podA.Spec = exportedPod.Spec if !reflect.DeepEqual(&podA, exportedPod) { @@ -483,6 +489,7 @@ func TestStoreBasicExport(t *testing.T) { if exportedPod.Labels["prepare_create"] != "true" { t.Errorf("expected: prepare_create->true, found: %s", exportedPod.Labels["prepare_create"]) } + delete(exportedPod.Labels, "prepare_create") exportObjectMeta(&podA.ObjectMeta, false) podA.Spec = exportedPod.Spec if !reflect.DeepEqual(&podA, exportedPod) { diff --git a/pkg/registry/horizontalpodautoscaler/etcd/etcd.go b/pkg/registry/horizontalpodautoscaler/etcd/etcd.go index d6d74b50cc..60fe1daf1f 100644 --- a/pkg/registry/horizontalpodautoscaler/etcd/etcd.go +++ b/pkg/registry/horizontalpodautoscaler/etcd/etcd.go @@ -18,6 +18,7 @@ package etcd import ( "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/rest" "k8s.io/kubernetes/pkg/apis/autoscaling" "k8s.io/kubernetes/pkg/fields" "k8s.io/kubernetes/pkg/labels" @@ -89,6 +90,6 @@ func (r *StatusREST) New() runtime.Object { } // Update alters the status subset of an object. -func (r *StatusREST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) { - return r.store.Update(ctx, obj) +func (r *StatusREST) Update(ctx api.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { + return r.store.Update(ctx, name, objInfo) } diff --git a/pkg/registry/ingress/etcd/etcd.go b/pkg/registry/ingress/etcd/etcd.go index 343e2b7358..85e6261e9d 100644 --- a/pkg/registry/ingress/etcd/etcd.go +++ b/pkg/registry/ingress/etcd/etcd.go @@ -18,6 +18,7 @@ package etcd import ( "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/rest" "k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/fields" "k8s.io/kubernetes/pkg/labels" @@ -91,6 +92,6 @@ func (r *StatusREST) New() runtime.Object { } // Update alters the status subset of an object. -func (r *StatusREST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) { - return r.store.Update(ctx, obj) +func (r *StatusREST) Update(ctx api.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { + return r.store.Update(ctx, name, objInfo) } diff --git a/pkg/registry/job/etcd/etcd.go b/pkg/registry/job/etcd/etcd.go index 575d102c30..71fac1e74c 100644 --- a/pkg/registry/job/etcd/etcd.go +++ b/pkg/registry/job/etcd/etcd.go @@ -18,6 +18,7 @@ package etcd import ( "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/rest" "k8s.io/kubernetes/pkg/apis/batch" "k8s.io/kubernetes/pkg/fields" "k8s.io/kubernetes/pkg/labels" @@ -93,6 +94,6 @@ func (r *StatusREST) New() runtime.Object { } // Update alters the status subset of an object. -func (r *StatusREST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) { - return r.store.Update(ctx, obj) +func (r *StatusREST) Update(ctx api.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { + return r.store.Update(ctx, name, objInfo) } diff --git a/pkg/registry/namespace/etcd/etcd.go b/pkg/registry/namespace/etcd/etcd.go index 9b4b5ce847..894304df9a 100644 --- a/pkg/registry/namespace/etcd/etcd.go +++ b/pkg/registry/namespace/etcd/etcd.go @@ -22,6 +22,7 @@ import ( "k8s.io/kubernetes/pkg/api" apierrors "k8s.io/kubernetes/pkg/api/errors" storageerr "k8s.io/kubernetes/pkg/api/errors/storage" + "k8s.io/kubernetes/pkg/api/rest" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/fields" "k8s.io/kubernetes/pkg/labels" @@ -176,8 +177,8 @@ func (r *StatusREST) New() runtime.Object { } // Update alters the status subset of an object. -func (r *StatusREST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) { - return r.store.Update(ctx, obj) +func (r *StatusREST) Update(ctx api.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { + return r.store.Update(ctx, name, objInfo) } func (r *FinalizeREST) New() runtime.Object { @@ -185,6 +186,6 @@ func (r *FinalizeREST) New() runtime.Object { } // Update alters the status finalizers subset of an object. -func (r *FinalizeREST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) { - return r.store.Update(ctx, obj) +func (r *FinalizeREST) Update(ctx api.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { + return r.store.Update(ctx, name, objInfo) } diff --git a/pkg/registry/namespace/registry.go b/pkg/registry/namespace/registry.go index 33b50d32e6..b78d3afd7f 100644 --- a/pkg/registry/namespace/registry.go +++ b/pkg/registry/namespace/registry.go @@ -69,7 +69,7 @@ func (s *storage) CreateNamespace(ctx api.Context, namespace *api.Namespace) err } func (s *storage) UpdateNamespace(ctx api.Context, namespace *api.Namespace) error { - _, _, err := s.Update(ctx, namespace) + _, _, err := s.Update(ctx, namespace.Name, rest.DefaultUpdatedObjectInfo(namespace, api.Scheme)) return err } diff --git a/pkg/registry/node/etcd/etcd.go b/pkg/registry/node/etcd/etcd.go index ae7ba107d1..bc707b7f11 100644 --- a/pkg/registry/node/etcd/etcd.go +++ b/pkg/registry/node/etcd/etcd.go @@ -55,8 +55,8 @@ func (r *StatusREST) New() runtime.Object { } // Update alters the status subset of an object. -func (r *StatusREST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) { - return r.store.Update(ctx, obj) +func (r *StatusREST) Update(ctx api.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { + return r.store.Update(ctx, name, objInfo) } // NewREST returns a RESTStorage object that will work against nodes. diff --git a/pkg/registry/node/registry.go b/pkg/registry/node/registry.go index 3d6f3bf07e..aae18bfc21 100644 --- a/pkg/registry/node/registry.go +++ b/pkg/registry/node/registry.go @@ -58,7 +58,7 @@ func (s *storage) CreateNode(ctx api.Context, node *api.Node) error { } func (s *storage) UpdateNode(ctx api.Context, node *api.Node) error { - _, _, err := s.Update(ctx, node) + _, _, err := s.Update(ctx, node.Name, rest.DefaultUpdatedObjectInfo(node, api.Scheme)) return err } diff --git a/pkg/registry/persistentvolume/etcd/etcd.go b/pkg/registry/persistentvolume/etcd/etcd.go index c9f032977e..1b53f4ddd1 100644 --- a/pkg/registry/persistentvolume/etcd/etcd.go +++ b/pkg/registry/persistentvolume/etcd/etcd.go @@ -18,6 +18,7 @@ package etcd import ( "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/rest" "k8s.io/kubernetes/pkg/fields" "k8s.io/kubernetes/pkg/labels" "k8s.io/kubernetes/pkg/registry/cachesize" @@ -81,6 +82,6 @@ func (r *StatusREST) New() runtime.Object { } // Update alters the status subset of an object. -func (r *StatusREST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) { - return r.store.Update(ctx, obj) +func (r *StatusREST) Update(ctx api.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { + return r.store.Update(ctx, name, objInfo) } diff --git a/pkg/registry/persistentvolume/etcd/etcd_test.go b/pkg/registry/persistentvolume/etcd/etcd_test.go index e43775c3d1..9339d98299 100644 --- a/pkg/registry/persistentvolume/etcd/etcd_test.go +++ b/pkg/registry/persistentvolume/etcd/etcd_test.go @@ -21,6 +21,7 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/resource" + "k8s.io/kubernetes/pkg/api/rest" "k8s.io/kubernetes/pkg/fields" "k8s.io/kubernetes/pkg/labels" "k8s.io/kubernetes/pkg/registry/generic" @@ -167,7 +168,7 @@ func TestUpdateStatus(t *testing.T) { }, } - _, _, err = statusStorage.Update(ctx, pvIn) + _, _, err = statusStorage.Update(ctx, pvIn.Name, rest.DefaultUpdatedObjectInfo(pvIn, api.Scheme)) if err != nil { t.Fatalf("Unexpected error: %v", err) } diff --git a/pkg/registry/persistentvolumeclaim/etcd/etcd.go b/pkg/registry/persistentvolumeclaim/etcd/etcd.go index 1946d1c057..5621f3878b 100644 --- a/pkg/registry/persistentvolumeclaim/etcd/etcd.go +++ b/pkg/registry/persistentvolumeclaim/etcd/etcd.go @@ -18,6 +18,7 @@ package etcd import ( "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/rest" "k8s.io/kubernetes/pkg/fields" "k8s.io/kubernetes/pkg/labels" "k8s.io/kubernetes/pkg/registry/cachesize" @@ -81,6 +82,6 @@ func (r *StatusREST) New() runtime.Object { } // Update alters the status subset of an object. -func (r *StatusREST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) { - return r.store.Update(ctx, obj) +func (r *StatusREST) Update(ctx api.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { + return r.store.Update(ctx, name, objInfo) } diff --git a/pkg/registry/persistentvolumeclaim/etcd/etcd_test.go b/pkg/registry/persistentvolumeclaim/etcd/etcd_test.go index a4378228ba..22a37d5f37 100644 --- a/pkg/registry/persistentvolumeclaim/etcd/etcd_test.go +++ b/pkg/registry/persistentvolumeclaim/etcd/etcd_test.go @@ -21,6 +21,7 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/resource" + "k8s.io/kubernetes/pkg/api/rest" "k8s.io/kubernetes/pkg/fields" "k8s.io/kubernetes/pkg/labels" "k8s.io/kubernetes/pkg/registry/generic" @@ -168,7 +169,7 @@ func TestUpdateStatus(t *testing.T) { }, } - _, _, err = statusStorage.Update(ctx, pvc) + _, _, err = statusStorage.Update(ctx, pvc.Name, rest.DefaultUpdatedObjectInfo(pvc, api.Scheme)) if err != nil { t.Fatalf("Unexpected error: %v", err) } diff --git a/pkg/registry/petset/etcd/etcd.go b/pkg/registry/petset/etcd/etcd.go index 4ffed933cd..dbda1a9779 100644 --- a/pkg/registry/petset/etcd/etcd.go +++ b/pkg/registry/petset/etcd/etcd.go @@ -18,6 +18,7 @@ package etcd import ( "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/rest" appsapi "k8s.io/kubernetes/pkg/apis/apps" "k8s.io/kubernetes/pkg/fields" "k8s.io/kubernetes/pkg/labels" @@ -91,6 +92,6 @@ func (r *StatusREST) New() runtime.Object { } // Update alters the status subset of an object. -func (r *StatusREST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) { - return r.store.Update(ctx, obj) +func (r *StatusREST) Update(ctx api.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { + return r.store.Update(ctx, name, objInfo) } diff --git a/pkg/registry/petset/etcd/etcd_test.go b/pkg/registry/petset/etcd/etcd_test.go index 6b4502781b..cbca15e4a4 100644 --- a/pkg/registry/petset/etcd/etcd_test.go +++ b/pkg/registry/petset/etcd/etcd_test.go @@ -20,6 +20,7 @@ import ( "testing" "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/rest" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/apis/apps" "k8s.io/kubernetes/pkg/fields" @@ -114,7 +115,7 @@ func TestStatusUpdate(t *testing.T) { }, } - if _, _, err := statusStorage.Update(ctx, &update); err != nil { + if _, _, err := statusStorage.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update, api.Scheme)); err != nil { t.Fatalf("unexpected error: %v", err) } obj, err := storage.Get(ctx, "foo") diff --git a/pkg/registry/pod/etcd/etcd.go b/pkg/registry/pod/etcd/etcd.go index dda8c9af53..cdbe23ec71 100644 --- a/pkg/registry/pod/etcd/etcd.go +++ b/pkg/registry/pod/etcd/etcd.go @@ -199,6 +199,6 @@ func (r *StatusREST) New() runtime.Object { } // Update alters the status subset of an object. -func (r *StatusREST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) { - return r.store.Update(ctx, obj) +func (r *StatusREST) Update(ctx api.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { + return r.store.Update(ctx, name, objInfo) } diff --git a/pkg/registry/pod/etcd/etcd_test.go b/pkg/registry/pod/etcd/etcd_test.go index 1b7dc18705..b9a6cb6a4c 100644 --- a/pkg/registry/pod/etcd/etcd_test.go +++ b/pkg/registry/pod/etcd/etcd_test.go @@ -608,7 +608,7 @@ func TestEtcdUpdateNotScheduled(t *testing.T) { } podIn := validChangedPod() - _, _, err := storage.Update(ctx, podIn) + _, _, err := storage.Update(ctx, podIn.Name, rest.DefaultUpdatedObjectInfo(podIn, api.Scheme)) if err != nil { t.Errorf("Unexpected error: %v", err) } @@ -675,7 +675,7 @@ func TestEtcdUpdateScheduled(t *testing.T) { SecurityContext: &api.PodSecurityContext{}, }, } - _, _, err = storage.Update(ctx, &podIn) + _, _, err = storage.Update(ctx, podIn.Name, rest.DefaultUpdatedObjectInfo(&podIn, api.Scheme)) if err != nil { t.Errorf("Unexpected error: %v", err) } @@ -756,7 +756,7 @@ func TestEtcdUpdateStatus(t *testing.T) { expected.Labels = podIn.Labels expected.Status = podIn.Status - _, _, err = statusStorage.Update(ctx, &podIn) + _, _, err = statusStorage.Update(ctx, podIn.Name, rest.DefaultUpdatedObjectInfo(&podIn, api.Scheme)) if err != nil { t.Fatalf("Unexpected error: %v", err) } @@ -765,10 +765,10 @@ func TestEtcdUpdateStatus(t *testing.T) { t.Errorf("unexpected error: %v", err) } podOut := obj.(*api.Pod) - // Check to verify the Spec, Label, and Status updates match from change above. Those are the fields changed. - if !api.Semantic.DeepEqual(podOut.Spec, podIn.Spec) || - !api.Semantic.DeepEqual(podOut.Labels, podIn.Labels) || - !api.Semantic.DeepEqual(podOut.Status, podIn.Status) { - t.Errorf("objects differ: %v", diff.ObjectDiff(podOut, podIn)) + // Check to verify the Label, and Status updates match from change above. Those are the fields changed. + if !api.Semantic.DeepEqual(podOut.Spec, expected.Spec) || + !api.Semantic.DeepEqual(podOut.Labels, expected.Labels) || + !api.Semantic.DeepEqual(podOut.Status, expected.Status) { + t.Errorf("objects differ: %v", diff.ObjectDiff(podOut, expected)) } } diff --git a/pkg/registry/poddisruptionbudget/etcd/etcd.go b/pkg/registry/poddisruptionbudget/etcd/etcd.go index d148cb1887..2e172fd243 100644 --- a/pkg/registry/poddisruptionbudget/etcd/etcd.go +++ b/pkg/registry/poddisruptionbudget/etcd/etcd.go @@ -18,6 +18,7 @@ package etcd import ( "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/rest" policyapi "k8s.io/kubernetes/pkg/apis/policy" "k8s.io/kubernetes/pkg/fields" "k8s.io/kubernetes/pkg/labels" @@ -91,6 +92,6 @@ func (r *StatusREST) New() runtime.Object { } // Update alters the status subset of an object. -func (r *StatusREST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) { - return r.store.Update(ctx, obj) +func (r *StatusREST) Update(ctx api.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { + return r.store.Update(ctx, name, objInfo) } diff --git a/pkg/registry/poddisruptionbudget/etcd/etcd_test.go b/pkg/registry/poddisruptionbudget/etcd/etcd_test.go index 02badac239..5efabb6849 100644 --- a/pkg/registry/poddisruptionbudget/etcd/etcd_test.go +++ b/pkg/registry/poddisruptionbudget/etcd/etcd_test.go @@ -20,6 +20,7 @@ import ( "testing" "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/rest" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/apis/policy" "k8s.io/kubernetes/pkg/fields" @@ -99,7 +100,7 @@ func TestStatusUpdate(t *testing.T) { }, } - if _, _, err := statusStorage.Update(ctx, &update); err != nil { + if _, _, err := statusStorage.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update, api.Scheme)); err != nil { t.Fatalf("unexpected error: %v", err) } obj, err := storage.Get(ctx, "foo") diff --git a/pkg/registry/replicaset/etcd/etcd.go b/pkg/registry/replicaset/etcd/etcd.go index 4571098e96..8d5aaeb283 100644 --- a/pkg/registry/replicaset/etcd/etcd.go +++ b/pkg/registry/replicaset/etcd/etcd.go @@ -116,8 +116,8 @@ func (r *StatusREST) New() runtime.Object { } // Update alters the status subset of an object. -func (r *StatusREST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) { - return r.store.Update(ctx, obj) +func (r *StatusREST) Update(ctx api.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { + return r.store.Update(ctx, name, objInfo) } type ScaleREST struct { @@ -144,7 +144,21 @@ func (r *ScaleREST) Get(ctx api.Context, name string) (runtime.Object, error) { return scale, err } -func (r *ScaleREST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) { +func (r *ScaleREST) Update(ctx api.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { + rs, err := r.registry.GetReplicaSet(ctx, name) + if err != nil { + return nil, false, errors.NewNotFound(extensions.Resource("replicasets/scale"), name) + } + + oldScale, err := scaleFromReplicaSet(rs) + if err != nil { + return nil, false, err + } + + obj, err := objInfo.UpdatedObject(ctx, oldScale) + if err != nil { + return nil, false, err + } if obj == nil { return nil, false, errors.NewBadRequest(fmt.Sprintf("nil update passed to Scale")) } @@ -157,10 +171,6 @@ func (r *ScaleREST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, return nil, false, errors.NewInvalid(extensions.Kind("Scale"), scale.Name, errs) } - rs, err := r.registry.GetReplicaSet(ctx, scale.Name) - if err != nil { - return nil, false, errors.NewNotFound(extensions.Resource("replicasets/scale"), scale.Name) - } rs.Spec.Replicas = scale.Spec.Replicas rs.ResourceVersion = scale.ResourceVersion rs, err = r.registry.UpdateReplicaSet(ctx, rs) diff --git a/pkg/registry/replicaset/etcd/etcd_test.go b/pkg/registry/replicaset/etcd/etcd_test.go index 30eaa6e5be..7443e7219f 100644 --- a/pkg/registry/replicaset/etcd/etcd_test.go +++ b/pkg/registry/replicaset/etcd/etcd_test.go @@ -21,6 +21,7 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/errors" + "k8s.io/kubernetes/pkg/api/rest" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/fields" @@ -162,7 +163,7 @@ func TestGenerationNumber(t *testing.T) { // Updates to spec should increment the generation number storedRS.Spec.Replicas += 1 - storage.ReplicaSet.Update(ctx, storedRS) + storage.ReplicaSet.Update(ctx, storedRS.Name, rest.DefaultUpdatedObjectInfo(storedRS, api.Scheme)) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -177,7 +178,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) + storage.ReplicaSet.Update(ctx, storedRS.Name, rest.DefaultUpdatedObjectInfo(storedRS, api.Scheme)) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -299,7 +300,7 @@ func TestScaleUpdate(t *testing.T) { }, } - if _, _, err := storage.Scale.Update(ctx, &update); err != nil { + if _, _, err := storage.Scale.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update, api.Scheme)); err != nil { t.Fatalf("error updating scale %v: %v", update, err) } @@ -315,7 +316,7 @@ func TestScaleUpdate(t *testing.T) { update.ResourceVersion = rs.ResourceVersion update.Spec.Replicas = 15 - if _, _, err = storage.Scale.Update(ctx, &update); err != nil && !errors.IsConflict(err) { + if _, _, err = storage.Scale.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update, api.Scheme)); err != nil && !errors.IsConflict(err) { t.Fatalf("unexpected error, expecting an update conflict but got %v", err) } } @@ -339,7 +340,7 @@ func TestStatusUpdate(t *testing.T) { }, } - if _, _, err := storage.Status.Update(ctx, &update); err != nil { + if _, _, err := storage.Status.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update, api.Scheme)); err != nil { t.Fatalf("unexpected error: %v", err) } obj, err := storage.ReplicaSet.Get(ctx, "foo") diff --git a/pkg/registry/replicaset/registry.go b/pkg/registry/replicaset/registry.go index d1279179a3..284ed47f32 100644 --- a/pkg/registry/replicaset/registry.go +++ b/pkg/registry/replicaset/registry.go @@ -80,7 +80,7 @@ func (s *storage) CreateReplicaSet(ctx api.Context, replicaSet *extensions.Repli } func (s *storage) UpdateReplicaSet(ctx api.Context, replicaSet *extensions.ReplicaSet) (*extensions.ReplicaSet, error) { - obj, _, err := s.Update(ctx, replicaSet) + obj, _, err := s.Update(ctx, replicaSet.Name, rest.DefaultUpdatedObjectInfo(replicaSet, api.Scheme)) if err != nil { return nil, err } diff --git a/pkg/registry/resourcequota/etcd/etcd.go b/pkg/registry/resourcequota/etcd/etcd.go index 3e08c64749..1e13c63e00 100644 --- a/pkg/registry/resourcequota/etcd/etcd.go +++ b/pkg/registry/resourcequota/etcd/etcd.go @@ -18,6 +18,7 @@ package etcd import ( "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/rest" "k8s.io/kubernetes/pkg/fields" "k8s.io/kubernetes/pkg/labels" "k8s.io/kubernetes/pkg/registry/cachesize" @@ -81,6 +82,6 @@ func (r *StatusREST) New() runtime.Object { } // Update alters the status subset of an object. -func (r *StatusREST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) { - return r.store.Update(ctx, obj) +func (r *StatusREST) Update(ctx api.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { + return r.store.Update(ctx, name, objInfo) } diff --git a/pkg/registry/resourcequota/etcd/etcd_test.go b/pkg/registry/resourcequota/etcd/etcd_test.go index 8154cc828f..97f4bf36cd 100644 --- a/pkg/registry/resourcequota/etcd/etcd_test.go +++ b/pkg/registry/resourcequota/etcd/etcd_test.go @@ -21,6 +21,7 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/resource" + "k8s.io/kubernetes/pkg/api/rest" "k8s.io/kubernetes/pkg/fields" "k8s.io/kubernetes/pkg/labels" "k8s.io/kubernetes/pkg/registry/generic" @@ -177,7 +178,7 @@ func TestUpdateStatus(t *testing.T) { }, } - _, _, err = status.Update(ctx, resourcequotaIn) + _, _, err = status.Update(ctx, resourcequotaIn.Name, rest.DefaultUpdatedObjectInfo(resourcequotaIn, api.Scheme)) if err != nil { t.Fatalf("Unexpected error: %v", err) } diff --git a/pkg/registry/secret/registry.go b/pkg/registry/secret/registry.go index ef989f0067..157ac7191a 100644 --- a/pkg/registry/secret/registry.go +++ b/pkg/registry/secret/registry.go @@ -69,7 +69,7 @@ func (s *storage) CreateSecret(ctx api.Context, secret *api.Secret) (*api.Secret } func (s *storage) UpdateSecret(ctx api.Context, secret *api.Secret) (*api.Secret, error) { - obj, _, err := s.Update(ctx, secret) + obj, _, err := s.Update(ctx, secret.Name, rest.DefaultUpdatedObjectInfo(secret, api.Scheme)) return obj.(*api.Secret), err } diff --git a/pkg/registry/service/etcd/etcd.go b/pkg/registry/service/etcd/etcd.go index 44b9a67d3e..fed31753e9 100644 --- a/pkg/registry/service/etcd/etcd.go +++ b/pkg/registry/service/etcd/etcd.go @@ -18,6 +18,7 @@ package etcd import ( "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/rest" "k8s.io/kubernetes/pkg/fields" "k8s.io/kubernetes/pkg/labels" "k8s.io/kubernetes/pkg/registry/cachesize" @@ -79,6 +80,6 @@ func (r *StatusREST) New() runtime.Object { } // Update alters the status subset of an object. -func (r *StatusREST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) { - return r.store.Update(ctx, obj) +func (r *StatusREST) Update(ctx api.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { + return r.store.Update(ctx, name, objInfo) } diff --git a/pkg/registry/service/registry.go b/pkg/registry/service/registry.go index 8fdbabab64..fa7df32af5 100644 --- a/pkg/registry/service/registry.go +++ b/pkg/registry/service/registry.go @@ -77,7 +77,7 @@ func (s *storage) DeleteService(ctx api.Context, name string) error { } func (s *storage) UpdateService(ctx api.Context, svc *api.Service) (*api.Service, error) { - obj, _, err := s.Update(ctx, svc) + obj, _, err := s.Update(ctx, svc.Name, rest.DefaultUpdatedObjectInfo(svc, api.Scheme)) if err != nil { return nil, err } diff --git a/pkg/registry/service/rest.go b/pkg/registry/service/rest.go index 40d7e373b0..875d15779c 100644 --- a/pkg/registry/service/rest.go +++ b/pkg/registry/service/rest.go @@ -212,17 +212,22 @@ func (*REST) NewList() runtime.Object { return &api.ServiceList{} } -func (rs *REST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) { +func (rs *REST) Update(ctx api.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { + oldService, err := rs.registry.GetService(ctx, name) + if err != nil { + return nil, false, err + } + + obj, err := objInfo.UpdatedObject(ctx, oldService) + if err != nil { + return nil, false, err + } + service := obj.(*api.Service) if !api.ValidNamespace(ctx, &service.ObjectMeta) { return nil, false, errors.NewConflict(api.Resource("services"), service.Namespace, fmt.Errorf("Service.Namespace does not match the provided context")) } - oldService, err := rs.registry.GetService(ctx, service.Name) - if err != nil { - return nil, false, err - } - // Copy over non-user fields // TODO: make this a merge function if errs := validation.ValidateServiceUpdate(service, oldService); len(errs) > 0 { diff --git a/pkg/registry/service/rest_test.go b/pkg/registry/service/rest_test.go index c1c7120b16..fd22db12f0 100644 --- a/pkg/registry/service/rest_test.go +++ b/pkg/registry/service/rest_test.go @@ -180,7 +180,7 @@ func TestServiceRegistryUpdate(t *testing.T) { if err != nil { t.Fatalf("Expected no error: %v", err) } - updated_svc, created, err := storage.Update(ctx, &api.Service{ + updated_svc, created, err := storage.Update(ctx, "foo", rest.DefaultUpdatedObjectInfo(&api.Service{ ObjectMeta: api.ObjectMeta{ Name: "foo", ResourceVersion: svc.ResourceVersion}, @@ -194,7 +194,7 @@ func TestServiceRegistryUpdate(t *testing.T) { TargetPort: intstr.FromInt(6502), }}, }, - }) + }, api.Scheme)) if err != nil { t.Fatalf("Expected no error: %v", err) } @@ -255,7 +255,7 @@ func TestServiceStorageValidatesUpdate(t *testing.T) { }, } for _, failureCase := range failureCases { - c, created, err := storage.Update(ctx, &failureCase) + c, created, err := storage.Update(ctx, failureCase.Name, rest.DefaultUpdatedObjectInfo(&failureCase, api.Scheme)) if c != nil || created { t.Errorf("Expected nil object or created false") } @@ -363,14 +363,14 @@ func TestServiceRegistryUpdateExternalService(t *testing.T) { // Modify load balancer to be external. svc2 := deepCloneService(svc1) svc2.Spec.Type = api.ServiceTypeLoadBalancer - if _, _, err := storage.Update(ctx, svc2); err != nil { + if _, _, err := storage.Update(ctx, svc2.Name, rest.DefaultUpdatedObjectInfo(svc2, api.Scheme)); err != nil { t.Fatalf("Unexpected error: %v", err) } // Change port. svc3 := deepCloneService(svc2) svc3.Spec.Ports[0].Port = 6504 - if _, _, err := storage.Update(ctx, svc3); err != nil { + if _, _, err := storage.Update(ctx, svc3.Name, rest.DefaultUpdatedObjectInfo(svc3, api.Scheme)); err != nil { t.Fatalf("Unexpected error: %v", err) } } @@ -406,7 +406,7 @@ func TestServiceRegistryUpdateMultiPortExternalService(t *testing.T) { // Modify ports svc2 := deepCloneService(svc1) svc2.Spec.Ports[1].Port = 8088 - if _, _, err := storage.Update(ctx, svc2); err != nil { + if _, _, err := storage.Update(ctx, svc2.Name, rest.DefaultUpdatedObjectInfo(svc2, api.Scheme)); err != nil { t.Fatalf("Unexpected error: %v", err) } } @@ -580,7 +580,7 @@ func TestServiceRegistryList(t *testing.T) { } func TestServiceRegistryIPAllocation(t *testing.T) { - rest, _ := NewTestREST(t, nil) + storage, _ := NewTestREST(t, nil) svc1 := &api.Service{ ObjectMeta: api.ObjectMeta{Name: "foo"}, @@ -596,7 +596,7 @@ func TestServiceRegistryIPAllocation(t *testing.T) { }, } ctx := api.NewDefaultContext() - created_svc1, _ := rest.Create(ctx, svc1) + created_svc1, _ := storage.Create(ctx, svc1) created_service_1 := created_svc1.(*api.Service) if created_service_1.Name != "foo" { t.Errorf("Expected foo, but got %v", created_service_1.Name) @@ -618,7 +618,7 @@ func TestServiceRegistryIPAllocation(t *testing.T) { }}, }} ctx = api.NewDefaultContext() - created_svc2, _ := rest.Create(ctx, svc2) + created_svc2, _ := storage.Create(ctx, svc2) created_service_2 := created_svc2.(*api.Service) if created_service_2.Name != "bar" { t.Errorf("Expected bar, but got %v", created_service_2.Name) @@ -630,7 +630,7 @@ func TestServiceRegistryIPAllocation(t *testing.T) { testIPs := []string{"1.2.3.93", "1.2.3.94", "1.2.3.95", "1.2.3.96"} testIP := "" for _, ip := range testIPs { - if !rest.serviceIPs.(*ipallocator.Range).Has(net.ParseIP(ip)) { + if !storage.serviceIPs.(*ipallocator.Range).Has(net.ParseIP(ip)) { testIP = ip break } @@ -651,7 +651,7 @@ func TestServiceRegistryIPAllocation(t *testing.T) { }, } ctx = api.NewDefaultContext() - created_svc3, err := rest.Create(ctx, svc3) + created_svc3, err := storage.Create(ctx, svc3) if err != nil { t.Fatal(err) } @@ -662,7 +662,7 @@ func TestServiceRegistryIPAllocation(t *testing.T) { } func TestServiceRegistryIPReallocation(t *testing.T) { - rest, _ := NewTestREST(t, nil) + storage, _ := NewTestREST(t, nil) svc1 := &api.Service{ ObjectMeta: api.ObjectMeta{Name: "foo"}, @@ -678,7 +678,7 @@ func TestServiceRegistryIPReallocation(t *testing.T) { }, } ctx := api.NewDefaultContext() - created_svc1, _ := rest.Create(ctx, svc1) + created_svc1, _ := storage.Create(ctx, svc1) created_service_1 := created_svc1.(*api.Service) if created_service_1.Name != "foo" { t.Errorf("Expected foo, but got %v", created_service_1.Name) @@ -687,7 +687,7 @@ func TestServiceRegistryIPReallocation(t *testing.T) { t.Errorf("Unexpected ClusterIP: %s", created_service_1.Spec.ClusterIP) } - _, err := rest.Delete(ctx, created_service_1.Name) + _, err := storage.Delete(ctx, created_service_1.Name) if err != nil { t.Errorf("Unexpected error deleting service: %v", err) } @@ -706,7 +706,7 @@ func TestServiceRegistryIPReallocation(t *testing.T) { }, } ctx = api.NewDefaultContext() - created_svc2, _ := rest.Create(ctx, svc2) + created_svc2, _ := storage.Create(ctx, svc2) created_service_2 := created_svc2.(*api.Service) if created_service_2.Name != "bar" { t.Errorf("Expected bar, but got %v", created_service_2.Name) @@ -717,7 +717,7 @@ func TestServiceRegistryIPReallocation(t *testing.T) { } func TestServiceRegistryIPUpdate(t *testing.T) { - rest, _ := NewTestREST(t, nil) + storage, _ := NewTestREST(t, nil) svc := &api.Service{ ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "1"}, @@ -733,7 +733,7 @@ func TestServiceRegistryIPUpdate(t *testing.T) { }, } ctx := api.NewDefaultContext() - created_svc, _ := rest.Create(ctx, svc) + created_svc, _ := storage.Create(ctx, svc) 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) @@ -745,7 +745,7 @@ func TestServiceRegistryIPUpdate(t *testing.T) { update := deepCloneService(created_service) update.Spec.Ports[0].Port = 6503 - updated_svc, _, _ := rest.Update(ctx, update) + updated_svc, _, _ := storage.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(update, api.Scheme)) 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) @@ -754,7 +754,7 @@ func TestServiceRegistryIPUpdate(t *testing.T) { testIPs := []string{"1.2.3.93", "1.2.3.94", "1.2.3.95", "1.2.3.96"} testIP := "" for _, ip := range testIPs { - if !rest.serviceIPs.(*ipallocator.Range).Has(net.ParseIP(ip)) { + if !storage.serviceIPs.(*ipallocator.Range).Has(net.ParseIP(ip)) { testIP = ip break } @@ -764,14 +764,14 @@ func TestServiceRegistryIPUpdate(t *testing.T) { update.Spec.Ports[0].Port = 6503 update.Spec.ClusterIP = testIP // Error: Cluster IP is immutable - _, _, err := rest.Update(ctx, update) + _, _, err := storage.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(update, api.Scheme)) if err == nil || !errors.IsInvalid(err) { t.Errorf("Unexpected error type: %v", err) } } func TestServiceRegistryIPLoadBalancer(t *testing.T) { - rest, _ := NewTestREST(t, nil) + storage, _ := NewTestREST(t, nil) svc := &api.Service{ ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "1"}, @@ -787,7 +787,7 @@ func TestServiceRegistryIPLoadBalancer(t *testing.T) { }, } ctx := api.NewDefaultContext() - created_svc, _ := rest.Create(ctx, svc) + created_svc, _ := storage.Create(ctx, svc) 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) @@ -798,20 +798,20 @@ func TestServiceRegistryIPLoadBalancer(t *testing.T) { update := deepCloneService(created_service) - _, _, err := rest.Update(ctx, update) + _, _, err := storage.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(update, api.Scheme)) if err != nil { t.Errorf("Unexpected error %v", err) } } func TestUpdateServiceWithConflictingNamespace(t *testing.T) { - storage := REST{} + storage, _ := NewTestREST(t, nil) service := &api.Service{ ObjectMeta: api.ObjectMeta{Name: "test", Namespace: "not-default"}, } ctx := api.NewDefaultContext() - obj, created, err := storage.Update(ctx, service) + obj, created, err := storage.Update(ctx, service.Name, rest.DefaultUpdatedObjectInfo(service, api.Scheme)) if obj != nil || created { t.Error("Expected a nil object, but we got a value or created was true") } diff --git a/pkg/registry/serviceaccount/registry.go b/pkg/registry/serviceaccount/registry.go index 4dad500bc6..2a7a1c7dd5 100644 --- a/pkg/registry/serviceaccount/registry.go +++ b/pkg/registry/serviceaccount/registry.go @@ -69,7 +69,7 @@ func (s *storage) CreateServiceAccount(ctx api.Context, serviceAccount *api.Serv } func (s *storage) UpdateServiceAccount(ctx api.Context, serviceAccount *api.ServiceAccount) error { - _, _, err := s.Update(ctx, serviceAccount) + _, _, err := s.Update(ctx, serviceAccount.Name, rest.DefaultUpdatedObjectInfo(serviceAccount, api.Scheme)) return err } diff --git a/pkg/registry/thirdpartyresourcedata/registry.go b/pkg/registry/thirdpartyresourcedata/registry.go index 5e560dede2..058276d1e2 100644 --- a/pkg/registry/thirdpartyresourcedata/registry.go +++ b/pkg/registry/thirdpartyresourcedata/registry.go @@ -70,7 +70,7 @@ func (s *storage) CreateThirdPartyResourceData(ctx api.Context, ThirdPartyResour } func (s *storage) UpdateThirdPartyResourceData(ctx api.Context, ThirdPartyResourceData *extensions.ThirdPartyResourceData) (*extensions.ThirdPartyResourceData, error) { - obj, _, err := s.Update(ctx, ThirdPartyResourceData) + obj, _, err := s.Update(ctx, ThirdPartyResourceData.Name, rest.DefaultUpdatedObjectInfo(ThirdPartyResourceData, api.Scheme)) return obj.(*extensions.ThirdPartyResourceData), err } diff --git a/plugin/pkg/admission/alwayspullimages/admission_test.go b/plugin/pkg/admission/alwayspullimages/admission_test.go index da9d044923..ffd5435ae2 100644 --- a/plugin/pkg/admission/alwayspullimages/admission_test.go +++ b/plugin/pkg/admission/alwayspullimages/admission_test.go @@ -46,7 +46,7 @@ func TestAdmission(t *testing.T) { }, }, } - err := handler.Admit(admission.NewAttributesRecord(&pod, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, nil)) + err := handler.Admit(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, nil)) if err != nil { t.Errorf("Unexpected error returned from admission handler") } @@ -108,7 +108,7 @@ func TestOtherResources(t *testing.T) { for _, tc := range tests { handler := &alwaysPullImages{} - err := handler.Admit(admission.NewAttributesRecord(tc.object, api.Kind(tc.kind).WithVersion("version"), namespace, name, api.Resource(tc.resource).WithVersion("version"), tc.subresource, admission.Create, nil)) + err := handler.Admit(admission.NewAttributesRecord(tc.object, nil, api.Kind(tc.kind).WithVersion("version"), namespace, name, api.Resource(tc.resource).WithVersion("version"), tc.subresource, admission.Create, nil)) if tc.expectError { if err == nil { diff --git a/plugin/pkg/admission/antiaffinity/admission_test.go b/plugin/pkg/admission/antiaffinity/admission_test.go index 74e864aaaf..bded0340b6 100644 --- a/plugin/pkg/admission/antiaffinity/admission_test.go +++ b/plugin/pkg/admission/antiaffinity/admission_test.go @@ -17,10 +17,11 @@ limitations under the License. package antiaffinity import ( + "testing" + "k8s.io/kubernetes/pkg/admission" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/unversioned" - "testing" ) // ensures the hard PodAntiAffinity is denied if it defines TopologyKey other than kubernetes.io/hostname. @@ -202,7 +203,7 @@ func TestInterPodAffinityAdmission(t *testing.T) { } for _, test := range tests { pod.ObjectMeta.Annotations = test.affinity - err := handler.Admit(admission.NewAttributesRecord(&pod, api.Kind("Pod").WithVersion("version"), "foo", "name", api.Resource("pods").WithVersion("version"), "", "ignored", nil)) + err := handler.Admit(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), "foo", "name", api.Resource("pods").WithVersion("version"), "", "ignored", nil)) if test.errorExpected && err == nil { t.Errorf("Expected error for Anti Affinity %+v but did not get an error", test.affinity) diff --git a/plugin/pkg/admission/deny/admission_test.go b/plugin/pkg/admission/deny/admission_test.go index 293763b6cb..3571b82633 100644 --- a/plugin/pkg/admission/deny/admission_test.go +++ b/plugin/pkg/admission/deny/admission_test.go @@ -25,7 +25,7 @@ import ( func TestAdmission(t *testing.T) { handler := NewAlwaysDeny() - err := handler.Admit(admission.NewAttributesRecord(nil, api.Kind("kind").WithVersion("version"), "namespace", "name", api.Resource("resource").WithVersion("version"), "subresource", admission.Create, nil)) + err := handler.Admit(admission.NewAttributesRecord(nil, nil, api.Kind("kind").WithVersion("version"), "namespace", "name", api.Resource("resource").WithVersion("version"), "subresource", admission.Create, nil)) if err == nil { t.Errorf("Expected error returned from admission handler") } diff --git a/plugin/pkg/admission/exec/admission_test.go b/plugin/pkg/admission/exec/admission_test.go index c85ed8c383..0934491cad 100644 --- a/plugin/pkg/admission/exec/admission_test.go +++ b/plugin/pkg/admission/exec/admission_test.go @@ -125,7 +125,7 @@ func testAdmission(t *testing.T, pod *api.Pod, handler *denyExec, shouldAccept b // pods/exec { req := &rest.ConnectRequest{Name: pod.Name, ResourcePath: "pods/exec"} - err := handler.Admit(admission.NewAttributesRecord(req, api.Kind("Pod").WithVersion("version"), "test", "name", api.Resource("pods").WithVersion("version"), "exec", admission.Connect, nil)) + err := handler.Admit(admission.NewAttributesRecord(req, nil, api.Kind("Pod").WithVersion("version"), "test", "name", api.Resource("pods").WithVersion("version"), "exec", admission.Connect, nil)) if shouldAccept && err != nil { t.Errorf("Unexpected error returned from admission handler: %v", err) } @@ -137,7 +137,7 @@ func testAdmission(t *testing.T, pod *api.Pod, handler *denyExec, shouldAccept b // pods/attach { req := &rest.ConnectRequest{Name: pod.Name, ResourcePath: "pods/attach"} - err := handler.Admit(admission.NewAttributesRecord(req, api.Kind("Pod").WithVersion("version"), "test", "name", api.Resource("pods").WithVersion("version"), "attach", admission.Connect, nil)) + err := handler.Admit(admission.NewAttributesRecord(req, nil, api.Kind("Pod").WithVersion("version"), "test", "name", api.Resource("pods").WithVersion("version"), "attach", admission.Connect, nil)) if shouldAccept && err != nil { t.Errorf("Unexpected error returned from admission handler: %v", err) } diff --git a/plugin/pkg/admission/initialresources/admission_test.go b/plugin/pkg/admission/initialresources/admission_test.go index 21064e3847..3f8736036b 100644 --- a/plugin/pkg/admission/initialresources/admission_test.go +++ b/plugin/pkg/admission/initialresources/admission_test.go @@ -107,7 +107,7 @@ func expectNoAnnotation(t *testing.T, pod *api.Pod) { func admit(t *testing.T, ir admission.Interface, pods []*api.Pod) { for i := range pods { p := pods[i] - if err := ir.Admit(admission.NewAttributesRecord(p, api.Kind("Pod").WithVersion("version"), "test", p.ObjectMeta.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, nil)); err != nil { + if err := ir.Admit(admission.NewAttributesRecord(p, nil, api.Kind("Pod").WithVersion("version"), "test", p.ObjectMeta.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, nil)); err != nil { t.Error(err) } } diff --git a/plugin/pkg/admission/limitranger/admission_test.go b/plugin/pkg/admission/limitranger/admission_test.go index c83edf9d70..8d4e36e924 100644 --- a/plugin/pkg/admission/limitranger/admission_test.go +++ b/plugin/pkg/admission/limitranger/admission_test.go @@ -527,12 +527,12 @@ func TestLimitRangerIgnoresSubresource(t *testing.T) { testPod := validPod("testPod", 1, api.ResourceRequirements{}) indexer.Add(&limitRange) - err := handler.Admit(admission.NewAttributesRecord(&testPod, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "", admission.Update, nil)) + err := handler.Admit(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "", admission.Update, nil)) if err == nil { t.Errorf("Expected an error since the pod did not specify resource limits in its update call") } - err = handler.Admit(admission.NewAttributesRecord(&testPod, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "status", admission.Update, nil)) + err = handler.Admit(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "status", admission.Update, nil)) if err != nil { t.Errorf("Should have ignored calls to any subresource of pod %v", err) } @@ -561,12 +561,12 @@ func TestLimitRangerCacheMisses(t *testing.T) { // add to the lru cache liveLookupCache.Add(limitRange.Namespace, liveLookupEntry{expiry: time.Now().Add(time.Duration(30 * time.Second)), items: []*api.LimitRange{&limitRange}}) - err = handler.Admit(admission.NewAttributesRecord(&testPod, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "", admission.Update, nil)) + err = handler.Admit(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "", admission.Update, nil)) if err == nil { t.Errorf("Expected an error since the pod did not specify resource limits in its update call") } - err = handler.Admit(admission.NewAttributesRecord(&testPod, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "status", admission.Update, nil)) + err = handler.Admit(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "status", admission.Update, nil)) if err != nil { t.Errorf("Should have ignored calls to any subresource of pod %v", err) } @@ -591,12 +591,12 @@ func TestLimitRangerCacheAndLRUMisses(t *testing.T) { testPod := validPod("testPod", 1, api.ResourceRequirements{}) - err = handler.Admit(admission.NewAttributesRecord(&testPod, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "", admission.Update, nil)) + err = handler.Admit(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "", admission.Update, nil)) if err == nil { t.Errorf("Expected an error since the pod did not specify resource limits in its update call") } - err = handler.Admit(admission.NewAttributesRecord(&testPod, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "status", admission.Update, nil)) + err = handler.Admit(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "status", admission.Update, nil)) if err != nil { t.Errorf("Should have ignored calls to any subresource of pod %v", err) } @@ -624,12 +624,12 @@ func TestLimitRangerCacheAndLRUExpiredMisses(t *testing.T) { // add to the lru cache liveLookupCache.Add(limitRange.Namespace, liveLookupEntry{expiry: time.Now().Add(time.Duration(-30 * time.Second)), items: []*api.LimitRange{}}) - err = handler.Admit(admission.NewAttributesRecord(&testPod, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "", admission.Update, nil)) + err = handler.Admit(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "", admission.Update, nil)) if err == nil { t.Errorf("Expected an error since the pod did not specify resource limits in its update call") } - err = handler.Admit(admission.NewAttributesRecord(&testPod, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "status", admission.Update, nil)) + err = handler.Admit(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "status", admission.Update, nil)) if err != nil { t.Errorf("Should have ignored calls to any subresource of pod %v", err) } diff --git a/plugin/pkg/admission/namespace/autoprovision/admission_test.go b/plugin/pkg/admission/namespace/autoprovision/admission_test.go index 55865c4ea1..d0b5852b3a 100644 --- a/plugin/pkg/admission/namespace/autoprovision/admission_test.go +++ b/plugin/pkg/admission/namespace/autoprovision/admission_test.go @@ -43,7 +43,7 @@ func TestAdmission(t *testing.T) { Containers: []api.Container{{Name: "ctr", Image: "image"}}, }, } - err := handler.Admit(admission.NewAttributesRecord(&pod, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, nil)) + err := handler.Admit(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, nil)) if err != nil { t.Errorf("Unexpected error returned from admission handler") } @@ -75,7 +75,7 @@ func TestAdmissionNamespaceExists(t *testing.T) { Containers: []api.Container{{Name: "ctr", Image: "image"}}, }, } - err := handler.Admit(admission.NewAttributesRecord(&pod, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, nil)) + err := handler.Admit(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, nil)) if err != nil { t.Errorf("Unexpected error returned from admission handler") } @@ -96,7 +96,7 @@ func TestIgnoreAdmission(t *testing.T) { Containers: []api.Container{{Name: "ctr", Image: "image"}}, }, } - err := handler.Admit(admission.NewAttributesRecord(&pod, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Update, nil)) + err := handler.Admit(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Update, nil)) if err != nil { t.Errorf("Unexpected error returned from admission handler") } @@ -125,7 +125,7 @@ func TestAdmissionNamespaceExistsUnknownToHandler(t *testing.T) { Containers: []api.Container{{Name: "ctr", Image: "image"}}, }, } - err := handler.Admit(admission.NewAttributesRecord(&pod, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, nil)) + err := handler.Admit(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, nil)) if err != nil { t.Errorf("Unexpected error returned from admission handler") } diff --git a/plugin/pkg/admission/namespace/lifecycle/admission_test.go b/plugin/pkg/admission/namespace/lifecycle/admission_test.go index 9befc1939b..ff26f18bb7 100644 --- a/plugin/pkg/admission/namespace/lifecycle/admission_test.go +++ b/plugin/pkg/admission/namespace/lifecycle/admission_test.go @@ -78,7 +78,7 @@ func TestAdmission(t *testing.T) { Containers: []api.Container{{Name: "ctr", Image: "image"}}, }, } - err := handler.Admit(admission.NewAttributesRecord(&pod, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, nil)) + err := handler.Admit(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, nil)) if err != nil { t.Errorf("Unexpected error returned from admission handler: %v", err) } @@ -90,47 +90,47 @@ func TestAdmission(t *testing.T) { store.Add(namespaceObj) // verify create operations in the namespace cause an error - err = handler.Admit(admission.NewAttributesRecord(&pod, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, nil)) + err = handler.Admit(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, nil)) if err == nil { t.Errorf("Expected error rejecting creates in a namespace when it is terminating") } // verify update operations in the namespace can proceed - err = handler.Admit(admission.NewAttributesRecord(&pod, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Update, nil)) + err = handler.Admit(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Update, nil)) if err != nil { t.Errorf("Unexpected error returned from admission handler: %v", err) } // verify delete operations in the namespace can proceed - err = handler.Admit(admission.NewAttributesRecord(nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Delete, nil)) + err = handler.Admit(admission.NewAttributesRecord(nil, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Delete, nil)) if err != nil { t.Errorf("Unexpected error returned from admission handler: %v", err) } // verify delete of namespace default can never proceed - err = handler.Admit(admission.NewAttributesRecord(nil, api.Kind("Namespace").WithVersion("version"), "", api.NamespaceDefault, api.Resource("namespaces").WithVersion("version"), "", admission.Delete, nil)) + err = handler.Admit(admission.NewAttributesRecord(nil, nil, api.Kind("Namespace").WithVersion("version"), "", api.NamespaceDefault, api.Resource("namespaces").WithVersion("version"), "", admission.Delete, nil)) if err == nil { t.Errorf("Expected an error that this namespace can never be deleted") } // verify delete of namespace other than default can proceed - err = handler.Admit(admission.NewAttributesRecord(nil, api.Kind("Namespace").WithVersion("version"), "", "other", api.Resource("namespaces").WithVersion("version"), "", admission.Delete, nil)) + err = handler.Admit(admission.NewAttributesRecord(nil, nil, api.Kind("Namespace").WithVersion("version"), "", "other", api.Resource("namespaces").WithVersion("version"), "", admission.Delete, nil)) if err != nil { t.Errorf("Did not expect an error %v", err) } // verify create/update/delete of object in non-existent namespace throws error - err = handler.Admit(admission.NewAttributesRecord(&badPod, api.Kind("Pod").WithVersion("version"), badPod.Namespace, badPod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, nil)) + err = handler.Admit(admission.NewAttributesRecord(&badPod, nil, api.Kind("Pod").WithVersion("version"), badPod.Namespace, badPod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, nil)) if err == nil { t.Errorf("Expected, but didn't get, an error (%v) that objects cannot be created in non-existant namespaces", err) } - err = handler.Admit(admission.NewAttributesRecord(&badPod, api.Kind("Pod").WithVersion("version"), badPod.Namespace, badPod.Name, api.Resource("pods").WithVersion("version"), "", admission.Update, nil)) + err = handler.Admit(admission.NewAttributesRecord(&badPod, nil, api.Kind("Pod").WithVersion("version"), badPod.Namespace, badPod.Name, api.Resource("pods").WithVersion("version"), "", admission.Update, nil)) if err == nil { t.Errorf("Expected, but didn't get, an error (%v) that objects cannot be updated in non-existant namespaces", err) } - err = handler.Admit(admission.NewAttributesRecord(&badPod, api.Kind("Pod").WithVersion("version"), badPod.Namespace, badPod.Name, api.Resource("pods").WithVersion("version"), "", admission.Delete, nil)) + err = handler.Admit(admission.NewAttributesRecord(&badPod, nil, api.Kind("Pod").WithVersion("version"), badPod.Namespace, badPod.Name, api.Resource("pods").WithVersion("version"), "", admission.Delete, nil)) if err == nil { t.Errorf("Expected, but didn't get, an error (%v) that objects cannot be deleted in non-existant namespaces", err) } diff --git a/plugin/pkg/admission/persistentvolume/label/admission_test.go b/plugin/pkg/admission/persistentvolume/label/admission_test.go index 369908ca66..a0e938e00d 100644 --- a/plugin/pkg/admission/persistentvolume/label/admission_test.go +++ b/plugin/pkg/admission/persistentvolume/label/admission_test.go @@ -87,20 +87,20 @@ func TestAdmission(t *testing.T) { } // Non-cloud PVs are ignored - err := handler.Admit(admission.NewAttributesRecord(&ignoredPV, api.Kind("PersistentVolume").WithVersion("version"), ignoredPV.Namespace, ignoredPV.Name, api.Resource("persistentvolumes").WithVersion("version"), "", admission.Create, nil)) + err := handler.Admit(admission.NewAttributesRecord(&ignoredPV, nil, api.Kind("PersistentVolume").WithVersion("version"), ignoredPV.Namespace, ignoredPV.Name, api.Resource("persistentvolumes").WithVersion("version"), "", admission.Create, nil)) if err != nil { t.Errorf("Unexpected error returned from admission handler (on ignored pv): %v", err) } // We only add labels on creation - err = handler.Admit(admission.NewAttributesRecord(&awsPV, api.Kind("PersistentVolume").WithVersion("version"), awsPV.Namespace, awsPV.Name, api.Resource("persistentvolumes").WithVersion("version"), "", admission.Delete, nil)) + err = handler.Admit(admission.NewAttributesRecord(&awsPV, nil, api.Kind("PersistentVolume").WithVersion("version"), awsPV.Namespace, awsPV.Name, api.Resource("persistentvolumes").WithVersion("version"), "", admission.Delete, nil)) if err != nil { t.Errorf("Unexpected error returned from admission handler (when deleting aws pv): %v", err) } // Errors from the cloudprovider block creation of the volume pvHandler.ebsVolumes = mockVolumeFailure(fmt.Errorf("invalid volume")) - err = handler.Admit(admission.NewAttributesRecord(&awsPV, api.Kind("PersistentVolume").WithVersion("version"), awsPV.Namespace, awsPV.Name, api.Resource("persistentvolumes").WithVersion("version"), "", admission.Create, nil)) + err = handler.Admit(admission.NewAttributesRecord(&awsPV, nil, api.Kind("PersistentVolume").WithVersion("version"), awsPV.Namespace, awsPV.Name, api.Resource("persistentvolumes").WithVersion("version"), "", admission.Create, nil)) if err == nil { t.Errorf("Expected error when aws pv info fails") } @@ -108,7 +108,7 @@ func TestAdmission(t *testing.T) { // Don't add labels if the cloudprovider doesn't return any labels := make(map[string]string) pvHandler.ebsVolumes = mockVolumeLabels(labels) - err = handler.Admit(admission.NewAttributesRecord(&awsPV, api.Kind("PersistentVolume").WithVersion("version"), awsPV.Namespace, awsPV.Name, api.Resource("persistentvolumes").WithVersion("version"), "", admission.Create, nil)) + err = handler.Admit(admission.NewAttributesRecord(&awsPV, nil, api.Kind("PersistentVolume").WithVersion("version"), awsPV.Namespace, awsPV.Name, api.Resource("persistentvolumes").WithVersion("version"), "", admission.Create, nil)) if err != nil { t.Errorf("Expected no error when creating aws pv") } @@ -118,7 +118,7 @@ func TestAdmission(t *testing.T) { // Don't panic if the cloudprovider returns nil, nil pvHandler.ebsVolumes = mockVolumeFailure(nil) - err = handler.Admit(admission.NewAttributesRecord(&awsPV, api.Kind("PersistentVolume").WithVersion("version"), awsPV.Namespace, awsPV.Name, api.Resource("persistentvolumes").WithVersion("version"), "", admission.Create, nil)) + err = handler.Admit(admission.NewAttributesRecord(&awsPV, nil, api.Kind("PersistentVolume").WithVersion("version"), awsPV.Namespace, awsPV.Name, api.Resource("persistentvolumes").WithVersion("version"), "", admission.Create, nil)) if err != nil { t.Errorf("Expected no error when cloud provider returns empty labels") } @@ -128,7 +128,7 @@ func TestAdmission(t *testing.T) { labels["a"] = "1" labels["b"] = "2" pvHandler.ebsVolumes = mockVolumeLabels(labels) - err = handler.Admit(admission.NewAttributesRecord(&awsPV, api.Kind("PersistentVolume").WithVersion("version"), awsPV.Namespace, awsPV.Name, api.Resource("persistentvolumes").WithVersion("version"), "", admission.Create, nil)) + err = handler.Admit(admission.NewAttributesRecord(&awsPV, nil, api.Kind("PersistentVolume").WithVersion("version"), awsPV.Namespace, awsPV.Name, api.Resource("persistentvolumes").WithVersion("version"), "", admission.Create, nil)) if err != nil { t.Errorf("Expected no error when creating aws pv") } @@ -140,7 +140,7 @@ func TestAdmission(t *testing.T) { awsPV.ObjectMeta.Labels = make(map[string]string) awsPV.ObjectMeta.Labels["a"] = "not1" awsPV.ObjectMeta.Labels["c"] = "3" - err = handler.Admit(admission.NewAttributesRecord(&awsPV, api.Kind("PersistentVolume").WithVersion("version"), awsPV.Namespace, awsPV.Name, api.Resource("persistentvolumes").WithVersion("version"), "", admission.Create, nil)) + err = handler.Admit(admission.NewAttributesRecord(&awsPV, nil, api.Kind("PersistentVolume").WithVersion("version"), awsPV.Namespace, awsPV.Name, api.Resource("persistentvolumes").WithVersion("version"), "", admission.Create, nil)) if err != nil { t.Errorf("Expected no error when creating aws pv") } diff --git a/plugin/pkg/admission/resourcequota/admission_test.go b/plugin/pkg/admission/resourcequota/admission_test.go index 4705e1487b..cfa3c71286 100644 --- a/plugin/pkg/admission/resourcequota/admission_test.go +++ b/plugin/pkg/admission/resourcequota/admission_test.go @@ -123,7 +123,7 @@ func TestAdmissionIgnoresDelete(t *testing.T) { t.Errorf("Unexpected error %v", err) } namespace := "default" - err = handler.Admit(admission.NewAttributesRecord(nil, api.Kind("Pod").WithVersion("version"), namespace, "name", api.Resource("pods").WithVersion("version"), "", admission.Delete, nil)) + err = handler.Admit(admission.NewAttributesRecord(nil, nil, api.Kind("Pod").WithVersion("version"), namespace, "name", api.Resource("pods").WithVersion("version"), "", admission.Delete, nil)) if err != nil { t.Errorf("ResourceQuota should admit all deletes: %v", err) } @@ -156,11 +156,11 @@ func TestAdmissionIgnoresSubresources(t *testing.T) { } indexer.Add(resourceQuota) newPod := validPod("123", 1, getResourceRequirements(getResourceList("100m", "2Gi"), getResourceList("", ""))) - err := handler.Admit(admission.NewAttributesRecord(newPod, api.Kind("Pod").WithVersion("version"), newPod.Namespace, newPod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, nil)) + err := handler.Admit(admission.NewAttributesRecord(newPod, nil, api.Kind("Pod").WithVersion("version"), newPod.Namespace, newPod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, nil)) if err == nil { t.Errorf("Expected an error because the pod exceeded allowed quota") } - err = handler.Admit(admission.NewAttributesRecord(newPod, api.Kind("Pod").WithVersion("version"), newPod.Namespace, newPod.Name, api.Resource("pods").WithVersion("version"), "subresource", admission.Create, nil)) + err = handler.Admit(admission.NewAttributesRecord(newPod, nil, api.Kind("Pod").WithVersion("version"), newPod.Namespace, newPod.Name, api.Resource("pods").WithVersion("version"), "subresource", admission.Create, nil)) if err != nil { t.Errorf("Did not expect an error because the action went to a subresource: %v", err) } @@ -197,7 +197,7 @@ func TestAdmitBelowQuotaLimit(t *testing.T) { } indexer.Add(resourceQuota) newPod := validPod("allowed-pod", 1, getResourceRequirements(getResourceList("100m", "2Gi"), getResourceList("", ""))) - err := handler.Admit(admission.NewAttributesRecord(newPod, api.Kind("Pod").WithVersion("version"), newPod.Namespace, newPod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, nil)) + err := handler.Admit(admission.NewAttributesRecord(newPod, nil, api.Kind("Pod").WithVersion("version"), newPod.Namespace, newPod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, nil)) if err != nil { t.Errorf("Unexpected error: %v", err) } @@ -274,7 +274,7 @@ func TestAdmitExceedQuotaLimit(t *testing.T) { } indexer.Add(resourceQuota) newPod := validPod("not-allowed-pod", 1, getResourceRequirements(getResourceList("3", "2Gi"), getResourceList("", ""))) - err := handler.Admit(admission.NewAttributesRecord(newPod, api.Kind("Pod").WithVersion("version"), newPod.Namespace, newPod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, nil)) + err := handler.Admit(admission.NewAttributesRecord(newPod, nil, api.Kind("Pod").WithVersion("version"), newPod.Namespace, newPod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, nil)) if err == nil { t.Errorf("Expected an error exceeding quota") } @@ -316,13 +316,13 @@ func TestAdmitEnforceQuotaConstraints(t *testing.T) { indexer.Add(resourceQuota) // verify all values are specified as required on the quota newPod := validPod("not-allowed-pod", 1, getResourceRequirements(getResourceList("100m", "2Gi"), getResourceList("200m", ""))) - err := handler.Admit(admission.NewAttributesRecord(newPod, api.Kind("Pod").WithVersion("version"), newPod.Namespace, newPod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, nil)) + err := handler.Admit(admission.NewAttributesRecord(newPod, nil, api.Kind("Pod").WithVersion("version"), newPod.Namespace, newPod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, nil)) if err == nil { t.Errorf("Expected an error because the pod does not specify a memory limit") } // verify the requests and limits are actually valid (in this case, we fail because the limits < requests) newPod = validPod("not-allowed-pod", 1, getResourceRequirements(getResourceList("200m", "2Gi"), getResourceList("100m", "1Gi"))) - err = handler.Admit(admission.NewAttributesRecord(newPod, api.Kind("Pod").WithVersion("version"), newPod.Namespace, newPod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, nil)) + err = handler.Admit(admission.NewAttributesRecord(newPod, nil, api.Kind("Pod").WithVersion("version"), newPod.Namespace, newPod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, nil)) if err == nil { t.Errorf("Expected an error because the pod does not specify a memory limit") } @@ -369,7 +369,7 @@ func TestAdmitPodInNamespaceWithoutQuota(t *testing.T) { newPod := validPod("not-allowed-pod", 1, getResourceRequirements(getResourceList("100m", "2Gi"), getResourceList("200m", ""))) // Add to the lru cache so we do not do a live client lookup liveLookupCache.Add(newPod.Namespace, liveLookupEntry{expiry: time.Now().Add(time.Duration(30 * time.Second)), items: []*api.ResourceQuota{}}) - err = handler.Admit(admission.NewAttributesRecord(newPod, api.Kind("Pod").WithVersion("version"), newPod.Namespace, newPod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, nil)) + err = handler.Admit(admission.NewAttributesRecord(newPod, nil, api.Kind("Pod").WithVersion("version"), newPod.Namespace, newPod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, nil)) if err != nil { t.Errorf("Did not expect an error because the pod is in a different namespace than the quota") } @@ -434,7 +434,7 @@ func TestAdmitBelowTerminatingQuotaLimit(t *testing.T) { newPod := validPod("allowed-pod", 1, getResourceRequirements(getResourceList("100m", "2Gi"), getResourceList("", ""))) activeDeadlineSeconds := int64(30) newPod.Spec.ActiveDeadlineSeconds = &activeDeadlineSeconds - err := handler.Admit(admission.NewAttributesRecord(newPod, api.Kind("Pod").WithVersion("version"), newPod.Namespace, newPod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, nil)) + err := handler.Admit(admission.NewAttributesRecord(newPod, nil, api.Kind("Pod").WithVersion("version"), newPod.Namespace, newPod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, nil)) if err != nil { t.Errorf("Unexpected error: %v", err) } @@ -534,7 +534,7 @@ func TestAdmitBelowBestEffortQuotaLimit(t *testing.T) { // create a pod that is best effort because it does not make a request for anything newPod := validPod("allowed-pod", 1, getResourceRequirements(getResourceList("", ""), getResourceList("", ""))) - err := handler.Admit(admission.NewAttributesRecord(newPod, api.Kind("Pod").WithVersion("version"), newPod.Namespace, newPod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, nil)) + err := handler.Admit(admission.NewAttributesRecord(newPod, nil, api.Kind("Pod").WithVersion("version"), newPod.Namespace, newPod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, nil)) if err != nil { t.Errorf("Unexpected error: %v", err) } @@ -620,7 +620,7 @@ func TestAdmitBestEffortQuotaLimitIgnoresBurstable(t *testing.T) { } indexer.Add(resourceQuota) newPod := validPod("allowed-pod", 1, getResourceRequirements(getResourceList("100m", "1Gi"), getResourceList("", ""))) - err := handler.Admit(admission.NewAttributesRecord(newPod, api.Kind("Pod").WithVersion("version"), newPod.Namespace, newPod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, nil)) + err := handler.Admit(admission.NewAttributesRecord(newPod, nil, api.Kind("Pod").WithVersion("version"), newPod.Namespace, newPod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, nil)) if err != nil { t.Errorf("Unexpected error: %v", err) } @@ -740,7 +740,7 @@ func TestAdmissionSetsMissingNamespace(t *testing.T) { // unset the namespace newPod.ObjectMeta.Namespace = "" - err := handler.Admit(admission.NewAttributesRecord(newPod, api.Kind("Pod").WithVersion("version"), namespace, newPod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, nil)) + err := handler.Admit(admission.NewAttributesRecord(newPod, nil, api.Kind("Pod").WithVersion("version"), namespace, newPod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, nil)) if err != nil { t.Errorf("Got unexpected error: %v", err) } diff --git a/plugin/pkg/admission/security/podsecuritypolicy/admission_test.go b/plugin/pkg/admission/security/podsecuritypolicy/admission_test.go index 04c9bbb190..186bc9f6eb 100644 --- a/plugin/pkg/admission/security/podsecuritypolicy/admission_test.go +++ b/plugin/pkg/admission/security/podsecuritypolicy/admission_test.go @@ -953,7 +953,7 @@ func testPSPAdmit(testCaseName string, psps []*extensions.PodSecurityPolicy, pod plugin := NewTestAdmission(store, tc) - attrs := kadmission.NewAttributesRecord(pod, kapi.Kind("Pod").WithVersion("version"), "namespace", "", kapi.Resource("pods").WithVersion("version"), "", kadmission.Create, &user.DefaultInfo{}) + attrs := kadmission.NewAttributesRecord(pod, nil, kapi.Kind("Pod").WithVersion("version"), "namespace", "", kapi.Resource("pods").WithVersion("version"), "", kadmission.Create, &user.DefaultInfo{}) err := plugin.Admit(attrs) if shouldPass && err != nil { diff --git a/plugin/pkg/admission/securitycontext/scdeny/admission_test.go b/plugin/pkg/admission/securitycontext/scdeny/admission_test.go index aeb6585200..e7c1ee1134 100644 --- a/plugin/pkg/admission/securitycontext/scdeny/admission_test.go +++ b/plugin/pkg/admission/securitycontext/scdeny/admission_test.go @@ -82,7 +82,7 @@ func TestAdmission(t *testing.T) { p.Spec.SecurityContext = tc.podSc p.Spec.Containers[0].SecurityContext = tc.sc - err := handler.Admit(admission.NewAttributesRecord(p, api.Kind("Pod").WithVersion("version"), "foo", "name", api.Resource("pods").WithVersion("version"), "", "ignored", nil)) + err := handler.Admit(admission.NewAttributesRecord(p, nil, api.Kind("Pod").WithVersion("version"), "foo", "name", api.Resource("pods").WithVersion("version"), "", "ignored", nil)) if err != nil && !tc.expectError { t.Errorf("%v: unexpected error: %v", tc.name, err) } else if err == nil && tc.expectError { @@ -96,7 +96,7 @@ func TestAdmission(t *testing.T) { p.Spec.InitContainers = p.Spec.Containers p.Spec.Containers = nil - err = handler.Admit(admission.NewAttributesRecord(p, api.Kind("Pod").WithVersion("version"), "foo", "name", api.Resource("pods").WithVersion("version"), "", "ignored", nil)) + err = handler.Admit(admission.NewAttributesRecord(p, nil, api.Kind("Pod").WithVersion("version"), "foo", "name", api.Resource("pods").WithVersion("version"), "", "ignored", nil)) if err != nil && !tc.expectError { t.Errorf("%v: unexpected error: %v", tc.name, err) } else if err == nil && tc.expectError { @@ -140,7 +140,7 @@ func TestPodSecurityContextAdmission(t *testing.T) { } for _, test := range tests { pod.Spec.SecurityContext = &test.securityContext - err := handler.Admit(admission.NewAttributesRecord(&pod, api.Kind("Pod").WithVersion("version"), "foo", "name", api.Resource("pods").WithVersion("version"), "", "ignored", nil)) + err := handler.Admit(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), "foo", "name", api.Resource("pods").WithVersion("version"), "", "ignored", nil)) if test.errorExpected && err == nil { t.Errorf("Expected error for security context %+v but did not get an error", test.securityContext) diff --git a/plugin/pkg/admission/serviceaccount/admission_test.go b/plugin/pkg/admission/serviceaccount/admission_test.go index 5619e31062..7f78a5329e 100644 --- a/plugin/pkg/admission/serviceaccount/admission_test.go +++ b/plugin/pkg/admission/serviceaccount/admission_test.go @@ -32,7 +32,7 @@ import ( func TestIgnoresNonCreate(t *testing.T) { pod := &api.Pod{} for _, op := range []admission.Operation{admission.Update, admission.Delete, admission.Connect} { - attrs := admission.NewAttributesRecord(pod, api.Kind("Pod").WithVersion("version"), "myns", "myname", api.Resource("pods").WithVersion("version"), "", op, nil) + attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), "myns", "myname", api.Resource("pods").WithVersion("version"), "", op, nil) handler := admission.NewChainHandler(NewServiceAccount(nil)) err := handler.Admit(attrs) if err != nil { @@ -43,7 +43,7 @@ func TestIgnoresNonCreate(t *testing.T) { func TestIgnoresNonPodResource(t *testing.T) { pod := &api.Pod{} - attrs := admission.NewAttributesRecord(pod, api.Kind("Pod").WithVersion("version"), "myns", "myname", api.Resource("CustomResource").WithVersion("version"), "", admission.Create, nil) + attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), "myns", "myname", api.Resource("CustomResource").WithVersion("version"), "", admission.Create, nil) err := NewServiceAccount(nil).Admit(attrs) if err != nil { t.Errorf("Expected non-pod resource allowed, got err: %v", err) @@ -51,7 +51,7 @@ func TestIgnoresNonPodResource(t *testing.T) { } func TestIgnoresNilObject(t *testing.T) { - attrs := admission.NewAttributesRecord(nil, api.Kind("Pod").WithVersion("version"), "myns", "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil) + attrs := admission.NewAttributesRecord(nil, nil, api.Kind("Pod").WithVersion("version"), "myns", "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil) err := NewServiceAccount(nil).Admit(attrs) if err != nil { t.Errorf("Expected nil object allowed allowed, got err: %v", err) @@ -60,7 +60,7 @@ func TestIgnoresNilObject(t *testing.T) { func TestIgnoresNonPodObject(t *testing.T) { obj := &api.Namespace{} - attrs := admission.NewAttributesRecord(obj, api.Kind("Pod").WithVersion("version"), "myns", "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil) + attrs := admission.NewAttributesRecord(obj, nil, api.Kind("Pod").WithVersion("version"), "myns", "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil) err := NewServiceAccount(nil).Admit(attrs) if err != nil { t.Errorf("Expected non pod object allowed, got err: %v", err) @@ -80,7 +80,7 @@ func TestIgnoresMirrorPod(t *testing.T) { }, }, } - attrs := admission.NewAttributesRecord(pod, api.Kind("Pod").WithVersion("version"), "myns", "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil) + attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), "myns", "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil) err := NewServiceAccount(nil).Admit(attrs) if err != nil { t.Errorf("Expected mirror pod without service account or secrets allowed, got err: %v", err) @@ -98,7 +98,7 @@ func TestRejectsMirrorPodWithServiceAccount(t *testing.T) { ServiceAccountName: "default", }, } - attrs := admission.NewAttributesRecord(pod, api.Kind("Pod").WithVersion("version"), "myns", "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil) + attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), "myns", "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil) err := NewServiceAccount(nil).Admit(attrs) if err == nil { t.Errorf("Expected a mirror pod to be prevented from referencing a service account") @@ -118,7 +118,7 @@ func TestRejectsMirrorPodWithSecretVolumes(t *testing.T) { }, }, } - attrs := admission.NewAttributesRecord(pod, api.Kind("Pod").WithVersion("version"), "myns", "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil) + attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), "myns", "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil) err := NewServiceAccount(nil).Admit(attrs) if err == nil { t.Errorf("Expected a mirror pod to be prevented from referencing a secret volume") @@ -141,7 +141,7 @@ func TestAssignsDefaultServiceAccountAndToleratesMissingAPIToken(t *testing.T) { }) pod := &api.Pod{} - attrs := admission.NewAttributesRecord(pod, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil) + attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil) err := admit.Admit(attrs) if err != nil { t.Errorf("Unexpected error: %v", err) @@ -167,7 +167,7 @@ func TestAssignsDefaultServiceAccountAndRejectsMissingAPIToken(t *testing.T) { }) pod := &api.Pod{} - attrs := admission.NewAttributesRecord(pod, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil) + attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil) err := admit.Admit(attrs) if err == nil || !errors.IsServerTimeout(err) { t.Errorf("Expected server timeout error for missing API token: %v", err) @@ -189,7 +189,7 @@ func TestFetchesUncachedServiceAccount(t *testing.T) { admit.RequireAPIToken = false pod := &api.Pod{} - attrs := admission.NewAttributesRecord(pod, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil) + attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil) err := admit.Admit(attrs) if err != nil { t.Errorf("Unexpected error: %v", err) @@ -208,7 +208,7 @@ func TestDeniesInvalidServiceAccount(t *testing.T) { admit := NewServiceAccount(client) pod := &api.Pod{} - attrs := admission.NewAttributesRecord(pod, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil) + attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil) err := admit.Admit(attrs) if err == nil { t.Errorf("Expected error for missing service account, got none") @@ -271,7 +271,7 @@ func TestAutomountsAPIToken(t *testing.T) { }, }, } - attrs := admission.NewAttributesRecord(pod, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil) + attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil) err := admit.Admit(attrs) if err != nil { t.Errorf("Unexpected error: %v", err) @@ -299,7 +299,7 @@ func TestAutomountsAPIToken(t *testing.T) { }, }, } - attrs = admission.NewAttributesRecord(pod, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil) + attrs = admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil) if err := admit.Admit(attrs); err != nil { t.Errorf("Unexpected error: %v", err) } @@ -377,7 +377,7 @@ func TestRespectsExistingMount(t *testing.T) { }, }, } - attrs := admission.NewAttributesRecord(pod, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil) + attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil) err := admit.Admit(attrs) if err != nil { t.Errorf("Unexpected error: %v", err) @@ -407,7 +407,7 @@ func TestRespectsExistingMount(t *testing.T) { }, }, } - attrs = admission.NewAttributesRecord(pod, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil) + attrs = admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil) if err := admit.Admit(attrs); err != nil { t.Errorf("Unexpected error: %v", err) } @@ -450,7 +450,7 @@ func TestAllowsReferencedSecret(t *testing.T) { }, }, } - attrs := admission.NewAttributesRecord(pod1, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil) + attrs := admission.NewAttributesRecord(pod1, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil) if err := admit.Admit(attrs); err != nil { t.Errorf("Unexpected error: %v", err) } @@ -474,7 +474,7 @@ func TestAllowsReferencedSecret(t *testing.T) { }, }, } - attrs = admission.NewAttributesRecord(pod2, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil) + attrs = admission.NewAttributesRecord(pod2, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil) if err := admit.Admit(attrs); err != nil { t.Errorf("Unexpected error: %v", err) } @@ -498,7 +498,7 @@ func TestAllowsReferencedSecret(t *testing.T) { }, }, } - attrs = admission.NewAttributesRecord(pod2, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil) + attrs = admission.NewAttributesRecord(pod2, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil) if err := admit.Admit(attrs); err != nil { t.Errorf("Unexpected error: %v", err) } @@ -526,7 +526,7 @@ func TestRejectsUnreferencedSecretVolumes(t *testing.T) { }, }, } - attrs := admission.NewAttributesRecord(pod1, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil) + attrs := admission.NewAttributesRecord(pod1, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil) if err := admit.Admit(attrs); err == nil { t.Errorf("Expected rejection for using a secret the service account does not reference") } @@ -550,7 +550,7 @@ func TestRejectsUnreferencedSecretVolumes(t *testing.T) { }, }, } - attrs = admission.NewAttributesRecord(pod2, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil) + attrs = admission.NewAttributesRecord(pod2, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil) if err := admit.Admit(attrs); err == nil || !strings.Contains(err.Error(), "with envVar") { t.Errorf("Unexpected error: %v", err) } @@ -574,7 +574,7 @@ func TestRejectsUnreferencedSecretVolumes(t *testing.T) { }, }, } - attrs = admission.NewAttributesRecord(pod2, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil) + attrs = admission.NewAttributesRecord(pod2, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil) if err := admit.Admit(attrs); err == nil || !strings.Contains(err.Error(), "with envVar") { t.Errorf("Unexpected error: %v", err) } @@ -603,7 +603,7 @@ func TestAllowUnreferencedSecretVolumesForPermissiveSAs(t *testing.T) { }, }, } - attrs := admission.NewAttributesRecord(pod, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil) + attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil) err := admit.Admit(attrs) if err == nil { t.Errorf("Expected rejection for using a secret the service account does not reference") @@ -633,7 +633,7 @@ func TestAllowsReferencedImagePullSecrets(t *testing.T) { ImagePullSecrets: []api.LocalObjectReference{{Name: "foo"}}, }, } - attrs := admission.NewAttributesRecord(pod, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil) + attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil) err := admit.Admit(attrs) if err != nil { t.Errorf("Unexpected error: %v", err) @@ -660,7 +660,7 @@ func TestRejectsUnreferencedImagePullSecrets(t *testing.T) { ImagePullSecrets: []api.LocalObjectReference{{Name: "foo"}}, }, } - attrs := admission.NewAttributesRecord(pod, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil) + attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil) err := admit.Admit(attrs) if err == nil { t.Errorf("Expected rejection for using a secret the service account does not reference") @@ -691,7 +691,7 @@ func TestDoNotAddImagePullSecrets(t *testing.T) { ImagePullSecrets: []api.LocalObjectReference{{Name: "foo"}}, }, } - attrs := admission.NewAttributesRecord(pod, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil) + attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil) err := admit.Admit(attrs) if err != nil { t.Errorf("Unexpected error: %v", err) @@ -723,7 +723,7 @@ func TestAddImagePullSecrets(t *testing.T) { admit.serviceAccounts.Add(sa) pod := &api.Pod{} - attrs := admission.NewAttributesRecord(pod, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil) + attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil) err := admit.Admit(attrs) if err != nil { t.Errorf("Unexpected error: %v", err)