How to Build a Command Line (CLI) Application with Go

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
as an engineer we've probably all come across a command line and encountered a thing called a terminal whether you write bash batch shell or powershell if you've run docker run npm install pip install terraform apply cube ctl apply git checkout git commit get pull grep curl and more you've come across command line applications welcome to another video on the go programming language in today's video we're going to be taking a look at the basics of how to write a command line application we've got a lot to cover so without further ado let's go if we take a look at my github repo i have a golang folder and in the golang folder i have an introduction folder with a readme and this readme talks about the introduction to go how to install and run go in a container how to write our first program and we talk about all the basics of go if you haven't seen that video check out the link below to my introduction to go and follow along in this video we're going to be taking a look at the part 4 folder which is all about command line applications and in the part 4 folder we have a readme and this is the introduction to go for command line applications and this is going to be showing all the steps that we are about to do today so be sure to check out the link down below to the source code so you can follow along as with every other go video we've done we start with a dev environment in this example i'll be using docker as my dev environment in our introduction to go video i've showed you how to run go in docker as well as install go on your local machine if you prefer to do so so in my github repo under the golang introduction part 4 folder i have a docker file and in here i just say from golang and i'm going to run go 1.15 and it's an alpine version so it's very small and then i also say my working directory is slash work and to get this container built and run it i can say cd golang introduction part 4 folder so i can change directory to this folder and then i can say docker build target as dev and i can tag the image as go and i copy that paste it to the terminal and that's gone ahead and built our container image to run the images very straightforward i say docker run minus it for interactive terminal i say minus v and i mount the current working directory into that folder slash work inside of the container this will give us access to the go source code so we can run go commands without having go installed and then i simply reference the container image i want to run which is go this is the one we've tagged up here and i run a shell terminal so i go ahead and copy this i paste it to my terminal and that'll give me a shell terminal so now i can say go version and we can see we have access to the go programming language inside of a container so no matter how complicated our application might get i always recommend to start with a hello world application we're going to define a go repository write a go module add a go file then define a package main with the main function so to do all of that i'm going to go ahead and create our application i'm going to make a new folder called videos so i say mkdir videos that'll create a folder on the left hand side here then i'm going to go ahead and change directory to that folder and then i'm going to create a module for our application and in this demo it's going to be a videos application so an application that gets and updates youtube videos so i'm going to say go mod init videos and that'll create a go module called videos we can see on the left hand side here it's created a go.mod file with our module name and it's running go 1.15 and the next step we're going to do is create our base source code so what i'm going to do is in this videos folder i'm going to create a new file called main.go and then i'm going to copy a hello world example into that file i'm going to go ahead and copy the snippet paste it into our file so what we do in the first line is we define a package called main in our introduction to go video we've covered packages modules and what they mean we're going to import a package here called fmt this allows us to print messages to the terminal and we create a function called main and in here i'm just going to say print line hello world so this will just print hello world to the screen so remember if you're new to go to check out my introduction to go video down below where we cover all the groundworks and basic that you're about to see in this video so next up we're gonna build an application that deals with youtube videos it's able to list and update videos once we've built that application we're going to be building a command line interface to drive the application so to test out our hello world application i can just say go build and that will produce a binary called videos on the left hand side then to run that application i can say dot slash videos which will run that static binary and we can see it prints hello world to the screen which is this line of code here now to create our videos application i'm going to create a new file in this videos folder called videos.go and this file will be managing videos like listing them and updating them so we need a function to get videos and save videos so we always start off with a package i'm going to create a package called package main and then because we're going to be storing our videos as json we need a data structure so we need package to deal with json in a previous video i've covered data structures and how to deal with json using go so to do that we're going to import ioutol and the encoding json package then we've also dealt with data structures like types and structs in go so when you're building an application like a videos application it's always good to design what a video might look like so we're going to create a new struct called video and this is what a video looks like it has an id a title a description an image url and a url to the video so every video will have these fields and next up we'll create a function which will return all the videos so i have this all copied into the readme so i'm going to grab this i'm going to paste it into myvideos.go file so this is a function called get videos which returns a slice of videos in our fundamentals video we've covered the concept of variables and slices we've also covered how to read json files so we're going to be reusing that snippet in today's video so you can see i'm using the i o util package and i'm reading a file called videos.json this file will store all our videos we'll read that back as a file bytes this is a byte array we also return any errors and we handle that error in case there's an error that has occurred by reading that file we then use the json package and call the unmarshall function to convert this byte array into our video slice then we also handle any errors that might occur from that and then we return the slice of video so in that previous video we've learned how to deal with json data and how to read and write to file so we'll be reusing that in this video we also have that videos folder and we have an example of that video's json file i'm going to right click i'm going to copy that and i'm going to paste it into my part 4 command line videos folder and if we take a look at that json file we can see that it has two videos in an array and it has an id a title image url url and a description so that is a function to return all the videos from a file next up we'll need a function to save the videos back to the file in case we want to make some changes so for that i have another function here called save videos which i'm going to copy and i'm going to paste it below our get videos function that function will take in a slice of videos use the json package to convert it back to a byte array and then write that byte array back to the videos.json file so we're using a file as our form of storage in this example now as you can see our functionality is completely standalone and separate in its own go file that means we can choose how we want to run that application we can expose our application over an http endpoint like a microservice or we can expose the application over the command line by writing a command line interface if you're interested to look at http and how to write a microservice in go check out my video down below on http with go to build a command line interface we use the flag package it allows us to define sub commands like get add delete and whatever we like it also allows us to add arguments or flags which allows us to accept inputs like strings numbers booleans and more so if we take a look at the go documentation there's a package called flag which implements command line flag passing so we can basically pass things to our application and the flag package will convert it into variables that we can understand so basically what the flag package allows us to do is pass things to our application so as i mentioned earlier we run our applications saying dot slash videos but we don't pass anything to it so what we can do with the flag package is we can do things like this i can make a function that can get all video so i can save videos i can add a sub command called get with an option to say all so get all the videos then i can also say get a video by its id so i can say videos get and i can pass a flag in called id with a video id that i want to return i can also add an add sub command with a multiple flags that allows me to add a video to our application so technically the videos get sub command in both cases would call the get videos function that we've written earlier the videos add sub command would call the save videos that we've mentioned earlier so to start we have to import this flag package so what i'm going to do is i'm going to say import and i'm gonna import the flag package so i'm gonna copy this block and i'm gonna head over to our main function since that's the entry point of our application and i'm gonna replace this import with the updated one so here we import fmt because we're still using it and we also call flag so now that we've imported the flag package we have to go ahead and define sub commands as well as arguments or flags that we're about to accept so what we need to do next is we need to go ahead and add these sub commands to our main.go i'm going to go to my main.go i'm just going to clear out this fmt statement and i'm going to copy this first command that i have over here and this is going to be the definition of our get sub command so we can say videos get so we define a new variable which is called flag set and flag sets are basically sub commands we give it a name called get and we tell it that it needs to exit on error if there's any error our application will exit now in our case our get command will have two types of inputs one is all so we want to get all the videos and the other one is getting a video by video id so we need to define two inputs so i'm going to copy this block over here which is that definition so these are the inputs for the get command i define an input new variable called get all which you can see i'm using this the flag set of get cmd over here and i'm defining a boolean variable and i pass in the name which is going to be dash dash all i give it a default value and a description this will tell the user when they run the help command how to use this field and the next input i say is get id which is also on that flag set and i'll get command i give it a name called id this time it's a string the default value is blank and i give it a description this is the youtube video id for the video we want to return so this is all we need to define our get command next up we'll need to create the add command now for the add command we also have to create a new flag set called add the same way we did the get command so i'm going to go ahead and copy this and paste it to a new line we define the add command and then we add all the fields we want same way we did on our get command so i'm going to go ahead and paste this to our file you can see here we've defined a new flag set called add this is our add command and then on this flag command we've added a bunch of variables we've added an id which is our video id we want to add the title of the video the url the image url as well as a description which are all string variables so now that we've defined our sub commands and our fields when a user runs our cli tool we may require to write some validation to ensure the user enters the right commands and the right number of fields so to add some simple validation and to make sure the user types a sub-command or even the correct sub-command we have to read what input a user has sent to our application to do this we use the golang package called os which allows us to read arguments passed to our application if you type control f and search for orgs you can see that this package has a variable called args which holds in the command line argument starting with the program name so we can firstly do a very simple check to ensure the person has passed in a sub command by checking the length of that argument so i have the following snippet that i can copy and paste to our code base and in our fundamentals video we've covered slices and arrays and there's a built-in function in go called len which can check the size of an array and in this case we call the os.orgs we pass that into the len function and we check its length and if it's less than 2 we print back an error saying that we're expecting a get or an add sub command and we exit the application this is just a simple check to ensure the user is passing in a sub command to our application i just thought i'd show you the os package which is very useful if you want to extend this functionality and perform any other requirements now that we've defined our command line interface inputs we need to feed this to our videos application and it's important to keep these two things separate that means if we want to update the command line interface we don't have to worry about it breaking our videos app if we want to update our videos app it's easier to ensure that it doesn't break the command line interface so let's go ahead and plug in this interface with our videos application so we've defined our get command with the fields as well as the add command with its fields so next up i'm going to create some handler functions to handle each of the commands to keep the code tidy i'm going to create a separate function for each sub command so i'll create a new function called handle get so outside of the main function let's paste that function which is called handle get it takes in our get command which is the flag set we've defined up here and it accepts all the inputs which is the boolean the all flag as well as the string the id flag that we've defined over here and then similarly for the get function i'm going to create a handle add function so if i go down i'm just going to paste that function which is handle add that'll take in our air black set which we've defined up here as well as all the fields which you can see now these are just empty functions they're currently not doing anything so to invoke these functions i'm going to write a simple switch statement that looks at the os args array to figure out what command the user is running so if the user runs the get command we want to call the handle get function if they run the add command we want to run the handle add function and if they pass in an unknown command we want to give them an error so to do that i have a simple switch statement which i'm which i'm going to copy and i'm going to paste it to our main function just after our short validation here so i'm going to paste that and that's going to run a switch statement on the os args and it'll look at the second value of the os args array which is going to be our sub command so you can see here we can separately handle each case so we have a case for get a case for add and if we don't understand the input a default so now that we have a handle function for each of our sub commands this allows us to take appropriate action in each of them the first thing we want to do is do some validation on the input that's been passed to us and we also have to parse all the arguments that's been passed to our application so the flag package has this all built in with a parse function now what i'm going to do inside of our handle get function i'm going to paste this code which is going to take our flag set that we've passed in called get cmd and parse it it's going to run the parse function and pass in the arguments that we've used earlier of the os package this parse command will basically pass all the command line input that we've passed to our application so that it can understand it and then next up we can define some input validation so for that i have a simple if statement over here that checks our input so what we're going to do is just paste that so what we say here is if all is false so the user did not pass in anything for the all flag and the id is blank we print back a message to the user saying the id is required or specify all for all videos we also print the defaults and we exit now with that validation in place let's run the functionality which is basically gonna run if the user passes in the all flag i have another snippet for that which i'm gonna copy and paste below this previous snippet and in this logic i say if all is true meaning the user is passing in the dash dash all flag we want to return all videos so what we're going to do here is call our get videos function we've defined earlier return that as a slice of videos and we're going to print it out to the user in the terminal so we can print our columns first you can see i'm using printf to format it i'm going to print the id a tab the title another tab the url another tab the image url and a description and a new line character and then what i'm going to do is write a simple for loop that ranges over the videos that we've returned and formats the output of each video so we're going to print the video id the title the url the image url and the video description to the screen and then we simply run the return statement as we don't want the application to execute further so this block is to handle if the user passes the all flag if the user passes an id flag we've got to go and search for that video so i have another statement block here that we need to copy and we can just paste it below the previous if statement and this block is really simple we basically say if the id is not blank so we just do a simple check we then get all our videos we read the id that's been passed in and then we loop through all the videos and check if the id is equal to the id that we've passed in and if it is we print out again the titles which are the column names and then we print out that video data so that will send a video back to the user if they've passed in the id flag if they're looking for a specific video so that is our handle get function which is a simple function that just takes two inputs now for the handle add function there's multiple inputs that we have to parse and validate so it would be good to create a separate function called validate videos where we can validate all that data that the user passed in before we run the save videos function so let's create a validate video function with similar inputs to the handle add function so i'm going to copy paste this snippet to our main.go below so here you can see the function is exactly the same as the handle add it's called validate video and it takes in the same input and it allows us to validate our video input and in this function i'm going to add a simple if statement i'm going to copy this expand this function and paste this if statement and what we do in here is we just make sure all these fields are passed in i'm going to say if the id is blank or the title is blank or the url or image url or description is blank print out saying all fields are required for adding a video i can also print the defaults which is a built-in function for the flag package and then i can run the exit command to prevent any further execution and that allows us to keep the validation functionality separate from our add handler so if i go to the handle add function i can call our validate video function by saying validate video and pass in all the inputs that's passed to this function now to add the video what we need to do is take in all these fields that the user has passed to us and form a new struct of video i'm going to define a new video like this i'm going to paste that to my code i'm going to say video equals new video struct pass in all these fields and then what i'm going to do is i'm going to go ahead and grab all our existing videos by saying videos equals get videos and then i'm simply going to append our videos to that list of videos so the new video will be added to this existing list using the built-in append function that'll give us a new video slice with the added video and then i simply call the save videos function so i save the videos back to file so now we've written our handle get function a validation function as well as a handle add function now we need to go ahead and plug those functions into our switch statement so they can be executed so what i'm going to do is in this get section here in case of get i want to paste this i want to call our handle get function and pass everything to it and if the user calls the add command i want to be able to call our handle add function and pass the input to that one now if i build my application i can say go build and if i try to run my application you can see the first line of validation has kicked in saying expected get or add sub commands i can then say dot slash videos get and we can see that the help text is printed out with our validation so we need to pass in either an id or we have to pass in all so i can say dot slash videos get dash dash all and that will print out all our videos in the json file you can also see if i run the videos add command that it prints out the help text for that and tell us exactly what fields we need to pass in so i can test out the add command by saying dot slash videos add and i can pass in each field like this you can see i'm just passing in test as a value for each of the fields i go ahead and run that and that will go ahead and add our videos to the list if i take a look at the videos.json file we can see that our video has now been added i can also do get all again and we can see our videos being returned the test one i can also return that video by id by saying videos get passing in that id and only returning that single test video now the final piece of the puzzle is to dockerize our application so we can run our command line app inside of a docker container so far we've only used our container as a development environment so what if we wanted to build a container that we can pass to other people to use our application so what we've done so far is defined a target called dev so that allows us to attach debuggers and do development stuff so what we're gonna do is take advantage of docker multi-stage to compile do all our unit testing and building and produce a lightweight image that our users can run so for that i'm going to add a new layer here called build which looks exactly like the top one the only difference is we're going to call it the build target because this is where we want to run go build and possibly do some unit testing if we'd like i'm going to create a working directory called videos and then what i'm going to do is copy in all my video source code in this videos folder on the left i'm going to copy that into the videos directory inside of the container and then i'm going to run go build and produce a binary called video now the reason i have this build stage separate from the dev stage is because the dev stage can have things like debuggers and other things and plugins to allow us to attach vs code to the container and do debugging which we may not want to do as part of the build step and now in this build step we'll be producing our binary by running go build but we don't want the entire golang sdk in our final image so what i'm going to do is create a new layer and this time i'm just going to use alpine which is a very small os which does not include the golang sdk and i'm going to call this one runtime and then i'm going to copy from the build step i'm going to copy the videos binary from that videos folder into user local bin video so we can simply just call our application and then for testing purpose i'm just going to copy in our videos json file as well and i'm going to copy it run.sh this is the command that we will run when the container starts up which i'll show you in a second we're going to give this run script execution rights and we're going to execute that run script as part of the entry point so in the part 4 folder i need to create a new file called run dot sh and this file is going to look very simple we're just going to start bin sh and we're going to run our videos binary and pass in anything that the user passes in to our binary so when we do the docker run command we can pass in our get or our add sub commands with all the fields to the container so to build up this container image i'm just going to say docker build dot minus t and i'm going to tag this image as video so i'm going to run this this will go ahead and build up our container image and to run the videos container i can just say docker run interactively the video's image name and i can say videos get help so this will run the get command and pass in the help flag and show us the help text for our git command i can also run the help command on the ad and we can see this is how we call the add one so this is how you would run a golan command line application in a container so i hope this video helped you understand the fundamentals of building your first command line application with go let me know down in the comments what sort of videos you'd like me to cover in the future and if you'd like the video remember to like and subscribe and hit the bell and if you want to support the channel even further hit the join button down below to become a member and also check out the link down below to the community page and as always thanks for watching and until next time [Music] peace
Info
Channel: That DevOps Guy
Views: 8,409
Rating: undefined out of 5
Keywords: devops, code, cloud, training, course, development, deployment, containers, web, programming, commandline, cli, coding, program, linux, learning, basics
Id: CODqM_rzwtk
Channel Id: undefined
Length: 25min 26sec (1526 seconds)
Published: Sun Mar 21 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.