k8s源码分析2-命令行工具cobra的使用
本节重点介绍 :
- kubectl的职责和 kubectl 的代码原理
- cobra库的使用简介
kubectl的职责
- 主要的工作是处理用户提交的东西(包括,命令行参数,yaml文件等)
- 然后其会把用户提交的这些东西组织成一个数据结构体
- 然后把其发送给 API Server
kubectl 的代码原理
- 从命令行和yaml文件中获取信息
- 通过Builder模式并把其转成一系列的资源
- 最后用 Visitor 模式模式来迭代处理这些Reources
kubectl create命令 执行入口在 cmd目录下各个组件下面
- 代码位置 D:\go_path\src\github.com\kubernetes\kubernetes\cmd\kubectl\kubectl.go
package main
import (
goflag "flag"
"math/rand"
"os"
"time"
"github.com/spf13/pflag"
cliflag "k8s.io/component-base/cli/flag"
"k8s.io/kubectl/pkg/cmd"
"k8s.io/kubectl/pkg/util/logs"
// Import to initialize client auth plugins.
_ "k8s.io/client-go/plugin/pkg/client/auth"
)
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)
}
}
rand.Seed设置随机数
调用kubectl库调用cmd 创建command对象
command := cmd.NewDefaultKubectlCommand()
cobra的使用
cobra 的主要功能如下
- 简易的子命令行模式,如 app server, app fetch 等等
- 完全兼容 posix 命令行模式
- 嵌套子命令 subcommand
- 支持全局,局部,串联 flags
- 使用 cobra 很容易的生成应用程序和命令,使用 cobra create appname 和 cobra add cmdname
- 如果命令输入错误,将提供智能建议,如 app srver,将提示 srver 没有,是不是 app server
- 自动生成 commands 和 flags 的帮助信息
- 自动生成详细的 help 信息,如 app help
- 自动识别帮助 flag -h,--help
- 自动生成应用程序在 bash 下命令自动完成功能
- 自动生成应用程序的 man 手册
- 命令行别名
- 自定义 help 和 usage 信息
- 可选的与 viper apps 的紧密集成
cobra 中的主要概念
- Commands 表示执行动作
- Args 就是执行参数
- Flags 是这些动作的标识符
举例说明
- git clone 命令
git clone https://github.com/spf13/cobra.git --bare
- 其中
- git代表执行的二进制
- clone 代表执行的动作,可以理解为子命令
- https://github.com/spf13/cobra.git代表 参数
- --bare 代表标识符,意思是创建个裸库
创建 cobra 应用
- 在创建 cobra 应用前需要先安装 cobra 包:
go get -u github.com/spf13/cobra/cobra
- 然后就可以用 cobra 程序生成应用程序框架了:
cobra init my_cobra --pkg-name my_cobra
Your Cobra application is ready at
D:\go_path\src/my_cobra
- 除了init生成应用程序框架,还可以通过 cobra add 命令生成子命令的代码文件,比如下面的命令会添加两个子命令 image 和 container 相关的代码文件:
D:\go_path\src\my_cobra>cobra add image
image created at D:\go_path\src\my_cobra
D:\go_path\src\my_cobra>cobra add container
container created at D:\go_path\src\my_cobra
- 打开my_cobra项目。执行 go mod init ,后可以看到相关的文件
$ find
./cmd
./cmd/container.go
./cmd/image.go
./cmd/root.go
./go.mod
./go.sum
./LICENSE
./main.go
- 到目前为止,我们一共为 my_cobra 程序添加了三个 Command,分别是 rootCmd(cobra init 命令默认生成)、imageCmd 和 containerCmd。
为命令添加具体的功能
- 打开文件 root.go ,找到变量 rootCmd 的初始化过程并为之设置 Run 方法:
var rootCmd = &cobra.Command{
Use: "my_cobra",
Short: "A brief description of your application",
Long: `A longer description that spans multiple lines and likely contains
examples and usage of using your application. 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.`,
// Uncomment the following line if your bare application
// has an action associated with it:
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("my_cobra")
},
}
- 编译运行后打印
D:\go_path\src\my_cobra>go run main.go
my_cobra
- 加上 image 参数或者 container参数试试
D:\go_path\src\my_cobra>go run main.go container
container called
- 可以看出执行的是对应 xxxCmd 下的Run方法
var containerCmd = &cobra.Command{
Use: "container",
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("container called")
},
}
- 赋值cmd/container.go 为version.go添加version信息
package cmd
import (
"fmt"
"github.com/spf13/cobra"
)
// containerCmd represents the container command
var versionCmd = &cobra.Command{
Use: "version",
Short: "Print the version of your my_cobra",
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("my_cobra version is v1.0")
},
}
func init() {
rootCmd.AddCommand(versionCmd)
}
- 执行
D:\go_path\src\my_cobra>go run main.go version
my_cobra version is v1.0
为 Command 添加选项(flags)
- 选项(flags)用来控制 Command 的具体行为。根据选项的作用范围,可以把选项分为两类:
- persistent
- local
- 对于 persistent 类型的选项,既可以设置给该 Command,又可以设置给该 Command 的子 Command。对于一些全局性的选项,比较适合设置为 persistent 类型,比如控制输出的 verbose 选项:
var Verbose bool
rootCmd.PersistentFlags().BoolVarP(&Verbose, "verbose", "v", false, "verbose output")
- local 类型的选项只能设置给指定的 Command,比如下面定义的 source 选项:该选项不能指定给 rootCmd 之外的其它 Command。
var Source string
rootCmd.Flags().StringVarP(&Source, "source", "s", "", "Source directory to read from")
命令行参数(arguments)
- 以常见的 ls 命令来说,其命令行的格式为:
ls [OPTION]... [FILE]…
- 其中的 OPTION 对应本文中介绍的 flags,以 - 或 -- 开头
- 而 FILE 则被称为参数(arguments)或位置参数
- 一般的规则是参数在所有选项的后面,上面的 … 表示可以指定多个选项和多个参数。
验证方法
cobra 默认提供了一些验证方法:
- NoArgs - 如果存在任何位置参数,该命令将报错
- ArbitraryArgs - 该命令会接受任何位置参数
- OnlyValidArgs - 如果有任何位置参数不在命令的 ValidArgs 字段中,该命令将报错
- MinimumNArgs(int) - 至少要有 N 个位置参数,否则报错
- MaximumNArgs(int) - 如果位置参数超过 N 个将报错
- ExactArgs(int) - 必须有 N 个位置参数,否则报错
- ExactValidArgs(int) 必须有 N 个位置参数,且都在命令的 ValidArgs 字段中,否则报错
- RangeArgs(min, max) - 如果位置参数的个数不在区间 min 和 max 之中,报错
设置一个MinimumNArgs的验证
- 新增一个cmd/times.go
package cmd
import (
"fmt"
"strings"
"github.com/spf13/cobra"
)
// containerCmd represents the container command
var echoTimes int
var timesCmd = &cobra.Command{
Use: "times [string to echo]",
Short: "Echo anything to the screen more times",
Long: `echo things multiple times back to the user by providing
a count and a string.`,
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
for i := 0; i < echoTimes; i++ {
fmt.Println("Echo: " + strings.Join(args, " "))
}
},
}
func init() {
rootCmd.AddCommand(timesCmd)
timesCmd.Flags().IntVarP(&echoTimes, "times", "t", 1, "times to echo the input")
}
- 因为我们为 timesCmd 命令设置了 Args: cobra.MinimumNArgs(1),所以必须为 times 子命令传入一个参数,不然 times 子命令会报错:
D:\go_path\src\my_cobra>go run main.go times
Error: requires at least 1 arg(s), only received 0
Usage:
my_cobra times [string to echo] [flags]
Flags:
-h, --help help for times
-t, --times int times to echo the input (default 1)
Global Flags:
--config string config file (default is $HOME/.my_cobra.yaml)
Error: requires at least 1 arg(s), only received 0
exit status 1
- 加上 -t
D:\go_path\src\my_cobra>go run main.go times -t=4 k8s
Echo: k8s
Echo: k8s
Echo: k8s
Echo: k8s
帮助信息(help command)
- cobra 会自动添加 --help(-h)选项,所以我们可以不必添加该选项而直接使用:
D:\go_path\src\my_cobra>go run main.go -h
A longer description that spans multiple lines and likely contains
examples and usage of using your application. 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.
Usage:
my_cobra [flags]
my_cobra [command]
Available Commands:
completion generate the autocompletion script for the specified shell
container A brief description of your command
help Help about any command
image A brief description of your command
times Echo anything to the screen more times
version Print the version of your my_cobra
Flags:
--config string config file (default is $HOME/.my_cobra.yaml)
-h, --help help for my_cobra
-t, --toggle Help message for toggle
Use "my_cobra [command] --help" for more information about a command.
子命令help
- cobra 同时还自动添加了 help 子命,默认效果和使用 --help 选项相同
- 如果为 help 命令传递其它命令作为参数,则会显示对应命令的帮助信息,下面的命令输出 image 子命令的帮助信息:
D:\go_path\src\my_cobra>go run main.go help times
echo things multiple times back to the user by providing
a count and a string.
Usage:
my_cobra times [string to echo] [flags]
Flags:
-h, --help help for times
-t, --times int times to echo the input (default 1)
Global Flags:
--config string config file (default is $HOME/.my_cobra.yaml)
在 Commnad 执行前后执行额外的操作
- Command 执行的操作是通过 Command.Run 方法实现的
- 为了支持我们在 Run 方法执行的前后执行一些其它的操作,Command 还提供了额外的几个方法,它们的执行顺序如下:
- PersistentPreRun
- PreRun
- Run
- PostRun
- PersistentPostRun
修改rootCmd
var rootCmd = &cobra.Command{
Use: "my_cobra",
Short: "A brief description of your application",
Long: `A longer description that spans multiple lines and likely contains
examples and usage of using your application. 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.`,
// Uncomment the following line if your bare application
// has an action associated with it:
PersistentPreRun: func(cmd *cobra.Command, args []string) {
fmt.Printf("[step_1]PersistentPreRun with args: %v\n", args)
},
PreRun: func(cmd *cobra.Command, args []string) {
fmt.Printf("[step_2]PreRun with args: %v\n", args)
},
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("[step_3]my_cobra version is v1.0: %v\n", args)
},
PostRun: func(cmd *cobra.Command, args []string) {
fmt.Printf("[step_4]PostRun with args: %v\n", args)
},
PersistentPostRun: func(cmd *cobra.Command, args []string) {
fmt.Printf("[step_5]PersistentPostRun with args: %v\n", args)
},
}
- 执行结果,从结果来看几个run的执行顺序
D:\go_path\src\my_cobra>go run main.go
[step_1]PersistentPreRun with args: []
[step_2]PreRun with args: []
[step_3]my_cobra version is v1.0: []
[step_4]PostRun with args: []
[step_5]PersistentPostRun with args: []
本节重点总结 :
- kubectl create命令执行入口
- cobra库的使用简介
如对您有帮助,支持下呗!
微信
支付宝