A Preliminary Study of Cobra in Golang Development

A Preliminary Study of Cobra in Golang Development

"This article has been summoned to participate in activities of a good text, click to see: the back-end, front-end dual-track large contributors, 20,000 yuan prize pool waiting for you to challenge! "

A background

At the moment when cloud native is in full swing, I believe that many people have heard about Kubernetes/etcd, etc. When we look at its source code or perform secondary development on it, we can find that they all use a command line library Cobra, which is An artifact used to write command lines, providing a scaffolding to quickly generate Cobra-based application frameworks.

The author is a very famous spf13, I believe everyone knows about vim, you can go to use vim's ultimate terminal spf13-vim, which can be configured with one click, which is very convenient, and its work viper is a complete configuration solution. It supports configuration files such as JSON/YAML/TOML/HCL/envFile. It can also be hot-loaded and saved. Hugo is also its work.

We can use Cobra to quickly develop the command line tools we want, which is very convenient and fast.

Two functional characteristics

  • Simple sub-command line mode, such as app server, app fetch, etc.
  • Fully compatible with posix command line mode
  • Nested subcommand
  • Support global, local, series flags
  • Use Cobra to easily generate applications and commands, use cobra create appname and cobra add cmdname
  • If the command is entered incorrectly, smart suggestions will be provided, such as app srver, and it will prompt whether there is no srver and whether it is an app server
  • Automatically generate help information for commands and flags
  • Automatically generate detailed help information, such as app help
  • Automatically recognize -h, --help help flag
  • Auto-generated application command auto-completion function under bash
  • Automatically generate man pages for applications
  • Command line alias
  • Custom help and usage information
  • Optional tightly integrated viper apps

3.use Cobra

3.1 Installation

Cobra installation is very simple, you can use go get to get it. After the installation is complete, open the GOPATH directory. There should be a compiled cobra in the bin directory. Of course, you can also use source code to compile and install.

Before using cobra, you need to understand three concepts, which are also the three parts of the command line, command, flag and args

  • Some basic information of the command itself, expressed by command, the specific object is cobra.Command
  • Some Peugeot or options of the command are represented by flag, and the specific object is flag.FlagSet
  • The last parameter, represented by args, usually []string

Corresponding to the following example:

go get -u test.com/a/b copy the code

Here

get
Is commond (it's special here),
-u
Is the flag,
test.com/a/b
Is args

3.2 Generate application

$ /Users/xuel/workspace/goworkspace/bin/cobra init --pkg-name smartant-cli Your Cobra application is ready at /Users/xuel/workspace/goworkspace/src/github.com/kaliarch/smartant-cli $ ls LICENSE cmd go.mod go.sum main.go $ tree . LICENSE cmd root.go go.mod go.sum main.go 1 directory, 5 files Copy code

3.3 Design cls program

Create an imp directory under the smartant-cli directory, rewrite the utils.go file, and insert the following

package utils import "fmt" func Show (name string , age int ) { fmt.Printf( "name is %s, age is %d" , name, age) } Copy code
  • main.go
package main import "github.com/kaliarch/smartant-cli/cmd" func main () { cmd.Execute() } Copy code

It can be seen that the main function executes the cmd package, so we only need to call the utils package in the cmd package to achieve the requirements of the smartant-cli program. Then open the root.go file to view:

  • root.go
/* Copyright 2021 NAME HERE <EMAIL ADDRESS> Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package cmd import ( "fmt" "github.com/spf13/cobra" "os" homedir "github.com/mitchellh/go-homedir" "github.com/spf13/viper" ) var cfgFile string //rootCmd represents the base command when called without any subcommands var rootCmd = &cobra.Command{ Use: "smartant-cli", Short: "SmartAnt linux agent cli", Long: ` smartant-cli is a CLI for SmartAnt applications. This application is a tool to migrations linux system.`, //Uncomment the following line if your bare application //has an action associated with it: //Run: func(cmd *cobra.Command, args []string) {}, } //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() { if err := rootCmd.Execute(); err != nil { fmt.Println(err) os.Exit(1) } } func init() { cobra.OnInitialize(initConfig) //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/.smartant-cli.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") } //initConfig reads in config file and ENV variables if set. func initConfig() { if cfgFile != "" { //Use config file from the flag. viper.SetConfigFile(cfgFile) } else { //Find home directory. home, err := homedir.Dir() if err != nil { fmt.Println(err) os.Exit(1) } //Search config in home directory with name ".smartant-cli" (without extension). viper.AddConfigPath(home) viper.SetConfigName(".smartant-cli") } viper.AutomaticEnv()//read in environment variables that match //If a config file is found, read it in. if err := viper.ReadInConfig(); err == nil { fmt.Println("Using config file:", viper.ConfigFileUsed()) } } Copy code

From the source code, the cmd package performs some initialization operations and provides an Execute interface. It's very simple. Viper is a library that reads the configuration file integrated by cobra. You don't need to use it here. We can comment it out (the application that may be generated without comment is about 10M, and it is better to comment out if it is not used here). All commands of cobra are implemented through the structure of cobra.Command. In order to realize the smartant-cli function, obviously we need to modify RootCmd. The modified code is as follows:

/* Copyright 2021 NAME HERE <EMAIL ADDRESS> Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package cmd import ( "fmt" "github.com/spf13/cobra" //"github.com/spf13/viper" "github.com/kaliarch/cobra-demo/utils" "os" ) var cfgFile string //var name string //var age int var command string //rootCmd represents the base command when called without any subcommands var rootCmd = &cobra.Command{ Use: "cobra-demo", 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) {}, Run: func(cmd *cobra.Command, args []string) { //if len(name) == 0 { //cmd.Help() //return //} //imp.Show(name, age) if len(command) == 0 { cmd.Help() return } utils.Cmd(command) }, } //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() { if err := rootCmd.Execute(); err != nil { fmt.Println(err) os.Exit(-1) } } func init() { //cobra.OnInitialize(initConfig) //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/.smartant-agent.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") //rootCmd.PersistentFlags().StringVarP(&name, "name", "n", "", "person name") //rootCmd.PersistentFlags().IntVarP(&age, "age", "a", 0, "person age") rootCmd.PersistentFlags().StringVarP(&command, "command", "o", "", "execute command context") } //initConfig reads in config file and ENV variables if set. //func initConfig() { //if cfgFile != "" { ////Use config file from the flag. //viper.SetConfigFile(cfgFile) //} else { ////Find home directory. //home, err := homedir.Dir() //if err != nil { //fmt.Println(err) //os.Exit(1) //} // ////Search config in home directory with name ".cobra-demo" (without extension). //viper.AddConfigPath(home) //viper.SetConfigName(".cobra-demo") //} // //viper.AutomaticEnv()//read in environment variables that match // ////If a config file is found, read it in. //if err := viper.ReadInConfig(); err == nil { //fmt.Println("Using config file:", viper.ConfigFileUsed()) //} //} Copy code

3.4 Execution

# Compile $ go build -o smartant-cli $ ./smartant-cli smartant-cli is a CLI for SmartAnt applications. This application is a tool to migrations linux system. Usage: smartant-cli [flags] Flags: -a, --age int persons age -h, --help help for smartant-cli -n, --name string persons name $ ./smartant-cli -a 11 -n "xuel" name is xuel, age is 11% Copy code

4.implementation of clis with subcommands

After executing the cobra.exe init demo, continue to use cobra to add the subcommand test to the demo:

4.1 Generate sysinfo subcommand

$/Users/xuel/workspace/goworkspace/bin/cobra add sysinfo sysinfo created at/Users/xuel/workspace/goworkspace/src/github.com/kaliarch/smartant-cli $ tree . LICENSE cmd root.go sysinfo.go go.mod go.sum main.go smartant-cli utils utils.go Copy code

4.2 View subcommands

$ go build -o smartant-cli $ ./smartant-cli smartant-cli is a CLI for SmartAnt applications. This application is a tool to migrations linux system. Usage: smartant-cli [flags] smartant-cli [command] Available Commands: help Help about any command sysinfo A brief description of your command Flags: -a, --age int persons age -h, --help help for smartant-cli -n, --name string persons name Use "smartant-cli [command] --help" for more information about a command. $ ./smartant-cli sysinfo -h 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. Usage: smartant-cli sysinfo [flags] Flags: -h, --help help for sysinfo Copy code

4.3 Writing subcommands

  • sysinfo.go
/* Copyright 2021 NAME HERE <EMAIL ADDRESS> Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package cmd import ( "fmt" "github.com/kaliarch/smartant-cli/utils" "github.com/spf13/cobra" ) var ( host, pwd, username string port int command string ) //sysinfoCmd represents the sysinfo command var sysinfoCmd = &cobra.Command{ Use: "sysinfo" , Short: "check sys info message" , 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 ) { if len (host) == 0 || len (pwd) == 0 { cmd.Help() return } fmt.Println( "sysinfo called" ) utils.Sysinfo(host, pwd, username, port, command) fmt.Println( "sysinfo called commpled" ) }, } func init () { rootCmd.AddCommand(sysinfoCmd) //Here you will define your flags and configuration settings. //Cobra supports Persistent Flags which will work for this command //and all subcommands, eg: //sysinfoCmd.PersistentFlags().String("foo", "", "A help for foo") //Cobra supports local flags which will only run when this command //is called directly, eg: //sysinfoCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") sysinfoCmd. Flags().StringVarP(&host, "host" , "i" , "" , "host ip addr" ) sysinfoCmd.Flags().StringVarP(&username, "username" , "u" , "" , "host username" ) sysinfoCmd.Flags().StringVarP(&command, "command" , "c" , "" , "command" ) sysinfoCmd.Flags().StringVarP(&pwd, "pwd" , "p" , "" , "host password" ) sysinfoCmd.Flags().IntVarP(&port, "port" , "P" , 0 , "host port" ) } Copy code
  • utils.go
package utils import ( "bytes" "fmt" "golang.org/x/crypto/ssh" "net" "strings" //"strconv" "log" ) //smartant-cli func Show (name string , age int ) { fmt.Printf( "name is %s, age is %d" , name, age) } func sshConnect (user, pwd, host string , port int ) (*ssh.Session, error) { var ( auth []ssh.AuthMethod addr string clientConfig *ssh.ClientConfig client *ssh.Client session *ssh.Session err error ) //get auth method auth = make ([]ssh.AuthMethod, 0 ) auth = append (auth, ssh.Password(pwd)) //host key callbk hostKeyCallbk := func (host string , remote net.Addr, key ssh.PublicKey) error { return nil } clientConfig = &ssh.ClientConfig{ User: user, Auth: auth, HostKeyCallback: hostKeyCallbk, BannerCallback: nil , //ClientVersion: "", //HostKeyAlgorithms: nil, //Timeout: 10000000, } //connet to ssh addr = fmt.Sprintf( "%s:%d" , host, port) if client, err = ssh.Dial( "tcp" , addr, clientConfig); err != nil { return nil , err } //create session if session, err = client.NewSession(); err != nil { return nil , err } return session, nil } func Sysinfo (host, pwd, username string , port int , cmd string ) { var stdOut, stdErr bytes.Buffer //Login with username and password session, err := sshConnect(username, pwd, host, port) if err != nil { log.Fatal(err) } defer session.Close() session.Stdout = &stdOut session.Stderr = &stdErr session.Run(cmd) fmt.Println(strings.Replace(stdOut.String(), "\n" , "" , -1 )) } Copy code
  • Execute test
$ ./smartant-cli sysinfo 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. Usage: smartant-cli sysinfo [flags] Flags: -c, --command string command -h, --help help for sysinfo -i, --host string host ip addr -P, --port int host port -p, --pwd string host password -u, --username string host username $ ./smartant-cli sysinfo -i 121.3.10.55 -u root -P 22 -p xxxxxxx -c "cat/etc/hosts" sysinfo called ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 127.0.0.1 localhost localhost 127.0.0.1 hw-server hw-server sysinfo called commpled

Cobra flag golang built-in flag Cobra