// 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{}, } storageVersions := make(map[string]string) storageDestinations := genericapiserver.NewStorageDestinations() storageDestinations.AddAPIGroup( api.GroupName, etcdstorage.NewEtcdStorage(server.Client, testapi.Default.Codec(), etcdtest.PathPrefix(), false)) storageDestinations.AddAPIGroup( autoscaling.GroupName, etcdstorage.NewEtcdStorage(server.Client, testapi.Autoscaling.Codec(), etcdtest.PathPrefix(), false)) storageDestinations.AddAPIGroup( batch.GroupName, etcdstorage.NewEtcdStorage(server.Client, testapi.Batch.Codec(), etcdtest.PathPrefix(), false)) storageDestinations.AddAPIGroup( extensions.GroupName, etcdstorage.NewEtcdStorage(server.Client, testapi.Extensions.Codec(), etcdtest.PathPrefix(), false)) config.StorageDestinations = storageDestinations storageVersions[api.GroupName] = testapi.Default.GroupVersion().String() storageVersions[autoscaling.GroupName] = testapi.Autoscaling.GroupVersion().String() storageVersions[batch.GroupName] = testapi.Batch.GroupVersion().String() storageVersions[extensions.GroupName] = testapi.Extensions.GroupVersion().String() config.StorageVersions = storageVersions config.PublicAddress = net.ParseIP("192.168.10.4") master.nodeRegistry = registrytest.NewNodeRegistry([]string{"node1", "node2"}, api.NodeResources{}) return master, server, config, assert.New(t) }
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) } }
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 TestWatch(t *testing.T) { server, etcdStorage := newEtcdTestStorage(t, testapi.Default.Codec(), etcdtest.PathPrefix()) defer server.Terminate(t) cacher := newTestCacher(etcdStorage) podFoo := makeTestPod("foo") podBar := makeTestPod("bar") podFooPrime := makeTestPod("foo") podFooPrime.Spec.NodeName = "fakeNode" podFooBis := makeTestPod("foo") podFooBis.Spec.NodeName = "anotherFakeNode" // 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) } fooCreated := updatePod(t, etcdStorage, podFoo, nil) _ = updatePod(t, etcdStorage, podBar, nil) fooUpdated := updatePod(t, etcdStorage, podFooPrime, fooCreated) verifyWatchEvent(t, watcher, watch.Added, podFoo) verifyWatchEvent(t, watcher, watch.Modified, podFooPrime) // Check whether we get too-old error. _, err = cacher.Watch(context.TODO(), "pods/ns/foo", "1", storage.Everything) if err == nil { t.Errorf("Expected 'error too old' error") } // Now test watch with initial state. // We want to observe fooCreation too, so need to pass smaller resource version. initialVersion, err := strconv.Atoi(fooCreated.ResourceVersion) if err != nil { t.Fatalf("Incorrect resourceVersion: %s", fooCreated.ResourceVersion) } initialVersion-- initialWatcher, err := cacher.Watch(context.TODO(), "pods/ns/foo", strconv.Itoa(initialVersion), storage.Everything) if err != nil { t.Fatalf("Unexpected error: %v", err) } verifyWatchEvent(t, initialWatcher, watch.Added, podFoo) verifyWatchEvent(t, initialWatcher, watch.Modified, podFooPrime) // 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) } verifyWatchEvent(t, nowWatcher, watch.Added, podFooPrime) _ = updatePod(t, etcdStorage, podFooBis, fooUpdated) verifyWatchEvent(t, nowWatcher, watch.Modified, podFooBis) }
func TestWatcherTimeout(t *testing.T) { server, etcdStorage := newEtcdTestStorage(t, testapi.Default.Codec(), etcdtest.PathPrefix()) defer server.Terminate(t) cacher := newTestCacher(etcdStorage) // Create a watcher that will not be reading any result. watcher, err := cacher.WatchList(context.TODO(), "pods/ns", "1", storage.Everything) if err != nil { t.Fatalf("Unexpected error: %v", err) } defer watcher.Stop() // Create a second watcher that will be reading result. readingWatcher, err := cacher.WatchList(context.TODO(), "pods/ns", "1", storage.Everything) if err != nil { t.Fatalf("Unexpected error: %v", err) } defer readingWatcher.Stop() for i := 1; i <= 22; i++ { pod := makeTestPod(strconv.Itoa(i)) _ = updatePod(t, etcdStorage, pod, nil) verifyWatchEvent(t, readingWatcher, watch.Added, pod) } }
func TestGuaranteedUpdateKeyNotFound(t *testing.T) { _, codec := testScheme(t) server := etcdtesting.NewEtcdTestClientServer(t) defer server.Terminate(t) key := "/some/key" helper := newEtcdHelper(server.Client, codec, etcdtest.PathPrefix()) // Create a new node. obj := &storagetesting.TestResource{ObjectMeta: metav1.ObjectMeta{Name: "foo"}, Value: 1} f := storage.SimpleUpdate(func(in runtime.Object) (runtime.Object, error) { return obj, nil }) ignoreNotFound := false err := helper.GuaranteedUpdate(context.TODO(), key, &storagetesting.TestResource{}, ignoreNotFound, nil, f) if err == nil { t.Errorf("Expected error for key not found.") } ignoreNotFound = true err = helper.GuaranteedUpdate(context.TODO(), key, &storagetesting.TestResource{}, ignoreNotFound, nil, f) if err != nil { t.Errorf("Unexpected error %v.", err) } }
func TestList(t *testing.T) { server := etcdtesting.NewEtcdTestClientServer(t) defer server.Terminate(t) helper := newEtcdHelper(server.Client, testapi.Default.Codec(), etcdtest.PathPrefix()) list := api.PodList{ Items: []api.Pod{ { ObjectMeta: metav1.ObjectMeta{Name: "bar"}, Spec: apitesting.DeepEqualSafePodSpec(), }, { ObjectMeta: metav1.ObjectMeta{Name: "baz"}, Spec: apitesting.DeepEqualSafePodSpec(), }, { ObjectMeta: metav1.ObjectMeta{Name: "foo"}, Spec: apitesting.DeepEqualSafePodSpec(), }, }, } createPodList(t, helper, &list) var got api.PodList // TODO: a sorted filter function could be applied such implied // ordering on the returned list doesn't matter. err := helper.List(context.TODO(), "/", "", storage.Everything, &got) if err != nil { t.Errorf("Unexpected error %v", err) } if e, a := list.Items, got.Items; !reflect.DeepEqual(e, a) { t.Errorf("Expected %#v, got %#v", e, a) } }
func TestCreate(t *testing.T) { obj := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}} server := etcdtesting.NewEtcdTestClientServer(t) defer server.Terminate(t) helper := newEtcdHelper(server.Client, testapi.Default.Codec(), etcdtest.PathPrefix()) returnedObj := &api.Pod{} err := helper.Create(context.TODO(), "/some/key", obj, returnedObj, 5) if err != nil { t.Errorf("Unexpected error %#v", err) } _, err = runtime.Encode(testapi.Default.Codec(), obj) if err != nil { t.Errorf("Unexpected error %#v", err) } err = helper.Get(context.TODO(), "/some/key", returnedObj, false) if err != nil { t.Errorf("Unexpected error %#v", err) } _, err = runtime.Encode(testapi.Default.Codec(), returnedObj) if err != nil { t.Errorf("Unexpected error %#v", err) } if obj.Name != returnedObj.Name { t.Errorf("Wanted %v, got %v", obj.Name, returnedObj.Name) } }
func TestGuaranteedUpdateNoChange(t *testing.T) { _, codec := testScheme(t) server := etcdtesting.NewEtcdTestClientServer(t) defer server.Terminate(t) key := "/some/key" helper := newEtcdHelper(server.Client, codec, etcdtest.PathPrefix()) obj := &storagetesting.TestResource{ObjectMeta: metav1.ObjectMeta{Name: "foo"}, Value: 1} err := helper.GuaranteedUpdate(context.TODO(), key, &storagetesting.TestResource{}, true, nil, 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 := &storagetesting.TestResource{ObjectMeta: metav1.ObjectMeta{Name: "foo"}, Value: 1} err = helper.GuaranteedUpdate(context.TODO(), key, &storagetesting.TestResource{}, true, nil, storage.SimpleUpdate(func(in runtime.Object) (runtime.Object, error) { callbackCalled = true return objUpdate, nil })) if err != nil { t.Fatalf("Unexpected error %#v", err) } if !callbackCalled { t.Errorf("tryUpdate callback should have been called.") } }
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) }
// This is to emulate the case where another party updates the object when // etcdHelper.Delete has verified the preconditions, but hasn't carried out the // deletion yet. Etcd will fail the deletion and report the conflict. etcdHelper // should retry until there is no conflict. func TestDeleteWithRetry(t *testing.T) { server := etcdtesting.NewEtcdTestClientServer(t) defer server.Terminate(t) prefix := path.Join("/", etcdtest.PathPrefix()) obj := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo", UID: "A"}} // fakeGet returns a large ModifiedIndex to emulate the case that another // party has updated the object. fakeGet := func(ctx context.Context, key string, opts *etcd.GetOptions) (*etcd.Response, error) { data, _ := runtime.Encode(testapi.Default.Codec(), obj) return &etcd.Response{Node: &etcd.Node{Value: string(data), ModifiedIndex: 99}}, nil } expectedRetries := 3 helper := newEtcdHelper(server.Client, testapi.Default.Codec(), prefix) fake := &fakeDeleteKeysAPI{KeysAPI: helper.etcdKeysAPI, fakeGetCap: expectedRetries, fakeGetFunc: fakeGet} helper.etcdKeysAPI = fake returnedObj := &api.Pod{} err := helper.Create(context.TODO(), "/some/key", obj, returnedObj, 0) if err != nil { t.Errorf("Unexpected error %#v", err) } err = helper.Delete(context.TODO(), "/some/key", obj, storage.NewUIDPreconditions("A")) if err != nil { t.Errorf("Unexpected error %#v", err) } if fake.getCount != expectedRetries { t.Errorf("Expect %d retries, got %d", expectedRetries, fake.getCount) } err = helper.Get(context.TODO(), "/some/key", obj, false) if !storage.IsNotFound(err) { t.Errorf("Expect an NotFound error, got %v", err) } }
// Returns a basic master config. func NewMasterConfig() *master.Config { etcdClient := NewEtcdClient() storageVersions := make(map[string]string) etcdStorage := etcdstorage.NewEtcdStorage(etcdClient, testapi.Default.Codec(), etcdtest.PathPrefix(), false) storageVersions[api.GroupName] = testapi.Default.GroupVersion().String() autoscalingEtcdStorage := NewAutoscalingEtcdStorage(etcdClient) storageVersions[autoscaling.GroupName] = testapi.Autoscaling.GroupVersion().String() batchEtcdStorage := NewBatchEtcdStorage(etcdClient) storageVersions[batch.GroupName] = testapi.Batch.GroupVersion().String() expEtcdStorage := NewExtensionsEtcdStorage(etcdClient) storageVersions[extensions.GroupName] = testapi.Extensions.GroupVersion().String() storageDestinations := genericapiserver.NewStorageDestinations() storageDestinations.AddAPIGroup(api.GroupName, etcdStorage) storageDestinations.AddAPIGroup(autoscaling.GroupName, autoscalingEtcdStorage) storageDestinations.AddAPIGroup(batch.GroupName, batchEtcdStorage) storageDestinations.AddAPIGroup(extensions.GroupName, expEtcdStorage) return &master.Config{ Config: &genericapiserver.Config{ StorageDestinations: storageDestinations, StorageVersions: storageVersions, APIPrefix: "/api", APIGroupPrefix: "/apis", Authorizer: apiserver.NewAlwaysAllowAuthorizer(), AdmissionControl: admit.NewAlwaysAdmit(), Serializer: api.Codecs, }, KubeletClient: kubeletclient.FakeKubeletClient{}, } }
func TestWatcherTimeout(t *testing.T) { server, etcdStorage := newEtcdTestStorage(t, testapi.Default.Codec(), etcdtest.PathPrefix()) defer server.Terminate(t) cacher := newTestCacher(etcdStorage) defer cacher.Stop() // initialVersion is used to initate the watcher at the beginning of the world, // which is not defined precisely in etcd. initialVersion, err := cacher.LastSyncResourceVersion() if err != nil { t.Fatalf("Unexpected error: %v", err) } startVersion := strconv.Itoa(int(initialVersion)) // Create a watcher that will not be reading any result. watcher, err := cacher.WatchList(context.TODO(), "pods/ns", startVersion, storage.Everything) if err != nil { t.Fatalf("Unexpected error: %v", err) } defer watcher.Stop() // Create a second watcher that will be reading result. readingWatcher, err := cacher.WatchList(context.TODO(), "pods/ns", startVersion, storage.Everything) if err != nil { t.Fatalf("Unexpected error: %v", err) } defer readingWatcher.Stop() for i := 1; i <= 22; i++ { pod := makeTestPod(strconv.Itoa(i)) _ = updatePod(t, etcdStorage, pod, nil) verifyWatchEvent(t, readingWatcher, watch.Added, pod) } }
func TestGet(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) // We pass the ResourceVersion from the above Create() operation. result := &api.Pod{} if err := cacher.Get(context.TODO(), "pods/ns/foo", fooCreated.ResourceVersion, result, true); err != nil { t.Errorf("Unexpected error: %v", err) } if e, a := *fooCreated, *result; !reflect.DeepEqual(e, a) { t.Errorf("Expected: %#v, got: %#v", e, a) } if err := cacher.Get(context.TODO(), "pods/ns/bar", fooCreated.ResourceVersion, result, true); err != nil { t.Errorf("Unexpected error: %v", err) } emptyPod := api.Pod{} if e, a := emptyPod, *result; !reflect.DeepEqual(e, a) { t.Errorf("Expected: %#v, got: %#v", e, a) } if err := cacher.Get(context.TODO(), "pods/ns/bar", fooCreated.ResourceVersion, result, false); !storage.IsNotFound(err) { t.Errorf("Unexpected error: %v", err) } }
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) } // Test normal case pod := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}} returnObj := &api.Pod{} err = h.Set(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() if _, open := <-watching.ResultChan(); open { t.Errorf("An injected error did not cause a graceful shutdown") } }
func NewTestGenericEtcdRegistry(t *testing.T) (*etcdtesting.EtcdTestServer, *Etcd) { podPrefix := "/pods" server := etcdtesting.NewEtcdTestClientServer(t) s := etcdstorage.NewEtcdStorage(server.Client, testapi.Default.Codec(), etcdtest.PathPrefix()) strategy := &testRESTStrategy{api.Scheme, api.SimpleNameGenerator, true, false, true} return server, &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, } }
// 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{}, } storageVersions := make(map[string]string) storageDestinations := genericapiserver.NewStorageDestinations() storageDestinations.AddAPIGroup( api.GroupName, etcdstorage.NewEtcdStorage(server.Client, testapi.Default.Codec(), etcdtest.PathPrefix(), false, etcdtest.DeserializationCacheSize)) storageDestinations.AddAPIGroup( autoscaling.GroupName, etcdstorage.NewEtcdStorage(server.Client, testapi.Autoscaling.Codec(), etcdtest.PathPrefix(), false, etcdtest.DeserializationCacheSize)) storageDestinations.AddAPIGroup( batch.GroupName, etcdstorage.NewEtcdStorage(server.Client, testapi.Batch.Codec(), etcdtest.PathPrefix(), false, etcdtest.DeserializationCacheSize)) storageDestinations.AddAPIGroup( extensions.GroupName, etcdstorage.NewEtcdStorage(server.Client, testapi.Extensions.Codec(), etcdtest.PathPrefix(), false, etcdtest.DeserializationCacheSize)) config.StorageDestinations = storageDestinations storageVersions[api.GroupName] = testapi.Default.GroupVersion().String() storageVersions[autoscaling.GroupName] = testapi.Autoscaling.GroupVersion().String() storageVersions[batch.GroupName] = testapi.Batch.GroupVersion().String() storageVersions[extensions.GroupName] = testapi.Extensions.GroupVersion().String() config.StorageVersions = storageVersions 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 storeToEtcd(client tools.EtcdClient, path, name string, obj interface{}) error { data, err := encodeToThirdParty(name, obj) if err != nil { return err } _, err = client.Set(etcdtest.PathPrefix()+path, string(data), 0) return err }
func testInstallThirdPartyAPIDeleteVersion(t *testing.T, version string) { _, fakeClient, server, assert := initThirdParty(t, version) defer server.Close() expectedObj := Foo{ ObjectMeta: api.ObjectMeta{ Name: "test", Namespace: "default", }, TypeMeta: unversioned.TypeMeta{ Kind: "Foo", }, SomeField: "test field", OtherField: 10, } if !assert.NoError(storeToEtcd(fakeClient, "/ThirdPartyResourceData/company.com/foos/default/test", "test", expectedObj)) { t.FailNow() return } resp, err := http.Get(server.URL + "/apis/company.com/" + version + "/namespaces/default/foos/test") if !assert.NoError(err) { return } assert.Equal(http.StatusOK, resp.StatusCode) item := Foo{} assert.NoError(decodeResponse(resp, &item)) // Fill in fields set by the apiserver expectedObj.SelfLink = item.SelfLink expectedObj.Namespace = item.Namespace if !assert.True(reflect.DeepEqual(item, expectedObj)) { t.Errorf("expected:\n%v\nsaw:\n%v\n", expectedObj, item) } resp, err = httpDelete(server.URL + "/apis/company.com/" + version + "/namespaces/default/foos/test") if !assert.NoError(err) { return } assert.Equal(http.StatusOK, resp.StatusCode) resp, err = http.Get(server.URL + "/apis/company.com/" + version + "/namespaces/default/foos/test") if !assert.NoError(err) { return } assert.Equal(http.StatusNotFound, resp.StatusCode) expectDeletedKeys := []string{etcdtest.PathPrefix() + "/ThirdPartyResourceData/company.com/foos/default/test"} if !assert.True(reflect.DeepEqual(fakeClient.DeletedKeys, expectDeletedKeys)) { t.Errorf("unexpected deleted keys: %v", fakeClient.DeletedKeys) } }
// TestListAcrossDirectories ensures that the client excludes directories and flattens tree-response - simulates cross-namespace query func TestListAcrossDirectories(t *testing.T) { server := etcdtesting.NewEtcdTestClientServer(t) defer server.Terminate(t) roothelper := newEtcdHelper(server.Client, testapi.Default.Codec(), etcdtest.PathPrefix()) helper1 := newEtcdHelper(server.Client, testapi.Default.Codec(), etcdtest.PathPrefix()+"/dir1") helper2 := newEtcdHelper(server.Client, testapi.Default.Codec(), etcdtest.PathPrefix()+"/dir2") list := api.PodList{ Items: []api.Pod{ { ObjectMeta: metav1.ObjectMeta{Name: "baz"}, Spec: apitesting.DeepEqualSafePodSpec(), }, { ObjectMeta: metav1.ObjectMeta{Name: "foo"}, Spec: apitesting.DeepEqualSafePodSpec(), }, { ObjectMeta: metav1.ObjectMeta{Name: "bar"}, Spec: apitesting.DeepEqualSafePodSpec(), }, }, } returnedObj := &api.Pod{} // create the 1st 2 elements in one directory createObj(t, helper1, list.Items[0].Name, &list.Items[0], returnedObj, 0) list.Items[0] = *returnedObj createObj(t, helper1, list.Items[1].Name, &list.Items[1], returnedObj, 0) list.Items[1] = *returnedObj // create the last element in the other directory createObj(t, helper2, list.Items[2].Name, &list.Items[2], returnedObj, 0) list.Items[2] = *returnedObj var got api.PodList err := roothelper.List(context.TODO(), "/", "", storage.Everything, &got) if err != nil { t.Errorf("Unexpected error %v", err) } if e, a := list.Items, got.Items; !reflect.DeepEqual(e, a) { t.Errorf("Expected %#v, got %#v", e, a) } }
func TestWatch(t *testing.T) { client := framework.NewEtcdClient() keysAPI := etcd.NewKeysAPI(client) etcdStorage := etcdstorage.NewEtcdStorage(client, testapi.Default.Codec(), etcdtest.PathPrefix(), false) ctx := context.TODO() framework.WithEtcdKey(func(key string) { key = etcdtest.AddPrefix(key) resp, err := keysAPI.Set(ctx, key, runtime.EncodeOrDie(testapi.Default.Codec(), &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}), nil) if err != nil { t.Fatalf("unexpected error: %v", err) } expectedVersion := resp.Node.ModifiedIndex // watch should load the object at the current index w, err := etcdStorage.Watch(ctx, key, "0", storage.Everything) if err != nil { t.Fatalf("Unexpected error: %v", err) } event := <-w.ResultChan() if event.Type != watch.Added || event.Object == nil { t.Fatalf("expected first value to be set to ADDED, got %#v", event) } // version should match what we set pod := event.Object.(*api.Pod) if pod.ResourceVersion != strconv.FormatUint(expectedVersion, 10) { t.Errorf("expected version %d, got %#v", expectedVersion, pod) } // should be no events in the stream select { case event, ok := <-w.ResultChan(): if !ok { t.Fatalf("channel closed unexpectedly") } t.Fatalf("unexpected object in channel: %#v", event) default: } // should return the previously deleted item in the watch, but with the latest index resp, err = keysAPI.Delete(ctx, key, nil) if err != nil { t.Fatalf("unexpected error: %v", err) } expectedVersion = resp.Node.ModifiedIndex event = <-w.ResultChan() if event.Type != watch.Deleted { t.Errorf("expected deleted event %#v", event) } pod = event.Object.(*api.Pod) if pod.ResourceVersion != strconv.FormatUint(expectedVersion, 10) { t.Errorf("expected version %d, got %#v", expectedVersion, pod) } }) }
func TestCreateNilOutParam(t *testing.T) { obj := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}} server := etcdtesting.NewEtcdTestClientServer(t) defer server.Terminate(t) helper := newEtcdHelper(server.Client, testapi.Default.Codec(), etcdtest.PathPrefix()) err := helper.Create(context.TODO(), "/some/key", obj, nil, 5) if err != nil { t.Errorf("Unexpected error %#v", err) } }
func TestSetFailCAS(t *testing.T) { obj := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "1"}} server := etcdtesting.NewEtcdTestClientServer(t) defer server.Terminate(t) helper := newEtcdHelper(server.Client, testapi.Default.Codec(), etcdtest.PathPrefix()) err := helper.Set(context.TODO(), "/some/key", obj, nil, 5) if err == nil { t.Errorf("Expecting error.") } }
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) } }
func NewEtcdStorage(t *testing.T, group string) (*storagebackend.Config, *etcdtesting.EtcdTestServer) { server := etcdtesting.NewUnsecuredEtcdTestClientServer(t) config := &storagebackend.Config{ Type: "etcd2", Prefix: etcdtest.PathPrefix(), ServerList: server.Client.Endpoints(), DeserializationCacheSize: etcdtest.DeserializationCacheSize, Codec: testapi.Groups[group].StorageCodec(), } return config, server }
// setUp is a convience function for setting up for (most) tests. func setUp(t *testing.T) (*Master, *etcdtesting.EtcdTestServer, Config, *assert.Assertions) { server := etcdtesting.NewUnsecuredEtcdTestClientServer(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{} config.RequestContextMapper = api.NewRequestContextMapper() // 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 newTestGenericStoreRegistry(t *testing.T, hasCacheEnabled bool) (*etcdtesting.EtcdTestServer, *Store) { podPrefix := "/pods" server := etcdtesting.NewEtcdTestClientServer(t) strategy := &testRESTStrategy{api.Scheme, api.SimpleNameGenerator, true, false, true} codec := testapi.Default.StorageCodec() s := etcdstorage.NewEtcdStorage(server.Client, codec, etcdtest.PathPrefix(), false, etcdtest.DeserializationCacheSize) if hasCacheEnabled { config := storage.CacherConfig{ CacheCapacity: 10, Storage: s, Versioner: etcdstorage.APIObjectVersioner{}, Type: &api.Pod{}, ResourcePrefix: podPrefix, KeyFunc: func(obj runtime.Object) (string, error) { return storage.NoNamespaceKeyFunc(podPrefix, obj) }, NewListFunc: func() runtime.Object { return &api.PodList{} }, Codec: codec, } s = storage.NewCacherFromConfig(config) } 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.SelectionPredicate { 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 TestGetNotFoundErr(t *testing.T) { server := etcdtesting.NewEtcdTestClientServer(t) defer server.Terminate(t) boguskey := "/some/boguskey" helper := newEtcdHelper(server.Client, testapi.Default.Codec(), etcdtest.PathPrefix()) var got api.Pod err := helper.Get(context.TODO(), boguskey, "", &got, false) if !storage.IsNotFound(err) { t.Errorf("Unexpected reponse on key=%v, err=%v", boguskey, err) } }
// 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") }
// NewEtcd3TestClientServer creates a new client and server for testing func NewUnsecuredEtcd3TestClientServer(t *testing.T) (*EtcdTestServer, *storagebackend.Config) { server := &EtcdTestServer{ v3Cluster: integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1}), } server.V3Client = server.v3Cluster.RandClient() config := &storagebackend.Config{ Type: "etcd3", Prefix: etcdtest.PathPrefix(), ServerList: server.V3Client.Endpoints(), DeserializationCacheSize: etcdtest.DeserializationCacheSize, } return server, config }