Skip to content

fix(server): release driver-owned sandbox resources on out-of-band removal - #3042

Merged
johntmyers merged 2 commits into
NVIDIA:mainfrom
politerealism:fix/podman-orphaned-resources-on-external-removal
Sep 1, 2026
Merged

fix(server): release driver-owned sandbox resources on out-of-band removal#3042
johntmyers merged 2 commits into
NVIDIA:mainfrom
politerealism:fix/podman-orphaned-resources-on-external-removal

Conversation

@politerealism

Copy link
Copy Markdown
Contributor

Summary

Release driver-owned sandbox resources (secrets, volumes) when a sandbox's compute resource is discovered gone out-of-band, instead of only cleaning up the gateway's store-side records. Closes a confirmed credential leak in the Podman driver.

Related Issue

Closes #2352

Changes

  • Add spawn_driver_sandbox_cleanup (watch path) and cleanup_driver_sandbox_resources (prune sweep) to ComputeRuntime, both calling the existing, already-idempotent DeleteSandbox RPC via a new shared call_driver_delete_sandbox helper.
  • The watch path defers the actual RPC to a background task: watch events are processed sequentially and must never block on a driver call. The lifecycle-gate check itself stays synchronous so it can't race a concurrent request-side operation.
  • Both paths skip the driver call entirely (non-blocking try_lock on the sandbox's lifecycle gate) when a request-side operation is already in flight for that sandbox, since that operation already owns driver-side cleanup and calling again would be redundant or racy.
  • The prune sweep calls the driver inline, since it already makes a blocking GetSandbox call per sandbox as part of its normal operation.
  • Updated architecture/compute-runtimes.md's Deletion Lifecycle section to document this behavior.

No proto changes. The fix reuses DeleteSandbox, which is already idempotent and already reclaims Podman's workspace volume, token secret, and proxy-auth secret when the container is already gone — the gap was purely that these two paths never called it.

Design Note

An earlier draft added a CleanupSandboxResources/ReconcileResources RPC pair per the design discussed on the issue. Investigation showed neither is needed: DeleteSandbox already does the reactive-cleanup job, and the gateway already writes a sandbox's store record before calling the driver's CreateSandbox, so the existing periodic sweep already covers the crash-recovery case (a driver resource with no matching store record never occurs from this code path). A ReconcileResources-style RPC would only matter for a store record lost independent of DeleteSandbox — a different, rarer failure mode than what this issue reports, deliberately left out of scope.

This PR closes the ticket for the Podman driver specifically. Docker's own DeleteSandbox has a related but separate gap (a branch that doesn't clean up its token file when the container is already gone with no pending record) — filed as #3041, not blocking this PR since it's a pre-existing gap in Docker's own idempotency, not something this change introduces or regresses.

Testing

  • mise run pre-commit passes
  • Unit tests added: apply_deleted_releases_driver_resources_for_out_of_band_removal, apply_deleted_removes_store_record_even_when_driver_cleanup_fails, prune_missing_sandbox_releases_driver_resources
  • Full compute:: module suite (169 tests) passes, including the pre-existing lifecycle-gate race tests (waiting_delete_retries_after_leader_failure_recovery, driver_completion_tolerates_watcher_removing_row_in_flight, delete_error_does_not_resurrect_row_removed_by_watcher) and the watch-loop non-blocking test (blocked_delete_does_not_delay_unrelated_deleted_watch_event)
  • E2E tests added/updated (if applicable)

Checklist

  • Follows Conventional Commits
  • Commits are signed off (DCO)
  • Architecture docs updated (if applicable)

@copy-pr-bot

copy-pr-bot Bot commented Aug 31, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@johntmyers johntmyers left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

gator-agent

PR Review Status

This focused fix is project-valid against linked issue #2352. The initial review found one blocking concurrency regression in the prune path.

Action required: update the prune cleanup so it does not await the driver while holding the gateway-wide synchronization lock, and add the blocked-driver regression test.

Blocking findings:

  • GATOR-9902b7bb-01: periodic reconciliation holds the global synchronization lock across an unbounded driver RPC.

Carried findings:

  • None

Non-blocking suggestions:

  • None
Gator metadata
  • Validation: Focused implementation for the reproduced resource leak in linked issue #2352; no duplicate candidate found.
  • Docs: Architecture documentation updated; no direct Fern UX documentation change required.
  • Checks: Current-head branch and Helm gates are pending; pipeline dispatch waits until review feedback is resolved.
  • E2E: Required for this sandbox lifecycle change, but not dispatched until the blocker is resolved.
  • Head SHA: 9902b7bbd3e57fa88d5712836335472a1f7ee46f
  • Base SHA: 883a1f01ce3d0ff023163fdb4d989c7c433c7974
  • Merge base SHA: 69a05ebb3b154e304a66fe80eed8504e889abc6d
  • Patch ID: 66c63619807eabd0ac02aad4da65d5e4637cfa8c
  • Gator payload: 7
  • Review mode: initial
  • Previous reviewed SHA: none
  • Review budget exhausted: no
  • Maintainer decision required: no
  • Next state: gator:in-review

Comment thread crates/openshell-server/src/compute/mod.rs Outdated
@johntmyers johntmyers added the gator:in-review Gator is reviewing or awaiting PR review feedback label Aug 31, 2026
politerealism added a commit to politerealism/OpenShell that referenced this pull request Aug 31, 2026
The prune sweep's second sync_lock acquisition has no inner block
scope, so it's held through the rest of prune_missing_sandbox,
including the driver cleanup call added in the previous commit. That
call is a network RPC to the compute driver; holding the gateway-wide
sync_lock across it blocks every other sandbox operation on the
gateway for as long as that one driver call takes, not just the
sandbox being pruned.

Use the same backgrounded spawn_driver_sandbox_cleanup already used by
the watch path instead of the inline cleanup_driver_sandbox_resources,
which is now unused and removed. Add a regression test that blocks the
driver's delete call indefinitely and confirms the prune sweep still
completes and removes the store record promptly.

Addresses gator finding GATOR-9902b7bb-01 on NVIDIA#3042.

Signed-off-by: politerealism <burdcat17@gmail.com>
@politerealism

Copy link
Copy Markdown
Contributor Author

Addressed GATOR-9902b7bb-01: the prune sweep's second sync_lock acquisition has no inner block scope, so it was held through the rest of prune_missing_sandbox, including the driver cleanup RPC I'd added inline. Switched that call site to the same backgrounded spawn_driver_sandbox_cleanup the watch path already used, and removed the now-unused inline cleanup_driver_sandbox_resources. Added a regression test (prune_sweep_does_not_block_on_a_stuck_driver_delete_call) that blocks the driver's delete call indefinitely and confirms the sweep still completes and removes the store record promptly.

@johntmyers johntmyers added the test:e2e Requires end-to-end coverage label Aug 31, 2026
@johntmyers

Copy link
Copy Markdown
Collaborator

/ok to test 1191803

@github-actions

Copy link
Copy Markdown

Label test:e2e applied for 1191803. Open the existing run and click Re-run all jobs to execute with the label set. The run will execute the standard E2E suite after building the required gateway and supervisor images once. The matching required CI gate status on this PR will flip green automatically once the run finishes.

@johntmyers johntmyers left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

gator-agent

Re-check After Author Update

Thanks @politerealism. I checked your update that backgrounds the prune-path driver cleanup and the new stuck-driver regression test. The prior lock-held RPC finding is resolved, and the follow-up review found no new blocking issues.

Blocking findings:

  • No blocking findings remain

Carried findings:

  • GATOR-9902b7bb-01: resolved by moving prune cleanup to spawn_driver_sandbox_cleanup and covering a blocked DeleteSandbox call.
Gator metadata
  • Validation: Focused fix for the reproduced resource leak in linked issue #2352.
  • Docs: Architecture documentation updated; no direct Fern UX documentation change required.
  • Checks: Current-head Branch Checks, Helm Lint, and E2E workflows are dispatched and in progress.
  • E2E: test:e2e applied; /ok to test 11918038cdf43fa3b58b40b9415be215955bffc1 posted; current-head run 33422088329 is in progress.
  • Head SHA: 11918038cdf43fa3b58b40b9415be215955bffc1
  • Base SHA: 883a1f01ce3d0ff023163fdb4d989c7c433c7974
  • Merge base SHA: 69a05ebb3b154e304a66fe80eed8504e889abc6d
  • Patch ID: b99f4f0b95633f4c4a9d416c593dccc667385f6e
  • Gator payload: 7
  • Review mode: follow_up
  • Previous reviewed SHA: 9902b7bbd3e57fa88d5712836335472a1f7ee46f
  • Review budget exhausted: no
  • Maintainer decision required: no
  • Review telemetry: 0 proposed findings, 0 blockers, 0 hypotheses, 0 unchanged-code proposals; 1 finding-bearing round before this clean follow-up.
  • Next state: gator:watch-pipeline

@johntmyers johntmyers added gator:watch-pipeline Gator is monitoring PR CI/CD status gator:blocked Gator is blocked by process or repository gates and removed gator:in-review Gator is reviewing or awaiting PR review feedback gator:watch-pipeline Gator is monitoring PR CI/CD status labels Aug 31, 2026
@politerealism

Copy link
Copy Markdown
Contributor Author

@johntmyers this looks more like CI flakiness. Can you rerun the tests?

@johntmyers

Copy link
Copy Markdown
Collaborator

you might be hitting a race in kube tests i have #3064 to hopefully fix it so may want to wait on that and then rebase

@johntmyers johntmyers added gator:follow-up-needed Gator needs submitter or maintainer follow-up and removed gator:blocked Gator is blocked by process or repository gates labels Sep 1, 2026
…moval

Neither the watch path nor the periodic prune sweep call the driver's
DeleteSandbox RPC when a sandbox's compute resource is discovered gone
out-of-band (container removed without going through OpenShell's own
delete path, or the gateway crashing mid-create). Only store-side
records were cleaned up, leaving driver-owned secrets and volumes
behind indefinitely — a credential leak for the Podman driver's
per-sandbox bearer-token and proxy-auth secrets.

DeleteSandbox is already idempotent and already reclaims these
resources when the underlying container is already gone, so no new
RPC is needed: this wires the existing call into the two paths that
discover a sandbox is gone without going through an explicit delete
request.

The watch path defers the actual call to a background task, gated by
a non-blocking check of the sandbox's lifecycle gate: watch events are
processed sequentially and must never block on a driver call, and
skipping the call entirely when a concurrent request-side operation
already holds the gate avoids racing that operation's own driver call.
The prune sweep calls it inline, since it already makes a blocking
GetSandbox call per sandbox as part of its normal operation.

Closes NVIDIA#2352

Signed-off-by: politerealism <burdcat17@gmail.com>
The prune sweep's second sync_lock acquisition has no inner block
scope, so it's held through the rest of prune_missing_sandbox,
including the driver cleanup call added in the previous commit. That
call is a network RPC to the compute driver; holding the gateway-wide
sync_lock across it blocks every other sandbox operation on the
gateway for as long as that one driver call takes, not just the
sandbox being pruned.

Use the same backgrounded spawn_driver_sandbox_cleanup already used by
the watch path instead of the inline cleanup_driver_sandbox_resources,
which is now unused and removed. Add a regression test that blocks the
driver's delete call indefinitely and confirms the prune sweep still
completes and removes the store record promptly.

Addresses gator finding GATOR-9902b7bb-01 on NVIDIA#3042.

Signed-off-by: politerealism <burdcat17@gmail.com>
@politerealism
politerealism force-pushed the fix/podman-orphaned-resources-on-external-removal branch from 1191803 to 31c6fd2 Compare September 1, 2026 16:07
@johntmyers

Copy link
Copy Markdown
Collaborator

/ok to test 31c6fd2

@politerealism

Copy link
Copy Markdown
Contributor Author

@johntmyers rebased onto main — this now includes #3064, so the kube race you flagged should be resolved. New head SHA: 31c6fd2a. DCO and gate checks are green; Branch Checks/E2E/Helm Lint are waiting on /ok to test for the new SHA whenever you get a chance.

@johntmyers

Copy link
Copy Markdown
Collaborator

gator-agent

PR Review Status

The current head is a rebase-equivalent form of the previously reviewed patch, so the resolved concurrency finding remains resolved and no new review round is needed. The maintainer's suggestion to wait for the Kubernetes race fix and rebase has been followed; I verified the effective patch identity and dispatched the required current-head workflows.

Blocking findings:

  • No blocking findings remain

Carried findings:

  • GATOR-9902b7bb-01: resolved on the prior reviewed patch; the rebase preserves that fix.
Gator metadata
  • Validation: Focused fix for the reproduced resource leak in linked issue bug(driver-podman): per-sandbox secrets and volumes are orphaned when a container disappears without DeleteSandbox #2352.
  • Docs: Architecture documentation updated; no direct Fern UX documentation change required.
  • Checks: Current-head Branch Checks, Helm Lint, and E2E workflows are queued.
  • E2E: test:e2e is applied; /ok to test 31c6fd2a5cb5e2c6a66f735c33aa21fba5e03fbd was posted and runs 33530254597, 33530254461, and 33530255423 are queued.
  • Head SHA: 31c6fd2a5cb5e2c6a66f735c33aa21fba5e03fbd
  • Base SHA: c8f13205e31674b4e239b428f5a5f6cb31710427
  • Merge base SHA: c8f13205e31674b4e239b428f5a5f6cb31710427
  • Patch ID: b99f4f0b95633f4c4a9d416c593dccc667385f6e
  • Gator payload: 7
  • Review mode: already_reviewed
  • Previous reviewed SHA: 11918038cdf43fa3b58b40b9415be215955bffc1
  • Review budget exhausted: no
  • Maintainer decision required: no
  • Next state: gator:watch-pipeline

@johntmyers johntmyers added gator:watch-pipeline Gator is monitoring PR CI/CD status and removed gator:follow-up-needed Gator needs submitter or maintainer follow-up labels Sep 1, 2026
@johntmyers johntmyers added gator:approval-needed Gator completed review; maintainer approval needed and removed gator:watch-pipeline Gator is monitoring PR CI/CD status labels Sep 1, 2026
@johntmyers
johntmyers added this pull request to the merge queue Sep 1, 2026
Merged via the queue into NVIDIA:main with commit 4ef8423 Sep 1, 2026
79 of 81 checks passed
@johntmyers

Copy link
Copy Markdown
Collaborator

gator-agent

Monitoring Complete

Monitoring is complete because this PR has merged.

Final status: The reviewed fix had no remaining blocking findings, required checks completed successfully, and maintainer approval was present before merge.

I removed the active gator:* label because there is nothing left for gator to monitor on this PR.

Gator metadata
  • Head SHA: 31c6fd2a5cb5e2c6a66f735c33aa21fba5e03fbd
  • Gator payload: 7
  • Final state: merged

@johntmyers johntmyers removed the gator:approval-needed Gator completed review; maintainer approval needed label Sep 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

test:e2e Requires end-to-end coverage

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(driver-podman): per-sandbox secrets and volumes are orphaned when a container disappears without DeleteSandbox

2 participants