Merge pull request #13092 from wojtek-t/refactor_etcd_create_test

Refactoring of create etcd tests.
pull/6/head
Wojciech Tyczynski 2015-08-25 15:07:42 +02:00
commit d14654589a
19 changed files with 373 additions and 474 deletions

View File

@ -36,9 +36,10 @@ import (
type Tester struct {
*testing.T
storage rest.Storage
storageError injectErrorFunc
clusterScope bool
storage rest.Storage
storageError injectErrorFunc
clusterScope bool
generatesName bool
}
type injectErrorFunc func(err error)
@ -62,6 +63,11 @@ func (t *Tester) ClusterScope() *Tester {
return t
}
func (t *Tester) GeneratesName() *Tester {
t.generatesName = true
return t
}
// TestNamespace returns the namespace that will be used when creating contexts.
// Returns NamespaceNone for cluster-scoped objects.
func (t *Tester) TestNamespace() string {
@ -96,14 +102,20 @@ func copyOrDie(obj runtime.Object) runtime.Object {
return out
}
type AssignFunc func(objs []runtime.Object) []runtime.Object
type SetRVFunc func(resourceVersion uint64)
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)
// Test creating an object.
func (t *Tester) TestCreate(valid runtime.Object, invalid ...runtime.Object) {
func (t *Tester) TestCreate(valid runtime.Object, setFn SetFunc, getFn GetFunc, invalid ...runtime.Object) {
t.testCreateHasMetadata(copyOrDie(valid))
t.testCreateGeneratesName(copyOrDie(valid))
t.testCreateGeneratesNameReturnsServerTimeout(copyOrDie(valid))
if !t.generatesName {
t.testCreateGeneratesName(copyOrDie(valid))
t.testCreateGeneratesNameReturnsServerTimeout(copyOrDie(valid))
}
t.testCreateEquals(copyOrDie(valid), getFn)
t.testCreateAlreadyExisting(copyOrDie(valid), setFn)
if t.clusterScope {
t.testCreateDiscardsObjectNamespace(copyOrDie(valid))
t.testCreateIgnoresContextNamespace(copyOrDie(valid))
@ -161,6 +173,53 @@ func (t *Tester) TestList(obj runtime.Object, assignFn AssignFunc, setRVFn SetRV
// =============================================================================
// Creation tests.
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 = ""
if err := setFn(ctx, foo); err != nil {
t.Errorf("unexpected error: %v", err)
}
_, err := t.storage.(rest.Creater).Create(ctx, foo)
if !errors.IsAlreadyExists(err) {
t.Errorf("expected already exists err, got %v", err)
}
}
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 = ""
created, err := t.storage.(rest.Creater).Create(ctx, foo)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
got, err := getFn(ctx, foo)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
// Set resource version which might be unset in created object.
createdMeta := t.getObjectMetaOrFail(created)
gotMeta := t.getObjectMetaOrFail(got)
createdMeta.ResourceVersion = gotMeta.ResourceVersion
if e, a := created, got; !api.Semantic.DeepEqual(e, a) {
t.Errorf("unexpected obj: %#v, expected %#v", e, a)
}
}
func (t *Tester) testCreateDiscardsObjectNamespace(valid runtime.Object) {
objectMeta := t.getObjectMetaOrFail(valid)

View File

@ -18,7 +18,6 @@ package etcd
import (
"strconv"
"strings"
"testing"
"time"
@ -87,102 +86,33 @@ var validController = api.ReplicationController{
Spec: validControllerSpec,
}
func TestEtcdCreateController(t *testing.T) {
ctx := api.NewDefaultContext()
func TestCreate(t *testing.T) {
storage, fakeClient := newStorage(t)
_, err := storage.Create(ctx, &validController)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
key, _ := storage.KeyFunc(ctx, validController.Name)
key = etcdtest.AddPrefix(key)
resp, err := fakeClient.Get(key, false, false)
if err != nil {
t.Fatalf("Unexpected error %v", err)
}
var ctrl api.ReplicationController
err = testapi.Codec().DecodeInto([]byte(resp.Node.Value), &ctrl)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if ctrl.Name != "foo" {
t.Errorf("Unexpected controller: %#v %s", ctrl, resp.Node.Value)
}
}
func TestEtcdCreateControllerAlreadyExisting(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)
_, err := storage.Create(ctx, &validController)
if !errors.IsAlreadyExists(err) {
t.Errorf("expected already exists err, got %#v", err)
}
}
func TestEtcdCreateControllerValidates(t *testing.T) {
ctx := api.NewDefaultContext()
storage, _ := newStorage(t)
emptyName := validController
emptyName.Name = ""
failureCases := []api.ReplicationController{emptyName}
for _, failureCase := range failureCases {
c, err := storage.Create(ctx, &failureCase)
if c != nil {
t.Errorf("Expected nil channel")
}
if !errors.IsInvalid(err) {
t.Errorf("Expected to get an invalid resource error, got %v", err)
}
}
}
func TestCreateControllerWithGeneratedName(t *testing.T) {
storage, _ := newStorage(t)
controller := &api.ReplicationController{
ObjectMeta: api.ObjectMeta{
Namespace: api.NamespaceDefault,
GenerateName: "rc-",
test := resttest.New(t, storage, fakeClient.SetError)
test.TestCreate(
// valid
&api.ReplicationController{
Spec: api.ReplicationControllerSpec{
Replicas: 2,
Selector: map[string]string{"a": "b"},
Template: &validPodTemplate.Template,
},
},
Spec: api.ReplicationControllerSpec{
Replicas: 2,
Selector: map[string]string{"a": "b"},
Template: &validPodTemplate.Template,
func(ctx api.Context, obj runtime.Object) error {
return registrytest.SetObject(fakeClient, storage.KeyFunc, ctx, obj)
},
}
ctx := api.NewDefaultContext()
_, err := storage.Create(ctx, controller)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if controller.Name == "rc-" || !strings.HasPrefix(controller.Name, "rc-") {
t.Errorf("unexpected name: %#v", controller)
}
}
func TestCreateControllerWithConflictingNamespace(t *testing.T) {
storage, _ := newStorage(t)
controller := &api.ReplicationController{
ObjectMeta: api.ObjectMeta{Name: "test", Namespace: "not-default"},
}
ctx := api.NewDefaultContext()
channel, err := storage.Create(ctx, controller)
if channel != nil {
t.Error("Expected a nil channel, but we got a value")
}
errSubString := "namespace"
if err == nil {
t.Errorf("Expected an error, but we didn't get one")
} else if !errors.IsBadRequest(err) ||
strings.Index(err.Error(), errSubString) == -1 {
t.Errorf("Expected a Bad Request error with the sub string '%s', got %v", errSubString, err)
}
func(ctx api.Context, obj runtime.Object) (runtime.Object, error) {
return registrytest.GetObject(fakeClient, storage.KeyFunc, storage.NewFunc, ctx, obj)
},
// invalid
&api.ReplicationController{
Spec: api.ReplicationControllerSpec{
Replicas: 2,
Selector: map[string]string{},
Template: &validPodTemplate.Template,
},
},
)
}
func TestEtcdControllerValidatesUpdate(t *testing.T) {
@ -553,29 +483,6 @@ func TestEtcdWatchControllersNotMatch(t *testing.T) {
}
}
func TestCreate(t *testing.T) {
storage, fakeClient := newStorage(t)
test := resttest.New(t, storage, fakeClient.SetError)
test.TestCreate(
// valid
&api.ReplicationController{
Spec: api.ReplicationControllerSpec{
Replicas: 2,
Selector: map[string]string{"a": "b"},
Template: &validPodTemplate.Template,
},
},
// invalid
&api.ReplicationController{
Spec: api.ReplicationControllerSpec{
Replicas: 2,
Selector: map[string]string{},
Template: &validPodTemplate.Template,
},
},
)
}
func TestDelete(t *testing.T) {
ctx := api.NewDefaultContext()
storage, fakeClient := newStorage(t)

View File

@ -17,7 +17,6 @@ limitations under the License.
package etcd
import (
"strings"
"testing"
"time"
@ -29,8 +28,8 @@ import (
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/labels"
etcdgeneric "k8s.io/kubernetes/pkg/registry/generic/etcd"
"k8s.io/kubernetes/pkg/registry/registrytest"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/storage"
etcdstorage "k8s.io/kubernetes/pkg/storage/etcd"
"k8s.io/kubernetes/pkg/tools"
"k8s.io/kubernetes/pkg/tools/etcdtest"
@ -41,18 +40,9 @@ const (
FAIL
)
func newEtcdStorage(t *testing.T) (*tools.FakeEtcdClient, storage.Interface) {
fakeEtcdClient := tools.NewFakeEtcdClient(t)
fakeEtcdClient.TestIndex = true
helper := etcdstorage.NewEtcdStorage(fakeEtcdClient, latest.Codec, etcdtest.PathPrefix())
return fakeEtcdClient, helper
}
// newStorage creates a REST storage backed by etcd helpers
func newStorage(t *testing.T) (*REST, *tools.FakeEtcdClient) {
fakeEtcdClient, h := newEtcdStorage(t)
storage := NewREST(h)
return storage, fakeEtcdClient
etcdStorage, fakeClient := registrytest.NewEtcdStorage(t)
return NewREST(etcdStorage), fakeClient
}
// createController is a helper function that returns a controller with the updated resource version.
@ -95,6 +85,33 @@ var validController = api.Daemon{
Spec: validControllerSpec,
}
func TestCreate(t *testing.T) {
storage, fakeClient := newStorage(t)
test := resttest.New(t, storage, fakeClient.SetError)
test.TestCreate(
// valid
&api.Daemon{
Spec: api.DaemonSpec{
Selector: map[string]string{"a": "b"},
Template: &validPodTemplate.Template,
},
},
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
&api.Daemon{
Spec: api.DaemonSpec{
Selector: map[string]string{},
Template: &validPodTemplate.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)
@ -106,103 +123,6 @@ func makeControllerListKey(ctx api.Context) string {
return etcdgeneric.NamespaceKeyRootFunc(ctx, daemonPrefix)
}
func TestEtcdCreateController(t *testing.T) {
ctx := api.NewDefaultContext()
storage, fakeClient := newStorage(t)
_, err := storage.Create(ctx, &validController)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
key, _ := makeControllerKey(ctx, validController.Name)
key = etcdtest.AddPrefix(key)
resp, err := fakeClient.Get(key, false, false)
if err != nil {
t.Fatalf("Unexpected error %v", err)
}
var ctrl api.Daemon
err = latest.Codec.DecodeInto([]byte(resp.Node.Value), &ctrl)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if ctrl.Name != "foo" {
t.Errorf("Unexpected controller: %#v %s", ctrl, resp.Node.Value)
}
}
func TestEtcdCreateControllerAlreadyExisting(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)
_, err := storage.Create(ctx, &validController)
if !errors.IsAlreadyExists(err) {
t.Errorf("expected already exists err, got %#v", err)
}
}
func TestEtcdCreateControllerValidates(t *testing.T) {
ctx := api.NewDefaultContext()
storage, _ := newStorage(t)
emptyName := validController
emptyName.Name = ""
failureCases := []api.Daemon{emptyName}
for _, failureCase := range failureCases {
c, err := storage.Create(ctx, &failureCase)
if c != nil {
t.Errorf("Expected nil channel")
}
if !errors.IsInvalid(err) {
t.Errorf("Expected to get an invalid resource error, got %v", err)
}
}
}
func TestCreateControllerWithGeneratedName(t *testing.T) {
storage, _ := newStorage(t)
controller := &api.Daemon{
ObjectMeta: api.ObjectMeta{
Namespace: api.NamespaceDefault,
GenerateName: "daemon-",
},
Spec: api.DaemonSpec{
Selector: map[string]string{"a": "b"},
Template: &validPodTemplate.Template,
},
}
ctx := api.NewDefaultContext()
_, err := storage.Create(ctx, controller)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if controller.Name == "daemon-" || !strings.HasPrefix(controller.Name, "daemon-") {
t.Errorf("unexpected name: %#v", controller)
}
}
func TestCreateControllerWithConflictingNamespace(t *testing.T) {
storage, _ := newStorage(t)
controller := &api.Daemon{
ObjectMeta: api.ObjectMeta{Name: "test", Namespace: "not-default"},
}
ctx := api.NewDefaultContext()
channel, err := storage.Create(ctx, controller)
if channel != nil {
t.Error("Expected a nil channel, but we got a value")
}
errSubString := "namespace"
if err == nil {
t.Errorf("Expected an error, but we didn't get one")
} else if !errors.IsBadRequest(err) ||
strings.Index(err.Error(), errSubString) == -1 {
t.Errorf("Expected a Bad Request error with the sub string '%s', got %v", errSubString, err)
}
}
func TestEtcdGetController(t *testing.T) {
ctx := api.NewDefaultContext()
storage, fakeClient := newStorage(t)
@ -653,27 +573,6 @@ func TestEtcdWatchControllersNotMatch(t *testing.T) {
}
}
func TestCreate(t *testing.T) {
storage, fakeClient := newStorage(t)
test := resttest.New(t, storage, fakeClient.SetError)
test.TestCreate(
// valid
&api.Daemon{
Spec: api.DaemonSpec{
Selector: map[string]string{"a": "b"},
Template: &validPodTemplate.Template,
},
},
// invalid
&api.Daemon{
Spec: api.DaemonSpec{
Selector: map[string]string{},
Template: &validPodTemplate.Template,
},
},
)
}
func TestDelete(t *testing.T) {
ctx := api.NewDefaultContext()
storage, fakeClient := newStorage(t)

View File

@ -67,6 +67,12 @@ func TestCreate(t *testing.T) {
test.TestCreate(
// valid
endpoints,
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
&api.Endpoints{
ObjectMeta: api.ObjectMeta{Name: "_-a123-a_"},

View File

@ -64,6 +64,12 @@ func TestCreate(t *testing.T) {
test.TestCreate(
// valid
autoscaler,
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
&expapi.HorizontalPodAutoscaler{},
)

View File

@ -17,21 +17,14 @@ limitations under the License.
package etcd
import (
"reflect"
"testing"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/resource"
"k8s.io/kubernetes/pkg/api/testapi"
etcdgeneric "k8s.io/kubernetes/pkg/registry/generic/etcd"
"k8s.io/kubernetes/pkg/api/rest/resttest"
"k8s.io/kubernetes/pkg/registry/registrytest"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/tools"
"k8s.io/kubernetes/pkg/tools/etcdtest"
"k8s.io/kubernetes/pkg/util"
"github.com/coreos/go-etcd/etcd"
)
func newStorage(t *testing.T) (*REST, *tools.FakeEtcdClient) {
@ -39,11 +32,11 @@ func newStorage(t *testing.T) (*REST, *tools.FakeEtcdClient) {
return NewREST(etcdStorage), fakeClient
}
func TestLimitRangeCreate(t *testing.T) {
limitRange := &api.LimitRange{
func validNewLimitRange() *api.LimitRange {
return &api.LimitRange{
ObjectMeta: api.ObjectMeta{
Name: "foo",
Namespace: "default",
Namespace: api.NamespaceDefault,
},
Spec: api.LimitRangeSpec{
Limits: []api.LimitRangeItem{
@ -61,72 +54,25 @@ func TestLimitRangeCreate(t *testing.T) {
},
},
}
nodeWithLimitRange := tools.EtcdResponseWithError{
R: &etcd.Response{
Node: &etcd.Node{
Value: runtime.EncodeOrDie(testapi.Codec(), limitRange),
ModifiedIndex: 1,
CreatedIndex: 1,
},
},
E: nil,
}
emptyNode := tools.EtcdResponseWithError{
R: &etcd.Response{},
E: tools.EtcdErrorNotFound,
}
ctx := api.NewDefaultContext()
key := "foo"
prefix := etcdtest.AddPrefix("limitranges")
path, err := etcdgeneric.NamespaceKeyFunc(ctx, prefix, key)
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
table := map[string]struct {
existing tools.EtcdResponseWithError
expect tools.EtcdResponseWithError
toCreate runtime.Object
errOK func(error) bool
}{
"normal": {
existing: emptyNode,
expect: nodeWithLimitRange,
toCreate: limitRange,
errOK: func(err error) bool { return err == nil },
},
"preExisting": {
existing: nodeWithLimitRange,
expect: nodeWithLimitRange,
toCreate: limitRange,
errOK: errors.IsAlreadyExists,
},
}
for name, item := range table {
storage, fakeClient := newStorage(t)
fakeClient.Data[path] = item.existing
_, err := storage.Create(ctx, item.toCreate)
if !item.errOK(err) {
t.Errorf("%v: unexpected error: %v", name, err)
}
received := fakeClient.Data[path]
var limitRange api.LimitRange
if err := testapi.Codec().DecodeInto([]byte(received.R.Node.Value), &limitRange); err != nil {
t.Errorf("unexpected error: %v", err)
}
// Unset CreationTimestamp and UID which are set automatically by infrastructure.
limitRange.ObjectMeta.CreationTimestamp = util.Time{}
limitRange.ObjectMeta.UID = ""
received.R.Node.Value = runtime.EncodeOrDie(testapi.Codec(), &limitRange)
if e, a := item.expect, received; !reflect.DeepEqual(e, a) {
t.Errorf("%v:\n%s", name, util.ObjectDiff(e, a))
}
}
}
func TestCreate(t *testing.T) {
storage, fakeClient := newStorage(t)
test := resttest.New(t, storage, fakeClient.SetError).GeneratesName()
validLimitRange := validNewLimitRange()
validLimitRange.ObjectMeta = api.ObjectMeta{}
test.TestCreate(
// valid
validLimitRange,
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
&api.LimitRange{
ObjectMeta: api.ObjectMeta{Name: "_-a123-a_"},
},
)
}

View File

@ -82,6 +82,12 @@ func TestCreate(t *testing.T) {
test.TestCreate(
// valid
node,
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
&api.Node{
ObjectMeta: api.ObjectMeta{Name: "_-a123-a_"},

View File

@ -68,6 +68,12 @@ func TestCreate(t *testing.T) {
test.TestCreate(
// valid
namespace,
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
&api.Namespace{
ObjectMeta: api.ObjectMeta{Name: "bad value"},

View File

@ -76,6 +76,12 @@ func TestCreate(t *testing.T) {
test.TestCreate(
// valid
pv,
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
&api.PersistentVolume{
ObjectMeta: api.ObjectMeta{Name: "*BadName!"},

View File

@ -73,6 +73,12 @@ func TestCreate(t *testing.T) {
test.TestCreate(
// valid
pv,
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
&api.PersistentVolumeClaim{
ObjectMeta: api.ObjectMeta{Name: "*BadName!"},

View File

@ -101,6 +101,12 @@ func TestCreate(t *testing.T) {
test.TestCreate(
// valid
pod,
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 (empty contains list)
&api.Pod{
Spec: api.PodSpec{
@ -494,25 +500,6 @@ func TestEtcdCreateFailsWithoutNamespace(t *testing.T) {
}
}
func TestEtcdCreateAlreadyExisting(t *testing.T) {
storage, _, _, fakeClient := newStorage(t)
ctx := api.NewDefaultContext()
key, _ := storage.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"}}),
},
},
E: nil,
}
_, err := storage.Create(ctx, validNewPod())
if !errors.IsAlreadyExists(err) {
t.Errorf("Unexpected error returned: %#v", err)
}
}
func TestEtcdCreateWithContainersNotFound(t *testing.T) {
storage, bindingStorage, _, fakeClient := newStorage(t)
ctx := api.NewDefaultContext()

View File

@ -22,6 +22,7 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/rest/resttest"
"k8s.io/kubernetes/pkg/registry/registrytest"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/tools"
"k8s.io/kubernetes/pkg/tools/etcdtest"
)
@ -66,6 +67,12 @@ func TestCreate(t *testing.T) {
test.TestCreate(
// valid
pod,
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
&api.PodTemplate{
Template: api.PodTemplateSpec{},

View File

@ -21,6 +21,7 @@ import (
"github.com/coreos/go-etcd/etcd"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/testapi"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/storage"
@ -36,8 +37,42 @@ func NewEtcdStorage(t *testing.T) (storage.Interface, *tools.FakeEtcdClient) {
return etcdStorage, fakeClient
}
func SetResourceVersion(fakeClient *tools.FakeEtcdClient, resourceVersion uint64) {
fakeClient.ChangeIndex = resourceVersion
type keyFunc func(api.Context, string) (string, error)
type newFunc func() runtime.Object
func GetObject(fakeClient *tools.FakeEtcdClient, keyFn keyFunc, newFn newFunc, ctx api.Context, obj runtime.Object) (runtime.Object, error) {
meta, err := api.ObjectMetaFor(obj)
if err != nil {
return nil, err
}
key, err := keyFn(ctx, meta.Name)
if err != nil {
return nil, err
}
key = etcdtest.AddPrefix(key)
resp, err := fakeClient.Get(key, false, false)
if err != nil {
return nil, err
}
result := newFn()
if err := testapi.Codec().DecodeInto([]byte(resp.Node.Value), result); err != nil {
return nil, err
}
return result, nil
}
func SetObject(fakeClient *tools.FakeEtcdClient, keyFn keyFunc, ctx api.Context, obj runtime.Object) error {
meta, err := api.ObjectMetaFor(obj)
if err != nil {
return err
}
key, err := keyFn(ctx, meta.Name)
if err != nil {
return err
}
key = etcdtest.AddPrefix(key)
_, err = fakeClient.Set(key, runtime.EncodeOrDie(testapi.Codec(), obj), 0)
return err
}
func SetObjectsForKey(fakeClient *tools.FakeEtcdClient, key string, objects []runtime.Object) []runtime.Object {
@ -66,3 +101,7 @@ func SetObjectsForKey(fakeClient *tools.FakeEtcdClient, key string, objects []ru
}
return result
}
func SetResourceVersion(fakeClient *tools.FakeEtcdClient, resourceVersion uint64) {
fakeClient.ChangeIndex = resourceVersion
}

View File

@ -18,12 +18,10 @@ package etcd
import (
"fmt"
"strings"
"testing"
"time"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/resource"
"k8s.io/kubernetes/pkg/api/rest/resttest"
"k8s.io/kubernetes/pkg/api/testapi"
@ -86,6 +84,12 @@ func TestCreate(t *testing.T) {
test.TestCreate(
// valid
resourcequota,
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
&api.ResourceQuota{
ObjectMeta: api.ObjectMeta{Name: "_-a123-a_"},
@ -202,36 +206,6 @@ func TestEtcdList(t *testing.T) {
})
}
func TestEtcdCreateFailsWithoutNamespace(t *testing.T) {
storage, _, _ := newStorage(t)
resourcequota := validNewResourceQuota()
resourcequota.Namespace = ""
_, err := storage.Create(api.NewContext(), resourcequota)
// Accept "namespace" or "Namespace".
if err == nil || !strings.Contains(err.Error(), "amespace") {
t.Fatalf("expected error that namespace was missing from context, got: %v", err)
}
}
func TestEtcdCreateAlreadyExisting(t *testing.T) {
storage, _, fakeClient := newStorage(t)
ctx := api.NewDefaultContext()
key, _ := storage.KeyFunc(ctx, "foo")
key = etcdtest.AddPrefix(key)
fakeClient.Data[key] = tools.EtcdResponseWithError{
R: &etcd.Response{
Node: &etcd.Node{
Value: runtime.EncodeOrDie(testapi.Codec(), &api.ResourceQuota{ObjectMeta: api.ObjectMeta{Name: "foo"}}),
},
},
E: nil,
}
_, err := storage.Create(ctx, validNewResourceQuota())
if !errors.IsAlreadyExists(err) {
t.Errorf("Unexpected error returned: %#v", err)
}
}
func TestEtcdUpdateStatus(t *testing.T) {
storage, status, fakeClient := newStorage(t)
ctx := api.NewDefaultContext()

View File

@ -22,6 +22,7 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/rest/resttest"
"k8s.io/kubernetes/pkg/registry/registrytest"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/tools"
"k8s.io/kubernetes/pkg/tools/etcdtest"
)
@ -51,6 +52,12 @@ func TestCreate(t *testing.T) {
test.TestCreate(
// valid
secret,
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
&api.Secret{},
&api.Secret{

View File

@ -0,0 +1,88 @@
/*
Copyright 2015 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package etcd
import (
"testing"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/rest/resttest"
"k8s.io/kubernetes/pkg/registry/registrytest"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/tools"
"k8s.io/kubernetes/pkg/util"
)
func newStorage(t *testing.T) (*REST, *tools.FakeEtcdClient) {
etcdStorage, fakeClient := registrytest.NewEtcdStorage(t)
return NewREST(etcdStorage), fakeClient
}
func validService() *api.Service {
return &api.Service{
ObjectMeta: api.ObjectMeta{
Name: "foo",
Namespace: api.NamespaceDefault,
},
Spec: api.ServiceSpec{
Selector: map[string]string{"bar": "baz"},
ClusterIP: "None",
SessionAffinity: "None",
Type: api.ServiceTypeClusterIP,
Ports: []api.ServicePort{{
Port: 6502,
Protocol: api.ProtocolTCP,
TargetPort: util.NewIntOrStringFromInt(6502),
}},
},
}
}
func TestCreate(t *testing.T) {
storage, fakeClient := newStorage(t)
test := resttest.New(t, storage, fakeClient.SetError)
validService := validService()
validService.ObjectMeta = api.ObjectMeta{}
test.TestCreate(
// valid
validService,
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
&api.Service{
Spec: api.ServiceSpec{},
},
// invalid
&api.Service{
Spec: api.ServiceSpec{
Selector: map[string]string{"bar": "baz"},
ClusterIP: "invalid",
SessionAffinity: "None",
Type: api.ServiceTypeClusterIP,
Ports: []api.ServicePort{{
Port: 6502,
Protocol: api.ProtocolTCP,
TargetPort: util.NewIntOrStringFromInt(6502),
}},
},
},
)
}

View File

@ -24,7 +24,6 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/rest"
"k8s.io/kubernetes/pkg/api/rest/resttest"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/registry/registrytest"
@ -33,6 +32,10 @@ import (
"k8s.io/kubernetes/pkg/util"
)
// TODO(wojtek-t): Cleanup this file.
// It is now testing mostly the same things as other resources but
// in a completely different way. We should unify it.
func NewTestREST(t *testing.T, endpoints *api.EndpointsList) (*REST, *registrytest.ServiceRegistry) {
registry := registrytest.NewServiceRegistry()
endpointRegistry := &registrytest.EndpointRegistry{
@ -741,25 +744,6 @@ func TestServiceRegistryIPLoadBalancer(t *testing.T) {
}
}
// TODO: remove, covered by TestCreate
func TestCreateServiceWithConflictingNamespace(t *testing.T) {
storage := REST{}
service := &api.Service{
ObjectMeta: api.ObjectMeta{Name: "test", Namespace: "not-default"},
}
ctx := api.NewDefaultContext()
obj, err := storage.Create(ctx, service)
if obj != nil {
t.Error("Expected a nil object, but we got a value")
}
if err == nil {
t.Errorf("Expected an error, but we didn't get one")
} else if strings.Contains(err.Error(), "Service.Namespace does not match the provided context") {
t.Errorf("Expected 'Service.Namespace does not match the provided context' error, got '%s'", err.Error())
}
}
func TestUpdateServiceWithConflictingNamespace(t *testing.T) {
storage := REST{}
service := &api.Service{
@ -777,43 +761,3 @@ func TestUpdateServiceWithConflictingNamespace(t *testing.T) {
t.Errorf("Expected 'Service.Namespace does not match the provided context' error, got '%s'", err.Error())
}
}
func TestCreate(t *testing.T) {
rest, registry := NewTestREST(t, nil)
test := resttest.New(t, rest, registry.SetError)
test.TestCreate(
// valid
&api.Service{
Spec: api.ServiceSpec{
Selector: map[string]string{"bar": "baz"},
ClusterIP: "None",
SessionAffinity: "None",
Type: api.ServiceTypeClusterIP,
Ports: []api.ServicePort{{
Port: 6502,
Protocol: api.ProtocolTCP,
TargetPort: util.NewIntOrStringFromInt(6502),
}},
},
},
// invalid
&api.Service{
Spec: api.ServiceSpec{},
},
// invalid
&api.Service{
Spec: api.ServiceSpec{
Selector: map[string]string{"bar": "baz"},
ClusterIP: "invalid",
SessionAffinity: "None",
Type: api.ServiceTypeClusterIP,
Ports: []api.ServicePort{{
Port: 6502,
Protocol: api.ProtocolTCP,
TargetPort: util.NewIntOrStringFromInt(6502),
}},
},
},
)
}

View File

@ -22,6 +22,7 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/rest/resttest"
"k8s.io/kubernetes/pkg/registry/registrytest"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/tools"
"k8s.io/kubernetes/pkg/tools/etcdtest"
)
@ -49,6 +50,12 @@ func TestCreate(t *testing.T) {
test.TestCreate(
// valid
serviceAccount,
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
&api.ServiceAccount{},
&api.ServiceAccount{

View File

@ -26,29 +26,22 @@ import (
"k8s.io/kubernetes/pkg/expapi/v1"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/registry/registrytest"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/storage"
etcdstorage "k8s.io/kubernetes/pkg/storage/etcd"
"k8s.io/kubernetes/pkg/tools"
"k8s.io/kubernetes/pkg/tools/etcdtest"
"github.com/coreos/go-etcd/etcd"
)
var scheme *runtime.Scheme
var codec runtime.Codec
func init() {
// Ensure that expapi/v1 packege is used, so that it will get initialized and register HorizontalPodAutoscaler object.
_ = v1.ThirdPartyResource{}
}
func newStorage(t *testing.T) (*REST, *tools.FakeEtcdClient, storage.Interface) {
fakeEtcdClient := tools.NewFakeEtcdClient(t)
fakeEtcdClient.TestIndex = true
etcdStorage := etcdstorage.NewEtcdStorage(fakeEtcdClient, testapi.Codec(), etcdtest.PathPrefix())
storage := NewREST(etcdStorage)
return storage, fakeEtcdClient, etcdStorage
func newStorage(t *testing.T) (*REST, *tools.FakeEtcdClient) {
etcdStorage, fakeClient := registrytest.NewEtcdStorage(t)
return NewREST(etcdStorage), fakeClient
}
func validNewThirdPartyResource(name string) *expapi.ThirdPartyResource {
@ -66,28 +59,34 @@ func validNewThirdPartyResource(name string) *expapi.ThirdPartyResource {
}
func TestCreate(t *testing.T) {
storage, fakeEtcdClient, _ := newStorage(t)
test := resttest.New(t, storage, fakeEtcdClient.SetError)
storage, fakeClient := newStorage(t)
test := resttest.New(t, storage, fakeClient.SetError)
rsrc := validNewThirdPartyResource("foo")
rsrc.ObjectMeta = api.ObjectMeta{}
test.TestCreate(
// valid
rsrc,
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
&expapi.ThirdPartyResource{},
)
}
func TestUpdate(t *testing.T) {
storage, fakeEtcdClient, _ := newStorage(t)
test := resttest.New(t, storage, fakeEtcdClient.SetError)
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)
fakeEtcdClient.ExpectNotFoundGet(key)
fakeEtcdClient.ChangeIndex = 2
fakeClient.ExpectNotFoundGet(key)
fakeClient.ChangeIndex = 2
rsrc := validNewThirdPartyResource("foo")
existing := validNewThirdPartyResource("exists")
existing.Namespace = test.TestNamespace()
@ -106,13 +105,13 @@ func TestUpdate(t *testing.T) {
func TestDelete(t *testing.T) {
ctx := api.NewDefaultContext()
storage, fakeEtcdClient, _ := newStorage(t)
test := resttest.New(t, storage, fakeEtcdClient.SetError)
storage, fakeClient := newStorage(t)
test := resttest.New(t, storage, fakeClient.SetError)
rsrc := validNewThirdPartyResource("foo2")
key, _ := storage.KeyFunc(ctx, "foo2")
key = etcdtest.AddPrefix(key)
createFn := func() runtime.Object {
fakeEtcdClient.Data[key] = tools.EtcdResponseWithError{
fakeClient.Data[key] = tools.EtcdResponseWithError{
R: &etcd.Response{
Node: &etcd.Node{
Value: runtime.EncodeOrDie(testapi.Codec(), rsrc),
@ -123,24 +122,24 @@ func TestDelete(t *testing.T) {
return rsrc
}
gracefulSetFn := func() bool {
if fakeEtcdClient.Data[key].R.Node == nil {
if fakeClient.Data[key].R.Node == nil {
return false
}
return fakeEtcdClient.Data[key].R.Node.TTL == 30
return fakeClient.Data[key].R.Node.TTL == 30
}
test.TestDeleteNoGraceful(createFn, gracefulSetFn)
}
func TestGet(t *testing.T) {
storage, fakeEtcdClient, _ := newStorage(t)
test := resttest.New(t, storage, fakeEtcdClient.SetError)
storage, fakeClient := newStorage(t)
test := resttest.New(t, storage, fakeClient.SetError)
rsrc := validNewThirdPartyResource("foo")
test.TestGet(rsrc)
}
func TestEmptyList(t *testing.T) {
ctx := api.NewDefaultContext()
registry, fakeClient, _ := newStorage(t)
registry, fakeClient := newStorage(t)
fakeClient.ChangeIndex = 1
key := registry.KeyRootFunc(ctx)
key = etcdtest.AddPrefix(key)
@ -162,7 +161,7 @@ func TestEmptyList(t *testing.T) {
func TestList(t *testing.T) {
ctx := api.NewDefaultContext()
registry, fakeClient, _ := newStorage(t)
registry, fakeClient := newStorage(t)
fakeClient.ChangeIndex = 1
key := registry.KeyRootFunc(ctx)
key = etcdtest.AddPrefix(key)