func TestUpdateDirectory(t *testing.T) { _, svc, rc := testData() f, tf, codec := NewAPIFactory() tf.Printer = &testPrinter{} tf.Client = &client.FakeRESTClient{ Codec: codec, Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { case strings.HasPrefix(p, "/namespaces/test/services/") && (m == "GET" || m == "PUT"): return &http.Response{StatusCode: 200, Body: objBody(codec, &svc.Items[0])}, nil case strings.HasPrefix(p, "/namespaces/test/replicationcontrollers/") && (m == "GET" || m == "PUT"): return &http.Response{StatusCode: 200, Body: objBody(codec, &rc.Items[0])}, nil default: t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) return nil, nil } }), } tf.Namespace = "test" buf := bytes.NewBuffer([]byte{}) cmd := NewCmdUpdate(f, buf) cmd.Flags().Set("filename", "../../../examples/guestbook") cmd.Flags().Set("namespace", "test") cmd.Run(cmd, []string{}) if buf.String() != "replicationcontrollers/rc1\nservices/baz\nreplicationcontrollers/rc1\nservices/baz\nreplicationcontrollers/rc1\nservices/baz\n" { t.Errorf("unexpected output: %s", buf.String()) } }
func TestUpdateObject(t *testing.T) { _, _, rc := testData() f, tf, codec := NewAPIFactory() tf.Printer = &testPrinter{} tf.Client = &client.FakeRESTClient{ Codec: codec, Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { case p == "/namespaces/test/replicationcontrollers/redis-master" && m == "GET": return &http.Response{StatusCode: 200, Body: objBody(codec, &rc.Items[0])}, nil case p == "/namespaces/test/replicationcontrollers/redis-master" && m == "PUT": return &http.Response{StatusCode: 200, Body: objBody(codec, &rc.Items[0])}, nil default: t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) return nil, nil } }), } tf.Namespace = "test" buf := bytes.NewBuffer([]byte{}) cmd := NewCmdUpdate(f, buf) cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.yaml") cmd.Run(cmd, []string{}) // uses the name from the file, not the response if buf.String() != "replicationcontrollers/rc1\n" { t.Errorf("unexpected output: %s", buf.String()) } }
func TestLog(t *testing.T) { tests := []struct { name, version, podPath, logPath, container string pod *api.Pod }{ { name: "v1beta3 - pod log", version: "v1beta3", podPath: "/api/v1beta3/namespaces/test/pods/foo", logPath: "/api/v1beta3/namespaces/test/pods/foo/log", pod: testPod(), }, { name: "v1 - pod log", version: "v1", podPath: "/api/v1/namespaces/test/pods/foo", logPath: "/api/v1/namespaces/test/pods/foo/log", pod: testPod(), }, } for _, test := range tests { logContent := "test log content" f, tf, codec := NewAPIFactory() tf.Client = &client.FakeRESTClient{ Codec: codec, Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { case p == test.podPath && m == "GET": body := objBody(codec, test.pod) return &http.Response{StatusCode: 200, Body: body}, nil case p == test.logPath && m == "GET": body := ioutil.NopCloser(bytes.NewBufferString(logContent)) return &http.Response{StatusCode: 200, Body: body}, nil default: // Ensures no GET is performed when deleting by name t.Errorf("%s: unexpected request: %#v\n%#v", test.name, req.URL, req) return nil, nil } }), } tf.Namespace = "test" tf.ClientConfig = &client.Config{Version: test.version} buf := bytes.NewBuffer([]byte{}) cmd := NewCmdLog(f, buf) cmd.Flags().Set("namespace", "test") cmd.Run(cmd, []string{"foo"}) if buf.String() != logContent { t.Errorf("%s: did not get expected log content. Got: %s", test.name, buf.String()) } } }
func TestLabelMultipleObjects(t *testing.T) { pods, _, _ := testData() f, tf, codec := NewAPIFactory() tf.Printer = &testPrinter{} tf.Client = &client.FakeRESTClient{ Codec: codec, Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) { switch req.Method { case "GET": switch req.URL.Path { case "/namespaces/test/pods": return &http.Response{StatusCode: 200, Body: objBody(codec, pods)}, nil default: t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) return nil, nil } case "PUT": switch req.URL.Path { case "/namespaces/test/pods/foo": return &http.Response{StatusCode: 200, Body: objBody(codec, &pods.Items[0])}, nil case "/namespaces/test/pods/bar": return &http.Response{StatusCode: 200, Body: objBody(codec, &pods.Items[1])}, nil default: t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) return nil, nil } default: t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req) return nil, nil } }), } tf.Namespace = "test" tf.ClientConfig = &client.Config{Version: testapi.Version()} buf := bytes.NewBuffer([]byte{}) cmd := NewCmdLabel(f, buf) cmd.Flags().Set("all", "true") if err := RunLabel(f, buf, cmd, []string{"pods", "a=b"}); err != nil { t.Fatalf("unexpected error: %v", err) } if tf.Printer.(*testPrinter).Objects == nil { t.Errorf("unexpected non print to default printer") } if !reflect.DeepEqual(tf.Printer.(*testPrinter).Objects[0].(*api.Pod).Labels, map[string]string{"a": "b"}) { t.Errorf("did not set labels: %#v", string(buf.Bytes())) } }
func fakeClientWith(testName string, t *testing.T, data map[string]string) ClientMapper { return ClientMapperFunc(func(*meta.RESTMapping) (RESTClient, error) { return &client.FakeRESTClient{ Codec: latest.Codec, Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) { p := req.URL.Path q := req.URL.RawQuery if len(q) != 0 { p = p + "?" + q } body, ok := data[p] if !ok { t.Fatalf("%s: unexpected request: %s (%s)\n%#v", testName, p, req.URL, req) } return &http.Response{ StatusCode: http.StatusOK, Body: stringBody(body), }, nil }), }, nil }) }
func TestAddDeploymentHash(t *testing.T) { buf := &bytes.Buffer{} codec := testapi.Codec() rc := &api.ReplicationController{ ObjectMeta: api.ObjectMeta{Name: "rc"}, Spec: api.ReplicationControllerSpec{ Selector: map[string]string{ "foo": "bar", }, Template: &api.PodTemplateSpec{ ObjectMeta: api.ObjectMeta{ Labels: map[string]string{ "foo": "bar", }, }, }, }, } podList := &api.PodList{ Items: []api.Pod{ {ObjectMeta: api.ObjectMeta{Name: "foo"}}, {ObjectMeta: api.ObjectMeta{Name: "bar"}}, {ObjectMeta: api.ObjectMeta{Name: "baz"}}, }, } seen := util.StringSet{} updatedRc := false fakeClient := &client.FakeRESTClient{ Codec: codec, Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { case p == testapi.ResourcePath("pods", "default", "") && m == "GET": if req.URL.RawQuery != "labelSelector=foo%3Dbar" { t.Errorf("Unexpected query string: %s", req.URL.RawQuery) } return &http.Response{StatusCode: 200, Body: objBody(codec, podList)}, nil case p == testapi.ResourcePath("pods", "default", "foo") && m == "PUT": seen.Insert("foo") obj := readOrDie(t, req, codec) podList.Items[0] = *(obj.(*api.Pod)) return &http.Response{StatusCode: 200, Body: objBody(codec, &podList.Items[0])}, nil case p == testapi.ResourcePath("pods", "default", "bar") && m == "PUT": seen.Insert("bar") obj := readOrDie(t, req, codec) podList.Items[1] = *(obj.(*api.Pod)) return &http.Response{StatusCode: 200, Body: objBody(codec, &podList.Items[1])}, nil case p == testapi.ResourcePath("pods", "default", "baz") && m == "PUT": seen.Insert("baz") obj := readOrDie(t, req, codec) podList.Items[2] = *(obj.(*api.Pod)) return &http.Response{StatusCode: 200, Body: objBody(codec, &podList.Items[2])}, nil case p == testapi.ResourcePath("replicationcontrollers", "default", "rc") && m == "PUT": updatedRc = true return &http.Response{StatusCode: 200, Body: objBody(codec, rc)}, nil default: t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) return nil, nil } }), } clientConfig := &client.Config{Version: testapi.Version()} client := client.NewOrDie(clientConfig) client.Client = fakeClient.Client if _, err := AddDeploymentKeyToReplicationController(rc, client, "dk", "hash", api.NamespaceDefault, buf); err != nil { t.Errorf("unexpected error: %v", err) } for _, pod := range podList.Items { if !seen.Has(pod.Name) { t.Errorf("Missing update for pod: %s", pod.Name) } } if !updatedRc { t.Errorf("Failed to update replication controller with new labels") } }
func TestUpdateWithRetries(t *testing.T) { codec := testapi.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: api.PodSpec{ RestartPolicy: api.RestartPolicyAlways, DNSPolicy: api.DNSClusterFirst, }, }, }, } // 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 := &client.FakeRESTClient{ Codec: codec, Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { case p == testapi.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.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.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) } }
func TestRunExposeService(t *testing.T) { tests := []struct { name string args []string ns string calls map[string]string input runtime.Object flags map[string]string output runtime.Object expected string status int }{ { name: "expose-service-from-service", args: []string{"service", "baz"}, ns: "test", calls: map[string]string{ "GET": "/namespaces/test/services/baz", "POST": "/namespaces/test/services", }, input: &api.Service{ ObjectMeta: api.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "12"}, TypeMeta: api.TypeMeta{Kind: "Service", APIVersion: "v1"}, Spec: api.ServiceSpec{ Selector: map[string]string{"app": "go"}, }, }, flags: map[string]string{"selector": "func=stream", "protocol": "UDP", "port": "14", "name": "foo", "labels": "svc=test"}, output: &api.Service{ ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "test", ResourceVersion: "12", Labels: map[string]string{"svc": "test"}}, TypeMeta: api.TypeMeta{Kind: "Service", APIVersion: "v1"}, Spec: api.ServiceSpec{ Ports: []api.ServicePort{ { Name: "default", Protocol: api.Protocol("UDP"), Port: 14, }, }, Selector: map[string]string{"func": "stream"}, }, }, status: 200, }, { name: "no-name-passed-from-the-cli", args: []string{"service", "mayor"}, ns: "default", calls: map[string]string{ "GET": "/namespaces/default/services/mayor", "POST": "/namespaces/default/services", }, input: &api.Service{ ObjectMeta: api.ObjectMeta{Name: "mayor", Namespace: "default", ResourceVersion: "12"}, TypeMeta: api.TypeMeta{Kind: "Service", APIVersion: "v1"}, Spec: api.ServiceSpec{ Selector: map[string]string{"run": "this"}, }, }, // No --name flag specified below. Service will use the rc's name passed via the 'default-name' parameter flags: map[string]string{"selector": "run=this", "port": "80", "labels": "runas=amayor"}, output: &api.Service{ ObjectMeta: api.ObjectMeta{Name: "mayor", Namespace: "default", ResourceVersion: "12", Labels: map[string]string{"runas": "amayor"}}, TypeMeta: api.TypeMeta{Kind: "Service", APIVersion: "v1"}, Spec: api.ServiceSpec{ Ports: []api.ServicePort{ { Name: "default", Protocol: api.Protocol("TCP"), Port: 80, }, }, Selector: map[string]string{"run": "this"}, }, }, status: 200, }, { name: "expose-external-service", args: []string{"service", "baz"}, ns: "test", calls: map[string]string{ "GET": "/namespaces/test/services/baz", "POST": "/namespaces/test/services", }, input: &api.Service{ ObjectMeta: api.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "12"}, TypeMeta: api.TypeMeta{Kind: "Service", APIVersion: "v1"}, Spec: api.ServiceSpec{ Selector: map[string]string{"app": "go"}, }, }, flags: map[string]string{"selector": "func=stream", "protocol": "UDP", "port": "14", "name": "foo", "labels": "svc=test", "create-external-load-balancer": "true"}, output: &api.Service{ ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "test", ResourceVersion: "12", Labels: map[string]string{"svc": "test"}}, TypeMeta: api.TypeMeta{Kind: "Service", APIVersion: "v1"}, Spec: api.ServiceSpec{ Ports: []api.ServicePort{ { Name: "default", Protocol: api.Protocol("UDP"), Port: 14, }, }, Selector: map[string]string{"func": "stream"}, Type: api.ServiceTypeLoadBalancer, }, }, expected: "you will also need to explicitly open a firewall", status: 200, }, } for _, test := range tests { f, tf, codec := NewAPIFactory() tf.Printer = &testPrinter{} tf.Client = &client.FakeRESTClient{ Codec: codec, Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { case p == test.calls[m] && m == "GET": return &http.Response{StatusCode: test.status, Body: objBody(codec, test.input)}, nil case p == test.calls[m] && m == "POST": return &http.Response{StatusCode: test.status, Body: objBody(codec, test.output)}, nil default: t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) return nil, nil } }), } tf.Namespace = test.ns buf := bytes.NewBuffer([]byte{}) cmd := NewCmdExposeService(f, buf) cmd.SetOutput(buf) for flag, value := range test.flags { cmd.Flags().Set(flag, value) } cmd.Run(cmd, test.args) if len(test.expected) > 0 { out := buf.String() if !strings.Contains(out, test.expected) { t.Errorf("%s: unexpected output: %s", test.name, out) } } } }
func TestExec(t *testing.T) { tests := []struct { name, version, podPath, execPath, container string pod *api.Pod execErr bool }{ { name: "v1beta3 - pod exec", version: "v1beta3", podPath: "/api/v1beta3/namespaces/test/pods/foo", execPath: "/api/v1beta3/namespaces/test/pods/foo/exec", pod: execPod(), }, { name: "v1beta3 - pod exec error", version: "v1beta3", podPath: "/api/v1beta3/namespaces/test/pods/foo", execPath: "/api/v1beta3/namespaces/test/pods/foo/exec", pod: execPod(), execErr: true, }, { name: "v1 - pod exec", version: "v1", podPath: "/api/v1/namespaces/test/pods/foo", execPath: "/api/v1/namespaces/test/pods/foo/exec", pod: execPod(), }, { name: "v1 - pod exec error", version: "v1", podPath: "/api/v1/namespaces/test/pods/foo", execPath: "/api/v1/namespaces/test/pods/foo/exec", pod: execPod(), execErr: true, }, } for _, test := range tests { f, tf, codec := NewAPIFactory() tf.Client = &client.FakeRESTClient{ Codec: codec, Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { case p == test.podPath && m == "GET": body := objBody(codec, test.pod) return &http.Response{StatusCode: 200, Body: body}, nil default: // Ensures no GET is performed when deleting by name t.Errorf("%s: unexpected request: %#v\n%#v", test.name, req.URL, req) return nil, nil } }), } tf.Namespace = "test" tf.ClientConfig = &client.Config{Version: test.version} bufOut := bytes.NewBuffer([]byte{}) bufErr := bytes.NewBuffer([]byte{}) bufIn := bytes.NewBuffer([]byte{}) ex := &fakeRemoteExecutor{} if test.execErr { ex.execErr = fmt.Errorf("exec error") } params := &execParams{ podName: "foo", containerName: "bar", } cmd := &cobra.Command{} err := RunExec(f, cmd, bufIn, bufOut, bufErr, params, []string{"test", "command"}, ex) if test.execErr && err != ex.execErr { t.Errorf("%s: Unexpected exec error: %v", test.name, err) } if !test.execErr && ex.req.URL().Path != test.execPath { t.Errorf("%s: Did not get expected path for exec request", test.name) } if !test.execErr && err != nil { t.Errorf("%s: Unexpected error: %v", test.name, err) } } }
func TestPortForward(t *testing.T) { tests := []struct { name, version, podPath, pfPath, container string pod *api.Pod pfErr bool }{ { name: "v1beta3 - pod portforward", version: "v1beta3", podPath: "/api/v1beta3/namespaces/test/pods/foo", pfPath: "/api/v1beta3/namespaces/test/pods/foo/portforward", pod: execPod(), }, { name: "v1beta3 - pod portforward error", version: "v1beta3", podPath: "/api/v1beta3/namespaces/test/pods/foo", pfPath: "/api/v1beta3/namespaces/test/pods/foo/portforward", pod: execPod(), pfErr: true, }, { name: "v1 - pod portforward", version: "v1", podPath: "/api/v1/namespaces/test/pods/foo", pfPath: "/api/v1/namespaces/test/pods/foo/portforward", pod: execPod(), }, { name: "v1 - pod portforward error", version: "v1", podPath: "/api/v1/namespaces/test/pods/foo", pfPath: "/api/v1/namespaces/test/pods/foo/portforward", pod: execPod(), pfErr: true, }, } for _, test := range tests { f, tf, codec := NewAPIFactory() tf.Client = &client.FakeRESTClient{ Codec: codec, Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { case p == test.podPath && m == "GET": body := objBody(codec, test.pod) return &http.Response{StatusCode: 200, Body: body}, nil default: // Ensures no GET is performed when deleting by name t.Errorf("%s: unexpected request: %#v\n%#v", test.name, req.URL, req) return nil, nil } }), } tf.Namespace = "test" tf.ClientConfig = &client.Config{Version: test.version} ff := &fakePortForwarder{} if test.pfErr { ff.pfErr = fmt.Errorf("pf error") } cmd := &cobra.Command{} podPtr := cmd.Flags().StringP("pod", "p", "", "Pod name") *podPtr = "foo" err := RunPortForward(f, cmd, []string{":5000", ":1000"}, ff) if test.pfErr && err != ff.pfErr { t.Errorf("%s: Unexpected exec error: %v", test.name, err) } if !test.pfErr && ff.req.URL().Path != test.pfPath { t.Errorf("%s: Did not get expected path for portforward request", test.name) } if !test.pfErr && err != nil { t.Errorf("%s: Unexpected error: %v", test.name, err) } } }