diff --git a/pkg/kube/client_test.go b/pkg/kube/client_test.go index df281f5f4..cd7c9850b 100644 --- a/pkg/kube/client_test.go +++ b/pkg/kube/client_test.go @@ -18,17 +18,15 @@ package kube import ( "bytes" - "encoding/json" "io" "io/ioutil" "net/http" "strings" "testing" - "k8s.io/kubernetes/pkg/api/meta" + "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/api/unversioned" - api "k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/api/validation" "k8s.io/kubernetes/pkg/client/restclient/fake" cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing" @@ -36,34 +34,106 @@ import ( "k8s.io/kubernetes/pkg/runtime" ) -func TestUpdateResource(t *testing.T) { +func objBody(codec runtime.Codec, obj runtime.Object) io.ReadCloser { + return ioutil.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(codec, obj)))) +} - tests := []struct { - name string - namespace string - modified *resource.Info - currentObj runtime.Object - err bool - errMessage string - }{ - { - name: "no changes when updating resources", - modified: createFakeInfo("nginx", nil), - currentObj: createFakePod("nginx", nil), - err: true, - errMessage: "Looks like there are no changes for nginx", +func newPod(name string) api.Pod { + return api.Pod{ + ObjectMeta: api.ObjectMeta{Name: name}, + Spec: api.PodSpec{ + Containers: []api.Container{{ + Name: "app:v4", + Image: "abc/app:v4", + Ports: []api.ContainerPort{{Name: "http", ContainerPort: 80}}, + }}, }, - //{ - //name: "valid update input", - //modified: createFakeInfo("nginx", map[string]string{"app": "nginx"}), - //currentObj: createFakePod("nginx", nil), - //}, } +} - for _, tt := range tests { - err := updateResource(tt.modified, tt.currentObj) - if err != nil && err.Error() != tt.errMessage { - t.Errorf("%q. expected error message: %v, got %v", tt.name, tt.errMessage, err) +func newPodList(names ...string) api.PodList { + var list api.PodList + for _, name := range names { + list.Items = append(list.Items, newPod(name)) + } + return list +} + +func notFoundBody() *unversioned.Status { + return &unversioned.Status{ + Code: http.StatusNotFound, + Status: unversioned.StatusFailure, + Reason: unversioned.StatusReasonNotFound, + Message: " \"\" not found", + Details: &unversioned.StatusDetails{}, + } +} + +func newResponse(code int, obj runtime.Object) (*http.Response, error) { + header := http.Header{} + header.Set("Content-Type", runtime.ContentTypeJSON) + body := ioutil.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(testapi.Default.Codec(), obj)))) + return &http.Response{StatusCode: code, Header: header, Body: body}, nil +} + +func TestUpdate(t *testing.T) { + listA := newPodList("starfish", "otter", "squid") + listB := newPodList("starfish", "otter", "dolphin") + listB.Items[0].Spec.Containers[0].Ports = []api.ContainerPort{{Name: "https", ContainerPort: 443}} + + actions := make(map[string]string) + + f, tf, codec, ns := cmdtesting.NewAPIFactory() + tf.Client = &fake.RESTClient{ + NegotiatedSerializer: ns, + Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { + p, m := req.URL.Path, req.Method + actions[p] = m + switch { + case p == "/namespaces/test/pods/starfish" && m == "GET": + return newResponse(200, &listA.Items[0]) + case p == "/namespaces/test/pods/otter" && m == "GET": + return newResponse(200, &listA.Items[1]) + case p == "/namespaces/test/pods/dolphin" && m == "GET": + return newResponse(404, notFoundBody()) + case p == "/namespaces/test/pods/starfish" && m == "PATCH": + data, err := ioutil.ReadAll(req.Body) + if err != nil { + t.Fatalf("could not dump request: %s", err) + } + req.Body.Close() + expected := `{"spec":{"containers":[{"name":"app:v4","ports":[{"containerPort":443,"name":"https","protocol":"TCP"},{"$patch":"delete","containerPort":80}]}]}}` + if string(data) != expected { + t.Errorf("expected patch %s, got %s", expected, string(data)) + } + return newResponse(200, &listB.Items[0]) + case p == "/namespaces/test/pods" && m == "POST": + return newResponse(200, &listB.Items[1]) + case p == "/namespaces/test/pods/squid" && m == "DELETE": + return newResponse(200, &listB.Items[1]) + default: + t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) + return nil, nil + } + }), + } + + c := &Client{Factory: f} + if err := c.Update("test", objBody(codec, &listA), objBody(codec, &listB)); err != nil { + t.Fatal(err) + } + + expectedActions := map[string]string{ + "/namespaces/test/pods/dolphin": "GET", + "/namespaces/test/pods/otter": "GET", + "/namespaces/test/pods/starfish": "PATCH", + "/namespaces/test/pods": "POST", + "/namespaces/test/pods/squid": "DELETE", + } + + for k, v := range expectedActions { + if m, ok := actions[k]; !ok || m != v { + t.Errorf("expected a %s request to %s", k, v) } } } @@ -319,52 +389,3 @@ spec: ports: - containerPort: 80 ` - -func createFakePod(name string, labels map[string]string) runtime.Object { - objectMeta := createObjectMeta(name, labels) - - object := &api.Pod{ - ObjectMeta: objectMeta, - } - - return object -} - -func createFakeInfo(name string, labels map[string]string) *resource.Info { - pod := createFakePod(name, labels) - marshaledObj, _ := json.Marshal(pod) - - mapping := &meta.RESTMapping{ - Resource: name, - Scope: meta.RESTScopeNamespace, - GroupVersionKind: unversioned.GroupVersionKind{ - Kind: "Pod", - Version: "v1", - }} - - client := &fake.RESTClient{ - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - header := http.Header{} - header.Set("Content-Type", runtime.ContentTypeJSON) - return &http.Response{ - StatusCode: 200, - Header: header, - Body: ioutil.NopCloser(bytes.NewReader(marshaledObj)), - }, nil - })} - info := resource.NewInfo(client, mapping, "default", "nginx", false) - - info.Object = pod - - return info -} - -func createObjectMeta(name string, labels map[string]string) api.ObjectMeta { - objectMeta := api.ObjectMeta{Name: name, Namespace: "default"} - - if labels != nil { - objectMeta.Labels = labels - } - - return objectMeta -}