Skip to navigation

nemo_gym.tasks.harbor.image_configs

View as Markdown

Record the OCI configuration of the images a task runs.

Docker reads an image’s environment, user, working directory, entrypoint, command and exposed ports from the image itself. A sandbox platform without Docker cannot, so Gym records them from the registry into compose-images.json next to the task folders, one entry per image reference exactly as the task wrote it, pinned to the linux/amd64 manifest digest it resolved to. Two readers depend on the file:

  • the harbor resources server, for the sidecar images a Compose task starts (see nemo_gym.sandbox.compose_config.resolve_compose);
  • the task loader, for the base image of a pull-mode or overlay-mode Dockerfile, whose ENV, WORKDIR and USER lines resolve from the base image’s own configuration the way docker build resolves them (see nemo_gym.tasks.harbor.dockerfile).

The image an entry pins is the digest of the manifest the written reference named: the multi-arch index behind a tag, from which a sandbox of any platform selects its own manifest, or the single manifest of a single-platform reference. The loader starts a pull-mode or overlay-mode task from that pinned reference rather than the tag the Dockerfile wrote, so a moved tag cannot change the image under a recorded configuration; the entry stays keyed by the written reference, which is how the task loader and the Compose resolver look it up.

Module Contents

Functions

NameDescription
compose_fileThe task’s Compose overlay, if it has one.
compose_image_referencesEvery image a Compose task starts: the task image for main and each sidecar’s image.
dockerfile_base_imagesThe base image of every task Dockerfile the loader resolves under folder, with the tasks using it.
load_compose_documentThe task’s Compose overlay with env_file entries inlined and ${VAR} references resolved.
record_base_imagesRecord the base image of every Dockerfile under folder next to the task folders, before they load.
record_compose_imagesWrite compose-images.json in folder for the Compose tasks among tasks.
record_image_configsRecord the configuration of references (image reference to the tasks using it) into folder.
without_main_builddocument with main’s build and pull_policy removed, for a task that runs from the image
without_pull_policydocument with pull_policy removed from every service, for the sandbox Compose adapter.
without_restated_main_builddocument with main’s build: dropped when it only restates Harbor’s own build of the task’s

Data

COMPOSE_FILE_NAMES

COMPOSE_IMAGES_FILE

LOGGER

MAIN_BUILD_KEYS

RESTATED_BUILD_KEYS

API

nemo_gym.tasks.harbor.image_configs.compose_file(
) -> pathlib.Path | None

The task’s Compose overlay, if it has one.

nemo_gym.tasks.harbor.image_configs.compose_image_references(
) -> list[str]

Every image a Compose task starts: the task image for main and each sidecar’s image.

nemo_gym.tasks.harbor.image_configs.dockerfile_base_images(
folder: pathlib.Path,
select: typing.Callable[[str], bool] | None = None
) -> dict[str, list[str]]

The base image of every task Dockerfile the loader resolves under folder, with the tasks using it.

folder is a task folder or a folder of task folders, as discover_tasks reads it; with select, only the tasks whose folder name it accepts count. The image a task declares is the effective one, exactly as load_task sees it: task.toml after the dataset’s [gym] overrides. A per-task docker_image override thus turns a Dockerfile with RUN lines into a prebuilt image whose FROM is never resolved, so there is nothing to record for it. Tasks whose task.toml does not load, and a dataset whose dataset.toml does not load (which fails every task), are left for the loader to report.

nemo_gym.tasks.harbor.image_configs.load_compose_document(
) -> dict[str, typing.Any] | None

The task’s Compose overlay with env_file entries inlined and ${VAR} references resolved.

Returns None for a task without a Compose file. Prepare and the resources server both read the file through this function, so the document the sandbox starts is the one prepare validated, provided both run with the same process environment (see compose_variables). A required variable (${VAR:?msg}) without a value, a task variable the host does not set or a missing env_file raises ImageConfigError naming it; a variable nobody sets is an empty string, with a warning.

nemo_gym.tasks.harbor.image_configs.record_base_images(
folder: pathlib.Path,
select: typing.Callable[[str], bool] | None = None
) -> pathlib.Path | None

Record the base image of every Dockerfile under folder next to the task folders, before they load.

select limits the scan to the tasks whose folder name it accepts (see dockerfile_base_images). Returns the file’s path, or None when no task has a Dockerfile the loader resolves.

nemo_gym.tasks.harbor.image_configs.record_compose_images(
folder: pathlib.Path,
skipped: dict[str, str] | None = None
) -> pathlib.Path | None

Write compose-images.json in folder for the Compose tasks among tasks.

Returns the file’s path, or None when no task uses Compose. A task whose Compose file does not resolve (an ImageConfigError from load_compose_document) does not stop the others: with skipped given it is recorded there by task id with the reason and left out, otherwise one ImageConfigError at the end names every such task. An image the registry cannot resolve still raises for the whole set, as record_image_configs says.

nemo_gym.tasks.harbor.image_configs.record_image_configs(
references: dict[str, list[str]],
folder: pathlib.Path,
what: str = 'image'
) -> pathlib.Path | None

Record the configuration of references (image reference to the tasks using it) into folder.

Entries already recorded are kept as they are, so a pinned recording never changes under a completed run; only references missing from the file are resolved. An entry an older Gym wrote (no current record_version: it lacks fields the Dockerfile resolver reads) is read again, at the digest it was pinned to rather than the tag, so the pin does not move either. Returns the file’s path, or None when there is nothing to record. A reference the registry cannot resolve raises ImageConfigError naming the image, the tasks and the registry’s answer.

The registry is asked outside the file’s sidecar lock (record_lock), so a slow registry never holds up another process; the file is then read again, merged and replaced under the lock, and an entry another process recorded in the meantime is kept over the one resolved here, as a pinned entry always is (a legacy entry is replaced by its re-read).

nemo_gym.tasks.harbor.image_configs.without_main_build(
document: dict[str, typing.Any]
) -> dict[str, typing.Any]

document with main’s build and pull_policy removed, for a task that runs from the image prepare built and recorded.

Harbor builds main from the task’s Compose file at every start, so the file may give main a build: (args, a target, a dockerfile_inline) and Harbor’s own template sets pull_policy: build. Gym builds that image once at prepare, with the same file, and the loader then substitutes the recorded hb__<hash> image for main (MAIN_IMAGE_NAME resolves to it). The two keys describe a build that already happened; the sandbox Compose adapter starts images only and refuses them as unsupported fields, so they are dropped here rather than failing every episode of a task prepare accepted. Nothing else is touched: a sidecar’s build: is refused at prepare, and a task in pull or overlay mode goes through without_restated_main_build instead.

nemo_gym.tasks.harbor.image_configs.without_pull_policy(
document: dict[str, typing.Any]
) -> dict[str, typing.Any]

document with pull_policy removed from every service, for the sandbox Compose adapter.

The key tells docker compose when to pull or build an image before starting it (Harbor’s template sets pull_policy: build on main; a task file may give a sidecar always or missing). The sandbox starts the recorded image it is given and has no pull step of its own to configure, so the key is dropped from the document the sandbox receives, in every image mode, instead of being refused as an unsupported field. build: is not touched here: a sidecar’s is still refused (no provider starts a locally built sidecar) and main’s is dropped by without_main_build for a task that runs from its recorded built image, or by without_restated_main_build when it only restates Harbor’s build.

nemo_gym.tasks.harbor.image_configs.without_restated_main_build(
document: dict[str, typing.Any]
) -> tuple[dict[str, typing.Any], str | None]

document with main’s build: dropped when it only restates Harbor’s own build of the task’s Dockerfile, for a task in pull or overlay mode, and a reason instead when that section carries a key Gym cannot apply without a build. Any other task is returned as it is.

Harbor builds main from environment/Dockerfile through its own template, which already sets build.context: ${CONTEXT_DIR} (docker-compose-build.yaml at v0.22.0, appended before the task file), so a task file that repeats context, dockerfile: Dockerfile or pull changes nothing there; orca-bench, terminal_bench_aai and data-eng-bench do, some 600 hub tasks. Gym runs such a Dockerfile without a build (the loader folded it into the task in pull mode, or runs its RUN lines at seed in overlay mode), so the restatement is dropped as a built task’s section is (without_main_build). args, target, dockerfile_inline, another dockerfile or another context would change the image Harbor builds and have no equivalent without one, so the task is reported, not started on the wrong image.

nemo_gym.tasks.harbor.image_configs.COMPOSE_FILE_NAMES = ('docker-compose.yaml', 'docker-compose.yml', 'compose.yaml', 'compose.yml')
nemo_gym.tasks.harbor.image_configs.COMPOSE_IMAGES_FILE = IMAGE_CONFIGS_FILE
nemo_gym.tasks.harbor.image_configs.LOGGER = logging.getLogger(__name__)
nemo_gym.tasks.harbor.image_configs.MAIN_BUILD_KEYS = ('build', 'pull_policy')
nemo_gym.tasks.harbor.image_configs.RESTATED_BUILD_KEYS = frozenset({'context', 'dockerfile', 'pull'})