kubectl源码分析之replace
  TEZNKK3IfmPf 2023年11月15日 25 0

————————————————

type ReplaceOptions struct {//replace结构体
  PrintFlags  *genericclioptions.PrintFlags
  RecordFlags *genericclioptions.RecordFlags

  DeleteFlags   *delete.DeleteFlags
  DeleteOptions *delete.DeleteOptions

  PrintObj func(obj runtime.Object) error

  createAnnotation bool
  validate         bool

  Schema      validation.Schema
  Builder     func() *resource.Builder
  BuilderArgs []string

  Namespace        string
  EnforceNamespace bool
  Raw              string

  Recorder genericclioptions.Recorder

  genericclioptions.IOStreams
}
func NewReplaceOptions(streams genericclioptions.IOStreams) *ReplaceOptions {
  return &ReplaceOptions{//初始化结构体
    PrintFlags:  genericclioptions.NewPrintFlags("replaced"),
    DeleteFlags: delete.NewDeleteFlags("to use to replace the resource."),

    IOStreams: streams,
  }
}
//创建replace命令
func NewCmdReplace(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
  o := NewReplaceOptions(streams)//初始化结构体

  cmd := &cobra.Command{//创建cobra命令
    Use:                   "replace -f FILENAME",
    DisableFlagsInUseLine: true,
    Short:                 i18n.T("Replace a resource by filename or stdin"),
    Long:                  replaceLong,
    Example:               replaceExample,
    Run: func(cmd *cobra.Command, args []string) {
      cmdutil.CheckErr(o.Complete(f, cmd, args))//准备
      cmdutil.CheckErr(o.Validate(cmd))//校验
      cmdutil.CheckErr(o.Run(f))//运行
    },
  }

  o.PrintFlags.AddFlags(cmd)//打印选项
  o.DeleteFlags.AddFlags(cmd)//删除选项
  o.RecordFlags.AddFlags(cmd)//record选项

  cmdutil.AddValidateFlags(cmd)//校验选项
  cmdutil.AddApplyAnnotationFlags(cmd)//save-config选项

  cmd.Flags().StringVar(&o.Raw, "raw", o.Raw, "Raw URI to PUT to the server.  Uses the transport specified by the kubeconfig file.")//raw选项

  return cmd
}
//准备
func (o *ReplaceOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
  var err error

  o.RecordFlags.Complete(cmd)//record准备
  o.Recorder, err = o.RecordFlags.ToRecorder()//record flag转recorder
  if err != nil {
    return err
  }

  o.validate = cmdutil.GetFlagBool(cmd, "validate")//设置validate
  o.createAnnotation = cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag)//设置createAnnotation

  printer, err := o.PrintFlags.ToPrinter()//print flag转printer
  if err != nil {
    return err
  }
  o.PrintObj = func(obj runtime.Object) error {//设置printObj函数
    return printer.PrintObj(obj, o.Out)
  }

  dynamicClient, err := f.DynamicClient()//获取dynamicClient
  if err != nil {
    return err
  }
  deleteOpts := o.DeleteFlags.ToOptions(dynamicClient, o.IOStreams)//deleteflag转deleteOption

  //Replace will create a resource if it doesn't exist already, so ignore not found error
  deleteOpts.IgnoreNotFound = true//设置IgnoreNotFound 
  if o.PrintFlags.OutputFormat != nil {
    deleteOpts.Output = *o.PrintFlags.OutputFormat//设置Output 
  }
  if deleteOpts.GracePeriod == 0 {//设置GracePeriod 和WaitForDeletion 
    // 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.
    deleteOpts.GracePeriod = 1
    deleteOpts.WaitForDeletion = true
  }
  o.DeleteOptions = deleteOpts//设置DeleteOptions 

  err = o.DeleteOptions.FilenameOptions.RequireFilenameOrKustomize()//文件是必须的
  if err != nil {
    return err
  }

  schema, err := f.Validator(o.validate)//获取validator
  if err != nil {
    return err
  }

  o.Schema = schema//设置validateSchema
  o.Builder = f.NewBuilder//设置builder
  o.BuilderArgs = args//设置参数

  o.Namespace, o.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace()//设置namespace和enforceNamespace
  if err != nil {
    return err
  }

  return nil
}
//校验
func (o *ReplaceOptions) Validate(cmd *cobra.Command) error {
  if o.DeleteOptions.GracePeriod >= 0 && !o.DeleteOptions.ForceDeletion {//如果指定了grace-period必须指定force
    return fmt.Errorf("--grace-period must have --force specified")
  }

  if o.DeleteOptions.Timeout != 0 && !o.DeleteOptions.ForceDeletion {//如果指定了timeout必须指定force
    return fmt.Errorf("--timeout must have --force specified")
  }

  if cmdutil.IsFilenameSliceEmpty(o.DeleteOptions.FilenameOptions.Filenames, o.DeleteOptions.FilenameOptions.Kustomize) {//文件是必须的
    return cmdutil.UsageErrorf(cmd, "Must specify --filename to replace")
  }

  if len(o.Raw) > 0 {//如果指定了--raw
    if len(o.DeleteOptions.FilenameOptions.Filenames) != 1 {//只能指定一个文件
      return cmdutil.UsageErrorf(cmd, "--raw can only use a single local file or stdin")
    }
    if strings.Index(o.DeleteOptions.FilenameOptions.Filenames[0], "http://") == 0 || strings.Index(o.DeleteOptions.FilenameOptions.Filenames[0], "https://") == 0 {//不能是url
      return cmdutil.UsageErrorf(cmd, "--raw cannot read from a url")
    }
    if o.DeleteOptions.FilenameOptions.Recursive {//不能指定递归
      return cmdutil.UsageErrorf(cmd, "--raw and --recursive are mutually exclusive")
    }
    if len(cmdutil.GetFlagString(cmd, "output")) > 0 {//不能指定output
      return cmdutil.UsageErrorf(cmd, "--raw and --output are mutually exclusive")
    }
    if _, err := url.ParseRequestURI(o.Raw); err != nil {//raw必须是有效url
      return cmdutil.UsageErrorf(cmd, "--raw must be a valid URL path: %v", err)
    }
  }

  return nil
}
//运行
func (o *ReplaceOptions) Run(f cmdutil.Factory) error {
  // raw only makes sense for a single file resource multiple objects aren't likely to do what you want.
  // the validator enforces this, so
  if len(o.Raw) > 0 {//如果指定了raw
    restClient, err := f.RESTClient()
    if err != nil {
      return err
    }
    return rawhttp.RawPut(restClient, o.IOStreams, o.Raw, o.DeleteOptions.Filenames[0])//执行rawPut
  }

  if o.DeleteOptions.ForceDeletion {//如果指定了force
    return o.forceReplace()//执行先删除后创建
  }

  r := o.Builder().
    Unstructured().
    Schema(o.Schema).
    ContinueOnError().
    NamespaceParam(o.Namespace).DefaultNamespace().
    FilenameParam(o.EnforceNamespace, &o.DeleteOptions.FilenameOptions).
    Flatten().
    Do()//用builder构造result对象
  if err := r.Err(); err != nil {
    return err
  }

  return r.Visit(func(info *resource.Info, err error) error {//visit result
    if err != nil {
      return err
    }

    if err := util.CreateOrUpdateAnnotation(o.createAnnotation, info.Object, scheme.DefaultJSONEncoder()); err != nil {//判断是否创建last-applied-configuration注解
      return cmdutil.AddSourceToErr("replacing", info.Source, err)
    }

    if err := o.Recorder.Record(info.Object); err != nil {//判断是否创建change-cause注解
      klog.V(4).Infof("error recording current command: %v", err)
    }

    // Serialize the object with the annotation applied.
    obj, err := resource.NewHelper(info.Client, info.Mapping).Replace(info.Namespace, info.Name, true, info.Object)//replace对象到服务端
    if err != nil {
      return cmdutil.AddSourceToErr("replacing", info.Source, err)
    }

    info.Refresh(obj, true)//刷新对象
    return o.PrintObj(info.Object)//打印对象
  })
}
//执行先删除后创建
func (o *ReplaceOptions) forceReplace() error {
  for i, filename := range o.DeleteOptions.FilenameOptions.Filenames {//遍历文件
    if filename == "-" {//如果文件是-
      tempDir, err := ioutil.TempDir("", "kubectl_replace_")//创建临时目录
      if err != nil {
        return err
      }
      defer os.RemoveAll(tempDir)//defer删除临时目录
      tempFilename := filepath.Join(tempDir, "resource.stdin")//构造文件名称
      err = cmdutil.DumpReaderToFile(os.Stdin, tempFilename)//输出stdin到文件
      if err != nil {
        return err
      }
      o.DeleteOptions.FilenameOptions.Filenames[i] = tempFilename//设置文件
    }
  }

  r := o.Builder().
    Unstructured().
    ContinueOnError().
    NamespaceParam(o.Namespace).DefaultNamespace().
    ResourceTypeOrNameArgs(false, o.BuilderArgs...).RequireObject(false).
    FilenameParam(o.EnforceNamespace, &o.DeleteOptions.FilenameOptions).
    Flatten().
    Do()//用builder构造result对象
  if err := r.Err(); err != nil {
    return err
  }

  if err := o.DeleteOptions.DeleteResult(r); err != nil {//删除对象
    return err
  }

  timeout := o.DeleteOptions.Timeout
  if timeout == 0 {//如果没指定timeout,则timeout为5分钟
    timeout = 5 * time.Minute
  }
  err := r.Visit(func(info *resource.Info, err error) error {//访问result
    if err != nil {
      return err
    }

    return wait.PollImmediate(1*time.Second, timeout, func() (bool, error) {//等待删除成功
      if err := info.Get(); !errors.IsNotFound(err) {
        return false, err
      }
      return true, nil
    })
  })
  if err != nil {
    return err
  }

  r = o.Builder().
    Unstructured().
    Schema(o.Schema).
    ContinueOnError().
    NamespaceParam(o.Namespace).DefaultNamespace().
    FilenameParam(o.EnforceNamespace, &o.DeleteOptions.FilenameOptions).
    Flatten().
    Do()//用build构造result对象
  err = r.Err()
  if err != nil {
    return err
  }

  count := 0
  err = r.Visit(func(info *resource.Info, err error) error {// visit result
    if err != nil {
      return err
    }

    if err := util.CreateOrUpdateAnnotation(o.createAnnotation, info.Object, scheme.DefaultJSONEncoder()); err != nil {//判断是否创建last-applied-configuration注解
      return err
    }

    if err := o.Recorder.Record(info.Object); err != nil {//判断是否创建change-cause注解
      klog.V(4).Infof("error recording current command: %v", err)
    }

    obj, err := resource.NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, info.Object, nil)//创建对象到服务端
    if err != nil {
      return err
    }

    count++
    info.Refresh(obj, true)//刷新对象
    return o.PrintObj(info.Object)//打印对象
  })
  if err != nil {
    return err
  }
  if count == 0 {
    return fmt.Errorf("no objects passed to replace")
  }
  return nil
}
【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月15日 0

暂无评论

推荐阅读
  TEZNKK3IfmPf   2024年03月22日   89   0   0 ios
  TEZNKK3IfmPf   2023年11月15日   24   0   0 ios
  TEZNKK3IfmPf   2024年03月30日   24   0   0 ios
TEZNKK3IfmPf