From 44a7a52a946fd349ef0f1dc4d42af831fde97b55 Mon Sep 17 00:00:00 2001 From: Janet Kuo Date: Fri, 31 Jul 2015 16:42:34 -0700 Subject: [PATCH] Hide failed/succeeded pods in 'kubectl get pods' by default --- contrib/completions/bash/kubectl | 14 ++ docs/man/man1/kubectl-annotate.1 | 4 + docs/man/man1/kubectl-config-view.1 | 4 + docs/man/man1/kubectl-expose.1 | 4 + docs/man/man1/kubectl-get.1 | 4 + docs/man/man1/kubectl-label.1 | 4 + docs/man/man1/kubectl-rolling-update.1 | 4 + docs/man/man1/kubectl-run.1 | 4 + docs/user-guide/kubectl/kubectl_annotate.md | 3 +- .../user-guide/kubectl/kubectl_config_view.md | 3 +- docs/user-guide/kubectl/kubectl_expose.md | 3 +- docs/user-guide/kubectl/kubectl_get.md | 5 +- docs/user-guide/kubectl/kubectl_label.md | 3 +- .../kubectl/kubectl_rolling-update.md | 3 +- docs/user-guide/kubectl/kubectl_run.md | 3 +- hack/verify-flags/known-flags.txt | 1 + pkg/client/unversioned/request.go | 4 +- pkg/kubectl/cmd/cmd_test.go | 144 +++++++++++++++++- pkg/kubectl/cmd/delete.go | 2 +- pkg/kubectl/cmd/get.go | 4 +- pkg/kubectl/cmd/get_test.go | 27 ++++ pkg/kubectl/cmd/util/factory.go | 8 +- pkg/kubectl/cmd/util/printing.go | 1 + pkg/kubectl/resource/helper.go | 4 +- pkg/kubectl/resource/selector.go | 3 +- pkg/kubectl/resource_printer.go | 110 ++++++------- pkg/kubectl/resource_printer_test.go | 121 +++++++++++++-- 27 files changed, 402 insertions(+), 92 deletions(-) diff --git a/contrib/completions/bash/kubectl b/contrib/completions/bash/kubectl index 3282cdaec86..a598f40d5c0 100644 --- a/contrib/completions/bash/kubectl +++ b/contrib/completions/bash/kubectl @@ -246,6 +246,8 @@ _kubectl_get() flags+=("--output-version=") flags+=("--selector=") two_word_flags+=("-l") + flags+=("--show-all") + flags+=("-a") flags+=("--sort-by=") flags+=("--template=") two_word_flags+=("-t") @@ -511,6 +513,8 @@ _kubectl_rolling-update() flags+=("--output-version=") flags+=("--poll-interval=") flags+=("--rollback") + flags+=("--show-all") + flags+=("-a") flags+=("--sort-by=") flags+=("--template=") two_word_flags+=("-t") @@ -675,6 +679,8 @@ _kubectl_run() flags+=("--replicas=") two_word_flags+=("-r") flags+=("--restart=") + flags+=("--show-all") + flags+=("-a") flags+=("--sort-by=") flags+=("--stdin") flags+=("-i") @@ -753,6 +759,8 @@ _kubectl_expose() flags+=("--public-ip=") flags+=("--selector=") flags+=("--session-affinity=") + flags+=("--show-all") + flags+=("-a") flags+=("--sort-by=") flags+=("--target-port=") flags+=("--template=") @@ -791,6 +799,8 @@ _kubectl_label() flags+=("--resource-version=") flags+=("--selector=") two_word_flags+=("-l") + flags+=("--show-all") + flags+=("-a") flags+=("--sort-by=") flags+=("--template=") two_word_flags+=("-t") @@ -818,6 +828,8 @@ _kubectl_annotate() flags+=("--output-version=") flags+=("--overwrite") flags+=("--resource-version=") + flags+=("--show-all") + flags+=("-a") flags+=("--sort-by=") flags+=("--template=") two_word_flags+=("-t") @@ -846,6 +858,8 @@ _kubectl_config_view() two_word_flags+=("-o") flags+=("--output-version=") flags+=("--raw") + flags+=("--show-all") + flags+=("-a") 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 e8de6dae75b..252e91655f1 100644 --- a/docs/man/man1/kubectl-annotate.1 +++ b/docs/man/man1/kubectl-annotate.1 @@ -57,6 +57,10 @@ resourcequotas (quota) or secrets. \fB\-\-resource\-version\fP="" 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. +.PP +\fB\-a\fP, \fB\-\-show\-all\fP=false + When printing, show all resources (default hide terminated pods.) + .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 2b4df879ecf..603cd5ea111 100644 --- a/docs/man/man1/kubectl-config-view.1 +++ b/docs/man/man1/kubectl-config-view.1 @@ -52,6 +52,10 @@ You can use \-\-output=template \-\-template=TEMPLATE to extract specific values \fB\-\-raw\fP=false display raw byte data +.PP +\fB\-a\fP, \fB\-\-show\-all\fP=false + When printing, show all resources (default hide terminated pods.) + .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 70bde00e32a..c29f68c13ab 100644 --- a/docs/man/man1/kubectl-expose.1 +++ b/docs/man/man1/kubectl-expose.1 @@ -90,6 +90,10 @@ re\-use the labels from the resource it exposes. \fB\-\-session\-affinity\fP="" If non\-empty, set the session affinity for the service to this; legal values: 'None', 'ClientIP' +.PP +\fB\-a\fP, \fB\-\-show\-all\fP=false + When printing, show all resources (default hide terminated pods.) + .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 4141453b47f..88e5049138a 100644 --- a/docs/man/man1/kubectl-get.1 +++ b/docs/man/man1/kubectl-get.1 @@ -59,6 +59,10 @@ of the \-\-template flag, you can filter the attributes of the fetched resource( \fB\-l\fP, \fB\-\-selector\fP="" Selector (label query) to filter on +.PP +\fB\-a\fP, \fB\-\-show\-all\fP=false + When printing, show all resources (default hide terminated pods.) + .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 b321f408162..6193052d95d 100644 --- a/docs/man/man1/kubectl-label.1 +++ b/docs/man/man1/kubectl-label.1 @@ -58,6 +58,10 @@ If \-\-resource\-version is specified, then updates will use this resource versi \fB\-l\fP, \fB\-\-selector\fP="" Selector (label query) to filter on +.PP +\fB\-a\fP, \fB\-\-show\-all\fP=false + When printing, show all resources (default hide terminated pods.) + .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 c569d587e36..5d36dabd08d 100644 --- a/docs/man/man1/kubectl-rolling-update.1 +++ b/docs/man/man1/kubectl-rolling-update.1 @@ -62,6 +62,10 @@ existing replication controller and overwrite at least one (common) label in its \fB\-\-rollback\fP=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 +.PP +\fB\-a\fP, \fB\-\-show\-all\fP=false + When printing, show all resources (default hide terminated pods.) + .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 eec61448c75..af56c147cc0 100644 --- a/docs/man/man1/kubectl-run.1 +++ b/docs/man/man1/kubectl-run.1 @@ -78,6 +78,10 @@ Creates a replication controller to manage the created container(s). \fB\-\-restart\fP="Always" The restart policy for this Pod. Legal values [Always, OnFailure, Never]. If set to 'Always' a replication controller is created for this pod, if set to OnFailure or Never, only the Pod is created and \-\-replicas must be 1. Default 'Always' +.PP +\fB\-a\fP, \fB\-\-show\-all\fP=false + When printing, show all resources (default hide terminated pods.) + .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 0a6ed58098d..ce68bf7494f 100644 --- a/docs/user-guide/kubectl/kubectl_annotate.md +++ b/docs/user-guide/kubectl/kubectl_annotate.md @@ -85,6 +85,7 @@ $ kubectl annotate pods foo description- --output-version="": Output the formatted object with the given version (default api-version). --overwrite[=false]: If true, allow annotations to be overwritten, otherwise reject annotation updates that overwrite existing annotations. --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. + -a, --show-all[=false]: When printing, show all resources (default hide terminated pods.) --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. -t, --template="": Template string or path to template file to use when -o=template or -o=templatefile. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview] ``` @@ -122,7 +123,7 @@ $ kubectl annotate pods foo description- * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra at 2015-08-10 20:35:06.431305933 +0000 UTC +###### Auto generated by spf13/cobra at 2015-08-18 17:36:23.97715989 +0000 UTC [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_annotate.md?pixel)]() diff --git a/docs/user-guide/kubectl/kubectl_config_view.md b/docs/user-guide/kubectl/kubectl_config_view.md index a4d8f2eae20..cd4ae1f1ab4 100644 --- a/docs/user-guide/kubectl/kubectl_config_view.md +++ b/docs/user-guide/kubectl/kubectl_config_view.md @@ -67,6 +67,7 @@ $ kubectl config view -o template --template='{{range .users}}{{ if eq .name "e2 -o, --output="": Output format. One of: json|yaml|template|templatefile|wide. --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.) --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. -t, --template="": Template string or path to template file to use when -o=template or -o=templatefile. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview] ``` @@ -104,7 +105,7 @@ $ kubectl config view -o template --template='{{range .users}}{{ if eq .name "e2 * [kubectl config](kubectl_config.md) - config modifies kubeconfig files -###### Auto generated by spf13/cobra at 2015-08-12 23:41:01.310054033 +0000 UTC +###### Auto generated by spf13/cobra at 2015-08-18 17:36:23.977277472 +0000 UTC [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_config_view.md?pixel)]() diff --git a/docs/user-guide/kubectl/kubectl_expose.md b/docs/user-guide/kubectl/kubectl_expose.md index b4874abbe34..41482e22d85 100644 --- a/docs/user-guide/kubectl/kubectl_expose.md +++ b/docs/user-guide/kubectl/kubectl_expose.md @@ -84,6 +84,7 @@ $ kubectl expose rc streamer --port=4100 --protocol=udp --name=video-stream --public-ip="": Name of a public IP address to set for the service. The service will be assigned this IP in addition to its generated service IP. --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.) --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. -t, --template="": Template string or path to template file to use when -o=template or -o=templatefile. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview] @@ -123,7 +124,7 @@ $ kubectl expose rc streamer --port=4100 --protocol=udp --name=video-stream * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra at 2015-08-12 23:41:01.308576759 +0000 UTC +###### Auto generated by spf13/cobra at 2015-08-18 17:36:23.976918949 +0000 UTC [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_expose.md?pixel)]() diff --git a/docs/user-guide/kubectl/kubectl_get.md b/docs/user-guide/kubectl/kubectl_get.md index 9aa21c5794f..440ad8fa419 100644 --- a/docs/user-guide/kubectl/kubectl_get.md +++ b/docs/user-guide/kubectl/kubectl_get.md @@ -49,7 +49,7 @@ By specifying the output as 'template' and providing a Go template as the value of the --template flag, you can filter the attributes of the fetched resource(s). ``` -kubectl get [(-o|--output=)json|yaml|template|wide|...] (TYPE [(NAME | -l label] | TYPE/NAME ...) +kubectl get [(-o|--output=)json|yaml|template|wide|...] (TYPE [(NAME | -l label] | TYPE/NAME ...) [flags] ``` ### Examples @@ -91,6 +91,7 @@ $ kubectl get rc/web service/frontend pods/web-pod-13je7 -o, --output="": Output format. One of: json|yaml|template|templatefile|wide. --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.) --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. -t, --template="": Template string or path to template file to use when -o=template or -o=templatefile. 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. @@ -130,7 +131,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 at 2015-08-13 06:12:05.386038784 +0000 UTC +###### Auto generated by spf13/cobra at 2015-08-18 17:36:23.974605429 +0000 UTC [![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 ed588928175..320a6ef783b 100644 --- a/docs/user-guide/kubectl/kubectl_label.md +++ b/docs/user-guide/kubectl/kubectl_label.md @@ -83,6 +83,7 @@ $ kubectl label pods foo bar- --overwrite[=false]: If true, allow labels to be overwritten, otherwise reject label updates that overwrite existing labels. --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.) --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. -t, --template="": Template string or path to template file to use when -o=template or -o=templatefile. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview] ``` @@ -120,7 +121,7 @@ $ kubectl label pods foo bar- * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra at 2015-08-13 02:21:16.349210188 +0000 UTC +###### Auto generated by spf13/cobra at 2015-08-18 17:36:23.977042997 +0000 UTC [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_label.md?pixel)]() diff --git a/docs/user-guide/kubectl/kubectl_rolling-update.md b/docs/user-guide/kubectl/kubectl_rolling-update.md index df6a72c7af2..0580645cb8a 100644 --- a/docs/user-guide/kubectl/kubectl_rolling-update.md +++ b/docs/user-guide/kubectl/kubectl_rolling-update.md @@ -79,6 +79,7 @@ $ kubectl rolling-update frontend --image=image:v2 --output-version="": Output the formatted object with the given version (default api-version). --poll-interval=3s: Time delay between polling for replication controller status after the update. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". --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 + -a, --show-all[=false]: When printing, show all resources (default hide terminated pods.) --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. -t, --template="": Template string or path to template file to use when -o=template or -o=templatefile. 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". @@ -118,7 +119,7 @@ $ kubectl rolling-update frontend --image=image:v2 * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra at 2015-08-12 23:41:01.305486289 +0000 UTC +###### Auto generated by spf13/cobra at 2015-08-18 17:36:23.97558907 +0000 UTC [![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 796c0496d57..17487cafa13 100644 --- a/docs/user-guide/kubectl/kubectl_run.md +++ b/docs/user-guide/kubectl/kubectl_run.md @@ -88,6 +88,7 @@ $ kubectl run nginx --image=nginx --command -- ... --port=-1: The port that this container exposes. -r, --replicas=1: Number of replicas to create for this container. Default is 1. --restart="Always": The restart policy for this Pod. Legal values [Always, OnFailure, Never]. If set to 'Always' a replication controller is created for this pod, if set to OnFailure or Never, only the Pod is created and --replicas must be 1. Default 'Always' + -a, --show-all[=false]: When printing, show all resources (default hide terminated pods.) --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. -t, --template="": Template string or path to template file to use when -o=template or -o=templatefile. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview] @@ -127,7 +128,7 @@ $ kubectl run nginx --image=nginx --command -- ... * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra at 2015-08-13 16:41:44.465440991 +0000 UTC +###### Auto generated by spf13/cobra at 2015-08-18 17:36:23.976238275 +0000 UTC [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_run.md?pixel)]() diff --git a/hack/verify-flags/known-flags.txt b/hack/verify-flags/known-flags.txt index c6e44676474..abf758756e9 100644 --- a/hack/verify-flags/known-flags.txt +++ b/hack/verify-flags/known-flags.txt @@ -213,6 +213,7 @@ service-node-port-range service-node-ports service-sync-period session-affinity +show-all shutdown-fd shutdown-fifo skip-munges diff --git a/pkg/client/unversioned/request.go b/pkg/client/unversioned/request.go index 82e39aff561..2df001cdbaf 100644 --- a/pkg/client/unversioned/request.go +++ b/pkg/client/unversioned/request.go @@ -261,6 +261,7 @@ const ( NodeUnschedulable = "spec.unschedulable" ObjectNameField = "metadata.name" PodHost = "spec.nodeName" + PodStatus = "status.phase" SecretType = "type" EventReason = "reason" @@ -318,7 +319,8 @@ var fieldMappings = versionToResourceToFieldMapping{ NodeUnschedulable: "spec.unschedulable", }, "pods": clientFieldNameToAPIVersionFieldName{ - PodHost: "spec.nodeName", + PodHost: "spec.nodeName", + PodStatus: "status.phase", }, "secrets": clientFieldNameToAPIVersionFieldName{ SecretType: "type", diff --git a/pkg/kubectl/cmd/cmd_test.go b/pkg/kubectl/cmd/cmd_test.go index ce66163270f..70cffd78a2b 100644 --- a/pkg/kubectl/cmd/cmd_test.go +++ b/pkg/kubectl/cmd/cmd_test.go @@ -152,7 +152,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, columnLabels []string) (kubectl.ResourcePrinter, error) { + Printer: func(mapping *meta.RESTMapping, noHeaders, withNamespace bool, wide bool, showAll bool, columnLabels []string) (kubectl.ResourcePrinter, error) { return t.Printer, t.Err }, Validator: func() (validation.Schema, error) { @@ -207,7 +207,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, columnLabels []string) (kubectl.ResourcePrinter, error) { + Printer: func(mapping *meta.RESTMapping, noHeaders, withNamespace bool, wide bool, showAll bool, columnLabels []string) (kubectl.ResourcePrinter, error) { return t.Printer, t.Err }, Validator: func() (validation.Schema, error) { @@ -256,7 +256,7 @@ func stringBody(body string) io.ReadCloser { func ExamplePrintReplicationControllerWithNamespace() { f, tf, codec := NewAPIFactory() - tf.Printer = kubectl.NewHumanReadablePrinter(false, true, false, []string{}) + tf.Printer = kubectl.NewHumanReadablePrinter(false, true, false, false, []string{}) tf.Client = &client.FakeRESTClient{ Codec: codec, Client: nil, @@ -298,7 +298,7 @@ func ExamplePrintReplicationControllerWithNamespace() { func ExamplePrintPodWithWideFormat() { f, tf, codec := NewAPIFactory() - tf.Printer = kubectl.NewHumanReadablePrinter(false, false, true, []string{}) + tf.Printer = kubectl.NewHumanReadablePrinter(false, false, true, false, []string{}) tf.Client = &client.FakeRESTClient{ Codec: codec, Client: nil, @@ -331,9 +331,143 @@ func ExamplePrintPodWithWideFormat() { // test1 1/2 podPhase 6 10y kubernetes-minion-abcd } +func newAllPhasePodList() *api.PodList { + nodeName := "kubernetes-minion-abcd" + return &api.PodList{ + Items: []api.Pod{ + { + ObjectMeta: api.ObjectMeta{ + Name: "test1", + CreationTimestamp: util.Time{time.Now().AddDate(-10, 0, 0)}, + }, + Spec: api.PodSpec{ + Containers: make([]api.Container, 2), + NodeName: nodeName, + }, + Status: api.PodStatus{ + Phase: api.PodPending, + ContainerStatuses: []api.ContainerStatus{ + {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}}, + {RestartCount: 3}, + }, + }, + }, + { + ObjectMeta: api.ObjectMeta{ + Name: "test2", + CreationTimestamp: util.Time{time.Now().AddDate(-10, 0, 0)}, + }, + Spec: api.PodSpec{ + Containers: make([]api.Container, 2), + NodeName: nodeName, + }, + Status: api.PodStatus{ + Phase: api.PodRunning, + ContainerStatuses: []api.ContainerStatus{ + {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}}, + {RestartCount: 3}, + }, + }, + }, + { + ObjectMeta: api.ObjectMeta{ + Name: "test3", + CreationTimestamp: util.Time{time.Now().AddDate(-10, 0, 0)}, + }, + Spec: api.PodSpec{ + Containers: make([]api.Container, 2), + NodeName: nodeName, + }, + Status: api.PodStatus{ + Phase: api.PodSucceeded, + ContainerStatuses: []api.ContainerStatus{ + {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}}, + {RestartCount: 3}, + }, + }, + }, + { + ObjectMeta: api.ObjectMeta{ + Name: "test4", + CreationTimestamp: util.Time{time.Now().AddDate(-10, 0, 0)}, + }, + Spec: api.PodSpec{ + Containers: make([]api.Container, 2), + NodeName: nodeName, + }, + Status: api.PodStatus{ + Phase: api.PodFailed, + ContainerStatuses: []api.ContainerStatus{ + {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}}, + {RestartCount: 3}, + }, + }, + }, + { + ObjectMeta: api.ObjectMeta{ + Name: "test5", + CreationTimestamp: util.Time{time.Now().AddDate(-10, 0, 0)}, + }, + Spec: api.PodSpec{ + Containers: make([]api.Container, 2), + NodeName: nodeName, + }, + Status: api.PodStatus{ + Phase: api.PodUnknown, + ContainerStatuses: []api.ContainerStatus{ + {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}}, + {RestartCount: 3}, + }, + }, + }}, + } +} + +func ExamplePrintPodHideTerminated() { + f, tf, codec := NewAPIFactory() + tf.Printer = kubectl.NewHumanReadablePrinter(false, false, false, false, []string{}) + tf.Client = &client.FakeRESTClient{ + Codec: codec, + Client: nil, + } + cmd := NewCmdRun(f, os.Stdin, os.Stdout, os.Stderr) + podList := newAllPhasePodList() + err := f.PrintObject(cmd, podList, os.Stdout) + if err != nil { + fmt.Printf("Unexpected error: %v", err) + } + // Output: + // NAME READY STATUS RESTARTS AGE + // test1 1/2 Pending 6 10y + // test2 1/2 Running 6 10y + // test5 1/2 Unknown 6 10y +} + +func ExamplePrintPodShowAll() { + f, tf, codec := NewAPIFactory() + tf.Printer = kubectl.NewHumanReadablePrinter(false, false, false, true, []string{}) + tf.Client = &client.FakeRESTClient{ + Codec: codec, + Client: nil, + } + cmd := NewCmdRun(f, os.Stdin, os.Stdout, os.Stderr) + podList := newAllPhasePodList() + err := f.PrintObject(cmd, podList, os.Stdout) + if err != nil { + fmt.Printf("Unexpected error: %v", err) + } + // Output: + // NAME READY STATUS RESTARTS AGE + // test1 1/2 Pending 6 10y + // test2 1/2 Running 6 10y + // test3 1/2 Succeeded 6 10y + // test4 1/2 Failed 6 10y + // test5 1/2 Unknown 6 10y +} + func ExamplePrintServiceWithNamespacesAndLabels() { f, tf, codec := NewAPIFactory() - tf.Printer = kubectl.NewHumanReadablePrinter(false, true, false, []string{"l1"}) + tf.Printer = kubectl.NewHumanReadablePrinter(false, true, false, false, []string{"l1"}) tf.Client = &client.FakeRESTClient{ Codec: codec, Client: nil, diff --git a/pkg/kubectl/cmd/delete.go b/pkg/kubectl/cmd/delete.go index 66304647260..4571939fad5 100644 --- a/pkg/kubectl/cmd/delete.go +++ b/pkg/kubectl/cmd/delete.go @@ -58,7 +58,7 @@ $ kubectl delete pods --all` ) func NewCmdDelete(f *cmdutil.Factory, out io.Writer) *cobra.Command { - p := kubectl.NewHumanReadablePrinter(false, false, false, []string{}) + p := kubectl.NewHumanReadablePrinter(false, false, false, false, []string{}) validArgs := p.HandledResources() cmd := &cobra.Command{ diff --git a/pkg/kubectl/cmd/get.go b/pkg/kubectl/cmd/get.go index 24c20af5d84..7396fc00253 100644 --- a/pkg/kubectl/cmd/get.go +++ b/pkg/kubectl/cmd/get.go @@ -65,11 +65,11 @@ $ kubectl get rc/web service/frontend pods/web-pod-13je7` // NewCmdGet creates a command object for the generic "get" action, which // retrieves one or more resources from a server. func NewCmdGet(f *cmdutil.Factory, out io.Writer) *cobra.Command { - p := kubectl.NewHumanReadablePrinter(false, false, false, []string{}) + p := kubectl.NewHumanReadablePrinter(false, false, false, false, []string{}) validArgs := p.HandledResources() cmd := &cobra.Command{ - Use: "get [(-o|--output=)json|yaml|template|wide|...] (TYPE [(NAME | -l label] | TYPE/NAME ...)", + Use: "get [(-o|--output=)json|yaml|template|wide|...] (TYPE [(NAME | -l label] | TYPE/NAME ...) [flags]", Short: "Display one or many resources", Long: get_long, Example: get_example, diff --git a/pkg/kubectl/cmd/get_test.go b/pkg/kubectl/cmd/get_test.go index 55d58e4cc62..0eaa822128f 100644 --- a/pkg/kubectl/cmd/get_test.go +++ b/pkg/kubectl/cmd/get_test.go @@ -331,6 +331,33 @@ func TestGetListObjects(t *testing.T) { } } +func TestGetAllListObjects(t *testing.T) { + pods, _, _ := testData() + + f, tf, codec := NewAPIFactory() + tf.Printer = &testPrinter{} + tf.Client = &client.FakeRESTClient{ + Codec: codec, + Resp: &http.Response{StatusCode: 200, Body: objBody(codec, pods)}, + } + tf.Namespace = "test" + buf := bytes.NewBuffer([]byte{}) + + cmd := NewCmdGet(f, buf) + cmd.SetOutput(buf) + cmd.Flags().Set("show-all", "true") + cmd.Run(cmd, []string{"pods"}) + + expected := []runtime.Object{pods} + actual := tf.Printer.(*testPrinter).Objects + if !reflect.DeepEqual(expected, actual) { + t.Errorf("unexpected object: %#v %#v", expected, actual) + } + if len(buf.String()) == 0 { + t.Errorf("unexpected empty output") + } +} + func TestGetListComponentStatus(t *testing.T) { statuses := testComponentStatusData() diff --git a/pkg/kubectl/cmd/util/factory.go b/pkg/kubectl/cmd/util/factory.go index fed40ad8273..0f7a2a7e595 100644 --- a/pkg/kubectl/cmd/util/factory.go +++ b/pkg/kubectl/cmd/util/factory.go @@ -67,7 +67,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, columnLabels []string) (kubectl.ResourcePrinter, error) + Printer func(mapping *meta.RESTMapping, noHeaders, withNamespace bool, wide bool, showAll 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. @@ -186,8 +186,8 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { } return nil, fmt.Errorf("no description has been implemented for %q", mapping.Kind) }, - Printer: func(mapping *meta.RESTMapping, noHeaders, withNamespace bool, wide bool, columnLabels []string) (kubectl.ResourcePrinter, error) { - return kubectl.NewHumanReadablePrinter(noHeaders, withNamespace, wide, columnLabels), nil + Printer: func(mapping *meta.RESTMapping, noHeaders, withNamespace bool, wide bool, showAll bool, columnLabels []string) (kubectl.ResourcePrinter, error) { + return kubectl.NewHumanReadablePrinter(noHeaders, withNamespace, wide, showAll, columnLabels), nil }, PodSelectorForObject: func(object runtime.Object) (string, error) { // TODO: replace with a swagger schema based approach (identify pod selector via schema introspection) @@ -467,7 +467,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), columnLabel) + printer, err = f.Printer(mapping, GetFlagBool(cmd, "no-headers"), withNamespace, GetWideFlag(cmd), GetFlagBool(cmd, "show-all"), columnLabel) if err != nil { return nil, err } diff --git a/pkg/kubectl/cmd/util/printing.go b/pkg/kubectl/cmd/util/printing.go index 251e4ae3a7b..9473cd14798 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().Bool("no-headers", false, "When using the default output, don't print headers.") cmd.Flags().StringP("template", "t", "", "Template string or path to template file to use when -o=template or -o=templatefile. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview]") cmd.Flags().String("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.") + cmd.Flags().BoolP("show-all", "a", false, "When printing, show all resources (default hide terminated pods.)") } // AddOutputFlagsForMutation adds output related flags to a command. Used by mutations only. diff --git a/pkg/kubectl/resource/helper.go b/pkg/kubectl/resource/helper.go index 42fa9a9ae11..4af6c8272f4 100644 --- a/pkg/kubectl/resource/helper.go +++ b/pkg/kubectl/resource/helper.go @@ -19,7 +19,6 @@ package resource import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/meta" - "k8s.io/kubernetes/pkg/fields" "k8s.io/kubernetes/pkg/labels" "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/watch" @@ -71,14 +70,13 @@ func (m *Helper) List(namespace, apiVersion string, selector labels.Selector) (r Get() } -func (m *Helper) Watch(namespace, resourceVersion, apiVersion string, labelSelector labels.Selector, fieldSelector fields.Selector) (watch.Interface, error) { +func (m *Helper) Watch(namespace, resourceVersion, apiVersion string, labelSelector labels.Selector) (watch.Interface, error) { return m.RESTClient.Get(). Prefix("watch"). NamespaceIfScoped(namespace, m.NamespaceScoped). Resource(m.Resource). Param("resourceVersion", resourceVersion). LabelsSelectorParam(labelSelector). - FieldsSelectorParam(fieldSelector). Watch() } diff --git a/pkg/kubectl/resource/selector.go b/pkg/kubectl/resource/selector.go index d7861fd2151..bf50070a208 100644 --- a/pkg/kubectl/resource/selector.go +++ b/pkg/kubectl/resource/selector.go @@ -21,7 +21,6 @@ import ( "k8s.io/kubernetes/pkg/api/errors" "k8s.io/kubernetes/pkg/api/meta" - "k8s.io/kubernetes/pkg/fields" "k8s.io/kubernetes/pkg/labels" "k8s.io/kubernetes/pkg/watch" ) @@ -72,7 +71,7 @@ func (r *Selector) Visit(fn VisitorFunc) error { } func (r *Selector) Watch(resourceVersion string) (watch.Interface, error) { - return NewHelper(r.Client, r.Mapping).Watch(r.Namespace, resourceVersion, r.ResourceMapping().APIVersion, r.Selector, fields.Everything()) + return NewHelper(r.Client, r.Mapping).Watch(r.Namespace, resourceVersion, r.ResourceMapping().APIVersion, r.Selector) } // ResourceMapping returns the mapping for this resource and implements ResourceMapping diff --git a/pkg/kubectl/resource_printer.go b/pkg/kubectl/resource_printer.go index 09ae64128c7..ee15a2b0fed 100644 --- a/pkg/kubectl/resource_printer.go +++ b/pkg/kubectl/resource_printer.go @@ -182,17 +182,19 @@ type HumanReadablePrinter struct { noHeaders bool withNamespace bool wide bool + showAll bool columnLabels []string lastType reflect.Type } // NewHumanReadablePrinter creates a HumanReadablePrinter. -func NewHumanReadablePrinter(noHeaders, withNamespace bool, wide bool, columnLabels []string) *HumanReadablePrinter { +func NewHumanReadablePrinter(noHeaders, withNamespace bool, wide bool, showAll bool, columnLabels []string) *HumanReadablePrinter { printer := &HumanReadablePrinter{ handlerMap: make(map[reflect.Type]*handlerEntry), noHeaders: noHeaders, withNamespace: withNamespace, wide: wide, + showAll: showAll, columnLabels: columnLabels, } printer.addDefaultHandlers() @@ -218,22 +220,22 @@ func (h *HumanReadablePrinter) Handler(columns []string, printFunc interface{}) // validatePrintHandlerFunc validates print handler signature. // printFunc is the function that will be called to print an object. // It must be of the following type: -// func printFunc(object ObjectType, w io.Writer, withNamespace bool, wide bool, columnLabels []string) error +// func printFunc(object ObjectType, w io.Writer, withNamespace bool, wide bool, showAll bool, columnLabels []string) error // where ObjectType is the type of the object that will be printed. func (h *HumanReadablePrinter) validatePrintHandlerFunc(printFunc reflect.Value) error { if printFunc.Kind() != reflect.Func { return fmt.Errorf("invalid print handler. %#v is not a function", printFunc) } funcType := printFunc.Type() - if funcType.NumIn() != 5 || funcType.NumOut() != 1 { + if funcType.NumIn() != 6 || funcType.NumOut() != 1 { return fmt.Errorf("invalid print handler." + - "Must accept 5 parameters and return 1 value.") + "Must accept 6 parameters and return 1 value.") } if funcType.In(1) != reflect.TypeOf((*io.Writer)(nil)).Elem() || - funcType.In(4) != reflect.TypeOf((*[]string)(nil)).Elem() || + funcType.In(5) != reflect.TypeOf((*[]string)(nil)).Elem() || funcType.Out(0) != reflect.TypeOf((*error)(nil)).Elem() { return fmt.Errorf("invalid print handler. The expected signature is: "+ - "func handler(obj %v, w io.Writer, withNamespace bool, wide bool, columnLabels []string) error", funcType.In(0)) + "func handler(obj %v, w io.Writer, withNamespace bool, wide bool, showAll bool, columnLabels []string) error", funcType.In(0)) } return nil } @@ -379,7 +381,7 @@ func translateTimestamp(timestamp util.Time) string { return shortHumanDuration(time.Now().Sub(timestamp.Time)) } -func printPod(pod *api.Pod, w io.Writer, withNamespace bool, wide bool, columnLabels []string) error { +func printPod(pod *api.Pod, w io.Writer, withNamespace bool, wide bool, showAll bool, columnLabels []string) error { name := pod.Name namespace := pod.Namespace @@ -388,6 +390,10 @@ func printPod(pod *api.Pod, w io.Writer, withNamespace bool, wide bool, columnLa readyContainers := 0 reason := string(pod.Status.Phase) + // if not printing all pods, skip terminated pods (default) + if !showAll && (reason == string(api.PodSucceeded) || reason == string(api.PodFailed)) { + return nil + } if pod.Status.Reason != "" { reason = pod.Status.Reason } @@ -443,16 +449,16 @@ func printPod(pod *api.Pod, w io.Writer, withNamespace bool, wide bool, columnLa return err } -func printPodList(podList *api.PodList, w io.Writer, withNamespace bool, wide bool, columnLabels []string) error { +func printPodList(podList *api.PodList, w io.Writer, withNamespace bool, wide bool, showAll bool, columnLabels []string) error { for _, pod := range podList.Items { - if err := printPod(&pod, w, withNamespace, wide, columnLabels); err != nil { + if err := printPod(&pod, w, withNamespace, wide, showAll, columnLabels); err != nil { return err } } return nil } -func printPodTemplate(pod *api.PodTemplate, w io.Writer, withNamespace bool, wide bool, columnLabels []string) error { +func printPodTemplate(pod *api.PodTemplate, w io.Writer, withNamespace bool, wide bool, showAll bool, columnLabels []string) error { name := pod.Name namespace := pod.Namespace @@ -496,16 +502,16 @@ func printPodTemplate(pod *api.PodTemplate, w io.Writer, withNamespace bool, wid return nil } -func printPodTemplateList(podList *api.PodTemplateList, w io.Writer, withNamespace bool, wide bool, columnLabels []string) error { +func printPodTemplateList(podList *api.PodTemplateList, w io.Writer, withNamespace bool, wide bool, showAll bool, columnLabels []string) error { for _, pod := range podList.Items { - if err := printPodTemplate(&pod, w, withNamespace, wide, columnLabels); err != nil { + if err := printPodTemplate(&pod, w, withNamespace, wide, showAll, columnLabels); err != nil { return err } } return nil } -func printReplicationController(controller *api.ReplicationController, w io.Writer, withNamespace bool, wide bool, columnLabels []string) error { +func printReplicationController(controller *api.ReplicationController, w io.Writer, withNamespace bool, wide bool, showAll bool, columnLabels []string) error { name := controller.Name namespace := controller.Namespace @@ -551,9 +557,9 @@ func printReplicationController(controller *api.ReplicationController, w io.Writ return nil } -func printReplicationControllerList(list *api.ReplicationControllerList, w io.Writer, withNamespace bool, wide bool, columnLabels []string) error { +func printReplicationControllerList(list *api.ReplicationControllerList, w io.Writer, withNamespace bool, wide bool, showAll bool, columnLabels []string) error { for _, controller := range list.Items { - if err := printReplicationController(&controller, w, withNamespace, wide, columnLabels); err != nil { + if err := printReplicationController(&controller, w, withNamespace, wide, showAll, columnLabels); err != nil { return err } } @@ -588,7 +594,7 @@ func makePortString(ports []api.ServicePort) string { return strings.Join(pieces, ",") } -func printService(svc *api.Service, w io.Writer, withNamespace bool, wide bool, columnLabels []string) error { +func printService(svc *api.Service, w io.Writer, withNamespace bool, wide bool, showAll bool, columnLabels []string) error { name := svc.Name namespace := svc.Namespace @@ -616,16 +622,16 @@ func printService(svc *api.Service, w io.Writer, withNamespace bool, wide bool, return nil } -func printServiceList(list *api.ServiceList, w io.Writer, withNamespace bool, wide bool, columnLabels []string) error { +func printServiceList(list *api.ServiceList, w io.Writer, withNamespace bool, wide bool, showAll bool, columnLabels []string) error { for _, svc := range list.Items { - if err := printService(&svc, w, withNamespace, wide, columnLabels); err != nil { + if err := printService(&svc, w, withNamespace, wide, showAll, columnLabels); err != nil { return err } } return nil } -func printEndpoints(endpoints *api.Endpoints, w io.Writer, withNamespace bool, wide bool, columnLabels []string) error { +func printEndpoints(endpoints *api.Endpoints, w io.Writer, withNamespace bool, wide bool, showAll bool, columnLabels []string) error { name := endpoints.Name namespace := endpoints.Namespace @@ -641,16 +647,16 @@ func printEndpoints(endpoints *api.Endpoints, w io.Writer, withNamespace bool, w return err } -func printEndpointsList(list *api.EndpointsList, w io.Writer, withNamespace bool, wide bool, columnLabels []string) error { +func printEndpointsList(list *api.EndpointsList, w io.Writer, withNamespace bool, wide bool, showAll bool, columnLabels []string) error { for _, item := range list.Items { - if err := printEndpoints(&item, w, withNamespace, wide, columnLabels); err != nil { + if err := printEndpoints(&item, w, withNamespace, wide, showAll, columnLabels); err != nil { return err } } return nil } -func printNamespace(item *api.Namespace, w io.Writer, withNamespace bool, wide bool, columnLabels []string) error { +func printNamespace(item *api.Namespace, w io.Writer, withNamespace bool, wide bool, showAll bool, columnLabels []string) error { if withNamespace { return fmt.Errorf("namespace is not namespaced") } @@ -662,16 +668,16 @@ func printNamespace(item *api.Namespace, w io.Writer, withNamespace bool, wide b return err } -func printNamespaceList(list *api.NamespaceList, w io.Writer, withNamespace bool, wide bool, columnLabels []string) error { +func printNamespaceList(list *api.NamespaceList, w io.Writer, withNamespace bool, wide bool, showAll bool, columnLabels []string) error { for _, item := range list.Items { - if err := printNamespace(&item, w, withNamespace, wide, columnLabels); err != nil { + if err := printNamespace(&item, w, withNamespace, wide, showAll, columnLabels); err != nil { return err } } return nil } -func printSecret(item *api.Secret, w io.Writer, withNamespace bool, wide bool, columnLabels []string) error { +func printSecret(item *api.Secret, w io.Writer, withNamespace bool, wide bool, showAll bool, columnLabels []string) error { name := item.Name namespace := item.Namespace @@ -687,9 +693,9 @@ func printSecret(item *api.Secret, w io.Writer, withNamespace bool, wide bool, c return err } -func printSecretList(list *api.SecretList, w io.Writer, withNamespace bool, wide bool, columnLabels []string) error { +func printSecretList(list *api.SecretList, w io.Writer, withNamespace bool, wide bool, showAll bool, columnLabels []string) error { for _, item := range list.Items { - if err := printSecret(&item, w, withNamespace, wide, columnLabels); err != nil { + if err := printSecret(&item, w, withNamespace, wide, showAll, columnLabels); err != nil { return err } } @@ -697,7 +703,7 @@ func printSecretList(list *api.SecretList, w io.Writer, withNamespace bool, wide return nil } -func printServiceAccount(item *api.ServiceAccount, w io.Writer, withNamespace bool, wide bool, columnLabels []string) error { +func printServiceAccount(item *api.ServiceAccount, w io.Writer, withNamespace bool, wide bool, showAll bool, columnLabels []string) error { name := item.Name namespace := item.Namespace @@ -713,9 +719,9 @@ func printServiceAccount(item *api.ServiceAccount, w io.Writer, withNamespace bo return err } -func printServiceAccountList(list *api.ServiceAccountList, w io.Writer, withNamespace bool, wide bool, columnLabels []string) error { +func printServiceAccountList(list *api.ServiceAccountList, w io.Writer, withNamespace bool, wide bool, showAll bool, columnLabels []string) error { for _, item := range list.Items { - if err := printServiceAccount(&item, w, withNamespace, wide, columnLabels); err != nil { + if err := printServiceAccount(&item, w, withNamespace, wide, showAll, columnLabels); err != nil { return err } } @@ -723,7 +729,7 @@ func printServiceAccountList(list *api.ServiceAccountList, w io.Writer, withName return nil } -func printNode(node *api.Node, w io.Writer, withNamespace bool, wide bool, columnLabels []string) error { +func printNode(node *api.Node, w io.Writer, withNamespace bool, wide bool, showAll bool, columnLabels []string) error { if withNamespace { return fmt.Errorf("node is not namespaced") } @@ -757,16 +763,16 @@ func printNode(node *api.Node, w io.Writer, withNamespace bool, wide bool, colum return err } -func printNodeList(list *api.NodeList, w io.Writer, withNamespace bool, wide bool, columnLabels []string) error { +func printNodeList(list *api.NodeList, w io.Writer, withNamespace bool, wide bool, showAll bool, columnLabels []string) error { for _, node := range list.Items { - if err := printNode(&node, w, withNamespace, wide, columnLabels); err != nil { + if err := printNode(&node, w, withNamespace, wide, showAll, columnLabels); err != nil { return err } } return nil } -func printPersistentVolume(pv *api.PersistentVolume, w io.Writer, withNamespace bool, wide bool, columnLabels []string) error { +func printPersistentVolume(pv *api.PersistentVolume, w io.Writer, withNamespace bool, wide bool, showAll bool, columnLabels []string) error { if withNamespace { return fmt.Errorf("persistentVolume is not namespaced") } @@ -799,16 +805,16 @@ func printPersistentVolume(pv *api.PersistentVolume, w io.Writer, withNamespace return err } -func printPersistentVolumeList(list *api.PersistentVolumeList, w io.Writer, withNamespace bool, wide bool, columnLabels []string) error { +func printPersistentVolumeList(list *api.PersistentVolumeList, w io.Writer, withNamespace bool, wide bool, showAll bool, columnLabels []string) error { for _, pv := range list.Items { - if err := printPersistentVolume(&pv, w, withNamespace, wide, columnLabels); err != nil { + if err := printPersistentVolume(&pv, w, withNamespace, wide, showAll, columnLabels); err != nil { return err } } return nil } -func printPersistentVolumeClaim(pvc *api.PersistentVolumeClaim, w io.Writer, withNamespace bool, wide bool, columnLabels []string) error { +func printPersistentVolumeClaim(pvc *api.PersistentVolumeClaim, w io.Writer, withNamespace bool, wide bool, showAll bool, columnLabels []string) error { name := pvc.Name namespace := pvc.Namespace @@ -836,16 +842,16 @@ func printPersistentVolumeClaim(pvc *api.PersistentVolumeClaim, w io.Writer, wit return err } -func printPersistentVolumeClaimList(list *api.PersistentVolumeClaimList, w io.Writer, withNamespace bool, wide bool, columnLabels []string) error { +func printPersistentVolumeClaimList(list *api.PersistentVolumeClaimList, w io.Writer, withNamespace bool, wide bool, showAll bool, columnLabels []string) error { for _, psd := range list.Items { - if err := printPersistentVolumeClaim(&psd, w, withNamespace, wide, columnLabels); err != nil { + if err := printPersistentVolumeClaim(&psd, w, withNamespace, wide, showAll, columnLabels); err != nil { return err } } return nil } -func printEvent(event *api.Event, w io.Writer, withNamespace bool, wide bool, columnLabels []string) error { +func printEvent(event *api.Event, w io.Writer, withNamespace bool, wide bool, showAll bool, columnLabels []string) error { namespace := event.Namespace if withNamespace { if _, err := fmt.Fprintf(w, "%s\t", namespace); err != nil { @@ -871,17 +877,17 @@ func printEvent(event *api.Event, w io.Writer, withNamespace bool, wide bool, co } // Sorts and prints the EventList in a human-friendly format. -func printEventList(list *api.EventList, w io.Writer, withNamespace bool, wide bool, columnLabels []string) error { +func printEventList(list *api.EventList, w io.Writer, withNamespace bool, wide bool, showAll bool, columnLabels []string) error { sort.Sort(SortableEvents(list.Items)) for i := range list.Items { - if err := printEvent(&list.Items[i], w, withNamespace, wide, columnLabels); err != nil { + if err := printEvent(&list.Items[i], w, withNamespace, wide, showAll, columnLabels); err != nil { return err } } return nil } -func printLimitRange(limitRange *api.LimitRange, w io.Writer, withNamespace bool, wide bool, columnLabels []string) error { +func printLimitRange(limitRange *api.LimitRange, w io.Writer, withNamespace bool, wide bool, showAll bool, columnLabels []string) error { name := limitRange.Name namespace := limitRange.Namespace @@ -903,16 +909,16 @@ func printLimitRange(limitRange *api.LimitRange, w io.Writer, withNamespace bool } // Prints the LimitRangeList in a human-friendly format. -func printLimitRangeList(list *api.LimitRangeList, w io.Writer, withNamespace bool, wide bool, columnLabels []string) error { +func printLimitRangeList(list *api.LimitRangeList, w io.Writer, withNamespace bool, wide bool, showAll bool, columnLabels []string) error { for i := range list.Items { - if err := printLimitRange(&list.Items[i], w, withNamespace, wide, columnLabels); err != nil { + if err := printLimitRange(&list.Items[i], w, withNamespace, wide, showAll, columnLabels); err != nil { return err } } return nil } -func printResourceQuota(resourceQuota *api.ResourceQuota, w io.Writer, withNamespace bool, wide bool, columnLabels []string) error { +func printResourceQuota(resourceQuota *api.ResourceQuota, w io.Writer, withNamespace bool, wide bool, showAll bool, columnLabels []string) error { name := resourceQuota.Name namespace := resourceQuota.Namespace @@ -934,16 +940,16 @@ func printResourceQuota(resourceQuota *api.ResourceQuota, w io.Writer, withNames } // Prints the ResourceQuotaList in a human-friendly format. -func printResourceQuotaList(list *api.ResourceQuotaList, w io.Writer, withNamespace bool, wide bool, columnLabels []string) error { +func printResourceQuotaList(list *api.ResourceQuotaList, w io.Writer, withNamespace bool, wide bool, showAll bool, columnLabels []string) error { for i := range list.Items { - if err := printResourceQuota(&list.Items[i], w, withNamespace, wide, columnLabels); err != nil { + if err := printResourceQuota(&list.Items[i], w, withNamespace, wide, showAll, columnLabels); err != nil { return err } } return nil } -func printComponentStatus(item *api.ComponentStatus, w io.Writer, withNamespace bool, wide bool, columnLabels []string) error { +func printComponentStatus(item *api.ComponentStatus, w io.Writer, withNamespace bool, wide bool, showAll bool, columnLabels []string) error { if withNamespace { return fmt.Errorf("componentStatus is not namespaced") } @@ -970,9 +976,9 @@ func printComponentStatus(item *api.ComponentStatus, w io.Writer, withNamespace return err } -func printComponentStatusList(list *api.ComponentStatusList, w io.Writer, withNamespace bool, wide bool, columnLabels []string) error { +func printComponentStatusList(list *api.ComponentStatusList, w io.Writer, withNamespace bool, wide bool, showAll bool, columnLabels []string) error { for _, item := range list.Items { - if err := printComponentStatus(&item, w, withNamespace, wide, columnLabels); err != nil { + if err := printComponentStatus(&item, w, withNamespace, wide, showAll, columnLabels); err != nil { return err } } @@ -1046,7 +1052,7 @@ func (h *HumanReadablePrinter) PrintObj(obj runtime.Object, output io.Writer) er h.printHeader(headers, w) h.lastType = t } - args := []reflect.Value{reflect.ValueOf(obj), reflect.ValueOf(w), reflect.ValueOf(h.withNamespace), reflect.ValueOf(h.wide), reflect.ValueOf(h.columnLabels)} + args := []reflect.Value{reflect.ValueOf(obj), reflect.ValueOf(w), reflect.ValueOf(h.withNamespace), reflect.ValueOf(h.wide), reflect.ValueOf(h.showAll), reflect.ValueOf(h.columnLabels)} resultValue := handler.printFunc.Call(args)[0] if resultValue.IsNil() { return nil diff --git a/pkg/kubectl/resource_printer_test.go b/pkg/kubectl/resource_printer_test.go index a4f79e4bf5f..a308afc5c2d 100644 --- a/pkg/kubectl/resource_printer_test.go +++ b/pkg/kubectl/resource_printer_test.go @@ -237,18 +237,18 @@ type TestUnknownType struct{} func (*TestUnknownType) IsAnAPIObject() {} -func PrintCustomType(obj *TestPrintType, w io.Writer, withNamespace bool, wide bool, columnLabels []string) error { +func PrintCustomType(obj *TestPrintType, w io.Writer, withNamespace bool, wide bool, showAll bool, columnLabels []string) error { _, err := fmt.Fprintf(w, "%s", obj.Data) return err } -func ErrorPrintHandler(obj *TestPrintType, w io.Writer, withNamespace bool, wide bool, columnLabels []string) error { +func ErrorPrintHandler(obj *TestPrintType, w io.Writer, withNamespace bool, wide bool, showAll bool, columnLabels []string) error { return fmt.Errorf("ErrorPrintHandler error") } func TestCustomTypePrinting(t *testing.T) { columns := []string{"Data"} - printer := NewHumanReadablePrinter(false, false, false, []string{}) + printer := NewHumanReadablePrinter(false, false, false, false, []string{}) printer.Handler(columns, PrintCustomType) obj := TestPrintType{"test object"} @@ -265,7 +265,7 @@ func TestCustomTypePrinting(t *testing.T) { func TestPrintHandlerError(t *testing.T) { columns := []string{"Data"} - printer := NewHumanReadablePrinter(false, false, false, []string{}) + printer := NewHumanReadablePrinter(false, false, false, false, []string{}) printer.Handler(columns, ErrorPrintHandler) obj := TestPrintType{"test object"} buffer := &bytes.Buffer{} @@ -276,7 +276,7 @@ func TestPrintHandlerError(t *testing.T) { } func TestUnknownTypePrinting(t *testing.T) { - printer := NewHumanReadablePrinter(false, false, false, []string{}) + printer := NewHumanReadablePrinter(false, false, false, false, []string{}) buffer := &bytes.Buffer{} err := printer.PrintObj(&TestUnknownType{}, buffer) if err == nil { @@ -451,8 +451,8 @@ func TestPrinters(t *testing.T) { t.Fatal(err) } printers := map[string]ResourcePrinter{ - "humanReadable": NewHumanReadablePrinter(true, false, false, []string{}), - "humanReadableHeaders": NewHumanReadablePrinter(false, false, false, []string{}), + "humanReadable": NewHumanReadablePrinter(true, false, false, false, []string{}), + "humanReadableHeaders": NewHumanReadablePrinter(false, false, false, false, []string{}), "json": &JSONPrinter{}, "yaml": &YAMLPrinter{}, "template": templatePrinter, @@ -489,7 +489,7 @@ func TestPrinters(t *testing.T) { func TestPrintEventsResultSorted(t *testing.T) { // Arrange - printer := NewHumanReadablePrinter(false /* noHeaders */, false, false, []string{}) + printer := NewHumanReadablePrinter(false /* noHeaders */, false, false, false, []string{}) obj := api.EventList{ Items: []api.Event{ @@ -530,7 +530,7 @@ func TestPrintEventsResultSorted(t *testing.T) { } func TestPrintMinionStatus(t *testing.T) { - printer := NewHumanReadablePrinter(false, false, false, []string{}) + printer := NewHumanReadablePrinter(false, false, false, false, []string{}) table := []struct { minion api.Node status string @@ -741,7 +741,7 @@ func TestPrintHumanReadableService(t *testing.T) { for _, svc := range tests { buff := bytes.Buffer{} - printService(&svc, &buff, false, false, []string{}) + printService(&svc, &buff, false, false, false, []string{}) output := string(buff.Bytes()) ip := svc.Spec.ClusterIP if !strings.Contains(output, ip) { @@ -922,7 +922,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, []string{}) + printer := NewHumanReadablePrinter(false, true, false, false, []string{}) buffer := &bytes.Buffer{} err := printer.PrintObj(test.obj, buffer) if err != nil { @@ -934,7 +934,7 @@ func TestPrintHumanReadableWithNamespace(t *testing.T) { } } else { // Expect error when trying to get all namespaces for un-namespaced object. - printer := NewHumanReadablePrinter(false, true, false, []string{}) + printer := NewHumanReadablePrinter(false, true, false, false, []string{}) buffer := &bytes.Buffer{} err := printer.PrintObj(test.obj, buffer) if err == nil { @@ -1029,7 +1029,100 @@ func TestPrintPod(t *testing.T) { buf := bytes.NewBuffer([]byte{}) for _, test := range tests { - printPod(&test.pod, buf, false, false, []string{}) + printPod(&test.pod, buf, false, false, true, []string{}) + // We ignore time + if !strings.HasPrefix(buf.String(), test.expect) { + t.Fatalf("Expected: %s, got: %s", test.expect, buf.String()) + } + buf.Reset() + } +} + +func TestPrintNonTerminatedPod(t *testing.T) { + tests := []struct { + pod api.Pod + expect string + }{ + { + // Test pod phase Running should be printed + api.Pod{ + ObjectMeta: api.ObjectMeta{Name: "test1"}, + Spec: api.PodSpec{Containers: make([]api.Container, 2)}, + Status: api.PodStatus{ + Phase: api.PodRunning, + ContainerStatuses: []api.ContainerStatus{ + {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}}, + {RestartCount: 3}, + }, + }, + }, + "test1\t1/2\tRunning\t6\t", + }, + { + // Test pod phase Pending should be printed + api.Pod{ + ObjectMeta: api.ObjectMeta{Name: "test2"}, + Spec: api.PodSpec{Containers: make([]api.Container, 2)}, + Status: api.PodStatus{ + Phase: api.PodPending, + ContainerStatuses: []api.ContainerStatus{ + {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}}, + {RestartCount: 3}, + }, + }, + }, + "test2\t1/2\tPending\t6\t", + }, + { + // Test pod phase Unknown should be printed + api.Pod{ + ObjectMeta: api.ObjectMeta{Name: "test3"}, + Spec: api.PodSpec{Containers: make([]api.Container, 2)}, + Status: api.PodStatus{ + Phase: api.PodUnknown, + ContainerStatuses: []api.ContainerStatus{ + {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}}, + {RestartCount: 3}, + }, + }, + }, + "test3\t1/2\tUnknown\t6\t", + }, + { + // Test pod phase Succeeded shouldn't be printed + api.Pod{ + ObjectMeta: api.ObjectMeta{Name: "test4"}, + Spec: api.PodSpec{Containers: make([]api.Container, 2)}, + Status: api.PodStatus{ + Phase: api.PodSucceeded, + ContainerStatuses: []api.ContainerStatus{ + {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}}, + {RestartCount: 3}, + }, + }, + }, + "", + }, + { + // Test pod phase Failed shouldn't be printed + api.Pod{ + ObjectMeta: api.ObjectMeta{Name: "test5"}, + Spec: api.PodSpec{Containers: make([]api.Container, 2)}, + Status: api.PodStatus{ + Phase: api.PodFailed, + ContainerStatuses: []api.ContainerStatus{ + {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}}, + {Ready: true, RestartCount: 3}, + }, + }, + }, + "", + }, + } + + buf := bytes.NewBuffer([]byte{}) + for _, test := range tests { + printPod(&test.pod, buf, false, false, false, []string{}) // We ignore time if !strings.HasPrefix(buf.String(), test.expect) { t.Fatalf("Expected: %s, got: %s", test.expect, buf.String()) @@ -1089,7 +1182,7 @@ func TestPrintPodWithLabels(t *testing.T) { buf := bytes.NewBuffer([]byte{}) for _, test := range tests { - printPod(&test.pod, buf, false, false, test.labelColumns) + printPod(&test.pod, buf, 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())