Build/Test/Deploy your Go App using IBM Cloud Toolchain
Tags: cloud, devops, go, beginners
Published: 2020-09-24 12:45:08

Whether you're a project manager, part of a dev team, or flying solo, there is always the looming issue of incremental changes to your project. Gone are the days of waterfall development - Agile is the 'in' thing. You are running fortnightly sprints and releasing on a regular basis. You've probably seen a lot of options for deployment methods, lots of continuous integration/continuous delivery (CI/CD) pipelines, and much more.

On the face of it, this is a journey for getting the application on your local system deployed into the cloud in stages using a pipeline.

A pipeline allows you to work on small features independently and deploy them one by one without taking down the application, or better yet, prevent you from having to write the application all in one go and then deploying it in one big push. This will allow you to fail small and fail fast. In return, leaving less technical debt and allowing faster development.

The diagram below outlines the aim of this using a simple pull request example
![Pull Request](https://dev-to-uploads.s3.amazonaws.com/i/h9lo3isuxsqjmmv7b3h5.png)


Now you can see how simple it really is, in this blog, I will show you how to build, test, and deploy your application under one roof in a matter of minutes using IBM Cloud Toolchain. That's right.. in a matter of minutes.

But first, you will need some prerequisites:
1) IBM Cloud Lite account - sign up [here](https://ibm.biz/Bdq3RU)
2) GitHub account
3) Golang installed on your machine

Let's get started!
## Create your Go app and get it into GitHub

### Step 1 - Create an empty repository in GitHub
Navigate to your GitHub profile, click on the `+` in the top right corner and select "New repository". Give it a name and make sure it is public. Feel free to also add a license .gitignore file and a README.md if you like
![Create New Repository](https://dev-to-uploads.s3.amazonaws.com/i/55azj26ry9cdcxj3nw8q.png)

### Step 2 - Let's create a simple Golang HTTP Server with an accompanying test

In a terminal clone your new repository into `$HOME/go/src/github.com`

Navigate into the cloned project using the `cd` command and initiate go modules with the command `go mod init` (in some cases you may need to specify the exact project path) and then open it in your preferred editor (I use Visual Studio Code)

Note - if you do not have this folder tree already (go/src/github.com), then create it

Create a test file in the root of the project (main_test.go):
```
package main

import (
	"net/http"
	"net/http/httptest"
	"testing"
)

func TestHomeRoute(t *testing.T) {
	t.Run("returns message", func(t *testing.T) {
		request, _ := http.NewRequest(http.MethodGet, "/", nil)
		response := httptest.NewRecorder()

		Home(response, request)

		got := response.Body.String()
		want := "This is a toolchain test PR"

		if got != want {
			t.Errorf("got %q, want %q", got, want)
		}
	})
}

```
Run the test and watch it fail using the following command in your terminal
`go test ./...`

Now it is time to write just enough code for the test to pass (this is called TDD - test-driven development)

Create a main file in the project root (main.go). This will be home to the sever code 
```
package main

import (
	"fmt"
	"net/http"
)

func main() {
	// Create the route handler listening on '/'
	http.HandleFunc("/", Home)
	fmt.Println("Starting server on port 8080")

	// Start the sever
	http.ListenAndServe(":8080", nil)
}

func Home(w http.ResponseWriter, r *http.Request) {
	// Assign the 'msg' variable with a string value
	msg := "This is a toolchain test PR"

	// Write the response to the byte array - Sprintf formats and returns a string without printing it anywhere
	w.Write([]byte(fmt.Sprintf(msg)))
}

```
Run the test command `go test ./...` and your test should pass

To see this in action, you can run the program with the `go run main.go` command and navigating to `localhost:8080` in your web browser

To quit the program use the commands `control + C` in your terminal

### Step 3 - Push your code into the GitHub repository

Let's get this code into GitHub.. Don't worry it's pretty simple, just follow the following commands

`git add .` - this command will add all of the new/modified files into staging ready to be committed

`git commit -m "initial commit"` - this command commits the changes ready to be pushed with a message so they can easily be identified

`git push origin master` - this will push the changes into the master/main branch - this is fine for an initial commit but going forward you will use feature branches

Awesome - you now have a simple Golang HTTP server with a test that you can push to any cloud, but what if you wanted to branch out and have others work on this? What if you had to add incremental changes and automate the deployment of it? - manual deployments need precision, they are time-consuming and weight on any chain for a team!

Let's use IBM Cloud Toolchain for this. 
"What is IBM Cloud Toolchain?" you ask...

Well, that's quite simple. It is a tool that allows you to concatenate actions, called stages. Each stage will have 'jobs' and these allow you to choose what you want to do with your application in that stage, be it build, test, or deploy - heck, why not all of them?

In this blog, we will run a test stage to ensure the code compiles and the tests pass on the pull request. Then once the pull request is merged into the main production branch another stage will compile the code, run the tests, and then automatically deploy the application into Cloud Foundry. Sound good? - let's go!

## Creating a toolchain

Login or [sign up](https://ibm.biz/Bdq3RU) to IBM Cloud

Navigate to the DevOps section in IBM Cloud using the navigation menu on the left
![DevOps](https://dev-to-uploads.s3.amazonaws.com/i/o54ta2jhzoxw8as6b7uv.png)

Select your region and then create a new toolchain
![New Toolchain](https://dev-to-uploads.s3.amazonaws.com/i/8twn69plr9fh8xxrrnxd.png)

Scroll down to the bottom and select "Build your own toolchain"
![Build your own toolchain](https://dev-to-uploads.s3.amazonaws.com/i/1nps7ofy16fohhpzutmd.png)

Choose an appropriate name and select the correct region for your toolchain
![Name Toolchain](https://dev-to-uploads.s3.amazonaws.com/i/yezb551djst7adfueaks.png)

Cool, you now have the foundation - add a new tool to your toolchain
![Add Tool](https://dev-to-uploads.s3.amazonaws.com/i/pk2fts0k3h00m3qj5fn4.png)

We want to have GitHub integration so search for "GitHub" and select the GitHub tool
![Add GitHub tool](https://dev-to-uploads.s3.amazonaws.com/i/8ma97rxcm7118a1y3ab6.png)

Set up the GitHub tool to have the correct information for your project repository, as an example, mine is
![Setup GitHub tool](https://dev-to-uploads.s3.amazonaws.com/i/zvxtenffem5vvcftsv4n.png)

You should now see that the GitHub tool on the toolchain overview has ticks and the word "configured". This will give it access to the repository information (you may be asked to confirm this on GitHub)

Next, you need to add a pipeline. On the toolchain overview, just like you did with the GitHub tool, select "Add tool" and search for "pipeline". Select "Delivery Pipeline"
![Add pipeline tool](https://dev-to-uploads.s3.amazonaws.com/i/4ebgcxu86z9254sb6ee8.png)

Configure the pipeline by giving it a name and ensuring the pipeline type is "Classic" and not "TekTon"
![Configure Pipeline](https://dev-to-uploads.s3.amazonaws.com/i/kmimzar6242s3rccssd2.png)

If you haven't used a pipeline in IBM Cloud before, you may see the following error. You just need to add a continuous delivery service which will enable the CI/CD
![Pipeline Error](https://dev-to-uploads.s3.amazonaws.com/i/wiu5pv5d0r97c9bi0gjc.png)

Search for continuous delivery and select "Continuous Delivery" - You only need to do this once!
![Search Continuous Delivery](https://dev-to-uploads.s3.amazonaws.com/i/tmxoe6mzeds3tb4ukhbw.png)

Setup the service. Make sure the region is set to the same as your toolchain, give it the same name as what you called your pipeline service. In this example, it is "CI-CD-Service". Once filled out, create it
![Continuous Delivery Setup 1](https://dev-to-uploads.s3.amazonaws.com/i/el0vyx9mflobw925f59e.png)
![Continuous Delivery Setup 2](https://dev-to-uploads.s3.amazonaws.com/i/6oscirtx43wnilmzypa9.png)

Now head back to your toolchain and click on the Pipeline box itself. You will be taken to a blank page. In the top right select "Add Stage"
![Add Stage](https://dev-to-uploads.s3.amazonaws.com/i/krfetfjgb3yfcepdt62u.png)

Call it the "Test Stage" and this will build the code and then run the tests. Make sure the input type is the git repository for your project, the branch is set to "master" and you want this stage to run "When a pull request is opened or updated"
![Test Stage Setup 1](https://dev-to-uploads.s3.amazonaws.com/i/21jns3spo01yjnhj3d2c.png)

Once the "Input" has been set up, move along to the "Jobs" and create 2 jobs. 1 x Build and 1 x Test. There is no Golang buildpack in the toolchain so we need to use a docker image with the environment set up for us
![Test Stage Setup 2](https://dev-to-uploads.s3.amazonaws.com/i/hmclqs0xw3ejlrttrsio.png)

Add the following script into the build script box on the "Build" stage and modify it to suit your project. I called my directory "gohttpserver" but you can change it to be the name of your repository/project. This script just moved the code from a temporary directory on the virtual machine into the directory tree much like you would have locally on your machine. It then builds a binary to test the code compiles
```
#!/bin/bash
pwd
ls -la
cd $HOME
mkdir -p go/src/github.com
mkdir -p go/pkg
mkdir -p go/bin
export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin
cd $HOME/go/src/github.com
cp -ar /home/pipeline/* /root/go/src/github.com
mv ./* gohttpserver
cd gohttpserver
go build -o serverbinary
chmod -v +x serverbinary
pwd
ls -la
echo code compiled successfully
```

Move onto the "Test" stage and do the same. Copy the script from your "Build" stage and modify the end of it to look something like this
```
#!/bin/bash
pwd
ls -la
cd $HOME
mkdir -p go/src/github.com
mkdir -p go/pkg
mkdir -p go/bin
export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin
cd $HOME/go/src/github.com
cp -ar /home/pipeline/* /root/go/src/github.com
mv ./* gohttpserver
cd gohttpserver
go test ./...
echo test run successful
```
This is just running the tests within the repository and not building a binary

That is the Build/Test stage complete, now we need to create the deploy stage. This stage will run when the pull request is merged into the main/master branch

Go back to the stages overview and clone the first stage we created by clicking the cog on the stage and selecting "Clone stage"
![Clone Stage](https://dev-to-uploads.s3.amazonaws.com/i/ifwqnsju7vnbhykc6aw0.png)

Click the cog on the newly cloned stage and click "configure". Rename it to "Deploy" and we want this stage to be triggered by a new commit into the master/main branch and not on a new pull request. On the "Input" tab, check the box "When a commit is pushed"
![Deploy Stage Trigger](https://dev-to-uploads.s3.amazonaws.com/i/i764ib2a9as1lf5k5xaq.png)

On the "Jobs" section, add a new "Deploy" job and fill out the details for your project. You want to make sure the deployer type is Cloud Foundry (you can choose Kubernetes if you wish but the setup will be different). You will also need to create a new API key for the integration to work (shown below)
![Deploy Setup 1](https://dev-to-uploads.s3.amazonaws.com/i/pp69kufzetbi4oaqca6i.png)

![Create API](https://dev-to-uploads.s3.amazonaws.com/i/zlxmjfr2mqqr6dwy7hg8.png)

Make sure the region selected is the same as your toolchain and the application name is the same as your pipeline. The script is very simple and should be pre-populated for you. Once completed, save and exit
![Deploy Setup 2](https://dev-to-uploads.s3.amazonaws.com/i/3u8oyt40ribsmecwjl40.png)

Your toolchain is now set up!

Time for a code change...
First, within your project, checkout a new local feature branch using the following command in a terminal
`git checkout -b test-new-toolchain`

Next, change the test file `main_test.go`
![Test Code Change](https://dev-to-uploads.s3.amazonaws.com/i/2pjqynwgcgz82y73jv8n.png)

And then change the corresponding code in `main.go` and push the code up to the new remote branch using the commands seen below
![Main Code Change](https://dev-to-uploads.s3.amazonaws.com/i/y2f4vepjeq4dnvxww6ik.png)

Now you can open a pull request in GitHub. Ensure the pull request is from the new branch you have just created, give it a meaningful title and description. Then click "Create pull request"
![Create Pull Request](https://dev-to-uploads.s3.amazonaws.com/i/h0rnwj4xokcch438gjcg.png)

Once open, you will see the IBM Cloud toolchain integration. If you check the toolchain stages you will see it running the first stage you called "Test Stage"
![Open Pull Request](https://dev-to-uploads.s3.amazonaws.com/i/zt9biguxx7gxf5ktog9x.png)

Once the test stage has finished, you will be able to merge the pull request. Go ahead and merge it. Then check the toolchain and you will see
![Stages Running](https://dev-to-uploads.s3.amazonaws.com/i/caomg3q7pjl3a4p3atk0.png)

Once the build stage has finished, click on the link "View logs and history". Scroll to the bottom and you will see the result of the build and where it is deployed in Cloud Foundry. Navigate to the route in a browser and you will see your app running
![Deployed Logs](https://dev-to-uploads.s3.amazonaws.com/i/8mu0ev0bs0v53yczyc4e.png)

![Running App](https://dev-to-uploads.s3.amazonaws.com/i/d4peujae1t0v9zq7pqj1.png)

If you check the merged pull request, you will see a message of successful deployment and a tag from the toolchain
![Merged Pull Request](https://dev-to-uploads.s3.amazonaws.com/i/s3j1c66votwwxu4ad0o1.png)

You've now got a CI/CD IBM Cloud Toolchain set up.. how easy was that?!

Although this blog uses a Docker image for the Golang environment, many other projects can be built using the readily available buildpacks in IBM Cloud (a list of these can be found in the stage configurator)

If you have any questions or want to see more content like this, feel free to reach out!

[GitHub](https://github.com/liamchampton)
[Twitter](https://twitter.com/liamchampton)
[Instagram](https://www.instagram.com/liamhampton/)
[LinkedIn](www.linkedin.com/in/liam-conroy-hampton)