天天看點

kubectl源碼分析之create job指令

 歡迎關注我的公衆号:

type CreateJobOptions struct {//create job結構體
  PrintFlags *genericclioptions.PrintFlags

  PrintObj func(obj runtime.Object) error

  Name    string
  Image   string
  From    string
  Command []string

  Namespace string
  Client    batchv1client.BatchV1Interface
  DryRun    bool
  Builder   *resource.Builder
  Cmd       *cobra.Command

  genericclioptions.IOStreams
}      
func NewCreateJobOptions(ioStreams genericclioptions.IOStreams) *CreateJobOptions {
  return &CreateJobOptions{//初始化create job結構體
    PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme),
    IOStreams:  ioStreams,
  }
}      
//建立create job指令
func NewCmdCreateJob(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
  o := NewCreateJobOptions(ioStreams)//初始化結構體
  cmd := &cobra.Command{//建立cobra指令
    Use:     "job NAME --image=image [--from=cronjob/name] -- [COMMAND] [args...]",
    Short:   jobLong,
    Long:    jobLong,
    Example: jobExample,
    Run: func(cmd *cobra.Command, args []string) {
      cmdutil.CheckErr(o.Complete(f, cmd, args))// 準備
      cmdutil.CheckErr(o.Validate())//校驗
      cmdutil.CheckErr(o.Run())//運作
    },
  }

  o.PrintFlags.AddFlags(cmd)//設定print選項

  cmdutil.AddApplyAnnotationFlags(cmd)//設定save-config選項
  cmdutil.AddValidateFlags(cmd)//設定validate選項
  cmdutil.AddDryRunFlag(cmd)//設定dry-run選項
  cmd.Flags().StringVar(&o.Image, "image", o.Image, "Image name to run.")//設定image選項
  cmd.Flags().StringVar(&o.From, "from", o.From, "The name of the resource to create a Job from (only cronjob is supported).")//設定from選項

  return cmd
}      
//準備方法
func (o *CreateJobOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
  name, err := NameFromCommandArgs(cmd, args)//擷取名稱
  if err != nil {
    return err
  }
  o.Name = name//設定名稱
  if len(args) > 1 {
    o.Command = args[1:]//設定command參數
  }

  clientConfig, err := f.ToRESTConfig()//擷取restConfig
  if err != nil {
    return err
  }
  o.Client, err = batchv1client.NewForConfig(clientConfig)//根據restConfig擷取client
  if err != nil {
    return err
  }

  o.Namespace, _, err = f.ToRawKubeConfigLoader().Namespace()//設定namespace
  if err != nil {
    return err
  }
  o.Builder = f.NewBuilder()//設定builder
  o.Cmd = cmd//設定cmd

  o.DryRun = cmdutil.GetDryRunFlag(cmd)//設定幹跑
  if o.DryRun {
    o.PrintFlags.Complete("%s (dry run)")
  }
  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)
  }

  return nil
}      
//校驗
func (o *CreateJobOptions) Validate() error {
  if (len(o.Image) == 0 && len(o.From) == 0) || (len(o.Image) != 0 && len(o.From) != 0) {
    return fmt.Errorf("either --image or --from must be specified")//image和from隻能指定一個
  }
  if o.Command != nil && len(o.Command) != 0 && len(o.From) != 0 {//command和from不能同時指定
    return fmt.Errorf("cannot specify --from and command")
  }
  return nil
}      
//運作指令
func (o *CreateJobOptions) Run() error {
  var job *batchv1.Job
  if len(o.Image) > 0 {//如果指定了image
    job = o.createJob()//構造job對象
  } else {//如果指定了from
    infos, err := o.Builder.
      Unstructured().
      NamespaceParam(o.Namespace).DefaultNamespace().
      ResourceTypeOrNameArgs(false, o.From).
      Flatten().
      Latest().
      Do().
      Infos()//擷取cronjob info對象
    if err != nil {
      return err
    }
    if len(infos) != 1 {//info必須是一個
      return fmt.Errorf("from must be an existing cronjob")
    }

    uncastVersionedObj, err := scheme.Scheme.ConvertToVersion(infos[0].Object, batchv1beta1.SchemeGroupVersion)
    if err != nil {
      return fmt.Errorf("from must be an existing cronjob: %v", err)
    }
    cronJob, ok := uncastVersionedObj.(*batchv1beta1.CronJob)//把obj轉為cronJob
    if !ok {
      return fmt.Errorf("from must be an existing cronjob")
    }

    job = o.createJobFromCronJob(cronJob)//從cronjob建立job
  }
  if !o.DryRun {
    var err error
    job, err = o.Client.Jobs(o.Namespace).Create(job)//用client建立job
    if err != nil {
      return fmt.Errorf("failed to create job: %v", err)
    }
  }

  return o.PrintObj(job)//列印結果
}      
func (o *CreateJobOptions) createJob() *batchv1.Job {
  return &batchv1.Job{//建立job對象
    // this is ok because we know exactly how we want to be serialized
    TypeMeta: metav1.TypeMeta{APIVersion: batchv1.SchemeGroupVersion.String(), Kind: "Job"},
    ObjectMeta: metav1.ObjectMeta{
      Name: o.Name,
    },
    Spec: batchv1.JobSpec{
      Template: corev1.PodTemplateSpec{
        Spec: corev1.PodSpec{
          Containers: []corev1.Container{
            {
              Name:    o.Name,
              Image:   o.Image,
              Command: o.Command,
            },
          },
          RestartPolicy: corev1.RestartPolicyNever,
        },
      },
    },
  }
}

//從cronjob建立job對象
func (o *CreateJobOptions) createJobFromCronJob(cronJob *batchv1beta1.CronJob) *batchv1.Job {
  annotations := make(map[string]string)
  annotations["cronjob.kubernetes.io/instantiate"] = "manual"
  for k, v := range cronJob.Spec.JobTemplate.Annotations {
    annotations[k] = v
  }

  return &batchv1.Job{
    // this is ok because we know exactly how we want to be serialized
    TypeMeta: metav1.TypeMeta{APIVersion: batchv1.SchemeGroupVersion.String(), Kind: "Job"},
    ObjectMeta: metav1.ObjectMeta{
      Name:        o.Name,
      Annotations: annotations,
      Labels:      cronJob.Spec.JobTemplate.Labels,
      OwnerReferences: []metav1.OwnerReference{
        *metav1.NewControllerRef(cronJob, appsv1.SchemeGroupVersion.WithKind("CronJob")),
      },
    },
    Spec: cronJob.Spec.JobTemplate.Spec,
  }
}