目录

kubectl源码解析

kubectl源码阅读

版本 v1.16.6

kubectl命令的创建

kubernetes/cmd/kubectl/kubectl.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
func main() {
	rand.Seed(time.Now().UnixNano())

	command := cmd.NewDefaultKubectlCommand()

	// TODO: once we switch everything over to Cobra commands, we can go back to calling
	// cliflag.InitFlags() (by removing its pflag.Parse() call). For now, we have to set the
	// normalize func and add the go flag set by hand.
	pflag.CommandLine.SetNormalizeFunc(cliflag.WordSepNormalizeFunc)
	pflag.CommandLine.AddGoFlagSet(goflag.CommandLine)
	// cliflag.InitFlags()
	logs.InitLogs()
	defer logs.FlushLogs()

	if err := command.Execute(); err != nil {
		os.Exit(1)
	}
}

重点 : command := cmd.NewDefaultKubectlCommand()

kubernetes/pkg/kubectl/cmd/cmd.go

1
2
3
4
// NewDefaultKubectlCommand创建带有默认参数的' kubectl '命令
func NewDefaultKubectlCommand() *cobra.Command {
	return NewDefaultKubectlCommandWithArgs(NewDefaultPluginHandler(plugin.ValidPluginFilenamePrefixes), os.Args, os.Stdin, os.Stdout, os.Stderr)
}

return调用NewDefaultKubectlCommandWithArgs方法传递参数讲解

  • NewDefaultPluginHandler(plugin.ValidPluginFilenamePrefixes)

    • plugin.ValidPluginFilenamePrefixes 为定义的变量
    1
    2
    
    位于Kubernetes\vendor\k8s.io\kubectl\pkg\cmd\plugin\plugin.go文本中定义
    ValidPluginFilenamePrefixes = []string{"kubectl"}
    
    • 函数
    1
    2
    3
    4
    5
    6
    
    // NewDefaultPluginHandler实例化DefaultPluginHandler,使用给定的文件名前缀列表来标识有效的插件文件名。
    func NewDefaultPluginHandler(validPrefixes []string) *DefaultPluginHandler {
    	return &DefaultPluginHandler{
          ValidPrefixes: validPrefixes,
    	}
    }
    
    • 返回值 为DefaultPluginHandler结构体指针
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    // DefaultPluginHandler implements PluginHandler
    type DefaultPluginHandler struct {
    	ValidPrefixes []string
    }
    此结构体实现了pluginHandler接口中定义的方法
    // 存在于给定文件名,或布尔值为false。查找将遍历给定前缀列表,以识别有效的插件文件名。返回与前缀匹配的第一个文件路径。
    Lookup(filename string) (string, bool)
    // ecute接收可执行文件的文件路径、参数片和环境变量片,以传递给可执行文件。
    Execute(executablePath string, cmdArgs, environment []string) error
      
    

实际调用NewDefaultKubectlCommandWithArgs函数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//NewDefaultKubectlCommandWithArgs创建带参数的' kubectl '命令
func NewDefaultKubectlCommandWithArgs(pluginHandler PluginHandler, args []string, in io.Reader, out, errout io.Writer) *cobra.Command {
	cmd := NewKubectlCommand(in, out, errout)//in、out、errout由上层函数传入,为系统标准输入os.stdin、标准输出os.stdout、标准错误os.stderr

	if pluginHandler == nil {
		return cmd
	}

	if len(args) > 1 {
		cmdPathPieces := args[1:]

		// only look for suitable extension executables if
		// the specified command does not already exist
		if _, _, err := cmd.Find(cmdPathPieces); err != nil {
			if err := HandlePluginCommand(pluginHandler, cmdPathPieces); err != nil {
				fmt.Fprintf(errout, "%v\n", err)
				os.Exit(1)
			}
		}
	}

	return cmd
}

其中参数pluginHandler为接口类型,定义如下:

1
2
3
4
5
6
7
PluginHandler能够解析命令行参数并执行可执行的文件名查找来搜索有效的插件文件,并执行找到的插件。
type PluginHandler interface {
	// 存在于给定文件名,或布尔值为false。查找将遍历给定前缀列表,以识别有效的插件文件名。返回与前缀匹配的第一个文件路径。
	Lookup(filename string) (string, bool)
	// ecute接收可执行文件的文件路径、参数片和环境变量片,以传递给可执行文件。
	Execute(executablePath string, cmdArgs, environment []string) error
}

重点:

cmd := NewKubectlCommand(in, out, errout)

  • 函数

      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    
    //newkubectl命令创建“kubectl”命令及其嵌套子命令
    func NewKubectlCommand(in io.Reader, out, err io.Writer) *cobra.Command {
    	//作用创建kubectl命令
    	...
    	// 添加标志位,如--kubeconfig、--cache-dir、--client-certificate、--client-key、--token、--as、--as-group、--username、--password、--cluster、--user、--namespace、--context、--server、--insecure-skip-tls-verify、--certificate-authority、--request-timeout等标志位
    	kubeConfigFlags := genericclioptions.NewConfigFlags(true).WithDeprecatedPasswordFlag()
    	kubeConfigFlags.AddFlags(flags)
      	
    	//添加标志位--match-server-version 要求服务器版本必须与客户端版本一致
    	matchVersionKubeConfigFlags := cmdutil.NewMatchVersionFlags(kubeConfigFlags)
    	matchVersionKubeConfigFlags.AddFlags(cmds.PersistentFlags())
      
    	//将标志位添加到全局标志位
    	cmds.PersistentFlags().AddGoFlagSet(flag.CommandLine)
      
    	//使用工厂函数创建factory的接口,由结构体factoryImpl实现此接口的所有方法,如:DynamicClient、KubernetesClientSet、RESTClient、NewBuilder、ClientForMapping、UnstructuredClientForMapping、Validator、OpenAPISchema
    	f := cmdutil.NewFactory(matchVersionKubeConfigFlags)
      
    	...
      	
    	//将系统的标准输入、标准输出、标准错误封装成一个结构体IOStreams
    	ioStreams := genericclioptions.IOStreams{In: in, Out: out, ErrOut: err}
      
    	// 所有子命令都要加入的父命令
    	groups := templates.CommandGroups{
          {
          //入门级命令有 create、expose、run、set
              Message: "Basic Commands (Beginner):",
              Commands: []*cobra.Command{
                  create.NewCmdCreate(f, ioStreams),
                  expose.NewCmdExposeService(f, ioStreams),
                  run.NewCmdRun(f, ioStreams),
                  set.NewCmdSet(f, ioStreams),
              },
          },
          {
              //初中级命令 explain、get、edit、delete
              Message: "Basic Commands (Intermediate):",
              Commands: []*cobra.Command{
                  explain.NewCmdExplain("kubectl", f, ioStreams),
                  get.NewCmdGet("kubectl", f, ioStreams),
                  edit.NewCmdEdit(f, ioStreams),
                  delete.NewCmdDelete(f, ioStreams),
              },
          },
          {
              //部署相关命令 rollout rollingupdate(已被官方移除)、scale、autoscale 
              Message: "Deploy Commands:",
              Commands: []*cobra.Command{
                  rollout.NewCmdRollout(f, ioStreams),
                  rollingupdate.NewCmdRollingUpdate(f, ioStreams),
                  scale.NewCmdScale(f, ioStreams),
                  autoscale.NewCmdAutoscale(f, ioStreams),
              },
          },
          {
              //集群管理命令 certificates、clusterinfo、top、cordon、uncordon、drain、taint
              Message: "Cluster Management Commands:",
              Commands: []*cobra.Command{
                  certificates.NewCmdCertificate(f, ioStreams),
                  clusterinfo.NewCmdClusterInfo(f, ioStreams),
                  top.NewCmdTop(f, ioStreams),
                  drain.NewCmdCordon(f, ioStreams),
                  drain.NewCmdUncordon(f, ioStreams),
                  drain.NewCmdDrain(f, ioStreams),
                  taint.NewCmdTaint(f, ioStreams),
              },
          },
          {
              //故障处理和Debugging命令 describe、logs、attach、exec、port-forward、proxy、cp、auth
              Message: "Troubleshooting and Debugging Commands:",
              Commands: []*cobra.Command{
                  describe.NewCmdDescribe("kubectl", f, ioStreams),
                  logs.NewCmdLogs(f, ioStreams),
                  attach.NewCmdAttach(f, ioStreams),
                  cmdexec.NewCmdExec(f, ioStreams),
                  portforward.NewCmdPortForward(f, ioStreams),
                  proxy.NewCmdProxy(f, ioStreams),
                  cp.NewCmdCp(f, ioStreams),
                  auth.NewCmdAuth(f, ioStreams),
              },
          },
          {
              //高级命令 diff、apply、patch、replace、wait、convert、kustomize
              Message: "Advanced Commands:",
              Commands: []*cobra.Command{
                  diff.NewCmdDiff(f, ioStreams),
                  apply.NewCmdApply("kubectl", f, ioStreams),
                  patch.NewCmdPatch(f, ioStreams),
                  replace.NewCmdReplace(f, ioStreams),
                  wait.NewCmdWait(f, ioStreams),
                  convert.NewCmdConvert(f, ioStreams),
                  kustomize.NewCmdKustomize(ioStreams),
              },
          },
          {
              //设置类命令  label、annotate、completion
              Message: "Settings Commands:",
              Commands: []*cobra.Command{
                  label.NewCmdLabel(f, ioStreams),
                  annotate.NewCmdAnnotate("kubectl", f, ioStreams),
                  completion.NewCmdCompletion(ioStreams.Out, ""),
              },
          },
    	}
      
    	...
    	//添加--kubeconfig 的falg参数
    	kubeConfigFlags := genericclioptions.NewConfigFlags(true).WithDeprecatedPasswordFlag()
    	kubeConfigFlags.AddFlags(flags)
    	matchVersionKubeConfigFlags := cmdutil.NewMatchVersionFlags(kubeConfigFlags)
    	matchVersionKubeConfigFlags.AddFlags(cmds.PersistentFlags())
    	...
      	
    	//其他类型命令 config、plugin、version、api-resources、api-versions
    	cmds.AddCommand(cmdconfig.NewCmdConfig(f, clientcmd.NewDefaultPathOptions(), ioStreams))
    	cmds.AddCommand(plugin.NewCmdPlugin(f, ioStreams))
    	cmds.AddCommand(version.NewCmdVersion(f, ioStreams))
    	cmds.AddCommand(apiresources.NewCmdAPIVersions(f, ioStreams))
    	cmds.AddCommand(apiresources.NewCmdAPIResources(f, ioStreams))
    	cmds.AddCommand(options.NewCmdOptions(ioStreams.Out))
    	return cmds
    }
    

kubectl create子命令详解

create 命令创建

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
// NewCmdCreate returns new initialized instance of create sub command
func NewCmdCreate(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
	//创建create命令的命令行选项
	o := NewCreateOptions(ioStreams)

	cmd := &cobra.Command{
		Use:                   "create -f FILENAME",
		DisableFlagsInUseLine: true,
		Short:                 i18n.T("Create a resource from a file or from stdin."),
		Long:                  createLong,
		Example:               createExample,
		Run: func(cmd *cobra.Command, args []string) {
			if cmdutil.IsFilenameSliceEmpty(o.FilenameOptions.Filenames, o.FilenameOptions.Kustomize) {
				ioStreams.ErrOut.Write([]byte("Error: must specify one of -f and -k\n\n"))
				defaultRunFunc := cmdutil.DefaultSubCommandRun(ioStreams.ErrOut)
				defaultRunFunc(cmd, args)
				return
			}
			cmdutil.CheckErr(o.Complete(f, cmd)) //运行创建命令前完成所有必要选项
			cmdutil.CheckErr(o.ValidateArgs(cmd, args)) //检查args参数,不会有多余的args参数
			cmdutil.CheckErr(o.RunCreate(f, cmd)) //主要运行逻辑
		},
	}

	// 添加--record选项
	o.RecordFlags.AddFlags(cmd)
	//添加选项-f --filename -k --kustimize选项 以及 -R --recursive (递归)
	usage := "to use to create the resource"
	cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, usage)
	//添加--validate选项
	cmdutil.AddValidateFlags(cmd)
	
	//添加--edit选项,创建前编辑,如 kubectl create -f chat_cloud_apis_chtweb.yml  --edit
	cmd.Flags().BoolVar(&o.EditBeforeCreate, "edit", o.EditBeforeCreate, "Edit the API resource before creating")
	cmd.Flags().Bool("windows-line-endings", runtime.GOOS == "windows",
		"Only relevant if --edit=true. Defaults to the line ending native to your platform.")
	//添加--save-config选项
	cmdutil.AddApplyAnnotationFlags(cmd)
	//添加--dry-run选项
	cmdutil.AddDryRunFlag(cmd)
	
	//添加选项--selector -l 
	cmd.Flags().StringVarP(&o.Selector, "selector", "l", o.Selector, "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)")
	//添加选项--raw
	cmd.Flags().StringVar(&o.Raw, "raw", o.Raw, "Raw URI to POST to the server.  Uses the transport specified by the kubeconfig file.")
	
	//添加--template --allow-missing-template-keys |  -o --output选项
	o.PrintFlags.AddFlags(cmd)

	// 添加子命令 namespace、quota、secret、configmap、serviceaccout、service、deployment、clusterrole、clusterrolebinding、role、rolebinding、poddisruptionbudget、priorityclass、job、cronjob,子命令的大致逻辑都差不多,下面会以namespace为例进行讲解
	cmd.AddCommand(NewCmdCreateNamespace(f, ioStreams))
	cmd.AddCommand(NewCmdCreateQuota(f, ioStreams))
	cmd.AddCommand(NewCmdCreateSecret(f, ioStreams))
	cmd.AddCommand(NewCmdCreateConfigMap(f, ioStreams))
	cmd.AddCommand(NewCmdCreateServiceAccount(f, ioStreams))
	cmd.AddCommand(NewCmdCreateService(f, ioStreams))
	cmd.AddCommand(NewCmdCreateDeployment(f, ioStreams))
	cmd.AddCommand(NewCmdCreateClusterRole(f, ioStreams))
	cmd.AddCommand(NewCmdCreateClusterRoleBinding(f, ioStreams))
	cmd.AddCommand(NewCmdCreateRole(f, ioStreams))
	cmd.AddCommand(NewCmdCreateRoleBinding(f, ioStreams))
	cmd.AddCommand(NewCmdCreatePodDisruptionBudget(f, ioStreams))
	cmd.AddCommand(NewCmdCreatePriorityClass(f, ioStreams))
	cmd.AddCommand(NewCmdCreateJob(f, ioStreams))
	cmd.AddCommand(NewCmdCreateCronJob(f, ioStreams))
	return cmd
}

o.RunCreate(f, cmd)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
func (o *CreateOptions) RunCreate(f cmdutil.Factory, cmd *cobra.Command) error {
  // raw选项只对当文件起作用,所以只在filenames中取第一个,然后使用restClient发起Post请求
  if len(o.Raw) > 0 {
      restClient, err := f.RESTClient()
      if err != nil {
          return err
      }
      return rawhttp.RawPost(restClient, o.IOStreams, o.Raw, o.FilenameOptions.Filenames[0])
  }
  
  //如果有--edit参数,那么再做完一系列检查后,然后使用restClient发起Patch请求
  if o.EditBeforeCreate {
      return RunEditOnCreate(f, o.PrintFlags, o.RecordFlags, o.IOStreams, cmd, &o.FilenameOptions)
  }
  
  //如果有--validate参数,就进行检查
  schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate"))
  if err != nil {
      return err
  }
  
  //通过clientConfig获取namespace
  cmdNamespace, enforceNamespace, err := f.ToRawKubeConfigLoader().Namespace()
  if err != nil {
      return err
  }

  //根据输入的参数生成Visitor,主要是根据path、label、resource、name 中的任意一个生成visitor,然后赋值给Result结构体的visitor属性,然后返回出来
  r := f.NewBuilder().
      Unstructured().
      Schema(schema).
      ContinueOnError().
      NamespaceParam(cmdNamespace).DefaultNamespace().
      FilenameParam(enforceNamespace, &o.FilenameOptions).
      LabelSelectorParam(o.Selector).
      Flatten().
      Do()
  err = r.Err()
  if err != nil {
      return err
  }

  count := 0
  //重要逻辑部分使用r.Visit去循环遍历所有的info
  err = r.Visit(func(info *resource.Info, err error) error {
      if err != nil {
          return err
      }
      //创建或更新的时候写入annotation
      if err := util.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info.Object, scheme.DefaultJSONEncoder()); err != nil {
          return cmdutil.AddSourceToErr("creating", info.Source, err)
      }

      //如果有--recode参数,那么就在annotation中添加键值对 kubernetes.io/change-cause : 命令 
      if err := o.Recorder.Record(info.Object); err != nil {
          klog.V(4).Infof("error recording current command: %v", err)
      }
      //如果没有--dry-run参数,那么就执行创建操作
      if !o.DryRun {
          if err := createAndRefresh(info); err != nil {
              return cmdutil.AddSourceToErr("creating", info.Source, err)
          }
      }

      count++

      return o.PrintObj(info.Object)
  })
  if err != nil {
      return err
  }
  if count == 0 {
      return fmt.Errorf("no objects passed to create")
  }
  return nil
}

createAndRefresh(info) 主要创建逻辑

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// createAndRefresh creates an object from input info and refreshes info with that object
func createAndRefresh(info *resource.Info) error {
	obj, err := resource.NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, info.Object, nil)
	if err != nil {
		return err
	}
	info.Refresh(obj, true)
	return nil
}

//最终调用的还是RESTClient的POST方法,如下
func (m *Helper) Create(namespace string, modify bool, obj runtime.Object, options *metav1.CreateOptions) (runtime.Object, error) {
	if options == nil {
		options = &metav1.CreateOptions{}
	}
	if modify {
		// Attempt to version the object based on client logic.
		version, err := metadataAccessor.ResourceVersion(obj)
		if err != nil {
			// We don't know how to clear the version on this object, so send it to the server as is
			return m.createResource(m.RESTClient, m.Resource, namespace, obj, options)
		}
		if version != "" {
			if err := metadataAccessor.SetResourceVersion(obj, ""); err != nil {
				return nil, err
			}
		}
	}

	return m.createResource(m.RESTClient, m.Resource, namespace, obj, options)
}

func (m *Helper) createResource(c RESTClient, resource, namespace string, obj runtime.Object, options *metav1.CreateOptions) (runtime.Object, error) {
	return c.Post().
		NamespaceIfScoped(namespace, m.NamespaceScoped).
		Resource(resource).
		VersionedParams(options, metav1.ParameterCodec).
		Body(obj).
		Do().
		Get()
}
子命令 create namespace
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// NewCmdCreateNamespace is a macro command to create a new namespace
func NewCmdCreateNamespace(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
	//namespace子命令选项
	options := &NamespaceOpts{
		CreateSubcommandOptions: NewCreateSubcommandOptions(ioStreams), //返回一个支持创建子命令的结构体
	}
	
	cmd := &cobra.Command{
		Use:                   "namespace NAME [--dry-run]",
		DisableFlagsInUseLine: true,
		Aliases:               []string{"ns"},
		Short:                 i18n.T("Create a namespace with the specified name"),
		Long:                  namespaceLong,
		Example:               namespaceExample,
		Run: func(cmd *cobra.Command, args []string) {
			//主要运行逻辑
			cmdutil.CheckErr(options.Complete(f, cmd, args))
			cmdutil.CheckErr(options.Run())
		},
	}
	//添加标志 -o | --output
	options.CreateSubcommandOptions.PrintFlags.AddFlags(cmd)

	//添加标志 --save-config
	cmdutil.AddApplyAnnotationFlags(cmd)
	//添加标志 --validate
	cmdutil.AddValidateFlags(cmd)
	//添加标志 --generator
	cmdutil.AddGeneratorFlags(cmd, generateversioned.NamespaceV1GeneratorName)

	return cmd
}

options.Complete(f, cmd, args)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
func (o *NamespaceOpts) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
	name, err := NameFromCommandArgs(cmd, args)
	if err != nil {
		return err
	}

	//结构体生成器
	var generator generate.StructuredGenerator
	switch generatorName := cmdutil.GetFlagString(cmd, "generator"); generatorName {
	case generateversioned.NamespaceV1GeneratorName: //generatorName 为 namespace/v1时
		generator = &generateversioned.NamespaceGeneratorV1{Name: name}
	default:
		return errUnsupportedGenerator(cmd, generatorName)
	}

	return o.CreateSubcommandOptions.Complete(f, cmd, args, generator)
}


// Complete completes all the required options
func (o *CreateSubcommandOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string, generator generate.StructuredGenerator) error {
	name, err := NameFromCommandArgs(cmd, args)
	if err != nil {
		return err
	}
	//获取必要的参数,以及将生成器赋值给StructuredGenerator
	o.Name = name
	o.StructuredGenerator = generator
	o.DryRun = cmdutil.GetDryRunFlag(cmd)
	o.CreateAnnotation = cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag)

	//--dryrun  不会发起请求,只打印
	if o.DryRun {
		o.PrintFlags.Complete("%s (dry run)")
	}

	printer, err := o.PrintFlags.ToPrinter()
	if err != nil {
		return err
	}

	o.PrintObj = func(obj kruntime.Object, out io.Writer) error {
		return printer.PrintObj(obj, out)
	}

	//获取名称空间以及创建DynamicClient客户端
	o.Namespace, o.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace()
	if err != nil {
		return err
	}

	o.DynamicClient, err = f.DynamicClient()
	if err != nil {
		return err
	}
	//将f.ToRESTMapper()赋值给o.Mapper,这里的f是最外层主命令的f,通过查找可以发现是Factory中的子接口genericclioptions.RESTClientGetter中的方法,而在NewFactory方法的时候,是将结构体factoryImpl返回出去的,所以从factoryImpl结构体中找到了ToRESTMapper的方法,但是factoryImpl中的ToRESTConfig方法也是属于结构体中成员clientGetter genericclioptions.RESTClientGetter接口的方法,再进行往上查找是有NewMatchVersionFlags将kubeConfigFlags方法创建的结构体ConfigFlags作为参数传递给NewMatchVersionFlags的接口Delegate genericclioptions.RESTClientGetter,所以具体的ToRESTMapper实际上是使用了ConfigFlags结构体中的ToRESTMapper方法
	
	o.Mapper, err = f.ToRESTMapper()
	if err != nil {
		return err
	}

	return nil
}

options.Run()

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
func (o *NamespaceOpts) Run() error {
	return o.CreateSubcommandOptions.Run()
}

func (o *CreateSubcommandOptions) Run() error {
	//通过生成器接口,生成namespace 的obj对象,从Complete部分我们可以看到StructuredGenerator赋值的结构体为&generateversioned.NamespaceGeneratorV1{Name: name},所以可以从NamespaceGeneratorV1这个结构体中找寻StructuredGenerate的方法
	obj, err := o.StructuredGenerator.StructuredGenerate()
	if err != nil {
		return err
	}
	
	//如果有没有加--dryrun,那就执行创建操作
	if !o.DryRun {
		// 获取对应obj的gvk信息
		gvks, _, err := scheme.Scheme.ObjectKinds(obj)
		if err != nil {
			return err
		}
		gvk := gvks[0]
		mapping, err := o.Mapper.RESTMapping(schema.GroupKind{Group: gvk.Group, Kind: gvk.Kind}, gvk.Version)
		if err != nil {
			return err
		}
		//在obj对象中添加或更新annotation的last-applied-configuration字段
		if err := util.CreateOrUpdateAnnotation(o.CreateAnnotation, obj, scheme.DefaultJSONEncoder()); err != nil {
			return err
		}
		
		asUnstructured := &unstructured.Unstructured{}
		//将obj对象转换为非结构化数据
		if err := scheme.Scheme.Convert(obj, asUnstructured, nil); err != nil {
			return err
		}
		if mapping.Scope.Name() == meta.RESTScopeNameRoot {
			o.Namespace = ""
		}
		//使用client-go的dynamicClient创建资源
		actualObject, err := o.DynamicClient.Resource(mapping.Resource).Namespace(o.Namespace).Create(asUnstructured, metav1.CreateOptions{})
		if err != nil {
			return err
		}

		// ensure we pass a versioned object to the printer
		obj = actualObject
	} else {
		if meta, err := meta.Accessor(obj); err == nil && o.EnforceNamespace {
			meta.SetNamespace(o.Namespace)
		}
	}

	return o.PrintObj(obj, o.Out)
}

obj, err := o.StructuredGenerator.StructuredGenerate()

1
2
3
4
5
6
7
8
func (g *NamespaceGeneratorV1) StructuredGenerate() (runtime.Object, error) {
	if err := g.validate(); err != nil {
		return nil, err
	}
	namespace := &v1.Namespace{}
	namespace.Name = g.Name
	return namespace, nil
}

其他的子命令过程都类似,只有细微的差别