diff --git a/pkg/api/rest/resttest/resttest.go b/pkg/api/rest/resttest/resttest.go index c09bda70960..5edfe698f39 100644 --- a/pkg/api/rest/resttest/resttest.go +++ b/pkg/api/rest/resttest/resttest.go @@ -36,10 +36,11 @@ import ( type Tester struct { *testing.T - storage rest.Storage - storageError injectErrorFunc - clusterScope bool - generatesName bool + storage rest.Storage + storageError injectErrorFunc + clusterScope bool + createOnUpdate bool + generatesName bool } type injectErrorFunc func(err error) @@ -63,6 +64,11 @@ func (t *Tester) ClusterScope() *Tester { return t } +func (t *Tester) AllowCreateOnUpdate() *Tester { + t.createOnUpdate = true + return t +} + func (t *Tester) GeneratesName() *Tester { t.generatesName = true return t @@ -94,6 +100,17 @@ func (t *Tester) getObjectMetaOrFail(obj runtime.Object) *api.ObjectMeta { return meta } +func (t *Tester) setObjectMeta(obj runtime.Object, name string) { + meta := t.getObjectMetaOrFail(obj) + meta.Name = name + if t.clusterScope { + meta.Namespace = api.NamespaceNone + } else { + meta.Namespace = api.NamespaceValue(t.TestContext()) + } + meta.GenerateName = "" +} + func copyOrDie(obj runtime.Object) runtime.Object { out, err := api.Scheme.Copy(obj) if err != nil { @@ -106,6 +123,7 @@ type AssignFunc func([]runtime.Object) []runtime.Object type GetFunc func(api.Context, runtime.Object) (runtime.Object, error) type SetFunc func(api.Context, runtime.Object) error type SetRVFunc func(uint64) +type UpdateFunc func(runtime.Object) runtime.Object // Test creating an object. func (t *Tester) TestCreate(valid runtime.Object, setFn SetFunc, getFn GetFunc, invalid ...runtime.Object) { @@ -127,9 +145,14 @@ func (t *Tester) TestCreate(valid runtime.Object, setFn SetFunc, getFn GetFunc, } // Test updating an object. -func (t *Tester) TestUpdate(valid runtime.Object, existing, older runtime.Object) { - t.testUpdateFailsOnNotFound(copyOrDie(valid)) - t.testUpdateFailsOnVersion(copyOrDie(older)) +func (t *Tester) TestUpdate(valid runtime.Object, setFn SetFunc, setRVFn SetRVFunc, getFn GetFunc, updateFn UpdateFunc, invalidUpdateFn ...UpdateFunc) { + t.testUpdateEquals(copyOrDie(valid), setFn, getFn, updateFn) + t.testUpdateFailsOnVersionTooOld(copyOrDie(valid), setFn, setRVFn) + t.testUpdateOnNotFound(copyOrDie(valid)) + if !t.clusterScope { + t.testUpdateRejectsMismatchedNamespace(copyOrDie(valid), setFn) + } + t.testUpdateInvokesValidation(copyOrDie(valid), setFn, invalidUpdateFn...) } // Test deleting an object. @@ -177,10 +200,7 @@ func (t *Tester) testCreateAlreadyExisting(obj runtime.Object, setFn SetFunc) { ctx := t.TestContext() foo := copyOrDie(obj) - fooMeta := t.getObjectMetaOrFail(foo) - fooMeta.Name = "foo1" - fooMeta.Namespace = api.NamespaceValue(ctx) - fooMeta.GenerateName = "" + t.setObjectMeta(foo, "foo1") if err := setFn(ctx, foo); err != nil { t.Errorf("unexpected error: %v", err) } @@ -195,10 +215,7 @@ func (t *Tester) testCreateEquals(obj runtime.Object, getFn GetFunc) { ctx := t.TestContext() foo := copyOrDie(obj) - fooMeta := t.getObjectMetaOrFail(foo) - fooMeta.Name = "foo2" - fooMeta.Namespace = api.NamespaceValue(ctx) - fooMeta.GenerateName = "" + t.setObjectMeta(foo, "foo2") created, err := t.storage.(rest.Creater).Create(ctx, foo) if err != nil { @@ -357,16 +374,56 @@ func (t *Tester) testCreateResetsUserData(valid runtime.Object) { // ============================================================================= // Update tests. -func (t *Tester) testUpdateFailsOnNotFound(valid runtime.Object) { - _, _, err := t.storage.(rest.Updater).Update(t.TestContext(), valid) - if err == nil { - t.Errorf("Expected an error, but we didn't get one") - } else if !errors.IsNotFound(err) { - t.Errorf("Expected NotFound error, got '%v'", err) +func (t *Tester) testUpdateEquals(obj runtime.Object, setFn SetFunc, getFn GetFunc, updateFn UpdateFunc) { + ctx := t.TestContext() + + foo := copyOrDie(obj) + t.setObjectMeta(foo, "foo2") + if err := setFn(ctx, foo); err != nil { + t.Errorf("unexpected error: %v", err) + } + + toUpdate, err := getFn(ctx, foo) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + toUpdate = updateFn(toUpdate) + updated, created, err := t.storage.(rest.Updater).Update(ctx, toUpdate) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + if created { + t.Errorf("unexpected creation") + } + got, err := getFn(ctx, foo) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + // Set resource version which might be unset in created object. + updatedMeta := t.getObjectMetaOrFail(updated) + gotMeta := t.getObjectMetaOrFail(got) + updatedMeta.ResourceVersion = gotMeta.ResourceVersion + + if e, a := updated, got; !api.Semantic.DeepEqual(e, a) { + t.Errorf("unexpected obj: %#v, expected %#v", e, a) } } -func (t *Tester) testUpdateFailsOnVersion(older runtime.Object) { +func (t *Tester) testUpdateFailsOnVersionTooOld(obj runtime.Object, setFn SetFunc, setRVFn SetRVFunc) { + ctx := t.TestContext() + + foo := copyOrDie(obj) + t.setObjectMeta(foo, "foo3") + + setRVFn(10) + if err := setFn(ctx, foo); err != nil { + t.Errorf("unexpected error: %v", err) + } + + older := copyOrDie(foo) + olderMeta := t.getObjectMetaOrFail(older) + olderMeta.ResourceVersion = "8" + _, _, err := t.storage.(rest.Updater).Update(t.TestContext(), older) if err == nil { t.Errorf("Expected an error, but we didn't get one") @@ -375,6 +432,70 @@ func (t *Tester) testUpdateFailsOnVersion(older runtime.Object) { } } +func (t *Tester) testUpdateInvokesValidation(obj runtime.Object, setFn SetFunc, invalidUpdateFn ...UpdateFunc) { + ctx := t.TestContext() + + foo := copyOrDie(obj) + t.setObjectMeta(foo, "foo4") + if err := setFn(ctx, foo); err != nil { + t.Errorf("unexpected error: %v", err) + } + + for _, update := range invalidUpdateFn { + toUpdate := update(copyOrDie(foo)) + got, created, err := t.storage.(rest.Updater).Update(t.TestContext(), toUpdate) + if got != nil || created { + t.Errorf("expected nil object and no creation") + } + if !errors.IsInvalid(err) && !errors.IsBadRequest(err) { + t.Errorf("expected invalid or bad request error, got %v", err) + } + } +} + +func (t *Tester) testUpdateOnNotFound(obj runtime.Object) { + t.setObjectMeta(obj, "foo") + _, created, err := t.storage.(rest.Updater).Update(t.TestContext(), obj) + if t.createOnUpdate { + if err != nil { + t.Errorf("creation allowed on updated, but got an error: %v", err) + } + if !created { + t.Errorf("creation allowed on update, but object not created") + } + } else { + if err == nil { + t.Errorf("Expected an error, but we didn't get one") + } else if !errors.IsNotFound(err) { + t.Errorf("Expected NotFound error, got '%v'", err) + } + } +} + +func (t *Tester) testUpdateRejectsMismatchedNamespace(obj runtime.Object, setFn SetFunc) { + ctx := t.TestContext() + + foo := copyOrDie(obj) + t.setObjectMeta(foo, "foo1") + if err := setFn(ctx, foo); err != nil { + t.Errorf("unexpected error: %v", err) + } + + objectMeta := t.getObjectMetaOrFail(obj) + objectMeta.Name = "foo1" + objectMeta.Namespace = "not-default" + + obj, updated, err := t.storage.(rest.Updater).Update(t.TestContext(), obj) + if obj != nil || updated { + t.Errorf("expected nil object and not updated") + } + if err == nil { + t.Errorf("expected an error, but didn't get one") + } else if !strings.Contains(err.Error(), "does not match the namespace sent on the request") { + t.Errorf("expected 'does not match the namespace sent on the request' error, got '%v'", err.Error()) + } +} + // ============================================================================= // Deletion tests. @@ -621,9 +742,7 @@ func (t *Tester) testGetDifferentNamespace(obj runtime.Object) { func (t *Tester) testGetFound(obj runtime.Object) { ctx := t.TestContext() - objMeta := t.getObjectMetaOrFail(obj) - objMeta.Name = "foo1" - objMeta.Namespace = api.NamespaceValue(ctx) + t.setObjectMeta(obj, "foo1") existing, err := t.storage.(rest.Creater).Create(ctx, obj) if err != nil { @@ -666,9 +785,7 @@ func (t *Tester) testGetMimatchedNamespace(obj runtime.Object) { func (t *Tester) testGetNotFound(obj runtime.Object) { ctx := t.TestContext() - objMeta := t.getObjectMetaOrFail(obj) - objMeta.Name = "foo2" - objMeta.Namespace = api.NamespaceValue(ctx) + t.setObjectMeta(obj, "foo2") _, err := t.storage.(rest.Creater).Create(ctx, obj) if err != nil { t.Errorf("unexpected error: %v", err) @@ -717,13 +834,9 @@ func (t *Tester) testListFound(obj runtime.Object, assignFn AssignFunc) { ctx := t.TestContext() foo1 := copyOrDie(obj) - foo1Meta := t.getObjectMetaOrFail(foo1) - foo1Meta.Name = "foo1" - foo1Meta.Namespace = api.NamespaceValue(ctx) + t.setObjectMeta(foo1, "foo1") foo2 := copyOrDie(obj) - foo2Meta := t.getObjectMetaOrFail(foo2) - foo2Meta.Name = "foo2" - foo2Meta.Namespace = api.NamespaceValue(ctx) + t.setObjectMeta(foo2, "foo2") existing := assignFn([]runtime.Object{foo1, foo2}) @@ -748,9 +861,7 @@ func (t *Tester) testListMatchLabels(obj runtime.Object, assignFn AssignFunc) { testLabels := map[string]string{"key": "value"} foo1 := copyOrDie(obj) - foo1Meta := t.getObjectMetaOrFail(foo1) - foo1Meta.Name = "foo1" - foo1Meta.Namespace = api.NamespaceValue(ctx) + t.setObjectMeta(foo1, "foo1") foo2 := copyOrDie(obj) foo2Meta := t.getObjectMetaOrFail(foo2) foo2Meta.Name = "foo2" diff --git a/pkg/registry/controller/etcd/etcd_test.go b/pkg/registry/controller/etcd/etcd_test.go index 408065e1b63..01f2bd8cf2d 100644 --- a/pkg/registry/controller/etcd/etcd_test.go +++ b/pkg/registry/controller/etcd/etcd_test.go @@ -17,13 +17,11 @@ limitations under the License. package etcd import ( - "strconv" "testing" "time" "github.com/coreos/go-etcd/etcd" "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/errors" "k8s.io/kubernetes/pkg/api/rest/resttest" "k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/fields" @@ -57,125 +55,104 @@ func createController(storage *REST, rc api.ReplicationController, t *testing.T) return *newRc, nil } -var validPodTemplate = api.PodTemplate{ - Template: api.PodTemplateSpec{ +func validNewController() *api.ReplicationController { + return &api.ReplicationController{ ObjectMeta: api.ObjectMeta{ - Labels: map[string]string{"a": "b"}, + Name: "foo", + Namespace: api.NamespaceDefault, }, - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Name: "test", - Image: "test_image", - ImagePullPolicy: api.PullIfNotPresent, + Spec: api.ReplicationControllerSpec{ + Selector: map[string]string{"a": "b"}, + Template: &api.PodTemplateSpec{ + ObjectMeta: api.ObjectMeta{ + Labels: map[string]string{"a": "b"}, + }, + Spec: api.PodSpec{ + Containers: []api.Container{ + { + Name: "test", + Image: "test_image", + ImagePullPolicy: api.PullIfNotPresent, + }, + }, + RestartPolicy: api.RestartPolicyAlways, + DNSPolicy: api.DNSClusterFirst, }, }, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, }, - }, + } } -var validControllerSpec = api.ReplicationControllerSpec{ - Selector: validPodTemplate.Template.Labels, - Template: &validPodTemplate.Template, -} - -var validController = api.ReplicationController{ - ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "default"}, - Spec: validControllerSpec, -} +var validController = *validNewController() func TestCreate(t *testing.T) { storage, fakeClient := newStorage(t) test := resttest.New(t, storage, fakeClient.SetError) + controller := validNewController() + controller.ObjectMeta = api.ObjectMeta{} test.TestCreate( // valid - &api.ReplicationController{ - Spec: api.ReplicationControllerSpec{ - Replicas: 2, - Selector: map[string]string{"a": "b"}, - Template: &validPodTemplate.Template, - }, - }, + controller, func(ctx api.Context, obj runtime.Object) error { return registrytest.SetObject(fakeClient, storage.KeyFunc, ctx, obj) }, func(ctx api.Context, obj runtime.Object) (runtime.Object, error) { return registrytest.GetObject(fakeClient, storage.KeyFunc, storage.NewFunc, ctx, obj) }, - // invalid + // invalid (invalid selector) &api.ReplicationController{ Spec: api.ReplicationControllerSpec{ Replicas: 2, Selector: map[string]string{}, - Template: &validPodTemplate.Template, + Template: validController.Spec.Template, }, }, ) } -func TestEtcdControllerValidatesUpdate(t *testing.T) { - ctx := api.NewDefaultContext() - storage, _ := newStorage(t) - - updateController, err := createController(storage, validController, t) - if err != nil { - t.Errorf("Failed to create controller, cannot proceed with test.") - } - - updaters := []func(rc api.ReplicationController) (runtime.Object, bool, error){ - func(rc api.ReplicationController) (runtime.Object, bool, error) { - rc.UID = "newUID" - return storage.Update(ctx, &rc) +func TestUpdate(t *testing.T) { + storage, fakeClient := newStorage(t) + test := resttest.New(t, storage, fakeClient.SetError) + test.TestUpdate( + // valid + validNewController(), + func(ctx api.Context, obj runtime.Object) error { + return registrytest.SetObject(fakeClient, storage.KeyFunc, ctx, obj) }, - func(rc api.ReplicationController) (runtime.Object, bool, error) { - rc.Name = "" - return storage.Update(ctx, &rc) + func(resourceVersion uint64) { + registrytest.SetResourceVersion(fakeClient, resourceVersion) }, - func(rc api.ReplicationController) (runtime.Object, bool, error) { - rc.Spec.Selector = map[string]string{} - return storage.Update(ctx, &rc) + func(ctx api.Context, obj runtime.Object) (runtime.Object, error) { + return registrytest.GetObject(fakeClient, storage.KeyFunc, storage.NewFunc, ctx, obj) }, - } - for _, u := range updaters { - c, updated, err := u(updateController) - if c != nil || updated { - t.Errorf("Expected nil object and not created") - } - if !errors.IsInvalid(err) && !errors.IsBadRequest(err) { - t.Errorf("Expected invalid or bad request error, got %v of type %T", err, err) - } - } -} - -func TestEtcdControllerValidatesNamespaceOnUpdate(t *testing.T) { - storage, _ := newStorage(t) - ns := "newnamespace" - - // The update should fail if the namespace on the controller is set to something - // other than the namespace on the given context, even if the namespace on the - // controller is valid. - updateController, err := createController(storage, validController, t) - - newNamespaceController := validController - newNamespaceController.Namespace = ns - _, err = createController(storage, newNamespaceController, t) - - c, updated, err := storage.Update(api.WithNamespace(api.NewContext(), ns), &updateController) - if c != nil || updated { - t.Errorf("Expected nil object and not created") - } - // TODO: Be more paranoid about the type of error and make sure it has the substring - // "namespace" in it, once #5684 is fixed. Ideally this would be a NewBadRequest. - if err == nil { - t.Errorf("Expected an error, but we didn't get one") - } + // updateFunc + func(obj runtime.Object) runtime.Object { + object := obj.(*api.ReplicationController) + object.Spec.Replicas = object.Spec.Replicas + 1 + return object + }, + // invalid updateFunc + func(obj runtime.Object) runtime.Object { + object := obj.(*api.ReplicationController) + object.UID = "newUID" + return object + }, + func(obj runtime.Object) runtime.Object { + object := obj.(*api.ReplicationController) + object.Name = "" + return object + }, + func(obj runtime.Object) runtime.Object { + object := obj.(*api.ReplicationController) + object.Spec.Selector = map[string]string{} + return object + }, + ) } func TestGenerationNumber(t *testing.T) { storage, _ := newStorage(t) - modifiedSno := validController + modifiedSno := *validNewController() modifiedSno.Generation = 100 modifiedSno.Status.ObservedGeneration = 10 ctx := api.NewDefaultContext() @@ -225,17 +202,15 @@ func TestGenerationNumber(t *testing.T) { func TestEtcdGetController(t *testing.T) { storage, fakeClient := newStorage(t) test := resttest.New(t, storage, fakeClient.SetError) - copy := validController - test.TestGet(©) + test.TestGet(validNewController()) } func TestEtcdListControllers(t *testing.T) { storage, fakeClient := newStorage(t) test := resttest.New(t, storage, fakeClient.SetError) key := etcdtest.AddPrefix(storage.KeyRootFunc(test.TestContext())) - copy := validController test.TestList( - ©, + validNewController(), func(objects []runtime.Object) []runtime.Object { return registrytest.SetObjectsForKey(fakeClient, key, objects) }, @@ -244,38 +219,13 @@ func TestEtcdListControllers(t *testing.T) { }) } -func TestEtcdUpdateController(t *testing.T) { - ctx := api.NewDefaultContext() - storage, fakeClient := newStorage(t) - key, _ := storage.KeyFunc(ctx, validController.Name) - key = etcdtest.AddPrefix(key) - - // set a key, then retrieve the current resource version and try updating it - resp, _ := fakeClient.Set(key, runtime.EncodeOrDie(testapi.Codec(), &validController), 0) - update := validController - update.ResourceVersion = strconv.FormatUint(resp.Node.ModifiedIndex, 10) - update.Spec.Replicas = validController.Spec.Replicas + 1 - _, created, err := storage.Update(ctx, &update) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if created { - t.Errorf("expected an update but created flag was returned") - } - ctrl, err := storage.Get(ctx, validController.Name) - updatedController, _ := ctrl.(*api.ReplicationController) - if updatedController.Spec.Replicas != validController.Spec.Replicas+1 { - t.Errorf("Unexpected controller: %#v", ctrl) - } -} - func TestEtcdDeleteController(t *testing.T) { ctx := api.NewDefaultContext() storage, fakeClient := newStorage(t) key, _ := storage.KeyFunc(ctx, validController.Name) key = etcdtest.AddPrefix(key) - fakeClient.Set(key, runtime.EncodeOrDie(testapi.Codec(), &validController), 0) + fakeClient.Set(key, runtime.EncodeOrDie(testapi.Codec(), validNewController()), 0) obj, err := storage.Delete(ctx, validController.Name, nil) if err != nil { t.Errorf("unexpected error: %v", err) @@ -491,7 +441,7 @@ func TestDelete(t *testing.T) { key = etcdtest.AddPrefix(key) createFn := func() runtime.Object { - rc := validController + rc := *validNewController() rc.ResourceVersion = "1" fakeClient.Data[key] = tools.EtcdResponseWithError{ R: &etcd.Response{ diff --git a/pkg/registry/daemon/etcd/etcd_test.go b/pkg/registry/daemon/etcd/etcd_test.go index 85ae8dec8ac..0eef368f588 100755 --- a/pkg/registry/daemon/etcd/etcd_test.go +++ b/pkg/registry/daemon/etcd/etcd_test.go @@ -22,7 +22,6 @@ import ( "github.com/coreos/go-etcd/etcd" "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/errors" "k8s.io/kubernetes/pkg/api/latest" "k8s.io/kubernetes/pkg/api/rest/resttest" "k8s.io/kubernetes/pkg/expapi" @@ -57,228 +56,133 @@ func createController(storage *REST, dc expapi.Daemon, t *testing.T) (expapi.Dae return *newDc, nil } -var validPodTemplate = api.PodTemplate{ - Template: api.PodTemplateSpec{ +func validNewDaemon() *expapi.Daemon { + return &expapi.Daemon{ ObjectMeta: api.ObjectMeta{ - Labels: map[string]string{"a": "b"}, + Name: "foo", + Namespace: api.NamespaceDefault, }, - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Name: "test", - Image: "test_image", - ImagePullPolicy: api.PullIfNotPresent, + Spec: expapi.DaemonSpec{ + Selector: map[string]string{"a": "b"}, + Template: &api.PodTemplateSpec{ + ObjectMeta: api.ObjectMeta{ + Labels: map[string]string{"a": "b"}, + }, + Spec: api.PodSpec{ + Containers: []api.Container{ + { + Name: "test", + Image: "test_image", + ImagePullPolicy: api.PullIfNotPresent, + }, + }, + RestartPolicy: api.RestartPolicyAlways, + DNSPolicy: api.DNSClusterFirst, }, }, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, }, - }, + } } -var validControllerSpec = expapi.DaemonSpec{ - Selector: validPodTemplate.Template.Labels, - Template: &validPodTemplate.Template, -} - -var validController = expapi.Daemon{ - ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "default"}, - Spec: validControllerSpec, -} +var validDaemon = *validNewDaemon() func TestCreate(t *testing.T) { storage, fakeClient := newStorage(t) test := resttest.New(t, storage, fakeClient.SetError) + controller := validNewDaemon() + controller.ObjectMeta = api.ObjectMeta{} test.TestCreate( // valid - &expapi.Daemon{ - Spec: expapi.DaemonSpec{ - Selector: map[string]string{"a": "b"}, - Template: &validPodTemplate.Template, - }, - }, + controller, func(ctx api.Context, obj runtime.Object) error { return registrytest.SetObject(fakeClient, storage.KeyFunc, ctx, obj) }, func(ctx api.Context, obj runtime.Object) (runtime.Object, error) { return registrytest.GetObject(fakeClient, storage.KeyFunc, storage.NewFunc, ctx, obj) }, - // invalid + // invalid (invalid selector) &expapi.Daemon{ Spec: expapi.DaemonSpec{ Selector: map[string]string{}, - Template: &validPodTemplate.Template, + Template: validDaemon.Spec.Template, }, }, ) } -// makeControllerKey constructs etcd paths to controller items enforcing namespace rules. -func makeControllerKey(ctx api.Context, id string) (string, error) { - return etcdgeneric.NamespaceKeyFunc(ctx, daemonPrefix, id) -} - -// makeControllerListKey constructs etcd paths to the root of the resource, -// not a specific controller resource -func makeControllerListKey(ctx api.Context) string { - return etcdgeneric.NamespaceKeyRootFunc(ctx, daemonPrefix) +func TestUpdate(t *testing.T) { + storage, fakeClient := newStorage(t) + test := resttest.New(t, storage, fakeClient.SetError) + test.TestUpdate( + // valid + validNewDaemon(), + func(ctx api.Context, obj runtime.Object) error { + return registrytest.SetObject(fakeClient, storage.KeyFunc, ctx, obj) + }, + func(resourceVersion uint64) { + registrytest.SetResourceVersion(fakeClient, resourceVersion) + }, + func(ctx api.Context, obj runtime.Object) (runtime.Object, error) { + return registrytest.GetObject(fakeClient, storage.KeyFunc, storage.NewFunc, ctx, obj) + }, + // updateFunc + func(obj runtime.Object) runtime.Object { + object := obj.(*expapi.Daemon) + object.Spec.Template.Spec.NodeSelector = map[string]string{"c": "d"} + return object + }, + // invalid updateFunc + func(obj runtime.Object) runtime.Object { + object := obj.(*expapi.Daemon) + object.UID = "newUID" + return object + }, + func(obj runtime.Object) runtime.Object { + object := obj.(*expapi.Daemon) + object.Name = "" + return object + }, + func(obj runtime.Object) runtime.Object { + object := obj.(*expapi.Daemon) + object.Spec.Template.Spec.RestartPolicy = api.RestartPolicyOnFailure + return object + }, + func(obj runtime.Object) runtime.Object { + object := obj.(*expapi.Daemon) + object.Spec.Selector = map[string]string{} + return object + }, + ) } func TestEtcdGetController(t *testing.T) { - ctx := api.NewDefaultContext() storage, fakeClient := newStorage(t) - key, _ := makeControllerKey(ctx, validController.Name) - key = etcdtest.AddPrefix(key) - fakeClient.Set(key, runtime.EncodeOrDie(latest.Codec, &validController), 0) - ctrl, err := storage.Get(ctx, validController.Name) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - controller, ok := ctrl.(*expapi.Daemon) - if !ok { - t.Errorf("Expected a controller, got %#v", ctrl) - } - if controller.Name != validController.Name { - t.Errorf("Unexpected controller: %#v", controller) - } + test := resttest.New(t, storage, fakeClient.SetError) + test.TestGet(validNewDaemon()) } -func TestEtcdControllerValidatesUpdate(t *testing.T) { - ctx := api.NewDefaultContext() - storage, _ := newStorage(t) - - updateController, err := createController(storage, validController, t) - if err != nil { - t.Errorf("Failed to create controller, cannot proceed with test.") - } - - updaters := []func(dc expapi.Daemon) (runtime.Object, bool, error){ - func(dc expapi.Daemon) (runtime.Object, bool, error) { - dc.UID = "newUID" - return storage.Update(ctx, &dc) - }, - func(dc expapi.Daemon) (runtime.Object, bool, error) { - dc.Name = "" - return storage.Update(ctx, &dc) - }, - func(dc expapi.Daemon) (runtime.Object, bool, error) { - dc.Spec.Template.Spec.RestartPolicy = api.RestartPolicyOnFailure - return storage.Update(ctx, &dc) - }, - func(dc expapi.Daemon) (runtime.Object, bool, error) { - dc.Spec.Selector = map[string]string{} - return storage.Update(ctx, &dc) - }, - } - for _, u := range updaters { - c, updated, err := u(updateController) - if c != nil || updated { - t.Errorf("Expected nil object and not created") - } - if !errors.IsInvalid(err) && !errors.IsBadRequest(err) { - t.Errorf("Expected invalid or bad request error, got %v of type %T", err, err) - } - } -} - -func TestEtcdControllerValidatesNamespaceOnUpdate(t *testing.T) { - storage, _ := newStorage(t) - ns := "newnamespace" - - // The update should fail if the namespace on the controller is set to something - // other than the namespace on the given context, even if the namespace on the - // controller is valid. - updateController, err := createController(storage, validController, t) - - newNamespaceController := validController - newNamespaceController.Namespace = ns - _, err = createController(storage, newNamespaceController, t) - - c, updated, err := storage.Update(api.WithNamespace(api.NewContext(), ns), &updateController) - if c != nil || updated { - t.Errorf("Expected nil object and not created") - } - // TODO: Be more paranoid about the type of error and make sure it has the substring - // "namespace" in it, once #5684 is fixed. Ideally this would be a NewBadRequest. - if err == nil { - t.Errorf("Expected an error, but we didn't get one") - } -} - -// TestEtcdGetControllerDifferentNamespace ensures same-name controllers in different namespaces do not clash -func TestEtcdGetControllerDifferentNamespace(t *testing.T) { +func TestEtcdListControllers(t *testing.T) { storage, fakeClient := newStorage(t) - - otherNs := "other" - ctx1 := api.NewDefaultContext() - ctx2 := api.WithNamespace(api.NewContext(), otherNs) - - key1, _ := makeControllerKey(ctx1, validController.Name) - key2, _ := makeControllerKey(ctx2, validController.Name) - - key1 = etcdtest.AddPrefix(key1) - key2 = etcdtest.AddPrefix(key2) - - fakeClient.Set(key1, runtime.EncodeOrDie(latest.Codec, &validController), 0) - otherNsController := validController - otherNsController.Namespace = otherNs - fakeClient.Set(key2, runtime.EncodeOrDie(latest.Codec, &otherNsController), 0) - - obj, err := storage.Get(ctx1, validController.Name) - ctrl1, _ := obj.(*expapi.Daemon) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if ctrl1.Name != "foo" { - t.Errorf("Unexpected controller: %#v", ctrl1) - } - if ctrl1.Namespace != "default" { - t.Errorf("Unexpected controller: %#v", ctrl1) - } - - obj, err = storage.Get(ctx2, validController.Name) - ctrl2, _ := obj.(*expapi.Daemon) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if ctrl2.Name != "foo" { - t.Errorf("Unexpected controller: %#v", ctrl2) - } - if ctrl2.Namespace != "other" { - t.Errorf("Unexpected controller: %#v", ctrl2) - } - -} - -func TestEtcdGetControllerNotFound(t *testing.T) { - ctx := api.NewDefaultContext() - storage, fakeClient := newStorage(t) - key, _ := makeControllerKey(ctx, validController.Name) - key = etcdtest.AddPrefix(key) - - fakeClient.Data[key] = tools.EtcdResponseWithError{ - R: &etcd.Response{ - Node: nil, + test := resttest.New(t, storage, fakeClient.SetError) + key := etcdtest.AddPrefix(storage.KeyRootFunc(test.TestContext())) + test.TestList( + validNewDaemon(), + func(objects []runtime.Object) []runtime.Object { + return registrytest.SetObjectsForKey(fakeClient, key, objects) }, - E: tools.EtcdErrorNotFound, - } - ctrl, err := storage.Get(ctx, validController.Name) - if ctrl != nil { - t.Errorf("Unexpected non-nil controller: %#v", ctrl) - } - if !errors.IsNotFound(err) { - t.Errorf("Unexpected error returned: %#v", err) - } + func(resourceVersion uint64) { + registrytest.SetResourceVersion(fakeClient, resourceVersion) + }) } func TestEtcdDeleteController(t *testing.T) { ctx := api.NewDefaultContext() storage, fakeClient := newStorage(t) - key, _ := makeControllerKey(ctx, validController.Name) + key, err := storage.KeyFunc(ctx, validDaemon.Name) key = etcdtest.AddPrefix(key) - fakeClient.Set(key, runtime.EncodeOrDie(latest.Codec, &validController), 0) - obj, err := storage.Delete(ctx, validController.Name, nil) + fakeClient.Set(key, runtime.EncodeOrDie(latest.Codec, validNewDaemon()), 0) + obj, err := storage.Delete(ctx, validDaemon.Name, nil) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -295,96 +199,6 @@ func TestEtcdDeleteController(t *testing.T) { } } -func TestEtcdListControllers(t *testing.T) { - storage, fakeClient := newStorage(t) - ctx := api.NewDefaultContext() - key := makeControllerListKey(ctx) - key = etcdtest.AddPrefix(key) - controller := validController - controller.Name = "bar" - fakeClient.Data[key] = tools.EtcdResponseWithError{ - R: &etcd.Response{ - Node: &etcd.Node{ - Nodes: []*etcd.Node{ - { - Value: runtime.EncodeOrDie(latest.Codec, &validController), - }, - { - Value: runtime.EncodeOrDie(latest.Codec, &controller), - }, - }, - }, - }, - E: nil, - } - objList, err := storage.List(ctx, labels.Everything(), fields.Everything()) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - controllers, _ := objList.(*expapi.DaemonList) - if len(controllers.Items) != 2 || controllers.Items[0].Name != validController.Name || controllers.Items[1].Name != controller.Name { - t.Errorf("Unexpected controller list: %#v", controllers) - } -} - -func TestEtcdListControllersNotFound(t *testing.T) { - storage, fakeClient := newStorage(t) - ctx := api.NewDefaultContext() - key := makeControllerListKey(ctx) - key = etcdtest.AddPrefix(key) - - fakeClient.Data[key] = tools.EtcdResponseWithError{ - R: &etcd.Response{}, - E: tools.EtcdErrorNotFound, - } - objList, err := storage.List(ctx, labels.Everything(), fields.Everything()) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - controllers, _ := objList.(*expapi.DaemonList) - if len(controllers.Items) != 0 { - t.Errorf("Unexpected controller list: %#v", controllers) - } -} - -func TestEtcdListControllersLabelsMatch(t *testing.T) { - storage, fakeClient := newStorage(t) - ctx := api.NewDefaultContext() - key := makeControllerListKey(ctx) - key = etcdtest.AddPrefix(key) - - controller := validController - controller.Labels = map[string]string{"k": "v"} - controller.Name = "bar" - - fakeClient.Data[key] = tools.EtcdResponseWithError{ - R: &etcd.Response{ - Node: &etcd.Node{ - Nodes: []*etcd.Node{ - { - Value: runtime.EncodeOrDie(latest.Codec, &validController), - }, - { - Value: runtime.EncodeOrDie(latest.Codec, &controller), - }, - }, - }, - }, - E: nil, - } - testLabels := labels.SelectorFromSet(labels.Set(controller.Labels)) - objList, err := storage.List(ctx, testLabels, fields.Everything()) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - controllers, _ := objList.(*expapi.DaemonList) - if len(controllers.Items) != 1 || controllers.Items[0].Name != controller.Name || - !testLabels.Matches(labels.Set(controllers.Items[0].Labels)) { - t.Errorf("Unexpected controller list: %#v for query with labels %#v", - controllers, testLabels) - } -} - func TestEtcdWatchController(t *testing.T) { ctx := api.NewDefaultContext() storage, fakeClient := newStorage(t) @@ -414,12 +228,12 @@ func TestEtcdWatchController(t *testing.T) { // Tests that we can watch for the creation of daemon controllers with specified labels. func TestEtcdWatchControllersMatch(t *testing.T) { - ctx := api.WithNamespace(api.NewDefaultContext(), validController.Namespace) + ctx := api.WithNamespace(api.NewDefaultContext(), validDaemon.Namespace) storage, fakeClient := newStorage(t) fakeClient.ExpectNotFoundGet(etcdgeneric.NamespaceKeyRootFunc(ctx, "/registry/pods")) watching, err := storage.Watch(ctx, - labels.SelectorFromSet(validController.Spec.Selector), + labels.SelectorFromSet(validDaemon.Spec.Selector), fields.Everything(), "1", ) @@ -434,7 +248,7 @@ func TestEtcdWatchControllersMatch(t *testing.T) { controller := &expapi.Daemon{ ObjectMeta: api.ObjectMeta{ Name: "foo", - Labels: validController.Spec.Selector, + Labels: validDaemon.Spec.Selector, Namespace: "default", }, } @@ -458,7 +272,7 @@ func TestEtcdWatchControllersMatch(t *testing.T) { // Tests that we can watch for daemon controllers with specified fields. func TestEtcdWatchControllersFields(t *testing.T) { - ctx := api.WithNamespace(api.NewDefaultContext(), validController.Namespace) + ctx := api.WithNamespace(api.NewDefaultContext(), validDaemon.Namespace) storage, fakeClient := newStorage(t) fakeClient.ExpectNotFoundGet(etcdgeneric.NamespaceKeyRootFunc(ctx, "/registry/pods")) @@ -480,7 +294,7 @@ func TestEtcdWatchControllersFields(t *testing.T) { controller := &expapi.Daemon{ ObjectMeta: api.ObjectMeta{ Name: "foo", - Labels: validController.Spec.Selector, + Labels: validDaemon.Spec.Selector, Namespace: "default", }, Status: expapi.DaemonStatus{ @@ -578,21 +392,21 @@ func TestDelete(t *testing.T) { ctx := api.NewDefaultContext() storage, fakeClient := newStorage(t) test := resttest.New(t, storage, fakeClient.SetError) - key, _ := makeControllerKey(ctx, validController.Name) + key, _ := storage.KeyFunc(ctx, validDaemon.Name) key = etcdtest.AddPrefix(key) createFn := func() runtime.Object { - dc := validController + dc := validNewDaemon() dc.ResourceVersion = "1" fakeClient.Data[key] = tools.EtcdResponseWithError{ R: &etcd.Response{ Node: &etcd.Node{ - Value: runtime.EncodeOrDie(latest.Codec, &dc), + Value: runtime.EncodeOrDie(latest.Codec, dc), ModifiedIndex: 1, }, }, } - return &dc + return dc } gracefulSetFn := func() bool { // If the controller is still around after trying to delete either the delete diff --git a/pkg/registry/endpoint/etcd/etcd_test.go b/pkg/registry/endpoint/etcd/etcd_test.go index cd446a9c0e1..0212f4914cf 100644 --- a/pkg/registry/endpoint/etcd/etcd_test.go +++ b/pkg/registry/endpoint/etcd/etcd_test.go @@ -80,6 +80,33 @@ func TestCreate(t *testing.T) { ) } +func TestUpdate(t *testing.T) { + storage, fakeClient := newStorage(t) + test := resttest.New(t, storage, fakeClient.SetError).AllowCreateOnUpdate() + test.TestUpdate( + // valid + validNewEndpoints(), + func(ctx api.Context, obj runtime.Object) error { + return registrytest.SetObject(fakeClient, storage.KeyFunc, ctx, obj) + }, + func(resourceVersion uint64) { + registrytest.SetResourceVersion(fakeClient, resourceVersion) + }, + func(ctx api.Context, obj runtime.Object) (runtime.Object, error) { + return registrytest.GetObject(fakeClient, storage.KeyFunc, storage.NewFunc, ctx, obj) + }, + // updateFunc + func(obj runtime.Object) runtime.Object { + object := obj.(*api.Endpoints) + object.Subsets = []api.EndpointSubset{{ + Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}, {IP: "5.6.7.8"}}, + Ports: []api.EndpointPort{{Port: 80, Protocol: "TCP"}}, + }} + return object + }, + ) +} + func TestDelete(t *testing.T) { ctx := api.NewDefaultContext() storage, fakeClient := newStorage(t) @@ -148,36 +175,6 @@ func TestEndpointsDecode(t *testing.T) { } } -func TestEtcdUpdateEndpoints(t *testing.T) { - ctx := api.NewDefaultContext() - storage, fakeClient := newStorage(t) - endpoints := validChangedEndpoints() - - key, _ := storage.KeyFunc(ctx, "foo") - key = etcdtest.AddPrefix(key) - fakeClient.Set(key, runtime.EncodeOrDie(testapi.Codec(), validNewEndpoints()), 0) - - _, _, err := storage.Update(ctx, endpoints) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - - response, err := fakeClient.Get(key, false, false) - if err != nil { - t.Fatalf("Unexpected error %v", err) - } - var endpointsOut api.Endpoints - err = testapi.Codec().DecodeInto([]byte(response.Node.Value), &endpointsOut) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - - endpoints.ObjectMeta.ResourceVersion = endpointsOut.ObjectMeta.ResourceVersion - if !api.Semantic.DeepEqual(endpoints, &endpointsOut) { - t.Errorf("Unexpected endpoints: %#v, expected %#v", &endpointsOut, endpoints) - } -} - func TestDeleteEndpoints(t *testing.T) { ctx := api.NewDefaultContext() storage, fakeClient := newStorage(t) diff --git a/pkg/registry/horizontalpodautoscaler/etcd/etcd_test.go b/pkg/registry/horizontalpodautoscaler/etcd/etcd_test.go index 89f21b06df5..a93ffafaa54 100644 --- a/pkg/registry/horizontalpodautoscaler/etcd/etcd_test.go +++ b/pkg/registry/horizontalpodautoscaler/etcd/etcd_test.go @@ -78,26 +78,24 @@ func TestCreate(t *testing.T) { func TestUpdate(t *testing.T) { storage, fakeClient := newStorage(t) test := resttest.New(t, storage, fakeClient.SetError) - key, err := storage.KeyFunc(test.TestContext(), "foo") - if err != nil { - t.Fatal(err) - } - key = etcdtest.AddPrefix(key) - fakeClient.ExpectNotFoundGet(key) - fakeClient.ChangeIndex = 2 - autoscaler := validNewHorizontalPodAutoscaler("foo") - existing := validNewHorizontalPodAutoscaler("exists") - existing.Namespace = test.TestNamespace() - obj, err := storage.Create(test.TestContext(), existing) - if err != nil { - t.Fatalf("unable to create object: %v", err) - } - older := obj.(*expapi.HorizontalPodAutoscaler) - older.ResourceVersion = "1" test.TestUpdate( - autoscaler, - existing, - older, + // valid + validNewHorizontalPodAutoscaler("foo"), + func(ctx api.Context, obj runtime.Object) error { + return registrytest.SetObject(fakeClient, storage.KeyFunc, ctx, obj) + }, + func(resourceVersion uint64) { + registrytest.SetResourceVersion(fakeClient, resourceVersion) + }, + func(ctx api.Context, obj runtime.Object) (runtime.Object, error) { + return registrytest.GetObject(fakeClient, storage.KeyFunc, storage.NewFunc, ctx, obj) + }, + // updateFunc + func(obj runtime.Object) runtime.Object { + object := obj.(*expapi.HorizontalPodAutoscaler) + object.Spec.MaxCount = object.Spec.MaxCount + 1 + return object + }, ) } diff --git a/pkg/registry/limitrange/etcd/etcd_test.go b/pkg/registry/limitrange/etcd/etcd_test.go index 4ed9e9bf7d1..b861e2b261f 100644 --- a/pkg/registry/limitrange/etcd/etcd_test.go +++ b/pkg/registry/limitrange/etcd/etcd_test.go @@ -76,3 +76,39 @@ func TestCreate(t *testing.T) { }, ) } + +func TestUpdate(t *testing.T) { + storage, fakeClient := newStorage(t) + test := resttest.New(t, storage, fakeClient.SetError).AllowCreateOnUpdate() + test.TestUpdate( + // valid + validNewLimitRange(), + func(ctx api.Context, obj runtime.Object) error { + return registrytest.SetObject(fakeClient, storage.KeyFunc, ctx, obj) + }, + func(resourceVersion uint64) { + registrytest.SetResourceVersion(fakeClient, resourceVersion) + }, + func(ctx api.Context, obj runtime.Object) (runtime.Object, error) { + return registrytest.GetObject(fakeClient, storage.KeyFunc, storage.NewFunc, ctx, obj) + }, + // updateFunc + func(obj runtime.Object) runtime.Object { + object := obj.(*api.LimitRange) + object.Spec.Limits = []api.LimitRangeItem{ + { + Type: api.LimitTypePod, + Max: api.ResourceList{ + api.ResourceCPU: resource.MustParse("1000"), + api.ResourceMemory: resource.MustParse("100000"), + }, + Min: api.ResourceList{ + api.ResourceCPU: resource.MustParse("10"), + api.ResourceMemory: resource.MustParse("1000"), + }, + }, + } + return object + }, + ) +} diff --git a/pkg/registry/minion/etcd/etcd_test.go b/pkg/registry/minion/etcd/etcd_test.go index 3e35ceb8915..435f467d1d7 100644 --- a/pkg/registry/minion/etcd/etcd_test.go +++ b/pkg/registry/minion/etcd/etcd_test.go @@ -95,6 +95,30 @@ func TestCreate(t *testing.T) { ) } +func TestUpdate(t *testing.T) { + storage, fakeClient := newStorage(t) + test := resttest.New(t, storage, fakeClient.SetError).ClusterScope() + test.TestUpdate( + // valid + validNewNode(), + func(ctx api.Context, obj runtime.Object) error { + return registrytest.SetObject(fakeClient, storage.KeyFunc, ctx, obj) + }, + func(resourceVersion uint64) { + registrytest.SetResourceVersion(fakeClient, resourceVersion) + }, + func(ctx api.Context, obj runtime.Object) (runtime.Object, error) { + return registrytest.GetObject(fakeClient, storage.KeyFunc, storage.NewFunc, ctx, obj) + }, + // updateFunc + func(obj runtime.Object) runtime.Object { + object := obj.(*api.Node) + object.Spec.Unschedulable = !object.Spec.Unschedulable + return object + }, + ) +} + func TestDelete(t *testing.T) { ctx := api.NewContext() storage, fakeClient := newStorage(t) @@ -145,36 +169,6 @@ func TestEtcdListNodes(t *testing.T) { }) } -func TestEtcdUpdateEndpoints(t *testing.T) { - ctx := api.NewContext() - storage, fakeClient := newStorage(t) - node := validChangedNode() - - key, _ := storage.KeyFunc(ctx, node.Name) - key = etcdtest.AddPrefix(key) - fakeClient.Set(key, runtime.EncodeOrDie(testapi.Codec(), validNewNode()), 0) - - _, _, err := storage.Update(ctx, node) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - - response, err := fakeClient.Get(key, false, false) - if err != nil { - t.Fatalf("Unexpected error %v", err) - } - var nodeOut api.Node - err = testapi.Codec().DecodeInto([]byte(response.Node.Value), &nodeOut) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - - node.ObjectMeta.ResourceVersion = nodeOut.ObjectMeta.ResourceVersion - if !api.Semantic.DeepEqual(node, &nodeOut) { - t.Errorf("Unexpected node: %#v, expected %#v", &nodeOut, node) - } -} - func TestEtcdDeleteNode(t *testing.T) { ctx := api.NewContext() storage, fakeClient := newStorage(t) diff --git a/pkg/registry/persistentvolume/etcd/etcd_test.go b/pkg/registry/persistentvolume/etcd/etcd_test.go index 1ee996da403..5ed5b5b6ddb 100644 --- a/pkg/registry/persistentvolume/etcd/etcd_test.go +++ b/pkg/registry/persistentvolume/etcd/etcd_test.go @@ -89,6 +89,32 @@ func TestCreate(t *testing.T) { ) } +func TestUpdate(t *testing.T) { + storage, _, fakeClient := newStorage(t) + test := resttest.New(t, storage, fakeClient.SetError).ClusterScope() + test.TestUpdate( + // valid + validNewPersistentVolume("foo"), + func(ctx api.Context, obj runtime.Object) error { + return registrytest.SetObject(fakeClient, storage.KeyFunc, ctx, obj) + }, + func(resourceVersion uint64) { + registrytest.SetResourceVersion(fakeClient, resourceVersion) + }, + func(ctx api.Context, obj runtime.Object) (runtime.Object, error) { + return registrytest.GetObject(fakeClient, storage.KeyFunc, storage.NewFunc, ctx, obj) + }, + // updateFunc + func(obj runtime.Object) runtime.Object { + object := obj.(*api.PersistentVolume) + object.Spec.Capacity = api.ResourceList{ + api.ResourceName(api.ResourceStorage): resource.MustParse("20G"), + } + return object + }, + ) +} + func TestDelete(t *testing.T) { ctx := api.NewContext() storage, _, fakeClient := newStorage(t) @@ -157,36 +183,6 @@ func TestPersistentVolumesDecode(t *testing.T) { } } -func TestEtcdUpdatePersistentVolumes(t *testing.T) { - ctx := api.NewContext() - storage, _, fakeClient := newStorage(t) - persistentVolume := validChangedPersistentVolume() - - key, _ := storage.KeyFunc(ctx, "foo") - key = etcdtest.AddPrefix(key) - fakeClient.Set(key, runtime.EncodeOrDie(testapi.Codec(), validNewPersistentVolume("foo")), 0) - - _, _, err := storage.Update(ctx, persistentVolume) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - - response, err := fakeClient.Get(key, false, false) - if err != nil { - t.Fatalf("Unexpected error %v", err) - } - var persistentVolumeOut api.PersistentVolume - err = testapi.Codec().DecodeInto([]byte(response.Node.Value), &persistentVolumeOut) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - - persistentVolume.ObjectMeta.ResourceVersion = persistentVolumeOut.ObjectMeta.ResourceVersion - if !api.Semantic.DeepEqual(persistentVolume, &persistentVolumeOut) { - t.Errorf("Unexpected persistentVolume: %#v, expected %#v", &persistentVolumeOut, persistentVolume) - } -} - func TestDeletePersistentVolumes(t *testing.T) { ctx := api.NewContext() storage, _, fakeClient := newStorage(t) diff --git a/pkg/registry/persistentvolumeclaim/etcd/etcd_test.go b/pkg/registry/persistentvolumeclaim/etcd/etcd_test.go index e774bb37e43..461671460d4 100644 --- a/pkg/registry/persistentvolumeclaim/etcd/etcd_test.go +++ b/pkg/registry/persistentvolumeclaim/etcd/etcd_test.go @@ -86,6 +86,34 @@ func TestCreate(t *testing.T) { ) } +func TestUpdate(t *testing.T) { + storage, _, fakeClient := newStorage(t) + test := resttest.New(t, storage, fakeClient.SetError) + test.TestUpdate( + // valid + validNewPersistentVolumeClaim("foo", api.NamespaceDefault), + func(ctx api.Context, obj runtime.Object) error { + return registrytest.SetObject(fakeClient, storage.KeyFunc, ctx, obj) + }, + func(resourceVersion uint64) { + registrytest.SetResourceVersion(fakeClient, resourceVersion) + }, + func(ctx api.Context, obj runtime.Object) (runtime.Object, error) { + return registrytest.GetObject(fakeClient, storage.KeyFunc, storage.NewFunc, ctx, obj) + }, + // updateFunc + func(obj runtime.Object) runtime.Object { + object := obj.(*api.PersistentVolumeClaim) + object.Spec.Resources = api.ResourceRequirements{ + Requests: api.ResourceList{ + api.ResourceName(api.ResourceStorage): resource.MustParse("20G"), + }, + } + return object + }, + ) +} + func TestDelete(t *testing.T) { ctx := api.NewDefaultContext() storage, _, fakeClient := newStorage(t) diff --git a/pkg/registry/pod/etcd/etcd_test.go b/pkg/registry/pod/etcd/etcd_test.go index 82f66c60b89..38c05e8c8d2 100644 --- a/pkg/registry/pod/etcd/etcd_test.go +++ b/pkg/registry/pod/etcd/etcd_test.go @@ -112,6 +112,30 @@ func TestCreate(t *testing.T) { ) } +func TestUpdate(t *testing.T) { + storage, _, _, fakeClient := newStorage(t) + test := resttest.New(t, storage, fakeClient.SetError) + test.TestUpdate( + // valid + validNewPod(), + func(ctx api.Context, obj runtime.Object) error { + return registrytest.SetObject(fakeClient, storage.KeyFunc, ctx, obj) + }, + func(resourceVersion uint64) { + registrytest.SetResourceVersion(fakeClient, resourceVersion) + }, + func(ctx api.Context, obj runtime.Object) (runtime.Object, error) { + return registrytest.GetObject(fakeClient, storage.KeyFunc, storage.NewFunc, ctx, obj) + }, + // updateFunc + func(obj runtime.Object) runtime.Object { + object := obj.(*api.Pod) + object.Labels = map[string]string{"a": "b"} + return object + }, + ) +} + func TestDelete(t *testing.T) { storage, _, _, fakeClient := newStorage(t) ctx := api.NewDefaultContext() @@ -199,37 +223,6 @@ func TestPodDecode(t *testing.T) { } } -func TestUpdateWithConflictingNamespace(t *testing.T) { - storage, _, _, fakeClient := newStorage(t) - ctx := api.NewDefaultContext() - key, _ := storage.Etcd.KeyFunc(ctx, "foo") - key = etcdtest.AddPrefix(key) - fakeClient.Data[key] = tools.EtcdResponseWithError{ - R: &etcd.Response{ - Node: &etcd.Node{ - Value: runtime.EncodeOrDie(testapi.Codec(), &api.Pod{ - ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "default"}, - Spec: api.PodSpec{NodeName: "machine"}, - }), - ModifiedIndex: 1, - }, - }, - } - - pod := validChangedPod() - pod.Namespace = "not-default" - - obj, created, err := storage.Update(api.NewDefaultContext(), pod) - if obj != nil || created { - t.Error("Expected a nil channel, but we got a value or created") - } - if err == nil { - t.Errorf("Expected an error, but we didn't get one") - } else if strings.Index(err.Error(), "the namespace of the provided object does not match the namespace sent on the request") == -1 { - t.Errorf("Expected 'Pod.Namespace does not match the provided context' error, got '%v'", err.Error()) - } -} - func TestResourceLocation(t *testing.T) { expectedIP := "1.2.3.4" testCases := []struct { @@ -691,33 +684,6 @@ func TestEtcdCreateBinding(t *testing.T) { } } -func TestEtcdUpdateNotFound(t *testing.T) { - storage, _, _, fakeClient := newStorage(t) - ctx := api.NewDefaultContext() - fakeClient.TestIndex = true - - key, _ := storage.KeyFunc(ctx, "foo") - key = etcdtest.AddPrefix(key) - fakeClient.Data[key] = tools.EtcdResponseWithError{ - R: &etcd.Response{}, - E: tools.EtcdErrorNotFound, - } - - podIn := api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - ResourceVersion: "1", - Labels: map[string]string{ - "foo": "bar", - }, - }, - } - _, _, err := storage.Update(ctx, &podIn) - if err == nil { - t.Errorf("unexpected non-error") - } -} - func TestEtcdUpdateNotScheduled(t *testing.T) { storage, _, _, fakeClient := newStorage(t) ctx := api.NewDefaultContext() diff --git a/pkg/registry/podtemplate/etcd/etcd_test.go b/pkg/registry/podtemplate/etcd/etcd_test.go index 9c2eb05e96f..4b3bb1a1750 100644 --- a/pkg/registry/podtemplate/etcd/etcd_test.go +++ b/pkg/registry/podtemplate/etcd/etcd_test.go @@ -24,7 +24,6 @@ import ( "k8s.io/kubernetes/pkg/registry/registrytest" "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/tools" - "k8s.io/kubernetes/pkg/tools/etcdtest" ) func newStorage(t *testing.T) (*REST, *tools.FakeEtcdClient) { @@ -83,27 +82,23 @@ func TestCreate(t *testing.T) { func TestUpdate(t *testing.T) { storage, fakeClient := newStorage(t) test := resttest.New(t, storage, fakeClient.SetError) - key, err := storage.KeyFunc(test.TestContext(), "foo") - if err != nil { - t.Fatal(err) - } - key = etcdtest.AddPrefix(key) - - fakeClient.ExpectNotFoundGet(key) - fakeClient.ChangeIndex = 2 - pod := validNewPodTemplate("foo") - existing := validNewPodTemplate("exists") - existing.Namespace = test.TestNamespace() - obj, err := storage.Create(test.TestContext(), existing) - if err != nil { - t.Fatalf("unable to create object: %v", err) - } - older := obj.(*api.PodTemplate) - older.ResourceVersion = "1" - test.TestUpdate( - pod, - existing, - older, + //valid + validNewPodTemplate("foo"), + func(ctx api.Context, obj runtime.Object) error { + return registrytest.SetObject(fakeClient, storage.KeyFunc, ctx, obj) + }, + func(resourceVersion uint64) { + registrytest.SetResourceVersion(fakeClient, resourceVersion) + }, + func(ctx api.Context, obj runtime.Object) (runtime.Object, error) { + return registrytest.GetObject(fakeClient, storage.KeyFunc, storage.NewFunc, ctx, obj) + }, + // updateFunc + func(obj runtime.Object) runtime.Object { + object := obj.(*api.PodTemplate) + object.Template.Spec.NodeSelector = map[string]string{"a": "b"} + return object + }, ) } diff --git a/pkg/registry/secret/etcd/etcd_test.go b/pkg/registry/secret/etcd/etcd_test.go index 6325d5feeac..1b538214a89 100644 --- a/pkg/registry/secret/etcd/etcd_test.go +++ b/pkg/registry/secret/etcd/etcd_test.go @@ -24,7 +24,6 @@ import ( "k8s.io/kubernetes/pkg/registry/registrytest" "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/tools" - "k8s.io/kubernetes/pkg/tools/etcdtest" ) func newStorage(t *testing.T) (*REST, *tools.FakeEtcdClient) { @@ -74,27 +73,23 @@ func TestCreate(t *testing.T) { func TestUpdate(t *testing.T) { storage, fakeClient := newStorage(t) test := resttest.New(t, storage, fakeClient.SetError) - key, err := storage.KeyFunc(test.TestContext(), "foo") - if err != nil { - t.Fatal(err) - } - key = etcdtest.AddPrefix(key) - - fakeClient.ExpectNotFoundGet(key) - fakeClient.ChangeIndex = 2 - secret := validNewSecret("foo") - existing := validNewSecret("exists") - existing.Namespace = test.TestNamespace() - obj, err := storage.Create(test.TestContext(), existing) - if err != nil { - t.Fatalf("unable to create object: %v", err) - } - older := obj.(*api.Secret) - older.ResourceVersion = "1" - test.TestUpdate( - secret, - existing, - older, + // valid + validNewSecret("foo"), + func(ctx api.Context, obj runtime.Object) error { + return registrytest.SetObject(fakeClient, storage.KeyFunc, ctx, obj) + }, + func(resourceVersion uint64) { + registrytest.SetResourceVersion(fakeClient, resourceVersion) + }, + func(ctx api.Context, obj runtime.Object) (runtime.Object, error) { + return registrytest.GetObject(fakeClient, storage.KeyFunc, storage.NewFunc, ctx, obj) + }, + // updateFunc + func(obj runtime.Object) runtime.Object { + object := obj.(*api.Secret) + object.Data["othertest"] = []byte("otherdata") + return object + }, ) } diff --git a/pkg/registry/service/etcd/etcd_test.go b/pkg/registry/service/etcd/etcd_test.go index 77cf28d239e..c17b1f8e716 100644 --- a/pkg/registry/service/etcd/etcd_test.go +++ b/pkg/registry/service/etcd/etcd_test.go @@ -86,3 +86,36 @@ func TestCreate(t *testing.T) { }, ) } + +func TestUpdate(t *testing.T) { + storage, fakeClient := newStorage(t) + test := resttest.New(t, storage, fakeClient.SetError).AllowCreateOnUpdate() + test.TestUpdate( + // valid + validService(), + func(ctx api.Context, obj runtime.Object) error { + return registrytest.SetObject(fakeClient, storage.KeyFunc, ctx, obj) + }, + func(resourceVersion uint64) { + registrytest.SetResourceVersion(fakeClient, resourceVersion) + }, + func(ctx api.Context, obj runtime.Object) (runtime.Object, error) { + return registrytest.GetObject(fakeClient, storage.KeyFunc, storage.NewFunc, ctx, obj) + }, + // updateFunc + func(obj runtime.Object) runtime.Object { + object := obj.(*api.Service) + object.Spec = api.ServiceSpec{ + Selector: map[string]string{"bar": "baz2"}, + SessionAffinity: api.ServiceAffinityNone, + Type: api.ServiceTypeClusterIP, + Ports: []api.ServicePort{{ + Port: 6502, + Protocol: api.ProtocolTCP, + TargetPort: util.NewIntOrStringFromInt(6502), + }}, + } + return object + }, + ) +} diff --git a/pkg/registry/serviceaccount/etcd/etcd_test.go b/pkg/registry/serviceaccount/etcd/etcd_test.go index a6a4417509b..8c32147b9b5 100644 --- a/pkg/registry/serviceaccount/etcd/etcd_test.go +++ b/pkg/registry/serviceaccount/etcd/etcd_test.go @@ -24,7 +24,6 @@ import ( "k8s.io/kubernetes/pkg/registry/registrytest" "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/tools" - "k8s.io/kubernetes/pkg/tools/etcdtest" ) func newStorage(t *testing.T) (*REST, *tools.FakeEtcdClient) { @@ -67,27 +66,23 @@ func TestCreate(t *testing.T) { func TestUpdate(t *testing.T) { storage, fakeClient := newStorage(t) test := resttest.New(t, storage, fakeClient.SetError) - key, err := storage.KeyFunc(test.TestContext(), "foo") - if err != nil { - t.Fatal(err) - } - key = etcdtest.AddPrefix(key) - - fakeClient.ExpectNotFoundGet(key) - fakeClient.ChangeIndex = 2 - serviceAccount := validNewServiceAccount("foo") - existing := validNewServiceAccount("exists") - existing.Namespace = test.TestNamespace() - obj, err := storage.Create(test.TestContext(), existing) - if err != nil { - t.Fatalf("unable to create object: %v", err) - } - older := obj.(*api.ServiceAccount) - older.ResourceVersion = "1" - test.TestUpdate( - serviceAccount, - existing, - older, + // valid + validNewServiceAccount("foo"), + func(ctx api.Context, obj runtime.Object) error { + return registrytest.SetObject(fakeClient, storage.KeyFunc, ctx, obj) + }, + func(resourceVersion uint64) { + registrytest.SetResourceVersion(fakeClient, resourceVersion) + }, + func(ctx api.Context, obj runtime.Object) (runtime.Object, error) { + return registrytest.GetObject(fakeClient, storage.KeyFunc, storage.NewFunc, ctx, obj) + }, + // updateFunc + func(obj runtime.Object) runtime.Object { + object := obj.(*api.ServiceAccount) + // TODO: Update this serviceAccount + return object + }, ) } diff --git a/pkg/registry/thirdpartyresource/etcd/etcd_test.go b/pkg/registry/thirdpartyresource/etcd/etcd_test.go index 5962ff031bd..b60bc020fe0 100644 --- a/pkg/registry/thirdpartyresource/etcd/etcd_test.go +++ b/pkg/registry/thirdpartyresource/etcd/etcd_test.go @@ -80,26 +80,24 @@ func TestCreate(t *testing.T) { func TestUpdate(t *testing.T) { storage, fakeClient := newStorage(t) test := resttest.New(t, storage, fakeClient.SetError) - key, err := storage.KeyFunc(test.TestContext(), "foo") - if err != nil { - t.Fatal(err) - } - key = etcdtest.AddPrefix(key) - fakeClient.ExpectNotFoundGet(key) - fakeClient.ChangeIndex = 2 - rsrc := validNewThirdPartyResource("foo") - existing := validNewThirdPartyResource("exists") - existing.Namespace = test.TestNamespace() - obj, err := storage.Create(test.TestContext(), existing) - if err != nil { - t.Fatalf("unable to create object: %v", err) - } - older := obj.(*expapi.ThirdPartyResource) - older.ResourceVersion = "1" test.TestUpdate( - rsrc, - existing, - older, + // valid + validNewThirdPartyResource("foo"), + func(ctx api.Context, obj runtime.Object) error { + return registrytest.SetObject(fakeClient, storage.KeyFunc, ctx, obj) + }, + func(resourceVersion uint64) { + registrytest.SetResourceVersion(fakeClient, resourceVersion) + }, + func(ctx api.Context, obj runtime.Object) (runtime.Object, error) { + return registrytest.GetObject(fakeClient, storage.KeyFunc, storage.NewFunc, ctx, obj) + }, + // updateFunc + func(obj runtime.Object) runtime.Object { + object := obj.(*expapi.ThirdPartyResource) + object.Description = "new description" + return object + }, ) }