func TestCreate(t *testing.T) {
	obj := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}
	server := etcdtesting.NewEtcdTestClientServer(t)
	defer server.Terminate(t)
	helper := newEtcdHelper(server.Client, testapi.Default.Codec(), etcdtest.PathPrefix())
	returnedObj := &api.Pod{}
	err := helper.Create(context.TODO(), "/some/key", obj, returnedObj, 5)
	if err != nil {
		t.Errorf("Unexpected error %#v", err)
	}
	_, err = runtime.Encode(testapi.Default.Codec(), obj)
	if err != nil {
		t.Errorf("Unexpected error %#v", err)
	}
	err = helper.Get(context.TODO(), "/some/key", returnedObj, false)
	if err != nil {
		t.Errorf("Unexpected error %#v", err)
	}
	_, err = runtime.Encode(testapi.Default.Codec(), returnedObj)
	if err != nil {
		t.Errorf("Unexpected error %#v", err)
	}
	if obj.Name != returnedObj.Name {
		t.Errorf("Wanted %v, got %v", obj.Name, returnedObj.Name)
	}
}
Exemple #2
0
func NewTestGenericEtcdRegistry(t *testing.T) (*etcdtesting.EtcdTestServer, *Etcd) {
	podPrefix := "/pods"
	server := etcdtesting.NewEtcdTestClientServer(t)
	s := etcdstorage.NewEtcdStorage(server.Client, testapi.Default.Codec(), etcdtest.PathPrefix())
	strategy := &testRESTStrategy{api.Scheme, api.SimpleNameGenerator, true, false, true}

	return server, &Etcd{
		NewFunc:        func() runtime.Object { return &api.Pod{} },
		NewListFunc:    func() runtime.Object { return &api.PodList{} },
		EndpointName:   "pods",
		CreateStrategy: strategy,
		UpdateStrategy: strategy,
		KeyRootFunc: func(ctx api.Context) string {
			return podPrefix
		},
		KeyFunc: func(ctx api.Context, id string) (string, error) {
			if _, ok := api.NamespaceFrom(ctx); !ok {
				return "", fmt.Errorf("namespace is required")
			}
			return path.Join(podPrefix, id), nil
		},
		ObjectNameFunc: func(obj runtime.Object) (string, error) { return obj.(*api.Pod).Name, nil },
		Storage:        s,
	}
}
func TestGuaranteedUpdateKeyNotFound(t *testing.T) {
	_, codec := testScheme(t)
	server := etcdtesting.NewEtcdTestClientServer(t)
	defer server.Terminate(t)
	key := etcdtest.AddPrefix("/some/key")
	helper := newEtcdHelper(server.Client, codec, key)

	// Create a new node.
	obj := &storagetesting.TestResource{ObjectMeta: api.ObjectMeta{Name: "foo"}, Value: 1}

	f := storage.SimpleUpdate(func(in runtime.Object) (runtime.Object, error) {
		return obj, nil
	})

	ignoreNotFound := false
	err := helper.GuaranteedUpdate(context.TODO(), key, &storagetesting.TestResource{}, ignoreNotFound, nil, f)
	if err == nil {
		t.Errorf("Expected error for key not found.")
	}

	ignoreNotFound = true
	err = helper.GuaranteedUpdate(context.TODO(), key, &storagetesting.TestResource{}, ignoreNotFound, nil, f)
	if err != nil {
		t.Errorf("Unexpected error %v.", err)
	}
}
func TestEtcdMasterNoOtherThenConflict(t *testing.T) {
	server := etcdtesting.NewEtcdTestClientServer(t)
	defer server.Terminate(t)

	// We set up the following scenario:
	// - after each Get() call, we write "baz" to a path
	// - this is simulating someone else writing a data
	// - the value written by someone else is the new value
	path := "foo"
	client := &MockClient{
		client: server.Client,
		t:      t,
		afterGetFunc: func() {
			if _, err := server.Client.Set(path, "baz", 0); err != nil {
				t.Errorf("unexpected error: %v", err)
			}
		},
		calls: make([]string, 0),
	}

	master := NewEtcdMasterElector(client)
	w := master.Elect(path, "bar")
	result := <-w.ResultChan()
	if result.Type != watch.Modified || result.Object.(Master) != "baz" {
		t.Errorf("unexpected event: %#v", result)
	}
	w.Stop()

	expectedCalls := []string{"get", "create", "get"}
	if !reflect.DeepEqual(client.calls, expectedCalls) {
		t.Errorf("unexpected calls: %#v", client.calls)
	}
}
func TestWatch(t *testing.T) {
	codec := testapi.Default.Codec()
	server := etcdtesting.NewEtcdTestClientServer(t)
	defer server.Terminate(t)
	key := "/some/key"
	h := newEtcdHelper(server.Client, codec, etcdtest.PathPrefix())

	watching, err := h.Watch(context.TODO(), key, 0, storage.Everything)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	// Test normal case
	pod := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}
	returnObj := &api.Pod{}
	err = h.Set(context.TODO(), key, pod, returnObj, 0)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	event := <-watching.ResultChan()
	if e, a := watch.Added, event.Type; e != a {
		t.Errorf("Expected %v, got %v", e, a)
	}
	if e, a := pod, event.Object; !api.Semantic.DeepDerivative(e, a) {
		t.Errorf("Expected %v, got %v", e, a)
	}

	watching.Stop()

	if _, open := <-watching.ResultChan(); open {
		t.Errorf("An injected error did not cause a graceful shutdown")
	}
}
// This is to emulate the case where another party updates the object when
// etcdHelper.Delete has verified the preconditions, but hasn't carried out the
// deletion yet. Etcd will fail the deletion and report the conflict. etcdHelper
// should retry until there is no conflict.
func TestDeleteWithRetry(t *testing.T) {
	server := etcdtesting.NewEtcdTestClientServer(t)
	defer server.Terminate(t)
	prefix := path.Join("/", etcdtest.PathPrefix())

	obj := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo", UID: "A"}}
	// fakeGet returns a large ModifiedIndex to emulate the case that another
	// party has updated the object.
	fakeGet := func(ctx context.Context, key string, opts *etcd.GetOptions) (*etcd.Response, error) {
		data, _ := runtime.Encode(testapi.Default.Codec(), obj)
		return &etcd.Response{Node: &etcd.Node{Value: string(data), ModifiedIndex: 99}}, nil
	}
	expectedRetries := 3
	helper := newEtcdHelper(server.Client, testapi.Default.Codec(), prefix)
	fake := &fakeDeleteKeysAPI{KeysAPI: helper.etcdKeysAPI, fakeGetCap: expectedRetries, fakeGetFunc: fakeGet}
	helper.etcdKeysAPI = fake

	returnedObj := &api.Pod{}
	err := helper.Create(context.TODO(), "/some/key", obj, returnedObj, 0)
	if err != nil {
		t.Errorf("Unexpected error %#v", err)
	}

	err = helper.Delete(context.TODO(), "/some/key", obj, storage.NewUIDPreconditions("A"))
	if err != nil {
		t.Errorf("Unexpected error %#v", err)
	}
	if fake.getCount != expectedRetries {
		t.Errorf("Expect %d retries, got %d", expectedRetries, fake.getCount)
	}
	err = helper.Get(context.TODO(), "/some/key", obj, false)
	if !storage.IsNotFound(err) {
		t.Errorf("Expect an NotFound error, got %v", err)
	}
}
func TestWatchListFromZeroIndex(t *testing.T) {
	codec := testapi.Default.Codec()
	key := etcdtest.AddPrefix("/some/key")
	server := etcdtesting.NewEtcdTestClientServer(t)
	defer server.Terminate(t)
	h := newEtcdHelper(server.Client, codec, key)

	watching, err := h.WatchList(context.TODO(), key, 0, storage.Everything)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	// creates key/foo which should trigger the WatchList for "key"
	pod := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}
	err = h.Create(context.TODO(), pod.Name, pod, pod, 0)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	event, _ := <-watching.ResultChan()
	if event.Type != watch.Added {
		t.Errorf("Unexpected event %#v", event)
	}

	if e, a := pod, event.Object; !api.Semantic.DeepDerivative(e, a) {
		t.Errorf("%s: expected %v, got %v", e, a)
	}

	watching.Stop()
}
Exemple #8
0
// setUp is a convience function for setting up for (most) tests.
func setUp(t *testing.T) (Master, *etcdtesting.EtcdTestServer, Config, *assert.Assertions) {
	server := etcdtesting.NewEtcdTestClientServer(t)

	master := Master{
		GenericAPIServer: &genericapiserver.GenericAPIServer{},
	}
	config := Config{
		Config: &genericapiserver.Config{},
	}
	storageVersions := make(map[string]string)
	storageDestinations := genericapiserver.NewStorageDestinations()
	storageDestinations.AddAPIGroup(
		api.GroupName, etcdstorage.NewEtcdStorage(server.Client, testapi.Default.Codec(), etcdtest.PathPrefix(), false))
	storageDestinations.AddAPIGroup(
		extensions.GroupName, etcdstorage.NewEtcdStorage(server.Client, testapi.Extensions.Codec(), etcdtest.PathPrefix(), false))

	config.StorageDestinations = storageDestinations
	storageVersions[api.GroupName] = testapi.Default.GroupVersion().String()
	storageVersions[extensions.GroupName] = testapi.Extensions.GroupVersion().String()
	config.StorageVersions = storageVersions
	config.PublicAddress = net.ParseIP("192.168.10.4")
	master.nodeRegistry = registrytest.NewNodeRegistry([]string{"node1", "node2"}, api.NodeResources{})

	return master, server, config, assert.New(t)
}
func TestList(t *testing.T) {
	server := etcdtesting.NewEtcdTestClientServer(t)
	defer server.Terminate(t)
	key := etcdtest.AddPrefix("/some/key")
	helper := newEtcdHelper(server.Client, testapi.Default.Codec(), key)

	list := api.PodList{
		Items: []api.Pod{
			{
				ObjectMeta: api.ObjectMeta{Name: "bar"},
				Spec:       apitesting.DeepEqualSafePodSpec(),
			},
			{
				ObjectMeta: api.ObjectMeta{Name: "baz"},
				Spec:       apitesting.DeepEqualSafePodSpec(),
			},
			{
				ObjectMeta: api.ObjectMeta{Name: "foo"},
				Spec:       apitesting.DeepEqualSafePodSpec(),
			},
		},
	}

	createPodList(t, helper, &list)
	var got api.PodList
	// TODO: a sorted filter function could be applied such implied
	// ordering on the returned list doesn't matter.
	err := helper.List(context.TODO(), key, 0, storage.Everything, &got)
	if err != nil {
		t.Errorf("Unexpected error %v", err)
	}
	if e, a := list.Items, got.Items; !reflect.DeepEqual(e, a) {
		t.Errorf("Expected %#v, got %#v", e, a)
	}
}
func TestWatchFromZeroIndex(t *testing.T) {
	codec := testapi.Default.Codec()
	pod := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}

	key := etcdtest.AddPrefix("/somekey/foo")
	server := etcdtesting.NewEtcdTestClientServer(t)
	defer server.Terminate(t)

	h := newEtcdHelper(server.Client, codec, etcdtest.PathPrefix())

	// set before the watch and verify events
	err := h.Create(context.TODO(), key, pod, pod, 0)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	pod.ResourceVersion = ""

	// check for concatenation on watch event with CAS
	updateFn := func(input runtime.Object, res storage.ResponseMeta) (runtime.Object, *uint64, error) {
		pod := input.(*api.Pod)
		pod.Name = "bar"
		return pod, nil, nil
	}
	err = h.GuaranteedUpdate(context.TODO(), key, &api.Pod{}, false, nil, updateFn)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	watching, err := h.Watch(context.TODO(), key, "0", storage.Everything)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	defer watching.Stop()

	// marked as modified b/c of concatenation
	event := <-watching.ResultChan()
	if event.Type != watch.Modified {
		t.Errorf("Unexpected event %#v", event)
	}

	pod.Name = "baz"
	updateFn = func(input runtime.Object, res storage.ResponseMeta) (runtime.Object, *uint64, error) {
		pod := input.(*api.Pod)
		pod.Name = "baz"
		return pod, nil, nil
	}
	err = h.GuaranteedUpdate(context.TODO(), key, &api.Pod{}, false, nil, updateFn)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	event = <-watching.ResultChan()
	if event.Type != watch.Modified {
		t.Errorf("Unexpected event %#v", event)
	}

	if e, a := pod, event.Object; !api.Semantic.DeepDerivative(e, a) {
		t.Errorf("Unexpected error: expected %#v, got %#v", e, a)
	}
}
func TestWatchPurposefulShutdown(t *testing.T) {
	_, codec := testScheme(t)
	server := etcdtesting.NewEtcdTestClientServer(t)
	defer server.Terminate(t)
	key := "/some/key"
	h := newEtcdHelper(server.Client, codec, etcdtest.PathPrefix())

	// Test purposeful shutdown
	watching, err := h.Watch(context.TODO(), key, "0", storage.Everything)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	watching.Stop()
	rt.Gosched()

	// There is a race in etcdWatcher so that after calling Stop() one of
	// two things can happen:
	// - ResultChan() may be closed (triggered by closing userStop channel)
	// - an Error "context cancelled" may be emitted (triggered by cancelling request
	//   to etcd and putting that error to etcdError channel)
	// We need to be prepared for both here.
	event, open := <-watching.ResultChan()
	if open && event.Type != watch.Error {
		t.Errorf("Unexpected event from stopped watcher: %#v", event)
	}
}
func TestWatchListIgnoresRootKey(t *testing.T) {
	codec := testapi.Default.Codec()
	pod := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}
	key := etcdtest.AddPrefix("/some/key")
	server := etcdtesting.NewEtcdTestClientServer(t)
	defer server.Terminate(t)
	h := newEtcdHelper(server.Client, codec, key)

	watching, err := h.WatchList(context.TODO(), key, 0, storage.Everything)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	// creates key/foo which should trigger the WatchList for "key"
	err = h.Create(context.TODO(), key, pod, pod, 0)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	// force context switch to ensure watches would catch and notify.
	rt.Gosched()

	select {
	case event, _ := <-watching.ResultChan():
		t.Fatalf("Unexpected event: %#v", event)
	default:
		// fall through, expected behavior
	}

	watching.Stop()
}
func TestGuaranteedUpdateNoChange(t *testing.T) {
	_, codec := testScheme(t)
	server := etcdtesting.NewEtcdTestClientServer(t)
	defer server.Terminate(t)
	key := etcdtest.AddPrefix("/some/key")
	helper := newEtcdHelper(server.Client, codec, key)

	obj := &storagetesting.TestResource{ObjectMeta: api.ObjectMeta{Name: "foo"}, Value: 1}
	err := helper.GuaranteedUpdate(context.TODO(), key, &storagetesting.TestResource{}, true, nil, storage.SimpleUpdate(func(in runtime.Object) (runtime.Object, error) {
		return obj, nil
	}))
	if err != nil {
		t.Errorf("Unexpected error %#v", err)
	}

	// Update an existing node with the same data
	callbackCalled := false
	objUpdate := &storagetesting.TestResource{ObjectMeta: api.ObjectMeta{Name: "foo"}, Value: 1}
	err = helper.GuaranteedUpdate(context.TODO(), key, &storagetesting.TestResource{}, true, nil, storage.SimpleUpdate(func(in runtime.Object) (runtime.Object, error) {
		callbackCalled = true
		return objUpdate, nil
	}))
	if err != nil {
		t.Fatalf("Unexpected error %#v", err)
	}
	if !callbackCalled {
		t.Errorf("tryUpdate callback should have been called.")
	}
}
// setUp is a convience function for setting up for (most) tests.
func setUp(t *testing.T) (GenericAPIServer, *etcdtesting.EtcdTestServer, Config, *assert.Assertions) {
	etcdServer := etcdtesting.NewEtcdTestClientServer(t)

	genericapiserver := GenericAPIServer{}
	config := Config{}
	config.PublicAddress = net.ParseIP("192.168.10.4")

	return genericapiserver, etcdServer, config, assert.New(t)
}
func TestSetFailCAS(t *testing.T) {
	obj := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "1"}}
	server := etcdtesting.NewEtcdTestClientServer(t)
	defer server.Terminate(t)
	helper := newEtcdHelper(server.Client, testapi.Default.Codec(), etcdtest.PathPrefix())
	err := helper.Set(context.TODO(), "/some/key", obj, nil, 5)
	if err == nil {
		t.Errorf("Expecting error.")
	}
}
func TestCreateNilOutParam(t *testing.T) {
	obj := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}
	server := etcdtesting.NewEtcdTestClientServer(t)
	defer server.Terminate(t)
	helper := newEtcdHelper(server.Client, testapi.Default.Codec(), etcdtest.PathPrefix())
	err := helper.Create(context.TODO(), "/some/key", obj, nil, 5)
	if err != nil {
		t.Errorf("Unexpected error %#v", err)
	}
}
func TestWatchEtcdState(t *testing.T) {
	codec := testapi.Default.Codec()
	key := etcdtest.AddPrefix("/somekey/foo")
	server := etcdtesting.NewEtcdTestClientServer(t)
	defer server.Terminate(t)

	h := newEtcdHelper(server.Client, codec, etcdtest.PathPrefix())
	watching, err := h.Watch(context.TODO(), key, "0", storage.Everything)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	defer watching.Stop()

	endpoint := &api.Endpoints{
		ObjectMeta: api.ObjectMeta{Name: "foo"},
		Subsets:    emptySubsets(),
	}

	err = h.Create(context.TODO(), key, endpoint, endpoint, 0)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	event := <-watching.ResultChan()
	if event.Type != watch.Added {
		t.Errorf("Unexpected event %#v", event)
	}

	subset := makeSubsets("127.0.0.1", 9000)
	endpoint.Subsets = subset
	endpoint.ResourceVersion = ""

	// CAS the previous value
	updateFn := func(input runtime.Object, res storage.ResponseMeta) (runtime.Object, *uint64, error) {
		newObj, err := api.Scheme.DeepCopy(endpoint)
		if err != nil {
			t.Errorf("unexpected error: %v", err)
			return nil, nil, err
		}
		return newObj.(*api.Endpoints), nil, nil
	}
	err = h.GuaranteedUpdate(context.TODO(), key, &api.Endpoints{}, false, nil, updateFn)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	event = <-watching.ResultChan()
	if event.Type != watch.Modified {
		t.Errorf("Unexpected event %#v", event)
	}

	if e, a := endpoint, event.Object; !api.Semantic.DeepDerivative(e, a) {
		t.Errorf("Unexpected error: expected %#v, got %#v", e, a)
	}
}
func TestGetNotFoundErr(t *testing.T) {
	server := etcdtesting.NewEtcdTestClientServer(t)
	defer server.Terminate(t)
	boguskey := "/some/boguskey"
	helper := newEtcdHelper(server.Client, testapi.Default.Codec(), etcdtest.PathPrefix())

	var got api.Pod
	err := helper.Get(context.TODO(), boguskey, "", &got, false)
	if !storage.IsNotFound(err) {
		t.Errorf("Unexpected reponse on key=%v, err=%v", boguskey, err)
	}
}
Exemple #19
0
// setUp is a convience function for setting up for (most) tests.
func setUp(t *testing.T) (*Master, *etcdtesting.EtcdTestServer, Config, *assert.Assertions) {
	server := etcdtesting.NewEtcdTestClientServer(t)

	master := &Master{
		GenericAPIServer: &genericapiserver.GenericAPIServer{},
	}
	config := Config{
		Config: &genericapiserver.Config{},
	}

	storageConfig := storagebackend.Config{
		Prefix:   etcdtest.PathPrefix(),
		CAFile:   server.CAFile,
		KeyFile:  server.KeyFile,
		CertFile: server.CertFile,
	}
	for _, url := range server.ClientURLs {
		storageConfig.ServerList = append(storageConfig.ServerList, url.String())
	}

	resourceEncoding := genericapiserver.NewDefaultResourceEncodingConfig()
	resourceEncoding.SetVersionEncoding(api.GroupName, *testapi.Default.GroupVersion(), unversioned.GroupVersion{Group: api.GroupName, Version: runtime.APIVersionInternal})
	resourceEncoding.SetVersionEncoding(autoscaling.GroupName, *testapi.Autoscaling.GroupVersion(), unversioned.GroupVersion{Group: autoscaling.GroupName, Version: runtime.APIVersionInternal})
	resourceEncoding.SetVersionEncoding(batch.GroupName, *testapi.Batch.GroupVersion(), unversioned.GroupVersion{Group: batch.GroupName, Version: runtime.APIVersionInternal})
	resourceEncoding.SetVersionEncoding(apps.GroupName, *testapi.Apps.GroupVersion(), unversioned.GroupVersion{Group: apps.GroupName, Version: runtime.APIVersionInternal})
	resourceEncoding.SetVersionEncoding(extensions.GroupName, *testapi.Extensions.GroupVersion(), unversioned.GroupVersion{Group: extensions.GroupName, Version: runtime.APIVersionInternal})
	resourceEncoding.SetVersionEncoding(rbac.GroupName, *testapi.Rbac.GroupVersion(), unversioned.GroupVersion{Group: rbac.GroupName, Version: runtime.APIVersionInternal})
	resourceEncoding.SetVersionEncoding(certificates.GroupName, *testapi.Certificates.GroupVersion(), unversioned.GroupVersion{Group: certificates.GroupName, Version: runtime.APIVersionInternal})
	storageFactory := genericapiserver.NewDefaultStorageFactory(storageConfig, testapi.StorageMediaType(), api.Codecs, resourceEncoding, DefaultAPIResourceConfigSource())

	config.StorageFactory = storageFactory
	config.APIResourceConfigSource = DefaultAPIResourceConfigSource()
	config.PublicAddress = net.ParseIP("192.168.10.4")
	config.Serializer = api.Codecs
	config.KubeletClient = client.FakeKubeletClient{}
	config.APIPrefix = "/api"
	config.APIGroupPrefix = "/apis"
	config.APIResourceConfigSource = DefaultAPIResourceConfigSource()
	config.ProxyDialer = func(network, addr string) (net.Conn, error) { return nil, nil }
	config.ProxyTLSClientConfig = &tls.Config{}

	// TODO: this is kind of hacky.  The trouble is that the sync loop
	// runs in a go-routine and there is no way to validate in the test
	// that the sync routine has actually run.  The right answer here
	// is probably to add some sort of callback that we can register
	// to validate that it's actually been run, but for now we don't
	// run the sync routine and register types manually.
	config.disableThirdPartyControllerForTesting = true

	master.nodeRegistry = registrytest.NewNodeRegistry([]string{"node1", "node2"}, api.NodeResources{})

	return master, server, config, assert.New(t)
}
Exemple #20
0
func newTestGenericStoreRegistry(t *testing.T, hasCacheEnabled bool) (*etcdtesting.EtcdTestServer, *Store) {
	podPrefix := "/pods"
	server := etcdtesting.NewEtcdTestClientServer(t)
	strategy := &testRESTStrategy{api.Scheme, api.SimpleNameGenerator, true, false, true}
	codec := testapi.Default.StorageCodec()
	s := etcdstorage.NewEtcdStorage(server.Client, codec, etcdtest.PathPrefix(), false, etcdtest.DeserializationCacheSize)
	if hasCacheEnabled {
		config := storage.CacherConfig{
			CacheCapacity:  10,
			Storage:        s,
			Versioner:      etcdstorage.APIObjectVersioner{},
			Type:           &api.Pod{},
			ResourcePrefix: podPrefix,
			KeyFunc:        func(obj runtime.Object) (string, error) { return storage.NoNamespaceKeyFunc(podPrefix, obj) },
			NewListFunc:    func() runtime.Object { return &api.PodList{} },
			Codec:          codec,
		}
		s = storage.NewCacherFromConfig(config)
	}

	return server, &Store{
		NewFunc:           func() runtime.Object { return &api.Pod{} },
		NewListFunc:       func() runtime.Object { return &api.PodList{} },
		QualifiedResource: api.Resource("pods"),
		CreateStrategy:    strategy,
		UpdateStrategy:    strategy,
		DeleteStrategy:    strategy,
		KeyRootFunc: func(ctx api.Context) string {
			return podPrefix
		},
		KeyFunc: func(ctx api.Context, id string) (string, error) {
			if _, ok := api.NamespaceFrom(ctx); !ok {
				return "", fmt.Errorf("namespace is required")
			}
			return path.Join(podPrefix, id), nil
		},
		ObjectNameFunc: func(obj runtime.Object) (string, error) { return obj.(*api.Pod).Name, nil },
		PredicateFunc: func(label labels.Selector, field fields.Selector) *generic.SelectionPredicate {
			return &generic.SelectionPredicate{
				Label: label,
				Field: field,
				GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) {
					pod, ok := obj.(*api.Pod)
					if !ok {
						return nil, nil, fmt.Errorf("not a pod")
					}
					return labels.Set(pod.ObjectMeta.Labels), generic.ObjectMetaFieldsSet(pod.ObjectMeta, true), nil
				},
			}
		},
		Storage: s,
	}
}
func TestEtcdMasterNoOther(t *testing.T) {
	server := etcdtesting.NewEtcdTestClientServer(t)
	defer server.Terminate(t)

	path := "foo"
	master := NewEtcdMasterElector(server.Client)
	w := master.Elect(path, "bar")
	result := <-w.ResultChan()
	if result.Type != watch.Modified || result.Object.(Master) != "bar" {
		t.Errorf("unexpected event: %#v", result)
	}
	w.Stop()
}
func TestGetNotFoundErr(t *testing.T) {
	server := etcdtesting.NewEtcdTestClientServer(t)
	defer server.Terminate(t)
	key := etcdtest.AddPrefix("/some/key")
	boguskey := etcdtest.AddPrefix("/some/boguskey")
	helper := newEtcdHelper(server.Client, testapi.Default.Codec(), key)

	var got api.Pod
	err := helper.Get(context.TODO(), boguskey, &got, false)
	if !IsEtcdNotFound(err) {
		t.Errorf("Unexpected reponse on key=%v, err=%v", key, err)
	}
}
// TestNewEtcdStorage verifies that the usage of NewEtcdStorage reacts properly when
// the correct data is input
func TestNewEtcdStorage(t *testing.T) {
	etcdserver := etcdtesting.NewEtcdTestClientServer(t)
	defer etcdserver.Terminate(t)

	assert := assert.New(t)
	// Pass case
	_, err := NewEtcdStorage(etcdserver.Client, latest.GroupOrDie("").InterfacesFor, testapi.Default.Version(), etcdtest.PathPrefix())
	assert.NoError(err, "Unable to create etcdstorage: %s", err)

	// Fail case
	errorFunc := func(apiVersion string) (*meta.VersionInterfaces, error) { return nil, errors.New("ERROR") }
	_, err = NewEtcdStorage(etcdserver.Client, errorFunc, testapi.Default.Version(), etcdtest.PathPrefix())
	assert.Error(err, "NewEtcdStorage should have failed")
}
func TestWatchFromZeroIndex(t *testing.T) {
	codec := testapi.Default.Codec()
	pod := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}

	key := etcdtest.AddPrefix("/somekey/foo")
	server := etcdtesting.NewEtcdTestClientServer(t)
	defer server.Terminate(t)

	h := newEtcdHelper(server.Client, codec, etcdtest.PathPrefix())

	// set before the watch and verify events
	err := h.Set(context.TODO(), key, pod, pod, 0)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	// check for concatenation on watch event with CAS
	pod.Name = "bar"
	err = h.Set(context.TODO(), key, pod, pod, 0)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	watching, err := h.Watch(context.TODO(), key, 0, storage.Everything)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	// marked as modified b/c of concatenation
	event := <-watching.ResultChan()
	if event.Type != watch.Modified {
		t.Errorf("Unexpected event %#v", event)
	}

	err = h.Set(context.TODO(), key, pod, pod, 0)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	event = <-watching.ResultChan()
	if event.Type != watch.Modified {
		t.Errorf("Unexpected event %#v", event)
	}

	if e, a := pod, event.Object; !api.Semantic.DeepDerivative(e, a) {
		t.Errorf("%s: expected %v, got %v", e, a)
	}

	watching.Stop()
}
func TestSetWithoutResourceVersioner(t *testing.T) {
	obj := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}
	server := etcdtesting.NewEtcdTestClientServer(t)
	defer server.Terminate(t)
	helper := newEtcdHelper(server.Client, testapi.Default.Codec(), etcdtest.PathPrefix())
	helper.versioner = nil
	returnedObj := &api.Pod{}
	err := helper.Set(context.TODO(), "/some/key", obj, returnedObj, 3)
	if err != nil {
		t.Errorf("Unexpected error %#v", err)
	}
	if returnedObj.ResourceVersion != "" {
		t.Errorf("Resource revision should not be set on returned objects")
	}
}
func TestGuaranteedUpdate_CreateCollision(t *testing.T) {
	_, codec := testScheme(t)
	server := etcdtesting.NewEtcdTestClientServer(t)
	defer server.Terminate(t)
	key := etcdtest.AddPrefix("/some/key")
	helper := newEtcdHelper(server.Client, codec, etcdtest.PathPrefix())

	const concurrency = 10
	var wgDone sync.WaitGroup
	var wgForceCollision sync.WaitGroup
	wgDone.Add(concurrency)
	wgForceCollision.Add(concurrency)

	for i := 0; i < concurrency; i++ {
		// Increment storagetesting.TestResource.Value by 1
		go func() {
			defer wgDone.Done()

			firstCall := true
			err := helper.GuaranteedUpdate(context.TODO(), key, &storagetesting.TestResource{}, true, nil, storage.SimpleUpdate(func(in runtime.Object) (runtime.Object, error) {
				defer func() { firstCall = false }()

				if firstCall {
					// Force collision by joining all concurrent GuaranteedUpdate operations here.
					wgForceCollision.Done()
					wgForceCollision.Wait()
				}

				currValue := in.(*storagetesting.TestResource).Value
				obj := &storagetesting.TestResource{ObjectMeta: api.ObjectMeta{Name: "foo"}, Value: currValue + 1}
				return obj, nil
			}))
			if err != nil {
				t.Errorf("Unexpected error %#v", err)
			}
		}()
	}
	wgDone.Wait()

	stored := &storagetesting.TestResource{}
	err := helper.Get(context.TODO(), key, stored, false)
	if err != nil {
		t.Errorf("Unexpected error %#v", stored)
	}
	if stored.Value != concurrency {
		t.Errorf("Some of the writes were lost. Stored value: %d", stored.Value)
	}
}
func TestWatchEtcdError(t *testing.T) {
	codec := testapi.Default.Codec()
	server := etcdtesting.NewEtcdTestClientServer(t)
	h := newEtcdHelper(server.Client, codec, etcdtest.PathPrefix())
	watching, err := h.Watch(context.TODO(), "/some/key", "4", storage.Everything)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	server.Terminate(t)

	got, ok := <-watching.ResultChan()
	if ok && got.Type != watch.Error {
		t.Fatalf("Unexpected non-error")
	}
	watching.Stop()
}
Exemple #28
0
// setUp is a convience function for setting up for (most) tests.
func setUp(t *testing.T) (*Master, *etcdtesting.EtcdTestServer, Config, *assert.Assertions) {
	server := etcdtesting.NewEtcdTestClientServer(t)

	master := &Master{
		GenericAPIServer: &genericapiserver.GenericAPIServer{},
	}
	config := Config{
		Config: &genericapiserver.Config{},
	}
	storageVersions := make(map[string]string)
	storageDestinations := genericapiserver.NewStorageDestinations()
	storageDestinations.AddAPIGroup(
		api.GroupName, etcdstorage.NewEtcdStorage(server.Client, testapi.Default.Codec(), etcdtest.PathPrefix(), false, etcdtest.DeserializationCacheSize))
	storageDestinations.AddAPIGroup(
		autoscaling.GroupName, etcdstorage.NewEtcdStorage(server.Client, testapi.Autoscaling.Codec(), etcdtest.PathPrefix(), false, etcdtest.DeserializationCacheSize))
	storageDestinations.AddAPIGroup(
		batch.GroupName, etcdstorage.NewEtcdStorage(server.Client, testapi.Batch.Codec(), etcdtest.PathPrefix(), false, etcdtest.DeserializationCacheSize))
	storageDestinations.AddAPIGroup(
		extensions.GroupName, etcdstorage.NewEtcdStorage(server.Client, testapi.Extensions.Codec(), etcdtest.PathPrefix(), false, etcdtest.DeserializationCacheSize))

	config.StorageDestinations = storageDestinations
	storageVersions[api.GroupName] = testapi.Default.GroupVersion().String()
	storageVersions[autoscaling.GroupName] = testapi.Autoscaling.GroupVersion().String()
	storageVersions[batch.GroupName] = testapi.Batch.GroupVersion().String()
	storageVersions[extensions.GroupName] = testapi.Extensions.GroupVersion().String()
	config.StorageVersions = storageVersions
	config.PublicAddress = net.ParseIP("192.168.10.4")
	config.Serializer = api.Codecs
	config.KubeletClient = client.FakeKubeletClient{}
	config.APIPrefix = "/api"
	config.APIGroupPrefix = "/apis"
	config.APIResourceConfigSource = DefaultAPIResourceConfigSource()
	config.ProxyDialer = func(network, addr string) (net.Conn, error) { return nil, nil }
	config.ProxyTLSClientConfig = &tls.Config{}

	// TODO: this is kind of hacky.  The trouble is that the sync loop
	// runs in a go-routine and there is no way to validate in the test
	// that the sync routine has actually run.  The right answer here
	// is probably to add some sort of callback that we can register
	// to validate that it's actually been run, but for now we don't
	// run the sync routine and register types manually.
	config.disableThirdPartyControllerForTesting = true

	master.nodeRegistry = registrytest.NewNodeRegistry([]string{"node1", "node2"}, api.NodeResources{})

	return master, server, config, assert.New(t)
}
func TestWatchEtcdState(t *testing.T) {
	codec := testapi.Default.Codec()
	key := etcdtest.AddPrefix("/somekey/foo")
	server := etcdtesting.NewEtcdTestClientServer(t)
	defer server.Terminate(t)

	h := newEtcdHelper(server.Client, codec, etcdtest.PathPrefix())
	watching, err := h.Watch(context.TODO(), key, 0, storage.Everything)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	endpoint := &api.Endpoints{
		ObjectMeta: api.ObjectMeta{Name: "foo"},
		Subsets:    emptySubsets(),
	}

	err = h.Set(context.TODO(), key, endpoint, endpoint, 0)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	event := <-watching.ResultChan()
	if event.Type != watch.Added {
		t.Errorf("Unexpected event %#v", event)
	}

	subset := makeSubsets("127.0.0.1", 9000)
	endpoint.Subsets = subset

	// CAS the previous value
	err = h.Set(context.TODO(), key, endpoint, endpoint, 0)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	event = <-watching.ResultChan()
	if event.Type != watch.Modified {
		t.Errorf("Unexpected event %#v", event)
	}

	if e, a := endpoint, event.Object; !api.Semantic.DeepDerivative(e, a) {
		t.Errorf("%s: expected %v, got %v", e, a)
	}

	watching.Stop()
}
// TestListAcrossDirectories ensures that the client excludes directories and flattens tree-response - simulates cross-namespace query
func TestListAcrossDirectories(t *testing.T) {
	server := etcdtesting.NewEtcdTestClientServer(t)
	defer server.Terminate(t)
	rootkey := etcdtest.AddPrefix("/some/key")
	key1 := etcdtest.AddPrefix("/some/key/directory1")
	key2 := etcdtest.AddPrefix("/some/key/directory2")

	roothelper := newEtcdHelper(server.Client, testapi.Default.Codec(), rootkey)
	helper1 := newEtcdHelper(server.Client, testapi.Default.Codec(), key1)
	helper2 := newEtcdHelper(server.Client, testapi.Default.Codec(), key2)

	list := api.PodList{
		Items: []api.Pod{
			{
				ObjectMeta: api.ObjectMeta{Name: "baz"},
				Spec:       apitesting.DeepEqualSafePodSpec(),
			},
			{
				ObjectMeta: api.ObjectMeta{Name: "foo"},
				Spec:       apitesting.DeepEqualSafePodSpec(),
			},
			{
				ObjectMeta: api.ObjectMeta{Name: "bar"},
				Spec:       apitesting.DeepEqualSafePodSpec(),
			},
		},
	}

	returnedObj := &api.Pod{}
	// create the 1st 2 elements in one directory
	createObj(t, helper1, list.Items[0].Name, &list.Items[0], returnedObj, 0)
	list.Items[0] = *returnedObj
	createObj(t, helper1, list.Items[1].Name, &list.Items[1], returnedObj, 0)
	list.Items[1] = *returnedObj
	// create the last element in the other directory
	createObj(t, helper2, list.Items[2].Name, &list.Items[2], returnedObj, 0)
	list.Items[2] = *returnedObj

	var got api.PodList
	err := roothelper.List(context.TODO(), rootkey, "", storage.Everything, &got)
	if err != nil {
		t.Errorf("Unexpected error %v", err)
	}
	if e, a := list.Items, got.Items; !reflect.DeepEqual(e, a) {
		t.Errorf("Expected %#v, got %#v", e, a)
	}
}