Building A Custom Brigade Gateway in 5 Minutes

Apr 23 2018

Brigade gateways trigger new events in the Brigade system. While the included GitHub and container registry hooks are useful, the Brigade system is designed to make it easy for you to build your own. In this post, I show the quickest way to create a Brigade gateway using Node.js. How quick? We should be able to have it running in about five minutes.

Prerequisites

You'll need Brigade installed and configured, and you will also need Draft installed and configured. Make sure Draft is pointed to the same cluster where Brigade is installed.

If you are planning to build a more complex gateway, you might also want Node.js installed locally.

Getting Started

Draft provides a way of bootstrapping a new application with a starter pack. Starter packs can contain things like Helm charts and Dockerfiles. But they can also include code. To make it easy and fast to create Brigade tooling, I have created a repository of starter packs for Brigade development. Here's how to add it to Draft:

$ draft pack-repo add https://github.com/technosophos/draft-brigade

This will give you a few new starter packs, but the one we care about is called brigade-gateway. Let's use this to create a new gateway:

$ mkdir mygateway
$ cd mygateway
$ draft create -p brigade-gateway

Once that command completes, our mygateway directory should contain a number of files, including a Dockerfile, a charts/ directory, a package.json file, and an index.js file.

Starting the Gateway

The default pack already has everything we need to start up a new gateway. So we can jump straight to running it without even writing a line of code.

From inside mygateway, simply run this:

$ draft up

That will build the new gateway for you and deploy it into your Kubernetes cluster. Once it's running, the easiest way to connect to it for testing is by running another Draft command:

$ draft connect

This will output a URL (something like localhost:8182), and will stay running in the foreground. As long as draft connect is running, you will have an open tunnel to your app, even if your app is running on a remote Kubernetes cluster.

We can now test out this cluster with Curl or from a web browser.

Testing Out the Gateway

At this point, the gateway is running and we have a tunnel connected to it. Using either curl or a web browser, we can now connect to it. But what URL do we use?

If we take a quick peak at the index.js file, we will see two existing routes:

app.post("/v1/webhook/:hook/:project", (req, res) => {
  // ...
})

// ...

app.get("/healthz", (req, res)=> {
    res.send("OK");
})

The second one registers a basic health check command, and is easy for us to test:

$ curl localhost:8182/healthz
OK

(Note that you should replace the port, 8182, with whatever port draft connect gave you.)

If the above command returns OK, then your gateway is up and running.

The other route is a functioning gateway callback. Before you can execute it, you will need the ID of an existing Brigade project. If you don't already have a Brigade project, the official documentation describes how to create one.

If you have the brig CLI installed, you can list your projects with brig project list.

Assuming we have a brigade project whose ID is brigade-12345678909876, we can trigger the webhook like this:

$ curl -XPOST http://localhost:8182/v1/webhook/myevent/brigade-12345678909876

(Again, the port, 8182, should be replaced with the one draft connect gave you.)

It should return a response like this:

{"status": "accepted"}

More importantly, though, it should have triggered a new Brigade build on your project. (brig build list is a quick way to check that).

If we look at the route declared in the index.js, we can see that it has two placeholders:

/v1/webhook/:hook/:project

We already saw that :project is replaced by the project ID. In the example above, we replaced :hook with myevent. This is the name of the event that your webhook emits. So to handle this webhook, your brigade.js would look something like this:

const { events } = require("brigadier");

events.on("myevent", (e, p) => {
  console.log("Got event!");
});

Note that in general, it's not particularly prudent to make the event name a URL variable. Typically you should hard code the event name into your script, lest a bad actor attempted to execute other events via this webhook.

So let's write a custom hook to demonstrate this.

Writing Your Own Webhook

Let's replace the built-in hook with one that has a fixed event name. Edit index.js and change the existing hook to look like this:

app.post("/v1/webhook/myevent/:project", (req, res) => {
    // This is now hard-coded
    const eventName = "myevent"
    // We get the project from the URL, still
    const project = req.params.project;
    // If the client uploads any data, it comes in here
    // as the body. It is treated as string data.
    const payload = req.body

    // This is how we create an event in Brigade.
    brigEvent = new Event(namespace);
    brigEvent.create(eventName, project, payload).then(() => {
        // This is the response sent back to the client.
        res.json({"status": "yay"});
    }).catch((e) => {
        // This is what happens if we hit an error when
        // sending the event.
        console.error(e);
        res.sendStatus(500);
    });
});

Now we have a custom webhook. This time, it has a fixed event name. It will always emit the event myevent, and no longer extracts that information from the URL.

Break out of draft connect, and re-run draft up && draft connect. (You might get a new port number from draft connect).

Now once again we can re-run that same curl command:

$ curl -XPOST http://localhost:8182/v1/webhook/myevent/brigade-12345678909876

Because of our change above, this time it should return a different result:

{"status": "yay"}

And once again, running brig build list should show you a new build.

Conclusion

From here, you can begin modifying your new gateway to suit your own needs. You can add authentication, modify the payload, supply a custom brigade.js script, or whatever. Take a look at brigade-event.js to see all that you can configure on the event object.

When you are done developing, the chart and Docker image created for you by Draft can be used to deploy your application into staging or production environments.

Keep in mind that you are not limited to webhook gateways. The only requirement from the Brigade side is that you trigger a Brigade event. One could, for example, replace the webhook portion of this code with something that watches an event queue, queries a database, listens for email, or whatever you desire.