diff --git a/pkg/kubectl/cmd/convert.go b/pkg/kubectl/cmd/convert.go index 4a696e9584..f9602621f1 100644 --- a/pkg/kubectl/cmd/convert.go +++ b/pkg/kubectl/cmd/convert.go @@ -19,7 +19,9 @@ package cmd import ( "fmt" - "k8s.io/apimachinery/pkg/api/meta" + "github.com/golang/glog" + "github.com/spf13/cobra" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" scheme "k8s.io/kubernetes/pkg/api/legacyscheme" @@ -30,9 +32,7 @@ import ( "k8s.io/kubernetes/pkg/kubectl/genericclioptions/printers" "k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource" "k8s.io/kubernetes/pkg/kubectl/util/i18n" - - "github.com/golang/glog" - "github.com/spf13/cobra" + "k8s.io/kubernetes/pkg/kubectl/validation" ) var ( @@ -59,46 +59,20 @@ var ( kubectl convert -f . | kubectl create -f -`)) ) -// NewCmdConvert creates a command object for the generic "convert" action, which -// translates the config file into a given version. -func NewCmdConvert(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command { - options := NewConvertOptions(ioStreams) - - cmd := &cobra.Command{ - Use: "convert -f FILENAME", - DisableFlagsInUseLine: true, - Short: i18n.T("Convert config files between different API versions"), - Long: convert_long, - Example: convert_example, - Run: func(cmd *cobra.Command, args []string) { - cmdutil.CheckErr(options.Complete(f, cmd)) - cmdutil.CheckErr(options.RunConvert()) - }, - } - - options.PrintFlags.AddFlags(cmd) - - usage := "to need to get converted." - cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage) - cmd.MarkFlagRequired("filename") - cmdutil.AddValidateFlags(cmd) - cmd.Flags().BoolVar(&options.local, "local", options.local, "If true, convert will NOT try to contact api-server but run locally.") - cmd.Flags().String("output-version", "", i18n.T("Output the formatted object with the given group version (for ex: 'extensions/v1beta1').)")) - return cmd -} - // ConvertOptions have the data required to perform the convert operation type ConvertOptions struct { PrintFlags *genericclioptions.PrintFlags - PrintObj printers.ResourcePrinterFunc + Printer printers.ResourcePrinter + + OutputVersion string + Namespace string + + builder func() *resource.Builder + local bool + validator func() (validation.Schema, error) resource.FilenameOptions - - builder *resource.Builder - local bool - genericclioptions.IOStreams - specifiedOutputVersion schema.GroupVersion } func NewConvertOptions(ioStreams genericclioptions.IOStreams) *ConvertOptions { @@ -109,57 +83,73 @@ func NewConvertOptions(ioStreams genericclioptions.IOStreams) *ConvertOptions { } } -// outputVersion returns the preferred output version for generic content (JSON, YAML, or templates) -// defaultVersion is never mutated. Nil simply allows clean passing in common usage from client.Config -func outputVersion(cmd *cobra.Command) (schema.GroupVersion, error) { - outputVersionString := cmdutil.GetFlagString(cmd, "output-version") - if len(outputVersionString) == 0 { - return schema.GroupVersion{}, nil +// NewCmdConvert creates a command object for the generic "convert" action, which +// translates the config file into a given version. +func NewCmdConvert(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command { + o := NewConvertOptions(ioStreams) + + cmd := &cobra.Command{ + Use: "convert -f FILENAME", + DisableFlagsInUseLine: true, + Short: i18n.T("Convert config files between different API versions"), + Long: convert_long, + Example: convert_example, + Run: func(cmd *cobra.Command, args []string) { + cmdutil.CheckErr(o.Complete(f, cmd)) + cmdutil.CheckErr(o.RunConvert()) + }, } - return schema.ParseGroupVersion(outputVersionString) + cmd.Flags().BoolVar(&o.local, "local", o.local, "If true, convert will NOT try to contact api-server but run locally.") + cmd.Flags().StringVar(&o.OutputVersion, "output-version", o.OutputVersion, i18n.T("Output the formatted object with the given group version (for ex: 'extensions/v1beta1').)")) + o.PrintFlags.AddFlags(cmd) + + cmdutil.AddValidateFlags(cmd) + cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, "to need to get converted.") + cmd.MarkFlagRequired("filename") + return cmd } // Complete collects information required to run Convert command from command line. func (o *ConvertOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) (err error) { - o.specifiedOutputVersion, err = outputVersion(cmd) + o.builder = f.NewBuilder + + o.Namespace, _, err = f.ToRawKubeConfigLoader().Namespace() if err != nil { return err } - // build the builder - o.builder = f.NewBuilder(). - WithScheme(scheme.Scheme). - LocalParam(o.local) - if !o.local { - schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate")) - if err != nil { - return err - } - o.builder.Schema(schema) + o.validator = func() (validation.Schema, error) { + return f.Validator(cmdutil.GetFlagBool(cmd, "validate")) } - cmdNamespace, _, err := f.ToRawKubeConfigLoader().Namespace() - if err != nil { - return err - } - o.builder.NamespaceParam(cmdNamespace). - ContinueOnError(). - FilenameParam(false, &o.FilenameOptions). - Flatten() - // build the printer - printer, err := o.PrintFlags.ToPrinter() + o.Printer, err = o.PrintFlags.ToPrinter() if err != nil { return err } - o.PrintObj = printer.PrintObj return nil } // RunConvert implements the generic Convert command func (o *ConvertOptions) RunConvert() error { - r := o.builder.Do() + b := o.builder(). + WithScheme(scheme.Scheme). + LocalParam(o.local) + if !o.local { + schema, err := o.validator() + if err != nil { + return err + } + b.Schema(schema) + } + + r := b.NamespaceParam(o.Namespace). + ContinueOnError(). + FilenameParam(false, &o.FilenameOptions). + Flatten(). + Do() + err := r.Err() if err != nil { return err @@ -175,40 +165,20 @@ func (o *ConvertOptions) RunConvert() error { return fmt.Errorf("no objects passed to convert") } - objects, err := asVersionedObject(infos, !singleItemImplied, o.specifiedOutputVersion, cmdutil.InternalVersionJSONEncoder()) + var specifiedOutputVersion schema.GroupVersion + if len(o.OutputVersion) > 0 { + specifiedOutputVersion, err = schema.ParseGroupVersion(o.OutputVersion) + if err != nil { + return err + } + } + + objects, err := asVersionedObject(infos, !singleItemImplied, specifiedOutputVersion, cmdutil.InternalVersionJSONEncoder()) if err != nil { return err } - if meta.IsListType(objects) { - listContent, err := meta.ExtractList(objects) - if err != nil { - return err - } - obj, err := objectListToVersionedObject(listContent, o.specifiedOutputVersion) - if err != nil { - return err - } - return o.PrintObj(obj, o.Out) - } - - return o.PrintObj(objects, o.Out) -} - -// objectListToVersionedObject receives a list of api objects and a group version -// and squashes the list's items into a single versioned runtime.Object. -func objectListToVersionedObject(objects []runtime.Object, specifiedOutputVersion schema.GroupVersion) (runtime.Object, error) { - objectList := &api.List{Items: objects} - targetVersions := []schema.GroupVersion{} - if !specifiedOutputVersion.Empty() { - targetVersions = append(targetVersions, specifiedOutputVersion) - } - targetVersions = append(targetVersions, schema.GroupVersion{Group: "", Version: "v1"}) - converted, err := tryConvert(scheme.Scheme, objectList, targetVersions...) - if err != nil { - return nil, err - } - return converted, nil + return o.Printer.PrintObj(objects, o.Out) } // asVersionedObject converts a list of infos into a single object - either a List containing