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