这一讲我们来看kubectl patch更新资源对象命令,它的实现在NewCmdPatch方法中

  1. 获取PatchOptions中保存的命令行输入的kubectl patch 后面跟的参数(如--all-namespace,--output,--patch等)
  2. 构建cmd patch命令,注册patch命令的实现方法
  3. 给patch命令添加相应的参数来控制patch命令的操作
func NewCmdPatch(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
	o := NewPatchOptions(ioStreams)

	// 构建cmd patch命令
	cmd := &cobra.Command{
		Use: "patch (-f FILENAME | TYPE NAME) -p PATCH",
		DisableFlagsInUseLine: true,
		Short:   i18n.T("Update field(s) of a resource using strategic merge patch"),
		Long:    patchLong,
		Example: patchExample,
		// 代码实际运行部分
		Run: func(cmd *cobra.Command, args []string) {
			cmdutil.CheckErr(o.Complete(f, cmd, args))
			cmdutil.CheckErr(o.Validate())
			// patch请求的具体实现方法
			cmdutil.CheckErr(o.RunPatch())
		},
	}

	o.RecordFlags.AddFlags(cmd)
	o.PrintFlags.AddFlags(cmd)

	// 参数flag提供修饰符来控制动作命令的操作,即给命令添加参数,这些参数都保存在PatchOptions中
	cmd.Flags().StringVarP(&o.Patch, "patch", "p", "", "The patch to be applied to the resource JSON file.")
	cmd.MarkFlagRequired("patch")
	cmd.Flags().StringVar(&o.PatchType, "type", "strategic", fmt.Sprintf("The type of patch being provided; one of %v", sets.StringKeySet(patchTypes).List()))
	cmdutil.AddDryRunFlag(cmd)
	cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, "identifying the resource to update")
	cmd.Flags().BoolVar(&o.Local, "local", o.Local, "If true, patch will operate on the content of the file, not the server-side resource.")

	return cmd
}

下面来看Patch命令具体的实现函数RunPatch,RunPatch函数首先从命令行中读取输入的patch类型和patchBytes补丁内容,然后同get命令一样构建Builder-Visitor资源对象访问机制,代码实现如下:

     //获取patch的类型
	patchType := types.StrategicMergePatchType
	if len(o.PatchType) != 0 {
		patchType = patchTypes[strings.ToLower(o.PatchType)]
	}
	//获取patchBytes补丁内容
	patchBytes, err := yaml.ToJSON([]byte(o.Patch))
	if err != nil {
		return fmt.Errorf("unable to parse %q: %v", o.Patch, err)
	}
	//构建Builder,Visitor
	r := o.builder.
		Unstructured().
		ContinueOnError().
		NamespaceParam(o.namespace).DefaultNamespace().
		FilenameParam(o.enforceNamespace, &o.FilenameOptions).
		ResourceTypeOrNameArgs(false, o.args...).
		Flatten().
		Do()
	err = r.Err()
	if err != nil {
		return err
	}

其中,Builder-Visitor已经在上一讲做过详细介绍,有兴趣可以移步k8s1.11命令行——实现源码导读之kubectl get获取资源对象

通过上述步骤,我们构建了访问要patch的资源对象的访问器(Visitor),通过回调函数Visit要获取资源并执行patch请求,整个过程如下:

  1. 获取资源对象的名字,命名空间以及映射器mapping
  2. 由资源映射器mapping和客户端client构建helper(Helper提供了检索或改变RESTful资源的方法)
  3. 调用helper的Patch方法通过向apiServer发送patch请求更新资源对象并返回
  4. 检查源对象info.Object和更新后的对象patchedObj是否一致
  5. 如果记录Recorder进行了更改,计算和创建另外一个patch(Recorder在注释中记录了runtime.Object更改的原因)
  6. 使用更新后的对象patchedObj更新源对象info.Object
  7. 打印输出

代码实现如下:

helper.go中的Patch方法:

func (m *Helper) Patch(namespace, name string, pt types.PatchType, data []byte) (runtime.Object, error) {
	return m.RESTClient.Patch(pt).
		NamespaceIfScoped(namespace, m.NamespaceScoped).
		Resource(m.Resource).
		Name(name).
		Body(data).
		Do().
		Get()
}

 

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐