func TestDeleteObject(t *testing.T) { _, _, rc := testData() f, tf, codec := NewAPIFactory() tf.Printer = &testPrinter{} tf.Client = &fake.RESTClient{ Codec: codec, Client: fake.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: 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/redis-master-controller.yaml") cmd.Flags().Set("cascade", "false") cmd.Flags().Set("output", "name") cmd.Run(cmd, []string{}) // uses the name from the file, not the response if buf.String() != "replicationcontroller/redis-master\n" { t.Errorf("unexpected output: %s", buf.String()) } }
func TestCreateMultipleObject(t *testing.T) { _, svc, rc := testData() f, tf, codec := NewAPIFactory() tf.Printer = &testPrinter{} tf.Client = &fake.RESTClient{ Codec: codec, Client: fake.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 TestDeleteObjectNotFound(t *testing.T) { f, tf, codec := NewAPIFactory() tf.Printer = &testPrinter{} tf.Client = &fake.RESTClient{ Codec: codec, Client: fake.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) options := &DeleteOptions{ Filenames: []string{"../../../examples/guestbook/redis-master-controller.yaml"}, } cmd.Flags().Set("cascade", "false") cmd.Flags().Set("output", "name") err := RunDelete(f, buf, cmd, []string{}, options) if err == nil || !errors.IsNotFound(err) { t.Errorf("unexpected error: expected NotFound, got %v", err) } }
func TestPatchObjectFromFile(t *testing.T) { _, svc, _ := testData() f, tf, codec := NewAPIFactory() tf.Printer = &testPrinter{} tf.Client = &fake.RESTClient{ Codec: codec, Client: fake.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.Flags().Set("filename", "../../../examples/guestbook/frontend-service.yaml") cmd.Run(cmd, []string{}) // uses the name from the file, not the response if buf.String() != "frontend\n" { t.Errorf("unexpected output: %s", buf.String()) } }
func TestDeleteObjectIgnoreNotFound(t *testing.T) { f, tf, codec := NewAPIFactory() tf.Printer = &testPrinter{} tf.Client = &fake.RESTClient{ Codec: codec, Client: fake.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("ignore-not-found", "true") cmd.Flags().Set("output", "name") cmd.Run(cmd, []string{}) if buf.String() != "" { t.Errorf("unexpected output: %s", buf.String()) } }
func TestDeleteMultipleResourcesWithTheSameName(t *testing.T) { _, svc, rc := testData() f, tf, codec := NewAPIFactory() tf.Printer = &testPrinter{} tf.Client = &fake.RESTClient{ Codec: codec, Client: fake.HTTPClientFunc(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { case p == "/namespaces/test/replicationcontrollers/baz" && m == "DELETE": return &http.Response{StatusCode: 200, Body: objBody(codec, &rc.Items[0])}, nil case p == "/namespaces/test/replicationcontrollers/foo" && m == "DELETE": return &http.Response{StatusCode: 200, Body: objBody(codec, &rc.Items[0])}, nil case p == "/namespaces/test/services/baz" && m == "DELETE": return &http.Response{StatusCode: 200, Body: objBody(codec, &svc.Items[0])}, nil case p == "/namespaces/test/services/foo" && m == "DELETE": return &http.Response{StatusCode: 200, Body: objBody(codec, &svc.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,services", "baz", "foo"}) if buf.String() != "replicationcontroller/baz\nreplicationcontroller/foo\nservice/baz\nservice/foo\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 = &fake.RESTClient{ Codec: codec, Client: fake.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 TestGetMultipleTypeObjectsAsList(t *testing.T) { pods, svc, _ := testData() f, tf, codec := NewAPIFactory() tf.Printer = &testPrinter{} tf.Client = &fake.RESTClient{ Codec: codec, Client: fake.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.Default.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 TestApplyMultipleObject(t *testing.T) { nameRC, currentRC := readAndAnnotateReplicationController(t, filenameRC) pathRC := "/namespaces/test/replicationcontrollers/" + nameRC nameSVC, currentSVC := readAndAnnotateService(t, filenameSVC) pathSVC := "/namespaces/test/services/" + nameSVC f, tf, codec := NewAPIFactory() tf.Printer = &testPrinter{} tf.Client = &fake.RESTClient{ Codec: codec, Client: fake.HTTPClientFunc(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { case p == pathRC && m == "GET": bodyRC := ioutil.NopCloser(bytes.NewReader(currentRC)) return &http.Response{StatusCode: 200, Body: bodyRC}, nil case p == pathRC && m == "PATCH": validatePatchApplication(t, req) bodyRC := ioutil.NopCloser(bytes.NewReader(currentRC)) return &http.Response{StatusCode: 200, Body: bodyRC}, nil case p == pathSVC && m == "GET": bodySVC := ioutil.NopCloser(bytes.NewReader(currentSVC)) return &http.Response{StatusCode: 200, Body: bodySVC}, nil case p == pathSVC && m == "PATCH": validatePatchApplication(t, req) bodySVC := ioutil.NopCloser(bytes.NewReader(currentSVC)) return &http.Response{StatusCode: 200, Body: bodySVC}, nil default: t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) return nil, nil } }), } tf.Namespace = "test" buf := bytes.NewBuffer([]byte{}) cmd := NewCmdApply(f, buf) cmd.Flags().Set("filename", filenameRC) cmd.Flags().Set("filename", filenameSVC) cmd.Flags().Set("output", "name") cmd.Run(cmd, []string{}) // Names should come from the REST response, NOT the files expectRC := "replicationcontroller/" + nameRC + "\n" expectSVC := "service/" + nameSVC + "\n" expect := expectRC + expectSVC if buf.String() != expect { t.Fatalf("unexpected output: %s\nexpected: %s", buf.String(), expect) } }
func TestAnnotateMultipleObjects(t *testing.T) { pods, _, _ := testData() f, tf, codec := NewAPIFactory() tf.Printer = &testPrinter{} tf.Client = &fake.RESTClient{ Codec: codec, Client: fake.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.Default.Version()} options := &AnnotateOptions{} options.all = true args := []string{"pods", "a=b", "c-"} if err := options.Complete(f, args); 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 TestLabelForResourceFromFile(t *testing.T) { pods, _, _ := testData() f, tf, codec := NewAPIFactory() tf.Client = &fake.RESTClient{ Codec: codec, Client: fake.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.Default.Version()} buf := bytes.NewBuffer([]byte{}) cmd := NewCmdLabel(f, buf) options := &LabelOptions{ Filenames: []string{"../../../examples/cassandra/cassandra.yaml"}, } err := RunLabel(f, buf, cmd, []string{"a=b"}, options) if err != nil { t.Fatalf("unexpected error: %v", err) } if !strings.Contains(buf.String(), "labeled") { t.Errorf("did not set labels: %s", buf.String()) } }
func TestReplaceDirectory(t *testing.T) { _, svc, rc := testData() f, tf, codec := NewAPIFactory() tf.Printer = &testPrinter{} tf.Client = &fake.RESTClient{ Codec: codec, Client: fake.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" || m == "DELETE"): return &http.Response{StatusCode: 200, Body: objBody(codec, &svc.Items[0])}, nil case strings.HasPrefix(p, "/namespaces/test/replicationcontrollers/") && (m == "GET" || m == "PUT" || m == "DELETE"): return &http.Response{StatusCode: 200, Body: objBody(codec, &rc.Items[0])}, nil case strings.HasPrefix(p, "/namespaces/test/services") && m == "POST": return &http.Response{StatusCode: 201, Body: objBody(codec, &svc.Items[0])}, nil case strings.HasPrefix(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") cmd.Flags().Set("namespace", "test") cmd.Flags().Set("output", "name") cmd.Run(cmd, []string{}) if buf.String() != "replicationcontroller/rc1\nservice/baz\nreplicationcontroller/rc1\nservice/baz\nreplicationcontroller/rc1\nservice/baz\n" { t.Errorf("unexpected output: %s", buf.String()) } buf.Reset() cmd.Flags().Set("force", "true") cmd.Flags().Set("cascade", "false") cmd.Run(cmd, []string{}) if buf.String() != "replicationcontroller/frontend\nservice/frontend\nreplicationcontroller/redis-master\nservice/redis-master\nreplicationcontroller/redis-slave\nservice/redis-slave\n"+ "replicationcontroller/rc1\nservice/baz\nreplicationcontroller/rc1\nservice/baz\nreplicationcontroller/rc1\nservice/baz\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: "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 = &fake.RESTClient{ Codec: codec, Client: fake.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.Client = &fake.RESTClient{ Codec: codec, Client: fake.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.Default.Version()} buf := bytes.NewBuffer([]byte{}) cmd := NewCmdLabel(f, buf) cmd.Flags().Set("all", "true") if err := RunLabel(f, buf, cmd, []string{"pods", "a=b"}, &LabelOptions{}); err != nil { t.Fatalf("unexpected error: %v", err) } if strings.Count(buf.String(), "labeled") != len(pods.Items) { t.Errorf("not all labels are set: %s", 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 = &fake.RESTClient{ Codec: codec, Client: fake.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 TestGetMultipleTypeObjectsWithSelector(t *testing.T) { pods, svc, _ := testData() f, tf, codec := NewAPIFactory() tf.Printer = &testPrinter{} tf.Client = &fake.RESTClient{ Codec: codec, Client: fake.HTTPClientFunc(func(req *http.Request) (*http.Response, error) { if req.URL.Query().Get(unversioned.LabelSelectorQueryParam(testapi.Default.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, 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.Flags().Set("selector", "a=b") cmd.Run(cmd, []string{"pods,services"}) expected, err := extractResourceList([]runtime.Object{pods, svc}) if err != nil { t.Fatalf("unexpected error: %v", err) } 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 TestDeleteMultipleSelector(t *testing.T) { pods, svc, _ := testData() f, tf, codec := NewAPIFactory() tf.Printer = &testPrinter{} tf.Client = &fake.RESTClient{ Codec: codec, Client: fake.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(unversioned.LabelSelectorQueryParam(testapi.Default.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(unversioned.LabelSelectorQueryParam(testapi.Default.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 = &fake.RESTClient{ Codec: codec, Client: fake.HTTPClientFunc(func(req *http.Request) (*http.Response, error) { if req.URL.Query().Get(unversioned.LabelSelectorQueryParam(testapi.Default.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:\nExpected: %#v\n\nGot: %#v\n\n", 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 = &fake.RESTClient{ Codec: codec, Client: fake.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"}, &DeleteOptions{}) 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 &fake.RESTClient{ Codec: testapi.Default.Codec(), Client: fake.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 TestWatchResourceIdentifiedByFile(t *testing.T) { pods, events := watchTestData() f, tf, codec := NewAPIFactory() tf.Printer = &testPrinter{} tf.Client = &fake.RESTClient{ Codec: codec, Client: fake.HTTPClientFunc(func(req *http.Request) (*http.Response, error) { switch req.URL.Path { case "/namespaces/test/pods/cassandra": return &http.Response{StatusCode: 200, Body: objBody(codec, &pods[0])}, nil case "/watch/namespaces/test/pods/cassandra": 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("filename", "../../../examples/cassandra/cassandra.yaml") cmd.Run(cmd, []string{}) expected := []runtime.Object{&pods[0], events[0].Object, events[1].Object} actual := tf.Printer.(*testPrinter).Objects if !reflect.DeepEqual(expected, actual) { t.Errorf("expected object: %#v unexpected object: %#v", expected, actual) } if len(buf.String()) == 0 { t.Errorf("unexpected empty output") } }
func TestValidateCachesSchema(t *testing.T) { schema, err := loadSchemaForTest() if err != nil { t.Errorf("Error loading schema: %v", err) t.FailNow() } output, err := json.Marshal(schema) if err != nil { t.Errorf("Error serializing schema: %v", err) t.FailNow() } requests := map[string]int{} c := &fake.RESTClient{ Codec: testapi.Default.Codec(), Client: fake.HTTPClientFunc(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { case strings.HasPrefix(p, "/swaggerapi") && m == "GET": requests[p] = requests[p] + 1 return &http.Response{StatusCode: 200, Body: ioutil.NopCloser(bytes.NewBuffer(output))}, nil default: t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) return nil, nil } }), } dir := os.TempDir() + "/schemaCache" os.RemoveAll(dir) obj := &api.Pod{} data, err := testapi.Default.Codec().Encode(obj) if err != nil { t.Errorf("unexpected error: %v", err) t.FailNow() } // Initial request, should use HTTP and write if getSchemaAndValidate(c, data, "foo", "bar", dir); err != nil { t.Errorf("unexpected error validating: %v", err) } if _, err := os.Stat(path.Join(dir, "foo", "bar", schemaFileName)); err != nil { t.Errorf("unexpected missing cache file: %v", err) } if requests["/swaggerapi/foo/bar"] != 1 { t.Errorf("expected 1 schema request, saw: %d", requests["/swaggerapi/foo/bar"]) } // Same version and group, should skip HTTP if getSchemaAndValidate(c, data, "foo", "bar", dir); err != nil { t.Errorf("unexpected error validating: %v", err) } if requests["/swaggerapi/foo/bar"] != 1 { t.Errorf("expected 1 schema request, saw: %d", requests["/swaggerapi/foo/bar"]) } // Different API group, should go to HTTP and write if getSchemaAndValidate(c, data, "foo", "baz", dir); err != nil { t.Errorf("unexpected error validating: %v", err) } if _, err := os.Stat(path.Join(dir, "foo", "baz", schemaFileName)); err != nil { t.Errorf("unexpected missing cache file: %v", err) } if requests["/swaggerapi/foo/baz"] != 1 { t.Errorf("expected 1 schema request, saw: %d", requests["/swaggerapi/foo/baz"]) } // Different version, should go to HTTP and write if getSchemaAndValidate(c, data, "foo2", "bar", dir); err != nil { t.Errorf("unexpected error validating: %v", err) } if _, err := os.Stat(path.Join(dir, "foo2", "bar", schemaFileName)); err != nil { t.Errorf("unexpected missing cache file: %v", err) } if requests["/swaggerapi/foo2/bar"] != 1 { t.Errorf("expected 1 schema request, saw: %d", requests["/swaggerapi/foo2/bar"]) } // No cache dir, should go straight to HTTP and not write if getSchemaAndValidate(c, data, "foo", "blah", ""); err != nil { t.Errorf("unexpected error validating: %v", err) } if requests["/swaggerapi/foo/blah"] != 1 { t.Errorf("expected 1 schema request, saw: %d", requests["/swaggerapi/foo/blah"]) } if _, err := os.Stat(path.Join(dir, "foo", "blah", schemaFileName)); err == nil || !os.IsNotExist(err) { t.Errorf("unexpected cache file error: %v", err) } }
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-no-selector-defined", 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"}, Spec: api.ServiceSpec{ Selector: map[string]string{"app": "go"}, }, }, flags: map[string]string{"protocol": "UDP", "port": "14", "name": "foo", "labels": "svc=test"}, output: &api.Service{ ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "", Labels: map[string]string{"svc": "test"}}, Spec: api.ServiceSpec{ Ports: []api.ServicePort{ { Protocol: api.ProtocolUDP, Port: 14, TargetPort: util.NewIntOrStringFromInt(14), }, }, Selector: map[string]string{"app": "go"}, }, }, expected: "service \"foo\" exposed", status: 200, }, { 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"}, 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: "", Labels: map[string]string{"svc": "test"}}, Spec: api.ServiceSpec{ Ports: []api.ServicePort{ { Protocol: api.ProtocolUDP, Port: 14, TargetPort: util.NewIntOrStringFromInt(14), }, }, Selector: map[string]string{"func": "stream"}, }, }, expected: "service \"foo\" exposed", 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"}, 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: "", Labels: map[string]string{"runas": "amayor"}}, Spec: api.ServiceSpec{ Ports: []api.ServicePort{ { Protocol: api.ProtocolTCP, Port: 80, TargetPort: util.NewIntOrStringFromInt(80), }, }, Selector: map[string]string{"run": "this"}, }, }, expected: "service \"mayor\" exposed", status: 200, }, { name: "expose-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"}, 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", "type": "LoadBalancer", "dry-run": "true"}, output: &api.Service{ ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "", Labels: map[string]string{"svc": "test"}}, Spec: api.ServiceSpec{ Ports: []api.ServicePort{ { Protocol: api.ProtocolUDP, Port: 14, TargetPort: util.NewIntOrStringFromInt(14), }, }, Selector: map[string]string{"func": "stream"}, Type: api.ServiceTypeLoadBalancer, }, }, status: 200, }, { name: "expose-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"}, 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", "type": "LoadBalancer", "session-affinity": "ClientIP", "dry-run": "true"}, output: &api.Service{ ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "", Labels: map[string]string{"svc": "test"}}, Spec: api.ServiceSpec{ Ports: []api.ServicePort{ { Protocol: api.ProtocolUDP, Port: 14, TargetPort: util.NewIntOrStringFromInt(14), }, }, Selector: map[string]string{"func": "stream"}, Type: api.ServiceTypeLoadBalancer, SessionAffinity: api.ServiceAffinityClientIP, }, }, 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"}, Spec: api.ServiceSpec{ Ports: []api.ServicePort{}, }, }, // Even if we specify --selector, since service/test doesn't need one it will ignore it flags: map[string]string{"selector": "svc=fromexternal", "port": "90", "labels": "svc=fromexternal", "name": "frombaz", "generator": "service/test", "dry-run": "true"}, output: &api.Service{ ObjectMeta: api.ObjectMeta{Name: "frombaz", Namespace: "", Labels: map[string]string{"svc": "fromexternal"}}, Spec: api.ServiceSpec{ Ports: []api.ServicePort{ { Protocol: api.ProtocolTCP, Port: 90, TargetPort: util.NewIntOrStringFromInt(90), }, }, }, }, status: 200, }, { name: "expose-from-file", args: []string{}, ns: "test", calls: map[string]string{ "GET": "/namespaces/test/services/redis-master", "POST": "/namespaces/test/services", }, input: &api.Service{ ObjectMeta: api.ObjectMeta{Name: "redis-master", Namespace: "test", ResourceVersion: "12"}, Spec: api.ServiceSpec{ Selector: map[string]string{"app": "go"}, }, }, flags: map[string]string{"filename": "../../../examples/guestbook/redis-master-service.yaml", "selector": "func=stream", "protocol": "UDP", "port": "14", "name": "foo", "labels": "svc=test", "dry-run": "true"}, output: &api.Service{ ObjectMeta: api.ObjectMeta{Name: "foo", Labels: map[string]string{"svc": "test"}}, Spec: api.ServiceSpec{ Ports: []api.ServicePort{ { Protocol: api.ProtocolUDP, Port: 14, TargetPort: util.NewIntOrStringFromInt(14), }, }, Selector: map[string]string{"func": "stream"}, }, }, status: 200, }, { name: "truncate-name", args: []string{"pod", "a-name-that-is-toooo-big-for-a-service"}, ns: "test", calls: map[string]string{ "GET": "/namespaces/test/pods/a-name-that-is-toooo-big-for-a-service", "POST": "/namespaces/test/services", }, input: &api.Pod{ ObjectMeta: api.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "12"}, }, flags: map[string]string{"selector": "svc=frompod", "port": "90", "labels": "svc=frompod", "generator": "service/v2"}, output: &api.Service{ ObjectMeta: api.ObjectMeta{Name: "a-name-that-is-toooo-big", Namespace: "", Labels: map[string]string{"svc": "frompod"}}, Spec: api.ServiceSpec{ Ports: []api.ServicePort{ { Protocol: api.ProtocolTCP, Port: 90, TargetPort: util.NewIntOrStringFromInt(90), }, }, Selector: map[string]string{"svc": "frompod"}, }, }, expected: "service \"a-name-that-is-toooo-big\" exposed", status: 200, }, { name: "expose-multiport-object", args: []string{"service", "foo"}, ns: "test", calls: map[string]string{ "GET": "/namespaces/test/services/foo", "POST": "/namespaces/test/services", }, input: &api.Service{ ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "", Labels: map[string]string{"svc": "multiport"}}, Spec: api.ServiceSpec{ Ports: []api.ServicePort{ { Protocol: api.ProtocolTCP, Port: 80, TargetPort: util.NewIntOrStringFromInt(80), }, { Protocol: api.ProtocolTCP, Port: 443, TargetPort: util.NewIntOrStringFromInt(443), }, }, }, }, flags: map[string]string{"selector": "svc=fromfoo", "generator": "service/v2", "name": "fromfoo", "dry-run": "true"}, output: &api.Service{ ObjectMeta: api.ObjectMeta{Name: "fromfoo", Namespace: "", Labels: map[string]string{"svc": "multiport"}}, Spec: api.ServiceSpec{ Ports: []api.ServicePort{ { Name: "port-1", Protocol: api.ProtocolTCP, Port: 80, TargetPort: util.NewIntOrStringFromInt(80), }, { Name: "port-2", Protocol: api.ProtocolTCP, Port: 443, TargetPort: util.NewIntOrStringFromInt(443), }, }, Selector: map[string]string{"svc": "fromfoo"}, }, }, status: 200, }, } for _, test := range tests { f, tf, codec := NewAPIFactory() tf.Printer = &kubectl.JSONPrinter{} tf.Client = &fake.RESTClient{ Codec: codec, Client: fake.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) out := buf.String() if _, ok := test.flags["dry-run"]; ok { buf.Reset() if err := tf.Printer.PrintObj(test.output, buf); err != nil { t.Errorf("%s: Unexpected error: %v", test.name, err) continue } test.expected = buf.String() } if !strings.Contains(out, test.expected) { t.Errorf("%s: Unexpected output! Expected\n%s\ngot\n%s", test.name, test.expected, out) } } }
func TestPortForward(t *testing.T) { version := testapi.Default.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 = &fake.RESTClient{ Codec: codec, Client: fake.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 && err != nil { t.Errorf("%s: Unexpected error: %v", test.name, err) } if test.pfErr { continue } if ff.url.Path != test.pfPath { t.Errorf("%s: Did not get expected path for portforward request", test.name) } if ff.method != "POST" { t.Errorf("%s: Did not get method for attach request: %s", test.name, ff.method) } } }
func TestGenerateService(t *testing.T) { tests := []struct { port string args []string serviceGenerator string params map[string]interface{} expectErr bool name string service api.Service expectPOST bool }{ { port: "80", args: []string{"foo"}, serviceGenerator: "service/v2", params: map[string]interface{}{ "name": "foo", }, expectErr: false, name: "basic", service: api.Service{ ObjectMeta: api.ObjectMeta{ Name: "foo", }, Spec: api.ServiceSpec{ Ports: []api.ServicePort{ { Port: 80, Protocol: "TCP", TargetPort: util.NewIntOrStringFromInt(80), }, }, Selector: map[string]string{ "run": "foo", }, Type: api.ServiceTypeClusterIP, SessionAffinity: api.ServiceAffinityNone, }, }, expectPOST: true, }, { port: "80", args: []string{"foo"}, serviceGenerator: "service/v2", params: map[string]interface{}{ "name": "foo", "labels": "app=bar", }, expectErr: false, name: "custom labels", service: api.Service{ ObjectMeta: api.ObjectMeta{ Name: "foo", Labels: map[string]string{"app": "bar"}, }, Spec: api.ServiceSpec{ Ports: []api.ServicePort{ { Port: 80, Protocol: "TCP", TargetPort: util.NewIntOrStringFromInt(80), }, }, Selector: map[string]string{ "app": "bar", }, Type: api.ServiceTypeClusterIP, SessionAffinity: api.ServiceAffinityNone, }, }, expectPOST: true, }, { expectErr: true, name: "missing port", expectPOST: false, }, { port: "80", args: []string{"foo"}, serviceGenerator: "service/v2", params: map[string]interface{}{ "name": "foo", }, expectErr: false, name: "dry-run", expectPOST: false, }, } for _, test := range tests { sawPOST := false f, tf, codec := NewAPIFactory() tf.ClientConfig = &client.Config{Version: testapi.Default.Version()} tf.Client = &fake.RESTClient{ Codec: codec, Client: fake.HTTPClientFunc(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { case test.expectPOST && m == "POST" && p == "/namespaces/namespace/services": sawPOST = true body := objBody(codec, &test.service) data, err := ioutil.ReadAll(req.Body) if err != nil { t.Errorf("unexpected error: %v", err) t.FailNow() } defer req.Body.Close() svc := &api.Service{} if err := codec.DecodeInto(data, svc); err != nil { t.Errorf("unexpected error: %v", err) t.FailNow() } // Copy things that are defaulted by the system test.service.Annotations = svc.Annotations if !reflect.DeepEqual(&test.service, svc) { t.Errorf("expected:\n%v\nsaw:\n%v\n", &test.service, svc) } 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") } }), } cmd := &cobra.Command{} cmd.Flags().String("output", "", "") addRunFlags(cmd) if !test.expectPOST { cmd.Flags().Set("dry-run", "true") } if len(test.port) > 0 { cmd.Flags().Set("port", test.port) test.params["port"] = test.port } buff := &bytes.Buffer{} err := generateService(f, cmd, test.args, test.serviceGenerator, test.params, "namespace", buff) if test.expectErr { if err == nil { t.Error("unexpected non-error") } continue } if err != nil { t.Errorf("unexpected error: %v", err) } if test.expectPOST != sawPOST { t.Error("expectPost: %v, sawPost: %v", test.expectPOST, sawPOST) } } }
func TestPodAndContainer(t *testing.T) { tests := []struct { args []string argsLenAtDash int p *ExecOptions name string expectError bool expectedPod string expectedContainer string expectedArgs []string }{ { p: &ExecOptions{}, argsLenAtDash: -1, expectError: true, name: "empty", }, { p: &ExecOptions{PodName: "foo"}, argsLenAtDash: -1, expectError: true, name: "no cmd", }, { p: &ExecOptions{PodName: "foo", ContainerName: "bar"}, argsLenAtDash: -1, expectError: true, name: "no cmd, w/ container", }, { p: &ExecOptions{PodName: "foo"}, args: []string{"cmd"}, argsLenAtDash: -1, expectedPod: "foo", expectedArgs: []string{"cmd"}, name: "pod in flags", }, { p: &ExecOptions{}, args: []string{"foo", "cmd"}, argsLenAtDash: 0, expectError: true, name: "no pod, pod name is behind dash", }, { p: &ExecOptions{}, args: []string{"foo"}, argsLenAtDash: -1, expectError: true, name: "no cmd, w/o flags", }, { p: &ExecOptions{}, args: []string{"foo", "cmd"}, argsLenAtDash: -1, expectedPod: "foo", expectedArgs: []string{"cmd"}, name: "cmd, w/o flags", }, { p: &ExecOptions{}, args: []string{"foo", "cmd"}, argsLenAtDash: 1, expectedPod: "foo", expectedArgs: []string{"cmd"}, name: "cmd, cmd is behind dash", }, { p: &ExecOptions{ContainerName: "bar"}, args: []string{"foo", "cmd"}, argsLenAtDash: -1, expectedPod: "foo", expectedContainer: "bar", expectedArgs: []string{"cmd"}, name: "cmd, container in flag", }, } for _, test := range tests { f, tf, codec := NewAPIFactory() tf.Client = &fake.RESTClient{ Codec: codec, Client: fake.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, test.argsLenAtDash) 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) } } }
func TestExec(t *testing.T) { version := testapi.Default.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 = &fake.RESTClient{ Codec: codec, Client: fake.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{} args := []string{"test", "command"} if err := params.Complete(f, cmd, args, -1); 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 { continue } if ex.url.Path != test.execPath { t.Errorf("%s: Did not get expected path for exec request", test.name) continue } if ex.method != "POST" { t.Errorf("%s: Did not get method for exec request: %s", test.name, ex.method) } } }
func TestNegotiateVersion(t *testing.T) { tests := []struct { name, version, expectedVersion string serverVersions []string clientVersions []string config *unversioned.Config expectErr bool }{ { name: "server supports client default", version: "version1", config: &unversioned.Config{}, serverVersions: []string{"version1", testapi.Default.Version()}, clientVersions: []string{"version1", testapi.Default.Version()}, expectedVersion: "version1", expectErr: false, }, { name: "server falls back to client supported", version: testapi.Default.Version(), config: &unversioned.Config{}, serverVersions: []string{"version1"}, clientVersions: []string{"version1", testapi.Default.Version()}, expectedVersion: "version1", expectErr: false, }, { name: "explicit version supported", version: "", config: &unversioned.Config{Version: testapi.Default.Version()}, serverVersions: []string{"version1", testapi.Default.Version()}, clientVersions: []string{"version1", testapi.Default.Version()}, expectedVersion: testapi.Default.Version(), expectErr: false, }, { name: "explicit version not supported", version: "", config: &unversioned.Config{Version: testapi.Default.Version()}, serverVersions: []string{"version1"}, clientVersions: []string{"version1", testapi.Default.Version()}, expectedVersion: "", expectErr: true, }, } codec := testapi.Default.Codec() for _, test := range tests { fakeClient := &fake.RESTClient{ Codec: codec, Resp: &http.Response{ StatusCode: 200, Body: objBody(&unversioned_api.APIVersions{Versions: test.serverVersions}), }, Client: fake.HTTPClientFunc(func(req *http.Request) (*http.Response, error) { return &http.Response{StatusCode: 200, Body: objBody(&unversioned_api.APIVersions{Versions: test.serverVersions})}, nil }), } c := unversioned.NewOrDie(test.config) c.Client = fakeClient.Client response, err := unversioned.NegotiateVersion(c, test.config, test.version, test.clientVersions) if err == nil && test.expectErr { t.Errorf("expected error, got nil for [%s].", test.name) } if err != nil && !test.expectErr { t.Errorf("unexpected error for [%s]: %v.", test.name, err) } if response != test.expectedVersion { t.Errorf("expected version %s, got %s.", test.expectedVersion, response) } } }
func TestUpdateWithRetries(t *testing.T) { codec := testapi.Default.Codec() grace := int64(30) 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, TerminationGracePeriodSeconds: &grace, }, }, }, } // 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) } }
func TestAddDeploymentHash(t *testing.T) { buf := &bytes.Buffer{} codec := testapi.Default.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 := sets.String{} updatedRc := false 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("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.Default.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.Default.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.Default.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.Default.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.Default.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") } }