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.

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 |
commands | create, get |
resources | cards, quotes ( in the future we might be adding more resources 😉 ) |
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) ..}

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..}

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.
1 thought on “Create kubectl Like CLI With GO And Cobra8 min read”
Comments are closed.