diff --git a/build/visible_to/BUILD b/build/visible_to/BUILD index 470b786f711..bd05a56b6a8 100644 --- a/build/visible_to/BUILD +++ b/build/visible_to/BUILD @@ -332,6 +332,7 @@ package_group( name = "pkg_kubectl_validation_CONSUMERS", packages = [ "//pkg/kubectl", + "//pkg/kubectl/cmd", "//pkg/kubectl/cmd/testing", "//pkg/kubectl/cmd/util", "//pkg/kubectl/resource", diff --git a/pkg/kubectl/cmd/BUILD b/pkg/kubectl/cmd/BUILD index 92a1356e5d5..c7e6697fa64 100644 --- a/pkg/kubectl/cmd/BUILD +++ b/pkg/kubectl/cmd/BUILD @@ -25,6 +25,7 @@ go_library( "convert.go", "cp.go", "delete.go", + "delete_flags.go", "describe.go", "diff.go", "drain.go", @@ -85,6 +86,7 @@ go_library( "//pkg/kubectl/util:go_default_library", "//pkg/kubectl/util/i18n:go_default_library", "//pkg/kubectl/util/term:go_default_library", + "//pkg/kubectl/validation:go_default_library", "//pkg/printers:go_default_library", "//pkg/printers/internalversion:go_default_library", "//pkg/util/interrupt:go_default_library", diff --git a/pkg/kubectl/cmd/delete.go b/pkg/kubectl/cmd/delete.go index 1143664cce4..fd0184a9ece 100644 --- a/pkg/kubectl/cmd/delete.go +++ b/pkg/kubectl/cmd/delete.go @@ -113,19 +113,8 @@ type DeleteOptions struct { ErrOut io.Writer } -func NewDeleteOptions(out, errout io.Writer) *DeleteOptions { - return &DeleteOptions{ - Cascade: true, - GracePeriod: -1, - Include3rdParty: true, - - Out: out, - ErrOut: errout, - } -} - func NewCmdDelete(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command { - options := NewDeleteOptions(out, errOut) + deleteFlags := NewDeleteCommandFlags("containing the resource to delete.") validArgs := cmdutil.ValidArgList(f) cmd := &cobra.Command{ @@ -135,7 +124,9 @@ func NewCmdDelete(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command { Long: delete_long, Example: delete_example, Run: func(cmd *cobra.Command, args []string) { + options := deleteFlags.ToOptions(out, errOut) cmdutil.CheckErr(cmdutil.ValidateOutputArgs(cmd)) + if err := options.Complete(f, out, errOut, args, cmd); err != nil { cmdutil.CheckErr(err) } @@ -151,22 +142,11 @@ func NewCmdDelete(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command { ArgAliases: kubectl.ResourceAliases(validArgs), } - usage := "containing the resource to delete." - cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage) - cmd.Flags().StringVarP(&options.Selector, "selector", "l", options.Selector, "Selector (label query) to filter on, not including uninitialized ones.") - cmd.Flags().BoolVar(&options.DeleteAll, "all", options.DeleteAll, "Delete all resources, including uninitialized ones, in the namespace of the specified resource types.") - cmd.Flags().BoolVar(&options.IgnoreNotFound, "ignore-not-found", options.IgnoreNotFound, "Treat \"resource not found\" as a successful delete. Defaults to \"true\" when --all is specified.") - cmd.Flags().BoolVar(&options.Cascade, "cascade", options.Cascade, "If true, cascade the deletion of the resources managed by this resource (e.g. Pods created by a ReplicationController). Default true.") - cmd.Flags().IntVar(&options.GracePeriod, "grace-period", options.GracePeriod, "Period of time in seconds given to the resource to terminate gracefully. Ignored if negative. Set to 1 for immediate shutdown. Can only be set to 0 when --force is true (force deletion).") - cmd.Flags().BoolVar(&options.DeleteNow, "now", options.DeleteNow, "If true, resources are signaled for immediate shutdown (same as --grace-period=1).") - cmd.Flags().BoolVar(&options.ForceDeletion, "force", options.ForceDeletion, "Only used when grace-period=0. If true, immediately remove resources from API and bypass graceful deletion. Note that immediate deletion of some resources may result in inconsistency or data loss and requires confirmation.") - cmd.Flags().DurationVar(&options.Timeout, "timeout", options.Timeout, "The length of time to wait before giving up on a delete, zero means determine a timeout from the size of the object") + deleteFlags.AddFlags(cmd) - // we do not need to wire PrintFlags through this command, - // as it does not deal with runtime.Objects, and only accepts the "name" output format - cmdutil.AddOutputVarFlagsForMutation(cmd, &options.Output) + // flag-specific output flag, as this command does not depend on PrintFlags + cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on, not including uninitialized ones.") - cmdutil.AddInclude3rdPartyVarFlags(cmd, &options.Include3rdParty) cmdutil.AddIncludeUninitializedFlag(cmd) return cmd } @@ -177,6 +157,7 @@ func (o *DeleteOptions) Complete(f cmdutil.Factory, out, errOut io.Writer, args return err } + o.Selector = cmdutil.GetFlagString(cmd, "selector") o.Reaper = f.Reaper includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, false) diff --git a/pkg/kubectl/cmd/delete_flags.go b/pkg/kubectl/cmd/delete_flags.go new file mode 100644 index 00000000000..8bf00a0dae8 --- /dev/null +++ b/pkg/kubectl/cmd/delete_flags.go @@ -0,0 +1,214 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cmd + +import ( + "io" + "time" + + "github.com/spf13/cobra" + + "k8s.io/kubernetes/pkg/kubectl" + "k8s.io/kubernetes/pkg/kubectl/resource" +) + +type FileNameFlags struct { + Usage string + + Filenames *[]string + Recursive *bool +} + +func (o *FileNameFlags) ToOptions() resource.FilenameOptions { + options := resource.FilenameOptions{} + + if o.Recursive != nil { + options.Recursive = *o.Recursive + } + if o.Filenames != nil { + options.Filenames = *o.Filenames + } + + return options +} + +func (o *FileNameFlags) AddFlags(cmd *cobra.Command) { + if o.Recursive != nil { + cmd.Flags().BoolVarP(o.Recursive, "recursive", "R", *o.Recursive, "Process the directory used in -f, --filename recursively. Useful when you want to manage related manifests organized within the same directory.") + } + if o.Filenames != nil { + kubectl.AddJsonFilenameFlag(cmd, o.Filenames, "Filename, directory, or URL to files "+o.Usage) + } +} + +// PrintFlags composes common printer flag structs +// used for commands requiring deletion logic. +type DeleteFlags struct { + FileNameFlags *FileNameFlags + + All *bool + Cascade *bool + Force *bool + GracePeriod *int + IgnoreNotFound *bool + Now *bool + Timeout *time.Duration + Output *string + + IncludeThirdParty *bool +} + +func (f *DeleteFlags) ToOptions(out, errOut io.Writer) *DeleteOptions { + options := &DeleteOptions{ + Out: out, + ErrOut: errOut, + } + + // add filename options + if f.FileNameFlags != nil { + options.FilenameOptions = f.FileNameFlags.ToOptions() + } + + // add output format + if f.Output != nil { + options.Output = *f.Output + } + + if f.All != nil { + options.DeleteAll = *f.All + } + if f.Cascade != nil { + options.Cascade = *f.Cascade + } + if f.Force != nil { + options.ForceDeletion = *f.Force + } + if f.GracePeriod != nil { + options.GracePeriod = *f.GracePeriod + } + if f.IgnoreNotFound != nil { + options.IgnoreNotFound = *f.IgnoreNotFound + } + if f.Now != nil { + options.DeleteNow = *f.Now + } + if f.Timeout != nil { + options.Timeout = *f.Timeout + } + + if f.IncludeThirdParty != nil { + options.Include3rdParty = *f.IncludeThirdParty + } + + return options +} + +func (f *DeleteFlags) AddFlags(cmd *cobra.Command) { + f.FileNameFlags.AddFlags(cmd) + + if f.All != nil { + cmd.Flags().BoolVar(f.All, "all", *f.All, "Delete all resources, including uninitialized ones, in the namespace of the specified resource types.") + } + if f.Force != nil { + cmd.Flags().BoolVar(f.Force, "force", *f.Force, "Only used when grace-period=0. If true, immediately remove resources from API and bypass graceful deletion. Note that immediate deletion of some resources may result in inconsistency or data loss and requires confirmation.") + } + if f.Cascade != nil { + cmd.Flags().BoolVar(f.Cascade, "cascade", *f.Cascade, "If true, cascade the deletion of the resources managed by this resource (e.g. Pods created by a ReplicationController). Default true.") + } + if f.Now != nil { + cmd.Flags().BoolVar(f.Now, "now", *f.Now, "If true, resources are signaled for immediate shutdown (same as --grace-period=1).") + } + if f.GracePeriod != nil { + cmd.Flags().IntVar(f.GracePeriod, "grace-period", *f.GracePeriod, "Period of time in seconds given to the resource to terminate gracefully. Ignored if negative. Set to 1 for immediate shutdown. Can only be set to 0 when --force is true (force deletion).") + } + if f.Timeout != nil { + cmd.Flags().DurationVar(f.Timeout, "timeout", *f.Timeout, "The length of time to wait before giving up on a delete, zero means determine a timeout from the size of the object") + } + if f.IgnoreNotFound != nil { + cmd.Flags().BoolVar(f.IgnoreNotFound, "ignore-not-found", *f.IgnoreNotFound, "Treat \"resource not found\" as a successful delete. Defaults to \"true\" when --all is specified.") + } + + if f.Output != nil { + cmd.Flags().StringVarP(f.Output, "output", "o", *f.Output, "Output mode. Use \"-o name\" for shorter output (resource/name).") + } + + // TODO: this is deprecated. Remove. + if f.IncludeThirdParty != nil { + cmd.Flags().BoolVar(f.IncludeThirdParty, "include-extended-apis", *f.IncludeThirdParty, "If true, include definitions of new APIs via calls to the API server. [default true]") + cmd.Flags().MarkDeprecated("include-extended-apis", "No longer required.") + } +} + +// NewDeleteCommandFlags provides default flags and values for use with the "delete" command +func NewDeleteCommandFlags(usage string) *DeleteFlags { + includeThirdParty := true + cascade := true + gracePeriod := -1 + + // setup command defaults + all := false + force := false + ignoreNotFound := false + now := false + output := "" + timeout := time.Duration(0) + + filenames := []string{} + recursive := false + + return &DeleteFlags{ + FileNameFlags: &FileNameFlags{Usage: usage, Filenames: &filenames, Recursive: &recursive}, + + Cascade: &cascade, + GracePeriod: &gracePeriod, + + All: &all, + Force: &force, + IgnoreNotFound: &ignoreNotFound, + Now: &now, + Timeout: &timeout, + Output: &output, + + IncludeThirdParty: &includeThirdParty, + } +} + +// NewDeleteFlags provides default flags and values for use in commands outside of "delete" +func NewDeleteFlags(usage string) *DeleteFlags { + includeThirdParty := true + cascade := true + gracePeriod := -1 + + force := false + timeout := time.Duration(0) + + filenames := []string{} + recursive := false + + return &DeleteFlags{ + FileNameFlags: &FileNameFlags{Usage: usage, Filenames: &filenames, Recursive: &recursive}, + + Cascade: &cascade, + GracePeriod: &gracePeriod, + + IncludeThirdParty: &includeThirdParty, + + // add non-defaults + Force: &force, + Timeout: &timeout, + } +} diff --git a/pkg/kubectl/cmd/delete_test.go b/pkg/kubectl/cmd/delete_test.go index cd937864beb..a7a1f607887 100644 --- a/pkg/kubectl/cmd/delete_test.go +++ b/pkg/kubectl/cmd/delete_test.go @@ -44,12 +44,17 @@ import ( var unstructuredSerializer = dynamic.ContentConfig().NegotiatedSerializer -var fakecmd = &cobra.Command{ - Use: "delete ([-f FILENAME] | TYPE [(NAME | -l label | --all)])", - DisableFlagsInUseLine: true, - Run: func(cmd *cobra.Command, args []string) { - cmdutil.CheckErr(cmdutil.ValidateOutputArgs(cmd)) - }, +func fakecmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "delete ([-f FILENAME] | TYPE [(NAME | -l label | --all)])", + DisableFlagsInUseLine: true, + Run: func(cmd *cobra.Command, args []string) { + cmdutil.CheckErr(cmdutil.ValidateOutputArgs(cmd)) + }, + } + + cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on, not including uninitialized ones.") + return cmd } func TestDeleteObjectByTuple(t *testing.T) { @@ -366,7 +371,7 @@ func TestDeleteObjectNotFound(t *testing.T) { Cascade: false, Output: "name", } - err := options.Complete(tf, buf, errBuf, []string{}, fakecmd) + err := options.Complete(tf, buf, errBuf, []string{}, fakecmd()) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -448,7 +453,7 @@ func TestDeleteAllNotFound(t *testing.T) { IgnoreNotFound: false, Output: "name", } - err := options.Complete(tf, buf, errBuf, []string{"services"}, fakecmd) + err := options.Complete(tf, buf, errBuf, []string{"services"}, fakecmd()) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -573,7 +578,7 @@ func TestDeleteMultipleObjectContinueOnMissing(t *testing.T) { Cascade: false, Output: "name", } - err := options.Complete(tf, buf, errBuf, []string{}, fakecmd) + err := options.Complete(tf, buf, errBuf, []string{}, fakecmd()) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -749,7 +754,7 @@ func TestResourceErrors(t *testing.T) { Cascade: false, Output: "name", } - err := options.Complete(tf, buf, errBuf, testCase.args, fakecmd) + err := options.Complete(tf, buf, errBuf, testCase.args, fakecmd()) if !testCase.errFn(err) { t.Errorf("%s: unexpected error: %v", k, err) return diff --git a/pkg/kubectl/cmd/replace.go b/pkg/kubectl/cmd/replace.go index e410147af53..2aec4ec007a 100644 --- a/pkg/kubectl/cmd/replace.go +++ b/pkg/kubectl/cmd/replace.go @@ -65,9 +65,10 @@ var ( ) type ReplaceOpts struct { - PrintFlags *printers.PrintFlags - FileNameOptions *resource.FilenameOptions - DeleteOptions *DeleteOptions + PrintFlags *printers.PrintFlags + DeleteFlags *DeleteFlags + + DeleteOptions *DeleteOptions PrintObj func(obj runtime.Object) error @@ -90,10 +91,8 @@ type ReplaceOpts struct { func NewCmdReplace(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command { options := &ReplaceOpts{ - PrintFlags: printers.NewPrintFlags("replaced"), - - FileNameOptions: &resource.FilenameOptions{}, - DeleteOptions: NewDeleteOptions(out, errOut), + PrintFlags: printers.NewPrintFlags("replaced"), + DeleteFlags: NewDeleteFlags("to use to replace the resource."), Out: out, ErrOut: errOut, @@ -114,18 +113,12 @@ func NewCmdReplace(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command { } options.PrintFlags.AddFlags(cmd) + options.DeleteFlags.AddFlags(cmd) - usage := "to use to replace the resource." - cmdutil.AddFilenameOptionFlags(cmd, options.FileNameOptions, usage) cmd.MarkFlagRequired("filename") - cmd.Flags().BoolVar(&options.DeleteOptions.ForceDeletion, "force", options.DeleteOptions.ForceDeletion, "Delete and re-create the specified resource") - cmd.Flags().BoolVar(&options.DeleteOptions.Cascade, "cascade", options.DeleteOptions.Cascade, "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).") - cmd.Flags().IntVar(&options.DeleteOptions.GracePeriod, "grace-period", options.DeleteOptions.GracePeriod, "Only relevant during a force replace. Period of time in seconds given to the old resource to terminate gracefully. Ignored if negative.") - cmd.Flags().DurationVar(&options.DeleteOptions.Timeout, "timeout", options.DeleteOptions.Timeout, "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. Any other values should contain a corresponding time unit (e.g. 1s, 2m, 3h).") cmdutil.AddValidateFlags(cmd) cmdutil.AddApplyAnnotationFlags(cmd) cmdutil.AddRecordFlag(cmd) - cmdutil.AddInclude3rdPartyFlags(cmd) return cmd } @@ -147,22 +140,21 @@ func (o *ReplaceOpts) Complete(f cmdutil.Factory, cmd *cobra.Command, args []str return printer.PrintObj(obj, o.Out) } - // complete delete options - // TODO(juanvallejo): Turn these fields in a DeleteFlags struct, similar to PrintFlags + deleteOpts := o.DeleteFlags.ToOptions(o.Out, o.ErrOut) + //Replace will create a resource if it doesn't exist already, so ignore not found error - o.DeleteOptions.IgnoreNotFound = true - o.DeleteOptions.Reaper = f.Reaper - + deleteOpts.IgnoreNotFound = true + deleteOpts.Reaper = f.Reaper if o.PrintFlags.OutputFormat != nil { - o.DeleteOptions.Output = *o.PrintFlags.OutputFormat + deleteOpts.Output = *o.PrintFlags.OutputFormat } - - if o.DeleteOptions.GracePeriod == 0 { + if deleteOpts.GracePeriod == 0 { // To preserve backwards compatibility, but prevent accidental data loss, we convert --grace-period=0 // into --grace-period=1 and wait until the object is successfully deleted. - o.DeleteOptions.GracePeriod = 1 - o.DeleteOptions.WaitForDeletion = true + deleteOpts.GracePeriod = 1 + deleteOpts.WaitForDeletion = true } + o.DeleteOptions = deleteOpts schema, err := f.Validator(o.validate) if err != nil { @@ -190,7 +182,7 @@ func (o *ReplaceOpts) Validate(cmd *cobra.Command) error { return fmt.Errorf("--timeout must have --force specified") } - if cmdutil.IsFilenameSliceEmpty(o.FileNameOptions.Filenames) { + if cmdutil.IsFilenameSliceEmpty(o.DeleteOptions.FilenameOptions.Filenames) { return cmdutil.UsageErrorf(cmd, "Must specify --filename to replace") } @@ -207,14 +199,14 @@ func (o *ReplaceOpts) Run() error { Schema(o.Schema). ContinueOnError(). NamespaceParam(o.Namespace).DefaultNamespace(). - FilenameParam(o.EnforceNamespace, o.FileNameOptions). + FilenameParam(o.EnforceNamespace, &o.DeleteOptions.FilenameOptions). Flatten(). Do() if err := r.Err(); err != nil { return err } - return o.Result.Visit(func(info *resource.Info, err error) error { + return r.Visit(func(info *resource.Info, err error) error { if err != nil { return err } @@ -241,7 +233,7 @@ func (o *ReplaceOpts) Run() error { } func (o *ReplaceOpts) forceReplace() error { - for i, filename := range o.FileNameOptions.Filenames { + for i, filename := range o.DeleteOptions.FilenameOptions.Filenames { if filename == "-" { tempDir, err := ioutil.TempDir("", "kubectl_replace_") if err != nil { @@ -253,7 +245,7 @@ func (o *ReplaceOpts) forceReplace() error { if err != nil { return err } - o.FileNameOptions.Filenames[i] = tempFilename + o.DeleteOptions.FilenameOptions.Filenames[i] = tempFilename } } @@ -261,8 +253,8 @@ func (o *ReplaceOpts) forceReplace() error { Unstructured(). ContinueOnError(). NamespaceParam(o.Namespace).DefaultNamespace(). - FilenameParam(o.EnforceNamespace, o.FileNameOptions). ResourceTypeOrNameArgs(false, o.BuilderArgs...).RequireObject(false). + FilenameParam(o.EnforceNamespace, &o.DeleteOptions.FilenameOptions). Flatten(). Do() if err := r.Err(); err != nil { @@ -303,7 +295,7 @@ func (o *ReplaceOpts) forceReplace() error { Schema(o.Schema). ContinueOnError(). NamespaceParam(o.Namespace).DefaultNamespace(). - FilenameParam(o.EnforceNamespace, o.FileNameOptions). + FilenameParam(o.EnforceNamespace, &o.DeleteOptions.FilenameOptions). Flatten(). Do() err = r.Err() diff --git a/pkg/kubectl/cmd/run.go b/pkg/kubectl/cmd/run.go index 640c5a512d0..227a94bd28b 100644 --- a/pkg/kubectl/cmd/run.go +++ b/pkg/kubectl/cmd/run.go @@ -91,6 +91,8 @@ type RunObject struct { } type RunOpts struct { + DeleteFlags *DeleteFlags + DeleteOptions *DeleteOptions DryRun bool @@ -115,7 +117,7 @@ type RunOpts struct { func NewCmdRun(f cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer) *cobra.Command { options := &RunOpts{ - DeleteOptions: NewDeleteOptions(cmdOut, cmdErr), + DeleteFlags: NewDeleteFlags("to use to replace the resource."), In: cmdIn, Out: cmdOut, @@ -134,11 +136,12 @@ func NewCmdRun(f cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer) *co }, } + options.DeleteFlags.AddFlags(cmd) + cmdutil.AddPrinterFlags(cmd) addRunFlags(cmd) cmdutil.AddApplyAnnotationFlags(cmd) cmdutil.AddRecordFlag(cmd) - cmdutil.AddInclude3rdPartyFlags(cmd) cmdutil.AddPodRunningTimeoutFlag(cmd, defaultPodAttachTimeout) return cmd } @@ -192,13 +195,13 @@ func (o *RunOpts) Complete(f cmdutil.Factory, cmd *cobra.Command) error { o.Attach = true } - // complete delete options - // TODO(juanvallejo): turn delete options into a DeleteFlags struct, similar to PrintFlags - o.DeleteOptions.IgnoreNotFound = true - o.DeleteOptions.Timeout = 0 - o.DeleteOptions.GracePeriod = -1 - o.DeleteOptions.WaitForDeletion = false - o.DeleteOptions.Reaper = f.Reaper + deleteOpts := o.DeleteFlags.ToOptions(o.Out, o.ErrOut) + deleteOpts.IgnoreNotFound = true + deleteOpts.WaitForDeletion = false + deleteOpts.GracePeriod = -1 + deleteOpts.Reaper = f.Reaper + + o.DeleteOptions = deleteOpts return nil } diff --git a/pkg/kubectl/cmd/run_test.go b/pkg/kubectl/cmd/run_test.go index 078a0ee5bea..c863dc70c38 100644 --- a/pkg/kubectl/cmd/run_test.go +++ b/pkg/kubectl/cmd/run_test.go @@ -196,8 +196,9 @@ func TestRunArgsFollowDashRules(t *testing.T) { cmd.Flags().Set("image", "nginx") cmd.Flags().Set("generator", "run/v1") + deleteFlags := NewDeleteFlags("to use to replace the resource.") opts := &RunOpts{ - DeleteOptions: NewDeleteOptions(os.Stdout, os.Stderr), + DeleteOptions: deleteFlags.ToOptions(os.Stdout, os.Stderr), In: os.Stdin, Out: os.Stdout, @@ -353,8 +354,9 @@ func TestGenerateService(t *testing.T) { } buff := &bytes.Buffer{} + deleteFlags := NewDeleteFlags("to use to replace the resource.") opts := &RunOpts{ - DeleteOptions: NewDeleteOptions(os.Stdout, os.Stderr), + DeleteOptions: deleteFlags.ToOptions(os.Stdout, os.Stderr), Out: buff, ErrOut: buff,