Skip to content

Commit 4d637d0

Browse files
committed
Merge pull request #10851 from jbeda/namespace-align
Fix up alignment of columns w/ namespaces.
2 parents 98b5576 + 0426ab3 commit 4d637d0

File tree

5 files changed

+220
-10
lines changed

5 files changed

+220
-10
lines changed

pkg/kubectl/cmd/cmd_test.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,72 @@ func ExamplePrintPodWithWideFormat() {
319319
// test1 1/2 podPhase 6 10y kubernetes-minion-abcd
320320
}
321321

322+
func ExamplePrintServiceWithNamespacesAndLabels() {
323+
f, tf, codec := NewAPIFactory()
324+
tf.Printer = kubectl.NewHumanReadablePrinter(false, true, false, []string{"l1"})
325+
tf.Client = &client.FakeRESTClient{
326+
Codec: codec,
327+
Client: nil,
328+
}
329+
cmd := NewCmdRun(f, os.Stdout)
330+
svc := &api.ServiceList{
331+
Items: []api.Service{
332+
{
333+
ObjectMeta: api.ObjectMeta{
334+
Name: "svc1",
335+
Namespace: "ns1",
336+
Labels: map[string]string{
337+
"l1": "value",
338+
},
339+
},
340+
Spec: api.ServiceSpec{
341+
Ports: []api.ServicePort{
342+
{Protocol: "UDP", Port: 53},
343+
{Protocol: "TCP", Port: 53},
344+
},
345+
Selector: map[string]string{
346+
"s": "magic",
347+
},
348+
ClusterIP: "10.1.1.1",
349+
},
350+
Status: api.ServiceStatus{},
351+
},
352+
{
353+
ObjectMeta: api.ObjectMeta{
354+
Name: "svc2",
355+
Namespace: "ns2",
356+
Labels: map[string]string{
357+
"l1": "dolla-bill-yall",
358+
},
359+
},
360+
Spec: api.ServiceSpec{
361+
Ports: []api.ServicePort{
362+
{Protocol: "TCP", Port: 80},
363+
{Protocol: "TCP", Port: 8080},
364+
},
365+
Selector: map[string]string{
366+
"s": "kazam",
367+
},
368+
ClusterIP: "10.1.1.2",
369+
},
370+
Status: api.ServiceStatus{},
371+
}},
372+
}
373+
ld := util.NewLineDelimiter(os.Stdout, "|")
374+
defer ld.Flush()
375+
err := f.PrintObject(cmd, svc, ld)
376+
if err != nil {
377+
fmt.Printf("Unexpected error: %v", err)
378+
}
379+
// Output:
380+
// |NAMESPACE NAME LABELS SELECTOR IP(S) PORT(S) L1|
381+
// |ns1 svc1 l1=value s=magic 10.1.1.1 53/UDP value|
382+
// | 53/TCP |
383+
// |ns2 svc2 l1=dolla-bill-yall s=kazam 10.1.1.2 80/TCP dolla-bill-yall|
384+
// | 8080/TCP |
385+
// ||
386+
}
387+
322388
func TestNormalizationFuncGlobalExistance(t *testing.T) {
323389
// This test can be safely deleted when we will not support multiple flag formats
324390
root := NewKubectlCommand(cmdutil.NewFactory(nil), os.Stdin, os.Stdout, os.Stderr)

pkg/kubectl/cmd/get.go

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -103,16 +103,21 @@ func RunGet(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string
103103
if len(args) == 0 {
104104
fmt.Fprint(out, `
105105
You must specify the type of resource to get. Valid resource types include:
106-
* pods (aka 'po')
107-
* replicationcontrollers (aka 'rc')
108-
* services
109-
* nodes (aka 'no')
106+
* componentStatuses (aka 'cs')
107+
* endpoints (aka 'ep')
110108
* events (aka 'ev')
111-
* secrets
112109
* limits
113-
* persistentVolumes (aka 'pv')
110+
* namespaces
111+
* nodes (aka 'no')
114112
* persistentVolumeClaims (aka 'pvc')
113+
* persistentVolumes (aka 'pv')
114+
* pods (aka 'po')
115+
* podTemplates
115116
* quota
117+
* replicationcontrollers (aka 'rc')
118+
* secrets
119+
* serviceAccounts
120+
* services
116121
`)
117122
return errors.New("Required resource not specified.")
118123
}

pkg/kubectl/resource_printer.go

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,8 @@ func (h *HumanReadablePrinter) HandledResources() []string {
254254
return keys
255255
}
256256

257+
// NOTE: When adding a new resource type here, please update the list
258+
// pkg/kubectl/cmd/get.go to reflect the new resource type.
257259
var podColumns = []string{"NAME", "READY", "STATUS", "RESTARTS", "AGE"}
258260
var podTemplateColumns = []string{"TEMPLATE", "CONTAINER(S)", "IMAGE(S)", "PODLABELS"}
259261
var replicationControllerColumns = []string{"CONTROLLER", "CONTAINER(S)", "IMAGE(S)", "SELECTOR", "REPLICAS"}
@@ -475,11 +477,18 @@ func printPodTemplate(pod *api.PodTemplate, w io.Writer, withNamespace bool, wid
475477
}
476478

477479
// Lay out all the other containers on separate lines.
480+
extraLinePrefix := "\t"
481+
if withNamespace {
482+
extraLinePrefix = "\t\t"
483+
}
478484
for _, container := range containers {
479-
_, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", "", container.Name, container.Image, "")
485+
_, err := fmt.Fprintf(w, "%s%s\t%s\t%s", extraLinePrefix, container.Name, container.Image, "")
480486
if err != nil {
481487
return err
482488
}
489+
if _, err := fmt.Fprint(w, appendLabelTabs(columnLabels)); err != nil {
490+
return err
491+
}
483492
}
484493
return nil
485494
}
@@ -522,11 +531,18 @@ func printReplicationController(controller *api.ReplicationController, w io.Writ
522531
}
523532

524533
// Lay out all the other containers on separate lines.
534+
extraLinePrefix := "\t"
535+
if withNamespace {
536+
extraLinePrefix = "\t\t"
537+
}
525538
for _, container := range containers {
526-
_, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", "", container.Name, container.Image, "", "")
539+
_, err := fmt.Fprintf(w, "%s%s\t%s\t%s\t%s", extraLinePrefix, container.Name, container.Image, "", "")
527540
if err != nil {
528541
return err
529542
}
543+
if _, err := fmt.Fprint(w, appendLabelTabs(columnLabels)); err != nil {
544+
return err
545+
}
530546
}
531547
return nil
532548
}
@@ -566,6 +582,10 @@ func printService(svc *api.Service, w io.Writer, withNamespace bool, wide bool,
566582
return err
567583
}
568584

585+
extraLinePrefix := "\t\t\t"
586+
if withNamespace {
587+
extraLinePrefix = "\t\t\t\t"
588+
}
569589
count := len(svc.Spec.Ports)
570590
if len(ips) > count {
571591
count = len(ips)
@@ -580,7 +600,10 @@ func printService(svc *api.Service, w io.Writer, withNamespace bool, wide bool,
580600
port = fmt.Sprintf("%d/%s", svc.Spec.Ports[i].Port, svc.Spec.Ports[i].Protocol)
581601
}
582602
// Lay out additional ports.
583-
if _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", "", "", "", ip, port); err != nil {
603+
if _, err := fmt.Fprintf(w, "%s%s\t%s", extraLinePrefix, ip, port); err != nil {
604+
return err
605+
}
606+
if _, err := fmt.Fprint(w, appendLabelTabs(columnLabels)); err != nil {
584607
return err
585608
}
586609
}
@@ -626,7 +649,7 @@ func printNamespace(item *api.Namespace, w io.Writer, withNamespace bool, wide b
626649
if withNamespace {
627650
return fmt.Errorf("namespace is not namespaced")
628651
}
629-
if _, err := fmt.Fprintf(w, "%s\t%s\t%s\n", item.Name, formatLabels(item.Labels), item.Status.Phase); err != nil {
652+
if _, err := fmt.Fprintf(w, "%s\t%s\t%s", item.Name, formatLabels(item.Labels), item.Status.Phase); err != nil {
630653
return err
631654
}
632655
_, err := fmt.Fprint(w, appendLabels(item.Labels, columnLabels))
@@ -945,6 +968,19 @@ func appendLabels(itemLabels map[string]string, columnLabels []string) string {
945968
return buffer.String()
946969
}
947970

971+
// Append a set of tabs for each label column. We need this in the case where
972+
// we have extra lines so that the tabwriter will still line things up.
973+
func appendLabelTabs(columnLabels []string) string {
974+
var buffer bytes.Buffer
975+
976+
for range columnLabels {
977+
buffer.WriteString("\t")
978+
}
979+
buffer.WriteString("\n")
980+
981+
return buffer.String()
982+
}
983+
948984
func formatLabelHeaders(columnLabels []string) []string {
949985
formHead := make([]string, len(columnLabels))
950986
for i, l := range columnLabels {

pkg/util/line_delimiter.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
Copyright 2015 The Kubernetes Authors All rights reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package util
18+
19+
import (
20+
"bytes"
21+
"io"
22+
"strings"
23+
)
24+
25+
// A Line Delimiter is a filter that will
26+
type LineDelimiter struct {
27+
output io.Writer
28+
delimiter []byte
29+
buf bytes.Buffer
30+
}
31+
32+
// NewLineDelimiter allocates a new io.Writer that will split input on lines
33+
// and bracket each line with the delimiter string. This can be useful in
34+
// output tests where it is difficult to see and test trailing whitespace.
35+
func NewLineDelimiter(output io.Writer, delimiter string) *LineDelimiter {
36+
return &LineDelimiter{output: output, delimiter: []byte(delimiter)}
37+
}
38+
39+
// Write writes buf to the LineDelimiter ld. The only errors returned are ones
40+
// encountered while writing to the underlying output stream.
41+
func (ld *LineDelimiter) Write(buf []byte) (n int, err error) {
42+
return ld.buf.Write(buf)
43+
}
44+
45+
// Flush all lines up until now. This will assume insert a linebreak at the current point of the stream.
46+
func (ld *LineDelimiter) Flush() (err error) {
47+
lines := strings.Split(ld.buf.String(), "\n")
48+
for _, line := range lines {
49+
if _, err = ld.output.Write(ld.delimiter); err != nil {
50+
return
51+
}
52+
if _, err = ld.output.Write([]byte(line)); err != nil {
53+
return
54+
}
55+
if _, err = ld.output.Write(ld.delimiter); err != nil {
56+
return
57+
}
58+
if _, err = ld.output.Write([]byte("\n")); err != nil {
59+
return
60+
}
61+
}
62+
return
63+
}

pkg/util/line_delimiter_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
Copyright 2015 The Kubernetes Authors All rights reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package util
18+
19+
import (
20+
"fmt"
21+
"os"
22+
)
23+
24+
func ExampleTrailingNewline() {
25+
ld := NewLineDelimiter(os.Stdout, "|")
26+
defer ld.Flush()
27+
fmt.Fprint(ld, " Hello \n World \n")
28+
// Output:
29+
// | Hello |
30+
// | World |
31+
// ||
32+
}
33+
func ExampleNoTrailingNewline() {
34+
ld := NewLineDelimiter(os.Stdout, "|")
35+
defer ld.Flush()
36+
fmt.Fprint(ld, " Hello \n World ")
37+
// Output:
38+
// | Hello |
39+
// | World |
40+
}

0 commit comments

Comments
 (0)