Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Development

Prerequisites

  • Python 3.12+
  • Rust 1.85+ (rustup recommended)
  • uv

Building from Source

git clone https://github.com/VladislavAkulich/loadpilot.git
cd loadpilot

# Build Rust coordinator + agent
cd engine && cargo build --release && cd ..

# Install Python CLI in editable mode
cd cli && uv pip install -e .

The coordinator binary is picked up from engine/target/release/coordinator by the CLI at runtime when running from source.

Running Tests

The test suite is split into three layers:

LayerFilesRequiresTime
Unitall except test_integration.py, test_e2e_smoke.pynothing~1s
Integrationtest_integration.pycoordinator binary~15s parallel
E2etest_e2e_smoke.pycoordinator + agent binaries~25s parallel

Unit tests

No Rust build required:

cd cli
uv sync --extra dev
just test-unit
# or: uv run pytest tests/ -v --ignore=tests/test_e2e_smoke.py --ignore=tests/test_integration.py

Integration + E2e tests

Build both binaries first, then run all subprocess-based tests in parallel:

cd engine && cargo build --package coordinator --package agent
cd ../cli
just test-e2e
# or: uv run pytest tests/test_integration.py tests/test_e2e_smoke.py -v -n auto --timeout=120

Tests that require the coordinator binary skip automatically with a clear message if the binary is not found.

All Python tests

just test-py
# or: cd cli && uv run pytest tests/ -v

Coverage is reported automatically after every run (configured in pyproject.toml). HTML coverage report is written to cli/htmlcov/index.html.

Rust tests + coverage

cd engine

# run unit tests (coordinator + agent)
cargo test

# agent-only tests
cargo test --package agent

# unit tests + coverage summary (requires cargo-llvm-cov)
cargo cov

# unit tests + HTML coverage report → target/llvm-cov/html/index.html
cargo cov-html

The agent test suite (engine/agent/src/runner.rs) covers:

TestWhat it guards
budget_low_rps_regressionround() → 0 bug at low RPS; budget accumulation fix
budget_matches_target_rps_over_one_secondCorrect request rate for 1–100 RPS
budget_residual_boundedBudget stays in [0, 1) — no runaway accumulation
task_urls_overrides_task_default_urlPer-VUser URL from on_start reaches agents
task_urls_falls_back_to_task_url_when_absentFallback to task’s static URL
empty_vuser_configs_uses_task_urlPool-size=0 path
ramp/constant/step/spike_mode_*All load profile modes
pick_task_respects_weightsWeighted task selection
ramp_total_duration_*Duration includes ramp-up for Ramp mode

Install cargo-llvm-cov if not present:

cargo install cargo-llvm-cov
rustup component add llvm-tools-preview

CI Pipeline

CI runs on every push to main and on pull requests that touch engine/, cli/, or .github/workflows/. Changes to docs, README, or justfile do not trigger CI.

JobWhat it runsRust build
lintruff, cargo fmt, cargo clippydebug (cached)
auditcargo audit, pip-auditdebug (cached)
rustcargo llvm-cov (unit tests + coverage)debug (cached)
pythonunit tests only (no coordinator needed)none
e2eintegration + e2e tests, -n auto, --timeout=120release (cached)

The e2e job uses release binaries so tests run at production speed and timing-sensitive assertions are reliable.

Security audits

just audit
# cargo audit  — checks Rust dependencies against RustSec advisory database
# pip-audit    — checks Python dependencies against OSV/PyPI advisories

Helm Chart

A Helm chart for deploying the distributed agent stack to Kubernetes is located at cli/loadpilot/charts/loadpilot/. It is not yet published to a Helm repository but can be installed directly from the source tree.

What the chart deploys

ComponentDescription
loadpilot-natsNATS broker (single-node, LoadBalancer)
loadpilot-agentN agent pods — connect to NATS and wait for plans
loadpilot-prometheusPrometheus scraping coordinator metrics
loadpilot-grafanaGrafana with pre-provisioned LoadPilot dashboard

Local install (kind / minikube)

# Build and load agent image
docker build -f Dockerfile.agent -t loadpilot-agent:local .
kind load docker-image loadpilot-agent:local --name <cluster-name>

# Install chart
helm install loadpilot cli/loadpilot/charts/loadpilot \
  --namespace loadpilot --create-namespace \
  --set agent.image=loadpilot-agent \
  --set agent.tag=local \
  --set agent.imagePullPolicy=Never \
  --set monitoring.coordinator.scrapeTarget=""

# Forward NATS + Grafana
kubectl port-forward -n loadpilot svc/loadpilot-nats 4222:4222
kubectl port-forward -n loadpilot svc/loadpilot-grafana 3000:3000

Run a test against the in-cluster agents:

loadpilot run scenarios/checkout.py \
  --target https://api.example.com \
  --nats-url nats://127.0.0.1:4222 \
  --external-agents <replicas>

Key values

ValueDefaultDescription
agent.replicas3Number of agent pods
agent.imagePullPolicyIfNotPresentUse Always with latest tag in prod
agent.livenessProbe.enabledtrueRestart pod if agent process hangs
agent.readinessProbe.enabledtrueMark pod ready once process is up
imagePullSecrets[]Secrets for private registries
nats.service.typeLoadBalancerNodePort for bare-metal / minikube
monitoring.enabledtrueDeploy Prometheus + Grafana
monitoring.coordinator.scrapeTargethost.docker.internal:9090Set "" in cloud (coordinator not in cluster)
monitoring.grafana.adminPasswordadminStored in a Kubernetes Secret
monitoring.prometheus.persistence.enabledfalseEnable PVC for Prometheus data
monitoring.grafana.persistence.enabledfalseEnable PVC for Grafana data

Enabling persistence

helm upgrade loadpilot cli/loadpilot/charts/loadpilot \
  --set monitoring.prometheus.persistence.enabled=true \
  --set monitoring.prometheus.persistence.size=20Gi \
  --set monitoring.grafana.persistence.enabled=true

Private registry

helm install loadpilot cli/loadpilot/charts/loadpilot \
  --set "imagePullSecrets[0].name=my-registry-secret"

Verifying the deployment

After install or upgrade, run the built-in smoke tests:

helm test loadpilot --namespace loadpilot
TestWhat it checks
loadpilot-test-natsTCP connectivity to NATS on port 4222
loadpilot-test-prometheusPrometheus /-/healthy returns 200
loadpilot-test-grafanaGrafana /api/health returns 200

Installing from OCI registry

After a release tag is pushed, the chart is published automatically to ghcr.io/vladislavakul ich/charts/loadpilot:

helm install loadpilot oci://ghcr.io/vladislavakul ich/charts/loadpilot \
  --version 0.1.7 \
  --namespace loadpilot --create-namespace

Running coordinator in-cluster

The coordinator can run inside the cluster in serve mode (--serve flag). Enable it via:

helm install loadpilot cli/loadpilot/charts/loadpilot \
  --set coordinator.enabled=true \
  --set coordinator.image=loadpilot-coordinator \
  --set coordinator.tag=local \
  --set coordinator.imagePullPolicy=Never \
  --set "coordinator.serveAgents=3" \
  --set monitoring.coordinator.scrapeTarget=""

In serve mode the coordinator listens on 0.0.0.0:8080:

EndpointDescription
POST /runAccept ScenarioPlan JSON, stream ndjson metrics. Returns 409 if a test is already running.
GET /healthzReadiness probe — returns ok

By default coordinator.enabled: false because the coordinator image must be built separately from the agent image.

Benchmark

cd bench
./run.sh

Runs LoadPilot, k6, and Locust sequentially against a Rust/axum echo server in Docker and generates results/report.html. See Benchmark for full details.

Project Structure

loadpilot/
  cli/                    ← Python package (pip install loadpilot)
    loadpilot/
      cli.py              ← CLI entry point, _build_plan()
      dsl.py              ← @scenario, @task, VUser, _scenarios registry
      models.py           ← Pydantic models: ScenarioPlan, AgentMetrics, ...
      client.py           ← LoadClient (httpx wrapper for on_start)
      _bridge.py          ← MockClient (used by _build_plan to extract URLs)
      report.py           ← HTML report generator
    tests/
      _helpers.py         ← Shared fixtures: MockServer, run_coordinator, free_port
      test_models.py      ← Unit: ScenarioPlan / TaskPlan validation
      test_dsl.py         ← Unit: @scenario / @task DSL
      test_cli_plan.py    ← Unit: _build_plan() scenario selection logic
      test_bridge.py      ← Unit: MockClient / PyO3 bridge helpers
      test_client.py      ← Unit: LoadClient
      test_report.py      ← Unit: HTML report generation
      test_integration.py ← Integration: Python plan → coordinator subprocess
      test_e2e_smoke.py   ← E2e: all run modes + graceful shutdown (parallel)

  engine/                 ← Rust workspace
    coordinator/src/
      coordinator.rs      ← Main run loop, token-bucket scheduler
      python_bridge.rs    ← PyO3 bridge, VUser threads, RustClient
      metrics.rs          ← Histogram, AgentMetrics, JSON serialisation
      plan.rs             ← ScenarioPlan deserialization + validation
      distributed.rs      ← NATS integration, agent coordination
      broker.rs           ← Embedded NATS broker
    agent/                ← Standalone agent binary (for remote machines)

  bench/                  ← Benchmark suite
    scenarios/            ← LoadPilot / k6 / Locust scenario files
    run.sh                ← Orchestration script
    report.py             ← HTML report generator

  docs/                   ← Documentation