More complex pipelines with Brigade: pull requests and tests in the feature branch

Štěpán Vraný
Nov 18, 2018 · 6 min read

Last week I was blogging about simple stuff with Brigade event driven automation platform. Basically we did nothing similar to real-life challenges we (Dev, Ops or DevOps) have to face every single day.

Today I’m gonna show you how to test the code from the submitted Pull Requests.

It’s fair to mention that I did not invented anything from the code below — I’ve just closely followed official documentation and examples provided by the community.

Github setup

Requirements for the OAuth key remain same as in the previous article, however there’s another configuration step we have to execute so the automation machine can do all the stuff properly — webhooks.

Webhooks will be triggering scripted events just in time, it means something happens right after the Pull Request is submitted to the Github etc.

First you need to get the IP address of your Github gateway. If you were following official tutorial — you’ll just need to inspect Kubernetes services since the default installation creates a LoadBalancer service with public IPv4 address. Otherwise I don’t have to give you any advices because you’re most likely sure what you’re doing 😀

kubectl get svc brigade-brigade-github-gw
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
brigade-brigade-github-gw LoadBalancer 10.51.240.54 35.246.215.26 7744:32577/TCP 4m

Now we just need to add webhook url to the Github project:

And that’s it, we’re done with Github setup. Let’s code some stuff.

Release the Kraken: Minimal Golang micro service

Before we start with the main pipeline code — I have to scavenge Web a bit and find some nice “Hello World!” web app written in Golang (because I’m obsessed with Golang). It was pretty easy, here it is:

package main

import (
"fmt"
"log"
"net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello world!")
}

func main() {
http.HandleFunc("/", handler)
log.Fatal(http.ListenAndServe(":80", nil))
}

Also we have some dummy (it will always pass) tests:

package main_testimport (
"testing"
"github.com/stretchr/testify/assert"
)
func Test_Something(t *testing.T) {
assert.Equal(t, "string", "string")
}

That’s enough, we can continue with my favorite Javascript now 😀

Simple pull request handler

Let’s say we want to execute go test and golint when pull request comes, also we’d like to notify Github about the build result.

Basic pull request handler looks almost the same as the exec handler we’ve made in the previous part:

const { events, Job, Group } = require("brigadier")events.on("pull_request", () => {
console.log("==> handling an 'pull_request' event")
})

Create brigade.js file, commit changes and create a new Pull request:

After submitting you can check logs for the Brigade’s Github gateway:

[GIN] 2018/11/17 - 09:00:45 | 200 |  107.924568ms |       10.48.1.1 |  POST     /events/github

Apparently something happened. Let’s check Kashti dashboard and see what happened there. In the console output we can see that Brigade did exactly what we wanted from it.

Unfortunately it’s still not the state we want, we’re still far away from functional pipeline.

Test job for Golang app

If we want to really do some stuff when Pull request event comes — we have to specify a new containerized job in the event’s context. Here’s short sample I’ve “invented” after a hour of trial&fail madness.

const { events, Job, Group } = require("brigadier")events.on("pull_request", () => {
console.log("==> handling an 'pull_request' event");
var testJob = new Job("ci-test", "golang:1.11.2")
testJob.tasks = [
'go get -u github.com/golang/dep/cmd/dep',
'mkdir -p /go/src/github.com/vranystepan/',
'ln -sf /src /go/src/github.com/vranystepan/azure-brigade-demo',
'cd /go/src/github.com/vranystepan/azure-brigade-demo',
'${GOPATH}/bin/dep ensure',
'go test -v'
]
testJob.run();
})

It basically just installs Golang dependency manager Dep, ensures all dependencies are in place and run the test. Please note that we’re gonna run test in verbose mode so we’ll see all testing functions in the output. It’s useful since we want to test whether Pull request events refer to the correct state of the repository.

Let’s submit a new Pull Request, in the branch which is supposed to be merged to the master I’ve made one change: file main_test.go has two testing functions instead of one.

After submitting of the Pull Request we should see detailed output about the executed test functions. Let’s check the dashboard.

In the output we can see that go test actually ran two testing functions, Test_Something and Test_SomethingElse. It means that Brigade behaves exactly as we want — it pulls code from the feature branch so we can test the new features prior to actual merge.

Notify Github about the build status

As you could have seen — everything in Brigade is just container. So notification handler will be container as well. On the Github I’ve found one project for this purpose, the setup is pretty straightforward however I had to redesign the code a bit so I don’t get lost in the Promise chains 😂

And I’m really sorry if you’re a Javascript developer — following piece of code will hurt you so much that I can’t even describe such level of pain 😂 Sorry!

const { events, Job, Group } = require("brigadier")const START_MESSAGE = 'ci-test in progress';
const FAILURE_MESSAGE = 'ci-test failed';
const SUCCESS_MESSAGE = 'successfully finished ci-test';
function runTestJob() {
var j = new Job("ci-test", "golang:1.11.2")
j.tasks = [
'go get -u github.com/golang/dep/cmd/dep',
'mkdir -p /go/src/github.com/vranystepan/',
'ln -sf /src /go/src/github.com/vranystepan/azure-brigade-demo',
'cd /go/src/github.com/vranystepan/azure-brigade-demo',
'${GOPATH}/bin/dep ensure',
'go test -v'
]
return j.run()
}
function notifyGithub(status, description, e, p) {
var j = new Job("gh-" + status, "technosophos/github-notify:latest");
j.env = {
GH_REPO: p.repo.name,
GH_STATE: status,
GH_DESCRIPTION: description,
GH_CONTEXT: "brigade",
GH_TOKEN: p.repo.token,
GH_COMMIT: e.revision.commit
};
return j.run();
}
function buildSuccessHandler(result, description, e, p) {
notifyGithub("success", description, e, p);
}
function buildFailedHandler(error, description, e, p) {
notifyGithub("failure", description, e, p);
}
function buildInProgressHandler(description, e, p) {
notifyGithub("pending", description, e, p);
}
events.on("pull_request", (e, p) => {
console.log("==> handling an 'pull_request' event");
buildInProgressHandler(START_MESSAGE, e, p);
runTestJob().then(function(result){
buildSuccessHandler(result, SUCCESS_MESSAGE, e, p);
}).catch(function(error){
buildFailedHandler(error, FAILURE_MESSAGE, e, p);
});
})

After submitting a new Pull request we can monitor the new scenario from the dashboard. There are brand new jobs— gh-pending and gh-success.

Let’s check the Pull Request details, have Github received the message about the successful build?

Yes, yes, yes! It works! It’s like to see baby’s first steps! Kinda.

Conclusion

Today we’ve finally come through some real life scenario cause this is definitely something you might know from the other CI/CD platforms (Travis, CircleCI or Jenkins). Next time we’ll try to setup some basic CD pipeline which will build Docker image and push it to the image repository.

Do you have any questions or comments? Leave a comment here! I’m looking forward to discuss Brigade stuff with you!

Štěpán Vraný

Written by

Freelance Automation / Cloud Engineer. Technology junkie and father of two children.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade