“This article has participated in the good article call order activity, click to see: back end, big front end double track submission, 20,000 yuan prize pool for you to challenge!”

A background

In the cloud native era, many people have heard about Kubernetes/ ETCD, etc. When we look at the source code or secondary development of Kubernetes, we can find that they use a command line library Cobra, which is a tool for writing command lines, providing a scaffolding, Used to quickly generate a Cobra based application framework.

The author is a very famous spf13, I believe you are familiar with vim, you can use vim’s ultimate terminal spf13-VIm, can be configured with one click, very convenient, there is also the work viper is a complete configuration solution. Support JSON/YAML/TOML/HCL/envFile configuration files, and can also heat load, the configuration of preserving, Hugo and his works.

We can use Cobra to quickly develop the command-line tools we want, very quickly.

Two Functional features

  • Simple subcommand line mode, such as App Server, app fetch, etc
  • Fully compatible with POSIX command line mode
  • Nested subcommand
  • Support global, local, concatenated flags
  • It is easy to build applications and commands using Cobra, using Cobra create AppName and Cobra Add cmdName
  • If the command input is incorrect, the system provides intelligent suggestions, such as app srver. The system displays a message indicating whether the srver is app Server or not
  • Automatically generates help information for Commands and flags
  • Automatically generate detailed help information, such as app Help
  • Automatic identification -h, –help Help flag
  • The auto-generated application commands autocomplete under bash
  • Automatically generate the man manual for the application
  • Command line alias
  • User-defined Help and Usage information
  • Optional tightly integrated Viper Apps

Three use Cobra

3.1 installation

Cobra installation is very simple and can be obtained by using go get. After the installation is complete, open the GOPATH directory. In the bin directory, there should be Cobra already compiled.

There are three concepts that need to be understood before using COBRA, which are also part of the command line: Command, Flag, and ARGS

  • Basic information about the command itself, represented by command (cobra.Command)
  • The flag object is flag.flagset
  • The final argument, denoted by args, is usually a []string

Corresponding to the following example:

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

Get is commond, -u is flag, test.com/a/b is args

3.2 Generating applications

$ /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.├ ── ├─ CMD │ ├─ root. Go ├─ go.mod ├─ goCopy the code

3.3 Design of CLS program

Create imp directory in smartant-cli directory and rewrite the utils. Go file as follows

package utils

import "fmt"

func Show(name string, age int) {
	fmt.Printf("name is %s, age is %d", name, age)
}

Copy the code
  • main.go
package main

import "github.com/kaliarch/smartant-cli/cmd"

func main(a) {
	cmd.Execute()
}
Copy the code

As you can see, the main function executes the CMD package, so we just need to call the utils package inside the CMD package to implement the smartAnt-cli program. Then open the root.go file to see:

  • 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 the code

From the source code, the CMD package does some initialization and provides the Execute interface. It’s very simple, viper is the cobra integrated profile reading library, we don’t need to use it here, we can comment it out (the resulting application is probably around 10M without comment, it’s better to comment out if you don’t use it here). All cobra commands are implemented through the structure cobra.Command. To implement smartAnt-CLI functionality, we obviously 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 the code

3.4 perform

$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 the code

Implement CLIS with subcommands

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

4.1 Generating the 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 │ └ ─ ─ Go ├── go-.mod ├── go-.sum ├─ main.go ├── smartant-cli ├── utils ├─ utils. GoCopy the code

4.2 Viewing 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 the code

4.3 Writing subcommands

  • sysinfo.go
/* Copyright © 2021 NAME HERE 
      
        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(a) {
	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, e.g.:
	// sysinfoCmd.PersistentFlags().String("foo", "", "A help for foo")

	// Cobra supports local flags which will only run when this command
	// is called directly, e.g.:
	// 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 the 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
	ifsession, 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
	// Use the user name and password to log in
	session, err := sshConnect(username, pwd, host, port)
	iferr ! =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 the code
  • Perform the 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 Localdomain localhost4 localhost4. Localdomain4 127.0.0.1 localhost localhost 127.0.0.1 hw-server HW-server sysinfo called commpledCopy the code

Five other

Cobra is very powerful and can help you quickly create a command-line tool, but if you simply write a very simple command-line tool, there are very few flag options. The Golang Built-in flag library is sufficient. Of course using a look at personal choices, Cobra is more suited to complex command-line tools.

Refer to the link

  • github.com/spf13/cobra

  • www.huweihuang.com/golang-note…

  • Juejin. Cn/post / 692454…

  • O-my-chenjian.com/2017/09/20/…