Create kubectl Like CLI With GO And Cobra

Reading Time: 6 minutes

We are using CLI tools for ages. You might be familiar 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 build 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 namegreetctl
commandscreate, get
resourcescards, quotes ( in the future we might be adding more resources 😉 )
resource-nameany 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 familiar commands.

kubectl create deployment webapp ---port=80
aws s3 mb s3://mybucket  --region=us-east-1
docker network 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 solution 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 familiar with Cobra library, you might be amazed to know that popular CLIs like kubectl, etcdctl, docker is built on this awesome library.

Cobra is built on a structure of commands, 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 resources 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 minimum 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 minimum skeleton
cobra init --pkg-name greetctl

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

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 specifications, 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 commands and sub-commands. 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 commands 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.

Update cardCmd as per given specification

Execute the following command to trigger card command.

$ go run main.go create card
card called

It is a bare minimum 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 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("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 commands 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 resources (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 resources

$  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 resources. 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.

Written by 

Mayank is a polyglot programmer who believes in selecting the right tool for the job. He has more than 8-year experience in Java Platform. He has been a Scala enthusiast ever since he came to know this beautiful language in 2010. He has been developing enterprise applications on the reactive stack. He's a big fan of agile development, scalable software and elegant code. Mayank has extensive knowledge in a huge spectrum of areas of software, and the ability to dive deeply into a new technology and achieve expert level in no-time. He found fun to architect complex systems in the simplest way and quite handy in Design patterns, micro-services & DevOps technologies. On the personal front, he is a marathon runner, spiritual learner & yoga practitioner.

1 thought on “Create kubectl Like CLI With GO And Cobra8 min read

Comments are closed.

Discover more from Knoldus Blogs

Subscribe now to keep reading and get access to the full archive.

Continue reading