HomeDockerCreate kubectl Like CLI With GO And Cobra

Create kubectl Like CLI With GO And Cobra

We are using CLI tools for ages. You might be acquainted with the CLI tools like git, docker, kubectl, awscli, etc. What does it take to create a CLI? Can we create a powerful but cool CLI with ease? In this blog post, I will walk you through a fictional CLI named greetctl. We will implement it in golang with the help of the cobra library. I will try to keep the syntax as close as possible to kubectl. So let’s get ready to construct a tool like kubectl.

Syntax of kubectl CLI with example

Use case

Let’s create a fictional use case. The requirement is to create customized greeting cards for different occasions like birthdays, new year, thanksgiving in different languages like English, French etc. I am calling this CLI greetctl.

Specifications of the CLI is given below.

tool name greetctl
instructions create, get
sources cards, quotes ( in the future we might be adding more sources 😉 )
resource-name any random string to identify resource
parameters –name (-n) : Name of the user.
–language (-l) : english (en), french (fr)
–occasion (-o): birthday, newyear, diwali, thanksgiving, christmas

Syntax of greetcli will be as follows

greetctl {command} {resource} {argument(s)} {flag(s) ..}
Syntax of CLI with example.

Let’s try to relate this syntax with the more acquainted instructions.

kubectl create deployment webapp ---port=80
aws s3 mb s3://mybucket  --region=us-east-1
docker community create --subnet 203.0.113.0/24 iptastic

Here are few examples of our CLI

$ greetctl create card bob --name="Bob Marley" --occasion=birthday   
card with id [bob] has been created

$ greetctl create card eva -n="Eva Green" -o=thanksgiving -l=fr 
card with id [eva] has been created

$ greetctl get card eva
Joyeux Action de Graces!! Eva Green 
 

Implementation

We are going to implement the resolution in GO. Source code can can be download from the github repository. Here we will leverage Cobra library to create the CLI. If you are not acquainted with Cobra library, you might be amazed to know that popular CLIs like kubectl, etcdctl, docker is constructed on this superior library.

Cobra is constructed on a structure of instructions, arguments & flags. Let fit our CLI structure in this framework. Commands represent actions, Args are things and Flags are modifiers for those actions.

Earlier we have defined following syntax to out CLI.

greetctl {command} {resource} {resource-name} {parameters..}

Since there is no concept of sources in Cobra so we will mark it as sub-command. Above syntax can be rewritten as follows

greetctl {command} {subcommand} {args..} {flags..}
CLI syntax with example.

Setup project skeleton

Here we will use cobra library to create a bare minimal skiliton of cli.

# Create a go module for CLI.
go mod init greetctl

# Get Cobra library
go get -u github.com/spf13/cobra/cobra

# Create a bare minimal skeleton
cobra init --pkg-name greetctl

If you look into the project directory, you will notice a basic structure for your CLI .

1611307511 906 Create kubectl Like CLI With GO And Cobra

You can run it as follows

$ go run main.go

For now please assume the CLI greetctl as go run main.go. We will create an executable binary `greetctl` after modifying this basic structure created by Cobra, as per our requirements.

$ go run main.go --help
# Above command can be considered as blow
$ greetctl --help

main.go just calls the Execute() function of cmd/root.go. You can assume root.go as top-level command greetctl. As per the given specs, we need to enable CLI to address the following expression.

$ greetctl create card bob --name="Bob Marley" --occasion=birthday
# In dev mode expression will be as follows
$ go run main.go create card bob --name="Bob Marley" --occasion=birthday   

Create New Commands

So here we need to create a few instructions and sub-instructions. Let’s generate the basic template files for create and card using Cobra.

$ cobra add create
create created at /workspace/new-greetctl

$ cobra add card
card created at /workspace/new-greetctl

By default, both create and card instructions will be added to the top-level command (root.go). But we want to add card (card.go) sub-command to create (create.go) command instead of top-level command (root.go) so lets edit /cmd/card.go. Add cardCmd in createCmd instead of rootCmd.

1611307511 525 Create kubectl Like CLI With GO And Cobra

Update cardCmd as per given specification

1611307512 157 Create kubectl Like CLI With GO And Cobra

Execute the following command to trigger card command.

$ go run main.go create card
card called

It is a bare minimal default implementation so output of cardCmd is “card created”. Here you can update the function “Run: func(cmd *cobra.Command, args []string)” as per requirement.

Read Arguments

go run main.go create card my-card

In this example, my-card is an argument. Arguments are recorded in args param of the Run function of cardCmd command.

var cardCmd = &cobra.Command{
	Use:   "card",
	Short: "A brief description of your command",
	Long: `A longer description that spans multiple lines and likely contains examples
and utilization 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("card called")
		fmt.Println("Here are the arguments of card command : " + strings.Join(args, ","))
	},

Let’s give it a try by adding few arguments.

$ go run main.go create card ARG1 ARG2 ARG3
card called
Here are the arguments of card command : ARG1,ARG2,ARG3

Create and Read Flags

So far we know, how to create instructions and read arguments. In this section, we will create few flags with descriptions and read them in the program.

go run main.go create card my-card -n="Bob Marley" -o=birthday

Here is a snippet to create flags. In this example name and occasion flags are required. It is possible to provide default values. Here “en” will be taken as language if the flag is not present.

func init() {
	createCmd.AddCommand(cardCmd)
	cardCmd.PersistentFlags().StringP("occasion", "o", "", "Possible values: newyear, thanksgiving, birthday")
	cardCmd.PersistentFlags().StringP("language", "l", "en", "Possible values: en, fr")
	cardCmd.PersistentFlags().StringP("name", "n", "", "Name of the user to whom you want to greet")
	cardCmd.MarkPersistentFlagRequired("name")
	cardCmd.MarkPersistentFlagRequired("occasion")
}

Command failed, if required flags are missing

$ go run main.go create card my-card

Error: required flag(s) "name", "occasion" not set
Usage:
  greetctl create card [flags]

Flags:
  -h, --help              help for card
  -l, --language string   Possible values: en, fr (default "en")
  -n, --name string       Name of the user to whom you want to greet
  -o, --occasion string   Possible values: newyear, thanksgiving, birthday

Global Flags:
      --config string   config file (default is $HOME/.greetctl.yaml)

Flags can be read inside the program as follows

Run: func(cmd *cobra.Command, args []string) {
  name, _ := cmd.Flags().GetString("name")
  language, _ := cmd.Flags().GetString("language")
  fmt.Println("value of the flag name :" + name)
  fmt.Println("value of the flag language :" + language)
},

Lets give the command a try with the specified flags.

$ go run main.go create card my-card -n="Bob Marley" -o=birthday -l=fr
value of the flag name :Bob Marley
value of the flag language :fr

Install CLI

Once you are done with all the changes you should create binary using following command.

$ go install greetctl

Let’s play with greetctl CLI

Finally, we have done with the implementation part. Now we are ready to play with CLI. Here are few examples.

This is how –help(-h) flag works on the card sub-command.

$ greetctl create card -h

This command creates cards. Example:
	greetctl create card eva -n="Eva Green" -o=thanksgiving -l=fr
	greetctl create card bob --name="Bob Marley" --occasion=birthday

Usage:
  greetctl create card  [flags]

Flags:
  -h, --help              help for card
  -l, --language string   Possible values: en, fr (default "en")
  -n, --name string       Name of the user to whom you want to greet
  -o, --occasion string   Possible values: newyear, thanksgiving, birthday

Let’s create some sources (cards).

$ greetctl create card bob --name="Bob Marley" --occasion=birthday
card with id [bob] has been created

$ greetctl create card eva -n="Eva Green" -o=thanksgiving -l=fr
card with id [eva] has been created

Let’s fetch the existing sources

$  greetctl get card eva
Joyeux Action de Graces!! Eva Green 

Conclusion

In this blog, we have covered some basic features of our CLI tool. Likewise RestApi, a good CLI tool should enable the user to perform specific actions on a set of sources. Users should be able to filter results or alter the behavior of actions using flags. If you are interested then please go through the github repository for the concrete implementation.

Source

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular