func NewConvertOptions(ioStreams genericclioptions.IOStreams) *ConvertOptions { return &ConvertOptions{//初始化convert结构体 PrintFlags: genericclioptions.NewPrintFlags("converted").WithTypeSetter(scheme.Scheme).WithDefaultOutput("yaml"), local: true, IOStreams: ioStreams, } }
//创建convert命令 func NewCmdConvert(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command { o := NewConvertOptions(ioStreams)//初始化结构体 cmd := &cobra.Command{//创建cobra命令 Use: "convert -f FILENAME", DisableFlagsInUseLine: true, Short: i18n.T("Convert config files between different API versions"), Long: convertLong, Example: convertExample, Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(o.Complete(f, cmd))//准备 cmdutil.CheckErr(o.RunConvert())//运行 }, } cmd.Flags().BoolVar(&o.local, "local", o.local, "If true, convert will NOT try to contact api-server but run locally.")//local选项 cmd.Flags().StringVar(&o.OutputVersion, "output-version", o.OutputVersion, i18n.T("Output the formatted object with the given group version (for ex: 'extensions/v1beta1')."))//output-version选项 o.PrintFlags.AddFlags(cmd)//打印选项 cmdutil.AddValidateFlags(cmd)//校验选项 cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, "to need to get converted.")//文件选项 return cmd }
//准备 func (o *ConvertOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) (err error) { err = o.FilenameOptions.RequireFilenameOrKustomize()//文件是必须的 if err != nil { return err } o.builder = f.NewBuilder//设置builder o.Namespace, _, err = f.ToRawKubeConfigLoader().Namespace()//设置namespace if err != nil { return err } o.validator = func() (validation.Schema, error) {//设置schemaValidator return f.Validator(cmdutil.GetFlagBool(cmd, "validate")) } // build the printer o.Printer, err = o.PrintFlags.ToPrinter()//printflag转printer if err != nil { return err } return nil }
//运行 func (o *ConvertOptions) RunConvert() error { // Convert must be removed from kubectl, since kubectl can not depend on // Kubernetes "internal" dependencies. These "internal" dependencies can // not be removed from convert. Another way to convert a resource is to // "kubectl apply" it to the cluster, then "kubectl get" at the desired version. // Another possible solution is to make convert a plugin. fmt.Fprintf(o.ErrOut, "kubectl convert is DEPRECATED and will be removed in a future version.\nIn order to convert, kubectl apply the object to the cluster, then kubectl get at the desired version.\n")//打印deprecated提示 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()//用builder构造result对象 err := r.Err() if err != nil { return err } singleItemImplied := false infos, err := r.IntoSingleItemImplied(&singleItemImplied).Infos()//获取infos if err != nil { return err } if len(infos) == 0 {//info为0个返回错误 return fmt.Errorf("no objects passed to convert") } var specifiedOutputVersion schema.GroupVersion if len(o.OutputVersion) > 0 {//如果指定了output-version specifiedOutputVersion, err = schema.ParseGroupVersion(o.OutputVersion)//解析output-version if err != nil { return err } } internalEncoder := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)//获取codec internalVersionJSONEncoder := unstructured.JSONFallbackEncoder{Encoder: internalEncoder}//构造json encoder objects, err := asVersionedObject(infos, !singleItemImplied, specifiedOutputVersion, internalVersionJSONEncoder)//转换对象 if err != nil { return err } return o.Printer.PrintObj(objects, o.Out)//打印结果 }
//转换对象 func asVersionedObject(infos []*resource.Info, forceList bool, specifiedOutputVersion schema.GroupVersion, encoder runtime.Encoder) (runtime.Object, error) { objects, err := asVersionedObjects(infos, specifiedOutputVersion, encoder)//转换对象 if err != nil { return nil, err } var object runtime.Object if len(objects) == 1 && !forceList {//如果结果是1个对象,不是forceList object = objects[0] } else { object = &api.List{Items: objects}//把对象包装成list targetVersions := []schema.GroupVersion{} if !specifiedOutputVersion.Empty() {//specifiedOutputVersion非空 targetVersions = append(targetVersions, specifiedOutputVersion)//append version } targetVersions = append(targetVersions, schema.GroupVersion{Group: "", Version: "v1"})//append version converted, err := tryConvert(scheme.Scheme, object, targetVersions...)//尝试转换 if err != nil { return nil, err } object = converted } actualVersion := object.GetObjectKind().GroupVersionKind()//获取对象实际的version if actualVersion.Version != specifiedOutputVersion.Version {//如果对象实际version不等于指定的version defaultVersionInfo := "" if len(actualVersion.Version) > 0 {//实际version非空,设置打印消息 defaultVersionInfo = fmt.Sprintf("Defaulting to %q", actualVersion.Version) } klog.V(1).Infof("info: the output version specified is invalid. %s\n", defaultVersionInfo)//输出klog日志 } return object, nil//返回对象 }
//转换对象 func asVersionedObjects(infos []*resource.Info, specifiedOutputVersion schema.GroupVersion, encoder runtime.Encoder) ([]runtime.Object, error) { objects := []runtime.Object{} for _, info := range infos {//遍历infos if info.Object == nil {//info的object为空继续 continue } targetVersions := []schema.GroupVersion{}//构造目标version slice // objects that are not part of api.Scheme must be converted to JSON // TODO: convert to map[string]interface{}, attach to runtime.Unknown? if !specifiedOutputVersion.Empty() {//指定的version非空 if _, _, err := scheme.Scheme.ObjectKinds(info.Object); runtime.IsNotRegisteredError(err) {//获取对象kind,如果是IsNotRegisteredError错误 // TODO: ideally this would encode to version, but we don't expose multiple codecs here. data, err := runtime.Encode(encoder, info.Object)//encode对象 if err != nil { return nil, err } // TODO: Set ContentEncoding and ContentType. objects = append(objects, &runtime.Unknown{Raw: data})//追加objects,继续 continue } targetVersions = append(targetVersions, specifiedOutputVersion)//追加目标vesions } else {//如果版本没指定 gvks, _, err := scheme.Scheme.ObjectKinds(info.Object)//获取对象kinds if err == nil { for _, gvk := range gvks { targetVersions = append(targetVersions, scheme.Scheme.PrioritizedVersionsForGroup(gvk.Group)...)//追加目标versions } } } converted, err := tryConvert(scheme.Scheme, info.Object, targetVersions...)//尝试convert if err != nil { return nil, err } objects = append(objects, converted)//追加对象 } return objects, nil//返回对象 }
//尝试转换 func tryConvert(converter runtime.ObjectConvertor, object runtime.Object, versions ...schema.GroupVersion) (runtime.Object, error) { var last error for _, version := range versions {//遍历目标版本 if version.Empty() {//如果版本为空,直接返回对象 return object, nil } obj, err := converter.ConvertToVersion(object, version)//转换对象到某个版本 if err != nil {//有错误继续 last = err continue } return obj, nil//返回对象 } return nil, last//返回错误 }