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 

创建 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 还提供了额外的几个方法,它们的执行顺序如下:
  1. PersistentPreRun
  2. PreRun
  3. Run
  4. PostRun
  5. 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库的使用简介
posted @ 2022-11-18 16:11  西门运维  阅读(190)  评论(0编辑  收藏  举报