// WatchPredicate starts a watch for the items that m matches. func (e *Etcd) WatchPredicate(ctx api.Context, m generic.Matcher, resourceVersion string) (watch.Interface, error) { version, err := storage.ParseWatchResourceVersion(resourceVersion, e.EndpointName) if err != nil { return nil, err } 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 } if name, ok := m.MatchesSingle(); ok { key, err := e.KeyFunc(ctx, name) if err != nil { return nil, err } return e.Storage.Watch(key, version, filterFunc) } return e.Storage.WatchList(e.KeyRootFunc(ctx), version, filterFunc) }
func TestWatchDeleteEventObjectHaveLatestRV(t *testing.T) { ctx, store, cluster := testSetup(t) defer cluster.Terminate(t) key, storedObj := testPropogateStore(ctx, t, store, &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}) w, err := store.Watch(ctx, key, storedObj.ResourceVersion, storage.Everything) if err != nil { t.Fatalf("Watch failed: %v", err) } etcdW := cluster.RandClient().Watch(ctx, "/", clientv3.WithPrefix()) if err := store.Delete(ctx, key, &api.Pod{}, &storage.Preconditions{}); err != nil { t.Fatalf("Delete failed: %v", err) } e := <-w.ResultChan() watchedDeleteObj := e.Object.(*api.Pod) var wres clientv3.WatchResponse wres = <-etcdW watchedDeleteRev, err := storage.ParseWatchResourceVersion(watchedDeleteObj.ResourceVersion) if err != nil { t.Fatalf("ParseWatchResourceVersion failed: %v", err) } if int64(watchedDeleteRev) != wres.Events[0].Kv.ModRevision { t.Errorf("Object from delete event have version: %v, should be the same as etcd delete's mod rev: %d", watchedDeleteRev, wres.Events[0].Kv.ModRevision) } }
// ListPredicate returns a list of all the items matching m. func (e *Etcd) ListPredicate(ctx api.Context, m generic.Matcher, options *api.ListOptions) (runtime.Object, error) { list := e.NewListFunc() trace := util.NewTrace("List " + reflect.TypeOf(list).String()) filterFunc := e.filterAndDecorateFunction(m) defer trace.LogIfLong(600 * time.Millisecond) if name, ok := m.MatchesSingle(); ok { if key, err := e.KeyFunc(ctx, name); err == nil { trace.Step("About to read single object") err := e.Storage.GetToList(ctx, key, filterFunc, list) trace.Step("Object extracted") return list, etcderr.InterpretListError(err, e.EndpointName) } // if we cannot extract a key based on the current context, the optimization is skipped } trace.Step("About to list directory") if options == nil { options = &api.ListOptions{ResourceVersion: "0"} } version, err := storage.ParseWatchResourceVersion(options.ResourceVersion, e.EndpointName) if err != nil { return nil, err } err = e.Storage.List(ctx, e.KeyRootFunc(ctx), version, filterFunc, list) trace.Step("List extracted") return list, etcderr.InterpretListError(err, e.EndpointName) }
func TestStartingResourceVersion(t *testing.T) { server, etcdStorage := newEtcdTestStorage(t, testapi.Default.Codec(), etcdtest.PathPrefix()) defer server.Terminate(t) cacher := newTestCacher(etcdStorage) defer cacher.Stop() // add 1 object podFoo := makeTestPod("foo") fooCreated := updatePod(t, etcdStorage, podFoo, nil) // Set up Watch starting at fooCreated.ResourceVersion + 10 rv, err := storage.ParseWatchResourceVersion(fooCreated.ResourceVersion) if err != nil { t.Fatalf("Unexpected error: %v", err) } rv += 10 startVersion := strconv.Itoa(int(rv)) watcher, err := cacher.Watch(context.TODO(), "pods/ns/foo", startVersion, storage.Everything) if err != nil { t.Fatalf("Unexpected error: %v", err) } defer watcher.Stop() lastFoo := fooCreated for i := 0; i < 11; i++ { podFooForUpdate := makeTestPod("foo") podFooForUpdate.Labels = map[string]string{"foo": strconv.Itoa(i)} lastFoo = updatePod(t, etcdStorage, podFooForUpdate, lastFoo) } select { case e := <-watcher.ResultChan(): pod := e.Object.(*api.Pod) podRV, err := storage.ParseWatchResourceVersion(pod.ResourceVersion) if err != nil { t.Fatalf("unexpected error: %v", err) } // event should have at least rv + 1, since we're starting the watch at rv if podRV <= rv { t.Errorf("expected event with resourceVersion of at least %d, got %d", rv+1, podRV) } case <-time.After(wait.ForeverTestTimeout): t.Errorf("timed out waiting for event") } }
func (s *store) watch(ctx context.Context, key string, rv string, pred storage.SelectionPredicate, recursive bool) (watch.Interface, error) { rev, err := storage.ParseWatchResourceVersion(rv) if err != nil { return nil, err } key = keyWithPrefix(s.pathPrefix, key) return s.watcher.Watch(ctx, key, int64(rev), recursive, pred) }
// Implements storage.Interface. func (h *etcdHelper) WatchList(ctx context.Context, key string, resourceVersion string, filter storage.FilterFunc) (watch.Interface, error) { if ctx == nil { glog.Errorf("Context is nil") } watchRV, err := storage.ParseWatchResourceVersion(resourceVersion) if err != nil { return nil, err } key = h.prefixEtcdKey(key) w := newEtcdWatcher(true, h.quorum, exceptKey(key), filter, h.codec, h.versioner, nil, h) go w.etcdWatch(ctx, h.etcdKeysAPI, key, watchRV) return w, nil }
// Implements storage.Interface. func (h *etcdHelper) Watch(ctx context.Context, key string, resourceVersion string, pred storage.SelectionPredicate) (watch.Interface, error) { if ctx == nil { glog.Errorf("Context is nil") } watchRV, err := storage.ParseWatchResourceVersion(resourceVersion) if err != nil { return nil, err } key = h.prefixEtcdKey(key) w := newEtcdWatcher(false, h.quorum, nil, storage.SimpleFilter(pred), h.codec, h.versioner, nil, h) go w.etcdWatch(ctx, h.etcdKeysAPI, key, watchRV) return w, nil }
// WatchPredicate starts a watch for the items that m matches. func (e *Etcd) WatchPredicate(ctx api.Context, m generic.Matcher, resourceVersion string) (watch.Interface, error) { version, err := storage.ParseWatchResourceVersion(resourceVersion, e.EndpointName) if err != nil { return nil, err } filterFunc := e.filterAndDecorateFunction(m) if name, ok := m.MatchesSingle(); ok { key, err := e.KeyFunc(ctx, name) if err != nil { return nil, err } return e.Storage.Watch(key, version, filterFunc) } return e.Storage.WatchList(e.KeyRootFunc(ctx), version, filterFunc) }
// WatchPredicate starts a watch for the items that m matches. func (e *Etcd) WatchPredicate(ctx api.Context, m generic.Matcher, resourceVersion string) (watch.Interface, error) { version, err := storage.ParseWatchResourceVersion(resourceVersion, e.EndpointName) if err != nil { return nil, err } filterFunc := e.filterAndDecorateFunction(m) if name, ok := m.MatchesSingle(); ok { if key, err := e.KeyFunc(ctx, name); err == nil { if err != nil { return nil, err } return e.Storage.Watch(key, version, filterFunc) } // if we cannot extract a key based on the current context, the optimization is skipped } return e.Storage.WatchList(e.KeyRootFunc(ctx), version, filterFunc) }
// ListPredicate returns a list of all the items matching m. func (e *Etcd) ListPredicate(ctx api.Context, m generic.Matcher, options *unversioned.ListOptions) (runtime.Object, error) { list := e.NewListFunc() filterFunc := e.filterAndDecorateFunction(m) if name, ok := m.MatchesSingle(); ok { if key, err := e.KeyFunc(ctx, name); err == nil { err := e.Storage.GetToList(ctx, key, filterFunc, list) return list, etcderr.InterpretListError(err, e.EndpointName) } // if we cannot extract a key based on the current context, the optimization is skipped } if options == nil { options = &unversioned.ListOptions{ResourceVersion: "0"} } version, err := storage.ParseWatchResourceVersion(options.ResourceVersion, e.EndpointName) if err != nil { return nil, err } err = e.Storage.List(ctx, e.KeyRootFunc(ctx), version, filterFunc, list) return list, etcderr.InterpretListError(err, e.EndpointName) }
// WatchServices begins watching for new, changed, or deleted service configurations. func (r *Registry) WatchServices(ctx api.Context, label labels.Selector, field fields.Selector, resourceVersion string) (watch.Interface, error) { version, err := storage.ParseWatchResourceVersion(resourceVersion, "service") if err != nil { return nil, err } if !label.Empty() { return nil, fmt.Errorf("label selectors are not supported on services") } if value, found := field.RequiresExactMatch("name"); found { key, err := makeServiceKey(ctx, value) if err != nil { return nil, err } // TODO: use generic.SelectionPredicate return r.Watch(key, version, storage.Everything) } if field.Empty() { return r.WatchList(makeServiceListKey(ctx), version, storage.Everything) } return nil, fmt.Errorf("only the 'name' and default (everything) field selectors are supported") }
func TestRandomWatchDeliver(t *testing.T) { server, etcdStorage := newEtcdTestStorage(t, testapi.Default.Codec(), etcdtest.PathPrefix()) defer server.Terminate(t) cacher := newTestCacher(etcdStorage, 10) defer cacher.Stop() fooCreated := updatePod(t, etcdStorage, makeTestPod("foo"), nil) rv, err := storage.ParseWatchResourceVersion(fooCreated.ResourceVersion) if err != nil { t.Fatalf("Unexpected error: %v", err) } startVersion := strconv.Itoa(int(rv)) watcher, err := cacher.WatchList(context.TODO(), "pods/ns", startVersion, storage.Everything) if err != nil { t.Fatalf("Unexpected error: %v", err) } // Now we can create exactly 21 events that should be delivered // to the watcher, before it will completely block cacher and as // a result will be dropped. for i := 0; i < 21; i++ { updatePod(t, etcdStorage, makeTestPod(fmt.Sprintf("foo-%d", i)), nil) } // Now stop the watcher and check if the consecutive events are being delivered. watcher.Stop() watched := 0 for { event, ok := <-watcher.ResultChan() if !ok { break } if a, e := event.Object.(*api.Pod).Name, fmt.Sprintf("foo-%d", watched); e != a { t.Errorf("Unexpected object watched: %s, expected %s", a, e) } watched++ } }
func TestInfiniteList(t *testing.T) { server, etcdStorage := newEtcdTestStorage(t, testapi.Default.Codec(), etcdtest.PathPrefix()) defer server.Terminate(t) cacher := newTestCacher(etcdStorage, 10) defer cacher.Stop() podFoo := makeTestPod("foo") fooCreated := updatePod(t, etcdStorage, podFoo, nil) // Set up List at fooCreated.ResourceVersion + 10 rv, err := storage.ParseWatchResourceVersion(fooCreated.ResourceVersion) if err != nil { t.Fatalf("Unexpected error: %v", err) } listRV := strconv.Itoa(int(rv + 10)) result := &api.PodList{} err = cacher.List(context.TODO(), "pods/ns", listRV, storage.Everything, result) if !errors.IsTimeout(err) { t.Errorf("Unexpected error: %v", err) } }
// WatchRoutes begins watching for new, changed, or deleted route configurations. func (registry *Etcd) WatchRoutes(ctx kapi.Context, label labels.Selector, field fields.Selector, resourceVersion string) (watch.Interface, error) { if !label.Empty() { return nil, fmt.Errorf("label selectors are not supported on routes yet") } version, err := storage.ParseWatchResourceVersion(resourceVersion, "pod") if err != nil { return nil, err } if value, found := field.RequiresExactMatch("ID"); found { key, err := makeRouteKey(ctx, value) if err != nil { return nil, err } return registry.Watch(key, version, storage.Everything) } if field.Empty() { key := kubeetcd.MakeEtcdListKey(ctx, RoutePath) return registry.WatchList(key, version, storage.Everything) } return nil, fmt.Errorf("only the 'ID' and default (everything) field selectors are supported") }