コード例 #1
0
ファイル: etcd_watcher_test.go プロジェクト: qinguoan/vulcan
func TestWatchPurposefulShutdown(t *testing.T) {
	fakeClient := tools.NewFakeEtcdClient(t)

	h := newEtcdHelper(fakeClient, codec, etcdtest.PathPrefix())
	key := "/some/key"
	prefixedKey := etcdtest.AddPrefix(key)
	fakeClient.ExpectNotFoundGet(prefixedKey)

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

	fakeClient.WaitForWatchCompletion()
	watching.Stop()

	// Did everything shut down?
	if _, open := <-fakeClient.WatchResponse; open {
		t.Errorf("A stop did not cause a graceful shutdown")
	}
	if _, open := <-watching.ResultChan(); open {
		t.Errorf("An injected error did not cause a graceful shutdown")
	}
}
コード例 #2
0
ファイル: etcd_test.go プロジェクト: qinguoan/vulcan
func NewTestGenericEtcdRegistry(t *testing.T) (*tools.FakeEtcdClient, *Etcd) {
	f := tools.NewFakeEtcdClient(t)
	f.TestIndex = true
	s := etcdstorage.NewEtcdStorage(f, testapi.Default.Codec(), etcdtest.PathPrefix())
	strategy := &testRESTStrategy{api.Scheme, api.SimpleNameGenerator, true, false, true}
	podPrefix := "/pods"
	return f, &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,
	}
}
コード例 #3
0
ファイル: etcd_watcher_test.go プロジェクト: qinguoan/vulcan
func TestWatchFromNotFound(t *testing.T) {
	fakeClient := tools.NewFakeEtcdClient(t)
	key := "/some/key"
	prefixedKey := etcdtest.AddPrefix(key)
	fakeClient.Data[prefixedKey] = tools.EtcdResponseWithError{
		R: &etcd.Response{
			Node: nil,
		},
		E: &etcd.EtcdError{
			Index:     2,
			ErrorCode: 100,
		},
	}
	h := newEtcdHelper(fakeClient, codec, etcdtest.PathPrefix())

	watching, err := h.Watch(context.TODO(), key, 0, storage.Everything)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	fakeClient.WaitForWatchCompletion()
	if fakeClient.WatchIndex != 3 {
		t.Errorf("Expected client to wait for %d, got %#v", 3, fakeClient)
	}

	watching.Stop()
}
コード例 #4
0
ファイル: master_test.go プロジェクト: qinguoan/vulcan
func initThirdParty(t *testing.T, version string) (*Master, *tools.FakeEtcdClient, *httptest.Server, *assert.Assertions) {
	master, _, assert := setUp(t)
	master.thirdPartyResources = map[string]*thirdpartyresourcedatastorage.REST{}
	api := &extensions.ThirdPartyResource{
		ObjectMeta: api.ObjectMeta{
			Name: "foo.company.com",
		},
		Versions: []extensions.APIVersion{
			{
				APIGroup: "group",
				Name:     version,
			},
		},
	}
	master.handlerContainer = restful.NewContainer()

	fakeClient := tools.NewFakeEtcdClient(t)
	fakeClient.Machines = []string{"http://machine1:4001", "http://machine2", "http://machine3:4003"}
	master.thirdPartyStorage = etcdstorage.NewEtcdStorage(fakeClient, testapi.Extensions.Codec(), etcdtest.PathPrefix())

	if !assert.NoError(master.InstallThirdPartyResource(api)) {
		t.FailNow()
	}

	server := httptest.NewServer(master.handlerContainer.ServeMux)
	return &master, fakeClient, server, assert
}
コード例 #5
0
ファイル: etcd_helper_test.go プロジェクト: qinguoan/vulcan
func TestSetWithoutResourceVersioner(t *testing.T) {
	obj := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}
	fakeClient := tools.NewFakeEtcdClient(t)
	helper := newEtcdHelper(fakeClient, testapi.Default.Codec(), etcdtest.PathPrefix())
	helper.versioner = nil
	returnedObj := &api.Pod{}
	err := helper.Set("/some/key", obj, returnedObj, 3)
	key := etcdtest.AddPrefix("/some/key")
	if err != nil {
		t.Errorf("Unexpected error %#v", err)
	}
	data, err := testapi.Default.Codec().Encode(obj)
	if err != nil {
		t.Errorf("Unexpected error %#v", err)
	}
	expect := string(data)
	got := fakeClient.Data[key].R.Node.Value
	if expect != got {
		t.Errorf("Wanted %v, got %v", expect, got)
	}
	if e, a := uint64(3), fakeClient.LastSetTTL; e != a {
		t.Errorf("Wanted %v, got %v", e, a)
	}
	if obj.ResourceVersion != returnedObj.ResourceVersion || obj.Name != returnedObj.Name {
		t.Errorf("If set was successful but returned object did not have correct resource version")
	}
}
コード例 #6
0
ファイル: etcd_watcher_test.go プロジェクト: qinguoan/vulcan
func TestWatchEtcdError(t *testing.T) {
	codec := testapi.Default.Codec()
	fakeClient := tools.NewFakeEtcdClient(t)
	fakeClient.ExpectNotFoundGet("/some/key")
	fakeClient.WatchImmediateError = fmt.Errorf("immediate error")
	h := newEtcdHelper(fakeClient, codec, etcdtest.PathPrefix())

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

	got := <-watching.ResultChan()
	if got.Type != watch.Error {
		t.Fatalf("Unexpected non-error")
	}
	status, ok := got.Object.(*unversioned.Status)
	if !ok {
		t.Fatalf("Unexpected non-error object type")
	}
	if status.Message != "immediate error" {
		t.Errorf("Unexpected wrong error")
	}
	if status.Status != unversioned.StatusFailure {
		t.Errorf("Unexpected wrong error status")
	}
}
コード例 #7
0
ファイル: etcd_helper_test.go プロジェクト: qinguoan/vulcan
func TestGuaranteedUpdateNoChange(t *testing.T) {
	fakeClient := tools.NewFakeEtcdClient(t)
	fakeClient.TestIndex = true
	helper := newEtcdHelper(fakeClient, codec, etcdtest.PathPrefix())
	key := etcdtest.AddPrefix("/some/key")

	// Create a new node.
	fakeClient.ExpectNotFoundGet(key)
	obj := &TestResource{ObjectMeta: api.ObjectMeta{Name: "foo"}, Value: 1}
	err := helper.GuaranteedUpdate("/some/key", &TestResource{}, true, 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 := &TestResource{ObjectMeta: api.ObjectMeta{Name: "foo"}, Value: 1}
	err = helper.GuaranteedUpdate("/some/key", &TestResource{}, true, storage.SimpleUpdate(func(in runtime.Object) (runtime.Object, error) {
		fakeClient.Err = errors.New("should not be called")
		callbackCalled = true
		return objUpdate, nil
	}))
	if err != nil {
		t.Fatalf("Unexpected error %#v", err)
	}
	if !callbackCalled {
		t.Errorf("tryUpdate callback should have been called.")
	}
}
コード例 #8
0
ファイル: etcd_helper_test.go プロジェクト: qinguoan/vulcan
func TestGuaranteedUpdateKeyNotFound(t *testing.T) {
	fakeClient := tools.NewFakeEtcdClient(t)
	fakeClient.TestIndex = true
	helper := newEtcdHelper(fakeClient, codec, etcdtest.PathPrefix())
	key := etcdtest.AddPrefix("/some/key")

	// Create a new node.
	fakeClient.ExpectNotFoundGet(key)
	obj := &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("/some/key", &TestResource{}, ignoreNotFound, f)
	if err == nil {
		t.Errorf("Expected error for key not found.")
	}

	ignoreNotFound = true
	err = helper.GuaranteedUpdate("/some/key", &TestResource{}, ignoreNotFound, f)
	if err != nil {
		t.Errorf("Unexpected error %v.", err)
	}
}
コード例 #9
0
ファイル: cacher_test.go プロジェクト: qinguoan/vulcan
func TestStorageError(t *testing.T) {
	fakeClient := tools.NewFakeEtcdClient(t)
	prefixedKey := etcdtest.AddPrefix("pods")
	fakeClient.ExpectNotFoundGet(prefixedKey)
	cacher := newTestCacher(fakeClient)
	fakeClient.WaitForWatchCompletion()

	podFoo := makeTestPod("foo")

	// Set up Watch for object "podFoo".
	watcher, err := cacher.Watch(context.TODO(), "pods/ns/foo", 1, storage.Everything)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	fakeClient.WatchResponse <- &etcd.Response{
		Action: "create",
		Node: &etcd.Node{
			Value:         string(runtime.EncodeOrDie(testapi.Default.Codec(), podFoo)),
			CreatedIndex:  1,
			ModifiedIndex: 1,
		},
	}
	_ = <-watcher.ResultChan()

	// Injecting error is simulating error from etcd.
	// This is almost the same what would happen e.g. in case of
	// "error too old" when reconnecting to etcd watch.
	fakeClient.WatchInjectError <- fmt.Errorf("fake error")

	_, ok := <-watcher.ResultChan()
	if ok {
		t.Errorf("unexpected event")
	}
}
コード例 #10
0
ファイル: etcd_watcher_test.go プロジェクト: qinguoan/vulcan
func TestWatch(t *testing.T) {
	codec := testapi.Default.Codec()
	fakeClient := tools.NewFakeEtcdClient(t)
	key := "/some/key"
	prefixedKey := etcdtest.AddPrefix(key)
	fakeClient.ExpectNotFoundGet(prefixedKey)
	h := newEtcdHelper(fakeClient, codec, etcdtest.PathPrefix())

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

	fakeClient.WaitForWatchCompletion()
	// when server returns not found, the watch index starts at the next value (1)
	if fakeClient.WatchIndex != 1 {
		t.Errorf("Expected client to be at index %d, got %#v", 1, fakeClient)
	}

	// Test normal case
	pod := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}
	podBytes, _ := codec.Encode(pod)
	fakeClient.WatchResponse <- &etcd.Response{
		Action: "set",
		Node: &etcd.Node{
			Value: string(podBytes),
		},
	}

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

	// Test error case
	fakeClient.WatchInjectError <- fmt.Errorf("Injected error")

	if errEvent, ok := <-watching.ResultChan(); !ok {
		t.Errorf("no error result?")
	} else {
		if e, a := watch.Error, errEvent.Type; e != a {
			t.Errorf("Expected %v, got %v", e, a)
		}
		if e, a := "Injected error", errEvent.Object.(*unversioned.Status).Message; e != a {
			t.Errorf("Expected %v, got %v", e, a)
		}
	}

	// Did everything shut down?
	if _, open := <-fakeClient.WatchResponse; open {
		t.Errorf("An injected error did not cause a graceful shutdown")
	}
	if _, open := <-watching.ResultChan(); open {
		t.Errorf("An injected error did not cause a graceful shutdown")
	}
}
コード例 #11
0
ファイル: etcd_helper_test.go プロジェクト: qinguoan/vulcan
func TestCreateNilOutParam(t *testing.T) {
	obj := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}
	fakeClient := tools.NewFakeEtcdClient(t)
	helper := newEtcdHelper(fakeClient, testapi.Default.Codec(), etcdtest.PathPrefix())
	err := helper.Create("/some/key", obj, nil, 5)
	if err != nil {
		t.Errorf("Unexpected error %#v", err)
	}
}
コード例 #12
0
ファイル: etcd_helper_test.go プロジェクト: qinguoan/vulcan
func TestSetFailCAS(t *testing.T) {
	obj := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "1"}}
	fakeClient := tools.NewFakeEtcdClient(t)
	fakeClient.CasErr = fakeClient.NewError(123)
	helper := newEtcdHelper(fakeClient, testapi.Default.Codec(), etcdtest.PathPrefix())
	err := helper.Set("/some/key", obj, nil, 5)
	if err == nil {
		t.Errorf("Expecting error.")
	}
}
コード例 #13
0
ファイル: etcd_helper_test.go プロジェクト: qinguoan/vulcan
func TestGuaranteedUpdate(t *testing.T) {
	fakeClient := tools.NewFakeEtcdClient(t)
	fakeClient.TestIndex = true
	helper := newEtcdHelper(fakeClient, codec, etcdtest.PathPrefix())
	key := etcdtest.AddPrefix("/some/key")

	// Create a new node.
	fakeClient.ExpectNotFoundGet(key)
	obj := &TestResource{ObjectMeta: api.ObjectMeta{Name: "foo"}, Value: 1}
	err := helper.GuaranteedUpdate("/some/key", &TestResource{}, true, storage.SimpleUpdate(func(in runtime.Object) (runtime.Object, error) {
		return obj, nil
	}))
	if err != nil {
		t.Errorf("Unexpected error %#v", err)
	}
	data, err := codec.Encode(obj)
	if err != nil {
		t.Errorf("Unexpected error %#v", err)
	}
	expect := string(data)
	got := fakeClient.Data[key].R.Node.Value
	if expect != got {
		t.Errorf("Wanted %v, got %v", expect, got)
	}

	// Update an existing node.
	callbackCalled := false
	objUpdate := &TestResource{ObjectMeta: api.ObjectMeta{Name: "foo"}, Value: 2}
	err = helper.GuaranteedUpdate("/some/key", &TestResource{}, true, storage.SimpleUpdate(func(in runtime.Object) (runtime.Object, error) {
		callbackCalled = true

		if in.(*TestResource).Value != 1 {
			t.Errorf("Callback input was not current set value")
		}

		return objUpdate, nil
	}))
	if err != nil {
		t.Errorf("Unexpected error %#v", err)
	}
	data, err = codec.Encode(objUpdate)
	if err != nil {
		t.Errorf("Unexpected error %#v", err)
	}
	expect = string(data)
	got = fakeClient.Data[key].R.Node.Value
	if expect != got {
		t.Errorf("Wanted %v, got %v", expect, got)
	}

	if !callbackCalled {
		t.Errorf("tryUpdate callback should have been called.")
	}
}
コード例 #14
0
ファイル: etcd_master_test.go プロジェクト: qinguoan/vulcan
func TestEtcdMasterOther(t *testing.T) {
	path := "foo"
	etcd := tools.NewFakeEtcdClient(t)
	etcd.Set(path, "baz", 0)
	master := NewEtcdMasterElector(etcd)
	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()
}
コード例 #15
0
ファイル: master_test.go プロジェクト: qinguoan/vulcan
// TestNewEtcdStorage verifies that the usage of NewEtcdStorage reacts properly when
// the correct data is input
func TestNewEtcdStorage(t *testing.T) {
	assert := assert.New(t)
	fakeClient := tools.NewFakeEtcdClient(t)
	// Pass case
	_, err := NewEtcdStorage(fakeClient, 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(fakeClient, errorFunc, testapi.Default.Version(), etcdtest.PathPrefix())
	assert.Error(err, "NewEtcdStorage should have failed")

}
コード例 #16
0
ファイル: etcd_helper_test.go プロジェクト: qinguoan/vulcan
func TestGuaranteedUpdate_CreateCollision(t *testing.T) {
	fakeClient := tools.NewFakeEtcdClient(t)
	fakeClient.TestIndex = true
	helper := newEtcdHelper(fakeClient, codec, etcdtest.PathPrefix())
	key := etcdtest.AddPrefix("/some/key")

	fakeClient.ExpectNotFoundGet(key)

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

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

			firstCall := true
			err := helper.GuaranteedUpdate("/some/key", &TestResource{}, true, 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.(*TestResource).Value
				obj := &TestResource{ObjectMeta: api.ObjectMeta{Name: "foo"}, Value: currValue + 1}
				return obj, nil
			}))
			if err != nil {
				t.Errorf("Unexpected error %#v", err)
			}
		}()
	}
	wgDone.Wait()

	// Check that stored TestResource has received all updates.
	body := fakeClient.Data[key].R.Node.Value
	stored := &TestResource{}
	if err := codec.DecodeInto([]byte(body), stored); err != nil {
		t.Errorf("Error decoding stored value: %v", body)
	}
	if stored.Value != concurrency {
		t.Errorf("Some of the writes were lost. Stored value: %d", stored.Value)
	}
}
コード例 #17
0
ファイル: master_test.go プロジェクト: qinguoan/vulcan
// setUp is a convience function for setting up for (most) tests.
func setUp(t *testing.T) (Master, Config, *assert.Assertions) {
	master := Master{}
	config := Config{}
	fakeClient := tools.NewFakeEtcdClient(t)
	fakeClient.Machines = []string{"http://machine1:4001", "http://machine2", "http://machine3:4003"}
	storageVersions := make(map[string]string)
	storageDestinations := NewStorageDestinations()
	storageDestinations.AddAPIGroup("", etcdstorage.NewEtcdStorage(fakeClient, testapi.Default.Codec(), etcdtest.PathPrefix()))
	storageDestinations.AddAPIGroup("extensions", etcdstorage.NewEtcdStorage(fakeClient, testapi.Extensions.Codec(), etcdtest.PathPrefix()))
	config.StorageDestinations = storageDestinations
	storageVersions[""] = testapi.Default.Version()
	storageVersions["extensions"] = testapi.Extensions.GroupAndVersion()
	config.StorageVersions = storageVersions
	master.nodeRegistry = registrytest.NewNodeRegistry([]string{"node1", "node2"}, api.NodeResources{})

	return master, config, assert.New(t)
}
コード例 #18
0
ファイル: etcd_watcher_test.go プロジェクト: qinguoan/vulcan
func TestWatchListIgnoresRootKey(t *testing.T) {
	codec := testapi.Default.Codec()
	pod := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}
	key := "/some/key"
	prefixedKey := etcdtest.AddPrefix(key)

	fakeClient := tools.NewFakeEtcdClient(t)
	h := newEtcdHelper(fakeClient, codec, etcdtest.PathPrefix())

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

	// This is the root directory of the watch, which happens to have a value encoded
	fakeClient.WatchResponse <- &etcd.Response{
		Action: "delete",
		PrevNode: &etcd.Node{
			Key:           prefixedKey,
			Value:         runtime.EncodeOrDie(codec, pod),
			CreatedIndex:  1,
			ModifiedIndex: 1,
		},
	}
	// Delete of the parent directory of a key is an event that a list watch would receive,
	// but will have no value so the decode will fail.
	fakeClient.WatchResponse <- &etcd.Response{
		Action: "delete",
		PrevNode: &etcd.Node{
			Key:           prefixedKey,
			Value:         "",
			CreatedIndex:  1,
			ModifiedIndex: 1,
		},
	}
	close(fakeClient.WatchStop)

	// the existing node is detected and the index set
	_, open := <-watching.ResultChan()
	if open {
		t.Fatalf("unexpected channel open")
	}

	watching.Stop()
}
コード例 #19
0
ファイル: etcd_helper_test.go プロジェクト: qinguoan/vulcan
func TestPrefixEtcdKey(t *testing.T) {
	fakeClient := tools.NewFakeEtcdClient(t)
	prefix := path.Join("/", etcdtest.PathPrefix())
	helper := newEtcdHelper(fakeClient, testapi.Default.Codec(), prefix)

	baseKey := "/some/key"

	// Verify prefix is added
	keyBefore := baseKey
	keyAfter := helper.prefixEtcdKey(keyBefore)

	assert.Equal(t, keyAfter, path.Join(prefix, baseKey), "Prefix incorrectly added by EtcdHelper")

	// Verify prefix is not added
	keyBefore = path.Join(prefix, baseKey)
	keyAfter = helper.prefixEtcdKey(keyBefore)

	assert.Equal(t, keyBefore, keyAfter, "Prefix incorrectly added by EtcdHelper")
}
コード例 #20
0
ファイル: etcd_helper_test.go プロジェクト: qinguoan/vulcan
func TestGetNotFoundErr(t *testing.T) {
	fakeClient := tools.NewFakeEtcdClient(t)
	helper := newEtcdHelper(fakeClient, testapi.Default.Codec(), etcdtest.PathPrefix())
	key1 := etcdtest.AddPrefix("/some/key")
	fakeClient.Data[key1] = tools.EtcdResponseWithError{
		R: &etcd.Response{
			Node: nil,
		},
		E: &etcd.EtcdError{
			ErrorCode: 100,
		},
	}
	key2 := etcdtest.AddPrefix("/some/key2")
	fakeClient.Data[key2] = tools.EtcdResponseWithError{
		R: &etcd.Response{
			Node: nil,
		},
	}
	key3 := etcdtest.AddPrefix("/some/key3")
	fakeClient.Data[key3] = tools.EtcdResponseWithError{
		R: &etcd.Response{
			Node: &etcd.Node{
				Value: "",
			},
		},
	}
	try := func(key string) {
		var got api.Pod
		err := helper.Get(key, &got, false)
		if err == nil {
			t.Errorf("%s: wanted error but didn't get one", key)
		}
		err = helper.Get(key, &got, true)
		if err != nil {
			t.Errorf("%s: didn't want error but got %#v", key, err)
		}
	}

	try("/some/key")
	try("/some/key2")
	try("/some/key3")
}
コード例 #21
0
ファイル: etcd_master_test.go プロジェクト: qinguoan/vulcan
func TestEtcdMasterNoOther(t *testing.T) {
	path := "foo"
	e := tools.NewFakeEtcdClient(t)
	e.TestIndex = true
	e.Data["foo"] = tools.EtcdResponseWithError{
		R: &etcd.Response{
			Node: nil,
		},
		E: &etcd.EtcdError{
			ErrorCode: tools.EtcdErrorCodeNotFound,
		},
	}
	master := NewEtcdMasterElector(e)
	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()
}
コード例 #22
0
ファイル: etcd_watcher_test.go プロジェクト: qinguoan/vulcan
func TestWatchFromOtherError(t *testing.T) {
	fakeClient := tools.NewFakeEtcdClient(t)
	key := "/some/key"
	prefixedKey := etcdtest.AddPrefix(key)
	fakeClient.Data[prefixedKey] = tools.EtcdResponseWithError{
		R: &etcd.Response{
			Node: nil,
		},
		E: &etcd.EtcdError{
			Index:     2,
			ErrorCode: 101,
		},
	}
	h := newEtcdHelper(fakeClient, codec, etcdtest.PathPrefix())

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

	errEvent := <-watching.ResultChan()
	if e, a := watch.Error, errEvent.Type; e != a {
		t.Errorf("Expected %v, got %v", e, a)
	}
	if e, a := "101:  () [2]", errEvent.Object.(*unversioned.Status).Message; e != a {
		t.Errorf("Expected %v, got %v", e, a)
	}

	select {
	case _, ok := <-watching.ResultChan():
		if ok {
			t.Fatalf("expected result channel to be closed")
		}
	case <-time.After(util.ForeverTestTimeout):
		t.Fatalf("watch should have closed channel: %#v", watching)
	}

	if fakeClient.WatchResponse != nil || fakeClient.WatchIndex != 0 {
		t.Fatalf("Watch should not have been invoked: %#v", fakeClient)
	}
}
コード例 #23
0
ファイル: etcd_master_test.go プロジェクト: qinguoan/vulcan
func TestEtcdMasterNoOtherThenConflict(t *testing.T) {
	path := "foo"
	e := tools.NewFakeEtcdClient(t)
	e.TestIndex = true
	// Ok, so we set up a chain of responses from etcd:
	//   1) Nothing there
	//   2) conflict (someone else wrote)
	//   3) new value (the data they wrote)
	empty := tools.EtcdResponseWithError{
		R: &etcd.Response{
			Node: nil,
		},
		E: &etcd.EtcdError{
			ErrorCode: tools.EtcdErrorCodeNotFound,
		},
	}
	empty.N = &tools.EtcdResponseWithError{
		R: &etcd.Response{},
		E: &etcd.EtcdError{
			ErrorCode: tools.EtcdErrorCodeNodeExist,
		},
	}
	empty.N.N = &tools.EtcdResponseWithError{
		R: &etcd.Response{
			Node: &etcd.Node{
				Value: "baz",
			},
		},
	}
	e.Data["foo"] = empty
	master := NewEtcdMasterElector(e)
	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()
}
コード例 #24
0
ファイル: etcd_helper_test.go プロジェクト: qinguoan/vulcan
func TestGet(t *testing.T) {
	fakeClient := tools.NewFakeEtcdClient(t)
	helper := newEtcdHelper(fakeClient, testapi.Default.Codec(), etcdtest.PathPrefix())
	key := etcdtest.AddPrefix("/some/key")
	grace := int64(30)
	expect := api.Pod{
		ObjectMeta: api.ObjectMeta{Name: "foo"},
		Spec: api.PodSpec{
			RestartPolicy:                 api.RestartPolicyAlways,
			DNSPolicy:                     api.DNSClusterFirst,
			TerminationGracePeriodSeconds: &grace,
		},
	}
	fakeClient.Set(key, runtime.EncodeOrDie(testapi.Default.Codec(), &expect), 0)
	var got api.Pod
	err := helper.Get("/some/key", &got, false)
	if err != nil {
		t.Errorf("Unexpected error %#v", err)
	}
	if !reflect.DeepEqual(got, expect) {
		t.Errorf("Wanted %#v, got %#v", expect, got)
	}
}
コード例 #25
0
ファイル: etcd_helper_test.go プロジェクト: qinguoan/vulcan
func TestSetWithVersion(t *testing.T) {
	obj := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "1"}}
	fakeClient := tools.NewFakeEtcdClient(t)
	fakeClient.TestIndex = true
	helper := newEtcdHelper(fakeClient, testapi.Default.Codec(), etcdtest.PathPrefix())
	key := etcdtest.AddPrefix("/some/key")
	fakeClient.Data[key] = tools.EtcdResponseWithError{
		R: &etcd.Response{
			Node: &etcd.Node{
				Value:         runtime.EncodeOrDie(testapi.Default.Codec(), obj),
				ModifiedIndex: 1,
			},
		},
	}

	returnedObj := &api.Pod{}
	err := helper.Set("/some/key", obj, returnedObj, 7)
	if err != nil {
		t.Fatalf("Unexpected error %#v", err)
	}
	data, err := testapi.Default.Codec().Encode(obj)
	if err != nil {
		t.Fatalf("Unexpected error %#v", err)
	}
	expect := string(data)
	got := fakeClient.Data[key].R.Node.Value
	if expect != got {
		t.Errorf("Wanted %v, got %v", expect, got)
	}
	if e, a := uint64(7), fakeClient.LastSetTTL; e != a {
		t.Errorf("Wanted %v, got %v", e, a)
	}
	if obj.ResourceVersion != returnedObj.ResourceVersion || obj.Name != returnedObj.Name {
		t.Errorf("If set was successful but returned object did not have correct resource version")
	}
}
コード例 #26
0
ファイル: etcd_helper_test.go プロジェクト: qinguoan/vulcan
func TestList(t *testing.T) {
	fakeClient := tools.NewFakeEtcdClient(t)
	helper := newEtcdHelper(fakeClient, testapi.Default.Codec(), etcdtest.PathPrefix())
	key := etcdtest.AddPrefix("/some/key")
	fakeClient.Data[key] = tools.EtcdResponseWithError{
		R: &etcd.Response{
			EtcdIndex: 10,
			Node: &etcd.Node{
				Dir: true,
				Nodes: []*etcd.Node{
					{
						Key:           "/foo",
						Value:         getEncodedPod("foo"),
						Dir:           false,
						ModifiedIndex: 1,
					},
					{
						Key:           "/bar",
						Value:         getEncodedPod("bar"),
						Dir:           false,
						ModifiedIndex: 2,
					},
					{
						Key:           "/baz",
						Value:         getEncodedPod("baz"),
						Dir:           false,
						ModifiedIndex: 3,
					},
				},
			},
		},
	}
	grace := int64(30)
	expect := api.PodList{
		ListMeta: unversioned.ListMeta{ResourceVersion: "10"},
		Items: []api.Pod{
			{
				ObjectMeta: api.ObjectMeta{Name: "bar", ResourceVersion: "2"},
				Spec: api.PodSpec{
					RestartPolicy:                 api.RestartPolicyAlways,
					DNSPolicy:                     api.DNSClusterFirst,
					TerminationGracePeriodSeconds: &grace,
				},
			},
			{
				ObjectMeta: api.ObjectMeta{Name: "baz", ResourceVersion: "3"},
				Spec: api.PodSpec{
					RestartPolicy:                 api.RestartPolicyAlways,
					DNSPolicy:                     api.DNSClusterFirst,
					TerminationGracePeriodSeconds: &grace,
				},
			},
			{
				ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "1"},
				Spec: api.PodSpec{
					RestartPolicy:                 api.RestartPolicyAlways,
					DNSPolicy:                     api.DNSClusterFirst,
					TerminationGracePeriodSeconds: &grace,
				},
			},
		},
	}

	var got api.PodList
	err := helper.List("/some/key", storage.Everything, &got)
	if err != nil {
		t.Errorf("Unexpected error %v", err)
	}
	if e, a := expect, got; !reflect.DeepEqual(e, a) {
		t.Errorf("Expected %#v, got %#v", e, a)
	}
}
コード例 #27
0
ファイル: etcd_helper_test.go プロジェクト: qinguoan/vulcan
func TestGuaranteedUpdateTTL(t *testing.T) {
	fakeClient := tools.NewFakeEtcdClient(t)
	fakeClient.TestIndex = true
	helper := newEtcdHelper(fakeClient, codec, etcdtest.PathPrefix())
	key := etcdtest.AddPrefix("/some/key")

	// Create a new node.
	fakeClient.ExpectNotFoundGet(key)
	obj := &TestResource{ObjectMeta: api.ObjectMeta{Name: "foo"}, Value: 1}
	err := helper.GuaranteedUpdate("/some/key", &TestResource{}, true, func(in runtime.Object, res storage.ResponseMeta) (runtime.Object, *uint64, error) {
		if res.TTL != 0 {
			t.Fatalf("unexpected response meta: %#v", res)
		}
		ttl := uint64(10)
		return obj, &ttl, nil
	})
	if err != nil {
		t.Errorf("Unexpected error %#v", err)
	}
	data, err := codec.Encode(obj)
	if err != nil {
		t.Errorf("Unexpected error %#v", err)
	}
	expect := string(data)
	got := fakeClient.Data[key].R.Node.Value
	if expect != got {
		t.Errorf("Wanted %v, got %v", expect, got)
	}
	if fakeClient.Data[key].R.Node.TTL != 10 {
		t.Errorf("expected TTL set: %d", fakeClient.Data[key].R.Node.TTL)
	}

	// Update an existing node.
	callbackCalled := false
	objUpdate := &TestResource{ObjectMeta: api.ObjectMeta{Name: "foo"}, Value: 2}
	err = helper.GuaranteedUpdate("/some/key", &TestResource{}, true, func(in runtime.Object, res storage.ResponseMeta) (runtime.Object, *uint64, error) {
		if res.TTL != 10 {
			t.Fatalf("unexpected response meta: %#v", res)
		}
		callbackCalled = true

		if in.(*TestResource).Value != 1 {
			t.Errorf("Callback input was not current set value")
		}

		return objUpdate, nil, nil
	})

	if err != nil {
		t.Errorf("Unexpected error %#v", err)
	}
	data, err = codec.Encode(objUpdate)
	if err != nil {
		t.Errorf("Unexpected error %#v", err)
	}
	expect = string(data)
	got = fakeClient.Data[key].R.Node.Value
	if expect != got {
		t.Errorf("Wanted %v, got %v", expect, got)
	}
	if fakeClient.Data[key].R.Node.TTL != 10 {
		t.Errorf("expected TTL remained set: %d", fakeClient.Data[key].R.Node.TTL)
	}

	// Update an existing node and change ttl
	callbackCalled = false
	objUpdate = &TestResource{ObjectMeta: api.ObjectMeta{Name: "foo"}, Value: 3}
	err = helper.GuaranteedUpdate("/some/key", &TestResource{}, true, func(in runtime.Object, res storage.ResponseMeta) (runtime.Object, *uint64, error) {
		if res.TTL != 10 {
			t.Fatalf("unexpected response meta: %#v", res)
		}
		callbackCalled = true

		if in.(*TestResource).Value != 2 {
			t.Errorf("Callback input was not current set value")
		}

		newTTL := uint64(20)
		return objUpdate, &newTTL, nil
	})
	if err != nil {
		t.Errorf("Unexpected error %#v", err)
	}
	data, err = codec.Encode(objUpdate)
	if err != nil {
		t.Errorf("Unexpected error %#v", err)
	}
	expect = string(data)
	got = fakeClient.Data[key].R.Node.Value
	if expect != got {
		t.Errorf("Wanted %v, got %v", expect, got)
	}
	if fakeClient.Data[key].R.Node.TTL != 20 {
		t.Errorf("expected TTL changed: %d", fakeClient.Data[key].R.Node.TTL)
	}

	if !callbackCalled {
		t.Errorf("tryUpdate callback should have been called.")
	}
}
コード例 #28
0
ファイル: cacher_test.go プロジェクト: qinguoan/vulcan
func TestWatch(t *testing.T) {
	fakeClient := tools.NewFakeEtcdClient(t)
	prefixedKey := etcdtest.AddPrefix("pods")
	fakeClient.ExpectNotFoundGet(prefixedKey)
	cacher := newTestCacher(fakeClient)
	fakeClient.WaitForWatchCompletion()

	podFoo := makeTestPod("foo")
	podBar := makeTestPod("bar")

	testCases := []struct {
		object       *api.Pod
		etcdResponse *etcd.Response
		event        watch.EventType
		filtered     bool
	}{
		{
			object: podFoo,
			etcdResponse: &etcd.Response{
				Action: "create",
				Node: &etcd.Node{
					Value:         string(runtime.EncodeOrDie(testapi.Default.Codec(), podFoo)),
					CreatedIndex:  2,
					ModifiedIndex: 2,
				},
			},
			event:    watch.Added,
			filtered: true,
		},
		{
			object: podBar,
			etcdResponse: &etcd.Response{
				Action: "create",
				Node: &etcd.Node{
					Value:         string(runtime.EncodeOrDie(testapi.Default.Codec(), podBar)),
					CreatedIndex:  3,
					ModifiedIndex: 3,
				},
			},
			event:    watch.Added,
			filtered: false,
		},
		{
			object: podFoo,
			etcdResponse: &etcd.Response{
				Action: "set",
				Node: &etcd.Node{
					Value:         string(runtime.EncodeOrDie(testapi.Default.Codec(), podFoo)),
					CreatedIndex:  2,
					ModifiedIndex: 4,
				},
				PrevNode: &etcd.Node{
					Value:         string(runtime.EncodeOrDie(testapi.Default.Codec(), podFoo)),
					CreatedIndex:  2,
					ModifiedIndex: 2,
				},
			},
			event:    watch.Modified,
			filtered: true,
		},
	}

	// Set up Watch for object "podFoo".
	watcher, err := cacher.Watch(context.TODO(), "pods/ns/foo", 2, storage.Everything)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	for _, test := range testCases {
		fakeClient.WatchResponse <- test.etcdResponse
		if test.filtered {
			event := <-watcher.ResultChan()
			if e, a := test.event, event.Type; e != a {
				t.Errorf("%v %v", e, a)
			}
			// unset fields that are set by the infrastructure
			obj := event.Object.(*api.Pod)
			obj.ObjectMeta.ResourceVersion = ""
			obj.ObjectMeta.CreationTimestamp = unversioned.Time{}
			if e, a := test.object, obj; !reflect.DeepEqual(e, a) {
				t.Errorf("expected: %#v, got: %#v", e, a)
			}
		}
	}

	// Check whether we get too-old error.
	_, err = cacher.Watch(context.TODO(), "pods/ns/foo", 1, storage.Everything)
	if err == nil {
		t.Errorf("exepcted 'error too old' error")
	}

	// Now test watch with initial state.
	initialWatcher, err := cacher.Watch(context.TODO(), "pods/ns/foo", 2, storage.Everything)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	for _, test := range testCases {
		if test.filtered {
			event := <-initialWatcher.ResultChan()
			if e, a := test.event, event.Type; e != a {
				t.Errorf("%v %v", e, a)
			}
			// unset fields that are set by the infrastructure
			obj := event.Object.(*api.Pod)
			obj.ObjectMeta.ResourceVersion = ""
			obj.ObjectMeta.CreationTimestamp = unversioned.Time{}
			if e, a := test.object, obj; !reflect.DeepEqual(e, a) {
				t.Errorf("expected: %#v, got: %#v", e, a)
			}
		}
	}

	// Now test watch from "now".
	nowWatcher, err := cacher.Watch(context.TODO(), "pods/ns/foo", 0, storage.Everything)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	select {
	case event := <-nowWatcher.ResultChan():
		if obj := event.Object.(*api.Pod); event.Type != watch.Added || obj.ResourceVersion != "4" {
			t.Errorf("unexpected event: %v", event)
		}
	case <-time.After(util.ForeverTestTimeout):
		t.Errorf("timed out waiting for an event")
	}
	// Emit a new event and check if it is observed by the watcher.
	fakeClient.WatchResponse <- &etcd.Response{
		Action: "set",
		Node: &etcd.Node{
			Value:         string(runtime.EncodeOrDie(testapi.Default.Codec(), podFoo)),
			CreatedIndex:  2,
			ModifiedIndex: 5,
		},
		PrevNode: &etcd.Node{
			Value:         string(runtime.EncodeOrDie(testapi.Default.Codec(), podFoo)),
			CreatedIndex:  2,
			ModifiedIndex: 4,
		},
	}
	event := <-nowWatcher.ResultChan()
	obj := event.Object.(*api.Pod)
	if event.Type != watch.Modified || obj.ResourceVersion != "5" {
		t.Errorf("unexpected event: %v", event)
	}

	close(fakeClient.WatchResponse)
}
コード例 #29
0
ファイル: cacher_test.go プロジェクト: qinguoan/vulcan
func TestListFromMemory(t *testing.T) {
	fakeClient := tools.NewFakeEtcdClient(t)
	prefixedKey := etcdtest.AddPrefix("pods")
	fakeClient.ExpectNotFoundGet(prefixedKey)
	cacher := newTestCacher(fakeClient)
	fakeClient.WaitForWatchCompletion()

	podFoo := makeTestPod("foo")
	podBar := makeTestPod("bar")
	podBaz := makeTestPod("baz")

	podFooPrime := makeTestPod("foo")
	podFooPrime.Spec.NodeName = "fakeNode"

	testCases := []*etcd.Response{
		{
			Action: "create",
			Node: &etcd.Node{
				Value:         string(runtime.EncodeOrDie(testapi.Default.Codec(), podFoo)),
				CreatedIndex:  1,
				ModifiedIndex: 1,
			},
		},
		{
			Action: "create",
			Node: &etcd.Node{
				Value:         string(runtime.EncodeOrDie(testapi.Default.Codec(), podBar)),
				CreatedIndex:  2,
				ModifiedIndex: 2,
			},
		},
		{
			Action: "create",
			Node: &etcd.Node{
				Value:         string(runtime.EncodeOrDie(testapi.Default.Codec(), podBaz)),
				CreatedIndex:  3,
				ModifiedIndex: 3,
			},
		},
		{
			Action: "set",
			Node: &etcd.Node{
				Value:         string(runtime.EncodeOrDie(testapi.Default.Codec(), podFooPrime)),
				CreatedIndex:  1,
				ModifiedIndex: 4,
			},
			PrevNode: &etcd.Node{
				Value:         string(runtime.EncodeOrDie(testapi.Default.Codec(), podFoo)),
				CreatedIndex:  1,
				ModifiedIndex: 1,
			},
		},
		{
			Action: "delete",
			Node: &etcd.Node{
				CreatedIndex:  1,
				ModifiedIndex: 5,
			},
			PrevNode: &etcd.Node{
				Value:         string(runtime.EncodeOrDie(testapi.Default.Codec(), podBar)),
				CreatedIndex:  1,
				ModifiedIndex: 1,
			},
		},
	}

	// Propagate some data to etcd.
	for _, test := range testCases {
		fakeClient.WatchResponse <- test
	}
	if err := waitForUpToDateCache(cacher, 5); err != nil {
		t.Errorf("watch cache didn't propagated correctly: %v", err)
	}

	result := &api.PodList{}
	if err := cacher.ListFromMemory("pods/ns", result); err != nil {
		t.Errorf("unexpected error: %v", err)
	}
	if result.ListMeta.ResourceVersion != "5" {
		t.Errorf("incorrect resource version: %v", result.ListMeta.ResourceVersion)
	}
	if len(result.Items) != 2 {
		t.Errorf("unexpected list result: %d", len(result.Items))
	}
	keys := sets.String{}
	for _, item := range result.Items {
		keys.Insert(item.ObjectMeta.Name)
	}
	if !keys.HasAll("foo", "baz") {
		t.Errorf("unexpected list result: %#v", result)
	}
	for _, item := range result.Items {
		// unset fields that are set by the infrastructure
		item.ObjectMeta.ResourceVersion = ""
		item.ObjectMeta.CreationTimestamp = unversioned.Time{}

		var expected *api.Pod
		switch item.ObjectMeta.Name {
		case "foo":
			expected = podFooPrime
		case "baz":
			expected = podBaz
		default:
			t.Errorf("unexpected item: %v", item)
		}
		if e, a := *expected, item; !reflect.DeepEqual(e, a) {
			t.Errorf("expected: %#v, got: %#v", e, a)
		}
	}

	close(fakeClient.WatchResponse)
}
コード例 #30
0
ファイル: cacher_test.go プロジェクト: qinguoan/vulcan
func TestFiltering(t *testing.T) {
	fakeClient := tools.NewFakeEtcdClient(t)
	prefixedKey := etcdtest.AddPrefix("pods")
	fakeClient.ExpectNotFoundGet(prefixedKey)
	cacher := newTestCacher(fakeClient)
	fakeClient.WaitForWatchCompletion()

	podFoo := makeTestPod("foo")
	podFoo.ObjectMeta.Labels = map[string]string{"filter": "foo"}
	podFooFiltered := makeTestPod("foo")

	testCases := []struct {
		object       *api.Pod
		etcdResponse *etcd.Response
		filtered     bool
		event        watch.EventType
	}{
		{
			object: podFoo,
			etcdResponse: &etcd.Response{
				Action: "create",
				Node: &etcd.Node{
					Value:         string(runtime.EncodeOrDie(testapi.Default.Codec(), podFoo)),
					CreatedIndex:  1,
					ModifiedIndex: 1,
				},
			},
			filtered: true,
			event:    watch.Added,
		},
		{
			object: podFooFiltered,
			etcdResponse: &etcd.Response{
				Action: "set",
				Node: &etcd.Node{
					Value:         string(runtime.EncodeOrDie(testapi.Default.Codec(), podFooFiltered)),
					CreatedIndex:  1,
					ModifiedIndex: 2,
				},
				PrevNode: &etcd.Node{
					Value:         string(runtime.EncodeOrDie(testapi.Default.Codec(), podFoo)),
					CreatedIndex:  1,
					ModifiedIndex: 1,
				},
			},
			filtered: true,
			// Deleted, because the new object doesn't match filter.
			event: watch.Deleted,
		},
		{
			object: podFoo,
			etcdResponse: &etcd.Response{
				Action: "set",
				Node: &etcd.Node{
					Value:         string(runtime.EncodeOrDie(testapi.Default.Codec(), podFoo)),
					CreatedIndex:  1,
					ModifiedIndex: 3,
				},
				PrevNode: &etcd.Node{
					Value:         string(runtime.EncodeOrDie(testapi.Default.Codec(), podFooFiltered)),
					CreatedIndex:  1,
					ModifiedIndex: 2,
				},
			},
			filtered: true,
			// Added, because the previous object didn't match filter.
			event: watch.Added,
		},
		{
			object: podFoo,
			etcdResponse: &etcd.Response{
				Action: "set",
				Node: &etcd.Node{
					Value:         string(runtime.EncodeOrDie(testapi.Default.Codec(), podFoo)),
					CreatedIndex:  1,
					ModifiedIndex: 4,
				},
				PrevNode: &etcd.Node{
					Value:         string(runtime.EncodeOrDie(testapi.Default.Codec(), podFoo)),
					CreatedIndex:  1,
					ModifiedIndex: 3,
				},
			},
			filtered: true,
			event:    watch.Modified,
		},
		{
			object: podFoo,
			etcdResponse: &etcd.Response{
				Action: "delete",
				Node: &etcd.Node{
					CreatedIndex:  1,
					ModifiedIndex: 5,
				},
				PrevNode: &etcd.Node{
					Value:         string(runtime.EncodeOrDie(testapi.Default.Codec(), podFoo)),
					CreatedIndex:  1,
					ModifiedIndex: 4,
				},
			},
			filtered: true,
			event:    watch.Deleted,
		},
	}

	// Set up Watch for object "podFoo" with label filter set.
	selector := labels.SelectorFromSet(labels.Set{"filter": "foo"})
	filter := func(obj runtime.Object) bool {
		metadata, err := meta.Accessor(obj)
		if err != nil {
			t.Errorf("unexpected error: %v", err)
			return false
		}
		return selector.Matches(labels.Set(metadata.Labels()))
	}
	watcher, err := cacher.Watch(context.TODO(), "pods/ns/foo", 1, filter)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	for _, test := range testCases {
		fakeClient.WatchResponse <- test.etcdResponse
		if test.filtered {
			event := <-watcher.ResultChan()
			if e, a := test.event, event.Type; e != a {
				t.Errorf("%v %v", e, a)
			}
			// unset fields that are set by the infrastructure
			obj := event.Object.(*api.Pod)
			obj.ObjectMeta.ResourceVersion = ""
			obj.ObjectMeta.CreationTimestamp = unversioned.Time{}
			if e, a := test.object, obj; !reflect.DeepEqual(e, a) {
				t.Errorf("expected: %#v, got: %#v", e, a)
			}
		}
	}

	close(fakeClient.WatchResponse)
}