Ejemplo n.º 1
0
func TestFiltering(t *testing.T) {
	server, etcdStorage := newEtcdTestStorage(t, testapi.Default.Codec(), etcdtest.PathPrefix())
	defer server.Terminate(t)
	cacher := newTestCacher(etcdStorage)
	defer cacher.Stop()

	// Ensure that the cacher is initialized, before creating any pods,
	// so that we are sure that all events will be present in cacher.
	syncWatcher, err := cacher.Watch(context.TODO(), "pods/ns/foo", "0", storage.Everything)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	syncWatcher.Stop()

	podFoo := makeTestPod("foo")
	podFoo.Labels = map[string]string{"filter": "foo"}
	podFooFiltered := makeTestPod("foo")
	podFooPrime := makeTestPod("foo")
	podFooPrime.Labels = map[string]string{"filter": "foo"}
	podFooPrime.Spec.NodeName = "fakeNode"

	podFooNS2 := makeTestPod("foo")
	podFooNS2.Namespace += "2"
	podFooNS2.Labels = map[string]string{"filter": "foo"}

	// Create in another namespace first to make sure events from other namespaces don't get delivered
	updatePod(t, etcdStorage, podFooNS2, nil)

	fooCreated := updatePod(t, etcdStorage, podFoo, nil)
	fooFiltered := updatePod(t, etcdStorage, podFooFiltered, fooCreated)
	fooUnfiltered := updatePod(t, etcdStorage, podFoo, fooFiltered)
	_ = updatePod(t, etcdStorage, podFooPrime, fooUnfiltered)

	deleted := api.Pod{}
	if err := etcdStorage.Delete(context.TODO(), etcdtest.AddPrefix("pods/ns/foo"), &deleted, nil); err != nil {
		t.Errorf("Unexpected error: %v", err)
	}

	// Set up Watch for object "podFoo" with label filter set.
	selector := labels.SelectorFromSet(labels.Set{"filter": "foo"})
	filterFunc := 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.GetLabels()))
	}
	filter := storage.NewSimpleFilter(filterFunc, storage.NoTriggerFunc)
	watcher, err := cacher.Watch(context.TODO(), "pods/ns/foo", fooCreated.ResourceVersion, filter)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	defer watcher.Stop()

	verifyWatchEvent(t, watcher, watch.Deleted, podFooFiltered)
	verifyWatchEvent(t, watcher, watch.Added, podFoo)
	verifyWatchEvent(t, watcher, watch.Modified, podFooPrime)
	verifyWatchEvent(t, watcher, watch.Deleted, podFooPrime)
}
Ejemplo n.º 2
0
func (e *Store) createFilter(m *generic.SelectionPredicate) storage.Filter {
	filterFunc := func(obj runtime.Object) bool {
		matches, err := m.Matches(obj)
		if err != nil {
			glog.Errorf("unable to match watch: %v", err)
			return false
		}
		return matches
	}
	return storage.NewSimpleFilter(filterFunc, m.MatcherIndex)
}
Ejemplo n.º 3
0
func TestGetToList(t *testing.T) {
	ctx, store, cluster := testSetup(t)
	defer cluster.Terminate(t)
	key, storedObj := testPropogateStore(t, store, ctx, &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}})

	tests := []struct {
		key         string
		filter      func(runtime.Object) bool
		trigger     func() []storage.MatchValue
		expectedOut []*api.Pod
	}{{ // test GetToList on existing key
		key:         key,
		filter:      storage.EverythingFunc,
		trigger:     storage.NoTriggerFunc,
		expectedOut: []*api.Pod{storedObj},
	}, { // test GetToList on non-existing key
		key:         "/non-existing",
		filter:      storage.EverythingFunc,
		trigger:     storage.NoTriggerFunc,
		expectedOut: nil,
	}, { // test GetToList with filter to reject the pod
		key: "/non-existing",
		filter: func(obj runtime.Object) bool {
			pod, ok := obj.(*api.Pod)
			if !ok {
				t.Fatal("It should be able to convert obj to *api.Pod")
			}
			return pod.Name != storedObj.Name
		},
		trigger:     storage.NoTriggerFunc,
		expectedOut: nil,
	}}

	for i, tt := range tests {
		out := &api.PodList{}
		filter := storage.NewSimpleFilter(tt.filter, tt.trigger)
		err := store.GetToList(ctx, tt.key, filter, out)
		if err != nil {
			t.Fatalf("GetToList failed: %v", err)
		}
		if len(out.Items) != len(tt.expectedOut) {
			t.Errorf("#%d: length of list want=%d, get=%d", i, len(tt.expectedOut), len(out.Items))
			continue
		}
		for j, wantPod := range tt.expectedOut {
			getPod := &out.Items[j]
			if !reflect.DeepEqual(wantPod, getPod) {
				t.Errorf("#%d: pod want=%#v, get=%#v", i, wantPod, getPod)
			}
		}
	}
}
Ejemplo n.º 4
0
func (e *Store) createFilter(m generic.Matcher) storage.Filter {
	filterFunc := func(obj runtime.Object) bool {
		matches, err := m.Matches(obj)
		if err != nil {
			glog.Errorf("unable to match watch: %v", err)
			return false
		}
		if matches && e.Decorator != nil {
			if err := e.Decorator(obj); err != nil {
				glog.Errorf("unable to decorate watch: %v", err)
				return false
			}
		}
		return matches
	}
	return storage.NewSimpleFilter(filterFunc, m.MatcherIndex)
}
Ejemplo n.º 5
0
func TestListFiltered(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)
	filterFunc := func(obj runtime.Object) bool {
		pod := obj.(*api.Pod)
		return pod.Name == "bar"
	}
	filter := storage.NewSimpleFilter(filterFunc, storage.NoTriggerFunc)

	var got api.PodList
	err := helper.List(context.TODO(), key, "", filter, &got)
	if err != nil {
		t.Errorf("Unexpected error %v", err)
	}
	// Check to make certain that the filter function only returns "bar"
	if e, a := list.Items[0], got.Items[0]; !reflect.DeepEqual(e, a) {
		t.Errorf("Expected %#v, got %#v", e, a)
	}
}
Ejemplo n.º 6
0
func TestList(t *testing.T) {
	cluster := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1})
	defer cluster.Terminate(t)
	store := newStore(cluster.RandClient(), testapi.Default.Codec(), "")
	ctx := context.Background()

	// Setup storage with the following structure:
	//  /
	//   - one-level/
	//  |            - test
	//  |
	//   - two-level/
	//               - 1/
	//              |   - test
	//              |
	//               - 2/
	//                  - test
	preset := []struct {
		key       string
		obj       *api.Pod
		storedObj *api.Pod
	}{{
		key: "/one-level/test",
		obj: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}},
	}, {
		key: "/two-level/1/test",
		obj: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}},
	}, {
		key: "/two-level/2/test",
		obj: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "bar"}},
	}}

	for i, ps := range preset {
		preset[i].storedObj = &api.Pod{}
		err := store.Create(ctx, ps.key, ps.obj, preset[i].storedObj, 0)
		if err != nil {
			t.Fatalf("Set failed: %v", err)
		}
	}

	tests := []struct {
		prefix      string
		filter      func(runtime.Object) bool
		trigger     func() []storage.MatchValue
		expectedOut []*api.Pod
	}{{ // test List on existing key
		prefix:      "/one-level/",
		filter:      storage.EverythingFunc,
		trigger:     storage.NoTriggerFunc,
		expectedOut: []*api.Pod{preset[0].storedObj},
	}, { // test List on non-existing key
		prefix:      "/non-existing/",
		filter:      storage.EverythingFunc,
		trigger:     storage.NoTriggerFunc,
		expectedOut: nil,
	}, { // test List with filter
		prefix: "/one-level/",
		filter: func(obj runtime.Object) bool {
			pod, ok := obj.(*api.Pod)
			if !ok {
				t.Fatal("It should be able to convert obj to *api.Pod")
			}
			return pod.Name != preset[0].storedObj.Name
		},
		trigger:     storage.NoTriggerFunc,
		expectedOut: nil,
	}, { // test List with multiple levels of directories and expect flattened result
		prefix:      "/two-level/",
		filter:      storage.EverythingFunc,
		trigger:     storage.NoTriggerFunc,
		expectedOut: []*api.Pod{preset[1].storedObj, preset[2].storedObj},
	}}

	for i, tt := range tests {
		out := &api.PodList{}
		filter := storage.NewSimpleFilter(tt.filter, tt.trigger)
		err := store.List(ctx, tt.prefix, "0", filter, out)
		if err != nil {
			t.Fatalf("List failed: %v", err)
		}
		if len(tt.expectedOut) != len(out.Items) {
			t.Errorf("#%d: length of list want=%d, get=%d", i, len(tt.expectedOut), len(out.Items))
			continue
		}
		for j, wantPod := range tt.expectedOut {
			getPod := &out.Items[j]
			if !reflect.DeepEqual(wantPod, getPod) {
				t.Errorf("#%d: pod want=%#v, get=%#v", i, wantPod, getPod)
			}
		}
	}
}
Ejemplo n.º 7
0
// It tests that
// - first occurrence of objects should notify Add event
// - update should trigger Modified event
// - update that gets filtered should trigger Deleted event
func testWatch(t *testing.T, recursive bool) {
	podFoo := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}
	podBar := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "bar"}}

	tests := []struct {
		key        string
		filter     func(runtime.Object) bool
		trigger    func() []storage.MatchValue
		watchTests []*testWatchStruct
	}{{ // create a key
		key:        "/somekey-1",
		watchTests: []*testWatchStruct{{podFoo, true, watch.Added}},
		filter:     storage.EverythingFunc,
		trigger:    storage.NoTriggerFunc,
	}, { // create a key but obj gets filtered
		key:        "/somekey-2",
		watchTests: []*testWatchStruct{{podFoo, false, ""}},
		filter:     func(runtime.Object) bool { return false },
		trigger:    storage.NoTriggerFunc,
	}, { // create a key but obj gets filtered. Then update it with unfiltered obj
		key:        "/somekey-3",
		watchTests: []*testWatchStruct{{podFoo, false, ""}, {podBar, true, watch.Added}},
		filter: func(obj runtime.Object) bool {
			pod := obj.(*api.Pod)
			return pod.Name == "bar"
		},
		trigger: storage.NoTriggerFunc,
	}, { // update
		key:        "/somekey-4",
		watchTests: []*testWatchStruct{{podFoo, true, watch.Added}, {podBar, true, watch.Modified}},
		filter:     storage.EverythingFunc,
		trigger:    storage.NoTriggerFunc,
	}, { // delete because of being filtered
		key:        "/somekey-5",
		watchTests: []*testWatchStruct{{podFoo, true, watch.Added}, {podBar, true, watch.Deleted}},
		filter: func(obj runtime.Object) bool {
			pod := obj.(*api.Pod)
			return pod.Name != "bar"
		},
		trigger: storage.NoTriggerFunc,
	}}
	for i, tt := range tests {
		ctx, store, cluster := testSetup(t)
		filter := storage.NewSimpleFilter(tt.filter, tt.trigger)
		w, err := store.watch(ctx, tt.key, "0", filter, recursive)
		if err != nil {
			t.Fatalf("Watch failed: %v", err)
		}
		var prevObj *api.Pod
		for _, watchTest := range tt.watchTests {
			out := &api.Pod{}
			key := tt.key
			if recursive {
				key = key + "/item"
			}
			err := store.GuaranteedUpdate(ctx, key, out, true, nil, storage.SimpleUpdate(
				func(runtime.Object) (runtime.Object, error) {
					return watchTest.obj, nil
				}))
			if err != nil {
				t.Fatalf("GuaranteedUpdate failed: %v", err)
			}
			if watchTest.expectEvent {
				expectObj := out
				if watchTest.watchType == watch.Deleted {
					expectObj = prevObj
					expectObj.ResourceVersion = out.ResourceVersion
				}
				testCheckResult(t, i, watchTest.watchType, w, expectObj)
			}
			prevObj = out
		}
		w.Stop()
		testCheckStop(t, i, w)
		cluster.Terminate(t)
	}
}
Ejemplo n.º 8
0
func TestSendResultDeleteEventHaveLatestIndex(t *testing.T) {
	codec := testapi.Default.Codec()
	filterFunc := func(obj runtime.Object) bool {
		return obj.(*api.Pod).Name != "bar"
	}
	filter := storage.NewSimpleFilter(filterFunc, storage.NoTriggerFunc)
	w := newEtcdWatcher(false, false, nil, filter, codec, versioner, nil, &fakeEtcdCache{})

	eventChan := make(chan watch.Event, 1)
	w.emit = func(e watch.Event) {
		eventChan <- e
	}

	fooPod := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}
	barPod := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "bar"}}
	fooBytes, err := runtime.Encode(codec, fooPod)
	if err != nil {
		t.Fatalf("Encode failed: %v", err)
	}
	barBytes, err := runtime.Encode(codec, barPod)
	if err != nil {
		t.Fatalf("Encode failed: %v", err)
	}

	tests := []struct {
		response *etcd.Response
		expRV    string
	}{{ // Delete event
		response: &etcd.Response{
			Action: EtcdDelete,
			Node: &etcd.Node{
				ModifiedIndex: 2,
			},
			PrevNode: &etcd.Node{
				Value:         string(fooBytes),
				ModifiedIndex: 1,
			},
		},
		expRV: "2",
	}, { // Modify event with uninterested data
		response: &etcd.Response{
			Action: EtcdSet,
			Node: &etcd.Node{
				Value:         string(barBytes),
				ModifiedIndex: 2,
			},
			PrevNode: &etcd.Node{
				Value:         string(fooBytes),
				ModifiedIndex: 1,
			},
		},
		expRV: "2",
	}}

	for i, tt := range tests {
		w.sendResult(tt.response)
		ev := <-eventChan
		if ev.Type != watch.Deleted {
			t.Errorf("#%d: event type want=Deleted, get=%s", i, ev.Type)
			return
		}
		rv := ev.Object.(*api.Pod).ResourceVersion
		if rv != tt.expRV {
			t.Errorf("#%d: resource version want=%s, get=%s", i, tt.expRV, rv)
		}
	}
	w.Stop()
}