nemo_gym.sandbox.adapters.docker_compose
nemo_gym.sandbox.adapters.docker_compose
Run Compose services through the sandbox API.
Input YAML is resolved upstream; unsupported runtime semantics are rejected
before provisioning services. Service fields get their meaning from
nemo_gym.sandbox.compose_config.check_compose_fields: deploy.resources
sizes each service’s sandbox, restart and pull_policy are recorded (and
the pull policy applied where the provider can), networks scope which
services see each other by name, extra_hosts add hostnames, post_start
hooks run once the service is up, and tmpfs, ulimits, sysctls and
security_opt need a provider that implements
SupportsSandboxServiceOptions. A service may run on its own provider
(service_providers), the way a GPU sidecar lands on a GPU deployment, and
then resolves its external volumes through that provider’s own mapping
(service_volume_sources).
Module Contents
Classes
Functions
Data
API
Start services from a Compose YAML file and own their sandbox lifecycle.
Deployment settings stay in the provider and optional service_specs.
Compose fields override the corresponding spec fields, except environment: a spec’s env is
the caller’s host-resolved override for that service and wins over the service’s Compose
environment (see _prepare). service_providers names another provider (instance or
single-key config) for particular services; every provider must reach the others’ sandboxes on
the network. platform is the one the deployment runs, which a service’s platform must
match. restart_policies and pull_policies record what each service declared.
x-sandbox.uploads entries (files or folders copied into a service once it starts) are read in
this process, so every source must lie under upload_root, the one folder the caller vouches for
(the task folder the planner validated against); a document with uploads and no root is refused.
x-sandbox.logs entries name the paths of Harbor’s /logs tree a service bind-mounted: each
source is created, world-writable, before the service’s command, hooks and healthcheck, and a target
elsewhere is linked to it, the way Docker creates both bind targets at container create.
volume_sources maps the external volume names and foreign bind paths of the collection’s provider to
its shared storage; service_volume_sources gives a service on its own provider that provider’s
mapping, which its volumes resolve through instead. service_setup names, per service, a coroutine
function the group awaits with that service’s sandbox once it is created and its runtime is configured,
before its log links, uploads, command, hooks and healthcheck. Named/external volumes are already mounted
when the sandbox is created, so setup (including Harbor’s overlay Dockerfile RUN lines) can write to
shared storage. For a service with setup, its working directory is applied afterwards:
setup may create that directory under a different user. An exception fails the start and stops the group.
placement_group keeps related collections and external helpers on the same router cell. It defaults
to this collection’s unique project; sharing placement does not share managed volumes or cleanup ownership.
The shared-storage init helper on the collection’s provider (one per provider that mounts volumes).
What a failed service’s command log says, for the failure’s message: a bounded tail of the log the run script redirects the command’s stdout and stderr to, a note that it wrote nothing, or why the log could not be read. The read is a second exec after the verdict that names the service, and the service’s sandbox may be gone by then (a provider that ends an OOM-killed pod between the final healthcheck and this read), so anything it raises goes into the message rather than replacing the verdict.
Create the service’s working directory once its sandbox is up, as Docker creates a missing
working_dir (or image WORKDIR) at container create.
Every exec in the service defaults to that directory, this group’s bootstrap ones included, and a
provider may refuse a command whose working directory does not exist (OpenSandbox answers 400): a
main started from a base image whose Dockerfile only names a WORKDIR has nothing else to create
it. Root from /, else the default user; the caller owns it afterwards as it owns a plain sandbox’s.
Run command as root, or as the sandbox’s default user when it cannot switch users.
The service whose network namespace name ends up in: itself when it declares no network_mode,
else the end of its network_mode: service:<target> chain. A chain that returns to a service it already
passed is a cycle, which raises naming it; _validate runs this before any sandbox exists.
The networks each service is on. A network_mode: service:<target> service shares the target’s
network namespace, so it is on the networks of the service at the end of its forwarding chain.
The paths mkdir -p would create for folders: each one and its missing ancestors, below /.
One probe lists every ancestor that does not exist yet; only paths from the candidate set are taken
from its output, so a name the shell printed oddly is dropped rather than chmod-ed. A probe that
fails reports nothing created: the chmod is best effort, the mkdir -p that follows is not.
Create each x-sandbox.logs source (a path of Harbor’s /logs tree the service bind-mounted) in
the service, world-writable, and link a target elsewhere to it.
Runs after the caller’s image setup and before the uploads, the service’s command, its post_start
hooks and its healthcheck, where Harbor’s bind sits between the image build and docker compose cp:
a healthcheck that needs /logs/verifier passes on a clean image, and an upload under the target lands
in the source through the link. /logs/artifacts:/app/output under Harbor is a second mount of one
host folder that hides whatever the image has at the target, so here the target becomes a link to the
source unconditionally (/logs/artifacts is where artifacts are collected from): a folder the image
ships there, or an overlay RUN mkdir -p created, is removed first, with a warning when it was not
empty, as a plain folder would keep the task’s artifacts out of the collection and show the agent what
the mount hides.
The provider a service runs on: its own from service_providers, else the collection’s.
Every distinct provider of the collection, the default first.
Run the service’s post_start hooks in order, once its command has started (as Compose does: after
the container starts, not after it is healthy). A hook that exits non-zero fails the start.
Copy each x-sandbox.uploads entry (a task file or folder a bind mount named) into the service.
Runs after the sandbox starts and before the service’s command, so the process finds the files
where the Compose file mounted them. The receiving folders are created first, and the folders
and files the upload touched (every folder created for it, the folders the files landed in and
the files; nothing else under the target) are made readable by every user, for an image whose
process runs as a non-root user. A folder’s tree is recreated under the target, empty folders
included (a program that expects the output or cache folder the task supplies finds it, as under
a bind mount), over whatever the image already has there (a merge, where a bind mount would hide
the image’s folder). The names the task digest skips (__pycache__, *.pyc, editor leftovers:
nemo_gym.data.tasks.harbor.digest.is_ignored) are left out, so what reaches the service is what
the digest covers, and a link inside the folder is refused, naming it, rather than read through.
Per service, the hostnames it resolves: itself, every service sharing a network with it (by name
and by the aliases on those networks) and its own extra_hosts.
The init helper on provider’s shared storage, started on first use. A service on its own provider
mounts that provider’s storage, so its directories are made and cleaned there.
The shared-storage mapping a service’s volumes resolve through: its own provider’s from
service_volume_sources, else the collection’s volume_sources.
Connect to all members without provisioning or restarting services.
Like AsyncSandbox.connect, stop() closes the connected sandboxes using
provider semantics. The creator retains ownership of managed-volume
cleanup and running service/forwarding tasks and must also call stop().
service_providers names the provider of members that live on another
one, as it did at start.
Describe a running collection using provider connection descriptors.
The creating process must keep the collection alive: it owns service and forwarding tasks and managed-volume cleanup. Provider configuration and YAML are not included.
Provider options from several sources: lists (run_args, volumes) concatenate, mappings
(extensions) merge with later layers winning, scalars are replaced.