Skip to content

fix(nemo-deployments): gate OpenShell READY on a curl reachability probe - #1259

Merged
maxdubrinsky merged 2 commits into
mainfrom
mdubrinsky/aircore-998-readiness-probe-interpreter-path
Aug 13, 2026
Merged

fix(nemo-deployments): gate OpenShell READY on a curl reachability probe#1259
maxdubrinsky merged 2 commits into
mainfrom
mdubrinsky/aircore-998-readiness-probe-interpreter-path

Conversation

@maxdubrinsky

@maxdubrinsky maxdubrinsky commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Summary

The readiness gate added in #1139 never ran: it located an interpreter with command -v python3 || command -v python, but packaged agent images ship Python only in /workspace/.venv, which is not on the exec PATH, so the probe hit its else exit 0 branch and the gate read that as reachable. READY was published about 14s before nat serve bound its port. This probes with curl instead, resolved at /usr/bin/curl (installed by the NAT and Fabric Dockerfiles, pinned by the sandbox policy) with a PATH fallback, and fails closed when curl is absent, so READY now means the port is reachable.

Related Issue

AIRCORE-998 (NVBug 6567588). Reopened by QA after #1139 did not change the measured behavior.

Changes

  • Replace the Python readiness probe with a curl probe in openshell/backend.py; a missing curl exits nonzero (fails closed) instead of exiting 0.
  • httpGet probe uses curl --fail --insecure (a 5xx reads as not-ready; a self-signed loopback cert is accepted).
  • tcpSocket and default probe do a loopback connect classified by curl exit code (7 = refused = not-ready, any other exit = ready). telnet:// was rejected because curl stays connected until --max-time on a live port.
  • deploy-sandbox SKILL: drop the "retry a 502 for 30-60s" guidance now that READY implies reachability.
  • Add a unit test for the fail-closed behavior and an integration test that asserts READY waits for the port to bind.

Type of Change

  • Code change (feature, bug fix, or refactor)
  • Code change with documentation updates
  • Documentation only
  • Contributor tooling or automation
  • CI, build, or test infrastructure

Quality Gates

  • Tests added or updated for changed behavior
  • Existing tests cover changed behavior — justification:
  • Tests not applicable — justification:
  • Documentation updated for user-visible behavior
  • Documentation not applicable — justification:

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 <4 changed files>: passed (ruff, ruff format, ty, copyright headers, plugin-import check, merge-conflict check). Full pre-commit run -a not run locally (heavy container hooks such as helm-docs and OpenAPI generation are irrelevant to these files); left to CI.
  • Unit: pytest plugins/nemo-deployments/tests/unit/backends/openshell/test_backend.py with the openshell extra: 71 passed, including a new fail-closed test.
  • Integration: pytest plugins/nemo-deployments/tests/integration/backends/openshell/test_openshell_backend.py against a live gateway (v0.0.92, docker driver): 2 passed, including a new slow-bind test asserting READY waits for bind.
  • Probe behavior confirmed directly via openshell sandbox exec (the backend's own exec path): unbound port -> curl exit 7 (not-ready); bound socket -> curl exit 0 (ready); with no Python on PATH the old probe exits 0 (READY, reproducing the bug) while the new probe exits 1 (STARTING, fixed).

Summary by CodeRabbit

  • Bug Fixes

    • Improved deployment readiness checks for HTTP and TCP services.
    • Deployments now remain in a starting state until the workload accepts connections.
    • Improved handling of unavailable readiness-check tools and HTTPS loopback checks.
    • Updated 502 troubleshooting guidance to recommend immediate serve-log inspection.
  • Tests

    • Added coverage for delayed service startup, readiness transitions, and endpoint availability.

The readiness gate added in #1139 never ran. It located an interpreter with
`command -v python3 || command -v python`, but packaged agent images ship Python
only in /workspace/.venv, which is not on the exec PATH. With no interpreter
found, the probe took its `else exit 0` branch, which the gate reads as
reachable, so READY was published about 14s before nat serve bound its port.

Probe with curl instead. curl is present in these images (the NAT and Fabric
Dockerfiles install it, and the sandbox policy pins /usr/bin/curl) and is
resolved at that absolute path with a PATH fallback. A missing curl now exits
nonzero, so the gate fails closed.

- httpGet: curl --fail --insecure (5xx not-ready, self-signed loopback ok)
- tcpSocket and default: loopback connect classified by curl exit code
  (7 = refused = not-ready, any other exit = ready)
- deploy-sandbox SKILL: drop the "retry a 502 for 30-60s" guidance, since READY
  now means the port is reachable
- integration: add a slow-bind regression test that asserts READY waits for bind

Fixes AIRCORE-998.

Signed-off-by: Max Dubrinsky <mdubrinsky@nvidia.com>
@github-actions github-actions Bot added the fix label Aug 12, 2026
@github-actions

github-actions Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor
Suite Lines Covered Line Rate Branch Rate
Unit Tests 32232/40906 78.8% 63.7%
Integration Tests 18647/38832 48.0% 20.7%

@maxdubrinsky
maxdubrinsky marked this pull request as ready for review August 12, 2026 18:49
@maxdubrinsky
maxdubrinsky requested review from a team as code owners August 12, 2026 18:49
@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

OpenShell readiness probes now use curl for HTTP and TCP checks. Tests cover curl selection, failure handling, HTTPS, and delayed workload binding. Sandbox troubleshooting now requires READY before invocation and directs 502 investigation to serve logs.

OpenShell readiness

Layer / File(s) Summary
Curl-based readiness probes
plugins/nemo-deployments/src/nemo_deployments_plugin/backends/openshell/backend.py, plugins/nemo-deployments/tests/unit/backends/openshell/test_backend.py
The backend selects curl for HTTP and TCP probes. HTTP probes use failure detection, disabled certificate verification, no-proxy mode, timeouts, and discarded output. Missing curl and connection refusal produce failed readiness.
Readiness lifecycle validation
plugins/nemo-deployments/tests/integration/backends/openshell/test_openshell_backend.py, plugins/nemo-deployments/src/nemo_deployments_plugin/skills/deploy-sandbox/SKILL.md
Integration coverage verifies that STARTING persists until the workload binds its port, followed by READY and endpoint exposure. Troubleshooting guidance requires READY before invocation and directs 502 investigation to serve logs.

Sequence Diagram(s)

sequenceDiagram
  participant OpenShellBackend
  participant Curl
  participant Workload
  OpenShellBackend->>Curl: Run HTTP or TCP readiness probe
  Curl->>Workload: Connect to loopback port
  Workload-->>Curl: Return response or connection result
  Curl-->>OpenShellBackend: Return exit status
  OpenShellBackend-->>OpenShellBackend: Set readiness state
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes gating OpenShell readiness on a curl reachability probe.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch mdubrinsky/aircore-998-readiness-probe-interpreter-path

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: 2

🤖 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/openshell/backend.py`:
- Around line 830-839: Add --noproxy '*' to every curl invocation generated by
_curl_probe_script in backend.py, covering both HTTP and TCP probes. Update
test_backend.py lines 773-797 to assert the option appears in both generated
probe commands.

In
`@plugins/nemo-deployments/src/nemo_deployments_plugin/skills/deploy-sandbox/SKILL.md`:
- Line 289: Update the readiness guidance in SKILL.md to remove the stale
instruction to wait after READY because readiness now includes port binding.
Keep the recovery guidance explaining that a 502 indicates nat serve on port
9000 is not answering and directs users to the serve log.
🪄 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: 7c2fb00a-d223-49d3-aec0-bad87ebdeda4

📥 Commits

Reviewing files that changed from the base of the PR and between d4b8171 and adba8ee.

📒 Files selected for processing (4)
  • plugins/nemo-deployments/src/nemo_deployments_plugin/backends/openshell/backend.py
  • plugins/nemo-deployments/src/nemo_deployments_plugin/skills/deploy-sandbox/SKILL.md
  • plugins/nemo-deployments/tests/integration/backends/openshell/test_openshell_backend.py
  • plugins/nemo-deployments/tests/unit/backends/openshell/test_backend.py

Loopback readiness probes ran plain curl, so http_proxy/https_proxy in the
sandbox image env would route the 127.0.0.1 request through a proxy. For the
TCP probe (exit 7 = not-ready, anything else = ready) a reachable-but-erroring
proxy returns a non-7 code, flipping the deployment to READY before nat serve
binds its port -- the exact race this gate closes. Add --noproxy '*' to both
the httpGet and TCP probe curls, and assert it in the unit tests.

Also drop the stale 'give serve a moment after READY' note in the deploy
skill: READY now gates on a reachability probe, so the endpoint serves as
soon as it reports READY.

Signed-off-by: Max Dubrinsky <mdubrinsky@nvidia.com>

@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-deployments/src/nemo_deployments_plugin/skills/deploy-sandbox/SKILL.md`:
- Line 275: Update the deployment command flow around the status polling and
recovery-table transition to add an explicit guard that exits before extracting
URL or reaching Step 6 unless status equals READY. Preserve the existing
recovery handling for FAILED and timed-out PENDING/STARTING states.
🪄 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: 90aa0afc-8c27-40f5-97a8-49ac2645826c

📥 Commits

Reviewing files that changed from the base of the PR and between adba8ee and 350b9a6.

📒 Files selected for processing (3)
  • plugins/nemo-deployments/src/nemo_deployments_plugin/backends/openshell/backend.py
  • plugins/nemo-deployments/src/nemo_deployments_plugin/skills/deploy-sandbox/SKILL.md
  • plugins/nemo-deployments/tests/unit/backends/openshell/test_backend.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • plugins/nemo-deployments/src/nemo_deployments_plugin/backends/openshell/backend.py
  • plugins/nemo-deployments/tests/unit/backends/openshell/test_backend.py

@maxdubrinsky
maxdubrinsky enabled auto-merge August 12, 2026 19:59
@maxdubrinsky
maxdubrinsky disabled auto-merge August 12, 2026 19:59
@maxdubrinsky
maxdubrinsky added this pull request to the merge queue Aug 13, 2026
Merged via the queue into main with commit 725ad48 Aug 13, 2026
145 of 149 checks passed
@maxdubrinsky
maxdubrinsky deleted the mdubrinsky/aircore-998-readiness-probe-interpreter-path branch August 13, 2026 14:14
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