diff --git a/go.mod b/go.mod index 618d6742..7aa93d9d 100644 --- a/go.mod +++ b/go.mod @@ -30,10 +30,10 @@ require ( github.com/stretchr/testify v1.8.0 golang.org/x/sys v0.3.0 gopkg.in/yaml.v2 v2.4.0 - k8s.io/api v0.0.0-20221209233039-f05a318e72c8 - k8s.io/apimachinery v0.0.0-20221209232825-d6e14b0fa141 + k8s.io/api v0.0.0-20221210033053-998898a96156 + k8s.io/apimachinery v0.0.0-20221210032835-008f4fd41615 k8s.io/cli-runtime v0.0.0-20221207032320-501e6958314f - k8s.io/client-go v0.0.0-20221209233352-d21defd4b1b0 + k8s.io/client-go v0.0.0-20221210033409-ca6015655408 k8s.io/component-base v0.0.0-20221207022911-5a27a217e76d k8s.io/component-helpers v0.0.0-20221207023249-ee02082c9cce k8s.io/klog/v2 v2.80.1 @@ -91,10 +91,10 @@ require ( ) replace ( - k8s.io/api => k8s.io/api v0.0.0-20221209233039-f05a318e72c8 - k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20221209232825-d6e14b0fa141 + k8s.io/api => k8s.io/api v0.0.0-20221210033053-998898a96156 + k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20221210032835-008f4fd41615 k8s.io/cli-runtime => k8s.io/cli-runtime v0.0.0-20221207032320-501e6958314f - k8s.io/client-go => k8s.io/client-go v0.0.0-20221209233352-d21defd4b1b0 + k8s.io/client-go => k8s.io/client-go v0.0.0-20221210033409-ca6015655408 k8s.io/code-generator => k8s.io/code-generator v0.0.0-20221207014433-154dfe63ab2d k8s.io/component-base => k8s.io/component-base v0.0.0-20221207022911-5a27a217e76d k8s.io/component-helpers => k8s.io/component-helpers v0.0.0-20221207023249-ee02082c9cce diff --git a/go.sum b/go.sum index 7a2669f8..2c90c042 100644 --- a/go.sum +++ b/go.sum @@ -540,14 +540,14 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.0.0-20221209233039-f05a318e72c8 h1:ApMPuq6O/ISUzDMH0E8Y/Tax2L75HqmTCqcF+fEUqe0= -k8s.io/api v0.0.0-20221209233039-f05a318e72c8/go.mod h1:2ltcqWp9F5Vy+OA26LvSiFavYNia+3K7OyFFkuNWZMw= -k8s.io/apimachinery v0.0.0-20221209232825-d6e14b0fa141 h1:PABIfpR2jr8tKmgBHecxLIU4Yl5a4d2ZETLD+vkTW9s= -k8s.io/apimachinery v0.0.0-20221209232825-d6e14b0fa141/go.mod h1:tnPmbONNJ7ByJNz9+n9kMjNP8ON+1qoAIIC70lztu74= +k8s.io/api v0.0.0-20221210033053-998898a96156 h1:6p9t0lYFlz9NrPF7JHYNx0Mq9/7ULuA0I7F6i3qE3ZI= +k8s.io/api v0.0.0-20221210033053-998898a96156/go.mod h1:KvCK5uBkmKqFXcfykmlYxJHpBNTgpSY8veYrWEOA2O8= +k8s.io/apimachinery v0.0.0-20221210032835-008f4fd41615 h1:VV7xh+h6bluTjxWpYQa7J5caEtAc7oQGaR+4YsgMDEc= +k8s.io/apimachinery v0.0.0-20221210032835-008f4fd41615/go.mod h1:tnPmbONNJ7ByJNz9+n9kMjNP8ON+1qoAIIC70lztu74= k8s.io/cli-runtime v0.0.0-20221207032320-501e6958314f h1:5ows8uOPcwt9tf1i3gkUKcJI9MUdr+7xg9L1h2gZ+Vo= k8s.io/cli-runtime v0.0.0-20221207032320-501e6958314f/go.mod h1:O2C0cvncfysbWQJ0s0LJ1VXL2iYB/z+Lt4GQHK+/PSA= -k8s.io/client-go v0.0.0-20221209233352-d21defd4b1b0 h1:lqr8yyh1rrQW+nnTx+5ASmwMxUWn14zXhhc5X4I3Ht0= -k8s.io/client-go v0.0.0-20221209233352-d21defd4b1b0/go.mod h1:0pNB+WcDGBiE7j8QSmc+c2Hn5BcwpksEvbXa2QozhZs= +k8s.io/client-go v0.0.0-20221210033409-ca6015655408 h1:xlgh6rsPl7bYNc68YDkoBeK/SwiL0R2AT+8Z4+G5oEU= +k8s.io/client-go v0.0.0-20221210033409-ca6015655408/go.mod h1:0pNB+WcDGBiE7j8QSmc+c2Hn5BcwpksEvbXa2QozhZs= k8s.io/component-base v0.0.0-20221207022911-5a27a217e76d h1:EFaKqZSBLxj6YLvdCsvv+Lj3XNtSR1C7I2qO5AGB0ow= k8s.io/component-base v0.0.0-20221207022911-5a27a217e76d/go.mod h1:ZFqADngLrzbcXx0C7c+Tx7JQ5clFSVl7Vw/y/hJZqzM= k8s.io/component-helpers v0.0.0-20221207023249-ee02082c9cce h1:ZGWLmSf5kBaTrdjHyZ3/g9gJ0X6e5fwpgAbq0wCXgfg= diff --git a/pkg/cmd/annotate/annotate.go b/pkg/cmd/annotate/annotate.go index d4a72bf9..70cf71f5 100644 --- a/pkg/cmd/annotate/annotate.go +++ b/pkg/cmd/annotate/annotate.go @@ -43,42 +43,73 @@ import ( "k8s.io/kubectl/pkg/util/templates" ) -// AnnotateOptions have the data required to perform the annotate operation -type AnnotateOptions struct { - PrintFlags *genericclioptions.PrintFlags - PrintObj printers.ResourcePrinterFunc - - // Filename options - resource.FilenameOptions - RecordFlags *genericclioptions.RecordFlags - +// AnnotateFlags directly reflect the information that CLI is gathering via flags. They will be converted to Options, which +// reflect the runtime requirements for the command. This structure reduces the transformation to wiring and makes +// the logic itself easy to unit test +type AnnotateFlags struct { // Common user flags - overwrite bool - list bool - local bool - dryRunStrategy cmdutil.DryRunStrategy - dryRunVerifier *resource.QueryParamVerifier - fieldManager string - all bool - allNamespaces bool - resourceVersion string - selector string - fieldSelector string - outputFormat string + All bool + AllNamespaces bool - // results of arg parsing - resources []string - newAnnotations map[string]string - removeAnnotations []string - Recorder genericclioptions.Recorder - namespace string - enforceNamespace bool - builder *resource.Builder - unstructuredClientForMapping func(mapping *meta.RESTMapping) (resource.RESTClient, error) + DryRunStrategy cmdutil.DryRunStrategy + DryRunVerifier *resource.QueryParamVerifier + FieldManager string + FieldSelector string + resource.FilenameOptions + List bool + Local bool + OutputFormat string + overwrite bool + PrintFlags *genericclioptions.PrintFlags + RecordFlags *genericclioptions.RecordFlags + resourceVersion string + Selector string genericclioptions.IOStreams } +// NewAnnotateFlags returns a default AnnotateFlags +func NewAnnotateFlags(streams genericclioptions.IOStreams) *AnnotateFlags { + return &AnnotateFlags{ + PrintFlags: genericclioptions.NewPrintFlags("annotate").WithTypeSetter(scheme.Scheme), + RecordFlags: genericclioptions.NewRecordFlags(), + IOStreams: streams, + } +} + +// AnnotateOptions have the data required to perform the annotate operation +type AnnotateOptions struct { + all bool + allNamespaces bool + + builder *resource.Builder + dryRunStrategy cmdutil.DryRunStrategy + dryRunVerifier *resource.QueryParamVerifier + + enforceNamespace bool + fieldSelector string + fieldManager string + resource.FilenameOptions + + genericclioptions.IOStreams + + list bool + local bool + namespace string + newAnnotations map[string]string + overwrite bool + + PrintObj printers.ResourcePrinterFunc + + Recorder genericclioptions.Recorder + resources []string + resourceVersion string + removeAnnotations []string + selector string + + unstructuredClientForMapping func(mapping *meta.RESTMapping) (resource.RESTClient, error) +} + var ( annotateLong = templates.LongDesc(i18n.T(` Update the annotations on one or more resources. @@ -114,20 +145,9 @@ var ( kubectl annotate pods foo description-`)) ) -// NewAnnotateOptions creates the options for annotate -func NewAnnotateOptions(ioStreams genericclioptions.IOStreams) *AnnotateOptions { - return &AnnotateOptions{ - PrintFlags: genericclioptions.NewPrintFlags("annotated").WithTypeSetter(scheme.Scheme), - - RecordFlags: genericclioptions.NewRecordFlags(), - Recorder: genericclioptions.NoopRecorder{}, - IOStreams: ioStreams, - } -} - // NewCmdAnnotate creates the `annotate` command -func NewCmdAnnotate(parent string, f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command { - o := NewAnnotateOptions(ioStreams) +func NewCmdAnnotate(parent string, f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command { + flags := NewAnnotateFlags(streams) cmd := &cobra.Command{ Use: "annotate [--overwrite] (-f FILENAME | TYPE NAME) KEY_1=VAL_1 ... KEY_N=VAL_N [--resource-version=version]", @@ -137,115 +157,138 @@ func NewCmdAnnotate(parent string, f cmdutil.Factory, ioStreams genericclioption Example: annotateExample, ValidArgsFunction: completion.ResourceTypeAndNameCompletionFunc(f), Run: func(cmd *cobra.Command, args []string) { - cmdutil.CheckErr(o.Complete(f, cmd, args)) - cmdutil.CheckErr(o.Validate()) + o, err := flags.ToOptions(f, cmd, args) + cmdutil.CheckErr(err) cmdutil.CheckErr(o.RunAnnotate()) }, } - // bind flag structs - o.RecordFlags.AddFlags(cmd) - o.PrintFlags.AddFlags(cmd) - - cmd.Flags().BoolVar(&o.overwrite, "overwrite", o.overwrite, "If true, allow annotations to be overwritten, otherwise reject annotation updates that overwrite existing annotations.") - cmd.Flags().BoolVar(&o.list, "list", o.list, "If true, display the annotations for a given resource.") - cmd.Flags().BoolVar(&o.local, "local", o.local, "If true, annotation will NOT contact api-server but run locally.") - cmd.Flags().StringVar(&o.fieldSelector, "field-selector", o.fieldSelector, "Selector (field query) to filter on, supports '=', '==', and '!='.(e.g. --field-selector key1=value1,key2=value2). The server only supports a limited number of field queries per type.") - cmd.Flags().BoolVar(&o.all, "all", o.all, "Select all resources, in the namespace of the specified resource types.") - cmd.Flags().BoolVarP(&o.allNamespaces, "all-namespaces", "A", o.allNamespaces, "If true, check the specified action in all namespaces.") - cmd.Flags().StringVar(&o.resourceVersion, "resource-version", o.resourceVersion, i18n.T("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 := "identifying the resource to update the annotation" - cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, usage) - cmdutil.AddDryRunFlag(cmd) - cmdutil.AddFieldManagerFlagVar(cmd, &o.fieldManager, "kubectl-annotate") - cmdutil.AddLabelSelectorFlagVar(cmd, &o.selector) + flags.AddFlags(cmd, streams) return cmd } -// Complete adapts from the command line args and factory to the data required. -func (o *AnnotateOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error { +// AddFlags registers flags for a cli. +func (flags *AnnotateFlags) AddFlags(cmd *cobra.Command, ioStreams genericclioptions.IOStreams) { + flags.PrintFlags.AddFlags(cmd) + flags.RecordFlags.AddFlags(cmd) + + cmdutil.AddDryRunFlag(cmd) + + usage := "identifying the resource to update the annotation" + cmdutil.AddFilenameOptionFlags(cmd, &flags.FilenameOptions, usage) + cmdutil.AddFieldManagerFlagVar(cmd, &flags.FieldManager, "kubectl-annotate") + cmdutil.AddLabelSelectorFlagVar(cmd, &flags.Selector) + + cmd.Flags().BoolVar(&flags.overwrite, "overwrite", flags.overwrite, "If true, allow annotations to be overwritten, otherwise reject annotation updates that overwrite existing annotations.") + cmd.Flags().BoolVar(&flags.List, "list", flags.List, "If true, display the annotations for a given resource.") + cmd.Flags().BoolVar(&flags.Local, "local", flags.Local, "If true, annotation will NOT contact api-server but run locally.") + cmd.Flags().StringVar(&flags.FieldSelector, "field-selector", flags.FieldSelector, "Selector (field query) to filter on, supports '=', '==', and '!='.(e.g. --field-selector key1=value1,key2=value2). The server only supports a limited number of field queries per type.") + cmd.Flags().BoolVar(&flags.All, "all", flags.All, "Select all resources, in the namespace of the specified resource types.") + cmd.Flags().BoolVarP(&flags.AllNamespaces, "all-namespaces", "A", flags.AllNamespaces, "If true, check the specified action in all namespaces.") + cmd.Flags().StringVar(&flags.resourceVersion, "resource-version", flags.resourceVersion, i18n.T("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.")) +} + +// ToOptions converts from CLI inputs to runtime inputs. +func (flags *AnnotateFlags) ToOptions(f cmdutil.Factory, cmd *cobra.Command, args []string) (*AnnotateOptions, error) { + options := &AnnotateOptions{ + all: flags.All, + allNamespaces: flags.AllNamespaces, + FilenameOptions: flags.FilenameOptions, + fieldSelector: flags.FieldSelector, + fieldManager: flags.FieldManager, + IOStreams: flags.IOStreams, + local: flags.Local, + list: flags.List, + overwrite: flags.overwrite, + resourceVersion: flags.resourceVersion, + Recorder: genericclioptions.NoopRecorder{}, + selector: flags.Selector, + } + var err error - o.RecordFlags.Complete(cmd) - o.Recorder, err = o.RecordFlags.ToRecorder() + flags.RecordFlags.Complete(cmd) + options.Recorder, err = flags.RecordFlags.ToRecorder() if err != nil { - return err + return nil, err } - o.outputFormat = cmdutil.GetFlagString(cmd, "output") - o.dryRunStrategy, err = cmdutil.GetDryRunStrategy(cmd) + options.dryRunStrategy, err = cmdutil.GetDryRunStrategy(cmd) if err != nil { - return err + return nil, err } + dynamicClient, err := f.DynamicClient() if err != nil { - return err + return nil, err } - o.dryRunVerifier = resource.NewQueryParamVerifier(dynamicClient, f.OpenAPIGetter(), resource.QueryParamDryRun) - cmdutil.PrintFlagsWithDryRunStrategy(o.PrintFlags, o.dryRunStrategy) - printer, err := o.PrintFlags.ToPrinter() + options.dryRunVerifier = resource.NewQueryParamVerifier(dynamicClient, f.OpenAPIGetter(), resource.QueryParamDryRun) + + cmdutil.PrintFlagsWithDryRunStrategy(flags.PrintFlags, options.dryRunStrategy) + printer, err := flags.PrintFlags.ToPrinter() if err != nil { - return err + return nil, err } - o.PrintObj = func(obj runtime.Object, out io.Writer) error { + options.PrintObj = func(obj runtime.Object, out io.Writer) error { return printer.PrintObj(obj, out) } - if o.list && len(o.outputFormat) > 0 { - return fmt.Errorf("--list and --output may not be specified together") - } - - o.namespace, o.enforceNamespace, err = f.ToRawKubeConfigLoader().Namespace() + options.namespace, options.enforceNamespace, err = f.ToRawKubeConfigLoader().Namespace() if err != nil { - return err + return nil, err } - o.builder = f.NewBuilder() - o.unstructuredClientForMapping = f.UnstructuredClientForMapping + options.builder = f.NewBuilder() + options.unstructuredClientForMapping = f.UnstructuredClientForMapping // retrieves resource and annotation args from args // also checks args to verify that all resources are specified before annotations resources, annotationArgs, err := cmdutil.GetResourcesAndPairs(args, "annotation") if err != nil { - return err + return nil, err } - o.resources = resources - o.newAnnotations, o.removeAnnotations, err = parseAnnotations(annotationArgs) + options.resources = resources + options.newAnnotations, options.removeAnnotations, err = parseAnnotations(annotationArgs) if err != nil { - return err + return nil, err } - return nil -} + // Checks the options and flags to see if there is sufficient information run the command. + if flags.List && len(flags.OutputFormat) > 0 { + return nil, fmt.Errorf("--list and --output may not be specified together") + } + if flags.All && len(flags.Selector) > 0 { + return nil, fmt.Errorf("cannot set --all and --selector at the same time") + } + if flags.All && len(flags.FieldSelector) > 0 { + return nil, fmt.Errorf("cannot set --all and --field-selector at the same time") + } -// Validate checks to the AnnotateOptions to see if there is sufficient information run the command. -func (o AnnotateOptions) Validate() error { - if o.all && len(o.selector) > 0 { - return fmt.Errorf("cannot set --all and --selector at the same time") - } - if o.all && len(o.fieldSelector) > 0 { - return fmt.Errorf("cannot set --all and --field-selector at the same time") - } - if !o.local { - if len(o.resources) < 1 && cmdutil.IsFilenameSliceEmpty(o.Filenames, o.Kustomize) { - return fmt.Errorf("one or more resources must be specified as or /") + if !flags.Local { + if len(options.resources) < 1 && cmdutil.IsFilenameSliceEmpty(flags.Filenames, flags.Kustomize) { + return nil, fmt.Errorf("one or more resources must be specified as or /") } } else { - if o.dryRunStrategy == cmdutil.DryRunServer { - return fmt.Errorf("cannot specify --local and --dry-run=server - did you mean --dry-run=client?") + if options.dryRunStrategy == cmdutil.DryRunServer { + return nil, fmt.Errorf("cannot specify --local and --dry-run=server - did you mean --dry-run=client?") } - if len(o.resources) > 0 { - return fmt.Errorf("can only use local files by -f rsrc.yaml or --filename=rsrc.json when --local=true is set") + if len(options.resources) > 0 { + return nil, fmt.Errorf("can only use local files by -f rsrc.yaml or --filename=rsrc.json when --local=true is set") } - if cmdutil.IsFilenameSliceEmpty(o.Filenames, o.Kustomize) { - return fmt.Errorf("one or more files must be specified as -f rsrc.yaml or --filename=rsrc.json") + if cmdutil.IsFilenameSliceEmpty(flags.Filenames, flags.Kustomize) { + return nil, fmt.Errorf("one or more files must be specified as -f rsrc.yaml or --filename=rsrc.json") } } - if len(o.newAnnotations) < 1 && len(o.removeAnnotations) < 1 && !o.list { - return fmt.Errorf("at least one annotation update is required") + if len(options.newAnnotations) < 1 && len(options.removeAnnotations) < 1 && !flags.List { + return nil, fmt.Errorf("at least one annotation update is required") } - return validateAnnotations(o.removeAnnotations, o.newAnnotations) + err = validateAnnotations(options.removeAnnotations, options.newAnnotations) + if err != nil { + return nil, err + } + + return options, nil } // RunAnnotate does the work diff --git a/pkg/cmd/annotate/annotate_test.go b/pkg/cmd/annotate/annotate_test.go index 2adb7f22..920bf794 100644 --- a/pkg/cmd/annotate/annotate_test.go +++ b/pkg/cmd/annotate/annotate_test.go @@ -445,11 +445,8 @@ func TestAnnotateErrors(t *testing.T) { cmd.SetOut(bufOut) cmd.SetErr(bufOut) - options := NewAnnotateOptions(iostreams) - err := options.Complete(tf, cmd, testCase.args) - if err == nil { - err = options.Validate() - } + flags := NewAnnotateFlags(iostreams) + _, err := flags.ToOptions(tf, cmd, testCase.args) if !testCase.errFn(err) { t.Errorf("%s: unexpected error: %v", k, err) return @@ -505,12 +502,11 @@ func TestAnnotateObject(t *testing.T) { cmd := NewCmdAnnotate("kubectl", tf, iostreams) cmd.SetOut(bufOut) cmd.SetErr(bufOut) - options := NewAnnotateOptions(iostreams) + flags := NewAnnotateFlags(iostreams) args := []string{"pods/foo", "a=b", "c-"} - if err := options.Complete(tf, cmd, args); err != nil { - t.Fatalf("unexpected error: %v", err) - } - if err := options.Validate(); err != nil { + + options, err := flags.ToOptions(tf, cmd, args) + if err != nil { t.Fatalf("unexpected error: %v", err) } if err := options.RunAnnotate(); err != nil { @@ -572,13 +568,13 @@ func TestAnnotateResourceVersion(t *testing.T) { cmd := NewCmdAnnotate("kubectl", tf, iostreams) cmd.SetOut(bufOut) cmd.SetErr(bufOut) - options := NewAnnotateOptions(iostreams) - options.resourceVersion = "10" + //options := NewAnnotateOptions(iostreams) + flags := NewAnnotateFlags(iostreams) + flags.resourceVersion = "10" args := []string{"pods/foo", "a=b"} - if err := options.Complete(tf, cmd, args); err != nil { - t.Fatalf("unexpected error: %v", err) - } - if err := options.Validate(); err != nil { + + options, err := flags.ToOptions(tf, cmd, args) + if err != nil { t.Fatalf("unexpected error: %v", err) } if err := options.RunAnnotate(); err != nil { @@ -627,15 +623,15 @@ func TestAnnotateObjectFromFile(t *testing.T) { cmd := NewCmdAnnotate("kubectl", tf, iostreams) cmd.SetOut(bufOut) cmd.SetErr(bufOut) - options := NewAnnotateOptions(iostreams) - options.Filenames = []string{"../../../testdata/controller.yaml"} + flags := NewAnnotateFlags(iostreams) + flags.Filenames = []string{"../../../testdata/controller.yaml"} args := []string{"a=b", "c-"} - if err := options.Complete(tf, cmd, args); err != nil { - t.Fatalf("unexpected error: %v", err) - } - if err := options.Validate(); err != nil { + + options, err := flags.ToOptions(tf, cmd, args) + if err != nil { t.Fatalf("unexpected error: %v", err) } + if err := options.RunAnnotate(); err != nil { t.Fatalf("unexpected error: %v", err) } @@ -657,16 +653,17 @@ func TestAnnotateLocal(t *testing.T) { iostreams, _, _, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdAnnotate("kubectl", tf, iostreams) - options := NewAnnotateOptions(iostreams) - options.local = true - options.Filenames = []string{"../../../testdata/controller.yaml"} + flags := NewAnnotateFlags(iostreams) + flags.Local = true + flags.Filenames = []string{"../../../testdata/controller.yaml"} args := []string{"a=b"} - if err := options.Complete(tf, cmd, args); err != nil { - t.Fatalf("unexpected error: %v", err) - } - if err := options.Validate(); err != nil { + + options, err := flags.ToOptions(tf, cmd, args) + + if err != nil { t.Fatalf("unexpected error: %v", err) } + if err := options.RunAnnotate(); err != nil { t.Fatalf("unexpected error: %v", err) } @@ -714,13 +711,12 @@ func TestAnnotateMultipleObjects(t *testing.T) { cmd := NewCmdAnnotate("kubectl", tf, iostreams) cmd.SetOut(iostreams.Out) cmd.SetErr(iostreams.Out) - options := NewAnnotateOptions(iostreams) - options.all = true + flags := NewAnnotateFlags(iostreams) + flags.All = true args := []string{"pods", "a=b", "c-"} - if err := options.Complete(tf, cmd, args); err != nil { - t.Fatalf("unexpected error: %v", err) - } - if err := options.Validate(); err != nil { + + options, err := flags.ToOptions(tf, cmd, args) + if err != nil { t.Fatalf("unexpected error: %v", err) } if err := options.RunAnnotate(); err != nil {