From f1b81ff67823dd8533aef8049282ce2229d48231 Mon Sep 17 00:00:00 2001 From: deads2k Date: Fri, 14 Aug 2015 14:46:43 -0400 Subject: [PATCH] bind filenames var instead of looking up --- pkg/kubectl/bash_comp_utils.go | 4 ++-- pkg/kubectl/cmd/annotate.go | 3 +-- pkg/kubectl/cmd/create.go | 17 ++++++++++----- pkg/kubectl/cmd/delete.go | 16 ++++++++++---- pkg/kubectl/cmd/delete_test.go | 15 +++++++------ pkg/kubectl/cmd/describe.go | 19 ++++++++++------ pkg/kubectl/cmd/expose.go | 16 ++++++++++---- pkg/kubectl/cmd/get.go | 21 +++++++++++------- pkg/kubectl/cmd/get_test.go | 2 +- pkg/kubectl/cmd/label.go | 19 ++++++++++------ pkg/kubectl/cmd/label_test.go | 10 +++++---- pkg/kubectl/cmd/patch.go | 16 ++++++++++---- pkg/kubectl/cmd/replace.go | 31 ++++++++++++++++----------- pkg/kubectl/cmd/rollingupdate.go | 19 ++++++++++------ pkg/kubectl/cmd/rollingupdate_test.go | 15 ++++++------- pkg/kubectl/cmd/scale.go | 16 ++++++++++---- pkg/kubectl/cmd/stop.go | 17 ++++++++++----- 17 files changed, 169 insertions(+), 87 deletions(-) diff --git a/pkg/kubectl/bash_comp_utils.go b/pkg/kubectl/bash_comp_utils.go index 0ad9afd88e0..bf480bd05d9 100644 --- a/pkg/kubectl/bash_comp_utils.go +++ b/pkg/kubectl/bash_comp_utils.go @@ -22,8 +22,8 @@ import ( "github.com/spf13/cobra" ) -func AddJsonFilenameFlag(cmd *cobra.Command, usage string) { - cmd.Flags().StringSliceP("filename", "f", []string{}, usage) +func AddJsonFilenameFlag(cmd *cobra.Command, value *[]string, usage string) { + cmd.Flags().StringSliceVarP(value, "filename", "f", *value, usage) annotations := []string{"json", "yaml", "yml"} cmd.Flags().SetAnnotation("filename", cobra.BashCompFilenameExt, annotations) diff --git a/pkg/kubectl/cmd/annotate.go b/pkg/kubectl/cmd/annotate.go index 45346ee5c7f..5e151056054 100644 --- a/pkg/kubectl/cmd/annotate.go +++ b/pkg/kubectl/cmd/annotate.go @@ -85,7 +85,6 @@ func NewCmdAnnotate(f *cmdutil.Factory, out io.Writer) *cobra.Command { Long: annotate_long, Example: annotate_example, Run: func(cmd *cobra.Command, args []string) { - options.filenames = cmdutil.GetFlagStringSlice(cmd, "filename") if err := options.Complete(f, args); err != nil { cmdutil.CheckErr(err) } @@ -101,7 +100,7 @@ func NewCmdAnnotate(f *cmdutil.Factory, out io.Writer) *cobra.Command { cmd.Flags().BoolVar(&options.all, "all", false, "select all resources in the namespace of the specified resource types") 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, usage) + kubectl.AddJsonFilenameFlag(cmd, &options.filenames, usage) return cmd } diff --git a/pkg/kubectl/cmd/create.go b/pkg/kubectl/cmd/create.go index 0ef66c7e579..f0d43ffe6ba 100644 --- a/pkg/kubectl/cmd/create.go +++ b/pkg/kubectl/cmd/create.go @@ -30,6 +30,12 @@ import ( "k8s.io/kubernetes/pkg/runtime" ) +// CreateOptions 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 CreateOptions struct { + Filenames []string +} + const ( create_long = `Create a resource by filename or stdin. @@ -42,6 +48,8 @@ $ cat pod.json | kubectl create -f -` ) func NewCmdCreate(f *cmdutil.Factory, out io.Writer) *cobra.Command { + options := &CreateOptions{} + cmd := &cobra.Command{ Use: "create -f FILENAME", Short: "Create a resource by filename or stdin", @@ -50,12 +58,12 @@ func NewCmdCreate(f *cmdutil.Factory, out io.Writer) *cobra.Command { Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(ValidateArgs(cmd, args)) cmdutil.CheckErr(cmdutil.ValidateOutputArgs(cmd)) - cmdutil.CheckErr(RunCreate(f, cmd, out)) + cmdutil.CheckErr(RunCreate(f, cmd, out, options)) }, } usage := "Filename, directory, or URL to file to use to create the resource" - kubectl.AddJsonFilenameFlag(cmd, usage) + kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage) cmd.MarkFlagRequired("filename") cmdutil.AddValidateFlag(cmd) cmdutil.AddOutputFlagsForMutation(cmd) @@ -69,7 +77,7 @@ func ValidateArgs(cmd *cobra.Command, args []string) error { return nil } -func RunCreate(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer) error { +func RunCreate(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, options *CreateOptions) error { schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate")) if err != nil { return err @@ -80,13 +88,12 @@ func RunCreate(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer) error { return err } - filenames := cmdutil.GetFlagStringSlice(cmd, "filename") mapper, typer := f.Object() r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). Schema(schema). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). - FilenameParam(enforceNamespace, filenames...). + FilenameParam(enforceNamespace, options.Filenames...). Flatten(). Do() err = r.Err() diff --git a/pkg/kubectl/cmd/delete.go b/pkg/kubectl/cmd/delete.go index 74544ed9dad..fd17b30ad57 100644 --- a/pkg/kubectl/cmd/delete.go +++ b/pkg/kubectl/cmd/delete.go @@ -31,6 +31,12 @@ import ( "k8s.io/kubernetes/pkg/kubectl/resource" ) +// DeleteOptions 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 DeleteOptions struct { + Filenames []string +} + const ( delete_long = `Delete resources by filenames, stdin, resources and names, or by resources and label selector. @@ -64,6 +70,8 @@ func NewCmdDelete(f *cmdutil.Factory, out io.Writer) *cobra.Command { p := kubectl.NewHumanReadablePrinter(false, false, false, false, []string{}) validArgs := p.HandledResources() + options := &DeleteOptions{} + cmd := &cobra.Command{ Use: "delete ([-f FILENAME] | TYPE [(NAME | -l label | --all)])", Short: "Delete resources by filenames, stdin, resources and names, or by resources and label selector.", @@ -71,13 +79,13 @@ func NewCmdDelete(f *cmdutil.Factory, out io.Writer) *cobra.Command { Example: delete_example, Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(cmdutil.ValidateOutputArgs(cmd)) - err := RunDelete(f, out, cmd, args) + err := RunDelete(f, out, cmd, args, options) cmdutil.CheckErr(err) }, ValidArgs: validArgs, } usage := "Filename, directory, or URL to a file containing the resource to delete." - kubectl.AddJsonFilenameFlag(cmd, usage) + kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage) 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.") @@ -88,7 +96,7 @@ func NewCmdDelete(f *cmdutil.Factory, out io.Writer) *cobra.Command { return cmd } -func RunDelete(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string) error { +func RunDelete(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, options *DeleteOptions) error { cmdNamespace, enforceNamespace, err := f.DefaultNamespace() if err != nil { return err @@ -98,7 +106,7 @@ func RunDelete(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). - FilenameParam(enforceNamespace, cmdutil.GetFlagStringSlice(cmd, "filename")...). + FilenameParam(enforceNamespace, options.Filenames...). SelectorParam(cmdutil.GetFlagString(cmd, "selector")). SelectAllParam(deleteAll). ResourceTypeOrNameArgs(false, args...).RequireObject(false). diff --git a/pkg/kubectl/cmd/delete_test.go b/pkg/kubectl/cmd/delete_test.go index 2a9d562ef8b..f12124440f8 100644 --- a/pkg/kubectl/cmd/delete_test.go +++ b/pkg/kubectl/cmd/delete_test.go @@ -143,10 +143,12 @@ func TestDeleteObjectNotFound(t *testing.T) { buf := bytes.NewBuffer([]byte{}) cmd := NewCmdDelete(f, buf) - cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.yaml") + options := &DeleteOptions{ + Filenames: []string{"../../../examples/guestbook/redis-master-controller.yaml"}, + } cmd.Flags().Set("cascade", "false") cmd.Flags().Set("output", "name") - err := RunDelete(f, buf, cmd, []string{}) + err := RunDelete(f, buf, cmd, []string{}, options) if err == nil || !errors.IsNotFound(err) { t.Errorf("unexpected error: expected NotFound, got %v", err) } @@ -218,7 +220,7 @@ func TestDeleteAllNotFound(t *testing.T) { cmd.Flags().Set("ignore-not-found", "false") cmd.Flags().Set("output", "name") - err := RunDelete(f, buf, cmd, []string{"services"}) + err := RunDelete(f, buf, cmd, []string{"services"}, &DeleteOptions{}) if err == nil || !errors.IsNotFound(err) { t.Errorf("unexpected error: expected NotFound, got %v", err) } @@ -321,11 +323,12 @@ func TestDeleteMultipleObjectContinueOnMissing(t *testing.T) { buf := bytes.NewBuffer([]byte{}) cmd := NewCmdDelete(f, buf) - cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.yaml") - cmd.Flags().Set("filename", "../../../examples/guestbook/frontend-service.yaml") + options := &DeleteOptions{ + Filenames: []string{"../../../examples/guestbook/redis-master-controller.yaml", "../../../examples/guestbook/frontend-service.yaml"}, + } cmd.Flags().Set("cascade", "false") cmd.Flags().Set("output", "name") - err := RunDelete(f, buf, cmd, []string{}) + err := RunDelete(f, buf, cmd, []string{}, options) if err == nil || !errors.IsNotFound(err) { t.Errorf("unexpected error: expected NotFound, got %v", err) } diff --git a/pkg/kubectl/cmd/describe.go b/pkg/kubectl/cmd/describe.go index 5ed7b2ab780..c5b6fc4668a 100644 --- a/pkg/kubectl/cmd/describe.go +++ b/pkg/kubectl/cmd/describe.go @@ -32,6 +32,12 @@ import ( "k8s.io/kubernetes/pkg/util/errors" ) +// DescribeOptions 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 DescribeOptions struct { + Filenames []string +} + const ( describe_long = `Show details of a specific resource or group of resources. @@ -68,31 +74,32 @@ $ kubectl describe pods frontend` ) func NewCmdDescribe(f *cmdutil.Factory, out io.Writer) *cobra.Command { + options := &DescribeOptions{} + cmd := &cobra.Command{ Use: "describe (-f FILENAME | TYPE [NAME_PREFIX | -l label] | TYPE/NAME)", Short: "Show details of a specific resource or group of resources", Long: describe_long, Example: describe_example, Run: func(cmd *cobra.Command, args []string) { - err := RunDescribe(f, out, cmd, args) + err := RunDescribe(f, out, cmd, args, options) cmdutil.CheckErr(err) }, ValidArgs: kubectl.DescribableResources(), } usage := "Filename, directory, or URL to a file containing the resource to describe" - kubectl.AddJsonFilenameFlag(cmd, usage) + kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage) cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on") return cmd } -func RunDescribe(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string) error { +func RunDescribe(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, options *DescribeOptions) error { selector := cmdutil.GetFlagString(cmd, "selector") cmdNamespace, enforceNamespace, err := f.DefaultNamespace() if err != nil { return err } - filenames := cmdutil.GetFlagStringSlice(cmd, "filename") - if len(args) == 0 && len(filenames) == 0 { + if len(args) == 0 && len(options.Filenames) == 0 { fmt.Fprint(out, "You must specify the type of resource to describe. ", valid_resources) return cmdutil.UsageError(cmd, "Required resource not specified.") } @@ -101,7 +108,7 @@ func RunDescribe(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []s r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). - FilenameParam(enforceNamespace, filenames...). + FilenameParam(enforceNamespace, options.Filenames...). SelectorParam(selector). ResourceTypeOrNameArgs(true, args...). Flatten(). diff --git a/pkg/kubectl/cmd/expose.go b/pkg/kubectl/cmd/expose.go index 2c4c8b8ae30..173eb1f92e3 100644 --- a/pkg/kubectl/cmd/expose.go +++ b/pkg/kubectl/cmd/expose.go @@ -27,6 +27,12 @@ import ( "k8s.io/kubernetes/pkg/kubectl/resource" ) +// ExposeOptions 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 ExposeOptions struct { + Filenames []string +} + const ( expose_long = `Take a replicated application and expose it as Kubernetes Service. @@ -48,13 +54,15 @@ $ kubectl expose rc streamer --port=4100 --protocol=udp --name=video-stream` ) func NewCmdExposeService(f *cmdutil.Factory, out io.Writer) *cobra.Command { + options := &ExposeOptions{} + cmd := &cobra.Command{ Use: "expose (-f FILENAME | TYPE NAME) --port=port [--protocol=TCP|UDP] [--target-port=number-or-name] [--name=name] [----external-ip=external-ip-of-service] [--type=type]", Short: "Take a replicated application and expose it as Kubernetes Service", Long: expose_long, Example: expose_example, Run: func(cmd *cobra.Command, args []string) { - err := RunExpose(f, out, cmd, args) + err := RunExpose(f, out, cmd, args, options) cmdutil.CheckErr(err) }, } @@ -76,11 +84,11 @@ func NewCmdExposeService(f *cmdutil.Factory, out io.Writer) *cobra.Command { cmd.Flags().String("session-affinity", "", "If non-empty, set the session affinity for the service to this; legal values: 'None', 'ClientIP'") usage := "Filename, directory, or URL to a file identifying the resource to expose a service" - kubectl.AddJsonFilenameFlag(cmd, usage) + kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage) return cmd } -func RunExpose(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string) error { +func RunExpose(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, options *ExposeOptions) error { namespace, enforceNamespace, err := f.DefaultNamespace() if err != nil { return err @@ -90,7 +98,7 @@ func RunExpose(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). ContinueOnError(). NamespaceParam(namespace).DefaultNamespace(). - FilenameParam(enforceNamespace, cmdutil.GetFlagStringSlice(cmd, "filename")...). + FilenameParam(enforceNamespace, options.Filenames...). ResourceTypeOrNameArgs(false, args...). Flatten(). Do() diff --git a/pkg/kubectl/cmd/get.go b/pkg/kubectl/cmd/get.go index 2da32081967..097183c02fb 100644 --- a/pkg/kubectl/cmd/get.go +++ b/pkg/kubectl/cmd/get.go @@ -27,6 +27,12 @@ import ( "k8s.io/kubernetes/pkg/watch" ) +// GetOptions 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 GetOptions struct { + Filenames []string +} + const ( get_long = `Display one or many resources. @@ -70,6 +76,7 @@ $ kubectl get rc/web service/frontend pods/web-pod-13je7` func NewCmdGet(f *cmdutil.Factory, out io.Writer) *cobra.Command { p := kubectl.NewHumanReadablePrinter(false, false, false, false, []string{}) validArgs := p.HandledResources() + options := &GetOptions{} cmd := &cobra.Command{ Use: "get [(-o|--output=)json|yaml|template|templatefile|wide|jsonpath|...] (TYPE [NAME | -l label] | TYPE/NAME ...) [flags]", @@ -77,7 +84,7 @@ func NewCmdGet(f *cmdutil.Factory, out io.Writer) *cobra.Command { Long: get_long, Example: get_example, Run: func(cmd *cobra.Command, args []string) { - err := RunGet(f, out, cmd, args) + err := RunGet(f, out, cmd, args, options) cmdutil.CheckErr(err) }, ValidArgs: validArgs, @@ -89,13 +96,13 @@ func NewCmdGet(f *cmdutil.Factory, out io.Writer) *cobra.Command { cmd.Flags().Bool("all-namespaces", false, "If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace.") cmd.Flags().StringSliceP("label-columns", "L", []string{}, "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...") usage := "Filename, directory, or URL to a file identifying the resource to get from a server." - kubectl.AddJsonFilenameFlag(cmd, usage) + kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage) return cmd } // RunGet implements the generic Get command // TODO: convert all direct flag accessors to a struct and pass that instead of cmd -func RunGet(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string) error { +func RunGet(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, options *GetOptions) error { selector := cmdutil.GetFlagString(cmd, "selector") allNamespaces := cmdutil.GetFlagBool(cmd, "all-namespaces") mapper, typer := f.Object() @@ -105,9 +112,7 @@ func RunGet(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string return err } - filenames := cmdutil.GetFlagStringSlice(cmd, "filename") - - if len(args) == 0 && len(filenames) == 0 { + if len(args) == 0 && len(options.Filenames) == 0 { fmt.Fprint(out, "You must specify the type of resource to get. ", valid_resources, ` * componentstatuses (aka 'cs') * endpoints (aka 'ep') `) @@ -119,7 +124,7 @@ func RunGet(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string if isWatch || isWatchOnly { r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). NamespaceParam(cmdNamespace).DefaultNamespace().AllNamespaces(allNamespaces). - FilenameParam(enforceNamespace, filenames...). + FilenameParam(enforceNamespace, options.Filenames...). SelectorParam(selector). ResourceTypeOrNameArgs(true, args...). SingleResourceType(). @@ -172,7 +177,7 @@ func RunGet(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string b := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). NamespaceParam(cmdNamespace).DefaultNamespace().AllNamespaces(allNamespaces). - FilenameParam(enforceNamespace, filenames...). + FilenameParam(enforceNamespace, options.Filenames...). SelectorParam(selector). ResourceTypeOrNameArgs(true, args...). ContinueOnError(). diff --git a/pkg/kubectl/cmd/get_test.go b/pkg/kubectl/cmd/get_test.go index 0eaa822128f..fab0093dcfc 100644 --- a/pkg/kubectl/cmd/get_test.go +++ b/pkg/kubectl/cmd/get_test.go @@ -206,7 +206,7 @@ func TestGetUnknownSchemaObjectListGeneric(t *testing.T) { cmd.SetOutput(buf) cmd.Flags().Set("output", "json") cmd.Flags().Set("output-version", test.outputVersion) - err := RunGet(f, buf, cmd, []string{"type/foo", "replicationcontrollers/foo"}) + err := RunGet(f, buf, cmd, []string{"type/foo", "replicationcontrollers/foo"}, &GetOptions{}) if err != nil { t.Errorf("%s: unexpected error: %v", k, err) continue diff --git a/pkg/kubectl/cmd/label.go b/pkg/kubectl/cmd/label.go index aa57df0e128..42aba6ac6b1 100644 --- a/pkg/kubectl/cmd/label.go +++ b/pkg/kubectl/cmd/label.go @@ -30,6 +30,12 @@ import ( "k8s.io/kubernetes/pkg/util" ) +// LabelOptions 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 LabelOptions struct { + Filenames []string +} + const ( label_long = `Update the labels on a resource. @@ -57,13 +63,15 @@ $ kubectl label pods foo bar-` ) func NewCmdLabel(f *cmdutil.Factory, out io.Writer) *cobra.Command { + options := &LabelOptions{} + cmd := &cobra.Command{ Use: "label [--overwrite] (-f FILENAME | TYPE NAME) KEY_1=VAL_1 ... KEY_N=VAL_N [--resource-version=version]", Short: "Update the labels on a resource", Long: fmt.Sprintf(label_long, util.LabelValueMaxLength), Example: label_example, Run: func(cmd *cobra.Command, args []string) { - err := RunLabel(f, out, cmd, args) + err := RunLabel(f, out, cmd, args, options) cmdutil.CheckErr(err) }, } @@ -73,7 +81,7 @@ func NewCmdLabel(f *cmdutil.Factory, out io.Writer) *cobra.Command { cmd.Flags().Bool("all", false, "select all resources in the namespace of the specified resource types") 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, usage) + kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage) return cmd } @@ -138,7 +146,7 @@ func labelFunc(obj runtime.Object, overwrite bool, resourceVersion string, label return nil } -func RunLabel(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string) error { +func RunLabel(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, options *LabelOptions) error { resources, labelArgs := []string{}, []string{} first := true for _, s := range args { @@ -155,8 +163,7 @@ func RunLabel(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []stri return cmdutil.UsageError(cmd, "all resources must be specified before label changes: %s", s) } } - filenames := cmdutil.GetFlagStringSlice(cmd, "filename") - if len(resources) < 1 && len(filenames) == 0 { + if len(resources) < 1 && len(options.Filenames) == 0 { return cmdutil.UsageError(cmd, "one or more resources must be specified as or /") } if len(labelArgs) < 1 { @@ -181,7 +188,7 @@ func RunLabel(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []stri b := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). - FilenameParam(enforceNamespace, filenames...). + FilenameParam(enforceNamespace, options.Filenames...). SelectorParam(selector). ResourceTypeOrNameArgs(all, resources...). Flatten(). diff --git a/pkg/kubectl/cmd/label_test.go b/pkg/kubectl/cmd/label_test.go index 82eaf69ca23..934e847963a 100644 --- a/pkg/kubectl/cmd/label_test.go +++ b/pkg/kubectl/cmd/label_test.go @@ -309,7 +309,7 @@ func TestLabelErrors(t *testing.T) { for k, v := range testCase.flags { cmd.Flags().Set(k, v) } - err := RunLabel(f, buf, cmd, testCase.args) + err := RunLabel(f, buf, cmd, testCase.args, &LabelOptions{}) if !testCase.errFn(err) { t.Errorf("%s: unexpected error: %v", k, err) continue @@ -357,9 +357,11 @@ func TestLabelForResourceFromFile(t *testing.T) { buf := bytes.NewBuffer([]byte{}) cmd := NewCmdLabel(f, buf) - cmd.Flags().Set("filename", "../../../examples/cassandra/cassandra.yaml") + options := &LabelOptions{ + Filenames: []string{"../../../examples/cassandra/cassandra.yaml"}, + } - err := RunLabel(f, buf, cmd, []string{"a=b"}) + err := RunLabel(f, buf, cmd, []string{"a=b"}, options) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -406,7 +408,7 @@ func TestLabelMultipleObjects(t *testing.T) { cmd := NewCmdLabel(f, buf) cmd.Flags().Set("all", "true") - if err := RunLabel(f, buf, cmd, []string{"pods", "a=b"}); err != nil { + if err := RunLabel(f, buf, cmd, []string{"pods", "a=b"}, &LabelOptions{}); err != nil { t.Fatalf("unexpected error: %v", err) } if strings.Count(buf.String(), "labeled") != len(pods.Items) { diff --git a/pkg/kubectl/cmd/patch.go b/pkg/kubectl/cmd/patch.go index b24168cad30..9b1cdf02005 100644 --- a/pkg/kubectl/cmd/patch.go +++ b/pkg/kubectl/cmd/patch.go @@ -27,6 +27,12 @@ import ( "k8s.io/kubernetes/pkg/kubectl/resource" ) +// PatchOptions 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 PatchOptions struct { + Filenames []string +} + const ( patch_long = `Update field(s) of a resource using strategic merge patch @@ -45,6 +51,8 @@ kubectl patch pod valid-pod -p '{"spec":{"containers":[{"name":"kubernetes-serve ) func NewCmdPatch(f *cmdutil.Factory, out io.Writer) *cobra.Command { + options := &PatchOptions{} + cmd := &cobra.Command{ Use: "patch (-f FILENAME | TYPE NAME) -p PATCH", Short: "Update field(s) of a resource by stdin.", @@ -53,7 +61,7 @@ func NewCmdPatch(f *cmdutil.Factory, out io.Writer) *cobra.Command { Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(cmdutil.ValidateOutputArgs(cmd)) shortOutput := cmdutil.GetFlagString(cmd, "output") == "name" - err := RunPatch(f, out, cmd, args, shortOutput) + err := RunPatch(f, out, cmd, args, shortOutput, options) cmdutil.CheckErr(err) }, } @@ -62,11 +70,11 @@ func NewCmdPatch(f *cmdutil.Factory, out io.Writer) *cobra.Command { cmdutil.AddOutputFlagsForMutation(cmd) usage := "Filename, directory, or URL to a file identifying the resource to update" - kubectl.AddJsonFilenameFlag(cmd, usage) + kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage) return cmd } -func RunPatch(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, shortOutput bool) error { +func RunPatch(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, shortOutput bool, options *PatchOptions) error { cmdNamespace, enforceNamespace, err := f.DefaultNamespace() if err != nil { return err @@ -81,7 +89,7 @@ func RunPatch(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []stri r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). - FilenameParam(enforceNamespace, cmdutil.GetFlagStringSlice(cmd, "filename")...). + FilenameParam(enforceNamespace, options.Filenames...). ResourceTypeOrNameArgs(false, args...). Flatten(). Do() diff --git a/pkg/kubectl/cmd/replace.go b/pkg/kubectl/cmd/replace.go index 8ae4f572e84..ad352707141 100644 --- a/pkg/kubectl/cmd/replace.go +++ b/pkg/kubectl/cmd/replace.go @@ -31,6 +31,12 @@ import ( "k8s.io/kubernetes/pkg/kubectl/resource" ) +// ReplaceOptions 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 ReplaceOptions struct { + Filenames []string +} + const ( replace_long = `Replace a resource by filename or stdin. @@ -53,6 +59,8 @@ kubectl replace --force -f ./pod.json` ) func NewCmdReplace(f *cmdutil.Factory, out io.Writer) *cobra.Command { + options := &ReplaceOptions{} + cmd := &cobra.Command{ Use: "replace -f FILENAME", // update is deprecated. @@ -62,12 +70,12 @@ func NewCmdReplace(f *cmdutil.Factory, out io.Writer) *cobra.Command { Example: replace_example, Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(cmdutil.ValidateOutputArgs(cmd)) - err := RunReplace(f, out, cmd, args) + err := RunReplace(f, out, cmd, args, options) cmdutil.CheckErr(err) }, } usage := "Filename, directory, or URL to file to use to replace the resource." - kubectl.AddJsonFilenameFlag(cmd, usage) + kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage) cmd.MarkFlagRequired("filename") cmd.Flags().Bool("force", false, "Delete and re-create the specified resource") cmd.Flags().Bool("cascade", false, "Only relevant during a force replace. If true, cascade the deletion of the resources managed by this resource (e.g. Pods created by a ReplicationController).") @@ -78,7 +86,7 @@ func NewCmdReplace(f *cmdutil.Factory, out io.Writer) *cobra.Command { return cmd } -func RunReplace(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string) error { +func RunReplace(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, options *ReplaceOptions) error { if len(os.Args) > 1 && os.Args[1] == "update" { printDeprecationWarning("replace", "update") } @@ -93,14 +101,13 @@ func RunReplace(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []st } force := cmdutil.GetFlagBool(cmd, "force") - filenames := cmdutil.GetFlagStringSlice(cmd, "filename") - if len(filenames) == 0 { + if len(options.Filenames) == 0 { return cmdutil.UsageError(cmd, "Must specify --filename to replace") } shortOutput := cmdutil.GetFlagString(cmd, "output") == "name" if force { - return forceReplace(f, out, cmd, args, filenames, shortOutput) + return forceReplace(f, out, cmd, args, shortOutput, options) } mapper, typer := f.Object() @@ -108,7 +115,7 @@ func RunReplace(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []st Schema(schema). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). - FilenameParam(enforceNamespace, filenames...). + FilenameParam(enforceNamespace, options.Filenames...). Flatten(). Do() err = r.Err() @@ -135,7 +142,7 @@ func RunReplace(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []st }) } -func forceReplace(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, filenames []string, shortOutput bool) error { +func forceReplace(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, shortOutput bool, options *ReplaceOptions) error { schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate")) if err != nil { return err @@ -146,7 +153,7 @@ func forceReplace(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args [] return err } - for i, filename := range filenames { + for i, filename := range options.Filenames { if filename == "-" { tempDir, err := ioutil.TempDir("", "kubectl_replace_") if err != nil { @@ -158,7 +165,7 @@ func forceReplace(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args [] if err != nil { return err } - filenames[i] = tempFilename + options.Filenames[i] = tempFilename } } @@ -166,7 +173,7 @@ func forceReplace(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args [] r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). - FilenameParam(enforceNamespace, filenames...). + FilenameParam(enforceNamespace, options.Filenames...). ResourceTypeOrNameArgs(false, args...).RequireObject(false). Flatten(). Do() @@ -191,7 +198,7 @@ func forceReplace(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args [] Schema(schema). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). - FilenameParam(enforceNamespace, filenames...). + FilenameParam(enforceNamespace, options.Filenames...). Flatten(). Do() err = r.Err() diff --git a/pkg/kubectl/cmd/rollingupdate.go b/pkg/kubectl/cmd/rollingupdate.go index d2e8ef0c23d..64d8182f8fe 100644 --- a/pkg/kubectl/cmd/rollingupdate.go +++ b/pkg/kubectl/cmd/rollingupdate.go @@ -34,6 +34,12 @@ import ( "k8s.io/kubernetes/pkg/kubectl/resource" ) +// RollingUpdateOptions 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 RollingUpdateOptions struct { + Filenames []string +} + const ( rollingUpdate_long = `Perform a rolling update of the given ReplicationController. @@ -62,6 +68,8 @@ var ( ) func NewCmdRollingUpdate(f *cmdutil.Factory, out io.Writer) *cobra.Command { + options := &RollingUpdateOptions{} + cmd := &cobra.Command{ Use: "rolling-update OLD_CONTROLLER_NAME ([NEW_CONTROLLER_NAME] --image=NEW_CONTAINER_IMAGE | -f NEW_CONTROLLER_SPEC)", // rollingupdate is deprecated. @@ -70,7 +78,7 @@ func NewCmdRollingUpdate(f *cmdutil.Factory, out io.Writer) *cobra.Command { Long: rollingUpdate_long, Example: rollingUpdate_example, Run: func(cmd *cobra.Command, args []string) { - err := RunRollingUpdate(f, out, cmd, args) + err := RunRollingUpdate(f, out, cmd, args, options) cmdutil.CheckErr(err) }, } @@ -78,7 +86,7 @@ func NewCmdRollingUpdate(f *cmdutil.Factory, out io.Writer) *cobra.Command { cmd.Flags().Duration("poll-interval", pollInterval, `Time delay between polling for replication controller status after the update. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".`) cmd.Flags().Duration("timeout", timeout, `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".`) usage := "Filename or URL to file to use to create the new replication controller." - kubectl.AddJsonFilenameFlag(cmd, usage) + kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage) cmd.MarkFlagRequired("filename") cmd.Flags().String("image", "", "Image to use for upgrading the replication controller. Can not be used with --filename/-f") cmd.MarkFlagRequired("image") @@ -90,10 +98,9 @@ func NewCmdRollingUpdate(f *cmdutil.Factory, out io.Writer) *cobra.Command { return cmd } -func validateArguments(cmd *cobra.Command, args []string) (deploymentKey, filename, image, oldName string, err error) { +func validateArguments(cmd *cobra.Command, filenames, args []string) (deploymentKey, filename, image, oldName string, err error) { deploymentKey = cmdutil.GetFlagString(cmd, "deployment-label-key") image = cmdutil.GetFlagString(cmd, "image") - filenames := cmdutil.GetFlagStringSlice(cmd, "filename") filename = "" if len(deploymentKey) == 0 { @@ -118,11 +125,11 @@ func validateArguments(cmd *cobra.Command, args []string) (deploymentKey, filena return deploymentKey, filename, image, args[0], nil } -func RunRollingUpdate(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string) error { +func RunRollingUpdate(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, options *RollingUpdateOptions) error { if len(os.Args) > 1 && os.Args[1] == "rollingupdate" { printDeprecationWarning("rolling-update", "rollingupdate") } - deploymentKey, filename, image, oldName, err := validateArguments(cmd, args) + deploymentKey, filename, image, oldName, err := validateArguments(cmd, options.Filenames, args) if err != nil { return err } diff --git a/pkg/kubectl/cmd/rollingupdate_test.go b/pkg/kubectl/cmd/rollingupdate_test.go index 75ab3550231..a132a7dcee6 100644 --- a/pkg/kubectl/cmd/rollingupdate_test.go +++ b/pkg/kubectl/cmd/rollingupdate_test.go @@ -26,6 +26,7 @@ func TestValidateArgs(t *testing.T) { tests := []struct { flags map[string]string + filenames []string args []string expectErr bool testName string @@ -41,11 +42,9 @@ func TestValidateArgs(t *testing.T) { testName: "no file, no image", }, { - flags: map[string]string{ - "filename": "bar.yaml", - }, - args: []string{"foo"}, - testName: "valid file example", + filenames: []string{"bar.yaml"}, + args: []string{"foo"}, + testName: "valid file example", }, { flags: map[string]string{ @@ -63,9 +62,9 @@ func TestValidateArgs(t *testing.T) { }, { flags: map[string]string{ - "image": "foo:v2", - "filename": "bar.yaml", + "image": "foo:v2", }, + filenames: []string{"bar.yaml"}, args: []string{"foo", "foo-v2"}, expectErr: true, testName: "both filename and image example", @@ -80,7 +79,7 @@ func TestValidateArgs(t *testing.T) { cmd.Flags().Set(key, val) } } - _, _, _, _, err := validateArguments(cmd, test.args) + _, _, _, _, err := validateArguments(cmd, test.filenames, test.args) if err != nil && !test.expectErr { t.Errorf("unexpected error: %v (%s)", err, test.testName) } diff --git a/pkg/kubectl/cmd/scale.go b/pkg/kubectl/cmd/scale.go index 2cb235a469e..cdef2e17258 100644 --- a/pkg/kubectl/cmd/scale.go +++ b/pkg/kubectl/cmd/scale.go @@ -29,6 +29,12 @@ import ( "k8s.io/kubernetes/pkg/util/errors" ) +// ScaleOptions 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 ScaleOptions struct { + Filenames []string +} + const ( scale_long = `Set a new size for a Replication Controller. @@ -51,6 +57,8 @@ $ kubectl scale --replicas=5 rc/foo rc/bar` // NewCmdScale returns a cobra command with the appropriate configuration and flags to run scale func NewCmdScale(f *cmdutil.Factory, out io.Writer) *cobra.Command { + options := &ScaleOptions{} + cmd := &cobra.Command{ Use: "scale [--resource-version=version] [--current-replicas=count] --replicas=COUNT (-f FILENAME | TYPE NAME)", // resize is deprecated @@ -61,7 +69,7 @@ func NewCmdScale(f *cmdutil.Factory, out io.Writer) *cobra.Command { Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(cmdutil.ValidateOutputArgs(cmd)) shortOutput := cmdutil.GetFlagString(cmd, "output") == "name" - err := RunScale(f, out, cmd, args, shortOutput) + err := RunScale(f, out, cmd, args, shortOutput, options) cmdutil.CheckErr(err) }, } @@ -73,12 +81,12 @@ func NewCmdScale(f *cmdutil.Factory, out io.Writer) *cobra.Command { cmdutil.AddOutputFlagsForMutation(cmd) usage := "Filename, directory, or URL to a file identifying the replication controller to set a new size" - kubectl.AddJsonFilenameFlag(cmd, usage) + kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage) return cmd } // RunScale executes the scaling -func RunScale(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, shortOutput bool) error { +func RunScale(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, shortOutput bool, options *ScaleOptions) error { if len(os.Args) > 1 && os.Args[1] == "resize" { printDeprecationWarning("scale", "resize") } @@ -97,7 +105,7 @@ func RunScale(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []stri r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). - FilenameParam(enforceNamespace, cmdutil.GetFlagStringSlice(cmd, "filename")...). + FilenameParam(enforceNamespace, options.Filenames...). ResourceTypeOrNameArgs(false, args...). Flatten(). Do() diff --git a/pkg/kubectl/cmd/stop.go b/pkg/kubectl/cmd/stop.go index f666ab91827..3919c22e12a 100644 --- a/pkg/kubectl/cmd/stop.go +++ b/pkg/kubectl/cmd/stop.go @@ -25,6 +25,12 @@ import ( "k8s.io/kubernetes/pkg/kubectl/resource" ) +// StopOptions 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 StopOptions struct { + Filenames []string +} + const ( stop_long = `Deprecated: Gracefully shut down a resource by name or filename. @@ -47,6 +53,8 @@ $ kubectl stop -f path/to/resources` ) func NewCmdStop(f *cmdutil.Factory, out io.Writer) *cobra.Command { + options := &StopOptions{} + cmd := &cobra.Command{ Use: "stop (-f FILENAME | TYPE (NAME | -l label | --all))", Short: "Deprecated: Gracefully shut down a resource by name or filename.", @@ -54,11 +62,11 @@ func NewCmdStop(f *cmdutil.Factory, out io.Writer) *cobra.Command { Example: stop_example, Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(cmdutil.ValidateOutputArgs(cmd)) - cmdutil.CheckErr(RunStop(f, cmd, args, out)) + cmdutil.CheckErr(RunStop(f, cmd, args, out, options)) }, } usage := "Filename, directory, or URL to file of resource(s) to be stopped." - kubectl.AddJsonFilenameFlag(cmd, usage) + kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage) 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.") @@ -68,19 +76,18 @@ func NewCmdStop(f *cmdutil.Factory, out io.Writer) *cobra.Command { return cmd } -func RunStop(f *cmdutil.Factory, cmd *cobra.Command, args []string, out io.Writer) error { +func RunStop(f *cmdutil.Factory, cmd *cobra.Command, args []string, out io.Writer, options *StopOptions) error { cmdNamespace, enforceNamespace, err := f.DefaultNamespace() if err != nil { return err } - filenames := cmdutil.GetFlagStringSlice(cmd, "filename") mapper, typer := f.Object() r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). ResourceTypeOrNameArgs(false, args...). - FilenameParam(enforceNamespace, filenames...). + FilenameParam(enforceNamespace, options.Filenames...). SelectorParam(cmdutil.GetFlagString(cmd, "selector")). SelectAllParam(cmdutil.GetFlagBool(cmd, "all")). Flatten().