Skip to content

fix(deployments): docker backend delivers config_files; unify agents config delivery - #1217

Merged
maxdubrinsky merged 4 commits into
mainfrom
aircore-955-docker-config-files-delivery/mdubrinsky
Aug 11, 2026
Merged

fix(deployments): docker backend delivers config_files; unify agents config delivery#1217
maxdubrinsky merged 4 commits into
mainfrom
aircore-955-docker-config-files-delivery/mdubrinsky

Conversation

@maxdubrinsky

@maxdubrinsky maxdubrinsky commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Summary

The docker deployments backend ignored DeploymentConfig.config_files, so the agents compiler delivered the NAT config through a NAT_CONFIG_YAML env var and reconstituted it with a shell one-liner. That docker-specific path made every other substrate inherit docker's config-delivery behavior, breaking OpenShell-backed deployments (nvbugs 6581239). Docker now delivers config_files via create + put_archive + start, the agents mode == "docker" env-delivery branch is deleted, and config_mount_path moves to /tmp/nemo/config.yaml (the writable intersection of all three substrates).

Related Issue

Part of AIRCORE-955 / AIRCORE-1016.

Changes

  • docker/backend.py: new _config_files_tar() builds an in-memory tar (with dir entries) from ConfigFile list. _run_server_container now does containers.create -> put_archive("/", tar) -> container.start(). Config is written into the container's own filesystem before the server command reads it, with no host-path coupling (correct when nmp-api itself runs in a container). Init and sidecar containers move to create + start for a uniform API; the volume-chmod docker run --rm stays on run.
  • runner/deployments_backend.py: deleted the mode == "docker" env-delivery branch (NAT_CONFIG_YAML / STAGED_CONFIG_FILES_B64_JSON / sh -c materialization). build_deployment_config always emits the direct server command and carries config_files on the DeploymentConfig, matching the k8s path. Removed dead helpers and unused imports (base64, json, shlex).
  • config.py: config_mount_path default /workspace/config.yaml -> /tmp/nemo/config.yaml. /tmp is writable by the docker container user, the openshell sandbox user (uid 999, cannot write /workspace), and k8s ConfigMap subPath (read-only).
  • Tests updated to model containers.create + container.start instead of containers.run.

Type of Change

  • Code change (feature, bug fix, or refactor)

Quality Gates

  • Tests added or updated for changed behavior
  • Documentation not applicable — justification: no user-visible behavior change; internal config-delivery mechanism

Verification

  • Pull request title follows the repository's Conventional Commit format
  • Every commit includes an appropriate Signed-off-by: trailer
  • uv run pre-commit run -a passes, or any blocked checks are identified below
  • Targeted tests pass, or tests are marked not applicable above
  • No secrets, API keys, or credentials are included

Targeted validation:

  • uv run pre-commit run --files <changed paths> — all hooks pass (ruff, ruff format, ty, copyright, merge-conflict).
  • uv run --frozen pytest plugins/nemo-deployments/tests/unit/backends/docker/test_backend_mocked.py plugins/nemo-deployments/tests/unit/backends/docker/test_idempotency.py plugins/nemo-deployments/tests/unit/backends/docker/test_image_pull_auth.py plugins/nemo-agents/tests/unit/test_runner_deployments.py -q — 106 passed.
  • uv run --frozen ty check on changed backend files — all checks passed.
  • DCO audit: 1 commit, DCO OK.

Pre-existing failures confirmed identical on origin/main (not caused by this change): port-allocation tests (environment-dependent real port binding), test_cli_list_output (timestamp substring), openshell integration test (AsyncEntitiesResource mock), and test_backend.py basename collection conflict between k8s and openshell dirs.

Summary by CodeRabbit

  • Improvements
    • Improved deployment compatibility across Docker, Kubernetes, and OpenShell environments.
    • Configuration files are now delivered directly to deployment containers, including multi-file configurations.
    • Deployment startup and retry handling are more reliable, with failed containers cleaned up automatically.
  • Bug Fixes
    • Fixed configuration mounting and startup behavior for NAT and Fabric deployments.
    • Improved handling of container creation failures, port conflicts, image pulls, and one-shot jobs.

@maxdubrinsky
maxdubrinsky requested review from a team as code owners August 10, 2026 20:18
@github-actions github-actions Bot added the fix label Aug 10, 2026
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The change moves agent configuration to /tmp/nemo, removes shell-based materialization, and updates Docker execution to create containers, upload configuration archives, and start containers explicitly. Tests cover commands, mounts, retries, idempotency, and lifecycle behavior.

Changes

Deployment configuration and Docker execution

Layer / File(s) Summary
Direct agent configuration and entrypoints
plugins/nemo-agents/src/nemo_agents_plugin/config.py, plugins/nemo-agents/src/nemo_agents_plugin/runner/deployments_backend.py, plugins/nemo-agents/tests/unit/test_runner_deployments.py
Agent deployments use /tmp/nemo paths, direct Fabric and NAT commands, and configured file mounts without shell or environment-based materialization.
Docker configuration delivery and startup
plugins/nemo-deployments/src/nemo_deployments_plugin/backends/docker/backend.py
The Docker backend creates containers, uploads configuration files as an in-memory tar archive, and starts primary, sidecar, and init containers explicitly.
Docker lifecycle and retry validation
plugins/nemo-deployments/tests/unit/backends/docker/test_backend_mocked.py, plugins/nemo-deployments/tests/unit/backends/docker/test_idempotency.py, plugins/nemo-deployments/tests/unit/backends/docker/test_image_pull_auth.py
Tests validate containers.create(), explicit startup, retries, cleanup, idempotency, one-shot jobs, and image-pull authentication.

Sequence Diagram(s)

sequenceDiagram
  participant DeploymentsBackend
  participant DockerBackend
  participant DockerContainer
  DeploymentsBackend->>DockerBackend: create deployment container
  DockerBackend->>DockerBackend: build ConfigFile tar archive
  DockerBackend->>DockerContainer: put_archive configuration files
  DockerBackend->>DockerContainer: start container
Loading

Possibly related PRs

Suggested reviewers: mikeknep, ironcommit

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the Docker config delivery change and the unified agents configuration delivery.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch aircore-955-docker-config-files-delivery/mdubrinsky

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
plugins/nemo-deployments/src/nemo_deployments_plugin/backends/docker/backend.py (1)

594-602: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Remove failed init containers.

If start() or wait() raises, container.remove() is skipped. The failed init container remains until another deployment attempt removes it. Move cleanup into finally after creation.

Proposed fix
         def _run_and_wait() -> int:
             create_kwargs = {k: v for k, v in run_kwargs.items() if k != "detach"}
             container = self._client.containers.create(**create_kwargs)
-            container.start()
-            result = container.wait(timeout=self._executor_config.docker_timeout)
-            exit_code = self._exit_code_from_wait_result(result)
             try:
+                container.start()
+                result = container.wait(timeout=self._executor_config.docker_timeout)
+                return self._exit_code_from_wait_result(result)
+            finally:
                 container.remove(force=True)
             except Exception:
                 logger.warning("Failed to remove init container %s", init_name, exc_info=True)
-            return exit_code
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@plugins/nemo-deployments/src/nemo_deployments_plugin/backends/docker/backend.py`
around lines 594 - 602, Ensure the init container created in this deployment
flow is removed even when container.start() or container.wait() raises. Wrap the
start/wait and exit-code handling in a try/finally, keeping
container.remove(force=True) and its warning handling in the finally block after
successful creation.
🧹 Nitpick comments (1)
plugins/nemo-agents/src/nemo_agents_plugin/runner/deployments_backend.py (1)

380-381: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Align staged-file fixtures with the Fabric server path.

The affected tests mock /workspace/agent.yaml, but the default Fabric server reads /tmp/nemo/agent.yaml. Use /tmp/nemo in both fixtures and assert that agent.yaml matches the server argument.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plugins/nemo-agents/src/nemo_agents_plugin/runner/deployments_backend.py`
around lines 380 - 381, The staged-file fixtures in
plugins/nemo-agents/src/nemo_agents_plugin/runner/deployments_backend.py:380-381
and plugins/nemo-agents/tests/unit/test_runner_deployments.py:978-982 must use
/tmp/nemo instead of /workspace for agent.yaml. Update both fixtures and make
the tests assert that agent.yaml matches the server argument used by the Fabric
deployment path.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@plugins/nemo-deployments/src/nemo_deployments_plugin/backends/docker/backend.py`:
- Around line 452-460: The container cleanup flow in the Docker backend must not
remove a container by name when containers.create fails. In the deployment
creation path around _remove_container_by_name, only clean up the container
object after creation succeeds and _deliver_config_files or container.start
fails; preserve the create-failure path without name-based removal. Update
plugins/nemo-deployments/tests/unit/backends/docker/test_backend_mocked.py lines
149-165 to assert removal of server_container rather than a separately returned
leftover container.

---

Outside diff comments:
In
`@plugins/nemo-deployments/src/nemo_deployments_plugin/backends/docker/backend.py`:
- Around line 594-602: Ensure the init container created in this deployment flow
is removed even when container.start() or container.wait() raises. Wrap the
start/wait and exit-code handling in a try/finally, keeping
container.remove(force=True) and its warning handling in the finally block after
successful creation.

---

Nitpick comments:
In `@plugins/nemo-agents/src/nemo_agents_plugin/runner/deployments_backend.py`:
- Around line 380-381: The staged-file fixtures in
plugins/nemo-agents/src/nemo_agents_plugin/runner/deployments_backend.py:380-381
and plugins/nemo-agents/tests/unit/test_runner_deployments.py:978-982 must use
/tmp/nemo instead of /workspace for agent.yaml. Update both fixtures and make
the tests assert that agent.yaml matches the server argument used by the Fabric
deployment path.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: ecda3dd9-9ea0-46ef-b4cd-35f4037d2073

📥 Commits

Reviewing files that changed from the base of the PR and between d8205de and 173e04f.

📒 Files selected for processing (7)
  • plugins/nemo-agents/src/nemo_agents_plugin/config.py
  • plugins/nemo-agents/src/nemo_agents_plugin/runner/deployments_backend.py
  • plugins/nemo-agents/tests/unit/test_runner_deployments.py
  • plugins/nemo-deployments/src/nemo_deployments_plugin/backends/docker/backend.py
  • plugins/nemo-deployments/tests/unit/backends/docker/test_backend_mocked.py
  • plugins/nemo-deployments/tests/unit/backends/docker/test_idempotency.py
  • plugins/nemo-deployments/tests/unit/backends/docker/test_image_pull_auth.py

Comment thread plugins/nemo-deployments/src/nemo_deployments_plugin/backends/docker/backend.py Outdated
@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor
Suite Lines Covered Line Rate Branch Rate
Unit Tests 31999/40622 78.8% 63.5%
Integration Tests 18552/38548 48.1% 20.8%

maxdubrinsky added a commit that referenced this pull request Aug 10, 2026
…E-955)

Address CodeRabbit review on #1217:

- _run_server_container: only remove the container object returned by
  containers.create; never name-lookup after a create failure, which could
  force-remove another deployment's container that won the name.
- _run_init_container: move container.remove into a finally so a start/wait
  raise still cleans up the init container.
- Fabric create-deployment test fixtures: align staged-file paths with the
  /tmp/nemo default config_mount_path.

Signed-off-by: Max Dubrinsky <mdubrinsky@nvidia.com>
…config delivery (AIRCORE-955)

The docker deployments backend ignored DeploymentConfig.config_files, so the agents compiler delivered the NAT config through a NAT_CONFIG_YAML env var and reconstituted it with a shell one-liner. That docker-specific path made every other substrate inherit docker's config-delivery behavior, breaking OpenShell-backed deployments (nvbugs 6581239).

Docker backend now delivers config_files via create + put_archive + start: the file is written into the container's own filesystem before the server command reads it, with no host-path coupling (correct when nmp-api itself runs in a container). Init and sidecar containers move to create + start for a uniform API; the volume-chmod docker run --rm stays on run.

The agents mode == docker env-delivery branch is deleted; build_deployment_config always emits the direct server command and carries config_files on the DeploymentConfig, matching the k8s path. config_mount_path default moves from /workspace/config.yaml to /tmp/nemo/config.yaml: /tmp is the writable intersection of the docker container user, the openshell sandbox user (uid 999, cannot write /workspace), and k8s ConfigMap subPath (read-only).

Signed-off-by: Max Dubrinsky <mdubrinsky@nvidia.com>
…E-955)

Address CodeRabbit review on #1217:

- _run_server_container: only remove the container object returned by
  containers.create; never name-lookup after a create failure, which could
  force-remove another deployment's container that won the name.
- _run_init_container: move container.remove into a finally so a start/wait
  raise still cleans up the init container.
- Fabric create-deployment test fixtures: align staged-file paths with the
  /tmp/nemo default config_mount_path.

Signed-off-by: Max Dubrinsky <mdubrinsky@nvidia.com>
@maxdubrinsky
maxdubrinsky force-pushed the aircore-955-docker-config-files-delivery/mdubrinsky branch from 14e1b71 to 73da8d3 Compare August 10, 2026 20:58

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@plugins/nemo-agents/tests/unit/test_runner_deployments.py`:
- Line 220: Update the default config_mount_path assertion in
DeploymentsRunnerConfig to use exact equality with the documented path
“/tmp/nemo/config.yaml” instead of a prefix check.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 7a483116-a334-4dc4-9fb1-bbcf53037c50

📥 Commits

Reviewing files that changed from the base of the PR and between 173e04f and 73da8d3.

📒 Files selected for processing (3)
  • plugins/nemo-agents/tests/unit/test_runner_deployments.py
  • plugins/nemo-deployments/src/nemo_deployments_plugin/backends/docker/backend.py
  • plugins/nemo-deployments/tests/unit/backends/docker/test_backend_mocked.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • plugins/nemo-deployments/src/nemo_deployments_plugin/backends/docker/backend.py
  • plugins/nemo-deployments/tests/unit/backends/docker/test_backend_mocked.py

Comment thread plugins/nemo-agents/tests/unit/test_runner_deployments.py Outdated
A prefix check accepts an incorrect filename under /tmp/nemo/. Use exact
equality so the test catches a wrong default.

Signed-off-by: Max Dubrinsky <mdubrinsky@nvidia.com>
Comment thread plugins/nemo-agents/src/nemo_agents_plugin/config.py
Comment thread plugins/nemo-deployments/src/nemo_deployments_plugin/backends/docker/backend.py Outdated
Comment thread plugins/nemo-deployments/src/nemo_deployments_plugin/backends/docker/backend.py Outdated
Signed-off-by: Max Dubrinsky <mdubrinsky@nvidia.com>
@maxdubrinsky
maxdubrinsky enabled auto-merge August 11, 2026 16:37

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
plugins/nemo-deployments/src/nemo_deployments_plugin/backends/docker/backend.py (2)

118-141: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Preserve ConfigFile.mode in the archive.

The archive builder assigns mode 0644 to every file. ConfigFile.mode is part of the deployment contract. A file declared 0600 becomes world-readable, and an executable file loses its execute bit. Set each file header from config_file.mode and add tests for non-default modes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@plugins/nemo-deployments/src/nemo_deployments_plugin/backends/docker/backend.py`
around lines 118 - 141, Update _config_files_tar to assign each file
TarInfo.mode from the corresponding ConfigFile.mode instead of hardcoding 0644,
preserving declared permissions including restrictive and executable modes. Add
tests covering non-default ConfigFile.mode values and verify those modes are
retained in the generated archive.

499-501: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Contain primary-container cleanup failures.

If created.remove() fails, _run_server_container raises before create_deployment releases the allocated GPU. Catch cleanup failures, log them, and return the startup error. Treat NotFound as already removed. Do not retry a port conflict when cleanup fails.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@plugins/nemo-deployments/src/nemo_deployments_plugin/backends/docker/backend.py`
around lines 499 - 501, Update the exception cleanup in _run_server_container to
contain failures from created.remove: treat NotFound as already removed, log
other cleanup errors, and return the original startup error instead of replacing
it. Ensure create_deployment still releases the allocated GPU and does not retry
a port conflict when cleanup fails.

Source: MCP tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In
`@plugins/nemo-deployments/src/nemo_deployments_plugin/backends/docker/backend.py`:
- Around line 118-141: Update _config_files_tar to assign each file TarInfo.mode
from the corresponding ConfigFile.mode instead of hardcoding 0644, preserving
declared permissions including restrictive and executable modes. Add tests
covering non-default ConfigFile.mode values and verify those modes are retained
in the generated archive.
- Around line 499-501: Update the exception cleanup in _run_server_container to
contain failures from created.remove: treat NotFound as already removed, log
other cleanup errors, and return the original startup error instead of replacing
it. Ensure create_deployment still releases the allocated GPU and does not retry
a port conflict when cleanup fails.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 77efc8f0-1428-418c-b85d-d37401a4eba4

📥 Commits

Reviewing files that changed from the base of the PR and between 23e1f05 and ebcbf40.

📒 Files selected for processing (1)
  • plugins/nemo-deployments/src/nemo_deployments_plugin/backends/docker/backend.py

@maxdubrinsky
maxdubrinsky added this pull request to the merge queue Aug 11, 2026
Merged via the queue into main with commit 60f5c8f Aug 11, 2026
55 checks passed
@maxdubrinsky
maxdubrinsky deleted the aircore-955-docker-config-files-delivery/mdubrinsky branch August 11, 2026 17:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants