func TestDeleteObjectNotFound(t *testing.T) { 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 == "DELETE": return &http.Response{StatusCode: 404, Body: stringBody("")}, nil default: t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) return nil, nil } }), } tf.Namespace = "test" buf := bytes.NewBuffer([]byte{}) cmd := NewCmdDelete(f, buf) cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.yaml") cmd.Flags().Set("cascade", "false") cmd.Flags().Set("output", "name") filenames := cmd.Flags().Lookup("filename").Value.(*util.StringList) err := RunDelete(f, buf, cmd, []string{}, *filenames, true) if err == nil || !errors.IsNotFound(err) { t.Errorf("unexpected error: expected NotFound, got %v", err) } }
func TestDeleteNamedObject(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-controller" && m == "DELETE": return &http.Response{StatusCode: 200, Body: objBody(codec, &rc.Items[0])}, nil default: // Ensures no GET is performed when deleting by name t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) return nil, nil } }), } tf.Namespace = "test" buf := bytes.NewBuffer([]byte{}) cmd := NewCmdDelete(f, buf) cmd.Flags().Set("namespace", "test") cmd.Flags().Set("cascade", "false") cmd.Flags().Set("output", "name") cmd.Run(cmd, []string{"replicationcontrollers", "redis-master-controller"}) if buf.String() != "replicationcontroller/redis-master-controller\n" { t.Errorf("unexpected output: %s", buf.String()) } }
func TestDeleteDirectory(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 == "DELETE": return &http.Response{StatusCode: 200, Body: objBody(codec, &svc.Items[0])}, nil case strings.HasPrefix(p, "/namespaces/test/replicationcontrollers/") && m == "DELETE": 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 := NewCmdDelete(f, buf) cmd.Flags().Set("filename", "../../../examples/guestbook") cmd.Flags().Set("cascade", "false") cmd.Flags().Set("output", "name") cmd.Run(cmd, []string{}) if buf.String() != "replicationcontroller/frontend\nservice/frontend\nreplicationcontroller/redis-master\nservice/redis-master\nreplicationcontroller/redis-slave\nservice/redis-slave\n" { t.Errorf("unexpected output: %s", buf.String()) } }
func TestGetMultipleTypeObjects(t *testing.T) { pods, svc, _ := 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.URL.Path { case "/namespaces/test/pods": return &http.Response{StatusCode: 200, Body: objBody(codec, pods)}, nil case "/namespaces/test/services": return &http.Response{StatusCode: 200, Body: objBody(codec, svc)}, nil default: t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) return nil, nil } }), } tf.Namespace = "test" buf := bytes.NewBuffer([]byte{}) cmd := NewCmdGet(f, buf) cmd.SetOutput(buf) cmd.Run(cmd, []string{"pods,services"}) expected := []runtime.Object{pods, svc} actual := tf.Printer.(*testPrinter).Objects if !reflect.DeepEqual(expected, actual) { t.Errorf("unexpected object: %#v", actual) } if len(buf.String()) == 0 { t.Errorf("unexpected empty output") } }
func TestForceReplaceObjectNotFound(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 == "DELETE": return &http.Response{StatusCode: 404, Body: stringBody("")}, nil case p == "/namespaces/test/replicationcontrollers" && m == "POST": return &http.Response{StatusCode: 201, 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 := NewCmdReplace(f, buf) cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.yaml") cmd.Flags().Set("force", "true") cmd.Flags().Set("cascade", "false") cmd.Flags().Set("output", "name") cmd.Run(cmd, []string{}) if buf.String() != "replicationcontroller/rc1\n" { t.Errorf("unexpected output: %s", buf.String()) } }
func TestDescribeObject(t *testing.T) { _, _, rc := testData() f, tf, codec := NewAPIFactory() d := &testDescriber{Output: "test output"} tf.Describer = d 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 default: t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) return nil, nil } }), } tf.Namespace = "test" buf := bytes.NewBuffer([]byte{}) cmd := NewCmdDescribe(f, buf) cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.yaml") cmd.Run(cmd, []string{}) if d.Name != "redis-master" || d.Namespace != "test" { t.Errorf("unexpected describer: %#v", d) } if buf.String() != fmt.Sprintf("%s\n\n", d.Output) { t.Errorf("unexpected output: %s", buf.String()) } }
func TestCreateMultipleObject(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 p == "/namespaces/test/services" && m == "POST": return &http.Response{StatusCode: 201, Body: objBody(codec, &svc.Items[0])}, nil case p == "/namespaces/test/replicationcontrollers" && m == "POST": return &http.Response{StatusCode: 201, 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 := NewCmdCreate(f, buf) cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.yaml") cmd.Flags().Set("filename", "../../../examples/guestbook/frontend-service.yaml") cmd.Flags().Set("output", "name") cmd.Run(cmd, []string{}) // Names should come from the REST response, NOT the files if buf.String() != "replicationcontroller/rc1\nservice/baz\n" { t.Errorf("unexpected output: %s", buf.String()) } }
func TestPatchObject(t *testing.T) { _, svc, _ := 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/services/frontend" && (m == "PATCH" || m == "GET"): return &http.Response{StatusCode: 200, Body: objBody(codec, &svc.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 := NewCmdPatch(f, buf) cmd.Flags().Set("namespace", "test") cmd.Flags().Set("patch", `{"spec":{"type":"NodePort"}}`) cmd.Flags().Set("output", "name") cmd.Run(cmd, []string{"services/frontend"}) // uses the name from the file, not the response if buf.String() != "frontend\n" { t.Errorf("unexpected output: %s", buf.String()) } }
func TestGetMultipleTypeObjectsAsList(t *testing.T) { pods, svc, _ := 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.URL.Path { case "/namespaces/test/pods": return &http.Response{StatusCode: 200, Body: objBody(codec, pods)}, nil case "/namespaces/test/services": return &http.Response{StatusCode: 200, Body: objBody(codec, svc)}, nil default: t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) return nil, nil } }), } tf.Namespace = "test" tf.ClientConfig = &client.Config{Version: testapi.Version()} buf := bytes.NewBuffer([]byte{}) cmd := NewCmdGet(f, buf) cmd.SetOutput(buf) cmd.Flags().Set("output", "json") cmd.Run(cmd, []string{"pods,services"}) if tf.Printer.(*testPrinter).Objects != nil { t.Errorf("unexpected print to default printer") } out, err := codec.Decode(buf.Bytes()) if err != nil { t.Fatalf("unexpected error: %v", err) } list, err := runtime.ExtractList(out) if err != nil { t.Fatalf("unexpected error: %v", err) } if errs := runtime.DecodeList(list, api.Scheme); len(errs) > 0 { t.Fatalf("unexpected error: %v", errs) } if err := runtime.SetList(out, list); err != nil { t.Fatalf("unexpected error: %v", err) } expected := &api.List{ Items: []runtime.Object{ &pods.Items[0], &pods.Items[1], &svc.Items[0], }, } if !reflect.DeepEqual(expected, out) { t.Errorf("unexpected output: %#v", out) } }
func TestLabelForResourceFromFile(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/cassandra": return &http.Response{StatusCode: 200, Body: objBody(codec, &pods.Items[0])}, 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/cassandra": return &http.Response{StatusCode: 200, Body: objBody(codec, &pods.Items[0])}, 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("filename", "../../../examples/cassandra/cassandra.yaml") cmd.SetOutput(buf) err := RunLabel(f, buf, cmd, []string{"a=b"}) if err != nil { t.Fatalf("unexpected error: %v", err) } if tf.Printer.(*testPrinter).Objects == nil { t.Errorf("unexpected 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 TestAnnotateMultipleObjects(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{}) options := &AnnotateOptions{} options.all = true args := []string{"pods", "a=b", "c-"} if err := options.Complete(f, args, buf); err != nil { t.Fatalf("unexpected error: %v", err) } if err := options.Validate(args); err != nil { t.Fatalf("unexpected error: %v", err) } if err := options.RunAnnotate(); err != nil { t.Fatalf("unexpected error: %v", err) } }
func TestLog(t *testing.T) { tests := []struct { name, version, podPath, logPath, container string pod *api.Pod }{ { 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 TestGetMultipleTypeObjectsWithDirectReference(t *testing.T) { _, svc, _ := testData() node := &api.Node{ ObjectMeta: api.ObjectMeta{ Name: "foo", }, Spec: api.NodeSpec{ ExternalID: "ext", }, } 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.URL.Path { case "/nodes/foo": return &http.Response{StatusCode: 200, Body: objBody(codec, node)}, nil case "/namespaces/test/services/bar": return &http.Response{StatusCode: 200, Body: objBody(codec, &svc.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 := NewCmdGet(f, buf) cmd.SetOutput(buf) cmd.Run(cmd, []string{"services/bar", "node/foo"}) expected := []runtime.Object{&svc.Items[0], node} actual := tf.Printer.(*testPrinter).Objects if !api.Semantic.DeepEqual(expected, actual) { t.Errorf("unexpected object: %s", util.ObjectDiff(expected, actual)) } if len(buf.String()) == 0 { t.Errorf("unexpected empty output") } }
func TestDeleteMultipleSelector(t *testing.T) { pods, svc, _ := 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/pods" && m == "GET": if req.URL.Query().Get(api.LabelSelectorQueryParam(testapi.Version())) != "a=b" { t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) } return &http.Response{StatusCode: 200, Body: objBody(codec, pods)}, nil case p == "/namespaces/test/services" && m == "GET": if req.URL.Query().Get(api.LabelSelectorQueryParam(testapi.Version())) != "a=b" { t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) } return &http.Response{StatusCode: 200, Body: objBody(codec, svc)}, nil case strings.HasPrefix(p, "/namespaces/test/pods/") && m == "DELETE": return &http.Response{StatusCode: 200, Body: objBody(codec, &pods.Items[0])}, nil case strings.HasPrefix(p, "/namespaces/test/services/") && m == "DELETE": return &http.Response{StatusCode: 200, Body: objBody(codec, &svc.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 := NewCmdDelete(f, buf) cmd.Flags().Set("selector", "a=b") cmd.Flags().Set("cascade", "false") cmd.Flags().Set("output", "name") cmd.Run(cmd, []string{"pods,services"}) if buf.String() != "pod/foo\npod/bar\nservice/baz\n" { t.Errorf("unexpected output: %s", buf.String()) } }
func TestWatchSelector(t *testing.T) { pods, events := watchTestData() f, tf, codec := NewAPIFactory() tf.Printer = &testPrinter{} tf.Client = &client.FakeRESTClient{ Codec: codec, Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) { if req.URL.Query().Get(api.LabelSelectorQueryParam(testapi.Version())) != "a=b" { t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) } switch req.URL.Path { case "/namespaces/test/pods": return &http.Response{StatusCode: 200, Body: objBody(codec, &api.PodList{Items: pods})}, nil case "/watch/namespaces/test/pods": return &http.Response{StatusCode: 200, Body: watchBody(codec, events)}, nil default: t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) return nil, nil } }), } tf.Namespace = "test" buf := bytes.NewBuffer([]byte{}) cmd := NewCmdGet(f, buf) cmd.SetOutput(buf) cmd.Flags().Set("watch", "true") cmd.Flags().Set("selector", "a=b") cmd.Run(cmd, []string{"pods"}) expected := []runtime.Object{&api.PodList{Items: pods}, events[0].Object, events[1].Object} actual := tf.Printer.(*testPrinter).Objects if !reflect.DeepEqual(expected, actual) { t.Errorf("unexpected object: %#v %#v", expected[0], actual[0]) } if len(buf.String()) == 0 { t.Errorf("unexpected empty output") } }
func TestDeleteAllNotFound(t *testing.T) { _, svc, _ := testData() f, tf, codec := NewAPIFactory() // Add an item to the list which will result in a 404 on delete svc.Items = append(svc.Items, api.Service{ObjectMeta: api.ObjectMeta{Name: "foo"}}) notFoundError := &errors.NewNotFound("Service", "foo").(*errors.StatusError).ErrStatus 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/services" && m == "GET": return &http.Response{StatusCode: 200, Body: objBody(codec, svc)}, nil case p == "/namespaces/test/services/foo" && m == "DELETE": return &http.Response{StatusCode: 404, Body: objBody(codec, notFoundError)}, nil case p == "/namespaces/test/services/baz" && m == "DELETE": return &http.Response{StatusCode: 200, Body: objBody(codec, &svc.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 := NewCmdDelete(f, buf) cmd.Flags().Set("all", "true") cmd.Flags().Set("cascade", "false") // Make sure we can explicitly choose to fail on NotFound errors, even with --all cmd.Flags().Set("ignore-not-found", "false") cmd.Flags().Set("output", "name") err := RunDelete(f, buf, cmd, []string{"services"}) if err == nil || !errors.IsNotFound(err) { t.Errorf("unexpected error: expected NotFound, got %v", err) } }
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 TestWatchOnlyResource(t *testing.T) { pods, events := watchTestData() 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.URL.Path { case "/namespaces/test/pods/foo": return &http.Response{StatusCode: 200, Body: objBody(codec, &pods[0])}, nil case "/watch/namespaces/test/pods/foo": return &http.Response{StatusCode: 200, Body: watchBody(codec, events)}, nil default: t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) return nil, nil } }), } tf.Namespace = "test" buf := bytes.NewBuffer([]byte{}) cmd := NewCmdGet(f, buf) cmd.SetOutput(buf) cmd.Flags().Set("watch-only", "true") cmd.Run(cmd, []string{"pods", "foo"}) expected := []runtime.Object{events[0].Object, events[1].Object} actual := tf.Printer.(*testPrinter).Objects if !reflect.DeepEqual(expected, actual) { t.Errorf("unexpected object: %#v", actual) } if len(buf.String()) == 0 { t.Errorf("unexpected empty output") } }
func TestDeleteMultipleObjectContinueOnMissing(t *testing.T) { _, svc, _ := 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 == "DELETE": return &http.Response{StatusCode: 404, Body: stringBody("")}, nil case p == "/namespaces/test/services/frontend" && m == "DELETE": return &http.Response{StatusCode: 200, Body: objBody(codec, &svc.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 := NewCmdDelete(f, buf) cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.yaml") cmd.Flags().Set("filename", "../../../examples/guestbook/frontend-service.yaml") cmd.Flags().Set("cascade", "false") cmd.Flags().Set("output", "name") err := RunDelete(f, buf, cmd, []string{}) if err == nil || !errors.IsNotFound(err) { t.Errorf("unexpected error: expected NotFound, got %v", err) } if buf.String() != "service/frontend\n" { t.Errorf("unexpected output: %s", buf.String()) } }
func TestExec(t *testing.T) { version := testapi.Version() tests := []struct { name, version, podPath, execPath, container string pod *api.Pod execErr bool }{ { name: "pod exec", version: version, podPath: "/api/" + version + "/namespaces/test/pods/foo", execPath: "/api/" + version + "/namespaces/test/pods/foo/exec", pod: execPod(), }, { name: "pod exec error", version: version, podPath: "/api/" + version + "/namespaces/test/pods/foo", execPath: "/api/" + version + "/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: %s %#v\n%#v", test.name, req.Method, req.URL, req) return nil, fmt.Errorf("unexpected request") } }), } 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 := &ExecOptions{ PodName: "foo", ContainerName: "bar", In: bufIn, Out: bufOut, Err: bufErr, Executor: ex, } cmd := &cobra.Command{} if err := params.Complete(f, cmd, []string{"test", "command"}); err != nil { t.Fatal(err) } err := params.Run() if test.execErr && err != ex.execErr { t.Errorf("%s: Unexpected exec error: %v", test.name, err) continue } if !test.execErr && err != nil { t.Errorf("%s: Unexpected error: %v", test.name, err) continue } if !test.execErr && ex.req.URL().Path != test.execPath { t.Errorf("%s: Did not get expected path for exec request", test.name) continue } } }
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 TestPortForward(t *testing.T) { version := testapi.Version() tests := []struct { name, version, podPath, pfPath, container string pod *api.Pod pfErr bool }{ { name: "pod portforward", version: version, podPath: "/api/" + version + "/namespaces/test/pods/foo", pfPath: "/api/" + version + "/namespaces/test/pods/foo/portforward", pod: execPod(), }, { name: "pod portforward error", version: version, podPath: "/api/" + version + "/namespaces/test/pods/foo", pfPath: "/api/" + version + "/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{} cmd.Flags().StringP("pod", "p", "", "Pod name") err := RunPortForward(f, cmd, []string{"foo", ":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) } } }
func TestPodAndContainer(t *testing.T) { tests := []struct { args []string p *ExecOptions name string expectError bool expectedPod string expectedContainer string expectedArgs []string }{ { p: &ExecOptions{}, expectError: true, name: "empty", }, { p: &ExecOptions{PodName: "foo"}, expectError: true, name: "no cmd", }, { p: &ExecOptions{PodName: "foo", ContainerName: "bar"}, expectError: true, name: "no cmd, w/ container", }, { p: &ExecOptions{PodName: "foo"}, args: []string{"cmd"}, expectedPod: "foo", expectedArgs: []string{"cmd"}, name: "pod in flags", }, { p: &ExecOptions{}, args: []string{"foo"}, expectError: true, name: "no cmd, w/o flags", }, { p: &ExecOptions{}, args: []string{"foo", "cmd"}, expectedPod: "foo", expectedArgs: []string{"cmd"}, name: "cmd, w/o flags", }, { p: &ExecOptions{ContainerName: "bar"}, args: []string{"foo", "cmd"}, expectedPod: "foo", expectedContainer: "bar", expectedArgs: []string{"cmd"}, name: "cmd, container in flag", }, } for _, test := range tests { f, tf, codec := NewAPIFactory() tf.Client = &client.FakeRESTClient{ Codec: codec, Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) { return nil, nil }), } tf.Namespace = "test" tf.ClientConfig = &client.Config{} cmd := &cobra.Command{} options := test.p err := options.Complete(f, cmd, test.args) if test.expectError && err == nil { t.Errorf("unexpected non-error (%s)", test.name) } if !test.expectError && err != nil { t.Errorf("unexpected error: %v (%s)", err, test.name) } if err != nil { continue } if options.PodName != test.expectedPod { t.Errorf("expected: %s, got: %s (%s)", test.expectedPod, options.PodName, test.name) } if options.ContainerName != test.expectedContainer { t.Errorf("expected: %s, got: %s (%s)", test.expectedContainer, options.ContainerName, test.name) } if !reflect.DeepEqual(test.expectedArgs, options.Command) { t.Errorf("expected: %v, got %v (%s)", test.expectedArgs, options.Command, test.name) } } }
// Verifies that schemas that are not in the master tree of Kubernetes can be retrieved via Get. // Because api.List is part of the Kube API, resource.Builder has to perform a conversion on // api.Scheme, which may not have access to all objects, and not all objects are at the same // internal versioning scheme. This test verifies that two isolated schemes (Test, and api.Scheme) // can be conjoined into a single output object. // // The expected behavior of the `kubectl get` command is: // 1. objects using unrecognized schemes will always be returned using that scheme/version, "unlikelyversion" in this test; // 2. if the specified output-version is a recognized, valid Scheme, then the list should use that scheme, and otherwise it will default to the client version, latest.Version in this test; // 3a. if the specified output-version is a recognized, valid Scheme, in which the requested object (replicationcontroller) can be represented, then the object should be returned using that version; // 3b. otherwise if the specified output-version is unrecognized, but the requested object (replicationcontroller) is recognized by the client's codec, then it will be converted to the client version, latest.Version in this test. func TestGetUnknownSchemaObjectListGeneric(t *testing.T) { testCases := map[string]struct { outputVersion string listVersion string testtypeVersion string rcVersion string }{ "handles specific version": { outputVersion: latest.Version, listVersion: latest.Version, testtypeVersion: "unlikelyversion", rcVersion: latest.Version, }, "handles second specific version": { outputVersion: "unlikelyversion", listVersion: latest.Version, testtypeVersion: "unlikelyversion", rcVersion: latest.Version, // see expected behavior 3b }, "handles common version": { outputVersion: testapi.Version(), listVersion: testapi.Version(), testtypeVersion: "unlikelyversion", rcVersion: testapi.Version(), }, } for k, test := range testCases { apiCodec := runtime.CodecFor(api.Scheme, testapi.Version()) regularClient := &client.FakeRESTClient{ Codec: apiCodec, Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) { return &http.Response{StatusCode: 200, Body: objBody(apiCodec, &api.ReplicationController{ObjectMeta: api.ObjectMeta{Name: "foo"}})}, nil }), } f, tf, codec := NewMixedFactory(regularClient) tf.Printer = &testPrinter{} tf.Client = &client.FakeRESTClient{ Codec: codec, Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) { return &http.Response{StatusCode: 200, Body: objBody(codec, &internalType{Name: "foo"})}, nil }), } tf.Namespace = "test" tf.ClientConfig = &client.Config{Version: latest.Version} buf := bytes.NewBuffer([]byte{}) cmd := NewCmdGet(f, buf) cmd.SetOutput(buf) cmd.Flags().Set("output", "json") cmd.Flags().Set("output-version", test.outputVersion) err := RunGet(f, buf, cmd, []string{"type/foo", "replicationcontrollers/foo"}) if err != nil { t.Errorf("%s: unexpected error: %v", k, err) continue } out := make(map[string]interface{}) if err := encjson.Unmarshal(buf.Bytes(), &out); err != nil { t.Errorf("%s: unexpected error: %v\n%s", k, err, buf.String()) continue } if out["apiVersion"] != test.listVersion { t.Errorf("%s: unexpected list: %#v", k, out) } arr := out["items"].([]interface{}) if arr[0].(map[string]interface{})["apiVersion"] != test.testtypeVersion { t.Errorf("%s: unexpected list: %#v", k, out) } if arr[1].(map[string]interface{})["apiVersion"] != test.rcVersion { t.Errorf("%s: unexpected list: %#v", k, out) } } }
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, TargetPort: util.NewIntOrStringFromInt(14), }, }, Selector: map[string]string{"func": "stream"}, Type: api.ServiceTypeLoadBalancer, }, }, status: 200, }, { name: "expose-external-affinity-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", "session-affinity": "ClientIP"}, 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, TargetPort: util.NewIntOrStringFromInt(14), }, }, Selector: map[string]string{"func": "stream"}, Type: api.ServiceTypeLoadBalancer, SessionAffinity: api.ServiceAffinityClientIP, }, }, 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 TestRunExposeServiceFromFile(t *testing.T) { test := struct { calls map[string]string input runtime.Object flags map[string]string output runtime.Object expected string status int }{ calls: map[string]string{ "GET": "/namespaces/test/services/redis-master", "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, } 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" buf := bytes.NewBuffer([]byte{}) cmd := NewCmdExposeService(f, buf) cmd.SetOutput(buf) for flag, value := range test.flags { cmd.Flags().Set(flag, value) } cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-service.yaml") cmd.Run(cmd, []string{}) if len(test.expected) > 0 { out := buf.String() if !strings.Contains(out, test.expected) { t.Errorf("unexpected output: %s", out) } } }