Change:
Compare the following two outputs (note the bad alignment in the last line): The custom-columns string is exactly the same in both cases, but something messes up the column widths when using openapi print columns. Is this a BUG REPORT or FEATURE REQUEST?: /kind bug What happened: Columns printed when using are misaligned. What you expected to happen: Columns in all rows should be aligned. How to reproduce it (as minimally and precisely as possible): Deploy Service Catalog, patched with Run the above two commands Environment: Kubernetes version (use ): Client Version: version.Info{Major:"1", Minor:"9+", GitVersion:"v1.9.0-alpha.3", GitCommit:"", GitTreeState:"clean", BuildDate:"2017-11-15T23:06:17Z", GoVersion:"go1.9.1", Compiler:"gc", Platform:"linux/amd64"} Server Version: version.Info{Major:"1", Minor:"8", GitVersion:"v1.8.0", GitCommit:"", GitTreeState:"dirty", BuildDate:"2017-10-17T15:09:55Z", GoVersion:"go1.8.3", Compiler:"gc", Platform:"linux/amd64"} (same behavior with kubectl built from current master branch)
/sig api-machinery
/remove-sig api-machinery /sig cli PR is ready:
Issues go stale after 90d of inactivity. Mark the issue as fresh with . Stale issues rot after an additional 30d of inactivity and eventually close. If this issue is safe to close now please do so with . Send feedback to sig-testing, kubernetes/test-infra and/or . /lifecycle stale
/remove-lifecycle stale

Discussion:
"k8s.io/client-go/util/jsonpath" ) <del> const ( columnwidth       = 10 tabwidth          = 4 padding           = 3 padding_character = ' ' flags             = 0 ) </del> var jsonRegexp = regexp.MustCompile("^{.?([^{}]+)}$|^.?([^{}]+)$") // RelaxedJSONPathExpression attempts to be flexible with JSONPath expressions, it accepts:

} func (s *CustomColumnsPrinter) PrintObj(obj runtime.Object, out io.Writer) error { <del> w := tabwriter.NewWriter(out, columnwidth, tabwidth, padding, padding_character, flags) </del> <ins> if w, found := out.(*tabwriter.Writer); !found { w = GetNewTabWriter(out) out = w defer w.Flush() } </ins> t := reflect.TypeOf(obj) if !s.NoHeaders && t != s.lastType {

for ix := range s.Columns { headers[ix] = s.Columns[ix].Header } <del> fmt.Fprintln(w, strings.Join(headers, "t")) </del> <ins> fmt.Fprintln(out, strings.Join(headers, "t")) </ins> s.lastType = t } parsers := make([]*jsonpath.JSONPath, len(s.Columns))

return err } for ix := range objs { <del> if err := s.printOneObject(objs[ix], parsers, w); err != nil { </del> <ins> if err := s.printOneObject(objs[ix], parsers, out); err != nil { </ins> return err } } } else { <del> if err := s.printOneObject(obj, parsers, w); err != nil { </del> <ins> if err := s.printOneObject(obj, parsers, out); err != nil { </ins> return err } } <del> return w.Flush() </del> <ins> return nil </ins> } func (s *CustomColumnsPrinter) printOneObject(obj runtime.Object, parsers []*jsonpath.JSONPath, out io.Writer) error {

} } } <ins> // this mimics how resource/get.go calls the customcolumn printer func TestIndividualPrintObjOnExistingTabWriter(t *testing.T) { columns := []printers.Column{ { Header:    "NAME", FieldSpec: "{.metadata.name}", }, { Header:    "LONG COLUMN NAME", // name is longer than all values of label1 FieldSpec: "{.metadata.labels.label1}", }, { Header:    "LABEL 2", FieldSpec: "{.metadata.labels.label2}", }, } objects := []*v1.Pod{ {ObjectMeta: metav1.ObjectMeta{Name: "foo", Labels: map[string]string{"label1": "foo", "label2": "foo"}}}, {ObjectMeta: metav1.ObjectMeta{Name: "bar", Labels: map[string]string{"label1": "bar", "label2": "bar"}}}, } expectedOutput := `NAME      LONG COLUMN NAME   LABEL 2 foo       foo                foo bar       bar                bar ` buffer := &bytes.Buffer{} tabWriter := printers.GetNewTabWriter(buffer) printer := &printers.CustomColumnsPrinter{ Columns: columns, Decoder: legacyscheme.Codecs.UniversalDecoder(), } for _, obj := range objects { if err := printer.PrintObj(obj, tabWriter); err != nil { t.Errorf("unexpected error: %v", err) } } tabWriter.Flush() if buffer.String() != expectedOutput { t.Errorf("nexpected:n'%s'nsawn'%s'n", expectedOutput, buffer.String()) } } </ins>