Lean multi-agent environments¶
Use this guide when several agents (or developers) need isolated Pulp stacks on one host to run scoped functional tests against a live API without sharing mutable state.
Model¶
Each agent environment is a full Pulp stack (API, content app, worker, Postgres, Redis) in its own compose project with its own volumes and API port. Agents do not share Postgres or a writable Python environment, so each can migrate, switch git revisions, and install or remove dependencies independently.
What is shared safely:
- The immutable base image (
localhost/oci_env/pulp:base) after a single host build - Optional shared fixtures (
local_fixtures) if every agent points at the same fixtures URL
The lean profile keeps each stack small: one worker and CPU/memory caps.
Each agent owns a single directory under .compiled/:
.compiled/agent_<id>/
compose.env # agent env file
plugin_volumes_compose.yaml # bind mounts for plugin checkouts
… # compose files written here on `up` (parse_profiles)
SRC_DIR for the agent is that directory. Plugin checkouts are always bind-mounted into
/src/<plugin> via the volume overlay (never by sharing the host SRC_DIR directly).
Prerequisites¶
- Install the
oci-envclient (pip install -e clientfrom this repo). - Build the base image once on the host:
oci-env compose build
-
Agent environments always use podman. They do not copy settings from the host
compose.env.PULP_SECRET_KEYdefaults todummy; pass other Pulp/Django/profile settings with--env KEY=VALUE. -
Have plugin checkouts under a common host directory (default: parent of
oci_env), for example:
~/devel/
├── oci_env
├── pulpcore # includes pulp_file
└── pulp_rpm
pulp_file ships inside the pulpcore checkout — do not list it in --plugins.
Typical agent loop¶
# 1. Create a lean env (resolves plugins under the default host SRC_DIR)
oci-env agent create pr-123 --plugins pulpcore
# Or point at an AI-tool / git worktree for plugins under test
oci-env agent create pr-123 \
--plugins pulpcore=~/.cursor/worktrees/pulpcore/my-wt
# 2. Start and wait for the live API
oci-env agent up pr-123
# 3. Generate/install clients for plugins under test
oci-env agent generate-client pr-123
# 4. Run narrowly scoped functional tests against that agent's API
oci-env agent test pr-123 -p pulpcore -- -k test_crud_repos
# 5. Tear down containers, volumes, and the agent compiled directory
oci-env agent destroy pr-123
List allocated agents:
oci-env agent ls
Create options¶
| Flag | Purpose |
|---|---|
--plugins pulpcore[=PATH]:… |
Colon-separated PLUGIN[=PATH] list (DEV_SOURCE_PATH; default pulpcore) |
--host-src-dir PATH |
Parent used when a plugin has no =PATH (default: host SRC_DIR or parent of oci_env) |
--port 5105 |
Pin API_PORT (default: allocate from 5100–5199) |
--env KEY=VALUE |
Set any other agent env var (repeatable), e.g. COMPOSE_PROFILE, LEAN_MEM_LIMIT |
Defaults baked into the agent env file: COMPOSE_PROFILE=lean, COMPOSE_PROJECT_NAME=agent_<id>,
COMPOSE_BINARY=podman, API_HOST=localhost, API_PROTOCOL=http, PULP_SECRET_KEY=dummy,
SRC_DIR=.compiled/agent_<id>. Lean profile defaults still supply PULP_WORKERS=1 and resource
caps unless you override them with --env.
Plugin paths¶
--plugins accepts an optional path per plugin. Omit the path to resolve
<host-src-dir>/<plugin>:
# Both plugins from the default host SRC_DIR (e.g. ~/devel/pulpcore, ~/devel/pulp_rpm)
oci-env agent create pr-123 --plugins pulpcore:pulp_rpm
# Explicit worktree for pulpcore; pulp_rpm from host SRC_DIR
oci-env agent create pr-123 \
--plugins pulpcore=~/.cursor/worktrees/pulpcore/my-wt:pulp_rpm
create always writes a compose volume overlay that bind-mounts each real checkout at
/src/<plugin> (nested under the base {SRC_DIR}:/src mount).
Destroy removes the entire .compiled/agent_<id>/ directory; it never deletes the plugin
checkouts that were bind-mounted.
Parallelism tips¶
- Build images once; agent
upshould not rebuild unless you pass--build. - Keep
DEV_SOURCE_PATHlimited to plugins under test — that dominates startup cost after workers. - Prefer scoped pytest paths/markers; full suites multiply host load.
- Destroy agents when finished so ports and volumes are reclaimed.
- Default credentials are
admin/passwordunless overridden with--env.
How tests hit the live API¶
oci-env agent test loads that agent's env file (API_PORT, credentials, SRC_DIR, project name)
and runs the existing oci-env test path inside that container. Functional tests therefore
exercise the agent's live API, not a shared default on port 5001.
The functional test runner prints the API URL and auth user before invoking pytest.
Manual env files¶
Agent commands write env files to .compiled/agent_<id>/compose.env. You can still use the generic
multi-env flow with -e if you prefer hand-written files; see
Run multiple environments.
When not to use lean agents¶
Interactive work that needs UI, Kafka, OpenTelemetry, MinIO, or other sidecars should keep using
the normal profiles. Add those profiles with --env COMPOSE_PROFILE=lean:… only when required —
they increase resource use and reduce how many agents fit on one machine.