Hookshot: serverless webhook cronjob -- 07 Nov 2018

I’m a big fan of DockerHub’s Automated Builds: they let you tie a git repo to a DockerHub image, so that updating the repo kicks off a fresh build of the image. It further lets you define Repository Links, so that if a parent image is updated, child images rebuild themselves.

In my case, I maintain a base image with the OS and package updates, and then a tree of images based on that. I want to rebuild that image every so often to get updated packages, but that feature is missing from DockerHub. So I wrote an AWS Lambda to handle it.

How it works

DockerHub doesn’t have a way to schedule rebuilds of an image, but it does support webhooks, so that posting to a unique URL triggers a rebuild. AWS Lambdas support being triggered on a schedule by a CloudWatch Event Rule, so they can provide the cron-like behavior I needed.

The Hookshot lambda

I wrote the Lambda payload in Golang, and it ended up being pretty easy. The meat of the code just has to take a list of URLs and hit them. To get the list, I pass in an S3 bucket and key as environment variables to the Lambda, and I reused a helper library I’d already written (which probably deserves a post of its own) to fetch the YAML config from that S3 path:

The full source for the repo can be found here.

I made a couple decisions during the process to simplify the code:

  • During config load, I create an http.Request object from each URL. That way, I don’t have to parse in the URL every time the lambda runs.
  • I don’t do error collection or retries. Because it’s easy to deploy multiple copies of the lambda, I didn’t complicate the code by having it keep trying down the list if one URL fails.

Deploying it

I’m a big believer in infra-as-code (more specifically, I’m a believer in automating infrastructure and in version-controling infrastructure state, which tends to be easiest via infra-as-code), so I deployed this via Terraform. I currently use a mono-repo for my AWS account, and the module for the Hookshot lambda lives here. It creates the Lambda, the Cloudwatch Event Rule, and the associated IAM entities. It also spins up a config S3 bucket for placing the config.

Configuring it

The config lives in a separate private GitHub repo, since it includes the unique DockerHub webhook URL. But since the Terraform code has created an S3 bucket and an AWS IAM keypair, that repo’s CI build pushes a YAML config file directly into S3.

The result

Once an hour, the Lambda fires and causes DockerHub to rebuild my image. Success!