Skip to content

mermoldy/tofu-http-backend

Repository files navigation

Tofu HTTP Backend

Static Badge

Implementation of the OpenTofu HTTP backend as part of the Scalr home assignment: https://github.com/Scalr/home-assignment-tofu-http-backend

Design

The application core is built using the FastAPI asyncio server with the Uvicorn ASGI server. Logging is handled using the structlog protocol, and testing is implemented with pytest framework. MinIO is used as an example for both storage and the lock backend.

The application layout follows the Hexagonal Architecture pattern. The configuration is located in config.toml and also supports loading properties from shell variables prefixed with TOFU_HTTP_<UPPERCASE_KEY>.

Setup

  • Install uv and restart your shell:
$~curl -LsSf https://astral.sh/uv/install.sh | sh
  • Sync the dependencies:
$~ uv sync --all-group
$~ . .venv/bin/activate

Development

Run the development HTTP server with hot-reload:

$~ ./cli.py dev

The application entrypoint is placed at src/cmd.py module. By default, the application runs on port 8000.

Run linters:

$~ ruff check src/
$~ mypy src/

Build

Use uv pip compile pyproject.toml to compile the production dependencies.

Then, run docker build . -t tofu-http-server:latest to build the Docker image, and docker run -it tofu-http-server:latest to start the container.

Testing

$~ pytest

see tests and examples for more.

API Documentation

The interactive API documentation is built using FastAPI and is available at http://0.0.0.0:8000/docs. OpenAPI schema is available at http://0.0.0.0:8000/openapi.json

Configuration Parameters

The table below outlines the configuration options for the application.

Parameter Type Default Value Description
log_level str "info" The log level.
username str | None None The username for HTTP basic authentication.
password str | None None The password for HTTP basic authentication.
storage_backend "minio" "minio" The remote storage backend used for storing state files.
lock_backend "minio" "minio" The remote storage backend used for state file locking.
minio_host str "play.min.io" The MinIO host.
minio_bucket str "e1cc89bb-b9f5-4b29-8163-c3e8da21bbba" The bucket in MinIO storage.
minio_access_key str Required The MinIO access key.
minio_secret_key str Required The MinIO private secret key.

All configuration properties can also be loaded from environment variables by using the TOFU_HTTP_<UPPERCASE_KEY> prefix.

For example:

export TOFU_HTTP_MINIO_HOST="my-minio.example.com"

Usage example

Add the following http backend configuration to your main.tf file:

terraform {
  backend "http" {
    address        = "http://localhost:8000/state/project/1"
    lock_address   = "http://localhost:8000/state/lock/project/1"
    unlock_address = "http://localhost:8000/state/unlock/project/1"
    lock_method    = "POST"
    unlock_method  = "POST"
    username       = "testscalr"
    password       = "testscalr"
  }
}

Run the server using Docker with the following command, using the pre-built image:

$~ docker run -it -p 8000:8000 -e TOFU_HTTP_USERNAME=testscalr -e TOFU_HTTP_PASSWORD=testscalr mermoldy/tofu-http-server:latest

Implementation notices

MinIO Lock Backend

Since MinIO does not natively support locking, the lock backend implements a simple mechanism by placing a .lock file with metadata in storage alongside the main blob file. However, this approach may not be sufficient for high-concurrency applications, as it can lead to collisions or race conditions. It is provided solely as an example.

Force-Unlock Behavior

The force-unlock implementation ignores the lock ID because Terraform lacks proper force-unlock functionality for the HTTP backend. See this issue for more details.

As a result, when you run tofu force-unlock XXX, Tofu/Terraform does not pass the lock ID (XXX) to the HTTP backend, causing the backend to skip lock ID verification.

About

Implementation of Scalr home assignment

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published