Cattle: per-tenant SaaS (Atlantis)

Tags: Substrate · Modeling · Automation · Self-host Substrate: Atlantis

The Atlantis-substrate variant of the Terraform Cloud demo. Same cattle scenario — a cluster fleet with capacity ceilings, tenants placed via least-loaded. The difference: the customer runs self-hosted Atlantis as their Terraform runner. Terrantula opens a PR; the customer's existing Atlantis picks it up via its webhook, plans, and applies on comment.

What you'll see

OnboardTenant runs placement, creates a Tenant entity in provisioning, then the pull-request trigger opens a PR that commits four things:

  • tenants/acme/main.tf — a module instantiation calling the shared terraform/ module.
  • tenants/acme/backend.tf — a per-tenant S3 backend (isolated state file).
  • tenants/acme/versions.tf — pinned provider versions.
  • atlantis.yaml — patched via a yaml-key operation to add a projects.tenant-acme entry.

Atlantis sees the PR through its existing GitHub webhook, runs terraform plan, and posts the output as a PR comment. A reviewer comments atlantis apply (or merges if auto-apply is configured). On merge, Terrantula's webhook transitions the tenant to active and creates the runs_on relationship. Atlantis applies; Terrantula never runs terraform apply.

Module-per-tenant is the natural shape here: Atlantis is project-oriented (one project = one TF directory = one state file), so each tenant maps to one Atlantis project.

Try it

The bundled run-demo.sh exercises placement + lifecycle against a local stack. Without a real GitHub token the PR step fails with a 401 (expected) — every cattle primitive still validates.

cd examples/cattle-saas-tenants-atlantis
./run-demo.sh

With a real token it opens a PR your Atlantis can pick up:

export GITHUB_TOKEN=ghp_...
export GITHUB_TARGET_REPO=my-org/my-terraform-repo
./run-demo.sh

Or run the steps manually:

terrantula apply --file blueprint.yaml
terrantula apply --file cluster-seed.yaml
terrantula cells add-member --name prod-clusters --entity-id <cluster-id-a>
terrantula secrets set-value --name github-token --value "$GITHUB_TOKEN"
terrantula actions trigger OnboardTenant \
  --param customer_id=acme \
  --param plan_tier=premium \
  --param region_preference=us-east-1 \
  --param target_repo=my-org/my-terraform-repo

The blueprint also ships a typed atlantis api-dispatch action (OnboardTenantAtlantisDispatch) that calls Atlantis's /api/plan endpoint directly (requires Atlantis ≥ 0.28 with API access). Apply is still triggered by comment or auto-apply — Terrantula never applies.

Key files

FileWhat it is
blueprint.yamlThe Terrantula schema plus both OnboardTenant actions (PR trigger + Atlantis api-dispatch).
cluster-seed.yamlTwo TenantCluster entities for the demo's cell.
atlantis.yamlA drop-in Atlantis config for the customer's Terraform repo.
run-demo.shThe end-to-end demo runner.
terraform/main.tfThe shared module each tenant directory instantiates.

View on GitHub

examples/cattle-saas-tenants-atlantis