From 7403878ac0c78d1f349940af493564a13fa3d504 Mon Sep 17 00:00:00 2001 From: Mike Metral Date: Mon, 28 Mar 2016 12:44:21 -0700 Subject: [PATCH] allow kubectl cmd to process dirs recursively reqs: - the kubectl cmd must support the -f | --filename flag - the kubectl cmd must support visiting a dir one level deep, or using more than one resource --- contrib/completions/bash/kubectl | 36 +++ docs/man/man1/kubectl-annotate.1 | 4 + docs/man/man1/kubectl-apply.1 | 4 + docs/man/man1/kubectl-autoscale.1 | 4 + docs/man/man1/kubectl-convert.1 | 4 + docs/man/man1/kubectl-create.1 | 4 + docs/man/man1/kubectl-delete.1 | 4 + docs/man/man1/kubectl-describe.1 | 4 + docs/man/man1/kubectl-edit.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-patch.1 | 4 + docs/man/man1/kubectl-replace.1 | 4 + docs/man/man1/kubectl-rollout-history.1 | 4 + docs/man/man1/kubectl-rollout-pause.1 | 4 + docs/man/man1/kubectl-rollout-resume.1 | 4 + docs/man/man1/kubectl-rollout-undo.1 | 4 + docs/man/man1/kubectl-scale.1 | 4 + docs/man/man1/kubectl-stop.1 | 4 + docs/user-guide/kubectl/kubectl_annotate.md | 3 +- docs/user-guide/kubectl/kubectl_apply.md | 3 +- docs/user-guide/kubectl/kubectl_autoscale.md | 3 +- docs/user-guide/kubectl/kubectl_convert.md | 3 +- docs/user-guide/kubectl/kubectl_create.md | 3 +- docs/user-guide/kubectl/kubectl_delete.md | 3 +- docs/user-guide/kubectl/kubectl_describe.md | 3 +- docs/user-guide/kubectl/kubectl_edit.md | 3 +- docs/user-guide/kubectl/kubectl_expose.md | 3 +- docs/user-guide/kubectl/kubectl_get.md | 3 +- docs/user-guide/kubectl/kubectl_label.md | 3 +- docs/user-guide/kubectl/kubectl_patch.md | 3 +- docs/user-guide/kubectl/kubectl_replace.md | 3 +- .../kubectl/kubectl_rollout_history.md | 3 +- .../kubectl/kubectl_rollout_pause.md | 3 +- .../kubectl/kubectl_rollout_resume.md | 3 +- .../kubectl/kubectl_rollout_undo.md | 3 +- docs/user-guide/kubectl/kubectl_scale.md | 3 +- pkg/kubectl/cmd/annotate.go | 5 +- pkg/kubectl/cmd/apply.go | 4 +- pkg/kubectl/cmd/autoscale.go | 19 +- pkg/kubectl/cmd/convert.go | 5 +- pkg/kubectl/cmd/create.go | 4 +- pkg/kubectl/cmd/delete.go | 4 +- pkg/kubectl/cmd/describe.go | 4 +- pkg/kubectl/cmd/edit.go | 19 +- pkg/kubectl/cmd/expose.go | 4 +- pkg/kubectl/cmd/get.go | 6 +- pkg/kubectl/cmd/label.go | 5 +- pkg/kubectl/cmd/patch.go | 4 +- pkg/kubectl/cmd/replace.go | 8 +- pkg/kubectl/cmd/rollingupdate.go | 2 +- pkg/kubectl/cmd/rollout/rollout_history.go | 4 +- pkg/kubectl/cmd/rollout/rollout_pause.go | 4 +- pkg/kubectl/cmd/rollout/rollout_resume.go | 4 +- pkg/kubectl/cmd/rollout/rollout_undo.go | 9 +- pkg/kubectl/cmd/scale.go | 4 +- pkg/kubectl/cmd/stop.go | 4 +- pkg/kubectl/cmd/util/helpers.go | 4 + pkg/kubectl/resource/builder.go | 8 +- pkg/kubectl/resource/builder_test.go | 220 +++++++++++++++--- 61 files changed, 438 insertions(+), 82 deletions(-) diff --git a/contrib/completions/bash/kubectl b/contrib/completions/bash/kubectl index 772ac4f8367..ec85d36f545 100644 --- a/contrib/completions/bash/kubectl +++ b/contrib/completions/bash/kubectl @@ -278,6 +278,8 @@ _kubectl_get() flags+=("--output=") two_word_flags+=("-o") flags+=("--output-version=") + flags+=("--recursive") + flags+=("-R") flags+=("--selector=") two_word_flags+=("-l") flags+=("--show-all") @@ -358,6 +360,8 @@ _kubectl_describe() two_word_flags+=("-f") flags_with_completion+=("-f") flags_completion+=("__handle_filename_extension_flag json|yaml|yml") + flags+=("--recursive") + flags+=("-R") flags+=("--selector=") two_word_flags+=("-l") flags+=("--alsologtostderr") @@ -756,6 +760,8 @@ _kubectl_create() flags+=("--output=") two_word_flags+=("-o") flags+=("--record") + flags+=("--recursive") + flags+=("-R") flags+=("--save-config") flags+=("--schema-cache-dir=") flags+=("--validate") @@ -812,6 +818,8 @@ _kubectl_replace() flags+=("--output=") two_word_flags+=("-o") flags+=("--record") + flags+=("--recursive") + flags+=("-R") flags+=("--save-config") flags+=("--schema-cache-dir=") flags+=("--timeout=") @@ -868,6 +876,8 @@ _kubectl_patch() flags+=("--patch=") two_word_flags+=("-p") flags+=("--record") + flags+=("--recursive") + flags+=("-R") flags+=("--type=") flags+=("--alsologtostderr") flags+=("--api-version=") @@ -922,6 +932,8 @@ _kubectl_delete() flags+=("--ignore-not-found") flags+=("--output=") two_word_flags+=("-o") + flags+=("--recursive") + flags+=("-R") flags+=("--selector=") two_word_flags+=("-l") flags+=("--timeout=") @@ -998,6 +1010,8 @@ _kubectl_edit() two_word_flags+=("-o") flags+=("--output-version=") flags+=("--record") + flags+=("--recursive") + flags+=("-R") flags+=("--save-config") flags+=("--windows-line-endings") flags+=("--alsologtostderr") @@ -1048,6 +1062,8 @@ _kubectl_apply() flags+=("--output=") two_word_flags+=("-o") flags+=("--record") + flags+=("--recursive") + flags+=("-R") flags+=("--schema-cache-dir=") flags+=("--validate") flags+=("--alsologtostderr") @@ -1259,6 +1275,8 @@ _kubectl_scale() flags+=("--output=") two_word_flags+=("-o") flags+=("--record") + flags+=("--recursive") + flags+=("-R") flags+=("--replicas=") flags+=("--resource-version=") flags+=("--timeout=") @@ -1708,6 +1726,8 @@ _kubectl_expose() flags+=("--port=") flags+=("--protocol=") flags+=("--record") + flags+=("--recursive") + flags+=("-R") flags+=("--save-config") flags+=("--selector=") flags+=("--session-affinity=") @@ -1775,6 +1795,8 @@ _kubectl_autoscale() two_word_flags+=("-o") flags+=("--output-version=") flags+=("--record") + flags+=("--recursive") + flags+=("-R") flags+=("--save-config") flags+=("--show-all") flags+=("-a") @@ -1828,6 +1850,8 @@ _kubectl_rollout_history() two_word_flags+=("-f") flags_with_completion+=("-f") flags_completion+=("__handle_filename_extension_flag json|yaml|yml") + flags+=("--recursive") + flags+=("-R") flags+=("--revision=") flags+=("--alsologtostderr") flags+=("--api-version=") @@ -1874,6 +1898,8 @@ _kubectl_rollout_pause() two_word_flags+=("-f") flags_with_completion+=("-f") flags_completion+=("__handle_filename_extension_flag json|yaml|yml") + flags+=("--recursive") + flags+=("-R") flags+=("--alsologtostderr") flags+=("--api-version=") flags+=("--certificate-authority=") @@ -1919,6 +1945,8 @@ _kubectl_rollout_resume() two_word_flags+=("-f") flags_with_completion+=("-f") flags_completion+=("__handle_filename_extension_flag json|yaml|yml") + flags+=("--recursive") + flags+=("-R") flags+=("--alsologtostderr") flags+=("--api-version=") flags+=("--certificate-authority=") @@ -1964,6 +1992,8 @@ _kubectl_rollout_undo() two_word_flags+=("-f") flags_with_completion+=("-f") flags_completion+=("__handle_filename_extension_flag json|yaml|yml") + flags+=("--recursive") + flags+=("-R") flags+=("--to-revision=") flags+=("--alsologtostderr") flags+=("--api-version=") @@ -2061,6 +2091,8 @@ _kubectl_label() flags+=("--output-version=") flags+=("--overwrite") flags+=("--record") + flags+=("--recursive") + flags+=("-R") flags+=("--resource-version=") flags+=("--selector=") two_word_flags+=("-l") @@ -2146,6 +2178,8 @@ _kubectl_annotate() flags+=("--output-version=") flags+=("--overwrite") flags+=("--record") + flags+=("--recursive") + flags+=("-R") flags+=("--resource-version=") flags+=("--selector=") two_word_flags+=("-l") @@ -2738,6 +2772,8 @@ _kubectl_convert() flags+=("--output=") two_word_flags+=("-o") flags+=("--output-version=") + flags+=("--recursive") + flags+=("-R") flags+=("--schema-cache-dir=") flags+=("--show-all") flags+=("-a") diff --git a/docs/man/man1/kubectl-annotate.1 b/docs/man/man1/kubectl-annotate.1 index 73e20129bcc..5ce78a4ac63 100644 --- a/docs/man/man1/kubectl-annotate.1 +++ b/docs/man/man1/kubectl-annotate.1 @@ -59,6 +59,10 @@ horizontalpodautoscalers (hpa), resourcequotas (quota) or secrets. \fB\-\-record\fP=false Record current kubectl command in the resource annotation. +.PP +\fB\-R\fP, \fB\-\-recursive\fP=false + If true, process directory recursively. + .PP \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. diff --git a/docs/man/man1/kubectl-apply.1 b/docs/man/man1/kubectl-apply.1 index dc4eef3b28b..4046f332488 100644 --- a/docs/man/man1/kubectl-apply.1 +++ b/docs/man/man1/kubectl-apply.1 @@ -33,6 +33,10 @@ JSON and YAML formats are accepted. \fB\-\-record\fP=false Record current kubectl command in the resource annotation. +.PP +\fB\-R\fP, \fB\-\-recursive\fP=false + If true, process directory recursively. + .PP \fB\-\-schema\-cache\-dir\fP="\~/.kube/schema" If non\-empty, load/store cached API schemas in this directory, default is '$HOME/.kube/schema' diff --git a/docs/man/man1/kubectl-autoscale.1 b/docs/man/man1/kubectl-autoscale.1 index da4f50c8a8e..c7674eb49d2 100644 --- a/docs/man/man1/kubectl-autoscale.1 +++ b/docs/man/man1/kubectl-autoscale.1 @@ -67,6 +67,10 @@ An autoscaler can automatically increase or decrease number of pods deployed wit \fB\-\-record\fP=false Record current kubectl command in the resource annotation. +.PP +\fB\-R\fP, \fB\-\-recursive\fP=false + If true, process directory recursively. + .PP \fB\-\-save\-config\fP=false If true, the configuration of current object will be saved in its annotation. This is useful when you want to perform kubectl apply on this object in the future. diff --git a/docs/man/man1/kubectl-convert.1 b/docs/man/man1/kubectl-convert.1 index 6e29e6aafba..4008eac3bec 100644 --- a/docs/man/man1/kubectl-convert.1 +++ b/docs/man/man1/kubectl-convert.1 @@ -49,6 +49,10 @@ to change to output destination. \fB\-\-output\-version\fP="" Output the formatted object with the given group version (for ex: 'extensions/v1beta1'). +.PP +\fB\-R\fP, \fB\-\-recursive\fP=false + If true, process directory recursively. + .PP \fB\-\-schema\-cache\-dir\fP="\~/.kube/schema" If non\-empty, load/store cached API schemas in this directory, default is '$HOME/.kube/schema' diff --git a/docs/man/man1/kubectl-create.1 b/docs/man/man1/kubectl-create.1 index 942410c5587..415e1bf2e54 100644 --- a/docs/man/man1/kubectl-create.1 +++ b/docs/man/man1/kubectl-create.1 @@ -32,6 +32,10 @@ JSON and YAML formats are accepted. \fB\-\-record\fP=false Record current kubectl command in the resource annotation. +.PP +\fB\-R\fP, \fB\-\-recursive\fP=false + If true, process directory recursively. + .PP \fB\-\-save\-config\fP=false If true, the configuration of current object will be saved in its annotation. This is useful when you want to perform kubectl apply on this object in the future. diff --git a/docs/man/man1/kubectl-delete.1 b/docs/man/man1/kubectl-delete.1 index 82341aaa267..cfb9be889ec 100644 --- a/docs/man/man1/kubectl-delete.1 +++ b/docs/man/man1/kubectl-delete.1 @@ -52,6 +52,10 @@ will be lost along with the rest of the resource. \fB\-o\fP, \fB\-\-output\fP="" Output mode. Use "\-o name" for shorter output (resource/name). +.PP +\fB\-R\fP, \fB\-\-recursive\fP=false + If true, process directory recursively. + .PP \fB\-l\fP, \fB\-\-selector\fP="" Selector (label query) to filter on. diff --git a/docs/man/man1/kubectl-describe.1 b/docs/man/man1/kubectl-describe.1 index 9a2a912de91..6c435ae4d9d 100644 --- a/docs/man/man1/kubectl-describe.1 +++ b/docs/man/man1/kubectl-describe.1 @@ -39,6 +39,10 @@ componentstatuses (cs), endpoints (ep), and secrets. \fB\-f\fP, \fB\-\-filename\fP=[] Filename, directory, or URL to a file containing the resource to describe +.PP +\fB\-R\fP, \fB\-\-recursive\fP=false + If true, process directory recursively. + .PP \fB\-l\fP, \fB\-\-selector\fP="" Selector (label query) to filter on diff --git a/docs/man/man1/kubectl-edit.1 b/docs/man/man1/kubectl-edit.1 index 88b22032089..fa04d8843e2 100644 --- a/docs/man/man1/kubectl-edit.1 +++ b/docs/man/man1/kubectl-edit.1 @@ -54,6 +54,10 @@ saved copy to include the latest resource version. \fB\-\-record\fP=false Record current kubectl command in the resource annotation. +.PP +\fB\-R\fP, \fB\-\-recursive\fP=false + If true, process directory recursively. + .PP \fB\-\-save\-config\fP=false If true, the configuration of current object will be saved in its annotation. This is useful when you want to perform kubectl apply on this object in the future. diff --git a/docs/man/man1/kubectl-expose.1 b/docs/man/man1/kubectl-expose.1 index efadb138f5a..8fba94a3453 100644 --- a/docs/man/man1/kubectl-expose.1 +++ b/docs/man/man1/kubectl-expose.1 @@ -91,6 +91,10 @@ labels are specified, the new service will re\-use the labels from the resource \fB\-\-record\fP=false Record current kubectl command in the resource annotation. +.PP +\fB\-R\fP, \fB\-\-recursive\fP=false + If true, process directory recursively. + .PP \fB\-\-save\-config\fP=false If true, the configuration of current object will be saved in its annotation. This is useful when you want to perform kubectl apply on this object in the future. diff --git a/docs/man/man1/kubectl-get.1 b/docs/man/man1/kubectl-get.1 index 24bc3999a45..d65518e41c6 100644 --- a/docs/man/man1/kubectl-get.1 +++ b/docs/man/man1/kubectl-get.1 @@ -58,6 +58,10 @@ of the \-\-template flag, you can filter the attributes of the fetched resource( \fB\-\-output\-version\fP="" Output the formatted object with the given group version (for ex: 'extensions/v1beta1'). +.PP +\fB\-R\fP, \fB\-\-recursive\fP=false + If true, process directory recursively. + .PP \fB\-l\fP, \fB\-\-selector\fP="" Selector (label query) to filter on diff --git a/docs/man/man1/kubectl-label.1 b/docs/man/man1/kubectl-label.1 index 478104c6042..1078be01fea 100644 --- a/docs/man/man1/kubectl-label.1 +++ b/docs/man/man1/kubectl-label.1 @@ -56,6 +56,10 @@ If \-\-resource\-version is specified, then updates will use this resource versi \fB\-\-record\fP=false Record current kubectl command in the resource annotation. +.PP +\fB\-R\fP, \fB\-\-recursive\fP=false + If true, process directory recursively. + .PP \fB\-\-resource\-version\fP="" 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. diff --git a/docs/man/man1/kubectl-patch.1 b/docs/man/man1/kubectl-patch.1 index 2d39770ee30..7b605320744 100644 --- a/docs/man/man1/kubectl-patch.1 +++ b/docs/man/man1/kubectl-patch.1 @@ -40,6 +40,10 @@ Please refer to the models in \fB\-\-record\fP=false Record current kubectl command in the resource annotation. +.PP +\fB\-R\fP, \fB\-\-recursive\fP=false + If true, process directory recursively. + .PP \fB\-\-type\fP="strategic" The type of patch being provided; one of [json merge strategic] diff --git a/docs/man/man1/kubectl-replace.1 b/docs/man/man1/kubectl-replace.1 index fe8bdecfc56..d86ead1c527 100644 --- a/docs/man/man1/kubectl-replace.1 +++ b/docs/man/man1/kubectl-replace.1 @@ -50,6 +50,10 @@ Please refer to the models in \fB\-\-record\fP=false Record current kubectl command in the resource annotation. +.PP +\fB\-R\fP, \fB\-\-recursive\fP=false + If true, process directory recursively. + .PP \fB\-\-save\-config\fP=false If true, the configuration of current object will be saved in its annotation. This is useful when you want to perform kubectl apply on this object in the future. diff --git a/docs/man/man1/kubectl-rollout-history.1 b/docs/man/man1/kubectl-rollout-history.1 index 03e98b36cf3..f776516ab1f 100644 --- a/docs/man/man1/kubectl-rollout-history.1 +++ b/docs/man/man1/kubectl-rollout-history.1 @@ -21,6 +21,10 @@ View previous rollout revisions and configurations. \fB\-f\fP, \fB\-\-filename\fP=[] Filename, directory, or URL to a file identifying the resource to get from a server. +.PP +\fB\-R\fP, \fB\-\-recursive\fP=false + If true, process directory recursively. + .PP \fB\-\-revision\fP=0 See the details, including podTemplate of the revision specified diff --git a/docs/man/man1/kubectl-rollout-pause.1 b/docs/man/man1/kubectl-rollout-pause.1 index af42e5826ff..b97555a05d2 100644 --- a/docs/man/man1/kubectl-rollout-pause.1 +++ b/docs/man/man1/kubectl-rollout-pause.1 @@ -26,6 +26,10 @@ Currently only deployments support being paused. \fB\-f\fP, \fB\-\-filename\fP=[] Filename, directory, or URL to a file identifying the resource to get from a server. +.PP +\fB\-R\fP, \fB\-\-recursive\fP=false + If true, process directory recursively. + .SH OPTIONS INHERITED FROM PARENT COMMANDS .PP diff --git a/docs/man/man1/kubectl-rollout-resume.1 b/docs/man/man1/kubectl-rollout-resume.1 index 8509571629e..9a7868a0b66 100644 --- a/docs/man/man1/kubectl-rollout-resume.1 +++ b/docs/man/man1/kubectl-rollout-resume.1 @@ -26,6 +26,10 @@ Currently only deployments support being resumed. \fB\-f\fP, \fB\-\-filename\fP=[] Filename, directory, or URL to a file identifying the resource to get from a server. +.PP +\fB\-R\fP, \fB\-\-recursive\fP=false + If true, process directory recursively. + .SH OPTIONS INHERITED FROM PARENT COMMANDS .PP diff --git a/docs/man/man1/kubectl-rollout-undo.1 b/docs/man/man1/kubectl-rollout-undo.1 index 20807f7ed34..40317a8d290 100644 --- a/docs/man/man1/kubectl-rollout-undo.1 +++ b/docs/man/man1/kubectl-rollout-undo.1 @@ -21,6 +21,10 @@ Rollback to a previous rollout. \fB\-f\fP, \fB\-\-filename\fP=[] Filename, directory, or URL to a file identifying the resource to get from a server. +.PP +\fB\-R\fP, \fB\-\-recursive\fP=false + If true, process directory recursively. + .PP \fB\-\-to\-revision\fP=0 The revision to rollback to. Default to 0 (last revision). diff --git a/docs/man/man1/kubectl-scale.1 b/docs/man/man1/kubectl-scale.1 index d0f7c075bd7..decf06f4237 100644 --- a/docs/man/man1/kubectl-scale.1 +++ b/docs/man/man1/kubectl-scale.1 @@ -39,6 +39,10 @@ scale is sent to the server. \fB\-\-record\fP=false Record current kubectl command in the resource annotation. +.PP +\fB\-R\fP, \fB\-\-recursive\fP=false + If true, process directory recursively. + .PP \fB\-\-replicas\fP=\-1 The new desired number of replicas. Required. diff --git a/docs/man/man1/kubectl-stop.1 b/docs/man/man1/kubectl-stop.1 index 98e6c0cdf0b..c4b760ae95e 100644 --- a/docs/man/man1/kubectl-stop.1 +++ b/docs/man/man1/kubectl-stop.1 @@ -45,6 +45,10 @@ If the resource is scalable it will be scaled to 0 before deletion. \fB\-o\fP, \fB\-\-output\fP="" Output mode. Use "\-o name" for shorter output (resource/name). +.PP +\fB\-R\fP, \fB\-\-recursive\fP=false + If true, process directory recursively. + .PP \fB\-l\fP, \fB\-\-selector\fP="" Selector (label query) to filter on. diff --git a/docs/user-guide/kubectl/kubectl_annotate.md b/docs/user-guide/kubectl/kubectl_annotate.md index 5772e73d104..cba6858bf89 100644 --- a/docs/user-guide/kubectl/kubectl_annotate.md +++ b/docs/user-guide/kubectl/kubectl_annotate.md @@ -89,6 +89,7 @@ kubectl annotate pods foo description- --output-version="": Output the formatted object with the given group version (for ex: 'extensions/v1beta1'). --overwrite[=false]: If true, allow annotations to be overwritten, otherwise reject annotation updates that overwrite existing annotations. --record[=false]: Record current kubectl command in the resource annotation. + -R, --recursive[=false]: If true, process directory recursively. --resource-version="": If non-empty, the annotation update will only succeed if this is the current resource-version for the object. Only valid when specifying a single resource. -l, --selector="": Selector (label query) to filter on -a, --show-all[=false]: When printing, show all resources (default hide terminated pods.) @@ -128,7 +129,7 @@ kubectl annotate pods foo description- * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra on 2-Mar-2016 +###### Auto generated by spf13/cobra on 28-Mar-2016 [![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_apply.md b/docs/user-guide/kubectl/kubectl_apply.md index c7617e5e35f..5e65d0412ee 100644 --- a/docs/user-guide/kubectl/kubectl_apply.md +++ b/docs/user-guide/kubectl/kubectl_apply.md @@ -64,6 +64,7 @@ cat pod.json | kubectl apply -f - -f, --filename=[]: Filename, directory, or URL to file that contains the configuration to apply -o, --output="": Output mode. Use "-o name" for shorter output (resource/name). --record[=false]: Record current kubectl command in the resource annotation. + -R, --recursive[=false]: If true, process directory recursively. --schema-cache-dir="~/.kube/schema": If non-empty, load/store cached API schemas in this directory, default is '$HOME/.kube/schema' --validate[=true]: If true, use a schema to validate the input before sending it ``` @@ -99,7 +100,7 @@ cat pod.json | kubectl apply -f - * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra on 2-Mar-2016 +###### Auto generated by spf13/cobra on 28-Mar-2016 [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_apply.md?pixel)]() diff --git a/docs/user-guide/kubectl/kubectl_autoscale.md b/docs/user-guide/kubectl/kubectl_autoscale.md index d7dcb0d9638..d8b71219e86 100644 --- a/docs/user-guide/kubectl/kubectl_autoscale.md +++ b/docs/user-guide/kubectl/kubectl_autoscale.md @@ -72,6 +72,7 @@ kubectl autoscale rc foo --max=5 --cpu-percent=80 -o, --output="": Output format. One of: json|yaml|wide|name|go-template=...|go-template-file=...|jsonpath=...|jsonpath-file=... See golang template [http://golang.org/pkg/text/template/#pkg-overview] and jsonpath template [http://releases.k8s.io/HEAD/docs/user-guide/jsonpath.md]. --output-version="": Output the formatted object with the given group version (for ex: 'extensions/v1beta1'). --record[=false]: Record current kubectl command in the resource annotation. + -R, --recursive[=false]: If true, process directory recursively. --save-config[=false]: If true, the configuration of current object will be saved in its annotation. This is useful when you want to perform kubectl apply on this object in the future. -a, --show-all[=false]: When printing, show all resources (default hide terminated pods.) --show-labels[=false]: When printing, show all labels as the last column (default hide labels column) @@ -110,7 +111,7 @@ kubectl autoscale rc foo --max=5 --cpu-percent=80 * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra on 22-Mar-2016 +###### Auto generated by spf13/cobra on 28-Mar-2016 [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_autoscale.md?pixel)]() diff --git a/docs/user-guide/kubectl/kubectl_convert.md b/docs/user-guide/kubectl/kubectl_convert.md index cde025d80e5..5ffcca93fee 100644 --- a/docs/user-guide/kubectl/kubectl_convert.md +++ b/docs/user-guide/kubectl/kubectl_convert.md @@ -77,6 +77,7 @@ kubectl convert -f . | kubectl create -f - --no-headers[=false]: When using the default output, don't print headers. -o, --output="": Output format. One of: json|yaml|wide|name|go-template=...|go-template-file=...|jsonpath=...|jsonpath-file=... See golang template [http://golang.org/pkg/text/template/#pkg-overview] and jsonpath template [http://releases.k8s.io/HEAD/docs/user-guide/jsonpath.md]. --output-version="": Output the formatted object with the given group version (for ex: 'extensions/v1beta1'). + -R, --recursive[=false]: If true, process directory recursively. --schema-cache-dir="~/.kube/schema": If non-empty, load/store cached API schemas in this directory, default is '$HOME/.kube/schema' -a, --show-all[=false]: When printing, show all resources (default hide terminated pods.) --show-labels[=false]: When printing, show all labels as the last column (default hide labels column) @@ -116,7 +117,7 @@ kubectl convert -f . | kubectl create -f - * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra on 2-Mar-2016 +###### Auto generated by spf13/cobra on 28-Mar-2016 [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_convert.md?pixel)]() diff --git a/docs/user-guide/kubectl/kubectl_create.md b/docs/user-guide/kubectl/kubectl_create.md index c9077bfa8e7..b893e278468 100644 --- a/docs/user-guide/kubectl/kubectl_create.md +++ b/docs/user-guide/kubectl/kubectl_create.md @@ -63,6 +63,7 @@ cat pod.json | kubectl create -f - -f, --filename=[]: Filename, directory, or URL to file to use to create the resource -o, --output="": Output mode. Use "-o name" for shorter output (resource/name). --record[=false]: Record current kubectl command in the resource annotation. + -R, --recursive[=false]: If true, process directory recursively. --save-config[=false]: If true, the configuration of current object will be saved in its annotation. This is useful when you want to perform kubectl apply on this object in the future. --schema-cache-dir="~/.kube/schema": If non-empty, load/store cached API schemas in this directory, default is '$HOME/.kube/schema' --validate[=true]: If true, use a schema to validate the input before sending it @@ -103,7 +104,7 @@ cat pod.json | kubectl create -f - * [kubectl create secret](kubectl_create_secret.md) - Create a secret using specified subcommand. * [kubectl create serviceaccount](kubectl_create_serviceaccount.md) - Create a service account with the specified name. -###### Auto generated by spf13/cobra on 2-Mar-2016 +###### Auto generated by spf13/cobra on 28-Mar-2016 [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_create.md?pixel)]() diff --git a/docs/user-guide/kubectl/kubectl_delete.md b/docs/user-guide/kubectl/kubectl_delete.md index a9e631a7ef0..c6e546832ae 100644 --- a/docs/user-guide/kubectl/kubectl_delete.md +++ b/docs/user-guide/kubectl/kubectl_delete.md @@ -84,6 +84,7 @@ kubectl delete pods --all --grace-period=-1: Period of time in seconds given to the resource to terminate gracefully. Ignored if negative. --ignore-not-found[=false]: Treat "resource not found" as a successful delete. Defaults to "true" when --all is specified. -o, --output="": Output mode. Use "-o name" for shorter output (resource/name). + -R, --recursive[=false]: If true, process directory recursively. -l, --selector="": Selector (label query) to filter on. --timeout=0: The length of time to wait before giving up on a delete, zero means determine a timeout from the size of the object ``` @@ -119,7 +120,7 @@ kubectl delete pods --all * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra on 2-Mar-2016 +###### Auto generated by spf13/cobra on 28-Mar-2016 [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_delete.md?pixel)]() diff --git a/docs/user-guide/kubectl/kubectl_describe.md b/docs/user-guide/kubectl/kubectl_describe.md index 4438aba4ca4..2a55ebe7b13 100644 --- a/docs/user-guide/kubectl/kubectl_describe.md +++ b/docs/user-guide/kubectl/kubectl_describe.md @@ -86,6 +86,7 @@ kubectl describe pods frontend ``` -f, --filename=[]: Filename, directory, or URL to a file containing the resource to describe + -R, --recursive[=false]: If true, process directory recursively. -l, --selector="": Selector (label query) to filter on ``` @@ -120,7 +121,7 @@ kubectl describe pods frontend * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra on 25-Mar-2016 +###### Auto generated by spf13/cobra on 28-Mar-2016 [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_describe.md?pixel)]() diff --git a/docs/user-guide/kubectl/kubectl_edit.md b/docs/user-guide/kubectl/kubectl_edit.md index 350c6893acf..b7a7875c198 100644 --- a/docs/user-guide/kubectl/kubectl_edit.md +++ b/docs/user-guide/kubectl/kubectl_edit.md @@ -83,6 +83,7 @@ kubectl edit (RESOURCE/NAME | -f FILENAME) -o, --output="yaml": Output format. One of: yaml|json. --output-version="": Output the formatted object with the given group version (for ex: 'extensions/v1beta1'). --record[=false]: Record current kubectl command in the resource annotation. + -R, --recursive[=false]: If true, process directory recursively. --save-config[=false]: If true, the configuration of current object will be saved in its annotation. This is useful when you want to perform kubectl apply on this object in the future. --windows-line-endings[=false]: Use Windows line-endings (default Unix line-endings) ``` @@ -118,7 +119,7 @@ kubectl edit (RESOURCE/NAME | -f FILENAME) * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra on 2-Mar-2016 +###### Auto generated by spf13/cobra on 28-Mar-2016 [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_edit.md?pixel)]() diff --git a/docs/user-guide/kubectl/kubectl_expose.md b/docs/user-guide/kubectl/kubectl_expose.md index 8e67cb9ead9..d8daf9a1f0c 100644 --- a/docs/user-guide/kubectl/kubectl_expose.md +++ b/docs/user-guide/kubectl/kubectl_expose.md @@ -95,6 +95,7 @@ kubectl expose deployment nginx --port=80 --target-port=8000 --port="": The port that the service should serve on. Copied from the resource being exposed, if unspecified --protocol="TCP": The network protocol for the service to be created. Default is 'tcp'. --record[=false]: Record current kubectl command in the resource annotation. + -R, --recursive[=false]: If true, process directory recursively. --save-config[=false]: If true, the configuration of current object will be saved in its annotation. This is useful when you want to perform kubectl apply on this object in the future. --selector="": A label selector to use for this service. Only equality-based selector requirements are supported. If empty (the default) infer the selector from the replication controller or replica set. --session-affinity="": If non-empty, set the session affinity for the service to this; legal values: 'None', 'ClientIP' @@ -137,7 +138,7 @@ kubectl expose deployment nginx --port=80 --target-port=8000 * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra on 22-Mar-2016 +###### Auto generated by spf13/cobra on 28-Mar-2016 [![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 c5fd9e4a19a..643bf54e758 100644 --- a/docs/user-guide/kubectl/kubectl_get.md +++ b/docs/user-guide/kubectl/kubectl_get.md @@ -92,6 +92,7 @@ kubectl get rc/web service/frontend pods/web-pod-13je7 --no-headers[=false]: When using the default output, don't print headers. -o, --output="": Output format. One of: json|yaml|wide|name|go-template=...|go-template-file=...|jsonpath=...|jsonpath-file=... See golang template [http://golang.org/pkg/text/template/#pkg-overview] and jsonpath template [http://releases.k8s.io/HEAD/docs/user-guide/jsonpath.md]. --output-version="": Output the formatted object with the given group version (for ex: 'extensions/v1beta1'). + -R, --recursive[=false]: If true, process directory recursively. -l, --selector="": Selector (label query) to filter on -a, --show-all[=false]: When printing, show all resources (default hide terminated pods.) --show-labels[=false]: When printing, show all labels as the last column (default hide labels column) @@ -132,7 +133,7 @@ kubectl get rc/web service/frontend pods/web-pod-13je7 * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra on 25-Mar-2016 +###### Auto generated by spf13/cobra on 28-Mar-2016 [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_get.md?pixel)]() diff --git a/docs/user-guide/kubectl/kubectl_label.md b/docs/user-guide/kubectl/kubectl_label.md index 370a150632c..716de827f8c 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- --output-version="": Output the formatted object with the given group version (for ex: 'extensions/v1beta1'). --overwrite[=false]: If true, allow labels to be overwritten, otherwise reject label updates that overwrite existing labels. --record[=false]: Record current kubectl command in the resource annotation. + -R, --recursive[=false]: If true, process directory recursively. --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.) @@ -122,7 +123,7 @@ kubectl label pods foo bar- * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra on 2-Mar-2016 +###### Auto generated by spf13/cobra on 28-Mar-2016 [![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_patch.md b/docs/user-guide/kubectl/kubectl_patch.md index 99d48d2ee4a..afe58dcd572 100644 --- a/docs/user-guide/kubectl/kubectl_patch.md +++ b/docs/user-guide/kubectl/kubectl_patch.md @@ -73,6 +73,7 @@ kubectl patch pod valid-pod -type='json' -p='[{"op": "replace", "path": "/spec/c -o, --output="": Output mode. Use "-o name" for shorter output (resource/name). -p, --patch="": The patch to be applied to the resource JSON file. --record[=false]: Record current kubectl command in the resource annotation. + -R, --recursive[=false]: If true, process directory recursively. --type="strategic": The type of patch being provided; one of [json merge strategic] ``` @@ -107,7 +108,7 @@ kubectl patch pod valid-pod -type='json' -p='[{"op": "replace", "path": "/spec/c * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra on 2-Mar-2016 +###### Auto generated by spf13/cobra on 28-Mar-2016 [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_patch.md?pixel)]() diff --git a/docs/user-guide/kubectl/kubectl_replace.md b/docs/user-guide/kubectl/kubectl_replace.md index 764f8c0cb50..6302d5e939d 100644 --- a/docs/user-guide/kubectl/kubectl_replace.md +++ b/docs/user-guide/kubectl/kubectl_replace.md @@ -76,6 +76,7 @@ kubectl replace --force -f ./pod.json --grace-period=-1: Only relevant during a force replace. Period of time in seconds given to the old resource to terminate gracefully. Ignored if negative. -o, --output="": Output mode. Use "-o name" for shorter output (resource/name). --record[=false]: Record current kubectl command in the resource annotation. + -R, --recursive[=false]: If true, process directory recursively. --save-config[=false]: If true, the configuration of current object will be saved in its annotation. This is useful when you want to perform kubectl apply on this object in the future. --schema-cache-dir="~/.kube/schema": If non-empty, load/store cached API schemas in this directory, default is '$HOME/.kube/schema' --timeout=0: Only relevant during a force replace. The length of time to wait before giving up on a delete of the old resource, zero means determine a timeout from the size of the object @@ -113,7 +114,7 @@ kubectl replace --force -f ./pod.json * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra on 2-Mar-2016 +###### Auto generated by spf13/cobra on 28-Mar-2016 [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_replace.md?pixel)]() diff --git a/docs/user-guide/kubectl/kubectl_rollout_history.md b/docs/user-guide/kubectl/kubectl_rollout_history.md index 9c97642d2d8..5ee18b5205b 100644 --- a/docs/user-guide/kubectl/kubectl_rollout_history.md +++ b/docs/user-guide/kubectl/kubectl_rollout_history.md @@ -59,6 +59,7 @@ kubectl rollout history deployment/abc --revision=3 ``` -f, --filename=[]: Filename, directory, or URL to a file identifying the resource to get from a server. + -R, --recursive[=false]: If true, process directory recursively. --revision=0: See the details, including podTemplate of the revision specified ``` @@ -93,7 +94,7 @@ kubectl rollout history deployment/abc --revision=3 * [kubectl rollout](kubectl_rollout.md) - rollout manages a deployment -###### Auto generated by spf13/cobra on 21-Mar-2016 +###### Auto generated by spf13/cobra on 28-Mar-2016 [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_rollout_history.md?pixel)]() diff --git a/docs/user-guide/kubectl/kubectl_rollout_pause.md b/docs/user-guide/kubectl/kubectl_rollout_pause.md index 558a0fd26ae..022d060b851 100644 --- a/docs/user-guide/kubectl/kubectl_rollout_pause.md +++ b/docs/user-guide/kubectl/kubectl_rollout_pause.md @@ -62,6 +62,7 @@ kubectl rollout pause deployment/nginx ``` -f, --filename=[]: Filename, directory, or URL to a file identifying the resource to get from a server. + -R, --recursive[=false]: If true, process directory recursively. ``` ### Options inherited from parent commands @@ -95,7 +96,7 @@ kubectl rollout pause deployment/nginx * [kubectl rollout](kubectl_rollout.md) - rollout manages a deployment -###### Auto generated by spf13/cobra on 2-Mar-2016 +###### Auto generated by spf13/cobra on 28-Mar-2016 [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_rollout_pause.md?pixel)]() diff --git a/docs/user-guide/kubectl/kubectl_rollout_resume.md b/docs/user-guide/kubectl/kubectl_rollout_resume.md index fa4338f2487..9787b2f7245 100644 --- a/docs/user-guide/kubectl/kubectl_rollout_resume.md +++ b/docs/user-guide/kubectl/kubectl_rollout_resume.md @@ -60,6 +60,7 @@ kubectl rollout resume deployment/nginx ``` -f, --filename=[]: Filename, directory, or URL to a file identifying the resource to get from a server. + -R, --recursive[=false]: If true, process directory recursively. ``` ### Options inherited from parent commands @@ -93,7 +94,7 @@ kubectl rollout resume deployment/nginx * [kubectl rollout](kubectl_rollout.md) - rollout manages a deployment -###### Auto generated by spf13/cobra on 2-Mar-2016 +###### Auto generated by spf13/cobra on 28-Mar-2016 [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_rollout_resume.md?pixel)]() diff --git a/docs/user-guide/kubectl/kubectl_rollout_undo.md b/docs/user-guide/kubectl/kubectl_rollout_undo.md index e8db87b7e9c..c50e0f25fa8 100644 --- a/docs/user-guide/kubectl/kubectl_rollout_undo.md +++ b/docs/user-guide/kubectl/kubectl_rollout_undo.md @@ -59,6 +59,7 @@ kubectl rollout undo deployment/abc --to-revision=3 ``` -f, --filename=[]: Filename, directory, or URL to a file identifying the resource to get from a server. + -R, --recursive[=false]: If true, process directory recursively. --to-revision=0: The revision to rollback to. Default to 0 (last revision). ``` @@ -93,7 +94,7 @@ kubectl rollout undo deployment/abc --to-revision=3 * [kubectl rollout](kubectl_rollout.md) - rollout manages a deployment -###### Auto generated by spf13/cobra on 21-Mar-2016 +###### Auto generated by spf13/cobra on 28-Mar-2016 [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_rollout_undo.md?pixel)]() diff --git a/docs/user-guide/kubectl/kubectl_scale.md b/docs/user-guide/kubectl/kubectl_scale.md index bd5d8afcf59..4acffcb2114 100644 --- a/docs/user-guide/kubectl/kubectl_scale.md +++ b/docs/user-guide/kubectl/kubectl_scale.md @@ -76,6 +76,7 @@ kubectl scale --replicas=3 job/cron -f, --filename=[]: Filename, directory, or URL to a file identifying the resource to set a new size -o, --output="": Output mode. Use "-o name" for shorter output (resource/name). --record[=false]: Record current kubectl command in the resource annotation. + -R, --recursive[=false]: If true, process directory recursively. --replicas=-1: The new desired number of replicas. Required. --resource-version="": Precondition for resource version. Requires that the current resource version match this value in order to scale. --timeout=0: The length of time to wait before giving up on a scale operation, zero means don't wait. @@ -112,7 +113,7 @@ kubectl scale --replicas=3 job/cron * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra on 22-Mar-2016 +###### Auto generated by spf13/cobra on 28-Mar-2016 [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_scale.md?pixel)]() diff --git a/pkg/kubectl/cmd/annotate.go b/pkg/kubectl/cmd/annotate.go index e4273c0a4e3..1737152b877 100644 --- a/pkg/kubectl/cmd/annotate.go +++ b/pkg/kubectl/cmd/annotate.go @@ -52,6 +52,8 @@ type AnnotateOptions struct { f *cmdutil.Factory out io.Writer cmd *cobra.Command + + recursive bool } const ( @@ -114,6 +116,7 @@ func NewCmdAnnotate(f *cmdutil.Factory, out io.Writer) *cobra.Command { cmd.Flags().StringVar(&options.resourceVersion, "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.") usage := "Filename, directory, or URL to a file identifying the resource to update the annotation" kubectl.AddJsonFilenameFlag(cmd, &options.filenames, usage) + cmdutil.AddRecursiveFlag(cmd, &options.recursive) cmdutil.AddRecordFlag(cmd) return cmd } @@ -162,7 +165,7 @@ func (o *AnnotateOptions) Complete(f *cmdutil.Factory, out io.Writer, cmd *cobra o.builder = resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). ContinueOnError(). NamespaceParam(namespace).DefaultNamespace(). - FilenameParam(enforceNamespace, o.filenames...). + FilenameParam(enforceNamespace, o.recursive, o.filenames...). SelectorParam(o.selector). ResourceTypeOrNameArgs(o.all, o.resources...). Flatten(). diff --git a/pkg/kubectl/cmd/apply.go b/pkg/kubectl/cmd/apply.go index 3ca6c8e4f70..7c25de7efcc 100644 --- a/pkg/kubectl/cmd/apply.go +++ b/pkg/kubectl/cmd/apply.go @@ -35,6 +35,7 @@ import ( // add them here instead of referencing the cmd.Flags() type ApplyOptions struct { Filenames []string + Recursive bool } const ( @@ -68,6 +69,7 @@ func NewCmdApply(f *cmdutil.Factory, out io.Writer) *cobra.Command { kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage) cmd.MarkFlagRequired("filename") cmdutil.AddValidateFlags(cmd) + cmdutil.AddRecursiveFlag(cmd, &options.Recursive) cmdutil.AddOutputFlagsForMutation(cmd) cmdutil.AddRecordFlag(cmd) return cmd @@ -98,7 +100,7 @@ func RunApply(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, options *Ap Schema(schema). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). - FilenameParam(enforceNamespace, options.Filenames...). + FilenameParam(enforceNamespace, options.Recursive, options.Filenames...). Flatten(). Do() err = r.Err() diff --git a/pkg/kubectl/cmd/autoscale.go b/pkg/kubectl/cmd/autoscale.go index 0541f75272e..4ceb59447fc 100644 --- a/pkg/kubectl/cmd/autoscale.go +++ b/pkg/kubectl/cmd/autoscale.go @@ -28,6 +28,13 @@ import ( "github.com/spf13/cobra" ) +// AutoscaleOptions is the start of the data required to perform the operation. As new fields are added, add them here instead of +// referencing the cmd.Flags() +type AutoscaleOptions struct { + Filenames []string + Recursive bool +} + const ( autoscaleLong = `Creates an autoscaler that automatically chooses and sets the number of pods that run in a kubernetes cluster. @@ -42,14 +49,15 @@ kubectl autoscale rc foo --max=5 --cpu-percent=80` ) func NewCmdAutoscale(f *cmdutil.Factory, out io.Writer) *cobra.Command { - filenames := []string{} + options := &AutoscaleOptions{} + cmd := &cobra.Command{ Use: "autoscale (-f FILENAME | TYPE NAME | TYPE/NAME) [--min=MINPODS] --max=MAXPODS [--cpu-percent=CPU] [flags]", Short: "Auto-scale a Deployment, ReplicaSet, or ReplicationController", Long: autoscaleLong, Example: autoscaleExample, Run: func(cmd *cobra.Command, args []string) { - err := RunAutoscale(f, out, cmd, args, filenames) + err := RunAutoscale(f, out, cmd, args, options) cmdutil.CheckErr(err) }, } @@ -62,13 +70,14 @@ func NewCmdAutoscale(f *cmdutil.Factory, out io.Writer) *cobra.Command { cmd.Flags().String("name", "", "The name for the newly created object. If not specified, the name of the input resource will be used.") cmd.Flags().Bool("dry-run", false, "If true, only print the object that would be sent, without creating it.") usage := "Filename, directory, or URL to a file identifying the resource to autoscale." - kubectl.AddJsonFilenameFlag(cmd, &filenames, usage) + kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage) + cmdutil.AddRecursiveFlag(cmd, &options.Recursive) cmdutil.AddApplyAnnotationFlags(cmd) cmdutil.AddRecordFlag(cmd) return cmd } -func RunAutoscale(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, filenames []string) error { +func RunAutoscale(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, options *AutoscaleOptions) error { namespace, enforceNamespace, err := f.DefaultNamespace() if err != nil { return err @@ -83,7 +92,7 @@ func RunAutoscale(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args [] r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). ContinueOnError(). NamespaceParam(namespace).DefaultNamespace(). - FilenameParam(enforceNamespace, filenames...). + FilenameParam(enforceNamespace, options.Recursive, options.Filenames...). ResourceTypeOrNameArgs(false, args...). Flatten(). Do() diff --git a/pkg/kubectl/cmd/convert.go b/pkg/kubectl/cmd/convert.go index ab55f0315da..64959f8f3a4 100644 --- a/pkg/kubectl/cmd/convert.go +++ b/pkg/kubectl/cmd/convert.go @@ -74,6 +74,7 @@ func NewCmdConvert(f *cmdutil.Factory, out io.Writer) *cobra.Command { usage := "Filename, directory, or URL to file to need to get converted." kubectl.AddJsonFilenameFlag(cmd, &options.filenames, usage) + cmdutil.AddRecursiveFlag(cmd, &options.recursive) cmd.MarkFlagRequired("filename") cmdutil.AddValidateFlags(cmd) cmdutil.AddPrinterFlags(cmd) @@ -93,6 +94,8 @@ type ConvertOptions struct { printer kubectl.ResourcePrinter outputVersion unversioned.GroupVersion + + recursive bool } // Complete collects information required to run Convert command from command line. @@ -125,7 +128,7 @@ func (o *ConvertOptions) Complete(f *cmdutil.Factory, out io.Writer, cmd *cobra. } o.builder = o.builder.NamespaceParam(cmdNamespace). ContinueOnError(). - FilenameParam(false, o.filenames...). + FilenameParam(false, o.recursive, o.filenames...). Flatten() // build the printer diff --git a/pkg/kubectl/cmd/create.go b/pkg/kubectl/cmd/create.go index 9ed779eecee..1edecccce04 100644 --- a/pkg/kubectl/cmd/create.go +++ b/pkg/kubectl/cmd/create.go @@ -35,6 +35,7 @@ import ( // referencing the cmd.Flags() type CreateOptions struct { Filenames []string + Recursive bool } const ( @@ -71,6 +72,7 @@ func NewCmdCreate(f *cmdutil.Factory, out io.Writer) *cobra.Command { kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage) cmd.MarkFlagRequired("filename") cmdutil.AddValidateFlags(cmd) + cmdutil.AddRecursiveFlag(cmd, &options.Recursive) cmdutil.AddOutputFlagsForMutation(cmd) cmdutil.AddApplyAnnotationFlags(cmd) cmdutil.AddRecordFlag(cmd) @@ -106,7 +108,7 @@ func RunCreate(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, options *C Schema(schema). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). - FilenameParam(enforceNamespace, options.Filenames...). + FilenameParam(enforceNamespace, options.Recursive, options.Filenames...). Flatten(). Do() err = r.Err() diff --git a/pkg/kubectl/cmd/delete.go b/pkg/kubectl/cmd/delete.go index 99f65d8eac0..e3b1a7eacee 100644 --- a/pkg/kubectl/cmd/delete.go +++ b/pkg/kubectl/cmd/delete.go @@ -35,6 +35,7 @@ import ( // referencing the cmd.Flags() type DeleteOptions struct { Filenames []string + Recursive bool } const ( @@ -91,6 +92,7 @@ func NewCmdDelete(f *cmdutil.Factory, out io.Writer) *cobra.Command { } usage := "Filename, directory, or URL to a file containing the resource to delete." kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage) + cmdutil.AddRecursiveFlag(cmd, &options.Recursive) cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on.") cmd.Flags().Bool("all", false, "[-all] to select all the specified resources.") cmd.Flags().Bool("ignore-not-found", false, "Treat \"resource not found\" as a successful delete. Defaults to \"true\" when --all is specified.") @@ -111,7 +113,7 @@ func RunDelete(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). - FilenameParam(enforceNamespace, options.Filenames...). + FilenameParam(enforceNamespace, options.Recursive, options.Filenames...). SelectorParam(cmdutil.GetFlagString(cmd, "selector")). SelectAllParam(deleteAll). ResourceTypeOrNameArgs(false, args...).RequireObject(false). diff --git a/pkg/kubectl/cmd/describe.go b/pkg/kubectl/cmd/describe.go index 0f780d611a7..ca6854c31aa 100644 --- a/pkg/kubectl/cmd/describe.go +++ b/pkg/kubectl/cmd/describe.go @@ -36,6 +36,7 @@ import ( // referencing the cmd.Flags() type DescribeOptions struct { Filenames []string + Recursive bool } const ( @@ -86,6 +87,7 @@ func NewCmdDescribe(f *cmdutil.Factory, out io.Writer) *cobra.Command { } usage := "Filename, directory, or URL to a file containing the resource to describe" kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage) + cmdutil.AddRecursiveFlag(cmd, &options.Recursive) cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on") return cmd } @@ -105,7 +107,7 @@ func RunDescribe(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []s r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). - FilenameParam(enforceNamespace, options.Filenames...). + FilenameParam(enforceNamespace, options.Recursive, options.Filenames...). SelectorParam(selector). ResourceTypeOrNameArgs(true, args...). Flatten(). diff --git a/pkg/kubectl/cmd/edit.go b/pkg/kubectl/cmd/edit.go index bbc7f23e1b3..8a8a001e98f 100644 --- a/pkg/kubectl/cmd/edit.go +++ b/pkg/kubectl/cmd/edit.go @@ -74,17 +74,25 @@ saved copy to include the latest resource version.` kubectl edit svc/docker-registry --output-version=v1 -o json` ) +// EditOptions is the start of the data required to perform the operation. As new fields are added, add them here instead of +// referencing the cmd.Flags() +type EditOptions struct { + Filenames []string + Recursive bool +} + var errExit = fmt.Errorf("exit directly") func NewCmdEdit(f *cmdutil.Factory, out, errOut io.Writer) *cobra.Command { - filenames := []string{} + options := &EditOptions{} + cmd := &cobra.Command{ Use: "edit (RESOURCE/NAME | -f FILENAME)", Short: "Edit a resource on the server", Long: editLong, Example: fmt.Sprintf(editExample), Run: func(cmd *cobra.Command, args []string) { - err := RunEdit(f, out, errOut, cmd, args, filenames) + err := RunEdit(f, out, errOut, cmd, args, options) if err == errExit { os.Exit(1) } @@ -92,7 +100,8 @@ func NewCmdEdit(f *cmdutil.Factory, out, errOut io.Writer) *cobra.Command { }, } usage := "Filename, directory, or URL to file to use to edit the resource" - kubectl.AddJsonFilenameFlag(cmd, &filenames, usage) + kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage) + cmdutil.AddRecursiveFlag(cmd, &options.Recursive) cmd.Flags().StringP("output", "o", "yaml", "Output format. One of: yaml|json.") cmd.Flags().String("output-version", "", "Output the formatted object with the given group version (for ex: 'extensions/v1beta1').") cmd.Flags().Bool("windows-line-endings", gruntime.GOOS == "windows", "Use Windows line-endings (default Unix line-endings)") @@ -101,7 +110,7 @@ func NewCmdEdit(f *cmdutil.Factory, out, errOut io.Writer) *cobra.Command { return cmd } -func RunEdit(f *cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args []string, filenames []string) error { +func RunEdit(f *cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args []string, options *EditOptions) error { var printer kubectl.ResourcePrinter var ext string switch format := cmdutil.GetFlagString(cmd, "output"); format { @@ -130,7 +139,7 @@ func RunEdit(f *cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). NamespaceParam(cmdNamespace).DefaultNamespace(). - FilenameParam(enforceNamespace, filenames...). + FilenameParam(enforceNamespace, options.Recursive, options.Filenames...). ResourceTypeOrNameArgs(true, args...). Latest(). Flatten(). diff --git a/pkg/kubectl/cmd/expose.go b/pkg/kubectl/cmd/expose.go index 785449382fe..7a79daabab0 100644 --- a/pkg/kubectl/cmd/expose.go +++ b/pkg/kubectl/cmd/expose.go @@ -34,6 +34,7 @@ import ( // referencing the cmd.Flags() type ExposeOptions struct { Filenames []string + Recursive bool } const ( @@ -102,6 +103,7 @@ func NewCmdExposeService(f *cmdutil.Factory, out io.Writer) *cobra.Command { usage := "Filename, directory, or URL to a file identifying the resource to expose a service" kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage) + cmdutil.AddRecursiveFlag(cmd, &options.Recursive) cmdutil.AddApplyAnnotationFlags(cmd) cmdutil.AddRecordFlag(cmd) return cmd @@ -117,7 +119,7 @@ func RunExpose(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). ContinueOnError(). NamespaceParam(namespace).DefaultNamespace(). - FilenameParam(enforceNamespace, options.Filenames...). + FilenameParam(enforceNamespace, options.Recursive, options.Filenames...). ResourceTypeOrNameArgs(false, args...). Flatten(). Do() diff --git a/pkg/kubectl/cmd/get.go b/pkg/kubectl/cmd/get.go index 8dd5f01e13b..0a253f89821 100644 --- a/pkg/kubectl/cmd/get.go +++ b/pkg/kubectl/cmd/get.go @@ -33,6 +33,7 @@ import ( // referencing the cmd.Flags() type GetOptions struct { Filenames []string + Recursive bool } const ( @@ -100,6 +101,7 @@ func NewCmdGet(f *cmdutil.Factory, out io.Writer) *cobra.Command { cmd.Flags().Bool("export", false, "If true, use 'export' for the resources. Exported resources are stripped of cluster-specific information.") usage := "Filename, directory, or URL to a file identifying the resource to get from a server." kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage) + cmdutil.AddRecursiveFlag(cmd, &options.Recursive) return cmd } @@ -139,7 +141,7 @@ func RunGet(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string if isWatch || isWatchOnly { r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). NamespaceParam(cmdNamespace).DefaultNamespace().AllNamespaces(allNamespaces). - FilenameParam(enforceNamespace, options.Filenames...). + FilenameParam(enforceNamespace, options.Recursive, options.Filenames...). SelectorParam(selector). ExportParam(export). ResourceTypeOrNameArgs(true, args...). @@ -194,7 +196,7 @@ func RunGet(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string b := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). NamespaceParam(cmdNamespace).DefaultNamespace().AllNamespaces(allNamespaces). - FilenameParam(enforceNamespace, options.Filenames...). + FilenameParam(enforceNamespace, options.Recursive, options.Filenames...). SelectorParam(selector). ExportParam(export). ResourceTypeOrNameArgs(true, args...). diff --git a/pkg/kubectl/cmd/label.go b/pkg/kubectl/cmd/label.go index 44db5f87b20..f0d82a6b702 100644 --- a/pkg/kubectl/cmd/label.go +++ b/pkg/kubectl/cmd/label.go @@ -39,6 +39,7 @@ import ( // referencing the cmd.Flags() type LabelOptions struct { Filenames []string + Recursive bool } const ( @@ -96,6 +97,7 @@ func NewCmdLabel(f *cmdutil.Factory, out io.Writer) *cobra.Command { cmd.Flags().String("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.") usage := "Filename, directory, or URL to a file identifying the resource to update the labels" kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage) + cmdutil.AddRecursiveFlag(cmd, &options.Recursive) cmd.Flags().Bool("dry-run", false, "If true, only print the object that would be sent, without sending it.") cmdutil.AddRecordFlag(cmd) @@ -203,10 +205,11 @@ func RunLabel(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []stri return cmdutil.UsageError(cmd, err.Error()) } mapper, typer := f.Object() + b := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). - FilenameParam(enforceNamespace, options.Filenames...). + FilenameParam(enforceNamespace, options.Recursive, options.Filenames...). SelectorParam(selector). ResourceTypeOrNameArgs(all, resources...). Flatten(). diff --git a/pkg/kubectl/cmd/patch.go b/pkg/kubectl/cmd/patch.go index 5b0b6b8d347..5ab7a1e501f 100644 --- a/pkg/kubectl/cmd/patch.go +++ b/pkg/kubectl/cmd/patch.go @@ -37,6 +37,7 @@ var patchTypes = map[string]api.PatchType{"json": api.JSONPatchType, "merge": ap // referencing the cmd.Flags() type PatchOptions struct { Filenames []string + Recursive bool } const ( @@ -82,6 +83,7 @@ func NewCmdPatch(f *cmdutil.Factory, out io.Writer) *cobra.Command { usage := "Filename, directory, or URL to a file identifying the resource to update" kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage) + cmdutil.AddRecursiveFlag(cmd, &options.Recursive) return cmd } @@ -114,7 +116,7 @@ func RunPatch(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []stri r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). - FilenameParam(enforceNamespace, options.Filenames...). + FilenameParam(enforceNamespace, options.Recursive, options.Filenames...). ResourceTypeOrNameArgs(false, args...). Flatten(). Do() diff --git a/pkg/kubectl/cmd/replace.go b/pkg/kubectl/cmd/replace.go index 32f961af1e5..807e0d409a6 100644 --- a/pkg/kubectl/cmd/replace.go +++ b/pkg/kubectl/cmd/replace.go @@ -35,6 +35,7 @@ import ( // referencing the cmd.Flags() type ReplaceOptions struct { Filenames []string + Recursive bool } const ( @@ -82,6 +83,7 @@ func NewCmdReplace(f *cmdutil.Factory, out io.Writer) *cobra.Command { cmd.Flags().Int("grace-period", -1, "Only relevant during a force replace. Period of time in seconds given to the old resource to terminate gracefully. Ignored if negative.") cmd.Flags().Duration("timeout", 0, "Only relevant during a force replace. The length of time to wait before giving up on a delete of the old resource, zero means determine a timeout from the size of the object") cmdutil.AddValidateFlags(cmd) + cmdutil.AddRecursiveFlag(cmd, &options.Recursive) cmdutil.AddOutputFlagsForMutation(cmd) cmdutil.AddApplyAnnotationFlags(cmd) cmdutil.AddRecordFlag(cmd) @@ -117,7 +119,7 @@ func RunReplace(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []st Schema(schema). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). - FilenameParam(enforceNamespace, options.Filenames...). + FilenameParam(enforceNamespace, options.Recursive, options.Filenames...). Flatten(). Do() err = r.Err() @@ -184,7 +186,7 @@ func forceReplace(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args [] r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). - FilenameParam(enforceNamespace, options.Filenames...). + FilenameParam(enforceNamespace, options.Recursive, options.Filenames...). ResourceTypeOrNameArgs(false, args...).RequireObject(false). Flatten(). Do() @@ -209,7 +211,7 @@ func forceReplace(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args [] Schema(schema). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). - FilenameParam(enforceNamespace, options.Filenames...). + FilenameParam(enforceNamespace, options.Recursive, options.Filenames...). Flatten(). Do() err = r.Err() diff --git a/pkg/kubectl/cmd/rollingupdate.go b/pkg/kubectl/cmd/rollingupdate.go index 315c33d53ab..c2b41d103c2 100644 --- a/pkg/kubectl/cmd/rollingupdate.go +++ b/pkg/kubectl/cmd/rollingupdate.go @@ -201,7 +201,7 @@ func RunRollingUpdate(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, arg request := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). Schema(schema). NamespaceParam(cmdNamespace).DefaultNamespace(). - FilenameParam(enforceNamespace, filename). + FilenameParam(enforceNamespace, false, filename). Do() obj, err := request.Object() if err != nil { diff --git a/pkg/kubectl/cmd/rollout/rollout_history.go b/pkg/kubectl/cmd/rollout/rollout_history.go index 63a101af063..d8d580ac364 100644 --- a/pkg/kubectl/cmd/rollout/rollout_history.go +++ b/pkg/kubectl/cmd/rollout/rollout_history.go @@ -32,6 +32,7 @@ import ( // referencing the cmd.Flags() type HistoryOptions struct { Filenames []string + Recursive bool } const ( @@ -59,6 +60,7 @@ func NewCmdRolloutHistory(f *cmdutil.Factory, out io.Writer) *cobra.Command { cmd.Flags().Int64("revision", 0, "See the details, including podTemplate of the revision specified") usage := "Filename, directory, or URL to a file identifying the resource to get from a server." kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage) + cmdutil.AddRecursiveFlag(cmd, &options.Recursive) return cmd } @@ -77,7 +79,7 @@ func RunHistory(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, args []st infos, err := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). NamespaceParam(cmdNamespace).DefaultNamespace(). - FilenameParam(enforceNamespace, options.Filenames...). + FilenameParam(enforceNamespace, options.Recursive, options.Filenames...). ResourceTypeOrNameArgs(true, args...). Latest(). Flatten(). diff --git a/pkg/kubectl/cmd/rollout/rollout_pause.go b/pkg/kubectl/cmd/rollout/rollout_pause.go index 5575ab714fa..16dffe6c24e 100644 --- a/pkg/kubectl/cmd/rollout/rollout_pause.go +++ b/pkg/kubectl/cmd/rollout/rollout_pause.go @@ -39,6 +39,7 @@ type PauseConfig struct { Out io.Writer Filenames []string + Recursive bool } const ( @@ -70,6 +71,7 @@ func NewCmdRolloutPause(f *cmdutil.Factory, out io.Writer) *cobra.Command { usage := "Filename, directory, or URL to a file identifying the resource to get from a server." kubectl.AddJsonFilenameFlag(cmd, &opts.Filenames, usage) + cmdutil.AddRecursiveFlag(cmd, &opts.Recursive) return cmd } @@ -89,7 +91,7 @@ func (o *PauseConfig) CompletePause(f *cmdutil.Factory, cmd *cobra.Command, out infos, err := resource.NewBuilder(o.Mapper, o.Typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). NamespaceParam(cmdNamespace).DefaultNamespace(). - FilenameParam(enforceNamespace, o.Filenames...). + FilenameParam(enforceNamespace, o.Recursive, o.Filenames...). ResourceTypeOrNameArgs(true, args...). SingleResourceType(). Latest(). diff --git a/pkg/kubectl/cmd/rollout/rollout_resume.go b/pkg/kubectl/cmd/rollout/rollout_resume.go index dbcd8774ccd..b76e66912f2 100644 --- a/pkg/kubectl/cmd/rollout/rollout_resume.go +++ b/pkg/kubectl/cmd/rollout/rollout_resume.go @@ -39,6 +39,7 @@ type ResumeConfig struct { Out io.Writer Filenames []string + Recursive bool } const ( @@ -68,6 +69,7 @@ func NewCmdRolloutResume(f *cmdutil.Factory, out io.Writer) *cobra.Command { usage := "Filename, directory, or URL to a file identifying the resource to get from a server." kubectl.AddJsonFilenameFlag(cmd, &opts.Filenames, usage) + cmdutil.AddRecursiveFlag(cmd, &opts.Recursive) return cmd } @@ -87,7 +89,7 @@ func (o *ResumeConfig) CompleteResume(f *cmdutil.Factory, cmd *cobra.Command, ou infos, err := resource.NewBuilder(o.Mapper, o.Typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). NamespaceParam(cmdNamespace).DefaultNamespace(). - FilenameParam(enforceNamespace, o.Filenames...). + FilenameParam(enforceNamespace, o.Recursive, o.Filenames...). ResourceTypeOrNameArgs(true, args...). SingleResourceType(). Latest(). diff --git a/pkg/kubectl/cmd/rollout/rollout_undo.go b/pkg/kubectl/cmd/rollout/rollout_undo.go index c3ac66d908e..814f9e3c503 100644 --- a/pkg/kubectl/cmd/rollout/rollout_undo.go +++ b/pkg/kubectl/cmd/rollout/rollout_undo.go @@ -37,8 +37,10 @@ type UndoOptions struct { Typer runtime.ObjectTyper Info *resource.Info ToRevision int64 - Out io.Writer - Filenames []string + + Out io.Writer + Filenames []string + Recursive bool } const ( @@ -67,6 +69,7 @@ func NewCmdRolloutUndo(f *cmdutil.Factory, out io.Writer) *cobra.Command { cmd.Flags().Int64("to-revision", 0, "The revision to rollback to. Default to 0 (last revision).") usage := "Filename, directory, or URL to a file identifying the resource to get from a server." kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage) + cmdutil.AddRecursiveFlag(cmd, &options.Recursive) return cmd } @@ -86,7 +89,7 @@ func (o *UndoOptions) CompleteUndo(f *cmdutil.Factory, cmd *cobra.Command, out i infos, err := resource.NewBuilder(o.Mapper, o.Typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). NamespaceParam(cmdNamespace).DefaultNamespace(). - FilenameParam(enforceNamespace, o.Filenames...). + FilenameParam(enforceNamespace, o.Recursive, o.Filenames...). ResourceTypeOrNameArgs(true, args...). Latest(). Flatten(). diff --git a/pkg/kubectl/cmd/scale.go b/pkg/kubectl/cmd/scale.go index 551f4427d55..19069762351 100644 --- a/pkg/kubectl/cmd/scale.go +++ b/pkg/kubectl/cmd/scale.go @@ -34,6 +34,7 @@ import ( // referencing the cmd.Flags() type ScaleOptions struct { Filenames []string + Recursive bool } const ( @@ -87,6 +88,7 @@ func NewCmdScale(f *cmdutil.Factory, out io.Writer) *cobra.Command { usage := "Filename, directory, or URL to a file identifying the resource to set a new size" kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage) + cmdutil.AddRecursiveFlag(cmd, &options.Recursive) return cmd } @@ -110,7 +112,7 @@ func RunScale(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []stri r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). - FilenameParam(enforceNamespace, options.Filenames...). + FilenameParam(enforceNamespace, options.Recursive, options.Filenames...). ResourceTypeOrNameArgs(false, args...). Flatten(). Do() diff --git a/pkg/kubectl/cmd/stop.go b/pkg/kubectl/cmd/stop.go index c147259d4f7..4a0c353d242 100644 --- a/pkg/kubectl/cmd/stop.go +++ b/pkg/kubectl/cmd/stop.go @@ -30,6 +30,7 @@ import ( // referencing the cmd.Flags() type StopOptions struct { Filenames []string + Recursive bool } const ( @@ -69,6 +70,7 @@ func NewCmdStop(f *cmdutil.Factory, out io.Writer) *cobra.Command { } usage := "Filename, directory, or URL to file of resource(s) to be stopped." kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage) + cmdutil.AddRecursiveFlag(cmd, &options.Recursive) cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on.") cmd.Flags().Bool("all", false, "[-all] to select all the specified resources.") cmd.Flags().Bool("ignore-not-found", false, "Treat \"resource not found\" as a successful stop.") @@ -89,7 +91,7 @@ func RunStop(f *cmdutil.Factory, cmd *cobra.Command, args []string, out io.Write ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). ResourceTypeOrNameArgs(false, args...). - FilenameParam(enforceNamespace, options.Filenames...). + FilenameParam(enforceNamespace, options.Recursive, options.Filenames...). SelectorParam(cmdutil.GetFlagString(cmd, "selector")). SelectAllParam(cmdutil.GetFlagBool(cmd, "all")). Flatten(). diff --git a/pkg/kubectl/cmd/util/helpers.go b/pkg/kubectl/cmd/util/helpers.go index 82ee77c2f80..4c674f97c0c 100644 --- a/pkg/kubectl/cmd/util/helpers.go +++ b/pkg/kubectl/cmd/util/helpers.go @@ -331,6 +331,10 @@ func AddValidateFlags(cmd *cobra.Command) { cmd.Flags().String("schema-cache-dir", fmt.Sprintf("~/%s/%s", clientcmd.RecommendedHomeDir, clientcmd.RecommendedSchemaName), fmt.Sprintf("If non-empty, load/store cached API schemas in this directory, default is '$HOME/%s/%s'", clientcmd.RecommendedHomeDir, clientcmd.RecommendedSchemaName)) } +func AddRecursiveFlag(cmd *cobra.Command, value *bool) { + cmd.Flags().BoolVarP(value, "recursive", "R", *value, "If true, process directory recursively.") +} + func AddApplyAnnotationFlags(cmd *cobra.Command) { cmd.Flags().Bool(ApplyAnnotationsFlag, false, "If true, the configuration of current object will be saved in its annotation. This is useful when you want to perform kubectl apply on this object in the future.") } diff --git a/pkg/kubectl/resource/builder.go b/pkg/kubectl/resource/builder.go index 7b7bc150402..0a40e7b21ba 100644 --- a/pkg/kubectl/resource/builder.go +++ b/pkg/kubectl/resource/builder.go @@ -98,7 +98,7 @@ func (b *Builder) Schema(schema validation.Schema) *Builder { // will cause an error. // If ContinueOnError() is set prior to this method, objects on the path that are not // recognized will be ignored (but logged at V(2)). -func (b *Builder) FilenameParam(enforceNamespace bool, paths ...string) *Builder { +func (b *Builder) FilenameParam(enforceNamespace, recursive bool, paths ...string) *Builder { for _, s := range paths { switch { case s == "-": @@ -111,7 +111,7 @@ func (b *Builder) FilenameParam(enforceNamespace bool, paths ...string) *Builder } b.URL(url) default: - b.Path(s) + b.Path(recursive, s) } } @@ -157,7 +157,7 @@ func (b *Builder) Stream(r io.Reader, name string) *Builder { // FileVisitor is streaming the content to a StreamVisitor. If ContinueOnError() is set // prior to this method being called, objects on the path that are unrecognized will be // ignored (but logged at V(2)). -func (b *Builder) Path(paths ...string) *Builder { +func (b *Builder) Path(recursive bool, paths ...string) *Builder { for _, p := range paths { _, err := os.Stat(p) if os.IsNotExist(err) { @@ -169,7 +169,7 @@ func (b *Builder) Path(paths ...string) *Builder { continue } - visitors, err := ExpandPathsToFileVisitors(b.mapper, p, false, FileExtensions, b.schema) + visitors, err := ExpandPathsToFileVisitors(b.mapper, p, recursive, FileExtensions, b.schema) if err != nil { b.errs = append(b.errs, fmt.Errorf("error reading %q: %v", p, err)) } diff --git a/pkg/kubectl/resource/builder_test.go b/pkg/kubectl/resource/builder_test.go index 2d784906238..a911afd497b 100644 --- a/pkg/kubectl/resource/builder_test.go +++ b/pkg/kubectl/resource/builder_test.go @@ -23,7 +23,9 @@ import ( "io/ioutil" "net/http" "net/http/httptest" + "os" "reflect" + "strings" "testing" "github.com/ghodss/yaml" @@ -38,6 +40,7 @@ import ( "k8s.io/kubernetes/pkg/client/unversioned/fake" "k8s.io/kubernetes/pkg/runtime" utilerrors "k8s.io/kubernetes/pkg/util/errors" + utiltesting "k8s.io/kubernetes/pkg/util/testing" "k8s.io/kubernetes/pkg/watch" watchjson "k8s.io/kubernetes/pkg/watch/json" ) @@ -178,9 +181,74 @@ func (v *testVisitor) Objects() []runtime.Object { return objects } +var aPod string = ` +{ + "kind": "Pod", + "apiVersion": "` + testapi.Default.GroupVersion().String() + `", + "metadata": { + "name": "busybox{id}", + "labels": { + "name": "busybox{id}" + } + }, + "spec": { + "containers": [ + { + "name": "busybox", + "image": "busybox", + "command": [ + "sleep", + "3600" + ], + "imagePullPolicy": "IfNotPresent" + } + ], + "restartPolicy": "Always" + } +} +` + +var aRC string = ` +{ + "kind": "ReplicationController", + "apiVersion": "` + testapi.Default.GroupVersion().String() + `", + "metadata": { + "name": "busybox{id}", + "labels": { + "app": "busybox" + } + }, + "spec": { + "replicas": 1, + "template": { + "metadata": { + "name": "busybox{id}", + "labels": { + "app": "busybox{id}" + } + }, + "spec": { + "containers": [ + { + "name": "busybox", + "image": "busybox", + "command": [ + "sleep", + "3600" + ], + "imagePullPolicy": "IfNotPresent" + } + ], + "restartPolicy": "Always" + } + } + } +} +` + func TestPathBuilderAndVersionedObjectNotDefaulted(t *testing.T) { b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). - FilenameParam(false, "../../../docs/user-guide/update-demo/kitten-rc.yaml") + FilenameParam(false, false, "../../../docs/user-guide/update-demo/kitten-rc.yaml") test := &testVisitor{} singular := false @@ -233,33 +301,131 @@ func TestNodeBuilder(t *testing.T) { } } +func createTestDir(t *testing.T, path string) { + if err := os.MkdirAll(path, 0750); err != nil { + t.Fatalf("error creating test dir: %v", err) + } +} + +func writeTestFile(t *testing.T, path string, contents string) { + if err := ioutil.WriteFile(path, []byte(contents), 0644); err != nil { + t.Fatalf("error creating test file %#v", err) + } +} + func TestPathBuilderWithMultiple(t *testing.T) { - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). - FilenameParam(false, "../../../examples/guestbook/redis-master-controller.yaml"). - FilenameParam(false, "../../../examples/pod"). - NamespaceParam("test").DefaultNamespace() + // create test dirs + tmpDir, err := utiltesting.MkTmpdir("recursive_test_multiple") + if err != nil { + t.Fatalf("error creating temp dir: %v", err) + } + createTestDir(t, fmt.Sprintf("%s/%s", tmpDir, "recursive/pod/pod_1")) + createTestDir(t, fmt.Sprintf("%s/%s", tmpDir, "recursive/rc/rc_1")) + createTestDir(t, fmt.Sprintf("%s/%s", tmpDir, "inode/hardlink")) + defer os.RemoveAll(tmpDir) - test := &testVisitor{} - singular := false - - err := b.Do().IntoSingular(&singular).Visit(test.Handle) - if err != nil || singular || len(test.Infos) != 2 { - t.Fatalf("unexpected response: %v %t %#v", err, singular, test.Infos) + // create test files + writeTestFile(t, fmt.Sprintf("%s/recursive/pod/busybox.json", tmpDir), strings.Replace(aPod, "{id}", "0", -1)) + writeTestFile(t, fmt.Sprintf("%s/recursive/pod/pod_1/busybox.json", tmpDir), strings.Replace(aPod, "{id}", "1", -1)) + writeTestFile(t, fmt.Sprintf("%s/recursive/rc/busybox.json", tmpDir), strings.Replace(aRC, "{id}", "0", -1)) + writeTestFile(t, fmt.Sprintf("%s/recursive/rc/rc_1/busybox.json", tmpDir), strings.Replace(aRC, "{id}", "1", -1)) + writeTestFile(t, fmt.Sprintf("%s/inode/hardlink/busybox.json", tmpDir), strings.Replace(aPod, "{id}", "0", -1)) + if err := os.Link(fmt.Sprintf("%s/inode/hardlink/busybox.json", tmpDir), fmt.Sprintf("%s/inode/hardlink/busybox-link.json", tmpDir)); err != nil { + t.Fatalf("error creating test file: %v", err) } - info := test.Infos[0] - if _, ok := info.Object.(*api.ReplicationController); !ok || info.Name != "redis-master" || info.Namespace != "test" { - t.Errorf("unexpected info: %#v", info) + tests := []struct { + name string + object runtime.Object + recursive bool + directory string + expectedNames []string + }{ + {"pod", &api.Pod{}, false, "../../../examples/pod", []string{"nginx"}}, + {"recursive-pod", &api.Pod{}, true, fmt.Sprintf("%s/recursive/pod", tmpDir), []string{"busybox0", "busybox1"}}, + {"rc", &api.ReplicationController{}, false, "../../../examples/guestbook/redis-master-controller.yaml", []string{"redis-master"}}, + {"recursive-rc", &api.ReplicationController{}, true, fmt.Sprintf("%s/recursive/rc", tmpDir), []string{"busybox0", "busybox1"}}, + {"hardlink", &api.Pod{}, false, fmt.Sprintf("%s/inode/hardlink/busybox-link.json", tmpDir), []string{"busybox0"}}, + {"hardlink", &api.Pod{}, true, fmt.Sprintf("%s/inode/hardlink/busybox-link.json", tmpDir), []string{"busybox0"}}, } - info = test.Infos[1] - if _, ok := info.Object.(*api.Pod); !ok || info.Name != "nginx" || info.Namespace != "test" { - t.Errorf("unexpected info: %#v", info) + + for _, test := range tests { + b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). + FilenameParam(false, test.recursive, test.directory). + NamespaceParam("test").DefaultNamespace() + + testVisitor := &testVisitor{} + singular := false + + err := b.Do().IntoSingular(&singular).Visit(testVisitor.Handle) + if err != nil { + t.Fatalf("unexpected response: %v %t %#v %s", err, singular, testVisitor.Infos, test.name) + } + + info := testVisitor.Infos + + for i, v := range info { + switch test.object.(type) { + case *api.Pod: + if _, ok := v.Object.(*api.Pod); !ok || v.Name != test.expectedNames[i] || v.Namespace != "test" { + t.Errorf("unexpected info: %#v", v) + } + case *api.ReplicationController: + if _, ok := v.Object.(*api.ReplicationController); !ok || v.Name != test.expectedNames[i] || v.Namespace != "test" { + t.Errorf("unexpected info: %#v", v) + } + } + } + } +} + +func TestPathBuilderWithMultipleInvalid(t *testing.T) { + // create test dirs + tmpDir, err := utiltesting.MkTmpdir("recursive_test_multiple_invalid") + if err != nil { + t.Fatalf("error creating temp dir: %v", err) + } + createTestDir(t, fmt.Sprintf("%s/%s", tmpDir, "inode/symlink/pod")) + defer os.RemoveAll(tmpDir) + + // create test files + writeTestFile(t, fmt.Sprintf("%s/inode/symlink/pod/busybox.json", tmpDir), strings.Replace(aPod, "{id}", "0", -1)) + if err := os.Symlink(fmt.Sprintf("%s/inode/symlink/pod", tmpDir), fmt.Sprintf("%s/inode/symlink/pod-link", tmpDir)); err != nil { + t.Fatalf("error creating test file: %v", err) + } + if err := os.Symlink(fmt.Sprintf("%s/inode/symlink/loop", tmpDir), fmt.Sprintf("%s/inode/symlink/loop", tmpDir)); err != nil { + t.Fatalf("error creating test file: %v", err) + } + + tests := []struct { + name string + recursive bool + directory string + }{ + {"symlink", false, fmt.Sprintf("%s/inode/symlink/pod-link", tmpDir)}, + {"symlink", true, fmt.Sprintf("%s/inode/symlink/pod-link", tmpDir)}, + {"loop", false, fmt.Sprintf("%s/inode/symlink/loop", tmpDir)}, + {"loop", true, fmt.Sprintf("%s/inode/symlink/loop", tmpDir)}, + } + + for _, test := range tests { + b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). + FilenameParam(false, test.recursive, test.directory). + NamespaceParam("test").DefaultNamespace() + + testVisitor := &testVisitor{} + singular := false + + err := b.Do().IntoSingular(&singular).Visit(testVisitor.Handle) + if err == nil { + t.Fatalf("unexpected response: %v %t %#v %s", err, singular, testVisitor.Infos, test.name) + } } } func TestDirectoryBuilder(t *testing.T) { b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). - FilenameParam(false, "../../../examples/guestbook"). + FilenameParam(false, false, "../../../examples/guestbook"). NamespaceParam("test").DefaultNamespace() test := &testVisitor{} @@ -290,7 +456,7 @@ func TestNamespaceOverride(t *testing.T) { // defer s.Close() b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). - FilenameParam(false, s.URL). + FilenameParam(false, false, s.URL). NamespaceParam("test") test := &testVisitor{} @@ -301,7 +467,7 @@ func TestNamespaceOverride(t *testing.T) { } b = NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). - FilenameParam(true, s.URL). + FilenameParam(true, false, s.URL). NamespaceParam("test") test = &testVisitor{} @@ -322,7 +488,7 @@ func TestURLBuilder(t *testing.T) { // defer s.Close() b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). - FilenameParam(false, s.URL). + FilenameParam(false, false, s.URL). NamespaceParam("foo") test := &testVisitor{} @@ -352,7 +518,7 @@ func TestURLBuilderRequireNamespace(t *testing.T) { // defer s.Close() b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). - FilenameParam(false, s.URL). + FilenameParam(false, false, s.URL). NamespaceParam("test").RequireNamespace() test := &testVisitor{} @@ -750,7 +916,7 @@ func TestContinueOnErrorVisitor(t *testing.T) { func TestSingularObject(t *testing.T) { obj, err := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). NamespaceParam("test").DefaultNamespace(). - FilenameParam(false, "../../../examples/guestbook/redis-master-controller.yaml"). + FilenameParam(false, false, "../../../examples/guestbook/redis-master-controller.yaml"). Flatten(). Do().Object() @@ -770,7 +936,7 @@ func TestSingularObject(t *testing.T) { func TestSingularObjectNoExtension(t *testing.T) { obj, err := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). NamespaceParam("test").DefaultNamespace(). - FilenameParam(false, "../../../examples/pod"). + FilenameParam(false, false, "../../../examples/pod"). Flatten(). Do().Object() @@ -881,7 +1047,7 @@ func TestWatch(t *testing.T) { }), }), testapi.Default.Codec()). NamespaceParam("test").DefaultNamespace(). - FilenameParam(false, "../../../examples/guestbook/redis-master-service.yaml").Flatten(). + FilenameParam(false, false, "../../../examples/guestbook/redis-master-service.yaml").Flatten(). Do().Watch("12") if err != nil { @@ -908,8 +1074,8 @@ func TestWatch(t *testing.T) { func TestWatchMultipleError(t *testing.T) { _, err := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). NamespaceParam("test").DefaultNamespace(). - FilenameParam(false, "../../../examples/guestbook/redis-master-controller.yaml").Flatten(). - FilenameParam(false, "../../../examples/guestbook/redis-master-controller.yaml").Flatten(). + FilenameParam(false, false, "../../../examples/guestbook/redis-master-controller.yaml").Flatten(). + FilenameParam(false, false, "../../../examples/guestbook/redis-master-controller.yaml").Flatten(). Do().Watch("") if err == nil {