Merge pull request #56629 from luksa/fix_custom_column_alignment
Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. Fix bad column alignment when using custom columns from OpenAPI schema Columns printed by `kubectl get` weren't aligned properly when they were coming from the OpenAPI schema. This was caused by `CustomColumnPrinter.PrintObj`, which was creating a new `tabwriter.Writer` instead of re-using the tabwriter received through the `out` method parameter (basically, a tabwriter was writing to another tabwriter). Because the PrintObj flushed the tabwriter after writing each individual line, the column widths would reset. **What this PR does / why we need it**: This PR fixes the bad column alignment. **Which issue(s) this PR fixes** Fixes #56282 **Special notes for your reviewer**: I've aligned how `CustomColumnPrinter.PrintObj` handles tabwriter with how `HumanReadablePrinter.PrintObj` does it (see https://github.com/kubernetes/kubernetes/blob/master/pkg/printers/humanreadable.go#L299-L303) **Release note**: ```release-note Fixed column alignment when kubectl get is used with custom columns from OpenAPI schema ```
This commit is contained in:
commit
9f4b851e91
@ -31,14 +31,6 @@ import (
|
|||||||
"k8s.io/client-go/util/jsonpath"
|
"k8s.io/client-go/util/jsonpath"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
columnwidth = 10
|
|
||||||
tabwidth = 4
|
|
||||||
padding = 3
|
|
||||||
padding_character = ' '
|
|
||||||
flags = 0
|
|
||||||
)
|
|
||||||
|
|
||||||
var jsonRegexp = regexp.MustCompile("^\\{\\.?([^{}]+)\\}$|^\\.?([^{}]+)$")
|
var jsonRegexp = regexp.MustCompile("^\\{\\.?([^{}]+)\\}$|^\\.?([^{}]+)$")
|
||||||
|
|
||||||
// RelaxedJSONPathExpression attempts to be flexible with JSONPath expressions, it accepts:
|
// RelaxedJSONPathExpression attempts to be flexible with JSONPath expressions, it accepts:
|
||||||
@ -163,7 +155,11 @@ func (s *CustomColumnsPrinter) AfterPrint(w io.Writer, res string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *CustomColumnsPrinter) PrintObj(obj runtime.Object, out io.Writer) error {
|
func (s *CustomColumnsPrinter) PrintObj(obj runtime.Object, out io.Writer) error {
|
||||||
w := tabwriter.NewWriter(out, columnwidth, tabwidth, padding, padding_character, flags)
|
if w, found := out.(*tabwriter.Writer); !found {
|
||||||
|
w = GetNewTabWriter(out)
|
||||||
|
out = w
|
||||||
|
defer w.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
t := reflect.TypeOf(obj)
|
t := reflect.TypeOf(obj)
|
||||||
if !s.NoHeaders && t != s.lastType {
|
if !s.NoHeaders && t != s.lastType {
|
||||||
@ -171,7 +167,7 @@ func (s *CustomColumnsPrinter) PrintObj(obj runtime.Object, out io.Writer) error
|
|||||||
for ix := range s.Columns {
|
for ix := range s.Columns {
|
||||||
headers[ix] = s.Columns[ix].Header
|
headers[ix] = s.Columns[ix].Header
|
||||||
}
|
}
|
||||||
fmt.Fprintln(w, strings.Join(headers, "\t"))
|
fmt.Fprintln(out, strings.Join(headers, "\t"))
|
||||||
s.lastType = t
|
s.lastType = t
|
||||||
}
|
}
|
||||||
parsers := make([]*jsonpath.JSONPath, len(s.Columns))
|
parsers := make([]*jsonpath.JSONPath, len(s.Columns))
|
||||||
@ -188,16 +184,16 @@ func (s *CustomColumnsPrinter) PrintObj(obj runtime.Object, out io.Writer) error
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for ix := range objs {
|
for ix := range objs {
|
||||||
if err := s.printOneObject(objs[ix], parsers, w); err != nil {
|
if err := s.printOneObject(objs[ix], parsers, out); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if err := s.printOneObject(obj, parsers, w); err != nil {
|
if err := s.printOneObject(obj, parsers, out); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return w.Flush()
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *CustomColumnsPrinter) printOneObject(obj runtime.Object, parsers []*jsonpath.JSONPath, out io.Writer) error {
|
func (s *CustomColumnsPrinter) printOneObject(obj runtime.Object, parsers []*jsonpath.JSONPath, out io.Writer) error {
|
||||||
|
@ -323,3 +323,45 @@ foo baz <none>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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'\nsaw\n'%s'\n", expectedOutput, buffer.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user