diff --git a/contrib/completions/bash/kubectl b/contrib/completions/bash/kubectl index 7a6b0c810e..8be1c80483 100644 --- a/contrib/completions/bash/kubectl +++ b/contrib/completions/bash/kubectl @@ -282,6 +282,7 @@ _kubectl_get() two_word_flags+=("-l") flags+=("--show-all") flags+=("-a") + flags+=("--show-labels") flags+=("--sort-by=") flags+=("--template=") two_word_flags+=("-t") @@ -1060,6 +1061,7 @@ _kubectl_rolling-update() flags+=("--schema-cache-dir=") flags+=("--show-all") flags+=("-a") + flags+=("--show-labels") flags+=("--sort-by=") flags+=("--template=") two_word_flags+=("-t") @@ -1496,6 +1498,7 @@ _kubectl_run() flags+=("--service-overrides=") flags+=("--show-all") flags+=("-a") + flags+=("--show-labels") flags+=("--sort-by=") flags+=("--stdin") flags+=("-i") @@ -1570,6 +1573,7 @@ _kubectl_expose() flags+=("--session-affinity=") flags+=("--show-all") flags+=("-a") + flags+=("--show-labels") flags+=("--sort-by=") flags+=("--target-port=") flags+=("--template=") @@ -1634,6 +1638,7 @@ _kubectl_autoscale() flags+=("--save-config") flags+=("--show-all") flags+=("-a") + flags+=("--show-labels") flags+=("--sort-by=") flags+=("--template=") two_word_flags+=("-t") @@ -1921,6 +1926,7 @@ _kubectl_label() two_word_flags+=("-l") flags+=("--show-all") flags+=("-a") + flags+=("--show-labels") flags+=("--sort-by=") flags+=("--template=") two_word_flags+=("-t") @@ -2004,6 +2010,7 @@ _kubectl_annotate() two_word_flags+=("-l") flags+=("--show-all") flags+=("-a") + flags+=("--show-labels") flags+=("--sort-by=") flags+=("--template=") two_word_flags+=("-t") @@ -2056,6 +2063,7 @@ _kubectl_config_view() flags+=("--raw") flags+=("--show-all") flags+=("-a") + flags+=("--show-labels") flags+=("--sort-by=") flags+=("--template=") two_word_flags+=("-t") @@ -2592,6 +2600,7 @@ _kubectl_convert() flags+=("--schema-cache-dir=") flags+=("--show-all") flags+=("-a") + flags+=("--show-labels") flags+=("--sort-by=") flags+=("--template=") two_word_flags+=("-t") diff --git a/docs/man/man1/kubectl-annotate.1 b/docs/man/man1/kubectl-annotate.1 index 3ff4336f22..6794d734a2 100644 --- a/docs/man/man1/kubectl-annotate.1 +++ b/docs/man/man1/kubectl-annotate.1 @@ -71,6 +71,10 @@ horizontalpodautoscalers (hpa), resourcequotas (quota) or secrets. \fB\-a\fP, \fB\-\-show\-all\fP=false When printing, show all resources (default hide terminated pods.) +.PP +\fB\-\-show\-labels\fP=false + When printing, show all labels as the last column (default hide labels column) + .PP \fB\-\-sort\-by\fP="" If non\-empty, sort list types using this field specification. The field specification is expressed as a JSONPath expression (e.g. 'ObjectMeta.Name'). The field in the API resource specified by this JSONPath expression must be an integer or a string. diff --git a/docs/man/man1/kubectl-autoscale.1 b/docs/man/man1/kubectl-autoscale.1 index 867e4471b4..33c5868975 100644 --- a/docs/man/man1/kubectl-autoscale.1 +++ b/docs/man/man1/kubectl-autoscale.1 @@ -75,6 +75,10 @@ An autoscaler can automatically increase or decrease number of pods deployed wit \fB\-a\fP, \fB\-\-show\-all\fP=false When printing, show all resources (default hide terminated pods.) +.PP +\fB\-\-show\-labels\fP=false + When printing, show all labels as the last column (default hide labels column) + .PP \fB\-\-sort\-by\fP="" If non\-empty, sort list types using this field specification. The field specification is expressed as a JSONPath expression (e.g. 'ObjectMeta.Name'). The field in the API resource specified by this JSONPath expression must be an integer or a string. diff --git a/docs/man/man1/kubectl-config-view.1 b/docs/man/man1/kubectl-config-view.1 index 2ac3848213..1446c90fa4 100644 --- a/docs/man/man1/kubectl-config-view.1 +++ b/docs/man/man1/kubectl-config-view.1 @@ -54,6 +54,10 @@ You can use \-\-output jsonpath={...} to extract specific values using a jsonpat \fB\-a\fP, \fB\-\-show\-all\fP=false When printing, show all resources (default hide terminated pods.) +.PP +\fB\-\-show\-labels\fP=false + When printing, show all labels as the last column (default hide labels column) + .PP \fB\-\-sort\-by\fP="" If non\-empty, sort list types using this field specification. The field specification is expressed as a JSONPath expression (e.g. 'ObjectMeta.Name'). The field in the API resource specified by this JSONPath expression must be an integer or a string. diff --git a/docs/man/man1/kubectl-convert.1 b/docs/man/man1/kubectl-convert.1 index bae38580fb..c0aebad0a9 100644 --- a/docs/man/man1/kubectl-convert.1 +++ b/docs/man/man1/kubectl-convert.1 @@ -57,6 +57,10 @@ to change to output destination. \fB\-a\fP, \fB\-\-show\-all\fP=false When printing, show all resources (default hide terminated pods.) +.PP +\fB\-\-show\-labels\fP=false + When printing, show all labels as the last column (default hide labels column) + .PP \fB\-\-sort\-by\fP="" If non\-empty, sort list types using this field specification. The field specification is expressed as a JSONPath expression (e.g. 'ObjectMeta.Name'). The field in the API resource specified by this JSONPath expression must be an integer or a string. diff --git a/docs/man/man1/kubectl-expose.1 b/docs/man/man1/kubectl-expose.1 index 6865f091b7..5274cce775 100644 --- a/docs/man/man1/kubectl-expose.1 +++ b/docs/man/man1/kubectl-expose.1 @@ -105,6 +105,10 @@ the new service will re\-use the labels from the resource it exposes. \fB\-a\fP, \fB\-\-show\-all\fP=false When printing, show all resources (default hide terminated pods.) +.PP +\fB\-\-show\-labels\fP=false + When printing, show all labels as the last column (default hide labels column) + .PP \fB\-\-sort\-by\fP="" If non\-empty, sort list types using this field specification. The field specification is expressed as a JSONPath expression (e.g. 'ObjectMeta.Name'). The field in the API resource specified by this JSONPath expression must be an integer or a string. diff --git a/docs/man/man1/kubectl-get.1 b/docs/man/man1/kubectl-get.1 index 0c517589e5..ba518037c5 100644 --- a/docs/man/man1/kubectl-get.1 +++ b/docs/man/man1/kubectl-get.1 @@ -66,6 +66,10 @@ of the \-\-template flag, you can filter the attributes of the fetched resource( \fB\-a\fP, \fB\-\-show\-all\fP=false When printing, show all resources (default hide terminated pods.) +.PP +\fB\-\-show\-labels\fP=false + When printing, show all labels as the last column (default hide labels column) + .PP \fB\-\-sort\-by\fP="" If non\-empty, sort list types using this field specification. The field specification is expressed as a JSONPath expression (e.g. 'ObjectMeta.Name'). The field in the API resource specified by this JSONPath expression must be an integer or a string. diff --git a/docs/man/man1/kubectl-label.1 b/docs/man/man1/kubectl-label.1 index ea14efdac4..9f2c7c5c4a 100644 --- a/docs/man/man1/kubectl-label.1 +++ b/docs/man/man1/kubectl-label.1 @@ -68,6 +68,10 @@ If \-\-resource\-version is specified, then updates will use this resource versi \fB\-a\fP, \fB\-\-show\-all\fP=false When printing, show all resources (default hide terminated pods.) +.PP +\fB\-\-show\-labels\fP=false + When printing, show all labels as the last column (default hide labels column) + .PP \fB\-\-sort\-by\fP="" If non\-empty, sort list types using this field specification. The field specification is expressed as a JSONPath expression (e.g. 'ObjectMeta.Name'). The field in the API resource specified by this JSONPath expression must be an integer or a string. diff --git a/docs/man/man1/kubectl-rolling-update.1 b/docs/man/man1/kubectl-rolling-update.1 index f492d35fbe..8e9a245c31 100644 --- a/docs/man/man1/kubectl-rolling-update.1 +++ b/docs/man/man1/kubectl-rolling-update.1 @@ -72,6 +72,10 @@ existing replication controller and overwrite at least one (common) label in its \fB\-a\fP, \fB\-\-show\-all\fP=false When printing, show all resources (default hide terminated pods.) +.PP +\fB\-\-show\-labels\fP=false + When printing, show all labels as the last column (default hide labels column) + .PP \fB\-\-sort\-by\fP="" If non\-empty, sort list types using this field specification. The field specification is expressed as a JSONPath expression (e.g. 'ObjectMeta.Name'). The field in the API resource specified by this JSONPath expression must be an integer or a string. diff --git a/docs/man/man1/kubectl-run.1 b/docs/man/man1/kubectl-run.1 index 9131f717d0..6ea5007970 100644 --- a/docs/man/man1/kubectl-run.1 +++ b/docs/man/man1/kubectl-run.1 @@ -120,6 +120,10 @@ Creates a replication controller or job to manage the created container(s). \fB\-a\fP, \fB\-\-show\-all\fP=false When printing, show all resources (default hide terminated pods.) +.PP +\fB\-\-show\-labels\fP=false + When printing, show all labels as the last column (default hide labels column) + .PP \fB\-\-sort\-by\fP="" If non\-empty, sort list types using this field specification. The field specification is expressed as a JSONPath expression (e.g. 'ObjectMeta.Name'). The field in the API resource specified by this JSONPath expression must be an integer or a string. diff --git a/docs/user-guide/kubectl/kubectl_annotate.md b/docs/user-guide/kubectl/kubectl_annotate.md index 5812ff4b6b..b7b273deb4 100644 --- a/docs/user-guide/kubectl/kubectl_annotate.md +++ b/docs/user-guide/kubectl/kubectl_annotate.md @@ -92,6 +92,7 @@ $ kubectl annotate pods foo description- --resource-version="": If non-empty, the annotation update will only succeed if this is the current resource-version for the object. Only valid when specifying a single resource. -l, --selector="": Selector (label query) to filter on -a, --show-all[=false]: When printing, show all resources (default hide terminated pods.) + --show-labels[=false]: When printing, show all labels as the last column (default hide labels column) --sort-by="": If non-empty, sort list types using this field specification. The field specification is expressed as a JSONPath expression (e.g. 'ObjectMeta.Name'). The field in the API resource specified by this JSONPath expression must be an integer or a string. --template="": Template string or path to template file to use when -o=go-template, -o=go-template-file. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview]. ``` diff --git a/docs/user-guide/kubectl/kubectl_autoscale.md b/docs/user-guide/kubectl/kubectl_autoscale.md index 61c0ff530e..ade072cef5 100644 --- a/docs/user-guide/kubectl/kubectl_autoscale.md +++ b/docs/user-guide/kubectl/kubectl_autoscale.md @@ -74,6 +74,7 @@ $ kubectl autoscale rc foo --max=5 --cpu-percent=80 --record[=false]: Record current kubectl command in the resource annotation. --save-config[=false]: If true, the configuration of current object will be saved in its annotation. This is useful when you want to perform kubectl apply on this object in the future. -a, --show-all[=false]: When printing, show all resources (default hide terminated pods.) + --show-labels[=false]: When printing, show all labels as the last column (default hide labels column) --sort-by="": If non-empty, sort list types using this field specification. The field specification is expressed as a JSONPath expression (e.g. 'ObjectMeta.Name'). The field in the API resource specified by this JSONPath expression must be an integer or a string. --template="": Template string or path to template file to use when -o=go-template, -o=go-template-file. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview]. ``` diff --git a/docs/user-guide/kubectl/kubectl_config_view.md b/docs/user-guide/kubectl/kubectl_config_view.md index db1a1adac6..bf9c8a995c 100644 --- a/docs/user-guide/kubectl/kubectl_config_view.md +++ b/docs/user-guide/kubectl/kubectl_config_view.md @@ -68,6 +68,7 @@ $ kubectl config view -o jsonpath='{.users[?(@.name == "e2e")].user.password}' --output-version="": Output the formatted object with the given version (default api-version). --raw[=false]: display raw byte data -a, --show-all[=false]: When printing, show all resources (default hide terminated pods.) + --show-labels[=false]: When printing, show all labels as the last column (default hide labels column) --sort-by="": If non-empty, sort list types using this field specification. The field specification is expressed as a JSONPath expression (e.g. 'ObjectMeta.Name'). The field in the API resource specified by this JSONPath expression must be an integer or a string. --template="": Template string or path to template file to use when -o=go-template, -o=go-template-file. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview]. ``` diff --git a/docs/user-guide/kubectl/kubectl_convert.md b/docs/user-guide/kubectl/kubectl_convert.md index e048ab4536..d83a39d6c4 100644 --- a/docs/user-guide/kubectl/kubectl_convert.md +++ b/docs/user-guide/kubectl/kubectl_convert.md @@ -74,6 +74,7 @@ $ kubectl convert -f . | kubectl create -f - --output-version="": Output the formatted object with the given version (default api-version). --schema-cache-dir="~/.kube/schema": If non-empty, load/store cached API schemas in this directory, default is '$HOME/.kube/schema' -a, --show-all[=false]: When printing, show all resources (default hide terminated pods.) + --show-labels[=false]: When printing, show all labels as the last column (default hide labels column) --sort-by="": If non-empty, sort list types using this field specification. The field specification is expressed as a JSONPath expression (e.g. 'ObjectMeta.Name'). The field in the API resource specified by this JSONPath expression must be an integer or a string. --template="": Template string or path to template file to use when -o=go-template, -o=go-template-file. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview]. --validate[=true]: If true, use a schema to validate the input before sending it @@ -111,7 +112,7 @@ $ kubectl convert -f . | kubectl create -f - * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra on 24-Nov-2015 +###### Auto generated by spf13/cobra on 20-Jan-2016 [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_convert.md?pixel)]() diff --git a/docs/user-guide/kubectl/kubectl_expose.md b/docs/user-guide/kubectl/kubectl_expose.md index 79e524157d..4d6e1d66d0 100644 --- a/docs/user-guide/kubectl/kubectl_expose.md +++ b/docs/user-guide/kubectl/kubectl_expose.md @@ -91,6 +91,7 @@ $ kubectl expose rc streamer --port=4100 --protocol=udp --name=video-stream --selector="": A label selector to use for this service. If empty (the default) infer the selector from the replication controller. --session-affinity="": If non-empty, set the session affinity for the service to this; legal values: 'None', 'ClientIP' -a, --show-all[=false]: When printing, show all resources (default hide terminated pods.) + --show-labels[=false]: When printing, show all labels as the last column (default hide labels column) --sort-by="": If non-empty, sort list types using this field specification. The field specification is expressed as a JSONPath expression (e.g. 'ObjectMeta.Name'). The field in the API resource specified by this JSONPath expression must be an integer or a string. --target-port="": Name or number for the port on the container that the service should direct traffic to. Optional. --template="": Template string or path to template file to use when -o=go-template, -o=go-template-file. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview]. diff --git a/docs/user-guide/kubectl/kubectl_get.md b/docs/user-guide/kubectl/kubectl_get.md index 25f21b8720..29c202fa30 100644 --- a/docs/user-guide/kubectl/kubectl_get.md +++ b/docs/user-guide/kubectl/kubectl_get.md @@ -94,6 +94,7 @@ $ kubectl get rc/web service/frontend pods/web-pod-13je7 --output-version="": Output the formatted object with the given version (default api-version). -l, --selector="": Selector (label query) to filter on -a, --show-all[=false]: When printing, show all resources (default hide terminated pods.) + --show-labels[=false]: When printing, show all labels as the last column (default hide labels column) --sort-by="": If non-empty, sort list types using this field specification. The field specification is expressed as a JSONPath expression (e.g. 'ObjectMeta.Name'). The field in the API resource specified by this JSONPath expression must be an integer or a string. --template="": Template string or path to template file to use when -o=go-template, -o=go-template-file. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview]. -w, --watch[=false]: After listing/getting the requested object, watch for changes. @@ -132,7 +133,7 @@ $ kubectl get rc/web service/frontend pods/web-pod-13je7 * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra on 22-Dec-2015 +###### Auto generated by spf13/cobra on 20-Jan-2016 [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_get.md?pixel)]() diff --git a/docs/user-guide/kubectl/kubectl_label.md b/docs/user-guide/kubectl/kubectl_label.md index 9455fff8e2..31d250e0b9 100644 --- a/docs/user-guide/kubectl/kubectl_label.md +++ b/docs/user-guide/kubectl/kubectl_label.md @@ -86,6 +86,7 @@ $ kubectl label pods foo bar- --resource-version="": If non-empty, the labels update will only succeed if this is the current resource-version for the object. Only valid when specifying a single resource. -l, --selector="": Selector (label query) to filter on -a, --show-all[=false]: When printing, show all resources (default hide terminated pods.) + --show-labels[=false]: When printing, show all labels as the last column (default hide labels column) --sort-by="": If non-empty, sort list types using this field specification. The field specification is expressed as a JSONPath expression (e.g. 'ObjectMeta.Name'). The field in the API resource specified by this JSONPath expression must be an integer or a string. --template="": Template string or path to template file to use when -o=go-template, -o=go-template-file. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview]. ``` diff --git a/docs/user-guide/kubectl/kubectl_rolling-update.md b/docs/user-guide/kubectl/kubectl_rolling-update.md index 65fa08d326..4c3889555b 100644 --- a/docs/user-guide/kubectl/kubectl_rolling-update.md +++ b/docs/user-guide/kubectl/kubectl_rolling-update.md @@ -85,6 +85,7 @@ $ kubectl rolling-update frontend-v1 frontend-v2 --rollback --rollback[=false]: If true, this is a request to abort an existing rollout that is partially rolled out. It effectively reverses current and next and runs a rollout --schema-cache-dir="~/.kube/schema": If non-empty, load/store cached API schemas in this directory, default is '$HOME/.kube/schema' -a, --show-all[=false]: When printing, show all resources (default hide terminated pods.) + --show-labels[=false]: When printing, show all labels as the last column (default hide labels column) --sort-by="": If non-empty, sort list types using this field specification. The field specification is expressed as a JSONPath expression (e.g. 'ObjectMeta.Name'). The field in the API resource specified by this JSONPath expression must be an integer or a string. --template="": Template string or path to template file to use when -o=go-template, -o=go-template-file. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview]. --timeout=5m0s: Max time to wait for a replication controller to update before giving up. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". @@ -124,7 +125,7 @@ $ kubectl rolling-update frontend-v1 frontend-v2 --rollback * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra on 8-Dec-2015 +###### Auto generated by spf13/cobra on 20-Jan-2016 [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_rolling-update.md?pixel)]() diff --git a/docs/user-guide/kubectl/kubectl_run.md b/docs/user-guide/kubectl/kubectl_run.md index 56b49387d5..887c123948 100644 --- a/docs/user-guide/kubectl/kubectl_run.md +++ b/docs/user-guide/kubectl/kubectl_run.md @@ -108,6 +108,7 @@ $ kubectl run pi --image=perl --restart=OnFailure -- perl -Mbignum=bpi -wle 'pri --service-generator="service/v2": The name of the generator to use for creating a service. Only used if --expose is true --service-overrides="": An inline JSON override for the generated service object. If this is non-empty, it is used to override the generated object. Requires that the object supply a valid apiVersion field. Only used if --expose is true. -a, --show-all[=false]: When printing, show all resources (default hide terminated pods.) + --show-labels[=false]: When printing, show all labels as the last column (default hide labels column) --sort-by="": If non-empty, sort list types using this field specification. The field specification is expressed as a JSONPath expression (e.g. 'ObjectMeta.Name'). The field in the API resource specified by this JSONPath expression must be an integer or a string. -i, --stdin[=false]: Keep stdin open on the container(s) in the pod, even if nothing is attached. --template="": Template string or path to template file to use when -o=go-template, -o=go-template-file. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview]. diff --git a/hack/verify-flags/known-flags.txt b/hack/verify-flags/known-flags.txt index e5de530b25..f4bb128f66 100644 --- a/hack/verify-flags/known-flags.txt +++ b/hack/verify-flags/known-flags.txt @@ -327,6 +327,7 @@ service-overrides service-sync-period session-affinity show-all +show-labels shutdown-fd shutdown-fifo since-seconds diff --git a/pkg/kubectl/cmd/cmd_test.go b/pkg/kubectl/cmd/cmd_test.go index 72f4ac867f..fbcc2be5fd 100644 --- a/pkg/kubectl/cmd/cmd_test.go +++ b/pkg/kubectl/cmd/cmd_test.go @@ -195,7 +195,7 @@ func NewTestFactory() (*cmdutil.Factory, *testFactory, runtime.Codec) { Describer: func(*meta.RESTMapping) (kubectl.Describer, error) { return t.Describer, t.Err }, - Printer: func(mapping *meta.RESTMapping, noHeaders, withNamespace bool, wide bool, showAll bool, absoluteTimestamps bool, columnLabels []string) (kubectl.ResourcePrinter, error) { + Printer: func(mapping *meta.RESTMapping, noHeaders, withNamespace bool, wide bool, showAll bool, showLabels bool, absoluteTimestamps bool, columnLabels []string) (kubectl.ResourcePrinter, error) { return t.Printer, t.Err }, Validator: func(validate bool, cacheDir string) (validation.Schema, error) { @@ -253,7 +253,7 @@ func NewAPIFactory() (*cmdutil.Factory, *testFactory, runtime.Codec) { Describer: func(*meta.RESTMapping) (kubectl.Describer, error) { return t.Describer, t.Err }, - Printer: func(mapping *meta.RESTMapping, noHeaders, withNamespace bool, wide bool, showAll bool, absoluteTimestamps bool, columnLabels []string) (kubectl.ResourcePrinter, error) { + Printer: func(mapping *meta.RESTMapping, noHeaders, withNamespace bool, wide bool, showAll bool, showLabels bool, absoluteTimestamps bool, columnLabels []string) (kubectl.ResourcePrinter, error) { return t.Printer, t.Err }, Validator: func(validate bool, cacheDir string) (validation.Schema, error) { @@ -327,7 +327,7 @@ func stringBody(body string) io.ReadCloser { func ExamplePrintReplicationControllerWithNamespace() { f, tf, codec := NewAPIFactory() - tf.Printer = kubectl.NewHumanReadablePrinter(false, true, false, false, false, []string{}) + tf.Printer = kubectl.NewHumanReadablePrinter(false, true, false, false, false, false, []string{}) tf.Client = &fake.RESTClient{ Codec: codec, Client: nil, @@ -369,7 +369,7 @@ func ExamplePrintReplicationControllerWithNamespace() { func ExamplePrintPodWithWideFormat() { f, tf, codec := NewAPIFactory() - tf.Printer = kubectl.NewHumanReadablePrinter(false, false, true, false, false, []string{}) + tf.Printer = kubectl.NewHumanReadablePrinter(false, false, true, false, false, false, []string{}) tf.Client = &fake.RESTClient{ Codec: codec, Client: nil, @@ -402,6 +402,45 @@ func ExamplePrintPodWithWideFormat() { // test1 1/2 podPhase 6 10y kubernetes-minion-abcd } +func ExamplePrintPodWithShowLabels() { + f, tf, codec := NewAPIFactory() + tf.Printer = kubectl.NewHumanReadablePrinter(false, false, false, false, true, false, []string{}) + tf.Client = &fake.RESTClient{ + Codec: codec, + Client: nil, + } + nodeName := "kubernetes-minion-abcd" + cmd := NewCmdRun(f, os.Stdin, os.Stdout, os.Stderr) + pod := &api.Pod{ + ObjectMeta: api.ObjectMeta{ + Name: "test1", + CreationTimestamp: unversioned.Time{Time: time.Now().AddDate(-10, 0, 0)}, + Labels: map[string]string{ + "l1": "key", + "l2": "value", + }, + }, + Spec: api.PodSpec{ + Containers: make([]api.Container, 2), + NodeName: nodeName, + }, + Status: api.PodStatus{ + Phase: "podPhase", + ContainerStatuses: []api.ContainerStatus{ + {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}}, + {RestartCount: 3}, + }, + }, + } + err := f.PrintObject(cmd, pod, os.Stdout) + if err != nil { + fmt.Printf("Unexpected error: %v", err) + } + // Output: + // NAME READY STATUS RESTARTS AGE LABELS + // test1 1/2 podPhase 6 10y l1=key,l2=value +} + func newAllPhasePodList() *api.PodList { nodeName := "kubernetes-minion-abcd" return &api.PodList{ @@ -496,7 +535,7 @@ func newAllPhasePodList() *api.PodList { func ExamplePrintPodHideTerminated() { f, tf, codec := NewAPIFactory() - tf.Printer = kubectl.NewHumanReadablePrinter(false, false, false, false, false, []string{}) + tf.Printer = kubectl.NewHumanReadablePrinter(false, false, false, false, false, false, []string{}) tf.Client = &fake.RESTClient{ Codec: codec, Client: nil, @@ -516,7 +555,7 @@ func ExamplePrintPodHideTerminated() { func ExamplePrintPodShowAll() { f, tf, codec := NewAPIFactory() - tf.Printer = kubectl.NewHumanReadablePrinter(false, false, false, true, false, []string{}) + tf.Printer = kubectl.NewHumanReadablePrinter(false, false, false, true, false, false, []string{}) tf.Client = &fake.RESTClient{ Codec: codec, Client: nil, @@ -538,7 +577,7 @@ func ExamplePrintPodShowAll() { func ExamplePrintServiceWithNamespacesAndLabels() { f, tf, codec := NewAPIFactory() - tf.Printer = kubectl.NewHumanReadablePrinter(false, true, false, false, false, []string{"l1"}) + tf.Printer = kubectl.NewHumanReadablePrinter(false, true, false, false, false, false, []string{"l1"}) tf.Client = &fake.RESTClient{ Codec: codec, Client: nil, diff --git a/pkg/kubectl/cmd/delete.go b/pkg/kubectl/cmd/delete.go index 94973d6e58..7dffd4f990 100644 --- a/pkg/kubectl/cmd/delete.go +++ b/pkg/kubectl/cmd/delete.go @@ -71,7 +71,7 @@ func NewCmdDelete(f *cmdutil.Factory, out io.Writer) *cobra.Command { // retrieve a list of handled resources from printer as valid args validArgs := []string{} - p, err := f.Printer(nil, false, false, false, false, false, []string{}) + p, err := f.Printer(nil, false, false, false, false, false, false, []string{}) cmdutil.CheckErr(err) if p != nil { validArgs = p.HandledResources() diff --git a/pkg/kubectl/cmd/get.go b/pkg/kubectl/cmd/get.go index e20d520ef1..c8123c9ff5 100644 --- a/pkg/kubectl/cmd/get.go +++ b/pkg/kubectl/cmd/get.go @@ -78,7 +78,7 @@ func NewCmdGet(f *cmdutil.Factory, out io.Writer) *cobra.Command { // retrieve a list of handled resources from printer as valid args validArgs := []string{} - p, err := f.Printer(nil, false, false, false, false, false, []string{}) + p, err := f.Printer(nil, false, false, false, false, false, false, []string{}) cmdutil.CheckErr(err) if p != nil { validArgs = p.HandledResources() diff --git a/pkg/kubectl/cmd/label.go b/pkg/kubectl/cmd/label.go index 9e6d76edc2..39c401d520 100644 --- a/pkg/kubectl/cmd/label.go +++ b/pkg/kubectl/cmd/label.go @@ -72,7 +72,7 @@ func NewCmdLabel(f *cmdutil.Factory, out io.Writer) *cobra.Command { // retrieve a list of handled resources from printer as valid args validArgs := []string{} - p, err := f.Printer(nil, false, false, false, false, false, []string{}) + p, err := f.Printer(nil, false, false, false, false, false, false, []string{}) cmdutil.CheckErr(err) if p != nil { validArgs = p.HandledResources() diff --git a/pkg/kubectl/cmd/util/factory.go b/pkg/kubectl/cmd/util/factory.go index 4633d24f92..38505cdf14 100644 --- a/pkg/kubectl/cmd/util/factory.go +++ b/pkg/kubectl/cmd/util/factory.go @@ -83,7 +83,7 @@ type Factory struct { // Returns a Describer for displaying the specified RESTMapping type or an error. Describer func(mapping *meta.RESTMapping) (kubectl.Describer, error) // Returns a Printer for formatting objects of the given type or an error. - Printer func(mapping *meta.RESTMapping, noHeaders, withNamespace bool, wide bool, showAll bool, absoluteTimestamps bool, columnLabels []string) (kubectl.ResourcePrinter, error) + Printer func(mapping *meta.RESTMapping, noHeaders, withNamespace bool, wide bool, showAll bool, showLabels bool, absoluteTimestamps bool, columnLabels []string) (kubectl.ResourcePrinter, error) // Returns a Scaler for changing the size of the specified RESTMapping type or an error Scaler func(mapping *meta.RESTMapping) (kubectl.Scaler, error) // Returns a Reaper for gracefully shutting down resources. @@ -238,8 +238,8 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { JSONEncoder: func() runtime.Encoder { return api.Codecs.LegacyCodec(registered.EnabledVersions()...) }, - Printer: func(mapping *meta.RESTMapping, noHeaders, withNamespace bool, wide bool, showAll bool, absoluteTimestamps bool, columnLabels []string) (kubectl.ResourcePrinter, error) { - return kubectl.NewHumanReadablePrinter(noHeaders, withNamespace, wide, showAll, absoluteTimestamps, columnLabels), nil + Printer: func(mapping *meta.RESTMapping, noHeaders, withNamespace bool, wide bool, showAll bool, showLabels bool, absoluteTimestamps bool, columnLabels []string) (kubectl.ResourcePrinter, error) { + return kubectl.NewHumanReadablePrinter(noHeaders, withNamespace, wide, showAll, showLabels, absoluteTimestamps, columnLabels), nil }, PodSelectorForObject: func(object runtime.Object) (string, error) { // TODO: replace with a swagger schema based approach (identify pod selector via schema introspection) @@ -753,7 +753,7 @@ func (f *Factory) PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMappin if err != nil { columnLabel = []string{} } - printer, err = f.Printer(mapping, GetFlagBool(cmd, "no-headers"), withNamespace, GetWideFlag(cmd), GetFlagBool(cmd, "show-all"), isWatch(cmd), columnLabel) + printer, err = f.Printer(mapping, GetFlagBool(cmd, "no-headers"), withNamespace, GetWideFlag(cmd), GetFlagBool(cmd, "show-all"), GetFlagBool(cmd, "show-labels"), isWatch(cmd), columnLabel) if err != nil { return nil, err } diff --git a/pkg/kubectl/cmd/util/printing.go b/pkg/kubectl/cmd/util/printing.go index ce68ca2d12..40d7c07fa0 100644 --- a/pkg/kubectl/cmd/util/printing.go +++ b/pkg/kubectl/cmd/util/printing.go @@ -33,6 +33,7 @@ func AddPrinterFlags(cmd *cobra.Command) { cmd.Flags().StringP("output", "o", "", "Output format. One of: json|yaml|wide|name|go-template=...|go-template-file=...|jsonpath=...|jsonpath-file=... See golang template [http://golang.org/pkg/text/template/#pkg-overview] and jsonpath template [http://releases.k8s.io/HEAD/docs/user-guide/jsonpath.md].") cmd.Flags().String("output-version", "", "Output the formatted object with the given version (default api-version).") cmd.Flags().Bool("no-headers", false, "When using the default output, don't print headers.") + cmd.Flags().Bool("show-labels", false, "When printing, show all labels as the last column (default hide labels column)") // template shorthand -t is deprecated to support -t for --tty // TODO: remove template flag shorthand -t cmd.Flags().StringP("template", "t", "", "Template string or path to template file to use when -o=go-template, -o=go-template-file. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview].") diff --git a/pkg/kubectl/resource_printer.go b/pkg/kubectl/resource_printer.go index 0a4044430d..d3c4b6ff00 100644 --- a/pkg/kubectl/resource_printer.go +++ b/pkg/kubectl/resource_printer.go @@ -304,6 +304,7 @@ type PrintOptions struct { WithNamespace bool Wide bool ShowAll bool + ShowLabels bool AbsoluteTimestamps bool ColumnLabels []string } @@ -319,7 +320,7 @@ type HumanReadablePrinter struct { } // NewHumanReadablePrinter creates a HumanReadablePrinter. -func NewHumanReadablePrinter(noHeaders, withNamespace bool, wide bool, showAll bool, absoluteTimestamps bool, columnLabels []string) *HumanReadablePrinter { +func NewHumanReadablePrinter(noHeaders, withNamespace bool, wide bool, showAll bool, showLabels bool, absoluteTimestamps bool, columnLabels []string) *HumanReadablePrinter { printer := &HumanReadablePrinter{ handlerMap: make(map[reflect.Type]*handlerEntry), options: PrintOptions{ @@ -327,6 +328,7 @@ func NewHumanReadablePrinter(noHeaders, withNamespace bool, wide bool, showAll b WithNamespace: withNamespace, Wide: wide, ShowAll: showAll, + ShowLabels: showLabels, AbsoluteTimestamps: absoluteTimestamps, ColumnLabels: columnLabels, }, @@ -399,16 +401,16 @@ var jobColumns = []string{"JOB", "CONTAINER(S)", "IMAGE(S)", "SELECTOR", "SUCCES var serviceColumns = []string{"NAME", "CLUSTER-IP", "EXTERNAL-IP", "PORT(S)", "SELECTOR", "AGE"} var ingressColumns = []string{"NAME", "RULE", "BACKEND", "ADDRESS"} var endpointColumns = []string{"NAME", "ENDPOINTS", "AGE"} -var nodeColumns = []string{"NAME", "LABELS", "STATUS", "AGE"} +var nodeColumns = []string{"NAME", "STATUS", "AGE"} var daemonSetColumns = []string{"NAME", "CONTAINER(S)", "IMAGE(S)", "SELECTOR", "NODE-SELECTOR"} var eventColumns = []string{"FIRSTSEEN", "LASTSEEN", "COUNT", "NAME", "KIND", "SUBOBJECT", "TYPE", "REASON", "SOURCE", "MESSAGE"} var limitRangeColumns = []string{"NAME", "AGE"} var resourceQuotaColumns = []string{"NAME", "AGE"} -var namespaceColumns = []string{"NAME", "LABELS", "STATUS", "AGE"} +var namespaceColumns = []string{"NAME", "STATUS", "AGE"} var secretColumns = []string{"NAME", "TYPE", "DATA", "AGE"} var serviceAccountColumns = []string{"NAME", "SECRETS", "AGE"} -var persistentVolumeColumns = []string{"NAME", "LABELS", "CAPACITY", "ACCESSMODES", "STATUS", "CLAIM", "REASON", "AGE"} -var persistentVolumeClaimColumns = []string{"NAME", "LABELS", "STATUS", "VOLUME", "CAPACITY", "ACCESSMODES", "AGE"} +var persistentVolumeColumns = []string{"NAME", "CAPACITY", "ACCESSMODES", "STATUS", "CLAIM", "REASON", "AGE"} +var persistentVolumeClaimColumns = []string{"NAME", "STATUS", "VOLUME", "CAPACITY", "ACCESSMODES", "AGE"} var componentStatusColumns = []string{"NAME", "STATUS", "MESSAGE", "ERROR"} var thirdPartyResourceColumns = []string{"NAME", "DESCRIPTION", "VERSION(S)"} var horizontalPodAutoscalerColumns = []string{"NAME", "REFERENCE", "TARGET", "CURRENT", "MINPODS", "MAXPODS", "AGE"} @@ -609,8 +611,14 @@ func printPodBase(pod *api.Pod, w io.Writer, options PrintOptions) error { } } - _, err := fmt.Fprint(w, appendLabels(pod.Labels, options.ColumnLabels)) - return err + if _, err := fmt.Fprint(w, appendLabels(pod.Labels, options.ColumnLabels)); err != nil { + return err + } + if _, err := fmt.Fprint(w, appendAllLabels(options.ShowLabels, pod.Labels)); err != nil { + return err + } + + return nil } func printPodList(podList *api.PodList, w io.Writer, options PrintOptions) error { @@ -648,6 +656,9 @@ func printPodTemplate(pod *api.PodTemplate, w io.Writer, options PrintOptions) e if _, err := fmt.Fprint(w, appendLabels(pod.Labels, options.ColumnLabels)); err != nil { return err } + if _, err := fmt.Fprint(w, appendAllLabels(options.ShowLabels, pod.Labels)); err != nil { + return err + } // Lay out all the other containers on separate lines. extraLinePrefix := "\t" @@ -702,6 +713,9 @@ func printReplicationController(controller *api.ReplicationController, w io.Writ if _, err := fmt.Fprint(w, appendLabels(controller.Labels, options.ColumnLabels)); err != nil { return err } + if _, err := fmt.Fprint(w, appendAllLabels(options.ShowLabels, controller.Labels)); err != nil { + return err + } // Lay out all the other containers on separate lines. extraLinePrefix := "\t" @@ -756,6 +770,9 @@ func printJob(job *extensions.Job, w io.Writer, options PrintOptions) error { if _, err := fmt.Fprint(w, appendLabels(job.Labels, options.ColumnLabels)); err != nil { return err } + if _, err := fmt.Fprint(w, appendAllLabels(options.ShowLabels, job.Labels)); err != nil { + return err + } // Lay out all the other containers on separate lines. extraLinePrefix := "\t" @@ -852,7 +869,8 @@ func printService(svc *api.Service, w io.Writer, options PrintOptions) error { if _, err := fmt.Fprint(w, appendLabels(svc.Labels, options.ColumnLabels)); err != nil { return err } - return nil + _, err := fmt.Fprint(w, appendAllLabels(options.ShowLabels, svc.Labels)) + return err } func printServiceList(list *api.ServiceList, w io.Writer, options PrintOptions) error { @@ -883,7 +901,7 @@ func printIngress(ingress *extensions.Ingress, w io.Writer, options PrintOptions } } - if _, err := fmt.Fprintf(w, "%s\t%v\t%v\t%v\n", + if _, err := fmt.Fprintf(w, "%s\t%v\t%v\t%v", name, "-", backendStringer(ingress.Spec.Backend), @@ -891,6 +909,10 @@ func printIngress(ingress *extensions.Ingress, w io.Writer, options PrintOptions return err } + if _, err := fmt.Fprint(w, appendAllLabels(options.ShowLabels, ingress.Labels)); err != nil { + return err + } + // Lay out all the rules on separate lines. extraLinePrefix := "" if options.WithNamespace { @@ -961,6 +983,9 @@ func printDaemonSet(ds *extensions.DaemonSet, w io.Writer, options PrintOptions) if _, err := fmt.Fprint(w, appendLabels(ds.Labels, options.ColumnLabels)); err != nil { return err } + if _, err := fmt.Fprint(w, appendAllLabels(options.ShowLabels, ds.Labels)); err != nil { + return err + } // Lay out all the other containers on separate lines. extraLinePrefix := "\t" @@ -1000,7 +1025,10 @@ func printEndpoints(endpoints *api.Endpoints, w io.Writer, options PrintOptions) if _, err := fmt.Fprintf(w, "%s\t%s\t%s", name, formatEndpoints(endpoints, nil), translateTimestamp(endpoints.CreationTimestamp)); err != nil { return err } - _, err := fmt.Fprint(w, appendLabels(endpoints.Labels, options.ColumnLabels)) + if _, err := fmt.Fprint(w, appendLabels(endpoints.Labels, options.ColumnLabels)); err != nil { + return err + } + _, err := fmt.Fprint(w, appendAllLabels(options.ShowLabels, endpoints.Labels)) return err } @@ -1018,10 +1046,13 @@ func printNamespace(item *api.Namespace, w io.Writer, options PrintOptions) erro return fmt.Errorf("namespace is not namespaced") } - if _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s", item.Name, labels.FormatLabels(item.Labels), item.Status.Phase, translateTimestamp(item.CreationTimestamp)); err != nil { + if _, err := fmt.Fprintf(w, "%s\t%s\t%s", item.Name, item.Status.Phase, translateTimestamp(item.CreationTimestamp)); err != nil { return err } - _, err := fmt.Fprint(w, appendLabels(item.Labels, options.ColumnLabels)) + if _, err := fmt.Fprint(w, appendLabels(item.Labels, options.ColumnLabels)); err != nil { + return err + } + _, err := fmt.Fprint(w, appendAllLabels(options.ShowLabels, item.Labels)) return err } @@ -1046,7 +1077,10 @@ func printSecret(item *api.Secret, w io.Writer, options PrintOptions) error { if _, err := fmt.Fprintf(w, "%s\t%s\t%v\t%s", name, item.Type, len(item.Data), translateTimestamp(item.CreationTimestamp)); err != nil { return err } - _, err := fmt.Fprint(w, appendLabels(item.Labels, options.ColumnLabels)) + if _, err := fmt.Fprint(w, appendLabels(item.Labels, options.ColumnLabels)); err != nil { + return err + } + _, err := fmt.Fprint(w, appendAllLabels(options.ShowLabels, item.Labels)) return err } @@ -1072,7 +1106,10 @@ func printServiceAccount(item *api.ServiceAccount, w io.Writer, options PrintOpt if _, err := fmt.Fprintf(w, "%s\t%d\t%s", name, len(item.Secrets), translateTimestamp(item.CreationTimestamp)); err != nil { return err } - _, err := fmt.Fprint(w, appendLabels(item.Labels, options.ColumnLabels)) + if _, err := fmt.Fprint(w, appendLabels(item.Labels, options.ColumnLabels)); err != nil { + return err + } + _, err := fmt.Fprint(w, appendAllLabels(options.ShowLabels, item.Labels)) return err } @@ -1113,10 +1150,14 @@ func printNode(node *api.Node, w io.Writer, options PrintOptions) error { status = append(status, "SchedulingDisabled") } - if _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s", node.Name, labels.FormatLabels(node.Labels), strings.Join(status, ","), translateTimestamp(node.CreationTimestamp)); err != nil { + if _, err := fmt.Fprintf(w, "%s\t%s\t%s", node.Name, strings.Join(status, ","), translateTimestamp(node.CreationTimestamp)); err != nil { return err } - _, err := fmt.Fprint(w, appendLabels(node.Labels, options.ColumnLabels)) + // Display caller specify column labels first. + if _, err := fmt.Fprint(w, appendLabels(node.Labels, options.ColumnLabels)); err != nil { + return err + } + _, err := fmt.Fprint(w, appendAllLabels(options.ShowLabels, node.Labels)) return err } @@ -1147,9 +1188,8 @@ func printPersistentVolume(pv *api.PersistentVolume, w io.Writer, options PrintO aQty := pv.Spec.Capacity[api.ResourceStorage] aSize := aQty.String() - if _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s", + if _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s", name, - labels.FormatLabels(pv.Labels), aSize, modesStr, pv.Status.Phase, claimRefUID, @@ -1158,7 +1198,10 @@ func printPersistentVolume(pv *api.PersistentVolume, w io.Writer, options PrintO ); err != nil { return err } - _, err := fmt.Fprint(w, appendLabels(pv.Labels, options.ColumnLabels)) + if _, err := fmt.Fprint(w, appendLabels(pv.Labels, options.ColumnLabels)); err != nil { + return err + } + _, err := fmt.Fprint(w, appendAllLabels(options.ShowLabels, pv.Labels)) return err } @@ -1181,7 +1224,6 @@ func printPersistentVolumeClaim(pvc *api.PersistentVolumeClaim, w io.Writer, opt } } - labels := labels.FormatLabels(pvc.Labels) phase := pvc.Status.Phase storage := pvc.Spec.Resources.Requests[api.ResourceStorage] capacity := "" @@ -1192,10 +1234,13 @@ func printPersistentVolumeClaim(pvc *api.PersistentVolumeClaim, w io.Writer, opt capacity = storage.String() } - if _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s", name, labels, phase, pvc.Spec.VolumeName, capacity, accessModes, translateTimestamp(pvc.CreationTimestamp)); err != nil { + if _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s", name, phase, pvc.Spec.VolumeName, capacity, accessModes, translateTimestamp(pvc.CreationTimestamp)); err != nil { return err } - _, err := fmt.Fprint(w, appendLabels(pvc.Labels, options.ColumnLabels)) + if _, err := fmt.Fprint(w, appendLabels(pvc.Labels, options.ColumnLabels)); err != nil { + return err + } + _, err := fmt.Fprint(w, appendAllLabels(options.ShowLabels, pvc.Labels)) return err } @@ -1241,7 +1286,10 @@ func printEvent(event *api.Event, w io.Writer, options PrintOptions) error { ); err != nil { return err } - _, err := fmt.Fprint(w, appendLabels(event.Labels, options.ColumnLabels)) + if _, err := fmt.Fprint(w, appendLabels(event.Labels, options.ColumnLabels)); err != nil { + return err + } + _, err := fmt.Fprint(w, appendAllLabels(options.ShowLabels, event.Labels)) return err } @@ -1273,7 +1321,10 @@ func printLimitRange(limitRange *api.LimitRange, w io.Writer, options PrintOptio ); err != nil { return err } - _, err := fmt.Fprint(w, appendLabels(limitRange.Labels, options.ColumnLabels)) + if _, err := fmt.Fprint(w, appendLabels(limitRange.Labels, options.ColumnLabels)); err != nil { + return err + } + _, err := fmt.Fprint(w, appendAllLabels(options.ShowLabels, limitRange.Labels)) return err } @@ -1304,7 +1355,10 @@ func printResourceQuota(resourceQuota *api.ResourceQuota, w io.Writer, options P ); err != nil { return err } - _, err := fmt.Fprint(w, appendLabels(resourceQuota.Labels, options.ColumnLabels)) + if _, err := fmt.Fprint(w, appendLabels(resourceQuota.Labels, options.ColumnLabels)); err != nil { + return err + } + _, err := fmt.Fprint(w, appendAllLabels(options.ShowLabels, resourceQuota.Labels)) return err } @@ -1341,7 +1395,10 @@ func printComponentStatus(item *api.ComponentStatus, w io.Writer, options PrintO if _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s", item.Name, status, message, error); err != nil { return err } - _, err := fmt.Fprint(w, appendLabels(item.Labels, options.ColumnLabels)) + if _, err := fmt.Fprint(w, appendLabels(item.Labels, options.ColumnLabels)); err != nil { + return err + } + _, err := fmt.Fprint(w, appendAllLabels(options.ShowLabels, item.Labels)) return err } @@ -1393,7 +1450,10 @@ func printDeployment(deployment *extensions.Deployment, w io.Writer, options Pri if _, err := fmt.Fprintf(w, "%s\t%d\t%d\t%d\t%d\t%s", deployment.Name, desiredReplicas, currentReplicas, updatedReplicas, availableReplicas, age); err != nil { return err } - _, err := fmt.Fprint(w, appendLabels(deployment.Labels, options.ColumnLabels)) + if _, err := fmt.Fprint(w, appendLabels(deployment.Labels, options.ColumnLabels)); err != nil { + return err + } + _, err := fmt.Fprint(w, appendAllLabels(options.ShowLabels, deployment.Labels)) return err } @@ -1443,7 +1503,10 @@ func printHorizontalPodAutoscaler(hpa *extensions.HorizontalPodAutoscaler, w io. ); err != nil { return err } - _, err := fmt.Fprint(w, appendLabels(hpa.Labels, options.ColumnLabels)) + if _, err := fmt.Fprint(w, appendLabels(hpa.Labels, options.ColumnLabels)); err != nil { + return err + } + _, err := fmt.Fprint(w, appendAllLabels(options.ShowLabels, hpa.Labels)) return err } @@ -1468,7 +1531,10 @@ func printConfigMap(configMap *api.ConfigMap, w io.Writer, options PrintOptions) if _, err := fmt.Fprintf(w, "%s\t%v\t%s", name, len(configMap.Data), translateTimestamp(configMap.CreationTimestamp)); err != nil { return err } - _, err := fmt.Fprint(w, appendLabels(configMap.Labels, options.ColumnLabels)) + if _, err := fmt.Fprint(w, appendLabels(configMap.Labels, options.ColumnLabels)); err != nil { + return err + } + _, err := fmt.Fprint(w, appendAllLabels(options.ShowLabels, configMap.Labels)) return err } @@ -1509,6 +1575,19 @@ func appendLabels(itemLabels map[string]string, columnLabels []string) string { buffer.WriteString("") } } + + return buffer.String() +} + +// Append all labels to a single column. We need this even when show-labels flag* is +// false, since this adds newline delimiter to the end of each row. +func appendAllLabels(showLabels bool, itemLabels map[string]string) string { + var buffer bytes.Buffer + + if showLabels { + buffer.WriteString(fmt.Sprint("\t")) + buffer.WriteString(labels.FormatLabels(itemLabels)) + } buffer.WriteString("\n") return buffer.String() @@ -1549,6 +1628,16 @@ func formatWideHeaders(wide bool, t reflect.Type) []string { return nil } +// headers for --show-labels=true +func formatShowLabelsHeader(showLabels bool, t reflect.Type) []string { + if showLabels { + if t.String() != "*api.ThirdPartyResource" && t.String() != "*api.ThirdPartyResourceList" { + return []string{"LABELS"} + } + } + return nil +} + // GetNewTabWriter returns a tabwriter that translates tabbed columns in input into properly aligned text. func GetNewTabWriter(output io.Writer) *tabwriter.Writer { return tabwriter.NewWriter(output, tabwriterMinWidth, tabwriterWidth, tabwriterPadding, tabwriterPadChar, tabwriterFlags) @@ -1567,6 +1656,8 @@ func (h *HumanReadablePrinter) PrintObj(obj runtime.Object, output io.Writer) er if !h.options.NoHeaders && t != h.lastType { headers := append(handler.columns, formatWideHeaders(h.options.Wide, t)...) headers = append(headers, formatLabelHeaders(h.options.ColumnLabels)...) + // LABELS is always the last column. + headers = append(headers, formatShowLabelsHeader(h.options.ShowLabels, t)...) if h.options.WithNamespace { headers = append(withNamespacePrefixColumns, headers...) } diff --git a/pkg/kubectl/resource_printer_test.go b/pkg/kubectl/resource_printer_test.go index dfb2da28a5..9e17063c39 100644 --- a/pkg/kubectl/resource_printer_test.go +++ b/pkg/kubectl/resource_printer_test.go @@ -224,7 +224,7 @@ func ErrorPrintHandler(obj *TestPrintType, w io.Writer, options PrintOptions) er func TestCustomTypePrinting(t *testing.T) { columns := []string{"Data"} - printer := NewHumanReadablePrinter(false, false, false, false, false, []string{}) + printer := NewHumanReadablePrinter(false, false, false, false, false, false, []string{}) printer.Handler(columns, PrintCustomType) obj := TestPrintType{"test object"} @@ -241,7 +241,7 @@ func TestCustomTypePrinting(t *testing.T) { func TestPrintHandlerError(t *testing.T) { columns := []string{"Data"} - printer := NewHumanReadablePrinter(false, false, false, false, false, []string{}) + printer := NewHumanReadablePrinter(false, false, false, false, false, false, []string{}) printer.Handler(columns, ErrorPrintHandler) obj := TestPrintType{"test object"} buffer := &bytes.Buffer{} @@ -252,7 +252,7 @@ func TestPrintHandlerError(t *testing.T) { } func TestUnknownTypePrinting(t *testing.T) { - printer := NewHumanReadablePrinter(false, false, false, false, false, []string{}) + printer := NewHumanReadablePrinter(false, false, false, false, false, false, []string{}) buffer := &bytes.Buffer{} err := printer.PrintObj(&TestUnknownType{}, buffer) if err == nil { @@ -456,8 +456,8 @@ func TestPrinters(t *testing.T) { t.Fatal(err) } printers := map[string]ResourcePrinter{ - "humanReadable": NewHumanReadablePrinter(true, false, false, false, false, []string{}), - "humanReadableHeaders": NewHumanReadablePrinter(false, false, false, false, false, []string{}), + "humanReadable": NewHumanReadablePrinter(true, false, false, false, false, false, []string{}), + "humanReadableHeaders": NewHumanReadablePrinter(false, false, false, false, false, false, []string{}), "json": &JSONPrinter{}, "yaml": &YAMLPrinter{}, "template": templatePrinter, @@ -500,7 +500,7 @@ func TestPrinters(t *testing.T) { func TestPrintEventsResultSorted(t *testing.T) { // Arrange - printer := NewHumanReadablePrinter(false /* noHeaders */, false, false, false, false, []string{}) + printer := NewHumanReadablePrinter(false /* noHeaders */, false, false, false, false, false, []string{}) obj := api.EventList{ Items: []api.Event{ @@ -544,7 +544,7 @@ func TestPrintEventsResultSorted(t *testing.T) { } func TestPrintNodeStatus(t *testing.T) { - printer := NewHumanReadablePrinter(false, false, false, false, false, []string{}) + printer := NewHumanReadablePrinter(false, false, false, false, false, false, []string{}) table := []struct { node api.Node status string @@ -755,7 +755,7 @@ func TestPrintHumanReadableService(t *testing.T) { for _, svc := range tests { buff := bytes.Buffer{} - printService(&svc, &buff, PrintOptions{false, false, false, false, false, []string{}}) + printService(&svc, &buff, PrintOptions{false, false, false, false, false, false, []string{}}) output := string(buff.Bytes()) ip := svc.Spec.ClusterIP if !strings.Contains(output, ip) { @@ -937,7 +937,7 @@ func TestPrintHumanReadableWithNamespace(t *testing.T) { for _, test := range table { if test.isNamespaced { // Expect output to include namespace when requested. - printer := NewHumanReadablePrinter(false, true, false, false, false, []string{}) + printer := NewHumanReadablePrinter(false, true, false, false, false, false, []string{}) buffer := &bytes.Buffer{} err := printer.PrintObj(test.obj, buffer) if err != nil { @@ -949,7 +949,7 @@ func TestPrintHumanReadableWithNamespace(t *testing.T) { } } else { // Expect error when trying to get all namespaces for un-namespaced object. - printer := NewHumanReadablePrinter(false, true, false, false, false, []string{}) + printer := NewHumanReadablePrinter(false, true, false, false, false, false, []string{}) buffer := &bytes.Buffer{} err := printer.PrintObj(test.obj, buffer) if err == nil { @@ -1044,7 +1044,7 @@ func TestPrintPod(t *testing.T) { buf := bytes.NewBuffer([]byte{}) for _, test := range tests { - printPod(&test.pod, buf, PrintOptions{false, false, false, true, false, []string{}}) + printPod(&test.pod, buf, PrintOptions{false, false, false, true, false, false, []string{}}) // We ignore time if !strings.HasPrefix(buf.String(), test.expect) { t.Fatalf("Expected: %s, got: %s", test.expect, buf.String()) @@ -1137,7 +1137,7 @@ func TestPrintNonTerminatedPod(t *testing.T) { buf := bytes.NewBuffer([]byte{}) for _, test := range tests { - printPod(&test.pod, buf, PrintOptions{false, false, false, false, false, []string{}}) + printPod(&test.pod, buf, PrintOptions{false, false, false, false, false, false, []string{}}) // We ignore time if !strings.HasPrefix(buf.String(), test.expect) { t.Fatalf("Expected: %s, got: %s", test.expect, buf.String()) @@ -1197,7 +1197,7 @@ func TestPrintPodWithLabels(t *testing.T) { buf := bytes.NewBuffer([]byte{}) for _, test := range tests { - printPod(&test.pod, buf, PrintOptions{false, false, false, false, false, test.labelColumns}) + printPod(&test.pod, buf, PrintOptions{false, false, false, false, false, false, test.labelColumns}) // We ignore time if !strings.HasPrefix(buf.String(), test.startsWith) || !strings.HasSuffix(buf.String(), test.endsWith) { t.Fatalf("Expected to start with: %s and end with: %s, but got: %s", test.startsWith, test.endsWith, buf.String()) @@ -1261,10 +1261,70 @@ func TestPrintDeployment(t *testing.T) { buf := bytes.NewBuffer([]byte{}) for _, test := range tests { - printDeployment(&test.deployment, buf, PrintOptions{false, false, false, true, false, []string{}}) + printDeployment(&test.deployment, buf, PrintOptions{false, false, false, true, false, false, []string{}}) if buf.String() != test.expect { t.Fatalf("Expected: %s, got: %s", test.expect, buf.String()) } buf.Reset() } } + +func TestPrintPodShowLabels(t *testing.T) { + tests := []struct { + pod api.Pod + startsWith string + endsWith string + showLabels bool + }{ + { + // Test name, num of containers, restarts, container ready status + api.Pod{ + ObjectMeta: api.ObjectMeta{ + Name: "test1", + Labels: map[string]string{"col1": "asd", "COL2": "zxc"}, + }, + Spec: api.PodSpec{Containers: make([]api.Container, 2)}, + Status: api.PodStatus{ + Phase: "podPhase", + ContainerStatuses: []api.ContainerStatus{ + {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}}, + {RestartCount: 3}, + }, + }, + }, + "test1\t1/2\tpodPhase\t6\t", + "\tCOL2=zxc,col1=asd\n", + true, + }, + { + // Test name, num of containers, restarts, container ready status + api.Pod{ + ObjectMeta: api.ObjectMeta{ + Name: "test1", + Labels: map[string]string{"col3": "asd", "COL4": "zxc"}, + }, + Spec: api.PodSpec{Containers: make([]api.Container, 2)}, + Status: api.PodStatus{ + Phase: "podPhase", + ContainerStatuses: []api.ContainerStatus{ + {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}}, + {RestartCount: 3}, + }, + }, + }, + "test1\t1/2\tpodPhase\t6\t", + "\n", + false, + }, + } + + buf := bytes.NewBuffer([]byte{}) + for _, test := range tests { + printPod(&test.pod, buf, PrintOptions{false, false, false, false, test.showLabels, false, []string{}}) + // We ignore time + if !strings.HasPrefix(buf.String(), test.startsWith) || !strings.HasSuffix(buf.String(), test.endsWith) { + t.Fatalf("Expected to start with: %s and end with: %s, but got: %s", test.startsWith, test.endsWith, buf.String()) + } + buf.Reset() + } +}