什么是cobra

Go语言中,我们可以使用cobra很方便的进行命令行工具的开发,Cobra提供了自己的程序,该程序将创建您的应用程序并添加所需的任何命令。cobra项目地址:https://github.com/spf13/cobra

Cobra广泛应用在CLI(命令行界面)

CLI根据软件工程的基本原理进行工作,接受输入,对其进行处理并给出输出。目前很多云原生工具都使用cobra,生成好用的二进制包,例如kuberneteus,etcd,istio,helm,kubeadm 等等, 目前使用cobra的项目:https://github.com/spf13/cobra/blob/v1.1.3/projects_using_cobra.md

Cobra命令、参数和标识符

Commands 表示执行动作
Args 就是执行参数
Flags 是这些动作的标识符

例如:kubeadm --help 可以看到可以执行的Commands 和Flags

Usage:
  kubeadm [command]

Available Commands:
  alpha       Kubeadm experimental sub-commands
  completion  Output shell completion code for the specified shell (bash or zsh).
  config      Manage configuration for a kubeadm cluster persisted in a ConfigMap in the cluster.
  help        Help about any command
  init        Run this command in order to set up the Kubernetes control plane.
  join        Run this on any machine you wish to join an existing cluster
  reset       Run this to revert any changes made to this host by 'kubeadm init' or 'kubeadm join'.
  token       Manage bootstrap tokens.
  upgrade     Upgrade your cluster smoothly to a newer version with this command.
  version     Print the version of kubeadm

Flags:
  -h, --help              help for kubeadm
      --log-file string   If non-empty, use this log file
      --rootfs string     [EXPERIMENTAL] The path to the 'real' host root filesystem.
      --skip-headers      If true, avoid header prefixes in the log messages
  -v, --v Level           number for the log level verbosity

Use "kubeadm [command] --help" for more information about a command.

如果Commands是token,执行:kubeadm token --help,我们看到了kubeadm token增加了执行参数,以及执行的动作,还有动作的flags

Usage:
  kubeadm token [flags]
  kubeadm token [command]

Available Commands:
  create      Create bootstrap tokens on the server.
  delete      Delete bootstrap tokens on the server.
  generate    Generate and print a bootstrap token, but do not create it on the server.
  list        List bootstrap tokens on the server.

Flags:
      --dry-run             Whether to enable dry-run mode or not
  -h, --help                help for token
      --kubeconfig string   The kubeconfig file to use when talking to the cluster. If the flag is not set, a set of standard locations can be searched for an existing kubeconfig file. (default "/etc/kubernetes/admin.conf")

Global Flags:
      --log-file string   If non-empty, use this log file
      --rootfs string     [EXPERIMENTAL] The path to the 'real' host root filesystem.
      --skip-headers      If true, avoid header prefixes in the log messages
  -v, --v Level           number for the log level verbosity

Use "kubeadm token [command] --help" for more information about a command.

安装cobra

本机启动docker后,运行容器并进入,设置代理,下载cobra包,现在默认最高版本是1.3.0

docker run -itd --rm --name golang golang:1.16.3-alpine3.13
docker exec -it golang sh
export GOPROXY=https://goproxy.cn
go get -u -v github.com/spf13/cobra/cobra

go get会调用两个命令,第一个是下载git clone源码包,第二步是使用go install来编译,并放到gopath的/pkg目录下,我们来看看有没有这个二进制文件?

/go # go env | grep -i gopath
GOPATH="/go"
/go # ls -l /go/bin/cobra
-rwxr-xr-x    1 root     root       8472493 Jan 10 13:33 /go/bin/cobra
/go # date
Mon Jan 10 13:38:35 UTC 2022
/go # which cobra
/go/bin/cobra
/go # find / -name cobra
/go/bin/cobra
/go/pkg/mod/cache/download/github.com/spf13/cobra
/go/pkg/mod/github.com/spf13/cobra@v1.3.0/cobra
/go # cobra --help
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:
  cobra [command]

Available Commands:
  add         Add a command to a Cobra Application
  completion  Generate the autocompletion script for the specified shell
  help        Help about any command
  init        Initialize a Cobra Application

Flags:
  -a, --author string    author name for copyright attribution (default "YOUR NAME")
      --config string    config file (default is $HOME/.cobra.yaml)
  -h, --help             help for cobra
  -l, --license string   name of license for the project
      --viper            use Viper for configuration

Use "cobra [command] --help" for more information about a command.

cobra安装成功后,先使用使用cobra init 命令来初始化 CLI 应用的脚手架,注意:老版本的cobra库需要--pkg-name参数来进行初始化模块名称,例如:cobra init --pkg-name Testapp

/go # mkdir Cobra && cd Cobra
/go/Cobra # go mod init Testapp
go: creating new go.mod: module Testapp
/go/Cobra # cobra init
Your Cobra application is ready at
/go/Cobra
/go/Cobra # ls -l
total 88
-rw-r--r--    1 root     root             0 Jan 10 13:44 LICENSE
drwxr-x--x    2 root     root          4096 Jan 10 13:44 cmd
-rw-r--r--    1 root     root            75 Jan 10 13:44 go.mod
-rw-r--r--    1 root     root         75705 Jan 10 13:44 go.sum
-rw-r--r--    1 root     root           118 Jan 10 13:44 main.go

查看cobra代码和执行流程

main.go是CLI应用的入口,在main.go里调用了cmd/root.go下面的Execute函数:

/go/Cobra # cat main.go
/*
Copyright © 2022 NAME HERE <EMAIL ADDRESS>
*/
package main
import "Testapp/cmd"
import "fmt"
func main() {
	fmt.Println("exec in main") //这行是我单独增加的,看执行顺序
	cmd.Execute()
}

然后进入cmd/root.go依次使用的函数执行顺序是:

1. init最先执行,init 函数是 Golang 中初始化包的时候第一个调用的函数

2. 然后执行main函数

3. 最后执行rootCmd.Execute()函数,root(根)命令是CLI工具的最基本的命令,比如go get XXX,其中go就是跟命令,而get就是go这个根命令的子命令,而在root.go中就直接使用了cobra命令来初始化rootCmd结构,CLI中的其他所有命令都将是Testapp这个根命令的子命令了

/go/Cobra # cat cmd/root.go
/*
Copyright © 2022 NAME HERE <EMAIL ADDRESS>
*/
package cmd
import (
	"os"
	"fmt"
	"github.com/spf13/cobra"
)
// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
	Use:   "Testapp",
	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("exec Execute") },  //这行是我单独增加的,看执行顺序
}
// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
	err := rootCmd.Execute()
	if err != nil {
		os.Exit(1)
	}
}
func init() {
        fmt.Println("exec init")  //这行是我单独增加的,看执行顺序
	// Here you will define your flags and configuration settings.
	// Cobra supports persistent flags, which, if defined here,
	// will be global for your application.
	// rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.Testapp.yaml)")
	// Cobra also supports local flags, which will only run
	// when this action is called directly.
	rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}

/go/Cobra # go run main.go
exec init
exec in main
exec Execute