Refactoring of update etcd tests.

This commit is contained in:
Wojciech Tyczynski 2015-08-28 15:45:38 +02:00
parent e21bcb8e7a
commit 836be0c432
15 changed files with 592 additions and 686 deletions

View File

@ -39,6 +39,7 @@ type Tester struct {
storage rest.Storage storage rest.Storage
storageError injectErrorFunc storageError injectErrorFunc
clusterScope bool clusterScope bool
createOnUpdate bool
generatesName bool generatesName bool
} }
@ -63,6 +64,11 @@ func (t *Tester) ClusterScope() *Tester {
return t return t
} }
func (t *Tester) AllowCreateOnUpdate() *Tester {
t.createOnUpdate = true
return t
}
func (t *Tester) GeneratesName() *Tester { func (t *Tester) GeneratesName() *Tester {
t.generatesName = true t.generatesName = true
return t return t
@ -94,6 +100,17 @@ func (t *Tester) getObjectMetaOrFail(obj runtime.Object) *api.ObjectMeta {
return meta 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 { func copyOrDie(obj runtime.Object) runtime.Object {
out, err := api.Scheme.Copy(obj) out, err := api.Scheme.Copy(obj)
if err != nil { 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 GetFunc func(api.Context, runtime.Object) (runtime.Object, error)
type SetFunc func(api.Context, runtime.Object) error type SetFunc func(api.Context, runtime.Object) error
type SetRVFunc func(uint64) type SetRVFunc func(uint64)
type UpdateFunc func(runtime.Object) runtime.Object
// Test creating an object. // Test creating an object.
func (t *Tester) TestCreate(valid runtime.Object, setFn SetFunc, getFn GetFunc, invalid ...runtime.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. // Test updating an object.
func (t *Tester) TestUpdate(valid runtime.Object, existing, older runtime.Object) { func (t *Tester) TestUpdate(valid runtime.Object, setFn SetFunc, setRVFn SetRVFunc, getFn GetFunc, updateFn UpdateFunc, invalidUpdateFn ...UpdateFunc) {
t.testUpdateFailsOnNotFound(copyOrDie(valid)) t.testUpdateEquals(copyOrDie(valid), setFn, getFn, updateFn)
t.testUpdateFailsOnVersion(copyOrDie(older)) 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. // Test deleting an object.
@ -177,10 +200,7 @@ func (t *Tester) testCreateAlreadyExisting(obj runtime.Object, setFn SetFunc) {
ctx := t.TestContext() ctx := t.TestContext()
foo := copyOrDie(obj) foo := copyOrDie(obj)
fooMeta := t.getObjectMetaOrFail(foo) t.setObjectMeta(foo, "foo1")
fooMeta.Name = "foo1"
fooMeta.Namespace = api.NamespaceValue(ctx)
fooMeta.GenerateName = ""
if err := setFn(ctx, foo); err != nil { if err := setFn(ctx, foo); err != nil {
t.Errorf("unexpected error: %v", err) t.Errorf("unexpected error: %v", err)
} }
@ -195,10 +215,7 @@ func (t *Tester) testCreateEquals(obj runtime.Object, getFn GetFunc) {
ctx := t.TestContext() ctx := t.TestContext()
foo := copyOrDie(obj) foo := copyOrDie(obj)
fooMeta := t.getObjectMetaOrFail(foo) t.setObjectMeta(foo, "foo2")
fooMeta.Name = "foo2"
fooMeta.Namespace = api.NamespaceValue(ctx)
fooMeta.GenerateName = ""
created, err := t.storage.(rest.Creater).Create(ctx, foo) created, err := t.storage.(rest.Creater).Create(ctx, foo)
if err != nil { if err != nil {
@ -357,21 +374,125 @@ func (t *Tester) testCreateResetsUserData(valid runtime.Object) {
// ============================================================================= // =============================================================================
// Update tests. // Update tests.
func (t *Tester) testUpdateFailsOnNotFound(valid runtime.Object) { func (t *Tester) testUpdateEquals(obj runtime.Object, setFn SetFunc, getFn GetFunc, updateFn UpdateFunc) {
_, _, err := t.storage.(rest.Updater).Update(t.TestContext(), valid) 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) 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")
} else if !errors.IsConflict(err) {
t.Errorf("Expected Conflict error, got '%v'", err)
}
}
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 { if err == nil {
t.Errorf("Expected an error, but we didn't get one") t.Errorf("Expected an error, but we didn't get one")
} else if !errors.IsNotFound(err) { } else if !errors.IsNotFound(err) {
t.Errorf("Expected NotFound error, got '%v'", err) t.Errorf("Expected NotFound error, got '%v'", err)
} }
} }
}
func (t *Tester) testUpdateFailsOnVersion(older runtime.Object) { func (t *Tester) testUpdateRejectsMismatchedNamespace(obj runtime.Object, setFn SetFunc) {
_, _, err := t.storage.(rest.Updater).Update(t.TestContext(), older) 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 { if err == nil {
t.Errorf("Expected an error, but we didn't get one") t.Errorf("expected an error, but didn't get one")
} else if !errors.IsConflict(err) { } else if !strings.Contains(err.Error(), "does not match the namespace sent on the request") {
t.Errorf("Expected Conflict error, got '%v'", err) t.Errorf("expected 'does not match the namespace sent on the request' error, got '%v'", err.Error())
} }
} }
@ -621,9 +742,7 @@ func (t *Tester) testGetDifferentNamespace(obj runtime.Object) {
func (t *Tester) testGetFound(obj runtime.Object) { func (t *Tester) testGetFound(obj runtime.Object) {
ctx := t.TestContext() ctx := t.TestContext()
objMeta := t.getObjectMetaOrFail(obj) t.setObjectMeta(obj, "foo1")
objMeta.Name = "foo1"
objMeta.Namespace = api.NamespaceValue(ctx)
existing, err := t.storage.(rest.Creater).Create(ctx, obj) existing, err := t.storage.(rest.Creater).Create(ctx, obj)
if err != nil { if err != nil {
@ -666,9 +785,7 @@ func (t *Tester) testGetMimatchedNamespace(obj runtime.Object) {
func (t *Tester) testGetNotFound(obj runtime.Object) { func (t *Tester) testGetNotFound(obj runtime.Object) {
ctx := t.TestContext() ctx := t.TestContext()
objMeta := t.getObjectMetaOrFail(obj) t.setObjectMeta(obj, "foo2")
objMeta.Name = "foo2"
objMeta.Namespace = api.NamespaceValue(ctx)
_, err := t.storage.(rest.Creater).Create(ctx, obj) _, err := t.storage.(rest.Creater).Create(ctx, obj)
if err != nil { if err != nil {
t.Errorf("unexpected error: %v", err) t.Errorf("unexpected error: %v", err)
@ -717,13 +834,9 @@ func (t *Tester) testListFound(obj runtime.Object, assignFn AssignFunc) {
ctx := t.TestContext() ctx := t.TestContext()
foo1 := copyOrDie(obj) foo1 := copyOrDie(obj)
foo1Meta := t.getObjectMetaOrFail(foo1) t.setObjectMeta(foo1, "foo1")
foo1Meta.Name = "foo1"
foo1Meta.Namespace = api.NamespaceValue(ctx)
foo2 := copyOrDie(obj) foo2 := copyOrDie(obj)
foo2Meta := t.getObjectMetaOrFail(foo2) t.setObjectMeta(foo2, "foo2")
foo2Meta.Name = "foo2"
foo2Meta.Namespace = api.NamespaceValue(ctx)
existing := assignFn([]runtime.Object{foo1, 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"} testLabels := map[string]string{"key": "value"}
foo1 := copyOrDie(obj) foo1 := copyOrDie(obj)
foo1Meta := t.getObjectMetaOrFail(foo1) t.setObjectMeta(foo1, "foo1")
foo1Meta.Name = "foo1"
foo1Meta.Namespace = api.NamespaceValue(ctx)
foo2 := copyOrDie(obj) foo2 := copyOrDie(obj)
foo2Meta := t.getObjectMetaOrFail(foo2) foo2Meta := t.getObjectMetaOrFail(foo2)
foo2Meta.Name = "foo2" foo2Meta.Name = "foo2"

View File

@ -17,13 +17,11 @@ limitations under the License.
package etcd package etcd
import ( import (
"strconv"
"testing" "testing"
"time" "time"
"github.com/coreos/go-etcd/etcd" "github.com/coreos/go-etcd/etcd"
"k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/rest/resttest" "k8s.io/kubernetes/pkg/api/rest/resttest"
"k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/api/testapi"
"k8s.io/kubernetes/pkg/fields" "k8s.io/kubernetes/pkg/fields"
@ -57,8 +55,15 @@ func createController(storage *REST, rc api.ReplicationController, t *testing.T)
return *newRc, nil return *newRc, nil
} }
var validPodTemplate = api.PodTemplate{ func validNewController() *api.ReplicationController {
Template: api.PodTemplateSpec{ return &api.ReplicationController{
ObjectMeta: api.ObjectMeta{
Name: "foo",
Namespace: api.NamespaceDefault,
},
Spec: api.ReplicationControllerSpec{
Selector: map[string]string{"a": "b"},
Template: &api.PodTemplateSpec{
ObjectMeta: api.ObjectMeta{ ObjectMeta: api.ObjectMeta{
Labels: map[string]string{"a": "b"}, Labels: map[string]string{"a": "b"},
}, },
@ -74,108 +79,80 @@ var validPodTemplate = api.PodTemplate{
DNSPolicy: api.DNSClusterFirst, DNSPolicy: api.DNSClusterFirst,
}, },
}, },
},
}
} }
var validControllerSpec = api.ReplicationControllerSpec{ var validController = *validNewController()
Selector: validPodTemplate.Template.Labels,
Template: &validPodTemplate.Template,
}
var validController = api.ReplicationController{
ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "default"},
Spec: validControllerSpec,
}
func TestCreate(t *testing.T) { func TestCreate(t *testing.T) {
storage, fakeClient := newStorage(t) storage, fakeClient := newStorage(t)
test := resttest.New(t, storage, fakeClient.SetError) test := resttest.New(t, storage, fakeClient.SetError)
controller := validNewController()
controller.ObjectMeta = api.ObjectMeta{}
test.TestCreate( test.TestCreate(
// valid // valid
&api.ReplicationController{ controller,
Spec: api.ReplicationControllerSpec{
Replicas: 2,
Selector: map[string]string{"a": "b"},
Template: &validPodTemplate.Template,
},
},
func(ctx api.Context, obj runtime.Object) error { func(ctx api.Context, obj runtime.Object) error {
return registrytest.SetObject(fakeClient, storage.KeyFunc, ctx, obj) return registrytest.SetObject(fakeClient, storage.KeyFunc, ctx, obj)
}, },
func(ctx api.Context, obj runtime.Object) (runtime.Object, error) { func(ctx api.Context, obj runtime.Object) (runtime.Object, error) {
return registrytest.GetObject(fakeClient, storage.KeyFunc, storage.NewFunc, ctx, obj) return registrytest.GetObject(fakeClient, storage.KeyFunc, storage.NewFunc, ctx, obj)
}, },
// invalid // invalid (invalid selector)
&api.ReplicationController{ &api.ReplicationController{
Spec: api.ReplicationControllerSpec{ Spec: api.ReplicationControllerSpec{
Replicas: 2, Replicas: 2,
Selector: map[string]string{}, Selector: map[string]string{},
Template: &validPodTemplate.Template, Template: validController.Spec.Template,
}, },
}, },
) )
} }
func TestEtcdControllerValidatesUpdate(t *testing.T) { func TestUpdate(t *testing.T) {
ctx := api.NewDefaultContext() storage, fakeClient := newStorage(t)
storage, _ := newStorage(t) test := resttest.New(t, storage, fakeClient.SetError)
test.TestUpdate(
updateController, err := createController(storage, validController, t) // valid
if err != nil { validNewController(),
t.Errorf("Failed to create controller, cannot proceed with test.") func(ctx api.Context, obj runtime.Object) error {
} return registrytest.SetObject(fakeClient, storage.KeyFunc, ctx, obj)
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(rc api.ReplicationController) (runtime.Object, bool, error) { func(resourceVersion uint64) {
rc.Name = "" registrytest.SetResourceVersion(fakeClient, resourceVersion)
return storage.Update(ctx, &rc)
}, },
func(rc api.ReplicationController) (runtime.Object, bool, error) { func(ctx api.Context, obj runtime.Object) (runtime.Object, error) {
rc.Spec.Selector = map[string]string{} return registrytest.GetObject(fakeClient, storage.KeyFunc, storage.NewFunc, ctx, obj)
return storage.Update(ctx, &rc)
}, },
} // updateFunc
for _, u := range updaters { func(obj runtime.Object) runtime.Object {
c, updated, err := u(updateController) object := obj.(*api.ReplicationController)
if c != nil || updated { object.Spec.Replicas = object.Spec.Replicas + 1
t.Errorf("Expected nil object and not created") return object
} },
if !errors.IsInvalid(err) && !errors.IsBadRequest(err) { // invalid updateFunc
t.Errorf("Expected invalid or bad request error, got %v of type %T", err, err) func(obj runtime.Object) runtime.Object {
} object := obj.(*api.ReplicationController)
} object.UID = "newUID"
} return object
},
func TestEtcdControllerValidatesNamespaceOnUpdate(t *testing.T) { func(obj runtime.Object) runtime.Object {
storage, _ := newStorage(t) object := obj.(*api.ReplicationController)
ns := "newnamespace" object.Name = ""
return object
// 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 func(obj runtime.Object) runtime.Object {
// controller is valid. object := obj.(*api.ReplicationController)
updateController, err := createController(storage, validController, t) object.Spec.Selector = map[string]string{}
return object
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")
}
} }
func TestGenerationNumber(t *testing.T) { func TestGenerationNumber(t *testing.T) {
storage, _ := newStorage(t) storage, _ := newStorage(t)
modifiedSno := validController modifiedSno := *validNewController()
modifiedSno.Generation = 100 modifiedSno.Generation = 100
modifiedSno.Status.ObservedGeneration = 10 modifiedSno.Status.ObservedGeneration = 10
ctx := api.NewDefaultContext() ctx := api.NewDefaultContext()
@ -225,17 +202,15 @@ func TestGenerationNumber(t *testing.T) {
func TestEtcdGetController(t *testing.T) { func TestEtcdGetController(t *testing.T) {
storage, fakeClient := newStorage(t) storage, fakeClient := newStorage(t)
test := resttest.New(t, storage, fakeClient.SetError) test := resttest.New(t, storage, fakeClient.SetError)
copy := validController test.TestGet(validNewController())
test.TestGet(&copy)
} }
func TestEtcdListControllers(t *testing.T) { func TestEtcdListControllers(t *testing.T) {
storage, fakeClient := newStorage(t) storage, fakeClient := newStorage(t)
test := resttest.New(t, storage, fakeClient.SetError) test := resttest.New(t, storage, fakeClient.SetError)
key := etcdtest.AddPrefix(storage.KeyRootFunc(test.TestContext())) key := etcdtest.AddPrefix(storage.KeyRootFunc(test.TestContext()))
copy := validController
test.TestList( test.TestList(
&copy, validNewController(),
func(objects []runtime.Object) []runtime.Object { func(objects []runtime.Object) []runtime.Object {
return registrytest.SetObjectsForKey(fakeClient, key, objects) 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) { func TestEtcdDeleteController(t *testing.T) {
ctx := api.NewDefaultContext() ctx := api.NewDefaultContext()
storage, fakeClient := newStorage(t) storage, fakeClient := newStorage(t)
key, _ := storage.KeyFunc(ctx, validController.Name) key, _ := storage.KeyFunc(ctx, validController.Name)
key = etcdtest.AddPrefix(key) 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) obj, err := storage.Delete(ctx, validController.Name, nil)
if err != nil { if err != nil {
t.Errorf("unexpected error: %v", err) t.Errorf("unexpected error: %v", err)
@ -491,7 +441,7 @@ func TestDelete(t *testing.T) {
key = etcdtest.AddPrefix(key) key = etcdtest.AddPrefix(key)
createFn := func() runtime.Object { createFn := func() runtime.Object {
rc := validController rc := *validNewController()
rc.ResourceVersion = "1" rc.ResourceVersion = "1"
fakeClient.Data[key] = tools.EtcdResponseWithError{ fakeClient.Data[key] = tools.EtcdResponseWithError{
R: &etcd.Response{ R: &etcd.Response{

View File

@ -22,7 +22,6 @@ import (
"github.com/coreos/go-etcd/etcd" "github.com/coreos/go-etcd/etcd"
"k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/latest" "k8s.io/kubernetes/pkg/api/latest"
"k8s.io/kubernetes/pkg/api/rest/resttest" "k8s.io/kubernetes/pkg/api/rest/resttest"
"k8s.io/kubernetes/pkg/expapi" "k8s.io/kubernetes/pkg/expapi"
@ -57,8 +56,15 @@ func createController(storage *REST, dc expapi.Daemon, t *testing.T) (expapi.Dae
return *newDc, nil return *newDc, nil
} }
var validPodTemplate = api.PodTemplate{ func validNewDaemon() *expapi.Daemon {
Template: api.PodTemplateSpec{ return &expapi.Daemon{
ObjectMeta: api.ObjectMeta{
Name: "foo",
Namespace: api.NamespaceDefault,
},
Spec: expapi.DaemonSpec{
Selector: map[string]string{"a": "b"},
Template: &api.PodTemplateSpec{
ObjectMeta: api.ObjectMeta{ ObjectMeta: api.ObjectMeta{
Labels: map[string]string{"a": "b"}, Labels: map[string]string{"a": "b"},
}, },
@ -74,211 +80,109 @@ var validPodTemplate = api.PodTemplate{
DNSPolicy: api.DNSClusterFirst, DNSPolicy: api.DNSClusterFirst,
}, },
}, },
},
}
} }
var validControllerSpec = expapi.DaemonSpec{ var validDaemon = *validNewDaemon()
Selector: validPodTemplate.Template.Labels,
Template: &validPodTemplate.Template,
}
var validController = expapi.Daemon{
ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "default"},
Spec: validControllerSpec,
}
func TestCreate(t *testing.T) { func TestCreate(t *testing.T) {
storage, fakeClient := newStorage(t) storage, fakeClient := newStorage(t)
test := resttest.New(t, storage, fakeClient.SetError) test := resttest.New(t, storage, fakeClient.SetError)
controller := validNewDaemon()
controller.ObjectMeta = api.ObjectMeta{}
test.TestCreate( test.TestCreate(
// valid // valid
&expapi.Daemon{ controller,
Spec: expapi.DaemonSpec{
Selector: map[string]string{"a": "b"},
Template: &validPodTemplate.Template,
},
},
func(ctx api.Context, obj runtime.Object) error { func(ctx api.Context, obj runtime.Object) error {
return registrytest.SetObject(fakeClient, storage.KeyFunc, ctx, obj) return registrytest.SetObject(fakeClient, storage.KeyFunc, ctx, obj)
}, },
func(ctx api.Context, obj runtime.Object) (runtime.Object, error) { func(ctx api.Context, obj runtime.Object) (runtime.Object, error) {
return registrytest.GetObject(fakeClient, storage.KeyFunc, storage.NewFunc, ctx, obj) return registrytest.GetObject(fakeClient, storage.KeyFunc, storage.NewFunc, ctx, obj)
}, },
// invalid // invalid (invalid selector)
&expapi.Daemon{ &expapi.Daemon{
Spec: expapi.DaemonSpec{ Spec: expapi.DaemonSpec{
Selector: map[string]string{}, Selector: map[string]string{},
Template: &validPodTemplate.Template, Template: validDaemon.Spec.Template,
}, },
}, },
) )
} }
// makeControllerKey constructs etcd paths to controller items enforcing namespace rules. func TestUpdate(t *testing.T) {
func makeControllerKey(ctx api.Context, id string) (string, error) { storage, fakeClient := newStorage(t)
return etcdgeneric.NamespaceKeyFunc(ctx, daemonPrefix, id) test := resttest.New(t, storage, fakeClient.SetError)
} test.TestUpdate(
// valid
// makeControllerListKey constructs etcd paths to the root of the resource, validNewDaemon(),
// not a specific controller resource func(ctx api.Context, obj runtime.Object) error {
func makeControllerListKey(ctx api.Context) string { return registrytest.SetObject(fakeClient, storage.KeyFunc, ctx, obj)
return etcdgeneric.NamespaceKeyRootFunc(ctx, daemonPrefix) },
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) { func TestEtcdGetController(t *testing.T) {
ctx := api.NewDefaultContext()
storage, fakeClient := newStorage(t) storage, fakeClient := newStorage(t)
key, _ := makeControllerKey(ctx, validController.Name) test := resttest.New(t, storage, fakeClient.SetError)
key = etcdtest.AddPrefix(key) test.TestGet(validNewDaemon())
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)
}
} }
func TestEtcdControllerValidatesUpdate(t *testing.T) { func TestEtcdListControllers(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) {
storage, fakeClient := newStorage(t) storage, fakeClient := newStorage(t)
test := resttest.New(t, storage, fakeClient.SetError)
otherNs := "other" key := etcdtest.AddPrefix(storage.KeyRootFunc(test.TestContext()))
ctx1 := api.NewDefaultContext() test.TestList(
ctx2 := api.WithNamespace(api.NewContext(), otherNs) validNewDaemon(),
func(objects []runtime.Object) []runtime.Object {
key1, _ := makeControllerKey(ctx1, validController.Name) return registrytest.SetObjectsForKey(fakeClient, key, objects)
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,
}, },
E: tools.EtcdErrorNotFound, func(resourceVersion uint64) {
} registrytest.SetResourceVersion(fakeClient, resourceVersion)
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 TestEtcdDeleteController(t *testing.T) { func TestEtcdDeleteController(t *testing.T) {
ctx := api.NewDefaultContext() ctx := api.NewDefaultContext()
storage, fakeClient := newStorage(t) storage, fakeClient := newStorage(t)
key, _ := makeControllerKey(ctx, validController.Name) key, err := storage.KeyFunc(ctx, validDaemon.Name)
key = etcdtest.AddPrefix(key) key = etcdtest.AddPrefix(key)
fakeClient.Set(key, runtime.EncodeOrDie(latest.Codec, &validController), 0) fakeClient.Set(key, runtime.EncodeOrDie(latest.Codec, validNewDaemon()), 0)
obj, err := storage.Delete(ctx, validController.Name, nil) obj, err := storage.Delete(ctx, validDaemon.Name, nil)
if err != nil { if err != nil {
t.Errorf("unexpected error: %v", err) 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) { func TestEtcdWatchController(t *testing.T) {
ctx := api.NewDefaultContext() ctx := api.NewDefaultContext()
storage, fakeClient := newStorage(t) 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. // Tests that we can watch for the creation of daemon controllers with specified labels.
func TestEtcdWatchControllersMatch(t *testing.T) { func TestEtcdWatchControllersMatch(t *testing.T) {
ctx := api.WithNamespace(api.NewDefaultContext(), validController.Namespace) ctx := api.WithNamespace(api.NewDefaultContext(), validDaemon.Namespace)
storage, fakeClient := newStorage(t) storage, fakeClient := newStorage(t)
fakeClient.ExpectNotFoundGet(etcdgeneric.NamespaceKeyRootFunc(ctx, "/registry/pods")) fakeClient.ExpectNotFoundGet(etcdgeneric.NamespaceKeyRootFunc(ctx, "/registry/pods"))
watching, err := storage.Watch(ctx, watching, err := storage.Watch(ctx,
labels.SelectorFromSet(validController.Spec.Selector), labels.SelectorFromSet(validDaemon.Spec.Selector),
fields.Everything(), fields.Everything(),
"1", "1",
) )
@ -434,7 +248,7 @@ func TestEtcdWatchControllersMatch(t *testing.T) {
controller := &expapi.Daemon{ controller := &expapi.Daemon{
ObjectMeta: api.ObjectMeta{ ObjectMeta: api.ObjectMeta{
Name: "foo", Name: "foo",
Labels: validController.Spec.Selector, Labels: validDaemon.Spec.Selector,
Namespace: "default", Namespace: "default",
}, },
} }
@ -458,7 +272,7 @@ func TestEtcdWatchControllersMatch(t *testing.T) {
// Tests that we can watch for daemon controllers with specified fields. // Tests that we can watch for daemon controllers with specified fields.
func TestEtcdWatchControllersFields(t *testing.T) { func TestEtcdWatchControllersFields(t *testing.T) {
ctx := api.WithNamespace(api.NewDefaultContext(), validController.Namespace) ctx := api.WithNamespace(api.NewDefaultContext(), validDaemon.Namespace)
storage, fakeClient := newStorage(t) storage, fakeClient := newStorage(t)
fakeClient.ExpectNotFoundGet(etcdgeneric.NamespaceKeyRootFunc(ctx, "/registry/pods")) fakeClient.ExpectNotFoundGet(etcdgeneric.NamespaceKeyRootFunc(ctx, "/registry/pods"))
@ -480,7 +294,7 @@ func TestEtcdWatchControllersFields(t *testing.T) {
controller := &expapi.Daemon{ controller := &expapi.Daemon{
ObjectMeta: api.ObjectMeta{ ObjectMeta: api.ObjectMeta{
Name: "foo", Name: "foo",
Labels: validController.Spec.Selector, Labels: validDaemon.Spec.Selector,
Namespace: "default", Namespace: "default",
}, },
Status: expapi.DaemonStatus{ Status: expapi.DaemonStatus{
@ -578,21 +392,21 @@ func TestDelete(t *testing.T) {
ctx := api.NewDefaultContext() ctx := api.NewDefaultContext()
storage, fakeClient := newStorage(t) storage, fakeClient := newStorage(t)
test := resttest.New(t, storage, fakeClient.SetError) test := resttest.New(t, storage, fakeClient.SetError)
key, _ := makeControllerKey(ctx, validController.Name) key, _ := storage.KeyFunc(ctx, validDaemon.Name)
key = etcdtest.AddPrefix(key) key = etcdtest.AddPrefix(key)
createFn := func() runtime.Object { createFn := func() runtime.Object {
dc := validController dc := validNewDaemon()
dc.ResourceVersion = "1" dc.ResourceVersion = "1"
fakeClient.Data[key] = tools.EtcdResponseWithError{ fakeClient.Data[key] = tools.EtcdResponseWithError{
R: &etcd.Response{ R: &etcd.Response{
Node: &etcd.Node{ Node: &etcd.Node{
Value: runtime.EncodeOrDie(latest.Codec, &dc), Value: runtime.EncodeOrDie(latest.Codec, dc),
ModifiedIndex: 1, ModifiedIndex: 1,
}, },
}, },
} }
return &dc return dc
} }
gracefulSetFn := func() bool { gracefulSetFn := func() bool {
// If the controller is still around after trying to delete either the delete // If the controller is still around after trying to delete either the delete

View File

@ -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) { func TestDelete(t *testing.T) {
ctx := api.NewDefaultContext() ctx := api.NewDefaultContext()
storage, fakeClient := newStorage(t) 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) { func TestDeleteEndpoints(t *testing.T) {
ctx := api.NewDefaultContext() ctx := api.NewDefaultContext()
storage, fakeClient := newStorage(t) storage, fakeClient := newStorage(t)

View File

@ -78,26 +78,24 @@ func TestCreate(t *testing.T) {
func TestUpdate(t *testing.T) { func TestUpdate(t *testing.T) {
storage, fakeClient := newStorage(t) storage, fakeClient := newStorage(t)
test := resttest.New(t, storage, fakeClient.SetError) 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( test.TestUpdate(
autoscaler, // valid
existing, validNewHorizontalPodAutoscaler("foo"),
older, 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
},
) )
} }

View File

@ -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
},
)
}

View File

@ -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) { func TestDelete(t *testing.T) {
ctx := api.NewContext() ctx := api.NewContext()
storage, fakeClient := newStorage(t) 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) { func TestEtcdDeleteNode(t *testing.T) {
ctx := api.NewContext() ctx := api.NewContext()
storage, fakeClient := newStorage(t) storage, fakeClient := newStorage(t)

View File

@ -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) { func TestDelete(t *testing.T) {
ctx := api.NewContext() ctx := api.NewContext()
storage, _, fakeClient := newStorage(t) 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) { func TestDeletePersistentVolumes(t *testing.T) {
ctx := api.NewContext() ctx := api.NewContext()
storage, _, fakeClient := newStorage(t) storage, _, fakeClient := newStorage(t)

View File

@ -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) { func TestDelete(t *testing.T) {
ctx := api.NewDefaultContext() ctx := api.NewDefaultContext()
storage, _, fakeClient := newStorage(t) storage, _, fakeClient := newStorage(t)

View File

@ -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) { func TestDelete(t *testing.T) {
storage, _, _, fakeClient := newStorage(t) storage, _, _, fakeClient := newStorage(t)
ctx := api.NewDefaultContext() 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) { func TestResourceLocation(t *testing.T) {
expectedIP := "1.2.3.4" expectedIP := "1.2.3.4"
testCases := []struct { 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) { func TestEtcdUpdateNotScheduled(t *testing.T) {
storage, _, _, fakeClient := newStorage(t) storage, _, _, fakeClient := newStorage(t)
ctx := api.NewDefaultContext() ctx := api.NewDefaultContext()

View File

@ -24,7 +24,6 @@ import (
"k8s.io/kubernetes/pkg/registry/registrytest" "k8s.io/kubernetes/pkg/registry/registrytest"
"k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/tools" "k8s.io/kubernetes/pkg/tools"
"k8s.io/kubernetes/pkg/tools/etcdtest"
) )
func newStorage(t *testing.T) (*REST, *tools.FakeEtcdClient) { func newStorage(t *testing.T) (*REST, *tools.FakeEtcdClient) {
@ -83,27 +82,23 @@ func TestCreate(t *testing.T) {
func TestUpdate(t *testing.T) { func TestUpdate(t *testing.T) {
storage, fakeClient := newStorage(t) storage, fakeClient := newStorage(t)
test := resttest.New(t, storage, fakeClient.SetError) 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( test.TestUpdate(
pod, //valid
existing, validNewPodTemplate("foo"),
older, 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
},
) )
} }

View File

@ -24,7 +24,6 @@ import (
"k8s.io/kubernetes/pkg/registry/registrytest" "k8s.io/kubernetes/pkg/registry/registrytest"
"k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/tools" "k8s.io/kubernetes/pkg/tools"
"k8s.io/kubernetes/pkg/tools/etcdtest"
) )
func newStorage(t *testing.T) (*REST, *tools.FakeEtcdClient) { func newStorage(t *testing.T) (*REST, *tools.FakeEtcdClient) {
@ -74,27 +73,23 @@ func TestCreate(t *testing.T) {
func TestUpdate(t *testing.T) { func TestUpdate(t *testing.T) {
storage, fakeClient := newStorage(t) storage, fakeClient := newStorage(t)
test := resttest.New(t, storage, fakeClient.SetError) 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( test.TestUpdate(
secret, // valid
existing, validNewSecret("foo"),
older, 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
},
) )
} }

View File

@ -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
},
)
}

View File

@ -24,7 +24,6 @@ import (
"k8s.io/kubernetes/pkg/registry/registrytest" "k8s.io/kubernetes/pkg/registry/registrytest"
"k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/tools" "k8s.io/kubernetes/pkg/tools"
"k8s.io/kubernetes/pkg/tools/etcdtest"
) )
func newStorage(t *testing.T) (*REST, *tools.FakeEtcdClient) { func newStorage(t *testing.T) (*REST, *tools.FakeEtcdClient) {
@ -67,27 +66,23 @@ func TestCreate(t *testing.T) {
func TestUpdate(t *testing.T) { func TestUpdate(t *testing.T) {
storage, fakeClient := newStorage(t) storage, fakeClient := newStorage(t)
test := resttest.New(t, storage, fakeClient.SetError) 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( test.TestUpdate(
serviceAccount, // valid
existing, validNewServiceAccount("foo"),
older, 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
},
) )
} }

View File

@ -80,26 +80,24 @@ func TestCreate(t *testing.T) {
func TestUpdate(t *testing.T) { func TestUpdate(t *testing.T) {
storage, fakeClient := newStorage(t) storage, fakeClient := newStorage(t)
test := resttest.New(t, storage, fakeClient.SetError) 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( test.TestUpdate(
rsrc, // valid
existing, validNewThirdPartyResource("foo"),
older, 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
},
) )
} }