diff --git a/pkg/kubectl/cmd/cmd.go b/pkg/kubectl/cmd/cmd.go index b67830809d6..d882ec96ed4 100644 --- a/pkg/kubectl/cmd/cmd.go +++ b/pkg/kubectl/cmd/cmd.go @@ -84,7 +84,7 @@ Find more information at https://github.com/GoogleCloudPlatform/kubernetes.`, cmds.PersistentFlags().StringP("server", "s", "", "Kubernetes apiserver to connect to") cmds.PersistentFlags().StringP("auth-path", "a", os.Getenv("HOME")+"/.kubernetes_auth", "Path to the auth info file. If missing, prompt the user. Only used if using https.") cmds.PersistentFlags().Bool("match-server-version", false, "Require server version to match client version") - cmds.PersistentFlags().String("api-version", latest.Version, "The version of the API to use against the server (used for viewing resources only)") + cmds.PersistentFlags().String("api-version", latest.Version, "The version of the API to use against the server") cmds.PersistentFlags().String("certificate-authority", "", "Path to a certificate file for the certificate authority") cmds.PersistentFlags().String("client-certificate", "", "Path to a client certificate for TLS.") cmds.PersistentFlags().String("client-key", "", "Path to a client key file for TLS.") diff --git a/pkg/kubectl/cmd/get.go b/pkg/kubectl/cmd/get.go index bf2e3d1ef13..bd6e2cbead3 100644 --- a/pkg/kubectl/cmd/get.go +++ b/pkg/kubectl/cmd/get.go @@ -17,6 +17,7 @@ limitations under the License. package cmd import ( + "fmt" "io" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl" @@ -33,7 +34,8 @@ func (f *Factory) NewCmdGet(out io.Writer) *cobra.Command { Possible resources include pods (po), replication controllers (rc), services (se) or minions (mi). -If you specify a Go template, you can use any field defined in pkg/api/types.go. +If you specify a Go template, you can use any fields defined for the API version +you are connecting to the server with. Examples: $ kubectl get pods @@ -54,16 +56,27 @@ Examples: client, err := f.Client(cmd, mapping) checkErr(err) - obj, err := kubectl.NewRESTHelper(client, mapping).Get(namespace, name, labels) - checkErr(err) - outputFormat := getFlagString(cmd, "output") templateFile := getFlagString(cmd, "template") defaultPrinter, err := f.Printer(cmd, mapping, getFlagBool(cmd, "no-headers")) checkErr(err) - err = kubectl.Print(out, obj, outputFormat, templateFile, defaultPrinter) + printer, versioned, err := kubectl.GetPrinter(outputFormat, templateFile, defaultPrinter) checkErr(err) + + obj, err := kubectl.NewRESTHelper(client, mapping).Get(namespace, name, labels) + checkErr(err) + + if versioned { + // TODO Add an --output-version lock which can ensure that regardless of the + // server version, the client output stays the same. + obj, err = mapping.ObjectConvertor.ConvertToVersion(obj, mapping.APIVersion) + checkErr(err) + } + + if err := printer.PrintObj(obj, out); err != nil { + checkErr(fmt.Errorf("Unable to output the provided object: %v", err)) + } }, } cmd.Flags().StringP("output", "o", "", "Output format: json|yaml|template|templatefile") diff --git a/pkg/kubectl/resource_printer.go b/pkg/kubectl/resource_printer.go index 5a34da68000..1248977924d 100644 --- a/pkg/kubectl/resource_printer.go +++ b/pkg/kubectl/resource_printer.go @@ -34,20 +34,10 @@ import ( "gopkg.in/v1/yaml" ) -// Print outputs a runtime.Object to an io.Writer in the given format -func Print(w io.Writer, obj runtime.Object, format string, templateFile string, defaultPrinter ResourcePrinter) error { - printer, err := getPrinter(format, templateFile, defaultPrinter) - if err != nil { - return err - } - - if err := printer.PrintObj(obj, w); err != nil { - return fmt.Errorf("Failed to print: %v\nRaw received object:\n%#v", err, obj) - } - return nil -} - -func getPrinter(format, templateFile string, defaultPrinter ResourcePrinter) (ResourcePrinter, error) { +// GetPrinter returns a resource printer and a bool indicating whether the object must be +// versioned for the given format. +func GetPrinter(format, templateFile string, defaultPrinter ResourcePrinter) (ResourcePrinter, bool, error) { + versioned := true var printer ResourcePrinter switch format { case "json": @@ -57,11 +47,11 @@ func getPrinter(format, templateFile string, defaultPrinter ResourcePrinter) (Re case "template": var data []byte if len(templateFile) == 0 { - return printer, fmt.Errorf("template format specified but no template given") + return nil, false, fmt.Errorf("template format specified but no template given") } tmpl, err := template.New("output").Parse(templateFile) if err != nil { - return printer, fmt.Errorf("Error parsing template %s, %v\n", string(data), err) + return nil, false, fmt.Errorf("Error parsing template %s, %v\n", string(data), err) } printer = &TemplatePrinter{ Template: tmpl, @@ -72,27 +62,30 @@ func getPrinter(format, templateFile string, defaultPrinter ResourcePrinter) (Re var err error data, err = ioutil.ReadFile(templateFile) if err != nil { - return printer, fmt.Errorf("Error reading template %s, %v\n", templateFile, err) + return nil, false, fmt.Errorf("Error reading template %s, %v\n", templateFile, err) } } else { - return printer, fmt.Errorf("templatefile format specified but no template file given") + return nil, false, fmt.Errorf("templatefile format specified but no template file given") } tmpl, err := template.New("output").Parse(string(data)) if err != nil { - return printer, fmt.Errorf("Error parsing template %s, %v\n", string(data), err) + return nil, false, fmt.Errorf("Error parsing template %s, %v\n", string(data), err) } printer = &TemplatePrinter{ Template: tmpl, } - default: + case "": printer = defaultPrinter + versioned = false + default: + return nil, false, fmt.Errorf("output format %q not recognized", format) } - return printer, nil + return printer, versioned, nil } // ResourcePrinter is an interface that knows how to print API resources. type ResourcePrinter interface { - // Print receives an arbitrary JSON body, formats it and prints it to a writer. + // Print receives an arbitrary object, formats it and prints it to a writer. PrintObj(runtime.Object, io.Writer) error } diff --git a/pkg/kubectl/resource_printer_test.go b/pkg/kubectl/resource_printer_test.go index 7fc8dfc6c8a..5cf4aacee00 100644 --- a/pkg/kubectl/resource_printer_test.go +++ b/pkg/kubectl/resource_printer_test.go @@ -54,9 +54,14 @@ func TestJSONPrinter(t *testing.T) { func TestPrintJSON(t *testing.T) { buf := bytes.NewBuffer([]byte{}) - if err := Print(buf, &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}, "json", "", nil); err != nil { + printer, versioned, err := GetPrinter("json", "", nil) + if err != nil { t.Errorf("unexpected error: %#v", err) } + if !versioned { + t.Errorf("printer should be versioned") + } + printer.PrintObj(&api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}, buf) obj := map[string]interface{}{} if err := json.Unmarshal(buf.Bytes(), &obj); err != nil { t.Errorf("unexpected error: %#v\n%s", err, buf.String()) @@ -65,9 +70,14 @@ func TestPrintJSON(t *testing.T) { func TestPrintYAML(t *testing.T) { buf := bytes.NewBuffer([]byte{}) - if err := Print(buf, &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}, "yaml", "", nil); err != nil { + printer, versioned, err := GetPrinter("yaml", "", nil) + if err != nil { t.Errorf("unexpected error: %#v", err) } + if !versioned { + t.Errorf("printer should be versioned") + } + printer.PrintObj(&api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}, buf) obj := map[string]interface{}{} if err := yaml.Unmarshal(buf.Bytes(), &obj); err != nil { t.Errorf("unexpected error: %#v\n%s", err, buf.String()) @@ -76,31 +86,33 @@ func TestPrintYAML(t *testing.T) { func TestPrintTemplate(t *testing.T) { buf := bytes.NewBuffer([]byte{}) - if err := Print(buf, &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}, "template", "{{ .Name }}", nil); err != nil { + printer, versioned, err := GetPrinter("template", "{{ .Name }}", nil) + if err != nil { t.Errorf("unexpected error: %#v", err) } + if !versioned { + t.Errorf("printer should be versioned") + } + printer.PrintObj(&api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}, buf) if buf.String() != "foo" { t.Errorf("unexpected output: %s", buf.String()) } } func TestPrintEmptyTemplate(t *testing.T) { - buf := bytes.NewBuffer([]byte{}) - if err := Print(buf, &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}, "template", "", nil); err == nil { + if _, _, err := GetPrinter("template", "", nil); err == nil { t.Errorf("unexpected non-error") } } func TestPrintBadTemplate(t *testing.T) { - buf := bytes.NewBuffer([]byte{}) - if err := Print(buf, &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}, "template", "{{ .Name", nil); err == nil { + if _, _, err := GetPrinter("template", "{{ .Name", nil); err == nil { t.Errorf("unexpected non-error") } } func TestPrintBadTemplateFile(t *testing.T) { - buf := bytes.NewBuffer([]byte{}) - if err := Print(buf, &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}, "templatefile", "", nil); err == nil { + if _, _, err := GetPrinter("templatefile", "", nil); err == nil { t.Errorf("unexpected non-error") } }