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) } defer watching.Stop() // 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 } }
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 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) } defer watching.Stop() // 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("Unexpected error: 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) } }
// 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 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) } }
// 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) }
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) } // watching is explicitly closed below. // Test normal case pod := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}} returnObj := &api.Pod{} err = h.Create(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() // 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 NewTestGenericStoreRegistry(t *testing.T) (*etcdtesting.EtcdTestServer, *Store) { podPrefix := "/pods" server := etcdtesting.NewEtcdTestClientServer(t) s := etcdstorage.NewEtcdStorage(server.Client, testapi.Default.StorageCodec(), etcdtest.PathPrefix(), false, etcdtest.DeserializationCacheSize) strategy := &testRESTStrategy{api.Scheme, api.SimpleNameGenerator, true, false, true} 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.Matcher { 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 NewEtcdStorage(t *testing.T, group string) (storage.Interface, *etcdtesting.EtcdTestServer) { server := etcdtesting.NewEtcdTestClientServer(t) storage := etcdstorage.NewEtcdStorage(server.Client, testapi.Groups[group].StorageCodec(), etcdtest.PathPrefix(), false, etcdtest.DeserializationCacheSize) return storage, server }
func newEtcdTestStorage(t *testing.T, codec runtime.Codec, prefix string) (*etcdtesting.EtcdTestServer, storage.Interface) { server := etcdtesting.NewEtcdTestClientServer(t) storage := etcdstorage.NewEtcdStorage(server.Client, codec, prefix, false, etcdtest.DeserializationCacheSize) return server, storage }