kubectl源码分析之apply
欢迎关注我的公众号:
目前刚开始写一个月,一共写了18篇原创文章,文章目录如下:
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
}
版权声明:本文为hxpjava1原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。