Skip to content

Fix up alignment of columns w/ namespaces. #10851

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 9, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions pkg/kubectl/cmd/cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,72 @@ func ExamplePrintPodWithWideFormat() {
// test1 1/2 podPhase 6 10y kubernetes-minion-abcd
}

func ExamplePrintServiceWithNamespacesAndLabels() {
f, tf, codec := NewAPIFactory()
tf.Printer = kubectl.NewHumanReadablePrinter(false, true, false, []string{"l1"})
tf.Client = &client.FakeRESTClient{
Codec: codec,
Client: nil,
}
cmd := NewCmdRun(f, os.Stdout)
svc := &api.ServiceList{
Items: []api.Service{
{
ObjectMeta: api.ObjectMeta{
Name: "svc1",
Namespace: "ns1",
Labels: map[string]string{
"l1": "value",
},
},
Spec: api.ServiceSpec{
Ports: []api.ServicePort{
{Protocol: "UDP", Port: 53},
{Protocol: "TCP", Port: 53},
},
Selector: map[string]string{
"s": "magic",
},
ClusterIP: "10.1.1.1",
},
Status: api.ServiceStatus{},
},
{
ObjectMeta: api.ObjectMeta{
Name: "svc2",
Namespace: "ns2",
Labels: map[string]string{
"l1": "dolla-bill-yall",
},
},
Spec: api.ServiceSpec{
Ports: []api.ServicePort{
{Protocol: "TCP", Port: 80},
{Protocol: "TCP", Port: 8080},
},
Selector: map[string]string{
"s": "kazam",
},
ClusterIP: "10.1.1.2",
},
Status: api.ServiceStatus{},
}},
}
ld := util.NewLineDelimiter(os.Stdout, "|")
defer ld.Flush()
err := f.PrintObject(cmd, svc, ld)
if err != nil {
fmt.Printf("Unexpected error: %v", err)
}
// Output:
// |NAMESPACE NAME LABELS SELECTOR IP(S) PORT(S) L1|
// |ns1 svc1 l1=value s=magic 10.1.1.1 53/UDP value|
// | 53/TCP |
// |ns2 svc2 l1=dolla-bill-yall s=kazam 10.1.1.2 80/TCP dolla-bill-yall|
// | 8080/TCP |
// ||
}

func TestNormalizationFuncGlobalExistance(t *testing.T) {
// This test can be safely deleted when we will not support multiple flag formats
root := NewKubectlCommand(cmdutil.NewFactory(nil), os.Stdin, os.Stdout, os.Stderr)
Expand Down
17 changes: 11 additions & 6 deletions pkg/kubectl/cmd/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,16 +103,21 @@ func RunGet(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string
if len(args) == 0 {
fmt.Fprint(out, `
You must specify the type of resource to get. Valid resource types include:
* pods (aka 'po')
* replicationcontrollers (aka 'rc')
* services
* nodes (aka 'no')
* componentStatuses (aka 'cs')
* endpoints (aka 'ep')
* events (aka 'ev')
* secrets
* limits
* persistentVolumes (aka 'pv')
* namespaces
* nodes (aka 'no')
* persistentVolumeClaims (aka 'pvc')
* persistentVolumes (aka 'pv')
* pods (aka 'po')
* podTemplates
* quota
* replicationcontrollers (aka 'rc')
* secrets
* serviceAccounts
* services
`)
return errors.New("Required resource not specified.")
}
Expand Down
44 changes: 40 additions & 4 deletions pkg/kubectl/resource_printer.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,8 @@ func (h *HumanReadablePrinter) HandledResources() []string {
return keys
}

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

// Lay out all the other containers on separate lines.
extraLinePrefix := "\t"
if withNamespace {
extraLinePrefix = "\t\t"
}
for _, container := range containers {
_, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", "", container.Name, container.Image, "")
_, err := fmt.Fprintf(w, "%s%s\t%s\t%s", extraLinePrefix, container.Name, container.Image, "")
if err != nil {
return err
}
if _, err := fmt.Fprint(w, appendLabelTabs(columnLabels)); err != nil {
return err
}
}
return nil
}
Expand Down Expand Up @@ -522,11 +531,18 @@ func printReplicationController(controller *api.ReplicationController, w io.Writ
}

// Lay out all the other containers on separate lines.
extraLinePrefix := "\t"
if withNamespace {
extraLinePrefix = "\t\t"
}
for _, container := range containers {
_, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", "", container.Name, container.Image, "", "")
_, err := fmt.Fprintf(w, "%s%s\t%s\t%s\t%s", extraLinePrefix, container.Name, container.Image, "", "")
if err != nil {
return err
}
if _, err := fmt.Fprint(w, appendLabelTabs(columnLabels)); err != nil {
return err
}
}
return nil
}
Expand Down Expand Up @@ -566,6 +582,10 @@ func printService(svc *api.Service, w io.Writer, withNamespace bool, wide bool,
return err
}

extraLinePrefix := "\t\t\t"
if withNamespace {
extraLinePrefix = "\t\t\t\t"
}
count := len(svc.Spec.Ports)
if len(ips) > count {
count = len(ips)
Expand All @@ -580,7 +600,10 @@ func printService(svc *api.Service, w io.Writer, withNamespace bool, wide bool,
port = fmt.Sprintf("%d/%s", svc.Spec.Ports[i].Port, svc.Spec.Ports[i].Protocol)
}
// Lay out additional ports.
if _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", "", "", "", ip, port); err != nil {
if _, err := fmt.Fprintf(w, "%s%s\t%s", extraLinePrefix, ip, port); err != nil {
return err
}
if _, err := fmt.Fprint(w, appendLabelTabs(columnLabels)); err != nil {
return err
}
}
Expand Down Expand Up @@ -626,7 +649,7 @@ func printNamespace(item *api.Namespace, w io.Writer, withNamespace bool, wide b
if withNamespace {
return fmt.Errorf("namespace is not namespaced")
}
if _, err := fmt.Fprintf(w, "%s\t%s\t%s\n", item.Name, formatLabels(item.Labels), item.Status.Phase); err != nil {
if _, err := fmt.Fprintf(w, "%s\t%s\t%s", item.Name, formatLabels(item.Labels), item.Status.Phase); err != nil {
return err
}
_, err := fmt.Fprint(w, appendLabels(item.Labels, columnLabels))
Expand Down Expand Up @@ -945,6 +968,19 @@ func appendLabels(itemLabels map[string]string, columnLabels []string) string {
return buffer.String()
}

// Append a set of tabs for each label column. We need this in the case where
// we have extra lines so that the tabwriter will still line things up.
func appendLabelTabs(columnLabels []string) string {
var buffer bytes.Buffer

for range columnLabels {
buffer.WriteString("\t")
}
buffer.WriteString("\n")

return buffer.String()
}

func formatLabelHeaders(columnLabels []string) []string {
formHead := make([]string, len(columnLabels))
for i, l := range columnLabels {
Expand Down
63 changes: 63 additions & 0 deletions pkg/util/line_delimiter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
Copyright 2015 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package util

import (
"bytes"
"io"
"strings"
)

// A Line Delimiter is a filter that will
type LineDelimiter struct {
output io.Writer
delimiter []byte
buf bytes.Buffer
}

// NewLineDelimiter allocates a new io.Writer that will split input on lines
// and bracket each line with the delimiter string. This can be useful in
// output tests where it is difficult to see and test trailing whitespace.
func NewLineDelimiter(output io.Writer, delimiter string) *LineDelimiter {
return &LineDelimiter{output: output, delimiter: []byte(delimiter)}
}

// Write writes buf to the LineDelimiter ld. The only errors returned are ones
// encountered while writing to the underlying output stream.
func (ld *LineDelimiter) Write(buf []byte) (n int, err error) {
return ld.buf.Write(buf)
}

// Flush all lines up until now. This will assume insert a linebreak at the current point of the stream.
func (ld *LineDelimiter) Flush() (err error) {
lines := strings.Split(ld.buf.String(), "\n")
for _, line := range lines {
if _, err = ld.output.Write(ld.delimiter); err != nil {
return
}
if _, err = ld.output.Write([]byte(line)); err != nil {
return
}
if _, err = ld.output.Write(ld.delimiter); err != nil {
return
}
if _, err = ld.output.Write([]byte("\n")); err != nil {
return
}
}
return
}
40 changes: 40 additions & 0 deletions pkg/util/line_delimiter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
Copyright 2015 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package util

import (
"fmt"
"os"
)

func ExampleTrailingNewline() {
ld := NewLineDelimiter(os.Stdout, "|")
defer ld.Flush()
fmt.Fprint(ld, " Hello \n World \n")
// Output:
// | Hello |
// | World |
// ||
}
func ExampleNoTrailingNewline() {
ld := NewLineDelimiter(os.Stdout, "|")
defer ld.Flush()
fmt.Fprint(ld, " Hello \n World ")
// Output:
// | Hello |
// | World |
}