func testData() (*api.PodList, *api.ServiceList) { pods := &api.PodList{ ListMeta: unversioned.ListMeta{ ResourceVersion: "15", }, Items: []api.Pod{ { ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "test", ResourceVersion: "10"}, Spec: apitesting.DeepEqualSafePodSpec(), }, { ObjectMeta: api.ObjectMeta{Name: "bar", Namespace: "test", ResourceVersion: "11"}, Spec: apitesting.DeepEqualSafePodSpec(), }, }, } svc := &api.ServiceList{ ListMeta: unversioned.ListMeta{ ResourceVersion: "16", }, Items: []api.Service{ { ObjectMeta: api.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "12"}, Spec: api.ServiceSpec{ Type: "ClusterIP", SessionAffinity: "None", }, }, }, } return pods, svc }
func TestList(t *testing.T) { server := NewEtcdTestClientServer(t) defer server.Terminate(t) key := etcdtest.AddPrefix("/some/key") helper := newEtcdHelper(server.client, testapi.Default.Codec(), key) list := api.PodList{ Items: []api.Pod{ { ObjectMeta: api.ObjectMeta{Name: "bar"}, Spec: apitesting.DeepEqualSafePodSpec(), }, { ObjectMeta: api.ObjectMeta{Name: "baz"}, Spec: apitesting.DeepEqualSafePodSpec(), }, { ObjectMeta: api.ObjectMeta{Name: "foo"}, Spec: apitesting.DeepEqualSafePodSpec(), }, }, } createPodList(t, helper, &list) 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(), key, 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) } }
// TestListAcrossDirectories ensures that the client excludes directories and flattens tree-response - simulates cross-namespace query func TestListAcrossDirectories(t *testing.T) { server := NewEtcdTestClientServer(t) defer server.Terminate(t) rootkey := etcdtest.AddPrefix("/some/key") key1 := etcdtest.AddPrefix("/some/key/directory1") key2 := etcdtest.AddPrefix("/some/key/directory2") roothelper := newEtcdHelper(server.client, testapi.Default.Codec(), rootkey) helper1 := newEtcdHelper(server.client, testapi.Default.Codec(), key1) helper2 := newEtcdHelper(server.client, testapi.Default.Codec(), key2) list := api.PodList{ Items: []api.Pod{ { ObjectMeta: api.ObjectMeta{Name: "baz"}, Spec: apitesting.DeepEqualSafePodSpec(), }, { ObjectMeta: api.ObjectMeta{Name: "foo"}, Spec: apitesting.DeepEqualSafePodSpec(), }, { ObjectMeta: api.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(), rootkey, 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 testData() (*api.PodList, *api.ServiceList, *api.ReplicationControllerList) { pods := &api.PodList{ ListMeta: unversioned.ListMeta{ ResourceVersion: "15", }, Items: []api.Pod{ { ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "test", ResourceVersion: "10"}, Spec: apitesting.DeepEqualSafePodSpec(), }, { ObjectMeta: api.ObjectMeta{Name: "bar", Namespace: "test", ResourceVersion: "11"}, Spec: apitesting.DeepEqualSafePodSpec(), }, }, } svc := &api.ServiceList{ ListMeta: unversioned.ListMeta{ ResourceVersion: "16", }, Items: []api.Service{ { ObjectMeta: api.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "12"}, Spec: api.ServiceSpec{ SessionAffinity: "None", Type: api.ServiceTypeClusterIP, }, }, }, } rc := &api.ReplicationControllerList{ ListMeta: unversioned.ListMeta{ ResourceVersion: "17", }, Items: []api.ReplicationController{ { ObjectMeta: api.ObjectMeta{Name: "rc1", Namespace: "test", ResourceVersion: "18"}, Spec: api.ReplicationControllerSpec{ Replicas: 1, }, }, }, } return pods, svc, rc }
func TestListFiltered(t *testing.T) { server := NewEtcdTestClientServer(t) defer server.Terminate(t) key := etcdtest.AddPrefix("/some/key") helper := newEtcdHelper(server.client, testapi.Default.Codec(), key) list := api.PodList{ Items: []api.Pod{ { ObjectMeta: api.ObjectMeta{Name: "bar"}, Spec: apitesting.DeepEqualSafePodSpec(), }, { ObjectMeta: api.ObjectMeta{Name: "baz"}, Spec: apitesting.DeepEqualSafePodSpec(), }, { ObjectMeta: api.ObjectMeta{Name: "foo"}, Spec: apitesting.DeepEqualSafePodSpec(), }, }, } createPodList(t, helper, &list) filter := func(obj runtime.Object) bool { pod := obj.(*api.Pod) return pod.Name == "bar" } var got api.PodList err := helper.List(context.TODO(), key, filter, &got) if err != nil { t.Errorf("Unexpected error %v", err) } // Check to make certain that the filter function only returns "bar" if e, a := list.Items[0], got.Items[0]; !reflect.DeepEqual(e, a) { t.Errorf("Expected %#v, got %#v", e, a) } }
func watchTestData() ([]api.Pod, []watch.Event) { pods := []api.Pod{ { ObjectMeta: api.ObjectMeta{ Name: "foo", Namespace: "test", ResourceVersion: "10", }, Spec: apitesting.DeepEqualSafePodSpec(), }, } events := []watch.Event{ { Type: watch.Modified, Object: &api.Pod{ ObjectMeta: api.ObjectMeta{ Name: "foo", Namespace: "test", ResourceVersion: "11", }, Spec: apitesting.DeepEqualSafePodSpec(), }, }, { Type: watch.Deleted, Object: &api.Pod{ ObjectMeta: api.ObjectMeta{ Name: "foo", Namespace: "test", ResourceVersion: "12", }, Spec: apitesting.DeepEqualSafePodSpec(), }, }, } return pods, events }
func TestGet(t *testing.T) { server := NewEtcdTestClientServer(t) defer server.Terminate(t) key := etcdtest.AddPrefix("/some/key") helper := newEtcdHelper(server.client, testapi.Default.Codec(), key) expect := api.Pod{ ObjectMeta: api.ObjectMeta{Name: "foo"}, Spec: apitesting.DeepEqualSafePodSpec(), } var got api.Pod if err := helper.Set(context.TODO(), key, &expect, &got, 0); err != nil { t.Errorf("Unexpected error %#v", err) } expect = got if err := helper.Get(context.TODO(), key, &got, false); err != nil { t.Errorf("Unexpected error %#v", err) } if !reflect.DeepEqual(got, expect) { t.Errorf("Wanted %#v, got %#v", expect, got) } }
func makeTestPod(name string) *api.Pod { return &api.Pod{ ObjectMeta: api.ObjectMeta{Namespace: "ns", Name: name}, Spec: apitesting.DeepEqualSafePodSpec(), } }
func TestHelperReplace(t *testing.T) { expectPut := func(req *http.Request) bool { if req.Method != "PUT" { t.Errorf("unexpected method: %#v", req) return false } parts := splitPath(req.URL.Path) if parts[1] != "bar" { t.Errorf("url doesn't contain namespace: %#v", req.URL) return false } if parts[2] != "foo" { t.Errorf("url doesn't contain name: %#v", req) return false } return true } tests := []struct { Resp *http.Response RespFunc fake.HTTPClientFunc HttpErr error Overwrite bool Object runtime.Object ExpectObject runtime.Object Err bool Req func(*http.Request) bool }{ { HttpErr: errors.New("failure"), Err: true, }, { Object: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}, Resp: &http.Response{ StatusCode: http.StatusNotFound, Body: objBody(&unversioned.Status{Status: unversioned.StatusFailure}), }, Err: true, }, { Object: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}, ExpectObject: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}, Resp: &http.Response{ StatusCode: http.StatusOK, Body: objBody(&unversioned.Status{Status: unversioned.StatusSuccess}), }, Req: expectPut, }, { Object: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}, ExpectObject: &api.Pod{ ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "10"}, Spec: apitesting.DeepEqualSafePodSpec(), }, Overwrite: true, RespFunc: func(req *http.Request) (*http.Response, error) { if req.Method == "PUT" { return &http.Response{StatusCode: http.StatusOK, Body: objBody(&unversioned.Status{Status: unversioned.StatusSuccess})}, nil } return &http.Response{StatusCode: http.StatusOK, Body: objBody(&api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "10"}})}, nil }, Req: expectPut, }, { Object: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "10"}}, ExpectObject: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "10"}}, Resp: &http.Response{StatusCode: http.StatusOK, Body: objBody(&unversioned.Status{Status: unversioned.StatusSuccess})}, Req: expectPut, }, } for i, test := range tests { client := &fake.RESTClient{ Codec: testapi.Default.Codec(), Resp: test.Resp, Err: test.HttpErr, } if test.RespFunc != nil { client.Client = test.RespFunc } modifier := &Helper{ RESTClient: client, Codec: testapi.Default.Codec(), Versioner: testapi.Default.MetadataAccessor(), NamespaceScoped: true, } data := []byte{} if test.Object != nil { data = []byte(runtime.EncodeOrDie(testapi.Default.Codec(), test.Object)) } _, err := modifier.Replace("bar", "foo", test.Overwrite, data) if (err != nil) != test.Err { t.Errorf("%d: unexpected error: %t %v", i, test.Err, err) } if err != nil { continue } if test.Req != nil && !test.Req(client.Req) { t.Errorf("%d: unexpected request: %#v", i, client.Req) } body, err := ioutil.ReadAll(client.Req.Body) if err != nil { t.Fatalf("%d: unexpected error: %#v", i, err) } t.Logf("got body: %s", string(body)) expect := []byte{} if test.ExpectObject != nil { expect = []byte(runtime.EncodeOrDie(testapi.Default.Codec(), test.ExpectObject)) } if !reflect.DeepEqual(expect, body) { t.Errorf("%d: unexpected body: %s", i, string(body)) } } }
func TestMerge(t *testing.T) { grace := int64(30) tests := []struct { obj runtime.Object fragment string expected runtime.Object expectErr bool kind string }{ { kind: "Pod", obj: &api.Pod{ ObjectMeta: api.ObjectMeta{ Name: "foo", }, }, fragment: fmt.Sprintf(`{ "apiVersion": "%s" }`, testapi.Default.Version()), expected: &api.Pod{ ObjectMeta: api.ObjectMeta{ Name: "foo", }, Spec: apitesting.DeepEqualSafePodSpec(), }, }, /* TODO: uncomment this test once Merge is updated to use strategic-merge-patch. See #8449. { kind: "Pod", obj: &api.Pod{ ObjectMeta: api.ObjectMeta{ Name: "foo", }, Spec: api.PodSpec{ Containers: []api.Container{ api.Container{ Name: "c1", Image: "red-image", }, api.Container{ Name: "c2", Image: "blue-image", }, }, }, }, fragment: fmt.Sprintf(`{ "apiVersion": "%s", "spec": { "containers": [ { "name": "c1", "image": "green-image" } ] } }`, testapi.Default.Version()), expected: &api.Pod{ ObjectMeta: api.ObjectMeta{ Name: "foo", }, Spec: api.PodSpec{ Containers: []api.Container{ api.Container{ Name: "c1", Image: "green-image", }, api.Container{ Name: "c2", Image: "blue-image", }, }, }, }, }, */ { kind: "Pod", obj: &api.Pod{ ObjectMeta: api.ObjectMeta{ Name: "foo", }, }, fragment: fmt.Sprintf(`{ "apiVersion": "%s", "spec": { "volumes": [ {"name": "v1"}, {"name": "v2"} ] } }`, testapi.Default.Version()), expected: &api.Pod{ ObjectMeta: api.ObjectMeta{ Name: "foo", }, Spec: api.PodSpec{ Volumes: []api.Volume{ { Name: "v1", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}, }, { Name: "v2", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}, }, }, RestartPolicy: api.RestartPolicyAlways, DNSPolicy: api.DNSClusterFirst, TerminationGracePeriodSeconds: &grace, SecurityContext: &api.PodSecurityContext{}, }, }, }, { kind: "Pod", obj: &api.Pod{}, fragment: "invalid json", expected: &api.Pod{}, expectErr: true, }, { kind: "Service", obj: &api.Service{}, fragment: `{ "apiVersion": "badVersion" }`, expectErr: true, }, { kind: "Service", obj: &api.Service{ Spec: api.ServiceSpec{}, }, fragment: fmt.Sprintf(`{ "apiVersion": "%s", "spec": { "ports": [ { "port": 0 } ] } }`, testapi.Default.Version()), expected: &api.Service{ Spec: api.ServiceSpec{ SessionAffinity: "None", Type: api.ServiceTypeClusterIP, Ports: []api.ServicePort{ { Protocol: api.ProtocolTCP, Port: 0, }, }, }, }, }, { kind: "Service", obj: &api.Service{ Spec: api.ServiceSpec{ Selector: map[string]string{ "version": "v1", }, }, }, fragment: fmt.Sprintf(`{ "apiVersion": "%s", "spec": { "selector": { "version": "v2" } } }`, testapi.Default.Version()), expected: &api.Service{ Spec: api.ServiceSpec{ SessionAffinity: "None", Type: api.ServiceTypeClusterIP, Selector: map[string]string{ "version": "v2", }, }, }, }, } for i, test := range tests { out, err := Merge(test.obj, test.fragment, test.kind) if !test.expectErr { if err != nil { t.Errorf("testcase[%d], unexpected error: %v", i, err) } else if !reflect.DeepEqual(out, test.expected) { t.Errorf("\n\ntestcase[%d]\nexpected:\n%+v\nsaw:\n%+v", i, test.expected, out) } } if test.expectErr && err == nil { t.Errorf("testcase[%d], unexpected non-error", i) } } }
func TestUpdateWithRetries(t *testing.T) { codec := testapi.Default.Codec() rc := &api.ReplicationController{ ObjectMeta: api.ObjectMeta{Name: "rc", Labels: map[string]string{ "foo": "bar", }, }, Spec: api.ReplicationControllerSpec{ Selector: map[string]string{ "foo": "bar", }, Template: &api.PodTemplateSpec{ ObjectMeta: api.ObjectMeta{ Labels: map[string]string{ "foo": "bar", }, }, Spec: apitesting.DeepEqualSafePodSpec(), }, }, } // Test end to end updating of the rc with retries. Essentially make sure the update handler // sees the right updates, failures in update/get are handled properly, and that the updated // rc with new resource version is returned to the caller. Without any of these rollingupdate // will fail cryptically. newRc := *rc newRc.ResourceVersion = "2" newRc.Spec.Selector["baz"] = "foobar" updates := []*http.Response{ {StatusCode: 500, Body: objBody(codec, &api.ReplicationController{})}, {StatusCode: 500, Body: objBody(codec, &api.ReplicationController{})}, {StatusCode: 200, Body: objBody(codec, &newRc)}, } gets := []*http.Response{ {StatusCode: 500, Body: objBody(codec, &api.ReplicationController{})}, {StatusCode: 200, Body: objBody(codec, rc)}, } fakeClient := &fake.RESTClient{ Codec: codec, Client: fake.HTTPClientFunc(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { case p == testapi.Default.ResourcePath("replicationcontrollers", "default", "rc") && m == "PUT": update := updates[0] updates = updates[1:] // We should always get an update with a valid rc even when the get fails. The rc should always // contain the update. if c, ok := readOrDie(t, req, codec).(*api.ReplicationController); !ok || !reflect.DeepEqual(rc, c) { t.Errorf("Unexpected update body, got %+v expected %+v", c, rc) } else if sel, ok := c.Spec.Selector["baz"]; !ok || sel != "foobar" { t.Errorf("Expected selector label update, got %+v", c.Spec.Selector) } else { delete(c.Spec.Selector, "baz") } return update, nil case p == testapi.Default.ResourcePath("replicationcontrollers", "default", "rc") && m == "GET": get := gets[0] gets = gets[1:] return get, nil default: t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) return nil, nil } }), } clientConfig := &client.Config{Version: testapi.Default.Version()} client := client.NewOrDie(clientConfig) client.Client = fakeClient.Client if rc, err := updateWithRetries( client.ReplicationControllers("default"), rc, func(c *api.ReplicationController) { c.Spec.Selector["baz"] = "foobar" }); err != nil { t.Errorf("unexpected error: %v", err) } else if sel, ok := rc.Spec.Selector["baz"]; !ok || sel != "foobar" || rc.ResourceVersion != "2" { t.Errorf("Expected updated rc, got %+v", rc) } if len(updates) != 0 || len(gets) != 0 { t.Errorf("Remaining updates %+v gets %+v", updates, gets) } }