0%

kubernetes源码阅读1:kubectl与cobra

kubectl的代码在源码包的cmd包下的kubectl文件夹,只有一个kubectl.go

kubectl的入口

kubectl的入口代码非常简单,只有一个kubectl.go(在源码的cmd下的kubectl文件夹)

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)
}
}

当调用cmd.NewDefaultKubectlCommand()生成command对象时,会调用k8s.io/kubectl/cmd.go,可以发现在这个cmd.go的同级别下有好多kubectl子命令的文件夹,其实这些很多都是cobra生成的代码,cmd.go当然也是cobra自动生成的文件

cobra

简介

cobra使用的就是子命令模式,然后附带一个二进制的cobra命令帮助生成框架,只需要开发者填入具体功能就行

概念

  • Command代表执行动作,比如git clone的clone动作
  • Args就是执行参数,比如git clone后面跟着的仓库地址就是参数,参数是属于子命令的
  • Flags是动作的标识符,通常-f啊或者--name这种都是标识符

安装

用下面这条命令安装cobra依赖库

1
go get -u "github.com/spf13/cobra/cobra"

但是不会有二进制的cobra命令,查看github发现二进制的cobra命令已经搬到cobra-cli去了

所以再安装下cobra-cli

1
go get -u "github.com/spf13/cobra-cli"

在gopath下的bin中就能找到cobra-cli这个二进制命令了,但是为了要能随时随地使用这个命令,还需要将它放在/usr/local/bin下(我是macOS系统),在gopath执行下面的命令

1
sudo cp cobra-cli /usr/local/bin/cobra

我将其改名成cobra了,所以在终端直接用cobra就可以了

初始化

新版的cobra初始化有了一些变化,首先我创建一个mycobra文件夹,在文件夹下

1
go mod init mycobra

然后用cobra

1
cobra init .

看到文件夹下会有这些文件

1
LICENSE cmd go.mod go.sum main.go

这样cobra的整个框架就搭好了,cmd文件夹就是我们存放子命令的实现的地方

main.go里非常简单

1
2
3
4
5
6
7
8
9
10
11
/*
Copyright © 2022 NAME HERE <EMAIL ADDRESS>

*/
package main

import "mycobra/cmd"

func main() {
cmd.Execute()
}

只是调用了cmd包下的Execute方法,打开cmd包发现只有一个root.go,毫无疑问这就是根命令了(因为我们还没有添加过任何子命令),等先说完添加子命令后,将会分析cmd包下的逻辑

添加子命令

只需要执行一下cobra-cli的add命令,这里假设我增加一个get子命令

1
cobra add get

就可以发现cmd底下多了一个get.go

如何编写代码

发现其实每个子命令都对应一个cobra.Command结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// getCmd represents the get command
var getCmd = &cobra.Command{
Use: "get",
Short: "A brief description of your command",
Long: `A longer description that spans multiple lines and likely contains examples
and usage of using your command. For example:

Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("get called")
},
}

Use字段代表子命令名称,Short是短简介,Long是长简介,Run是一个函数对象,当然就对应着这个子命令执行的具体逻辑。整个结构体的运行阶段还不止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
// The *Run functions are executed in the following order:
// * PersistentPreRun()
// * PreRun()
// * Run()
// * PostRun()
// * PersistentPostRun()
// All functions get the same args, the arguments after the command name.
//
// PersistentPreRun: children of this command will inherit and execute.
PersistentPreRun func(cmd *Command, args []string)
// PersistentPreRunE: PersistentPreRun but returns an error.
PersistentPreRunE func(cmd *Command, args []string) error
// PreRun: children of this command will not inherit.
PreRun func(cmd *Command, args []string)
// PreRunE: PreRun but returns an error.
PreRunE func(cmd *Command, args []string) error
// Run: Typically the actual work function. Most commands will only implement this.
Run func(cmd *Command, args []string)
// RunE: Run but returns an error.
RunE func(cmd *Command, args []string) error
// PostRun: run after the Run command.
PostRun func(cmd *Command, args []string)
// PostRunE: PostRun but returns an error.
PostRunE func(cmd *Command, args []string) error
// PersistentPostRun: children of this command will inherit and execute after PostRun.
PersistentPostRun func(cmd *Command, args []string)
// PersistentPostRunE: PersistentPostRun but returns an error.
PersistentPostRunE func(cmd *Command, args []string) error

其中可供调用的就是PersistentPreRun、PreRun、Run、PostRun、PersistentPostRun这五个阶段

还可以使用 Command结构体 的 Args 字段指定位置参数的验证

下面的验证符是内置的:

  • NoArgs - 如果有任何位置参数,该命令将报告错误。
  • ArbitraryArgs - 命令将接受任意参数
  • OnlyValidArgs - 如果 Command 的 ValidArgs 字段中不存在该位置参数,则该命令将报告错误。
  • MinimumNArgs(int) - 如果不存在至少 N 个位置参数,则该命令将报告错误。
  • MaximumNArgs(int) - 如果存在超过 N 个位置参数,则该命令将报告错误。
  • ExactArgs(int) - 如果不存在 N 个位置参数,则该命令将报告错误。
  • ExactValidArgs(int) - 如果没有确切的 N 个位置参数,或者如果 Command 的 ValidArgs 字段中不存在该位置参数,则该命令将报告并出错。
  • RangeArgs(min, max) - 如果 args 的数目不在期望的 args 的最小和最大数目之间,则该命令将报告并出错。

在Command结构体填入Args: cobra.MinimumNArgs(1)就可以指定子命令必须接受一个及一个以上的参数