T

Testcontainers

Fixtures & lifecycle available

Spin ephemeral service containers up for a run and down after.

Install
$ loadr plugin install testcontainers
examples/plugins/testcontainers.yaml
# Ephemeral test fixtures, stood up by the `loadr-plugin-testcontainers` native
# SERVICE plugin.
#
# A service plugin has a start/stop lifecycle: loadr calls `start(config)` once
# before the run and `stop()` once after. On start this plugin talks to the
# Docker Engine directly over its HTTP API on /var/run/docker.sock (no Docker
# client library, no CLI shell-out, no testcontainers SDK — hand-rolled HTTP/1.1
# over a Unix socket) and, for each declared container:
#
#   1. POST /containers/create  (pulling the image first if it isn't local)
#   2. POST /containers/{id}/start
#   3. reads the ephemeral host port Docker assigned
#   4. blocks until the `wait` condition is satisfied
#
# Each published port is exported as an env var named
#   LOADR_TC_<IMAGE>_<CONTAINER_PORT>
# (image basename upper-cased, tag stripped), so requests reference the
# host-assigned ephemeral port through normal ${env.…} interpolation. On stop
# the plugin removes every container it created; teardown is idempotent and a
# start that fails partway rolls back what it already created.
#
# Build + install the plugin, then run:
#   cargo build -p loadr-plugin-testcontainers --release
#   mkdir -p dist && cp plugins/loadr-plugin-testcontainers/plugin.toml dist/ \
#     && cp target/release/libloadr_plugin_testcontainers.so dist/
#   loadr plugin install dist
#   loadr run examples/plugins/testcontainers.yaml
#
# Requires a running Docker Engine reachable on the local socket (or $DOCKER_HOST).
name: testcontainers
description: Stand up throwaway Postgres + Redis fixtures for the run, then tear them down

plugins:
  # Resolve `testcontainers` by name from the plugins dir (after
  # `loadr plugin install`). To run straight from a build tree instead, set:
  #   path: target/release/libloadr_plugin_testcontainers.so
  - name: testcontainers
    config:
      # How long start() waits for ALL containers to become ready before it
      # rolls everything back and fails the run.
      startup_timeout: 60s
      containers:
        - image: postgres:16
          # Publish 5432 to an ephemeral host port -> LOADR_TC_POSTGRES_5432.
          port: 5432
          # Block until Postgres actually accepts connections, not just "running".
          wait: "log:database system is ready to accept connections"
          env:
            POSTGRES_PASSWORD: test
        - image: redis:7-alpine
          port: 6379
          wait: "log:Ready to accept connections"

scenarios:
  main:
    executor: constant-vus
    vus: 20
    duration: 30s
    flow:
      # Both requests reach the containers through the ephemeral host ports the
      # plugin exported as LOADR_TC_* env vars — never a hard-coded host port,
      # so concurrent runs on one machine never clash.
      - request:
          name: query
          protocol: postgres
          url: postgres://postgres:test@127.0.0.1:${env.LOADR_TC_POSTGRES_5432}/postgres
          plugin:
            query: "SELECT 1"
      - request:
          name: cache ping
          url: redis://127.0.0.1:${env.LOADR_TC_REDIS_6379}
          plugin:
            command: ["PING"]

thresholds:
  checks: [ "rate>0.99" ]

A real run: install from the signed index, then watch the plugin work.

A runtime plugin, never in the binary

Installing pulls a per-platform driver from the signed index, verifies its SHA-256 and checks its ABI before it ever loads. Remove it any time with loadr plugin remove testcontainers.