kubectl源码分析之apply

 欢迎关注我的公众号:

 目前刚开始写一个月,一共写了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 ApplyOptions struct {//apply结构体
	RecordFlags *genericclioptions.RecordFlags
	Recorder    genericclioptions.Recorder

	PrintFlags *genericclioptions.PrintFlags
	ToPrinter  func(string) (printers.ResourcePrinter, error)

	DeleteFlags   *delete.DeleteFlags
	DeleteOptions *delete.DeleteOptions

	ServerSideApply bool
	ForceConflicts  bool
	FieldManager    string
	Selector        string
	DryRun          bool
	ServerDryRun    bool
	Prune           bool
	PruneResources  []pruneResource
	cmdBaseName     string
	All             bool
	Overwrite       bool
	OpenAPIPatch    bool
	PruneWhitelist  []string

	Validator       validation.Schema
	Builder         *resource.Builder
	Mapper          meta.RESTMapper
	DynamicClient   dynamic.Interface
	DiscoveryClient discovery.DiscoveryInterface
	OpenAPISchema   openapi.Resources

	Namespace        string
	EnforceNamespace bool

	genericclioptions.IOStreams
}
func NewApplyOptions(ioStreams genericclioptions.IOStreams) *ApplyOptions {
	return &ApplyOptions{//初始化apply结构体
		RecordFlags: genericclioptions.NewRecordFlags(),
		DeleteFlags: delete.NewDeleteFlags("that contains the configuration to apply"),
		PrintFlags:  genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme),

		Overwrite:    true,
		OpenAPIPatch: true,

		Recorder: genericclioptions.NoopRecorder{},

		IOStreams: ioStreams,
	}
}
//创建apply命令
func NewCmdApply(baseName string, f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
	o := NewApplyOptions(ioStreams)//初始化结构体

	// Store baseName for use in printing warnings / messages involving the base command name.
	// This is useful for downstream command that wrap this one.
	o.cmdBaseName = baseName//基命令名称

	cmd := &cobra.Command{//创建cobra命令
		Use:                   "apply (-f FILENAME | -k DIRECTORY)",
		DisableFlagsInUseLine: true,
		Short:                 i18n.T("Apply a configuration to a resource by filename or stdin"),
		Long:                  applyLong,
		Example:               applyExample,
		Run: func(cmd *cobra.Command, args []string) {
			cmdutil.CheckErr(o.Complete(f, cmd))//准备
			cmdutil.CheckErr(validateArgs(cmd, args))//校验
			cmdutil.CheckErr(validatePruneAll(o.Prune, o.All, o.Selector))//校验
			cmdutil.CheckErr(o.Run())//运行
		},
	}

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

	cmd.Flags().BoolVar(&o.Overwrite, "overwrite", o.Overwrite, "Automatically resolve conflicts between the modified and live configuration by using values from the modified configuration")//客户端patch时是否覆盖选项
	cmd.Flags().BoolVar(&o.Prune, "prune", o.Prune, "Automatically delete resource objects, including the uninitialized ones, that do not appear in the configs and are created by either apply or create --save-config. Should be used with either -l or --all.")//是否修剪选项
	cmdutil.AddValidateFlags(cmd)//校验选项
	cmd.Flags().StringVarP(&o.Selector, "selector", "l", o.Selector, "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)")//selector选项
	cmd.Flags().BoolVar(&o.All, "all", o.All, "Select all resources in the namespace of the specified resource types.")//all选项
	cmd.Flags().StringArrayVar(&o.PruneWhitelist, "prune-whitelist", o.PruneWhitelist, "Overwrite the default whitelist with <group/version/kind> for --prune")//prune白名单选项
	cmd.Flags().BoolVar(&o.OpenAPIPatch, "openapi-patch", o.OpenAPIPatch, "If true, use openapi to calculate diff when the openapi presents and the resource can be found in the openapi spec. Otherwise, fall back to use baked-in types.")//用openapi比较差异选项
	cmd.Flags().BoolVar(&o.ServerDryRun, "server-dry-run", o.ServerDryRun, "If true, request will be sent to server with dry-run flag, which means the modifications won't be persisted. This is an alpha feature and flag.")//服务器干跑选项
	cmd.Flags().Bool("dry-run", false, "If true, only print the object that would be sent, without sending it. Warning: --dry-run cannot accurately output the result of merging the local manifest and the server-side data. Use --server-dry-run to get the merged result instead.")//干跑选项
	cmdutil.AddIncludeUninitializedFlag(cmd)
	cmdutil.AddServerSideApplyFlags(cmd)//服务端patch选项

	// apply subcommands
	cmd.AddCommand(NewCmdApplyViewLastApplied(f, ioStreams))//添加view命令
	cmd.AddCommand(NewCmdApplySetLastApplied(f, ioStreams))//添加set命令
	cmd.AddCommand(NewCmdApplyEditLastApplied(f, ioStreams))//添加edit选项

	return cmd
}
//准备
func (o *ApplyOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
	o.ServerSideApply = cmdutil.GetServerSideApplyFlag(cmd)//获取server-side选项
	o.ForceConflicts = cmdutil.GetForceConflictsFlag(cmd)//获取force-confilict选项
	o.FieldManager = cmdutil.GetFieldManagerFlag(cmd)//获取field-manager选项
	o.DryRun = cmdutil.GetDryRunFlag(cmd)//获取干跑选项

	if o.ForceConflicts && !o.ServerSideApply {//如果指定了force-conflicts则server-side必须指定
		return fmt.Errorf("--force-conflicts only works with --server-side")
	}

	if o.DryRun && o.ServerSideApply {//客户端干跑和server-side不能同时指定
		return fmt.Errorf("--dry-run doesn't work with --server-side (did you mean --server-dry-run instead?)")
	}

	if o.DryRun && o.ServerDryRun {//客户端干跑和服务端干跑不能同时指定
		return fmt.Errorf("--dry-run and --server-dry-run can't be used together")
	}

	// allow for a success message operation to be specified at print time
	o.ToPrinter = func(operation string) (printers.ResourcePrinter, error) {//print 选项转printer函数
		o.PrintFlags.NamePrintFlags.Operation = operation
		if o.DryRun {
			o.PrintFlags.Complete("%s (dry run)")
		}
		if o.ServerDryRun {
			o.PrintFlags.Complete("%s (server dry run)")
		}
		return o.PrintFlags.ToPrinter()
	}

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

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

	dynamicClient, err := f.DynamicClient()//设置dynamicClient
	if err != nil {
		return err
	}
	o.DeleteOptions = o.DeleteFlags.ToOptions(dynamicClient, o.IOStreams)//delete选项转deleteOption
	err = o.DeleteOptions.FilenameOptions.RequireFilenameOrKustomize()//文件必须
	if err != nil {
		return err
	}

	o.OpenAPISchema, _ = f.OpenAPISchema()//设置OpenApiSchema
	o.Validator, err = f.Validator(cmdutil.GetFlagBool(cmd, "validate"))//设置validator校验器
	o.Builder = f.NewBuilder()//设置builder
	o.Mapper, err = f.ToRESTMapper()//设置restMapper
	if err != nil {
		return err
	}

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

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

	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
}

//校验prune参数
func validatePruneAll(prune, all bool, selector string) error {
	if all && len(selector) > 0 {//all和selector不能同时指定
		return fmt.Errorf("cannot set --all and --selector at the same time")
	}
	if prune && !all && selector == "" {//如果指定了prune必须指定all或selector
		return fmt.Errorf("all resources selected for prune without explicitly passing --all. To prune all resources, pass the --all flag. If you did not mean to prune all resources, specify a label selector")
	}
	return nil
}
//运行
func (o *ApplyOptions) Run() error {
	var openapiSchema openapi.Resources
	if o.OpenAPIPatch {//如果指定了用openapi比较patch则设置openapiSchema
		openapiSchema = o.OpenAPISchema
	}

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

	// include the uninitialized objects by default if --prune is true
	// unless explicitly set --include-uninitialized=false
	r := o.Builder.
		Unstructured().
		Schema(o.Validator).
		ContinueOnError().
		NamespaceParam(o.Namespace).DefaultNamespace().
		FilenameParam(o.EnforceNamespace, &o.DeleteOptions.FilenameOptions).
		LabelSelectorParam(o.Selector).
		Flatten().
		Do()//用builder构造result对象
	if err := r.Err(); err != nil {// result有错误返回错误
		return err
	}

	var err error
	if o.Prune {//如果指定了prune,解析prune资源
		o.PruneResources, err = parsePruneResources(o.Mapper, o.PruneWhitelist)
		if err != nil {
			return err
		}
	}

	output := *o.PrintFlags.OutputFormat
	shortOutput := output == "name"//如果--output为name则短输出为true

	visitedUids := sets.NewString()//访问过的uid的string集合
	visitedNamespaces := sets.NewString()//访问过的namespace的string集合

	var objs []runtime.Object//Object切片

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

		// If server-dry-run is requested but the type doesn't support it, fail right away.
		if o.ServerDryRun {//如果指定了服务端干跑,校验gvk是否支持干跑
			if err := dryRunVerifier.HasSupport(info.Mapping.GroupVersionKind); err != nil {
				return err
			}
		}

		if info.Namespaced() {//如果info是名称空间资源,则把名称空间加到访问过的名称空间集合里
			visitedNamespaces.Insert(info.Namespace)
		}

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

		if o.ServerSideApply {//如果指定了server-side
			// Send the full object to be applied on the server side.
			data, err := runtime.Encode(unstructured.UnstructuredJSONScheme, info.Object)//把object编码为json
			if err != nil {
				return cmdutil.AddSourceToErr("serverside-apply", info.Source, err)
			}

			options := metav1.PatchOptions{//创建patchOption结构体
				Force:        &o.ForceConflicts,
				FieldManager: o.FieldManager,
			}
			if o.ServerDryRun {//如果指定了服务端干跑,则设置option干跑属性
				options.DryRun = []string{metav1.DryRunAll}
			}

			obj, err := resource.NewHelper(info.Client, info.Mapping).Patch(//应用全量patch到服务端
				info.Namespace,
				info.Name,
				types.ApplyPatchType,
				data,
				&options,
			)
			if err != nil {
				if isIncompatibleServerError(err) {
					err = fmt.Errorf("Server-side apply not available on the server: (%v)", err)
				}
				if errors.IsConflict(err) {
					err = fmt.Errorf(`%v

Please review the fields above--they currently have other managers. Here
are the ways you can resolve this warning:
* If you intend to manage all of these fields, please re-run the apply
  command with the `+"`--force-conflicts`"+` flag.
* If you do not intend to manage all of the fields, please edit your
  manifest to remove references to the fields that should keep their
  current managers.
* You may co-own fields by updating your manifest to match the existing
  value; in this case, you'll become the manager if the other manager(s)
  stop managing the field (remove it from their configuration).

See http://k8s.io/docs/reference/using-api/api-concepts/#conflicts`, err)
				}
				return err
			}

			info.Refresh(obj, true)//刷新对象
			metadata, err := meta.Accessor(info.Object)//访问对象元数据
			if err != nil {
				return err
			}

			visitedUids.Insert(string(metadata.GetUID()))//获取对象uid,插入到访问过的uid集合里
			count++// count 加1
			if len(output) > 0 && !shortOutput {//如果指定了output并且不是shortOutput则添加对象到object集合里
				objs = append(objs, info.Object)
				return nil
			}

			printer, err := o.ToPrinter("serverside-applied")// printflag转printer
			if err != nil {
				return err
			}

			return printer.PrintObj(info.Object, o.Out)//打印对象
		}

		// Get the modified configuration of the object. Embed the result
		// as an annotation in the modified configuration, so that it will appear
		// in the patch sent to the server.
		modified, err := util.GetModifiedConfiguration(info.Object, true, unstructured.UnstructuredJSONScheme)//获取修改后的对象配置信息
		if err != nil {
			return cmdutil.AddSourceToErr(fmt.Sprintf("retrieving modified configuration from:\n%s\nfor:", info.String()), info.Source, err)
		}

		// Print object only if output format other than "name" is specified
		printObject := len(output) > 0 && !shortOutput//如果指定了output并且不是shortOutput则打印对象为true

		if err := info.Get(); err != nil {//获取远程info对象
			if !errors.IsNotFound(err) {//如果不是非找到错误则返回错误
				return cmdutil.AddSourceToErr(fmt.Sprintf("retrieving current configuration of:\n%s\nfrom server for:", info.String()), info.Source, err)
			}

			// Create the resource if it doesn't exist
			// First, update the annotation used by kubectl apply
			if err := util.CreateApplyAnnotation(info.Object, unstructured.UnstructuredJSONScheme); err != nil {//判断是否创建last-applied-configuration注解
				return cmdutil.AddSourceToErr("creating", info.Source, err)
			}

			if !o.DryRun {//如果是非干跑
				// Then create the resource and skip the three-way merge
				options := metav1.CreateOptions{}//创建createOption
				if o.ServerDryRun {//如果是服务端干跑
					options.DryRun = []string{metav1.DryRunAll}
				}
				obj, err := resource.NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, info.Object, &options)//创建对象到服务端
				if err != nil {
					return cmdutil.AddSourceToErr("creating", info.Source, err)
				}
				info.Refresh(obj, true)//刷新对象
			}

			metadata, err := meta.Accessor(info.Object)// 访问对象元数据
			if err != nil {
				return err
			}
			visitedUids.Insert(string(metadata.GetUID()))//添加对象uid到访问过的uid列表里

			count++

			if printObject {//如果是打印对象,则把对象加到objs切片里
				objs = append(objs, info.Object)
				return nil
			}

			printer, err := o.ToPrinter("created")//print flag转printer
			if err != nil {
				return err
			}
			return printer.PrintObj(info.Object, o.Out)//打印对象
		}

		metadata, err := meta.Accessor(info.Object)//访问对象元数据
		if err != nil {
			return err
		}
		visitedUids.Insert(string(metadata.GetUID()))//把对象uid加到访问过的uid里面

		if !o.DryRun {//非干跑
			annotationMap := metadata.GetAnnotations()//获取注解
			if _, ok := annotationMap[corev1.LastAppliedConfigAnnotation]; !ok {//如果没有last-applied-configuration注解输出告警
				fmt.Fprintf(o.ErrOut, warningNoLastAppliedConfigAnnotation, o.cmdBaseName)
			}

			helper := resource.NewHelper(info.Client, info.Mapping)//创建helper
			patcher := &Patcher{//创建patcher结构体
				Mapping:       info.Mapping,
				Helper:        helper,
				DynamicClient: o.DynamicClient,
				Overwrite:     o.Overwrite,
				BackOff:       clockwork.NewRealClock(),
				Force:         o.DeleteOptions.ForceDeletion,
				Cascade:       o.DeleteOptions.Cascade,
				Timeout:       o.DeleteOptions.Timeout,
				GracePeriod:   o.DeleteOptions.GracePeriod,
				ServerDryRun:  o.ServerDryRun,
				OpenapiSchema: openapiSchema,
				Retries:       maxPatchRetry,
			}

			patchBytes, patchedObject, err := patcher.Patch(info.Object, modified, info.Source, info.Namespace, info.Name, o.ErrOut)//应用patch到服务端
			if err != nil {
				return cmdutil.AddSourceToErr(fmt.Sprintf("applying patch:\n%s\nto:\n%v\nfor:", patchBytes, info), info.Source, err)
			}

			info.Refresh(patchedObject, true)//刷新对象

			if string(patchBytes) == "{}" && !printObject {//如果patchBytes为空,并且不打印对象
				count++

				printer, err := o.ToPrinter("unchanged")//print flag转printer
				if err != nil {
					return err
				}
				return printer.PrintObj(info.Object, o.Out)// 打印对象
			}
		}
		count++

		if printObject {//如果打印对象,把对象放到objs切片中
			objs = append(objs, info.Object)
			return nil
		}

		printer, err := o.ToPrinter("configured")//print flag转printer
		if err != nil {
			return err
		}
		return printer.PrintObj(info.Object, o.Out)//打印对象
	})
	if err != nil {
		return err
	}

	if count == 0 {//count为0个返回错误
		return fmt.Errorf("no objects passed to apply")
	}

	// print objects
	if len(objs) > 0 {// 如果objs个数大于0个
		printer, err := o.ToPrinter("")//print flag转printer
		if err != nil {
			return err
		}

		objToPrint := objs[0]
		if len(objs) > 1 {//如果objs大于1个
			list := &corev1.List{//创建list对象
				TypeMeta: metav1.TypeMeta{
					Kind:       "List",
					APIVersion: "v1",
				},
				ListMeta: metav1.ListMeta{},
			}
			if err := meta.SetList(list, objs); err != nil {
				return err
			}

			objToPrint = list//打印的对象设为list
		}
		if err := printer.PrintObj(objToPrint, o.Out); err != nil {//打印对象
			return err
		}
	}

	if !o.Prune {//如果没有指定prune,则返回
		return nil
	}

	p := pruner{//构造pruner对象
		mapper:        o.Mapper,
		dynamicClient: o.DynamicClient,

		labelSelector: o.Selector,
		visitedUids:   visitedUids,

		cascade:      o.DeleteOptions.Cascade,
		dryRun:       o.DryRun,
		serverDryRun: o.ServerDryRun,
		gracePeriod:  o.DeleteOptions.GracePeriod,

		toPrinter: o.ToPrinter,

		out: o.Out,
	}

	namespacedRESTMappings, nonNamespacedRESTMappings, err := getRESTMappings(o.Mapper, &(o.PruneResources))//获取namespacedRestMapping和nonNamespacedRestmapping
	if err != nil {
		return fmt.Errorf("error retrieving RESTMappings to prune: %v", err)
	}

	for n := range visitedNamespaces {//遍历访问过的名称空间
		for _, m := range namespacedRESTMappings {//遍历namespacedRESTMappings 
			if err := p.prune(n, m); err != nil {//执行修剪
				return fmt.Errorf("error pruning namespaced object %v: %v", m.GroupVersionKind, err)
			}
		}
	}
	for _, m := range nonNamespacedRESTMappings {//遍历nonNamespacedRESTMappings 
		if err := p.prune(metav1.NamespaceNone, m); err != nil {//执行修剪
			return fmt.Errorf("error pruning nonNamespaced object %v: %v", m.GroupVersionKind, err)
		}
	}

	return nil
}
//解析prune资源
func parsePruneResources(mapper meta.RESTMapper, gvks []string) ([]pruneResource, error) {
	pruneResources := []pruneResource{}
	for _, groupVersionKind := range gvks {//遍历gvks slice
		gvk := strings.Split(groupVersionKind, "/")//用/分割
		if len(gvk) != 3 {//分割结果不为3个报错
			return nil, fmt.Errorf("invalid GroupVersionKind format: %v, please follow <group/version/kind>", groupVersionKind)
		}

		if gvk[0] == "core" {//如果第0个结果为core,则设置为空字符串
			gvk[0] = ""
		}
		mapping, err := mapper.RESTMapping(schema.GroupKind{Group: gvk[0], Kind: gvk[2]}, gvk[1])//根据gvk获取mapping
		if err != nil {
			return pruneResources, err
		}
		var namespaced bool
		namespaceScope := mapping.Scope.Name()//判断是否名称空间资源
		switch namespaceScope {
		case meta.RESTScopeNameNamespace:
			namespaced = true
		case meta.RESTScopeNameRoot:
			namespaced = false
		default:
			return pruneResources, fmt.Errorf("Unknown namespace scope: %q", namespaceScope)
		}

		pruneResources = append(pruneResources, pruneResource{gvk[0], gvk[1], gvk[2], namespaced})//append pruneREsource
	}
	return pruneResources, nil
}
//判断gvk是否支持干跑
func (v *DryRunVerifier) HasSupport(gvk schema.GroupVersionKind) error {
	oapi, err := v.OpenAPIGetter.OpenAPISchema()//获取openapiSchema
	if err != nil {
		return fmt.Errorf("failed to download openapi: %v", err)
	}
	supports, err := openapi.SupportsDryRun(oapi, gvk)//判断是否支持干跑
	if err != nil {
		// We assume that we couldn't find the type, then check for namespace:
		supports, _ = openapi.SupportsDryRun(oapi, schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Namespace"})//判断是否支持干跑
		// If namespace supports dryRun, then we will support dryRun for CRDs only.
		if supports {
			supports, err = v.Finder.HasCRD(gvk.GroupKind())
			if err != nil {
				return fmt.Errorf("failed to check CRD: %v", err)
			}
		}
	}
	if !supports {//不支持则返回错误
		return fmt.Errorf("%v doesn't support dry-run", gvk)
	}
	return nil
}
//获取修改后的配置
func GetModifiedConfiguration(obj runtime.Object, annotate bool, codec runtime.Encoder) ([]byte, error) {
	// First serialize the object without the annotation to prevent recursion,
	// then add that serialization to it as the annotation and serialize it again.
	var modified []byte

	// Otherwise, use the server side version of the object.
	// Get the current annotations from the object.
	annots, err := metadataAccessor.Annotations(obj)//获取annotations
	if err != nil {
		return nil, err
	}

	if annots == nil {
		annots = map[string]string{}
	}

	original := annots[v1.LastAppliedConfigAnnotation]//保存原始的last-applied-configuation注解
	delete(annots, v1.LastAppliedConfigAnnotation)//删除last-applied-configuration注解
	if err := metadataAccessor.SetAnnotations(obj, annots); err != nil {//设置注解
		return nil, err
	}

	modified, err = runtime.Encode(codec, obj)//编码obj成json
	if err != nil {
		return nil, err
	}

	if annotate {// 如果包含annotations
		annots[v1.LastAppliedConfigAnnotation] = string(modified)//设置last-applied-configuation为修改后的json
		if err := metadataAccessor.SetAnnotations(obj, annots); err != nil {//设置注解
			return nil, err
		}

		modified, err = runtime.Encode(codec, obj)//编码obj为json
		if err != nil {
			return nil, err
		}
	}

	// Restore the object to its original condition.
	annots[v1.LastAppliedConfigAnnotation] = original//还原注解
	if err := metadataAccessor.SetAnnotations(obj, annots); err != nil {//设置注解
		return nil, err
	}

	return modified, nil//返回json
}
//应用增量patch到服务端
func (p *Patcher) Patch(current runtime.Object, modified []byte, source, namespace, name string, errOut io.Writer) ([]byte, runtime.Object, error) {
	var getErr error
	patchBytes, patchObject, err := p.patchSimple(current, modified, source, namespace, name, errOut)//应用patch
	if p.Retries == 0 {
		p.Retries = maxPatchRetry//设置重试次数
	}
	for i := 1; i <= p.Retries && errors.IsConflict(err); i++ {//如果错误是冲突错误,并小于重试次数,则循环应用patch到服务端
		if i > triesBeforeBackOff {
			p.BackOff.Sleep(backOffPeriod)
		}
		current, getErr = p.Helper.Get(namespace, name, false)//获取当前对象
		if getErr != nil {
			return nil, nil, getErr
		}
		patchBytes, patchObject, err = p.patchSimple(current, modified, source, namespace, name, errOut)//引用patch到服务端
	}
	if err != nil && (errors.IsConflict(err) || errors.IsInvalid(err)) && p.Force {//如果指定force,并且有错误,错误为冲突或无效,则执行删除后创建
		patchBytes, patchObject, err = p.deleteAndCreate(current, modified, namespace, name)
	}
	return patchBytes, patchObject, err
}
//应用patch到服务端
func (p *Patcher) patchSimple(obj runtime.Object, modified []byte, source, namespace, name string, errOut io.Writer) ([]byte, runtime.Object, error) {
	// Serialize the current configuration of the object from the server.
	current, err := runtime.Encode(unstructured.UnstructuredJSONScheme, obj)//把当前对象转成json
	if err != nil {
		return nil, nil, cmdutil.AddSourceToErr(fmt.Sprintf("serializing current configuration from:\n%v\nfor:", obj), source, err)
	}

	// Retrieve the original configuration of the object from the annotation.
	original, err := util.GetOriginalConfiguration(obj)//获取原始对象json
	if err != nil {
		return nil, nil, cmdutil.AddSourceToErr(fmt.Sprintf("retrieving original configuration from:\n%v\nfor:", obj), source, err)
	}

	var patchType types.PatchType
	var patch []byte
	var lookupPatchMeta strategicpatch.LookupPatchMeta
	var schema oapi.Schema
	createPatchErrFormat := "creating patch with:\noriginal:\n%s\nmodified:\n%s\ncurrent:\n%s\nfor:"

	// Create the versioned struct from the type defined in the restmapping
	// (which is the API version we'll be submitting the patch to)
	versionedObject, err := scheme.Scheme.New(p.Mapping.GroupVersionKind)//获取schema对象
	switch {
	case runtime.IsNotRegisteredError(err):
		// fall back to generic JSON merge patch
		patchType = types.MergePatchType
		preconditions := []mergepatch.PreconditionFunc{mergepatch.RequireKeyUnchanged("apiVersion"),
			mergepatch.RequireKeyUnchanged("kind"), mergepatch.RequireMetadataKeyUnchanged("name")}
		patch, err = jsonmergepatch.CreateThreeWayJSONMergePatch(original, modified, current, preconditions...)
		if err != nil {
			if mergepatch.IsPreconditionFailed(err) {
				return nil, nil, fmt.Errorf("%s", "At least one of apiVersion, kind and name was changed")
			}
			return nil, nil, cmdutil.AddSourceToErr(fmt.Sprintf(createPatchErrFormat, original, modified, current), source, err)
		}
	case err != nil:
		return nil, nil, cmdutil.AddSourceToErr(fmt.Sprintf("getting instance of versioned object for %v:", p.Mapping.GroupVersionKind), source, err)
	case err == nil:
		// Compute a three way strategic merge patch to send to server.
		patchType = types.StrategicMergePatchType//设置merge策略

		// Try to use openapi first if the openapi spec is available and can successfully calculate the patch.
		// Otherwise, fall back to baked-in types.
		if p.OpenapiSchema != nil {
			if schema = p.OpenapiSchema.LookupResource(p.Mapping.GroupVersionKind); schema != nil {
				lookupPatchMeta = strategicpatch.PatchMetaFromOpenAPI{Schema: schema}
				if openapiPatch, err := strategicpatch.CreateThreeWayMergePatch(original, modified, current, lookupPatchMeta, p.Overwrite); err != nil {//创建patch
					fmt.Fprintf(errOut, "warning: error calculating patch from openapi spec: %v\n", err)
				} else {
					patchType = types.StrategicMergePatchType
					patch = openapiPatch//设置patch
				}
			}
		}

		if patch == nil {
			lookupPatchMeta, err = strategicpatch.NewPatchMetaFromStruct(versionedObject)//获取patch元数据
			if err != nil {
				return nil, nil, cmdutil.AddSourceToErr(fmt.Sprintf(createPatchErrFormat, original, modified, current), source, err)
			}
			patch, err = strategicpatch.CreateThreeWayMergePatch(original, modified, current, lookupPatchMeta, p.Overwrite)//创建patch
			if err != nil {
				return nil, nil, cmdutil.AddSourceToErr(fmt.Sprintf(createPatchErrFormat, original, modified, current), source, err)
			}
		}
	}

	if string(patch) == "{}" {
		return patch, obj, nil
	}

	if p.ResourceVersion != nil {
		patch, err = addResourceVersion(patch, *p.ResourceVersion)//设置patch资源
		if err != nil {
			return nil, nil, cmdutil.AddSourceToErr("Failed to insert resourceVersion in patch", source, err)
		}
	}

	options := metav1.PatchOptions{}
	if p.ServerDryRun {
		options.DryRun = []string{metav1.DryRunAll}
	}

	patchedObj, err := p.Helper.Patch(namespace, name, patchType, patch, &options)//应用patch到服务端
	return patch, patchedObj, err
}
//删除重建资源
func (p *Patcher) deleteAndCreate(original runtime.Object, modified []byte, namespace, name string) ([]byte, runtime.Object, error) {
	if err := p.delete(namespace, name); err != nil {//删除资源
		return modified, nil, err
	}
	// TODO: use wait
	if err := wait.PollImmediate(1*time.Second, p.Timeout, func() (bool, error) {//等待资源删除完成
		if _, err := p.Helper.Get(namespace, name, false); !errors.IsNotFound(err) {
			return false, err
		}
		return true, nil
	}); err != nil {
		return modified, nil, err
	}
	versionedObject, _, err := unstructured.UnstructuredJSONScheme.Decode(modified, nil, nil)//把modified转成json
	if err != nil {
		return modified, nil, err
	}
	options := metav1.CreateOptions{}//构造创建选项
	if p.ServerDryRun {//设置干跑
		options.DryRun = []string{metav1.DryRunAll}
	}
	createdObject, err := p.Helper.Create(namespace, true, versionedObject, &options)//创建对象到服务端
	if err != nil {
		// restore the original object if we fail to create the new one
		// but still propagate and advertise error to user
		recreated, recreateErr := p.Helper.Create(namespace, true, original, &options)//有错误重新创建
		if recreateErr != nil {
			err = fmt.Errorf("An error occurred force-replacing the existing object with the newly provided one:\n\n%v.\n\nAdditionally, an error occurred attempting to restore the original object:\n\n%v", err, recreateErr)
		} else {
			createdObject = recreated
		}
	}
	return modified, createdObject, err//返回重建成功的对象
}
//删除对象
func (p *pruner) delete(namespace, name string, mapping *meta.RESTMapping) error {
	return runDelete(namespace, name, mapping, p.dynamicClient, p.cascade, p.gracePeriod, p.serverDryRun)
}

//删除对象
func runDelete(namespace, name string, mapping *meta.RESTMapping, c dynamic.Interface, cascade bool, gracePeriod int, serverDryRun bool) error {
	options := &metav1.DeleteOptions{}//创建删除选项
	if gracePeriod >= 0 {// 如果grace-period大于0,则重新创建删除选项
		options = metav1.NewDeleteOptions(int64(gracePeriod))
	}
	if serverDryRun {//设置干跑
		options.DryRun = []string{metav1.DryRunAll}
	}
	policy := metav1.DeletePropagationForeground
	if !cascade {//设置级联策略
		policy = metav1.DeletePropagationOrphan
	}
	options.PropagationPolicy = &policy
	return c.Resource(mapping.Resource).Namespace(namespace).Delete(name, options)//执行删除
}


//删除对象
func (p *Patcher) delete(namespace, name string) error {
	return runDelete(namespace, name, p.Mapping, p.DynamicClient, p.Cascade, p.GracePeriod, p.ServerDryRun)
}
type pruner struct {//修剪结构体
	mapper        meta.RESTMapper
	dynamicClient dynamic.Interface

	visitedUids   sets.String
	labelSelector string
	fieldSelector string

	cascade      bool
	serverDryRun bool
	dryRun       bool
	gracePeriod  int

	toPrinter func(string) (printers.ResourcePrinter, error)

	out io.Writer
}

//执行修剪
func (p *pruner) prune(namespace string, mapping *meta.RESTMapping) error {
	objList, err := p.dynamicClient.Resource(mapping.Resource).
		Namespace(namespace).
		List(metav1.ListOptions{
			LabelSelector: p.labelSelector,
			FieldSelector: p.fieldSelector,
		})//获取资源列表
	if err != nil {
		return err
	}

	objs, err := meta.ExtractList(objList)//抽取对象列表
	if err != nil {
		return err
	}

	for _, obj := range objs {//遍历对象
		metadata, err := meta.Accessor(obj)//访问对象元数据
		if err != nil {
			return err
		}
		annots := metadata.GetAnnotations()//获取注解
		if _, ok := annots[corev1.LastAppliedConfigAnnotation]; !ok {//如果没有last-applied-configuation注解则跳过
			// don't prune resources not created with apply
			continue
		}
		uid := metadata.GetUID()//获取uid
		if p.visitedUids.Has(string(uid)) {//如果访问过的uid包含这个uid则跳过
			continue
		}
		name := metadata.GetName()//获取名称
		if !p.dryRun {
			if err := p.delete(namespace, name, mapping); err != nil {//删除资源
				return err
			}
		}

		printer, err := p.toPrinter("pruned")// printflag转printer
		if err != nil {
			return err
		}
		printer.PrintObj(obj, p.out)//打印结果
	}
	return nil
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

hxpjava1

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

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

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

打赏作者

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

抵扣说明:

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

余额充值