funk-cli

Turbodrive Terraform


Keywords
funk
License
MIT
Install
pip install funk-cli==0.1.1

Documentation

terrafunk

cloud build status

Assemble a Terraform bot net on GCP.

Requirements

  • Google Cloud
  • Bitbucket Cloud

Motivation

I was looking for the best way to run Terraform on GCP. I wanted to use on-demand compute resources because Terraform does not always need to be running. I wanted to leverage IAM, only giving each Terraform workspace the permissions it needs. I wanted to avoid handling credentials and instead have Terraform inherit implicit permissions. I wanted to run Terraform plans and applies in parallel. I wanted tight integration with pull requests. And I didn't want to have to do a lot of work to set this up...

Terrafunk is the result. Its core component is funk which basically wraps the terraform command. It accepts pull request comments as commands to run plan and apply, and in turn it forwards plan and apply output as pull request comments. It doesn't do much more than that other than govern that behaviour between pull requests and terraform.

Then there is another component: the webhook. This is necessary in order to relay pull request events from a repository. It's designed to be deployed as a Cloud Function but there's no reason why it can't be deployed on any other compute resource.

Cloud Build is then used for the executor: it's responsible for actually running the funk commands. Each sequence of commands (the typical init, validate, plan, and apply steps) constitutes a 'build'. The steps can be customised using a cloudbuild.yaml file.

There is also the invoker, responsible for triggering a build. Again, this is intended to be deployed as a Cloud Function, either deployed together with webhook, or separately.

Finally there is the optional use of Cloud Pub/Sub. A Pub/Sub topic is necessary if you deploy more than one invoker, which you'll want to do if you want to run builds in different projects.

At a minimum the only thing you need to deploy is a Cloud Function. That can run both the webhook and the invoker, and the invoker will take care of creating builds in Cloud Build.

Design

Terrafunk is composed of several components:

  • The Webhook
  • The Invoker
  • Cloud Build
  • The Funk runner
  • A Cloud Pub/Sub topic (optional)

TODO: diagram

Diagram

Comparison to Atlantis

Atlantis is a single component, designed to be deployed singularly into the cloud. Terrafunk is a composition of several components deployed in a distributed fashion.

Atlantis has no dependencies, whereas Terrafunk leverages cloud services to perform the heavy lifting.

Atlantis runs with a single set of credentials for all workspaces, while with Terrafunk multiple sets of credentials may well be in use, only running each workspace with the permissions it needs.

Atlantis only persists state in pull requests. Because Terrafunk uses Cloud Build, build information such as logs are persisted and are available for querying and streaming in real-time.

Installation

Set some environment variables first:

  • GOOGLE_CLOUD_PROJECT: the project in which to deploy the function
  • TOPIC_NAME: the Pub/Sub topic to which to forward the POST payloads
  • TOPIC_PROJECT: (optional) the project hosting the Pub/Sub topic; the default is the same project as the function
  • IP_WHITELIST: (optional) comma delimited list of IP ranges from which the HTTP POST must originate, e.g. 1.2.3.4/32,1.2.3.5/24; defaults to allowing all requests

Create Topic

Create a Cloud Pub/Sub topic:

gcloud pubsub topics create $TOPIC_NAME

Configure IAM

Create a new service account for use by the Cloud Function:

gcloud iam service-accounts create webhook

Grant permissions to publish to the topic:

gcloud pubsub topics add-iam-policy-binding $TOPIC_NAME \
    --member "serviceAccount:webhook@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com" \
    --role roles/pubsub.publisher \
    --project $TOPIC_PROJECT

Deploy

gcloud beta functions deploy webhook \
     --source . \
     --runtime python37 \
     --entry-point pubsub_webhook \
     --service-account webhook@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com \
     --set-env-vars TOPIC_NAME=${TOPIC_NAME},TOPIC_PROJECT=${TOPIC_PROJECT},IP_WHITELIST=${IP_WHITELIST} \
     --trigger-http \
     --allow-unauthenticated

Test

Run an integration test against a deployed function:

make integration

Ensure you've set the environment variable TOPIC_NAME first.

Ideas