kubectl源码分析之diff

 欢迎关注我的公众号:

 目前刚开始写一个月,一共写了18篇原创文章,文章目录如下:

istio多集群探秘,部署了50次多集群后我得出的结论

istio多集群链路追踪,附实操视频

istio防故障利器,你知道几个,istio新手不要读,太难!

istio业务权限控制,原来可以这么玩

istio实现非侵入压缩,微服务之间如何实现压缩

不懂envoyfilter也敢说精通istio系列-http-rbac-不要只会用AuthorizationPolicy配置权限

不懂envoyfilter也敢说精通istio系列-02-http-corsFilter-不要只会vs

不懂envoyfilter也敢说精通istio系列-03-http-csrf filter-再也不用再代码里写csrf逻辑了

不懂envoyfilter也敢说精通istio系列http-jwt_authn-不要只会RequestAuthorization

不懂envoyfilter也敢说精通istio系列-05-fault-filter-故障注入不止是vs

不懂envoyfilter也敢说精通istio系列-06-http-match-配置路由不只是vs

不懂envoyfilter也敢说精通istio系列-07-负载均衡配置不止是dr

不懂envoyfilter也敢说精通istio系列-08-连接池和断路器

不懂envoyfilter也敢说精通istio系列-09-http-route filter

不懂envoyfilter也敢说精通istio系列-network filter-redis proxy

不懂envoyfilter也敢说精通istio系列-network filter-HttpConnectionManager

不懂envoyfilter也敢说精通istio系列-ratelimit-istio ratelimit完全手册

 

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

type DiffOptions struct {//diff结构体
	FilenameOptions resource.FilenameOptions

	ServerSideApply bool
	ForceConflicts  bool

	OpenAPISchema    openapi.Resources
	DiscoveryClient  discovery.DiscoveryInterface
	DynamicClient    dynamic.Interface
	DryRunVerifier   *apply.DryRunVerifier
	CmdNamespace     string
	EnforceNamespace bool
	Builder          *resource.Builder
	Diff             *DiffProgram
}
func NewDiffOptions(ioStreams genericclioptions.IOStreams) *DiffOptions {
	return &DiffOptions{//创建diff结构体
		Diff: &DiffProgram{
			Exec:      exec.New(),
			IOStreams: ioStreams,
		},
	}
}
//创建diff命令
func NewCmdDiff(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
	options := NewDiffOptions(streams)//初始化结构体
	cmd := &cobra.Command{//创建cobra命令
		Use:                   "diff -f FILENAME",
		DisableFlagsInUseLine: true,
		Short:                 i18n.T("Diff live version against would-be applied version"),
		Long:                  diffLong,
		Example:               diffExample,
		Run: func(cmd *cobra.Command, args []string) {
			cmdutil.CheckErr(options.Complete(f, cmd))//准备方法
			cmdutil.CheckErr(validateArgs(cmd, args))//校验
			cmdutil.CheckErr(options.Run())//运行
		},
	}

	usage := "contains the configuration to diff"
	cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage)//文件选项
	cmdutil.AddServerSideApplyFlags(cmd)//serverside选项

	return cmd
}
//准备
func (o *DiffOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
	var err error

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

	o.ServerSideApply = cmdutil.GetServerSideApplyFlag(cmd)//获取server-side选项
	o.ForceConflicts = cmdutil.GetForceConflictsFlag(cmd)//获取force-conflicts选项
	if o.ForceConflicts && !o.ServerSideApply {//如果指定force-conflicts选项则server-side选项为必须
		return fmt.Errorf("--force-conflicts only works with --server-side")
	}

	if !o.ServerSideApply {//如果没有设置server-side选项
		o.OpenAPISchema, err = f.OpenAPISchema()//获取OpenAPISchema
		if err != nil {
			return err
		}
	}

	o.DiscoveryClient, err = f.ToDiscoveryClient()//设置DiscoveryClient
	if err != nil {
		return err
	}

	o.DynamicClient, err = f.DynamicClient()//设置DynamicClient
	if err != nil {
		return err
	}

	o.DryRunVerifier = &apply.DryRunVerifier{//设置干跑校验器
		Finder:        cmdutil.NewCRDFinder(cmdutil.CRDFromDynamic(o.DynamicClient)),
		OpenAPIGetter: o.DiscoveryClient,
	}

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

	o.Builder = f.NewBuilder()//设置builder
	return nil
}
//校验
func validateArgs(cmd *cobra.Command, args []string) error {
	if len(args) != 0 {//参数必须是0个
		return cmdutil.UsageErrorf(cmd, "Unexpected args: %v", args)
	}
	return nil
}
//运行
func (o *DiffOptions) Run() error {
	differ, err := NewDiffer("LIVE", "MERGED")//构造differ
	if err != nil {
		return err
	}
	defer differ.TearDown()//清理diff

	printer := Printer{}//构造printer

	r := o.Builder.
		Unstructured().
		NamespaceParam(o.CmdNamespace).DefaultNamespace().
		FilenameParam(o.EnforceNamespace, &o.FilenameOptions).
		Flatten().
		Do()//构造result对象
	if err := r.Err(); err != nil {//result有错误返回
		return err
	}

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

		if err := o.DryRunVerifier.HasSupport(info.Mapping.GroupVersionKind); err != nil {//校验是否支持干跑
			return err
		}

		local := info.Object.DeepCopyObject()//拷贝info.Object
		for i := 1; i <= maxRetries; i++ {//最大尝试次数maxRetries,循环
			if err = info.Get(); err != nil {//获取info
				if !errors.IsNotFound(err) {//如果是非找到错误,返回
					return err
				}
				info.Object = nil//如果info没找到,info.Object设为空
			}

			force := i == maxRetries//如果尝试次数到达最大重试次数,则输出告警
			if force {
				klog.Warningf(
					"Object (%v: %v) keeps changing, diffing without lock",
					info.Object.GetObjectKind().GroupVersionKind(),
					info.Name,
				)
			}
			obj := InfoObject{//构造InfoObject对象
				LocalObj:        local,
				Info:            info,
				Encoder:         scheme.DefaultJSONEncoder(),
				OpenAPI:         o.OpenAPISchema,
				Force:           force,
				ServerSideApply: o.ServerSideApply,
				ForceConflicts:  o.ForceConflicts,
			}

			err = differ.Diff(obj, printer)//准备diff
			if !isConflict(err) {//入股欧式非冲突错误,跳出循环
				break
			}
		}
		return err
	})
	if err != nil {
		return err
	}

	return differ.Run(o.Diff)//运行diff
}
type Differ struct {//differ 结构体
	From *DiffVersion
	To   *DiffVersion
}
func NewDiffer(from, to string) (*Differ, error) {//创建differ
	differ := Differ{}//构造differ结构体
	var err error
	differ.From, err = NewDiffVersion(from)//创建from
	if err != nil {
		return nil, err
	}
	differ.To, err = NewDiffVersion(to)//创建to
	if err != nil {
		differ.From.Dir.Delete()//删除from
		return nil, err
	}

	return &differ, nil//返回
}
//准备diff
func (d *Differ) Diff(obj Object, printer Printer) error {
	if err := d.From.Print(obj, printer); err != nil {//把yaml文件输出到from
		return err
	}
	if err := d.To.Print(obj, printer); err != nil {//把yaml文件输出到to
		return err
	}
	return nil
}

// Run runs the diff program against both directories.
func (d *Differ) Run(diff *DiffProgram) error {//运行diff
	return diff.Run(d.From.Dir.Name, d.To.Dir.Name)
}

// TearDown removes both temporary directories recursively.
func (d *Differ) TearDown() {//清理diff
	d.From.Dir.Delete() // Ignore error删除from目录
	d.To.Dir.Delete()   // Ignore error删除to目录
}
type DiffVersion struct {//diffVersion结构体
	Dir  *Directory
	Name string
}

// NewDiffVersion creates a new DiffVersion with the named version.
func NewDiffVersion(name string) (*DiffVersion, error) {//DiffVersion
	dir, err := CreateDirectory(name)//创建目录
	if err != nil {
		return nil, err
	}
	return &DiffVersion{//构造diffVersion
		Dir:  dir,
		Name: name,
	}, nil
}

func (v *DiffVersion) getObject(obj Object) (runtime.Object, error) {//获取对象
	switch v.Name {
	case "LIVE"://获取live对象
		return obj.Live(), nil
	case "MERGED"://获取merged对象
		return obj.Merged()
	}
	return nil, fmt.Errorf("Unknown version: %v", v.Name)
}

// Print prints the object using the printer into a new file in the directory.
func (v *DiffVersion) Print(obj Object, printer Printer) error {//打印对象到文件
	vobj, err := v.getObject(obj)//获取对象
	if err != nil {
		return err
	}
	f, err := v.Dir.NewFile(obj.Name())//创建文件
	if err != nil {
		return err
	}
	defer f.Close()
	return printer.Print(vobj, f)//输出对象到文件
}
type Printer struct{}//printer结构体

// Print the object inside the writer w.
func (p *Printer) Print(obj runtime.Object, w io.Writer) error {//打印对象到文件
	if obj == nil {
		return nil
	}
	data, err := yaml.Marshal(obj)//对象转yaml
	if err != nil {
		return err
	}
	_, err = w.Write(data)//输出到文件
	return err

}
type DiffProgram struct {//diffProgram结构体
	Exec exec.Interface
	genericclioptions.IOStreams
}

func (d *DiffProgram) getCommand(args ...string) exec.Cmd {//获取命令
	diff := ""
	if envDiff := os.Getenv("KUBECTL_EXTERNAL_DIFF"); envDiff != "" {//获取环境变量
		diff = envDiff
	} else {//环境变量不存在则用diff
		diff = "diff"
		args = append([]string{"-u", "-N"}, args...)//设置参数
	}

	cmd := d.Exec.Command(diff, args...)//获取cmd
	cmd.SetStdout(d.Out)
	cmd.SetStderr(d.ErrOut)

	return cmd//返回cmd
}

// Run runs the detected diff program. `from` and `to` are the directory to diff.
func (d *DiffProgram) Run(from, to string) error {//运行diffProgram
	return d.getCommand(from, to).Run()
}
type Directory struct {//directory结构体
	Name string
}

// CreateDirectory does create the actual disk directory, and return a
// new representation of it.
func CreateDirectory(prefix string) (*Directory, error) {//创建临时目录
	name, err := ioutil.TempDir("", prefix+"-")//创建临时目录
	if err != nil {
		return nil, err
	}

	return &Directory{//返回目录
		Name: name,
	}, nil
}

// NewFile creates a new file in the directory.
func (d *Directory) NewFile(name string) (*os.File, error) {//在目录下创建文件
	return os.OpenFile(filepath.Join(d.Name, name), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0700)
}

// Delete removes the directory recursively.
func (d *Directory) Delete() error {//删除目录
	return os.RemoveAll(d.Name)
}
type Object interface {//Object接口
	Live() runtime.Object
	Merged() (runtime.Object, error)

	Name() string
}

// InfoObject is an implementation of the Object interface. It gets all
// the information from the Info object.
type InfoObject struct {//InfoObject结构体
	LocalObj        runtime.Object
	Info            *resource.Info
	Encoder         runtime.Encoder
	OpenAPI         openapi.Resources
	Force           bool
	ServerSideApply bool
	ForceConflicts  bool
}

var _ Object = &InfoObject{}

// Returns the live version of the object
获取live对象
func (obj InfoObject) Live() runtime.Object {
	return obj.Info.Object
}

// Returns the "merged" object, as it would look like if applied or
// created.
//获取merged对象
func (obj InfoObject) Merged() (runtime.Object, error) {
	if obj.ServerSideApply {//如果指定server-side
		data, err := runtime.Encode(unstructured.UnstructuredJSONScheme, obj.LocalObj)//把localObj编码为json
		if err != nil {
			return nil, err
		}
		options := metav1.PatchOptions{//设置patch选项
			Force:  &obj.ForceConflicts,
			DryRun: []string{metav1.DryRunAll},
		}
		return resource.NewHelper(obj.Info.Client, obj.Info.Mapping).Patch(
			obj.Info.Namespace,
			obj.Info.Name,
			types.ApplyPatchType,
			data,
			&options,
		)//干跑patch到服务端,返回结果
	}

	// Build the patcher, and then apply the patch with dry-run, unless the object doesn't exist, in which case we need to create it.
	if obj.Live() == nil {//如果live为空
		// Dry-run create if the object doesn't exist.
		return resource.NewHelper(obj.Info.Client, obj.Info.Mapping).Create(
			obj.Info.Namespace,
			true,
			obj.LocalObj,
			&metav1.CreateOptions{DryRun: []string{metav1.DryRunAll}},
		)//干跑创建到服务端,返回结果
	}

	var resourceVersion *string
	if !obj.Force {//如果force为true,获取resourceVersion
		accessor, err := meta.Accessor(obj.Info.Object)
		if err != nil {
			return nil, err
		}
		str := accessor.GetResourceVersion()
		resourceVersion = &str
	}

	modified, err := util.GetModifiedConfiguration(obj.LocalObj, false, unstructured.UnstructuredJSONScheme)//获取yaml配置
	if err != nil {
		return nil, err
	}

	// This is using the patcher from apply, to keep the same behavior.
	// We plan on replacing this with server-side apply when it becomes available.
	patcher := &apply.Patcher{//创建patcher
		Mapping:         obj.Info.Mapping,
		Helper:          resource.NewHelper(obj.Info.Client, obj.Info.Mapping),
		Overwrite:       true,
		BackOff:         clockwork.NewRealClock(),
		ServerDryRun:    true,
		OpenapiSchema:   obj.OpenAPI,
		ResourceVersion: resourceVersion,
	}

	_, result, err := patcher.Patch(obj.Info.Object, modified, obj.Info.Source, obj.Info.Namespace, obj.Info.Name, nil)//应用patch,返回结果
	return result, err
}

func (obj InfoObject) Name() string {获取文件名称
	group := ""
	if obj.Info.Mapping.GroupVersionKind.Group != "" {设置group
		group = fmt.Sprintf("%v.", obj.Info.Mapping.GroupVersionKind.Group)
	}
	return group + fmt.Sprintf(
		"%v.%v.%v.%v",
		obj.Info.Mapping.GroupVersionKind.Version,
		obj.Info.Mapping.GroupVersionKind.Kind,
		obj.Info.Namespace,
		obj.Info.Name,
	)//返回拼接的名称
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hxpjava1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值