From b61a905b19b75eae77a1ea7396bb4c185df4ae7f Mon Sep 17 00:00:00 2001 From: Dai Zuozhuo Date: Tue, 4 Aug 2015 16:14:31 +0800 Subject: [PATCH] add jsonpath to kubectl --- docs/man/man1/kubectl-annotate.1 | 6 +- docs/man/man1/kubectl-config-view.1 | 6 +- docs/man/man1/kubectl-expose.1 | 6 +- docs/man/man1/kubectl-get.1 | 6 +- docs/man/man1/kubectl-label.1 | 6 +- docs/man/man1/kubectl-rolling-update.1 | 6 +- docs/man/man1/kubectl-run.1 | 6 +- docs/user-guide/kubectl/kubectl_annotate.md | 4 +- .../user-guide/kubectl/kubectl_config_view.md | 10 +- docs/user-guide/kubectl/kubectl_expose.md | 10 +- docs/user-guide/kubectl/kubectl_get.md | 25 ++++- docs/user-guide/kubectl/kubectl_label.md | 14 ++- .../kubectl/kubectl_rolling-update.md | 13 ++- docs/user-guide/kubectl/kubectl_run.md | 14 ++- hack/lib/test.sh | 25 +++++ hack/test-cmd.sh | 5 + pkg/kubectl/cmd/get.go | 2 +- pkg/kubectl/cmd/util/printing.go | 4 +- pkg/kubectl/resource_printer.go | 44 ++++++++ pkg/kubectl/resource_printer_test.go | 23 ++++ pkg/util/jsonpath/jsonpath.go | 103 ++++-------------- pkg/util/jsonpath/jsonpath_test.go | 51 +++++---- third_party/golang/template/exec.go | 5 + 23 files changed, 250 insertions(+), 144 deletions(-) diff --git a/docs/man/man1/kubectl-annotate.1 b/docs/man/man1/kubectl-annotate.1 index 252e91655f..515880efe2 100644 --- a/docs/man/man1/kubectl-annotate.1 +++ b/docs/man/man1/kubectl-annotate.1 @@ -43,7 +43,7 @@ resourcequotas (quota) or secrets. .PP \fB\-o\fP, \fB\-\-output\fP="" - Output format. One of: json|yaml|template|templatefile|wide. + Output format. One of: json|yaml|template|templatefile|wide|jsonpath. .PP \fB\-\-output\-version\fP="" @@ -67,8 +67,8 @@ resourcequotas (quota) or secrets. .PP \fB\-t\fP, \fB\-\-template\fP="" - Template string or path to template file to use when \-o=template or \-o=templatefile. The template format is golang templates [ -\[la]http://golang.org/pkg/text/template/#pkg-overview\[ra]] + Template string or path to template file to use when \-o=template, \-o=templatefile or \-o=jsonpath. The template format is golang templates [ +\[la]http://golang.org/pkg/text/template/#pkg-overview\[ra]]. The jsonpath template is composed of jsonpath expressions enclosed by {} .SH OPTIONS INHERITED FROM PARENT COMMANDS diff --git a/docs/man/man1/kubectl-config-view.1 b/docs/man/man1/kubectl-config-view.1 index 603cd5ea11..94c5bd6c85 100644 --- a/docs/man/man1/kubectl-config-view.1 +++ b/docs/man/man1/kubectl-config-view.1 @@ -42,7 +42,7 @@ You can use \-\-output=template \-\-template=TEMPLATE to extract specific values .PP \fB\-o\fP, \fB\-\-output\fP="" - Output format. One of: json|yaml|template|templatefile|wide. + Output format. One of: json|yaml|template|templatefile|wide|jsonpath. .PP \fB\-\-output\-version\fP="" @@ -62,8 +62,8 @@ You can use \-\-output=template \-\-template=TEMPLATE to extract specific values .PP \fB\-t\fP, \fB\-\-template\fP="" - Template string or path to template file to use when \-o=template or \-o=templatefile. The template format is golang templates [ -\[la]http://golang.org/pkg/text/template/#pkg-overview\[ra]] + Template string or path to template file to use when \-o=template, \-o=templatefile or \-o=jsonpath. The template format is golang templates [ +\[la]http://golang.org/pkg/text/template/#pkg-overview\[ra]]. The jsonpath template is composed of jsonpath expressions enclosed by {} .SH OPTIONS INHERITED FROM PARENT COMMANDS diff --git a/docs/man/man1/kubectl-expose.1 b/docs/man/man1/kubectl-expose.1 index c29f68c13a..2ca7cda883 100644 --- a/docs/man/man1/kubectl-expose.1 +++ b/docs/man/man1/kubectl-expose.1 @@ -60,7 +60,7 @@ re\-use the labels from the resource it exposes. .PP \fB\-o\fP, \fB\-\-output\fP="" - Output format. One of: json|yaml|template|templatefile|wide. + Output format. One of: json|yaml|template|templatefile|wide|jsonpath. .PP \fB\-\-output\-version\fP="" @@ -104,8 +104,8 @@ re\-use the labels from the resource it exposes. .PP \fB\-t\fP, \fB\-\-template\fP="" - Template string or path to template file to use when \-o=template or \-o=templatefile. The template format is golang templates [ -\[la]http://golang.org/pkg/text/template/#pkg-overview\[ra]] + Template string or path to template file to use when \-o=template, \-o=templatefile or \-o=jsonpath. The template format is golang templates [ +\[la]http://golang.org/pkg/text/template/#pkg-overview\[ra]]. The jsonpath template is composed of jsonpath expressions enclosed by {} .PP \fB\-\-type\fP="" diff --git a/docs/man/man1/kubectl-get.1 b/docs/man/man1/kubectl-get.1 index 88e5049138..c7e04253c8 100644 --- a/docs/man/man1/kubectl-get.1 +++ b/docs/man/man1/kubectl-get.1 @@ -49,7 +49,7 @@ of the \-\-template flag, you can filter the attributes of the fetched resource( .PP \fB\-o\fP, \fB\-\-output\fP="" - Output format. One of: json|yaml|template|templatefile|wide. + Output format. One of: json|yaml|template|templatefile|wide|jsonpath. .PP \fB\-\-output\-version\fP="" @@ -69,8 +69,8 @@ of the \-\-template flag, you can filter the attributes of the fetched resource( .PP \fB\-t\fP, \fB\-\-template\fP="" - Template string or path to template file to use when \-o=template or \-o=templatefile. The template format is golang templates [ -\[la]http://golang.org/pkg/text/template/#pkg-overview\[ra]] + Template string or path to template file to use when \-o=template, \-o=templatefile or \-o=jsonpath. The template format is golang templates [ +\[la]http://golang.org/pkg/text/template/#pkg-overview\[ra]]. The jsonpath template is composed of jsonpath expressions enclosed by {} .PP \fB\-w\fP, \fB\-\-watch\fP=false diff --git a/docs/man/man1/kubectl-label.1 b/docs/man/man1/kubectl-label.1 index 6193052d95..cb0a6e1a97 100644 --- a/docs/man/man1/kubectl-label.1 +++ b/docs/man/man1/kubectl-label.1 @@ -40,7 +40,7 @@ If \-\-resource\-version is specified, then updates will use this resource versi .PP \fB\-o\fP, \fB\-\-output\fP="" - Output format. One of: json|yaml|template|templatefile|wide. + Output format. One of: json|yaml|template|templatefile|wide|jsonpath. .PP \fB\-\-output\-version\fP="" @@ -68,8 +68,8 @@ If \-\-resource\-version is specified, then updates will use this resource versi .PP \fB\-t\fP, \fB\-\-template\fP="" - Template string or path to template file to use when \-o=template or \-o=templatefile. The template format is golang templates [ -\[la]http://golang.org/pkg/text/template/#pkg-overview\[ra]] + Template string or path to template file to use when \-o=template, \-o=templatefile or \-o=jsonpath. The template format is golang templates [ +\[la]http://golang.org/pkg/text/template/#pkg-overview\[ra]]. The jsonpath template is composed of jsonpath expressions enclosed by {} .SH OPTIONS INHERITED FROM PARENT COMMANDS diff --git a/docs/man/man1/kubectl-rolling-update.1 b/docs/man/man1/kubectl-rolling-update.1 index 5d36dabd08..9e30eb1d67 100644 --- a/docs/man/man1/kubectl-rolling-update.1 +++ b/docs/man/man1/kubectl-rolling-update.1 @@ -48,7 +48,7 @@ existing replication controller and overwrite at least one (common) label in its .PP \fB\-o\fP, \fB\-\-output\fP="" - Output format. One of: json|yaml|template|templatefile|wide. + Output format. One of: json|yaml|template|templatefile|wide|jsonpath. .PP \fB\-\-output\-version\fP="" @@ -72,8 +72,8 @@ existing replication controller and overwrite at least one (common) label in its .PP \fB\-t\fP, \fB\-\-template\fP="" - Template string or path to template file to use when \-o=template or \-o=templatefile. The template format is golang templates [ -\[la]http://golang.org/pkg/text/template/#pkg-overview\[ra]] + Template string or path to template file to use when \-o=template, \-o=templatefile or \-o=jsonpath. The template format is golang templates [ +\[la]http://golang.org/pkg/text/template/#pkg-overview\[ra]]. The jsonpath template is composed of jsonpath expressions enclosed by {} .PP \fB\-\-timeout\fP=5m0s diff --git a/docs/man/man1/kubectl-run.1 b/docs/man/man1/kubectl-run.1 index af56c147cc..23415306db 100644 --- a/docs/man/man1/kubectl-run.1 +++ b/docs/man/man1/kubectl-run.1 @@ -56,7 +56,7 @@ Creates a replication controller to manage the created container(s). .PP \fB\-o\fP, \fB\-\-output\fP="" - Output format. One of: json|yaml|template|templatefile|wide. + Output format. One of: json|yaml|template|templatefile|wide|jsonpath. .PP \fB\-\-output\-version\fP="" @@ -92,8 +92,8 @@ Creates a replication controller to manage the created container(s). .PP \fB\-t\fP, \fB\-\-template\fP="" - Template string or path to template file to use when \-o=template or \-o=templatefile. The template format is golang templates [ -\[la]http://golang.org/pkg/text/template/#pkg-overview\[ra]] + Template string or path to template file to use when \-o=template, \-o=templatefile or \-o=jsonpath. The template format is golang templates [ +\[la]http://golang.org/pkg/text/template/#pkg-overview\[ra]]. The jsonpath template is composed of jsonpath expressions enclosed by {} .PP \fB\-\-tty\fP=false diff --git a/docs/user-guide/kubectl/kubectl_annotate.md b/docs/user-guide/kubectl/kubectl_annotate.md index ce68bf7494..2c81262c0c 100644 --- a/docs/user-guide/kubectl/kubectl_annotate.md +++ b/docs/user-guide/kubectl/kubectl_annotate.md @@ -81,13 +81,13 @@ $ kubectl annotate pods foo description- --all[=false]: select all resources in the namespace of the specified resource types -h, --help[=false]: help for annotate --no-headers[=false]: When using the default output, don't print headers. - -o, --output="": Output format. One of: json|yaml|template|templatefile|wide. + -o, --output="": Output format. One of: json|yaml|template|templatefile|wide|jsonpath. --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] + -t, --template="": Template string or path to template file to use when -o=template, -o=templatefile or -o=jsonpath. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview]. The jsonpath template is composed of jsonpath expressions enclosed by {} ``` ### Options inherited from parent commands diff --git a/docs/user-guide/kubectl/kubectl_config_view.md b/docs/user-guide/kubectl/kubectl_config_view.md index cd4ae1f1ab..c1c3c58521 100644 --- a/docs/user-guide/kubectl/kubectl_config_view.md +++ b/docs/user-guide/kubectl/kubectl_config_view.md @@ -64,12 +64,12 @@ $ kubectl config view -o template --template='{{range .users}}{{ if eq .name "e2 --merge=true: merge together the full hierarchy of kubeconfig files --minify[=false]: remove all information not used by current-context from the output --no-headers[=false]: When using the default output, don't print headers. - -o, --output="": Output format. One of: json|yaml|template|templatefile|wide. + -o, --output="": Output format. One of: json|yaml|template|templatefile|wide|jsonpath. --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] + -t, --template="": Template string or path to template file to use when -o=template, -o=templatefile or -o=jsonpath. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview]. The jsonpath template is composed of jsonpath expressions enclosed by {} ``` ### Options inherited from parent commands @@ -105,7 +105,11 @@ $ 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-18 17:36:23.977277472 +0000 UTC +<<<<<<< HEAD +###### Auto generated by spf13/cobra at 2015-08-12 23:41:01.310054033 +0000 UTC +======= +###### Auto generated by spf13/cobra at 2015-08-11 10:20:05.920471764 +0000 UTC +>>>>>>> add jsonpath to kubectl [![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 41482e22d8..086be677ed 100644 --- a/docs/user-guide/kubectl/kubectl_expose.md +++ b/docs/user-guide/kubectl/kubectl_expose.md @@ -76,7 +76,7 @@ $ kubectl expose rc streamer --port=4100 --protocol=udp --name=video-stream -l, --labels="": Labels to apply to the service created by this call. --name="": The name for the newly created object. --no-headers[=false]: When using the default output, don't print headers. - -o, --output="": Output format. One of: json|yaml|template|templatefile|wide. + -o, --output="": Output format. One of: json|yaml|template|templatefile|wide|jsonpath. --output-version="": Output the formatted object with the given version (default api-version). --overrides="": An inline JSON override for the generated object. If this is non-empty, it is used to override the generated object. Requires that the object supply a valid apiVersion field. --port=-1: The port that the service should serve on. Copied from the resource being exposed, if unspecified @@ -87,7 +87,7 @@ $ kubectl expose rc streamer --port=4100 --protocol=udp --name=video-stream -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] + -t, --template="": Template string or path to template file to use when -o=template, -o=templatefile or -o=jsonpath. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview]. The jsonpath template is composed of jsonpath expressions enclosed by {} --type="": Type for this service: ClusterIP, NodePort, or LoadBalancer. Default is 'ClusterIP' unless --create-external-load-balancer is specified. ``` @@ -124,7 +124,11 @@ $ 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-18 17:36:23.976918949 +0000 UTC +<<<<<<< HEAD +###### Auto generated by spf13/cobra at 2015-08-12 23:41:01.308576759 +0000 UTC +======= +###### Auto generated by spf13/cobra at 2015-08-11 10:20:05.919806434 +0000 UTC +>>>>>>> add jsonpath to kubectl [![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 440ad8fa41..672969b060 100644 --- a/docs/user-guide/kubectl/kubectl_get.md +++ b/docs/user-guide/kubectl/kubectl_get.md @@ -49,7 +49,11 @@ 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). ``` +<<<<<<< HEAD kubectl get [(-o|--output=)json|yaml|template|wide|...] (TYPE [(NAME | -l label] | TYPE/NAME ...) [flags] +======= +kubectl get [(-o|--output=)json|yaml|template|templatefile|wide|jsonpath|...] (TYPE [NAME | -l label] | TYPE/NAME ...) +>>>>>>> add jsonpath to kubectl ``` ### Examples @@ -88,12 +92,19 @@ $ kubectl get rc/web service/frontend pods/web-pod-13je7 -h, --help[=false]: help for get -L, --label-columns=[]: Accepts a comma separated list of labels that are going to be presented as columns. Names are case-sensitive. You can also use multiple flag statements like -L label1 -L label2... --no-headers[=false]: When using the default output, don't print headers. - -o, --output="": Output format. One of: json|yaml|template|templatefile|wide. + -o, --output="": Output format. One of: json|yaml|template|templatefile|wide|jsonpath. --output-version="": Output the formatted object with the given version (default api-version). -l, --selector="": Selector (label query) to filter on +<<<<<<< HEAD -a, --show-all[=false]: When printing, show all resources (default hide terminated pods.) +======= +<<<<<<< HEAD +>>>>>>> add jsonpath to kubectl --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] +======= + -t, --template="": Template string or path to template file to use when -o=template, -o=templatefile or -o=jsonpath. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview]. The jsonpath template is composed of jsonpath expressions enclosed by {} +>>>>>>> add jsonpath to kubectl -w, --watch[=false]: After listing/getting the requested object, watch for changes. --watch-only[=false]: Watch for changes to the requested object(s), without listing/getting first. ``` @@ -131,7 +142,19 @@ $ kubectl get rc/web service/frontend pods/web-pod-13je7 * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager +<<<<<<< HEAD ###### Auto generated by spf13/cobra at 2015-08-18 17:36:23.974605429 +0000 UTC +======= +<<<<<<< HEAD +###### Auto generated by spf13/cobra at 2015-08-13 06:12:05.386038784 +0000 UTC +======= +<<<<<<< HEAD +###### Auto generated by spf13/cobra at 2015-08-12 23:41:01.301023165 +0000 UTC +======= +###### Auto generated by spf13/cobra at 2015-08-11 10:20:05.915175811 +0000 UTC +>>>>>>> add jsonpath to kubectl +>>>>>>> add jsonpath to kubectl +>>>>>>> add jsonpath to kubectl [![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 320a6ef783..46d625eff9 100644 --- a/docs/user-guide/kubectl/kubectl_label.md +++ b/docs/user-guide/kubectl/kubectl_label.md @@ -78,14 +78,14 @@ $ kubectl label pods foo bar- -f, --filename=[]: Filename, directory, or URL to a file identifying the resource to update the labels -h, --help[=false]: help for label --no-headers[=false]: When using the default output, don't print headers. - -o, --output="": Output format. One of: json|yaml|template|templatefile|wide. + -o, --output="": Output format. One of: json|yaml|template|templatefile|wide|jsonpath. --output-version="": Output the formatted object with the given version (default api-version). --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] + -t, --template="": Template string or path to template file to use when -o=template, -o=templatefile or -o=jsonpath. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview]. The jsonpath template is composed of jsonpath expressions enclosed by {} ``` ### Options inherited from parent commands @@ -121,7 +121,15 @@ $ kubectl label pods foo bar- * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra at 2015-08-18 17:36:23.977042997 +0000 UTC +<<<<<<< HEAD +###### Auto generated by spf13/cobra at 2015-08-13 02:21:16.349210188 +0000 UTC +======= +<<<<<<< HEAD +###### Auto generated by spf13/cobra at 2015-08-12 23:41:01.309176995 +0000 UTC +======= +###### Auto generated by spf13/cobra at 2015-08-11 10:20:05.920043611 +0000 UTC +>>>>>>> add jsonpath to kubectl +>>>>>>> add jsonpath to kubectl [![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 0580645cb8..e33c6059b3 100644 --- a/docs/user-guide/kubectl/kubectl_rolling-update.md +++ b/docs/user-guide/kubectl/kubectl_rolling-update.md @@ -75,13 +75,16 @@ $ kubectl rolling-update frontend --image=image:v2 -h, --help[=false]: help for rolling-update --image="": Image to use for upgrading the replication controller. Can not be used with --filename/-f --no-headers[=false]: When using the default output, don't print headers. - -o, --output="": Output format. One of: json|yaml|template|templatefile|wide. + -o, --output="": Output format. One of: json|yaml|template|templatefile|wide|jsonpath. --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.) +<<<<<<< HEAD --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] +======= + -t, --template="": Template string or path to template file to use when -o=template, -o=templatefile or -o=jsonpath. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview]. The jsonpath template is composed of jsonpath expressions enclosed by {} +>>>>>>> add jsonpath to kubectl --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". --update-period=1m0s: Time to wait between updating pods. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". ``` @@ -119,7 +122,11 @@ $ kubectl rolling-update frontend --image=image:v2 * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra at 2015-08-18 17:36:23.97558907 +0000 UTC +<<<<<<< HEAD +###### Auto generated by spf13/cobra at 2015-08-12 23:41:01.305486289 +0000 UTC +======= +###### Auto generated by spf13/cobra at 2015-08-11 10:20:05.917101859 +0000 UTC +>>>>>>> add jsonpath to kubectl [![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 17487cafa1..cd4bea9f4b 100644 --- a/docs/user-guide/kubectl/kubectl_run.md +++ b/docs/user-guide/kubectl/kubectl_run.md @@ -82,7 +82,7 @@ $ kubectl run nginx --image=nginx --command -- ... --image="": The image for the container to run. -l, --labels="": Labels to apply to the pod(s). --no-headers[=false]: When using the default output, don't print headers. - -o, --output="": Output format. One of: json|yaml|template|templatefile|wide. + -o, --output="": Output format. One of: json|yaml|template|templatefile|wide|jsonpath. --output-version="": Output the formatted object with the given version (default api-version). --overrides="": An inline JSON override for the generated object. If this is non-empty, it is used to override the generated object. Requires that the object supply a valid apiVersion field. --port=-1: The port that this container exposes. @@ -91,7 +91,7 @@ $ kubectl run nginx --image=nginx --command -- ... -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] + -t, --template="": Template string or path to template file to use when -o=template, -o=templatefile or -o=jsonpath. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview]. The jsonpath template is composed of jsonpath expressions enclosed by {} --tty[=false]: Allocated a TTY for each container in the pod. Because -t is currently shorthand for --template, -t is not supported for --tty. This shorthand is deprecated and we expect to adopt -t for --tty soon. ``` @@ -128,7 +128,15 @@ $ kubectl run nginx --image=nginx --command -- ... * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra at 2015-08-18 17:36:23.976238275 +0000 UTC +<<<<<<< HEAD +###### Auto generated by spf13/cobra at 2015-08-13 16:41:44.465440991 +0000 UTC +======= +<<<<<<< HEAD +###### Auto generated by spf13/cobra at 2015-08-12 23:41:01.307766241 +0000 UTC +======= +###### Auto generated by spf13/cobra at 2015-08-11 10:20:05.919181854 +0000 UTC +>>>>>>> add jsonpath to kubectl +>>>>>>> add jsonpath to kubectl [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_run.md?pixel)]() diff --git a/hack/lib/test.sh b/hack/lib/test.sh index 02a4c8ad9e..6ccd66a31a 100644 --- a/hack/lib/test.sh +++ b/hack/lib/test.sh @@ -51,6 +51,31 @@ kube::test::get_object_assert() { fi } +kube::test::get_object_jsonpath_assert() { + local object=$1 + local request=$2 + local expected=$3 + + res=$(eval kubectl get "${kube_flags[@]}" $object -o jsonpath -t \"$request\") + + if [[ "$res" =~ ^$expected$ ]]; then + echo -n ${green} + echo "Successful get $object $request: $res" + echo -n ${reset} + return 0 + else + echo ${bold}${red} + echo "FAIL!" + echo "Get $object $request" + echo " Expected: $expected" + echo " Got: $res" + echo ${reset}${red} + caller + echo ${reset} + return 1 + fi +} + kube::test::describe_object_assert() { local resource=$1 local object=$2 diff --git a/hack/test-cmd.sh b/hack/test-cmd.sh index a96de063cc..a39eb5ad4a 100755 --- a/hack/test-cmd.sh +++ b/hack/test-cmd.sh @@ -234,6 +234,11 @@ runTests() { kube::test::get_object_assert 'pod valid-pod' "{{$id_field}}" 'valid-pod' kube::test::get_object_assert 'pod/valid-pod' "{{$id_field}}" 'valid-pod' kube::test::get_object_assert 'pods/valid-pod' "{{$id_field}}" 'valid-pod' + # Repeat above test using jsonpath template + kube::test::get_object_jsonpath_assert pods "{.items[*]$id_field}" 'valid-pod' + kube::test::get_object_jsonpath_assert 'pod valid-pod' "{$id_field}" 'valid-pod' + kube::test::get_object_jsonpath_assert 'pod/valid-pod' "{$id_field}" 'valid-pod' + kube::test::get_object_jsonpath_assert 'pods/valid-pod' "{$id_field}" 'valid-pod' # Describe command should print detailed information kube::test::describe_object_assert pods 'valid-pod' "Name:" "Image(s):" "Node:" "Labels:" "Status:" "Replication Controllers" # Describe command (resource only) should print detailed information diff --git a/pkg/kubectl/cmd/get.go b/pkg/kubectl/cmd/get.go index 7396fc0025..b5028b057c 100644 --- a/pkg/kubectl/cmd/get.go +++ b/pkg/kubectl/cmd/get.go @@ -69,7 +69,7 @@ func NewCmdGet(f *cmdutil.Factory, out io.Writer) *cobra.Command { validArgs := p.HandledResources() cmd := &cobra.Command{ - Use: "get [(-o|--output=)json|yaml|template|wide|...] (TYPE [(NAME | -l label] | TYPE/NAME ...) [flags]", + Use: "get [(-o|--output=)json|yaml|template|templatefile|wide|jsonpath|...] (TYPE [NAME | -l label] | TYPE/NAME ...)", Short: "Display one or many resources", Long: get_long, Example: get_example, diff --git a/pkg/kubectl/cmd/util/printing.go b/pkg/kubectl/cmd/util/printing.go index 9473cd1479..31e93ae933 100644 --- a/pkg/kubectl/cmd/util/printing.go +++ b/pkg/kubectl/cmd/util/printing.go @@ -28,10 +28,10 @@ import ( // AddPrinterFlags adds printing related flags to a command (e.g. output format, no headers, template path) func AddPrinterFlags(cmd *cobra.Command) { - cmd.Flags().StringP("output", "o", "", "Output format. One of: json|yaml|template|templatefile|wide.") + cmd.Flags().StringP("output", "o", "", "Output format. One of: json|yaml|template|templatefile|wide|jsonpath.") 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().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().StringP("template", "t", "", "Template string or path to template file to use when -o=template, -o=templatefile or -o=jsonpath. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview]. The jsonpath template is composed of jsonpath expressions enclosed by {}") 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.)") } diff --git a/pkg/kubectl/resource_printer.go b/pkg/kubectl/resource_printer.go index a346f2661e..5b6fac2b7a 100644 --- a/pkg/kubectl/resource_printer.go +++ b/pkg/kubectl/resource_printer.go @@ -35,6 +35,7 @@ import ( "k8s.io/kubernetes/pkg/conversion" "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util" + "k8s.io/kubernetes/pkg/util/jsonpath" "k8s.io/kubernetes/pkg/volume" ) @@ -71,6 +72,15 @@ func GetPrinter(format, formatArgument string) (ResourcePrinter, bool, error) { if err != nil { return nil, false, fmt.Errorf("error parsing template %s, %v\n", string(data), err) } + case "jsonpath": + if len(formatArgument) == 0 { + return nil, false, fmt.Errorf("jsonpath format specified but no jsonpath template given") + } + var err error + printer, err = NewJSONPathPrinter(formatArgument) + if err != nil { + return nil, false, fmt.Errorf("error parsing jsonpath %s, %v\n", formatArgument, err) + } case "wide": fallthrough case "": @@ -1205,3 +1215,37 @@ func indirect(v reflect.Value) (rv reflect.Value, isNil bool) { } return v, false } + +// JSONPathPrinter is an implementation of ResourcePrinter which formats data with jsonpath expression. +type JSONPathPrinter struct { + rawTemplate string + *jsonpath.JSONPath +} + +func NewJSONPathPrinter(tmpl string) (*JSONPathPrinter, error) { + j := jsonpath.New("out") + if err := j.Parse(tmpl); err != nil { + return nil, err + } + return &JSONPathPrinter{tmpl, j}, nil +} + +// PrintObj formats the obj with the JSONPath Template. +func (j *JSONPathPrinter) PrintObj(obj runtime.Object, w io.Writer) error { + data, err := json.Marshal(obj) + if err != nil { + return err + } + out := map[string]interface{}{} + if err := json.Unmarshal(data, &out); err != nil { + return err + } + if err = j.JSONPath.Execute(w, out); err != nil { + fmt.Fprintf(w, "Error executing template: %v\n", err) + fmt.Fprintf(w, "template was:\n\t%v\n", j.rawTemplate) + fmt.Fprintf(w, "raw data was:\n\t%v\n", string(data)) + fmt.Fprintf(w, "object given to template engine was:\n\t%+v\n", out) + return fmt.Errorf("error executing jsonpath '%v': '%v'\n----data----\n%+v\n", j.rawTemplate, err, out) + } + return nil +} diff --git a/pkg/kubectl/resource_printer_test.go b/pkg/kubectl/resource_printer_test.go index a308afc5c2..1ea9479700 100644 --- a/pkg/kubectl/resource_printer_test.go +++ b/pkg/kubectl/resource_printer_test.go @@ -164,6 +164,23 @@ func TestPrintTemplate(t *testing.T) { } } +func TestPrintJSONPath(t *testing.T) { + buf := bytes.NewBuffer([]byte{}) + printer, found, err := GetPrinter("jsonpath", "{.metadata.name}") + if err != nil || !found { + t.Fatalf("unexpected error: %#v", err) + } + unversionedPod := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}} + obj, err := api.Scheme.ConvertToVersion(unversionedPod, testapi.Version()) + err = printer.PrintObj(obj, buf) + if err != nil { + t.Fatalf("unexpected error: %s %#v", buf, err) + } + if buf.String() != "foo" { + t.Errorf("unexpected output: %s", buf.String()) + } +} + func TestPrintEmptyTemplate(t *testing.T) { if _, _, err := GetPrinter("template", ""); err == nil { t.Errorf("unexpected non-error") @@ -450,6 +467,10 @@ func TestPrinters(t *testing.T) { if err != nil { t.Fatal(err) } + jsonpathPrinter, err := NewJSONPathPrinter("{.metadata.name}") + if err != nil { + t.Fatal(err) + } printers := map[string]ResourcePrinter{ "humanReadable": NewHumanReadablePrinter(true, false, false, false, []string{}), "humanReadableHeaders": NewHumanReadablePrinter(false, false, false, false, []string{}), @@ -457,6 +478,7 @@ func TestPrinters(t *testing.T) { "yaml": &YAMLPrinter{}, "template": templatePrinter, "template2": templatePrinter2, + "jsonpath": jsonpathPrinter, } objects := map[string]runtime.Object{ "pod": &api.Pod{ObjectMeta: om("pod")}, @@ -471,6 +493,7 @@ func TestPrinters(t *testing.T) { // map of printer name to set of objects it should fail on. expectedErrors := map[string]util.StringSet{ "template2": util.NewStringSet("pod", "emptyPodList", "endpoints"), + "jsonpath": util.NewStringSet("emptyPodList", "nonEmptyPodList", "endpoints"), } for pName, p := range printers { diff --git a/pkg/util/jsonpath/jsonpath.go b/pkg/util/jsonpath/jsonpath.go index f74ed35e9b..b924e76f5d 100644 --- a/pkg/util/jsonpath/jsonpath.go +++ b/pkg/util/jsonpath/jsonpath.go @@ -21,7 +21,6 @@ import ( "fmt" "io" "reflect" - "strconv" "k8s.io/kubernetes/third_party/golang/template" ) @@ -214,10 +213,9 @@ func (j *JSONPath) evalIdentifier(input []reflect.Value, node *IdentifierNode) ( func (j *JSONPath) evalArray(input []reflect.Value, node *ArrayNode) ([]reflect.Value, error) { result := []reflect.Value{} for _, value := range input { - if value.Kind() == reflect.Interface { - value = reflect.ValueOf(value.Interface()) - } - if value.Kind() != reflect.Array && value.Kind() != reflect.Slice { + + value, isNil := template.Indirect(value) + if isNil || (value.Kind() != reflect.Array && value.Kind() != reflect.Slice) { return input, fmt.Errorf("%v is not array or slice", value) } params := node.Params @@ -265,9 +263,11 @@ func (j *JSONPath) evalField(input []reflect.Value, node *FieldNode) ([]reflect. results := []reflect.Value{} for _, value := range input { var result reflect.Value - if value.Kind() == reflect.Interface { - value = reflect.ValueOf(value.Interface()) + value, isNil := template.Indirect(value) + if isNil { + continue } + if value.Kind() == reflect.Struct { result = value.FieldByName(node.Value) } else if value.Kind() == reflect.Map { @@ -287,6 +287,11 @@ func (j *JSONPath) evalField(input []reflect.Value, node *FieldNode) ([]reflect. func (j *JSONPath) evalWildcard(input []reflect.Value, node *WildcardNode) ([]reflect.Value, error) { results := []reflect.Value{} for _, value := range input { + value, isNil := template.Indirect(value) + if isNil { + continue + } + kind := value.Kind() if kind == reflect.Struct { for i := 0; i < value.NumField(); i++ { @@ -310,6 +315,11 @@ func (j *JSONPath) evalRecursive(input []reflect.Value, node *RecursiveNode) ([] result := []reflect.Value{} for _, value := range input { results := []reflect.Value{} + value, isNil := template.Indirect(value) + if isNil { + continue + } + kind := value.Kind() if kind == reflect.Struct { for i := 0; i < value.NumField(); i++ { @@ -340,9 +350,8 @@ func (j *JSONPath) evalRecursive(input []reflect.Value, node *RecursiveNode) ([] func (j *JSONPath) evalFilter(input []reflect.Value, node *FilterNode) ([]reflect.Value, error) { results := []reflect.Value{} for _, value := range input { - if value.Kind() == reflect.Interface { - value = reflect.ValueOf(value.Interface()) - } + value, _ = template.Indirect(value) + if value.Kind() != reflect.Array && value.Kind() != reflect.Slice { return input, fmt.Errorf("%v is not array or slice", value) } @@ -407,77 +416,11 @@ func (j *JSONPath) evalFilter(input []reflect.Value, node *FilterNode) ([]reflec // evalToText translates reflect value to corresponding text func (j *JSONPath) evalToText(v reflect.Value) ([]byte, error) { - if v.Kind() == reflect.Interface { - v = reflect.ValueOf(v.Interface()) + iface, ok := template.PrintableValue(v) + if !ok { + return nil, fmt.Errorf("can't print type %s", v.Type()) } var buffer bytes.Buffer - switch v.Kind() { - case reflect.Invalid: - //pass - case reflect.Ptr: - text, err := j.evalToText(reflect.Indirect(v)) - if err != nil { - return nil, err - } - buffer.Write(text) - case reflect.Bool: - if variable := v.Bool(); variable { - buffer.WriteString("True") - } else { - buffer.WriteString("False") - } - case reflect.Float32: - buffer.WriteString(strconv.FormatFloat(v.Float(), 'f', -1, 32)) - case reflect.Float64: - buffer.WriteString(strconv.FormatFloat(v.Float(), 'f', -1, 64)) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - buffer.WriteString(strconv.FormatInt(v.Int(), 10)) - case reflect.String: - buffer.WriteString(v.String()) - case reflect.Array, reflect.Slice: - buffer.WriteString("[") - for i := 0; i < v.Len(); i++ { - text, err := j.evalToText(v.Index(i)) - if err != nil { - return nil, err - } - buffer.Write(text) - if i != v.Len()-1 { - buffer.WriteString(", ") - } - } - buffer.WriteString("]") - case reflect.Struct: - buffer.WriteString("{") - for i := 0; i < v.NumField(); i++ { - text, err := j.evalToText(v.Field(i)) - if err != nil { - return nil, err - } - pair := fmt.Sprintf("%s: %s", v.Type().Field(i).Name, text) - buffer.WriteString(pair) - if i != v.NumField()-1 { - buffer.WriteString(", ") - } - } - buffer.WriteString("}") - case reflect.Map: - buffer.WriteString("{") - for i, key := range v.MapKeys() { - text, err := j.evalToText(v.MapIndex(key)) - if err != nil { - return nil, err - } - pair := fmt.Sprintf("%s: %s", key, text) - buffer.WriteString(pair) - if i != len(v.MapKeys())-1 { - buffer.WriteString(", ") - } - } - buffer.WriteString("}") - - default: - return nil, fmt.Errorf("%v is not printable", v.Kind()) - } + fmt.Fprint(&buffer, iface) return buffer.Bytes(), nil } diff --git a/pkg/util/jsonpath/jsonpath_test.go b/pkg/util/jsonpath/jsonpath_test.go index 78cebc92f0..879c0a691a 100644 --- a/pkg/util/jsonpath/jsonpath_test.go +++ b/pkg/util/jsonpath/jsonpath_test.go @@ -19,6 +19,7 @@ package jsonpath import ( "bytes" "encoding/json" + "fmt" "testing" ) @@ -69,25 +70,30 @@ func testFailJSONPath(tests []jsonpathTest, t *testing.T) { } } +type book struct { + Category string + Author string + Title string + Price float32 +} + +func (b book) String() string { + return fmt.Sprintf("{Category: %s, Author: %s, Title: %s, Price: %v}", b.Category, b.Author, b.Title, b.Price) +} + +type bicycle struct { + Color string + Price float32 +} + +type store struct { + Book []book + Bicycle bicycle + Name string + Labels map[string]int +} + func TestStructInput(t *testing.T) { - type book struct { - Category string - Author string - Title string - Price float32 - } - - type bicycle struct { - Color string - Price float32 - } - - type store struct { - Book []book - Bicycle bicycle - Name string - Labels map[string]int - } storeData := store{ Name: "jsonpath", @@ -106,7 +112,7 @@ func TestStructInput(t *testing.T) { storeTests := []jsonpathTest{ {"plain", "hello jsonpath", nil, "hello jsonpath"}, - {"recursive", "{..}", []int{1, 2, 3}, "[1, 2, 3]"}, + {"recursive", "{..}", []int{1, 2, 3}, "[1 2 3]"}, {"filter", "{[?(@<5)]}", []int{2, 6, 3, 7}, "2 3"}, {"quote", `{"{"}`, nil, "{"}, {"union", "{[1,3,4]}", []int{0, 1, 2, 3, 4}, "1 3 4"}, @@ -197,6 +203,7 @@ func TestKubenates(t *testing.T) { if err != nil { t.Error(err) } + nodesTests := []jsonpathTest{ {"range item", "{range .items[*]}{.metadata.name}, {end}{.kind}", nodesData, `127.0.0.1, 127.0.0.2, List`}, {"range addresss", "{.items[*].status.addresses[*].address}", nodesData, @@ -205,10 +212,10 @@ func TestKubenates(t *testing.T) { `127.0.0.1, 127.0.0.2, 127.0.0.3, `}, {"item name", "{.items[*].metadata.name}", nodesData, `127.0.0.1 127.0.0.2`}, {"union nodes capacity", "{.items[*]['metadata.name', 'status.capacity']}", nodesData, - `127.0.0.1 127.0.0.2 {cpu: 4} {cpu: 8}`}, + `127.0.0.1 127.0.0.2 map[cpu:4] map[cpu:8]`}, {"range nodes capacity", "{range .items[*]}[{.metadata.name}, {.status.capacity}] {end}", nodesData, - `[127.0.0.1, {cpu: 4}] [127.0.0.2, {cpu: 8}] `}, - {"user password", `{.users[?(@.name=="e2e")].user.password}`, nodesData, "secret"}, + `[127.0.0.1, map[cpu:4]] [127.0.0.2, map[cpu:8]] `}, + {"user password", `{.users[?(@.name=="e2e")].user.password}`, &nodesData, "secret"}, } testJSONPath(nodesTests, t) } diff --git a/third_party/golang/template/exec.go b/third_party/golang/template/exec.go index 11fef63f48..82519edc93 100644 --- a/third_party/golang/template/exec.go +++ b/third_party/golang/template/exec.go @@ -1,9 +1,14 @@ +//This package is copied from Go library text/template. +//The original private functions indirect and printableValue +//are exported as public functions. package template import ( "reflect" "fmt" ) +var Indirect = indirect +var PrintableValue = printableValue var ( errorType = reflect.TypeOf((*error)(nil)).Elem()