func TestWatchPurposefulShutdown(t *testing.T) { fakeClient := tools.NewFakeEtcdClient(t) h := newEtcdHelper(fakeClient, codec, etcdtest.PathPrefix()) key := "/some/key" prefixedKey := etcdtest.AddPrefix(key) fakeClient.ExpectNotFoundGet(prefixedKey) // Test purposeful shutdown watching, err := h.Watch(context.TODO(), key, 0, storage.Everything) if err != nil { t.Fatalf("Unexpected error: %v", err) } fakeClient.WaitForWatchCompletion() watching.Stop() // Did everything shut down? if _, open := <-fakeClient.WatchResponse; open { t.Errorf("A stop did not cause a graceful shutdown") } if _, open := <-watching.ResultChan(); open { t.Errorf("An injected error did not cause a graceful shutdown") } }
func NewTestGenericEtcdRegistry(t *testing.T) (*tools.FakeEtcdClient, *Etcd) { f := tools.NewFakeEtcdClient(t) f.TestIndex = true s := etcdstorage.NewEtcdStorage(f, testapi.Default.Codec(), etcdtest.PathPrefix()) strategy := &testRESTStrategy{api.Scheme, api.SimpleNameGenerator, true, false, true} podPrefix := "/pods" return f, &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, } }
func TestWatchFromNotFound(t *testing.T) { fakeClient := tools.NewFakeEtcdClient(t) key := "/some/key" prefixedKey := etcdtest.AddPrefix(key) fakeClient.Data[prefixedKey] = tools.EtcdResponseWithError{ R: &etcd.Response{ Node: nil, }, E: &etcd.EtcdError{ Index: 2, ErrorCode: 100, }, } h := newEtcdHelper(fakeClient, codec, etcdtest.PathPrefix()) watching, err := h.Watch(context.TODO(), key, 0, storage.Everything) if err != nil { t.Fatalf("Unexpected error: %v", err) } fakeClient.WaitForWatchCompletion() if fakeClient.WatchIndex != 3 { t.Errorf("Expected client to wait for %d, got %#v", 3, fakeClient) } watching.Stop() }
func initThirdParty(t *testing.T, version string) (*Master, *tools.FakeEtcdClient, *httptest.Server, *assert.Assertions) { master, _, assert := setUp(t) master.thirdPartyResources = map[string]*thirdpartyresourcedatastorage.REST{} api := &extensions.ThirdPartyResource{ ObjectMeta: api.ObjectMeta{ Name: "foo.company.com", }, Versions: []extensions.APIVersion{ { APIGroup: "group", Name: version, }, }, } master.handlerContainer = restful.NewContainer() fakeClient := tools.NewFakeEtcdClient(t) fakeClient.Machines = []string{"http://machine1:4001", "http://machine2", "http://machine3:4003"} master.thirdPartyStorage = etcdstorage.NewEtcdStorage(fakeClient, testapi.Extensions.Codec(), etcdtest.PathPrefix()) if !assert.NoError(master.InstallThirdPartyResource(api)) { t.FailNow() } server := httptest.NewServer(master.handlerContainer.ServeMux) return &master, fakeClient, server, assert }
func TestSetWithoutResourceVersioner(t *testing.T) { obj := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}} fakeClient := tools.NewFakeEtcdClient(t) helper := newEtcdHelper(fakeClient, testapi.Default.Codec(), etcdtest.PathPrefix()) helper.versioner = nil returnedObj := &api.Pod{} err := helper.Set("/some/key", obj, returnedObj, 3) key := etcdtest.AddPrefix("/some/key") if err != nil { t.Errorf("Unexpected error %#v", err) } data, err := testapi.Default.Codec().Encode(obj) if err != nil { t.Errorf("Unexpected error %#v", err) } expect := string(data) got := fakeClient.Data[key].R.Node.Value if expect != got { t.Errorf("Wanted %v, got %v", expect, got) } if e, a := uint64(3), fakeClient.LastSetTTL; e != a { t.Errorf("Wanted %v, got %v", e, a) } if obj.ResourceVersion != returnedObj.ResourceVersion || obj.Name != returnedObj.Name { t.Errorf("If set was successful but returned object did not have correct resource version") } }
func TestWatchEtcdError(t *testing.T) { codec := testapi.Default.Codec() fakeClient := tools.NewFakeEtcdClient(t) fakeClient.ExpectNotFoundGet("/some/key") fakeClient.WatchImmediateError = fmt.Errorf("immediate error") h := newEtcdHelper(fakeClient, codec, etcdtest.PathPrefix()) watching, err := h.Watch(context.TODO(), "/some/key", 4, storage.Everything) if err != nil { t.Fatalf("Unexpected error: %v", err) } defer watching.Stop() got := <-watching.ResultChan() if got.Type != watch.Error { t.Fatalf("Unexpected non-error") } status, ok := got.Object.(*unversioned.Status) if !ok { t.Fatalf("Unexpected non-error object type") } if status.Message != "immediate error" { t.Errorf("Unexpected wrong error") } if status.Status != unversioned.StatusFailure { t.Errorf("Unexpected wrong error status") } }
func TestGuaranteedUpdateNoChange(t *testing.T) { fakeClient := tools.NewFakeEtcdClient(t) fakeClient.TestIndex = true helper := newEtcdHelper(fakeClient, codec, etcdtest.PathPrefix()) key := etcdtest.AddPrefix("/some/key") // Create a new node. fakeClient.ExpectNotFoundGet(key) obj := &TestResource{ObjectMeta: api.ObjectMeta{Name: "foo"}, Value: 1} err := helper.GuaranteedUpdate("/some/key", &TestResource{}, true, 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 := &TestResource{ObjectMeta: api.ObjectMeta{Name: "foo"}, Value: 1} err = helper.GuaranteedUpdate("/some/key", &TestResource{}, true, storage.SimpleUpdate(func(in runtime.Object) (runtime.Object, error) { fakeClient.Err = errors.New("should not be called") 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 TestGuaranteedUpdateKeyNotFound(t *testing.T) { fakeClient := tools.NewFakeEtcdClient(t) fakeClient.TestIndex = true helper := newEtcdHelper(fakeClient, codec, etcdtest.PathPrefix()) key := etcdtest.AddPrefix("/some/key") // Create a new node. fakeClient.ExpectNotFoundGet(key) obj := &TestResource{ObjectMeta: api.ObjectMeta{Name: "foo"}, Value: 1} f := storage.SimpleUpdate(func(in runtime.Object) (runtime.Object, error) { return obj, nil }) ignoreNotFound := false err := helper.GuaranteedUpdate("/some/key", &TestResource{}, ignoreNotFound, f) if err == nil { t.Errorf("Expected error for key not found.") } ignoreNotFound = true err = helper.GuaranteedUpdate("/some/key", &TestResource{}, ignoreNotFound, f) if err != nil { t.Errorf("Unexpected error %v.", err) } }
func TestStorageError(t *testing.T) { fakeClient := tools.NewFakeEtcdClient(t) prefixedKey := etcdtest.AddPrefix("pods") fakeClient.ExpectNotFoundGet(prefixedKey) cacher := newTestCacher(fakeClient) fakeClient.WaitForWatchCompletion() podFoo := makeTestPod("foo") // 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) } fakeClient.WatchResponse <- &etcd.Response{ Action: "create", Node: &etcd.Node{ Value: string(runtime.EncodeOrDie(testapi.Default.Codec(), podFoo)), CreatedIndex: 1, ModifiedIndex: 1, }, } _ = <-watcher.ResultChan() // Injecting error is simulating error from etcd. // This is almost the same what would happen e.g. in case of // "error too old" when reconnecting to etcd watch. fakeClient.WatchInjectError <- fmt.Errorf("fake error") _, ok := <-watcher.ResultChan() if ok { t.Errorf("unexpected event") } }
func TestWatch(t *testing.T) { codec := testapi.Default.Codec() fakeClient := tools.NewFakeEtcdClient(t) key := "/some/key" prefixedKey := etcdtest.AddPrefix(key) fakeClient.ExpectNotFoundGet(prefixedKey) h := newEtcdHelper(fakeClient, codec, etcdtest.PathPrefix()) watching, err := h.Watch(context.TODO(), key, 0, storage.Everything) if err != nil { t.Fatalf("Unexpected error: %v", err) } fakeClient.WaitForWatchCompletion() // when server returns not found, the watch index starts at the next value (1) if fakeClient.WatchIndex != 1 { t.Errorf("Expected client to be at index %d, got %#v", 1, fakeClient) } // Test normal case pod := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}} podBytes, _ := codec.Encode(pod) fakeClient.WatchResponse <- &etcd.Response{ Action: "set", Node: &etcd.Node{ Value: string(podBytes), }, } 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) } // Test error case fakeClient.WatchInjectError <- fmt.Errorf("Injected error") if errEvent, ok := <-watching.ResultChan(); !ok { t.Errorf("no error result?") } else { if e, a := watch.Error, errEvent.Type; e != a { t.Errorf("Expected %v, got %v", e, a) } if e, a := "Injected error", errEvent.Object.(*unversioned.Status).Message; e != a { t.Errorf("Expected %v, got %v", e, a) } } // Did everything shut down? if _, open := <-fakeClient.WatchResponse; open { t.Errorf("An injected error did not cause a graceful shutdown") } if _, open := <-watching.ResultChan(); open { t.Errorf("An injected error did not cause a graceful shutdown") } }
func TestCreateNilOutParam(t *testing.T) { obj := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}} fakeClient := tools.NewFakeEtcdClient(t) helper := newEtcdHelper(fakeClient, testapi.Default.Codec(), etcdtest.PathPrefix()) err := helper.Create("/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"}} fakeClient := tools.NewFakeEtcdClient(t) fakeClient.CasErr = fakeClient.NewError(123) helper := newEtcdHelper(fakeClient, testapi.Default.Codec(), etcdtest.PathPrefix()) err := helper.Set("/some/key", obj, nil, 5) if err == nil { t.Errorf("Expecting error.") } }
func TestGuaranteedUpdate(t *testing.T) { fakeClient := tools.NewFakeEtcdClient(t) fakeClient.TestIndex = true helper := newEtcdHelper(fakeClient, codec, etcdtest.PathPrefix()) key := etcdtest.AddPrefix("/some/key") // Create a new node. fakeClient.ExpectNotFoundGet(key) obj := &TestResource{ObjectMeta: api.ObjectMeta{Name: "foo"}, Value: 1} err := helper.GuaranteedUpdate("/some/key", &TestResource{}, true, storage.SimpleUpdate(func(in runtime.Object) (runtime.Object, error) { return obj, nil })) if err != nil { t.Errorf("Unexpected error %#v", err) } data, err := codec.Encode(obj) if err != nil { t.Errorf("Unexpected error %#v", err) } expect := string(data) got := fakeClient.Data[key].R.Node.Value if expect != got { t.Errorf("Wanted %v, got %v", expect, got) } // Update an existing node. callbackCalled := false objUpdate := &TestResource{ObjectMeta: api.ObjectMeta{Name: "foo"}, Value: 2} err = helper.GuaranteedUpdate("/some/key", &TestResource{}, true, storage.SimpleUpdate(func(in runtime.Object) (runtime.Object, error) { callbackCalled = true if in.(*TestResource).Value != 1 { t.Errorf("Callback input was not current set value") } return objUpdate, nil })) if err != nil { t.Errorf("Unexpected error %#v", err) } data, err = codec.Encode(objUpdate) if err != nil { t.Errorf("Unexpected error %#v", err) } expect = string(data) got = fakeClient.Data[key].R.Node.Value if expect != got { t.Errorf("Wanted %v, got %v", expect, got) } if !callbackCalled { t.Errorf("tryUpdate callback should have been called.") } }
func TestEtcdMasterOther(t *testing.T) { path := "foo" etcd := tools.NewFakeEtcdClient(t) etcd.Set(path, "baz", 0) master := NewEtcdMasterElector(etcd) w := master.Elect(path, "bar") result := <-w.ResultChan() if result.Type != watch.Modified || result.Object.(Master) != "baz" { t.Errorf("unexpected event: %#v", result) } w.Stop() }
// 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") }
func TestGuaranteedUpdate_CreateCollision(t *testing.T) { fakeClient := tools.NewFakeEtcdClient(t) fakeClient.TestIndex = true helper := newEtcdHelper(fakeClient, codec, etcdtest.PathPrefix()) key := etcdtest.AddPrefix("/some/key") fakeClient.ExpectNotFoundGet(key) const concurrency = 10 var wgDone sync.WaitGroup var wgForceCollision sync.WaitGroup wgDone.Add(concurrency) wgForceCollision.Add(concurrency) for i := 0; i < concurrency; i++ { // Increment TestResource.Value by 1 go func() { defer wgDone.Done() firstCall := true err := helper.GuaranteedUpdate("/some/key", &TestResource{}, true, storage.SimpleUpdate(func(in runtime.Object) (runtime.Object, error) { defer func() { firstCall = false }() if firstCall { // Force collision by joining all concurrent GuaranteedUpdate operations here. wgForceCollision.Done() wgForceCollision.Wait() } currValue := in.(*TestResource).Value obj := &TestResource{ObjectMeta: api.ObjectMeta{Name: "foo"}, Value: currValue + 1} return obj, nil })) if err != nil { t.Errorf("Unexpected error %#v", err) } }() } wgDone.Wait() // Check that stored TestResource has received all updates. body := fakeClient.Data[key].R.Node.Value stored := &TestResource{} if err := codec.DecodeInto([]byte(body), stored); err != nil { t.Errorf("Error decoding stored value: %v", body) } if stored.Value != concurrency { t.Errorf("Some of the writes were lost. Stored value: %d", stored.Value) } }
// setUp is a convience function for setting up for (most) tests. func setUp(t *testing.T) (Master, Config, *assert.Assertions) { master := Master{} config := Config{} fakeClient := tools.NewFakeEtcdClient(t) fakeClient.Machines = []string{"http://machine1:4001", "http://machine2", "http://machine3:4003"} storageVersions := make(map[string]string) storageDestinations := NewStorageDestinations() storageDestinations.AddAPIGroup("", etcdstorage.NewEtcdStorage(fakeClient, testapi.Default.Codec(), etcdtest.PathPrefix())) storageDestinations.AddAPIGroup("extensions", etcdstorage.NewEtcdStorage(fakeClient, testapi.Extensions.Codec(), etcdtest.PathPrefix())) config.StorageDestinations = storageDestinations storageVersions[""] = testapi.Default.Version() storageVersions["extensions"] = testapi.Extensions.GroupAndVersion() config.StorageVersions = storageVersions master.nodeRegistry = registrytest.NewNodeRegistry([]string{"node1", "node2"}, api.NodeResources{}) return master, config, assert.New(t) }
func TestWatchListIgnoresRootKey(t *testing.T) { codec := testapi.Default.Codec() pod := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}} key := "/some/key" prefixedKey := etcdtest.AddPrefix(key) fakeClient := tools.NewFakeEtcdClient(t) h := newEtcdHelper(fakeClient, codec, etcdtest.PathPrefix()) watching, err := h.WatchList(context.TODO(), key, 1, storage.Everything) if err != nil { t.Fatalf("Unexpected error: %v", err) } fakeClient.WaitForWatchCompletion() // This is the root directory of the watch, which happens to have a value encoded fakeClient.WatchResponse <- &etcd.Response{ Action: "delete", PrevNode: &etcd.Node{ Key: prefixedKey, Value: runtime.EncodeOrDie(codec, pod), CreatedIndex: 1, ModifiedIndex: 1, }, } // Delete of the parent directory of a key is an event that a list watch would receive, // but will have no value so the decode will fail. fakeClient.WatchResponse <- &etcd.Response{ Action: "delete", PrevNode: &etcd.Node{ Key: prefixedKey, Value: "", CreatedIndex: 1, ModifiedIndex: 1, }, } close(fakeClient.WatchStop) // the existing node is detected and the index set _, open := <-watching.ResultChan() if open { t.Fatalf("unexpected channel open") } watching.Stop() }
func TestPrefixEtcdKey(t *testing.T) { fakeClient := tools.NewFakeEtcdClient(t) prefix := path.Join("/", etcdtest.PathPrefix()) helper := newEtcdHelper(fakeClient, testapi.Default.Codec(), prefix) baseKey := "/some/key" // Verify prefix is added keyBefore := baseKey keyAfter := helper.prefixEtcdKey(keyBefore) assert.Equal(t, keyAfter, path.Join(prefix, baseKey), "Prefix incorrectly added by EtcdHelper") // Verify prefix is not added keyBefore = path.Join(prefix, baseKey) keyAfter = helper.prefixEtcdKey(keyBefore) assert.Equal(t, keyBefore, keyAfter, "Prefix incorrectly added by EtcdHelper") }
func TestGetNotFoundErr(t *testing.T) { fakeClient := tools.NewFakeEtcdClient(t) helper := newEtcdHelper(fakeClient, testapi.Default.Codec(), etcdtest.PathPrefix()) key1 := etcdtest.AddPrefix("/some/key") fakeClient.Data[key1] = tools.EtcdResponseWithError{ R: &etcd.Response{ Node: nil, }, E: &etcd.EtcdError{ ErrorCode: 100, }, } key2 := etcdtest.AddPrefix("/some/key2") fakeClient.Data[key2] = tools.EtcdResponseWithError{ R: &etcd.Response{ Node: nil, }, } key3 := etcdtest.AddPrefix("/some/key3") fakeClient.Data[key3] = tools.EtcdResponseWithError{ R: &etcd.Response{ Node: &etcd.Node{ Value: "", }, }, } try := func(key string) { var got api.Pod err := helper.Get(key, &got, false) if err == nil { t.Errorf("%s: wanted error but didn't get one", key) } err = helper.Get(key, &got, true) if err != nil { t.Errorf("%s: didn't want error but got %#v", key, err) } } try("/some/key") try("/some/key2") try("/some/key3") }
func TestEtcdMasterNoOther(t *testing.T) { path := "foo" e := tools.NewFakeEtcdClient(t) e.TestIndex = true e.Data["foo"] = tools.EtcdResponseWithError{ R: &etcd.Response{ Node: nil, }, E: &etcd.EtcdError{ ErrorCode: tools.EtcdErrorCodeNotFound, }, } master := NewEtcdMasterElector(e) w := master.Elect(path, "bar") result := <-w.ResultChan() if result.Type != watch.Modified || result.Object.(Master) != "bar" { t.Errorf("unexpected event: %#v", result) } w.Stop() }
func TestWatchFromOtherError(t *testing.T) { fakeClient := tools.NewFakeEtcdClient(t) key := "/some/key" prefixedKey := etcdtest.AddPrefix(key) fakeClient.Data[prefixedKey] = tools.EtcdResponseWithError{ R: &etcd.Response{ Node: nil, }, E: &etcd.EtcdError{ Index: 2, ErrorCode: 101, }, } h := newEtcdHelper(fakeClient, codec, etcdtest.PathPrefix()) watching, err := h.Watch(context.TODO(), key, 0, storage.Everything) if err != nil { t.Fatalf("Unexpected error: %v", err) } errEvent := <-watching.ResultChan() if e, a := watch.Error, errEvent.Type; e != a { t.Errorf("Expected %v, got %v", e, a) } if e, a := "101: () [2]", errEvent.Object.(*unversioned.Status).Message; e != a { t.Errorf("Expected %v, got %v", e, a) } select { case _, ok := <-watching.ResultChan(): if ok { t.Fatalf("expected result channel to be closed") } case <-time.After(util.ForeverTestTimeout): t.Fatalf("watch should have closed channel: %#v", watching) } if fakeClient.WatchResponse != nil || fakeClient.WatchIndex != 0 { t.Fatalf("Watch should not have been invoked: %#v", fakeClient) } }
func TestEtcdMasterNoOtherThenConflict(t *testing.T) { path := "foo" e := tools.NewFakeEtcdClient(t) e.TestIndex = true // Ok, so we set up a chain of responses from etcd: // 1) Nothing there // 2) conflict (someone else wrote) // 3) new value (the data they wrote) empty := tools.EtcdResponseWithError{ R: &etcd.Response{ Node: nil, }, E: &etcd.EtcdError{ ErrorCode: tools.EtcdErrorCodeNotFound, }, } empty.N = &tools.EtcdResponseWithError{ R: &etcd.Response{}, E: &etcd.EtcdError{ ErrorCode: tools.EtcdErrorCodeNodeExist, }, } empty.N.N = &tools.EtcdResponseWithError{ R: &etcd.Response{ Node: &etcd.Node{ Value: "baz", }, }, } e.Data["foo"] = empty master := NewEtcdMasterElector(e) w := master.Elect(path, "bar") result := <-w.ResultChan() if result.Type != watch.Modified || result.Object.(Master) != "bar" { t.Errorf("unexpected event: %#v", result) } w.Stop() }
func TestGet(t *testing.T) { fakeClient := tools.NewFakeEtcdClient(t) helper := newEtcdHelper(fakeClient, testapi.Default.Codec(), etcdtest.PathPrefix()) key := etcdtest.AddPrefix("/some/key") grace := int64(30) expect := api.Pod{ ObjectMeta: api.ObjectMeta{Name: "foo"}, Spec: api.PodSpec{ RestartPolicy: api.RestartPolicyAlways, DNSPolicy: api.DNSClusterFirst, TerminationGracePeriodSeconds: &grace, }, } fakeClient.Set(key, runtime.EncodeOrDie(testapi.Default.Codec(), &expect), 0) var got api.Pod err := helper.Get("/some/key", &got, false) if err != nil { t.Errorf("Unexpected error %#v", err) } if !reflect.DeepEqual(got, expect) { t.Errorf("Wanted %#v, got %#v", expect, got) } }
func TestSetWithVersion(t *testing.T) { obj := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "1"}} fakeClient := tools.NewFakeEtcdClient(t) fakeClient.TestIndex = true helper := newEtcdHelper(fakeClient, testapi.Default.Codec(), etcdtest.PathPrefix()) key := etcdtest.AddPrefix("/some/key") fakeClient.Data[key] = tools.EtcdResponseWithError{ R: &etcd.Response{ Node: &etcd.Node{ Value: runtime.EncodeOrDie(testapi.Default.Codec(), obj), ModifiedIndex: 1, }, }, } returnedObj := &api.Pod{} err := helper.Set("/some/key", obj, returnedObj, 7) if err != nil { t.Fatalf("Unexpected error %#v", err) } data, err := testapi.Default.Codec().Encode(obj) if err != nil { t.Fatalf("Unexpected error %#v", err) } expect := string(data) got := fakeClient.Data[key].R.Node.Value if expect != got { t.Errorf("Wanted %v, got %v", expect, got) } if e, a := uint64(7), fakeClient.LastSetTTL; e != a { t.Errorf("Wanted %v, got %v", e, a) } if obj.ResourceVersion != returnedObj.ResourceVersion || obj.Name != returnedObj.Name { t.Errorf("If set was successful but returned object did not have correct resource version") } }
func TestList(t *testing.T) { fakeClient := tools.NewFakeEtcdClient(t) helper := newEtcdHelper(fakeClient, testapi.Default.Codec(), etcdtest.PathPrefix()) key := etcdtest.AddPrefix("/some/key") fakeClient.Data[key] = tools.EtcdResponseWithError{ R: &etcd.Response{ EtcdIndex: 10, Node: &etcd.Node{ Dir: true, Nodes: []*etcd.Node{ { Key: "/foo", Value: getEncodedPod("foo"), Dir: false, ModifiedIndex: 1, }, { Key: "/bar", Value: getEncodedPod("bar"), Dir: false, ModifiedIndex: 2, }, { Key: "/baz", Value: getEncodedPod("baz"), Dir: false, ModifiedIndex: 3, }, }, }, }, } grace := int64(30) expect := api.PodList{ ListMeta: unversioned.ListMeta{ResourceVersion: "10"}, Items: []api.Pod{ { ObjectMeta: api.ObjectMeta{Name: "bar", ResourceVersion: "2"}, Spec: api.PodSpec{ RestartPolicy: api.RestartPolicyAlways, DNSPolicy: api.DNSClusterFirst, TerminationGracePeriodSeconds: &grace, }, }, { ObjectMeta: api.ObjectMeta{Name: "baz", ResourceVersion: "3"}, Spec: api.PodSpec{ RestartPolicy: api.RestartPolicyAlways, DNSPolicy: api.DNSClusterFirst, TerminationGracePeriodSeconds: &grace, }, }, { ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "1"}, Spec: api.PodSpec{ RestartPolicy: api.RestartPolicyAlways, DNSPolicy: api.DNSClusterFirst, TerminationGracePeriodSeconds: &grace, }, }, }, } var got api.PodList err := helper.List("/some/key", storage.Everything, &got) if err != nil { t.Errorf("Unexpected error %v", err) } if e, a := expect, got; !reflect.DeepEqual(e, a) { t.Errorf("Expected %#v, got %#v", e, a) } }
func TestGuaranteedUpdateTTL(t *testing.T) { fakeClient := tools.NewFakeEtcdClient(t) fakeClient.TestIndex = true helper := newEtcdHelper(fakeClient, codec, etcdtest.PathPrefix()) key := etcdtest.AddPrefix("/some/key") // Create a new node. fakeClient.ExpectNotFoundGet(key) obj := &TestResource{ObjectMeta: api.ObjectMeta{Name: "foo"}, Value: 1} err := helper.GuaranteedUpdate("/some/key", &TestResource{}, true, func(in runtime.Object, res storage.ResponseMeta) (runtime.Object, *uint64, error) { if res.TTL != 0 { t.Fatalf("unexpected response meta: %#v", res) } ttl := uint64(10) return obj, &ttl, nil }) if err != nil { t.Errorf("Unexpected error %#v", err) } data, err := codec.Encode(obj) if err != nil { t.Errorf("Unexpected error %#v", err) } expect := string(data) got := fakeClient.Data[key].R.Node.Value if expect != got { t.Errorf("Wanted %v, got %v", expect, got) } if fakeClient.Data[key].R.Node.TTL != 10 { t.Errorf("expected TTL set: %d", fakeClient.Data[key].R.Node.TTL) } // Update an existing node. callbackCalled := false objUpdate := &TestResource{ObjectMeta: api.ObjectMeta{Name: "foo"}, Value: 2} err = helper.GuaranteedUpdate("/some/key", &TestResource{}, true, func(in runtime.Object, res storage.ResponseMeta) (runtime.Object, *uint64, error) { if res.TTL != 10 { t.Fatalf("unexpected response meta: %#v", res) } callbackCalled = true if in.(*TestResource).Value != 1 { t.Errorf("Callback input was not current set value") } return objUpdate, nil, nil }) if err != nil { t.Errorf("Unexpected error %#v", err) } data, err = codec.Encode(objUpdate) if err != nil { t.Errorf("Unexpected error %#v", err) } expect = string(data) got = fakeClient.Data[key].R.Node.Value if expect != got { t.Errorf("Wanted %v, got %v", expect, got) } if fakeClient.Data[key].R.Node.TTL != 10 { t.Errorf("expected TTL remained set: %d", fakeClient.Data[key].R.Node.TTL) } // Update an existing node and change ttl callbackCalled = false objUpdate = &TestResource{ObjectMeta: api.ObjectMeta{Name: "foo"}, Value: 3} err = helper.GuaranteedUpdate("/some/key", &TestResource{}, true, func(in runtime.Object, res storage.ResponseMeta) (runtime.Object, *uint64, error) { if res.TTL != 10 { t.Fatalf("unexpected response meta: %#v", res) } callbackCalled = true if in.(*TestResource).Value != 2 { t.Errorf("Callback input was not current set value") } newTTL := uint64(20) return objUpdate, &newTTL, nil }) if err != nil { t.Errorf("Unexpected error %#v", err) } data, err = codec.Encode(objUpdate) if err != nil { t.Errorf("Unexpected error %#v", err) } expect = string(data) got = fakeClient.Data[key].R.Node.Value if expect != got { t.Errorf("Wanted %v, got %v", expect, got) } if fakeClient.Data[key].R.Node.TTL != 20 { t.Errorf("expected TTL changed: %d", fakeClient.Data[key].R.Node.TTL) } if !callbackCalled { t.Errorf("tryUpdate callback should have been called.") } }
func TestWatch(t *testing.T) { fakeClient := tools.NewFakeEtcdClient(t) prefixedKey := etcdtest.AddPrefix("pods") fakeClient.ExpectNotFoundGet(prefixedKey) cacher := newTestCacher(fakeClient) fakeClient.WaitForWatchCompletion() podFoo := makeTestPod("foo") podBar := makeTestPod("bar") testCases := []struct { object *api.Pod etcdResponse *etcd.Response event watch.EventType filtered bool }{ { object: podFoo, etcdResponse: &etcd.Response{ Action: "create", Node: &etcd.Node{ Value: string(runtime.EncodeOrDie(testapi.Default.Codec(), podFoo)), CreatedIndex: 2, ModifiedIndex: 2, }, }, event: watch.Added, filtered: true, }, { object: podBar, etcdResponse: &etcd.Response{ Action: "create", Node: &etcd.Node{ Value: string(runtime.EncodeOrDie(testapi.Default.Codec(), podBar)), CreatedIndex: 3, ModifiedIndex: 3, }, }, event: watch.Added, filtered: false, }, { object: podFoo, etcdResponse: &etcd.Response{ Action: "set", Node: &etcd.Node{ Value: string(runtime.EncodeOrDie(testapi.Default.Codec(), podFoo)), CreatedIndex: 2, ModifiedIndex: 4, }, PrevNode: &etcd.Node{ Value: string(runtime.EncodeOrDie(testapi.Default.Codec(), podFoo)), CreatedIndex: 2, ModifiedIndex: 2, }, }, event: watch.Modified, filtered: true, }, } // Set up Watch for object "podFoo". watcher, err := cacher.Watch(context.TODO(), "pods/ns/foo", 2, storage.Everything) if err != nil { t.Fatalf("unexpected error: %v", err) } for _, test := range testCases { fakeClient.WatchResponse <- test.etcdResponse if test.filtered { event := <-watcher.ResultChan() if e, a := test.event, event.Type; e != a { t.Errorf("%v %v", e, a) } // unset fields that are set by the infrastructure obj := event.Object.(*api.Pod) obj.ObjectMeta.ResourceVersion = "" obj.ObjectMeta.CreationTimestamp = unversioned.Time{} if e, a := test.object, obj; !reflect.DeepEqual(e, a) { t.Errorf("expected: %#v, got: %#v", e, a) } } } // Check whether we get too-old error. _, err = cacher.Watch(context.TODO(), "pods/ns/foo", 1, storage.Everything) if err == nil { t.Errorf("exepcted 'error too old' error") } // Now test watch with initial state. initialWatcher, err := cacher.Watch(context.TODO(), "pods/ns/foo", 2, storage.Everything) if err != nil { t.Fatalf("unexpected error: %v", err) } for _, test := range testCases { if test.filtered { event := <-initialWatcher.ResultChan() if e, a := test.event, event.Type; e != a { t.Errorf("%v %v", e, a) } // unset fields that are set by the infrastructure obj := event.Object.(*api.Pod) obj.ObjectMeta.ResourceVersion = "" obj.ObjectMeta.CreationTimestamp = unversioned.Time{} if e, a := test.object, obj; !reflect.DeepEqual(e, a) { t.Errorf("expected: %#v, got: %#v", e, a) } } } // 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) } select { case event := <-nowWatcher.ResultChan(): if obj := event.Object.(*api.Pod); event.Type != watch.Added || obj.ResourceVersion != "4" { t.Errorf("unexpected event: %v", event) } case <-time.After(util.ForeverTestTimeout): t.Errorf("timed out waiting for an event") } // Emit a new event and check if it is observed by the watcher. fakeClient.WatchResponse <- &etcd.Response{ Action: "set", Node: &etcd.Node{ Value: string(runtime.EncodeOrDie(testapi.Default.Codec(), podFoo)), CreatedIndex: 2, ModifiedIndex: 5, }, PrevNode: &etcd.Node{ Value: string(runtime.EncodeOrDie(testapi.Default.Codec(), podFoo)), CreatedIndex: 2, ModifiedIndex: 4, }, } event := <-nowWatcher.ResultChan() obj := event.Object.(*api.Pod) if event.Type != watch.Modified || obj.ResourceVersion != "5" { t.Errorf("unexpected event: %v", event) } close(fakeClient.WatchResponse) }
func TestListFromMemory(t *testing.T) { fakeClient := tools.NewFakeEtcdClient(t) prefixedKey := etcdtest.AddPrefix("pods") fakeClient.ExpectNotFoundGet(prefixedKey) cacher := newTestCacher(fakeClient) fakeClient.WaitForWatchCompletion() podFoo := makeTestPod("foo") podBar := makeTestPod("bar") podBaz := makeTestPod("baz") podFooPrime := makeTestPod("foo") podFooPrime.Spec.NodeName = "fakeNode" testCases := []*etcd.Response{ { Action: "create", Node: &etcd.Node{ Value: string(runtime.EncodeOrDie(testapi.Default.Codec(), podFoo)), CreatedIndex: 1, ModifiedIndex: 1, }, }, { Action: "create", Node: &etcd.Node{ Value: string(runtime.EncodeOrDie(testapi.Default.Codec(), podBar)), CreatedIndex: 2, ModifiedIndex: 2, }, }, { Action: "create", Node: &etcd.Node{ Value: string(runtime.EncodeOrDie(testapi.Default.Codec(), podBaz)), CreatedIndex: 3, ModifiedIndex: 3, }, }, { Action: "set", Node: &etcd.Node{ Value: string(runtime.EncodeOrDie(testapi.Default.Codec(), podFooPrime)), CreatedIndex: 1, ModifiedIndex: 4, }, PrevNode: &etcd.Node{ Value: string(runtime.EncodeOrDie(testapi.Default.Codec(), podFoo)), CreatedIndex: 1, ModifiedIndex: 1, }, }, { Action: "delete", Node: &etcd.Node{ CreatedIndex: 1, ModifiedIndex: 5, }, PrevNode: &etcd.Node{ Value: string(runtime.EncodeOrDie(testapi.Default.Codec(), podBar)), CreatedIndex: 1, ModifiedIndex: 1, }, }, } // Propagate some data to etcd. for _, test := range testCases { fakeClient.WatchResponse <- test } if err := waitForUpToDateCache(cacher, 5); err != nil { t.Errorf("watch cache didn't propagated correctly: %v", err) } result := &api.PodList{} if err := cacher.ListFromMemory("pods/ns", result); err != nil { t.Errorf("unexpected error: %v", err) } if result.ListMeta.ResourceVersion != "5" { t.Errorf("incorrect resource version: %v", result.ListMeta.ResourceVersion) } if len(result.Items) != 2 { t.Errorf("unexpected list result: %d", len(result.Items)) } keys := sets.String{} for _, item := range result.Items { keys.Insert(item.ObjectMeta.Name) } if !keys.HasAll("foo", "baz") { t.Errorf("unexpected list result: %#v", result) } for _, item := range result.Items { // unset fields that are set by the infrastructure item.ObjectMeta.ResourceVersion = "" item.ObjectMeta.CreationTimestamp = unversioned.Time{} var expected *api.Pod switch item.ObjectMeta.Name { case "foo": expected = podFooPrime case "baz": expected = podBaz default: t.Errorf("unexpected item: %v", item) } if e, a := *expected, item; !reflect.DeepEqual(e, a) { t.Errorf("expected: %#v, got: %#v", e, a) } } close(fakeClient.WatchResponse) }
func TestFiltering(t *testing.T) { fakeClient := tools.NewFakeEtcdClient(t) prefixedKey := etcdtest.AddPrefix("pods") fakeClient.ExpectNotFoundGet(prefixedKey) cacher := newTestCacher(fakeClient) fakeClient.WaitForWatchCompletion() podFoo := makeTestPod("foo") podFoo.ObjectMeta.Labels = map[string]string{"filter": "foo"} podFooFiltered := makeTestPod("foo") testCases := []struct { object *api.Pod etcdResponse *etcd.Response filtered bool event watch.EventType }{ { object: podFoo, etcdResponse: &etcd.Response{ Action: "create", Node: &etcd.Node{ Value: string(runtime.EncodeOrDie(testapi.Default.Codec(), podFoo)), CreatedIndex: 1, ModifiedIndex: 1, }, }, filtered: true, event: watch.Added, }, { object: podFooFiltered, etcdResponse: &etcd.Response{ Action: "set", Node: &etcd.Node{ Value: string(runtime.EncodeOrDie(testapi.Default.Codec(), podFooFiltered)), CreatedIndex: 1, ModifiedIndex: 2, }, PrevNode: &etcd.Node{ Value: string(runtime.EncodeOrDie(testapi.Default.Codec(), podFoo)), CreatedIndex: 1, ModifiedIndex: 1, }, }, filtered: true, // Deleted, because the new object doesn't match filter. event: watch.Deleted, }, { object: podFoo, etcdResponse: &etcd.Response{ Action: "set", Node: &etcd.Node{ Value: string(runtime.EncodeOrDie(testapi.Default.Codec(), podFoo)), CreatedIndex: 1, ModifiedIndex: 3, }, PrevNode: &etcd.Node{ Value: string(runtime.EncodeOrDie(testapi.Default.Codec(), podFooFiltered)), CreatedIndex: 1, ModifiedIndex: 2, }, }, filtered: true, // Added, because the previous object didn't match filter. event: watch.Added, }, { object: podFoo, etcdResponse: &etcd.Response{ Action: "set", Node: &etcd.Node{ Value: string(runtime.EncodeOrDie(testapi.Default.Codec(), podFoo)), CreatedIndex: 1, ModifiedIndex: 4, }, PrevNode: &etcd.Node{ Value: string(runtime.EncodeOrDie(testapi.Default.Codec(), podFoo)), CreatedIndex: 1, ModifiedIndex: 3, }, }, filtered: true, event: watch.Modified, }, { object: podFoo, etcdResponse: &etcd.Response{ Action: "delete", Node: &etcd.Node{ CreatedIndex: 1, ModifiedIndex: 5, }, PrevNode: &etcd.Node{ Value: string(runtime.EncodeOrDie(testapi.Default.Codec(), podFoo)), CreatedIndex: 1, ModifiedIndex: 4, }, }, filtered: true, event: watch.Deleted, }, } // Set up Watch for object "podFoo" with label filter set. selector := labels.SelectorFromSet(labels.Set{"filter": "foo"}) filter := 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.Labels())) } watcher, err := cacher.Watch(context.TODO(), "pods/ns/foo", 1, filter) if err != nil { t.Fatalf("unexpected error: %v", err) } for _, test := range testCases { fakeClient.WatchResponse <- test.etcdResponse if test.filtered { event := <-watcher.ResultChan() if e, a := test.event, event.Type; e != a { t.Errorf("%v %v", e, a) } // unset fields that are set by the infrastructure obj := event.Object.(*api.Pod) obj.ObjectMeta.ResourceVersion = "" obj.ObjectMeta.CreationTimestamp = unversioned.Time{} if e, a := test.object, obj; !reflect.DeepEqual(e, a) { t.Errorf("expected: %#v, got: %#v", e, a) } } } close(fakeClient.WatchResponse) }