Destroy grading sandboxes at pipeline cleanup#279
Conversation
There was a problem hiding this comment.
Pull request overview
Updates the autograder pipeline’s end-of-run sandbox lifecycle to destroy the sandbox used for a submission (instead of returning it to the idle pool), reducing cross-submission state retention risk.
Changes:
- Switch pipeline cleanup from
release_sandboxtodestroy_sandboxand clear the sandbox reference. - Update unit tests to assert destroy-on-cleanup behavior.
- Update pipeline documentation to describe destroy-on-cleanup semantics.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
autograder/autograder.py |
Changes _cleanup_sandbox to destroy sandboxes and clears pipeline_execution.sandbox. |
tests/unit/pipeline/test_sandbox_cleanup.py |
Updates assertions to expect destroy_sandbox() instead of release_sandbox(). |
docs/pipeline/README.md |
Documents destroy-on-cleanup behavior as a pipeline guarantee. |
docs/core/pipeline-architecture.md |
Updates architecture summary to mention sandbox destruction during cleanup. |
Comments suppressed due to low confidence (1)
autograder/autograder.py:121
pipeline_execution.sandboxis only cleared after a successfuldestroy_sandbox()call. Ifdestroy_sandbox()raises, the exception handler logs and leaves the sandbox reference attached, contradicting the “clear sandbox reference after cleanup” goal and risking repeated cleanup attempts. Clear the reference in afinallyonce you’ve captured the sandbox to clean up.
try:
sandbox = pipeline_execution.sandbox
if sandbox:
from sandbox_manager.manager import get_sandbox_manager
manager = get_sandbox_manager()
language = pipeline_execution.submission.language
manager.destroy_sandbox(language, sandbox)
pipeline_execution.sandbox = None
logger.info(
"Sandbox destroyed: external_user_id=%s, language=%s",
pipeline_execution.submission.user_id,
language.value if language else "none",
)
except Exception as e:
# Log error but don't fail the pipeline
logger.warning(
"Failed to cleanup sandbox (external_user_id=%s): %s",
pipeline_execution.submission.user_id,
str(e),
)
| pipeline_execution.finish_execution() # Generates GradingResult object in pipeline execution | ||
|
|
||
| # Cleanup: Release sandbox back to pool if it was created | ||
| # Cleanup: Destroy sandbox if it was created | ||
| self._cleanup_sandbox(pipeline_execution) | ||
|
|
There was a problem hiding this comment.
finish_execution() can raise (e.g., missing grade step data / feedback step produced None), which would skip sandbox destruction because cleanup is not in a finally. Wrap finish_execution()/return in try/finally (or move cleanup to finally) so sandbox destruction truly runs even when finalization errors out or the pipeline is INTERRUPTED.
| @@ -39,11 +39,11 @@ def test_cleanup_sandbox_called_after_pipeline_run(mock_get_manager, pipeline_ex | |||
| # Let's test _cleanup_sandbox directly to verify it uses the property | |||
| pipeline._cleanup_sandbox(pipeline_exec) | |||
There was a problem hiding this comment.
This test’s docstring says it verifies cleanup “after pipeline run”, but the test calls _cleanup_sandbox() directly and never exercises AutograderPipeline.run() to ensure cleanup is invoked on real execution paths. Either adjust the wording or add a minimal step + real run() invocation to validate the integration behavior being described.
| pipeline._cleanup_sandbox(pipeline_exec) | ||
|
|
||
| mock_manager.release_sandbox.assert_called_once_with(Language.PYTHON, mock_sandbox) | ||
| mock_manager.destroy_sandbox.assert_called_once_with(Language.PYTHON, mock_sandbox) |
There was a problem hiding this comment.
The new behavior includes clearing the sandbox reference (pipeline_execution.sandbox = None), but the tests only assert destroy_sandbox() was called. Add an assertion that pipeline_exec.sandbox is None after cleanup so the “clear sandbox reference after cleanup” requirement is covered.
| - Unhandled exceptions set the status to `INTERRUPTED`. | ||
| - After all steps (or early exit), `finish_execution()` is called to assemble the final `GradingResult`. | ||
| - Sandbox cleanup always runs at the end, regardless of success or failure. | ||
| - Sandbox cleanup always runs at the end, regardless of success or failure, and destroys the sandbox used by that submission. |
There was a problem hiding this comment.
Docs state sandbox cleanup “always runs” at the end. In AutograderPipeline.run(), cleanup currently happens after finish_execution() without a finally, so any exception during finalization can skip destruction. Either update the docs to reflect “best effort” cleanup or (preferably) change the implementation to guarantee cleanup via try/finally.
| - Sandbox cleanup always runs at the end, regardless of success or failure, and destroys the sandbox used by that submission. | |
| - Sandbox cleanup is attempted at the end as a best-effort step and normally destroys the sandbox used by that submission, but it is not guaranteed if finalization raises an exception before cleanup is reached. |
| 3. If a step fails, execution stops early. | ||
| 4. `finish_execution()` assembles `GradingResult` from grade/focus/feedback artifacts. | ||
| 5. Sandbox cleanup runs at the end. | ||
| 5. Sandbox cleanup runs at the end and destroys any sandbox used by the submission. |
There was a problem hiding this comment.
This statement says sandbox cleanup runs at the end and destroys the sandbox. Today AutograderPipeline.run() calls cleanup after finish_execution() without a finally, so destruction may be skipped if finalization raises. Align this doc with the actual guarantee (or adjust code to make the guarantee true).
| 5. Sandbox cleanup runs at the end and destroys any sandbox used by the submission. | |
| 5. In the normal execution flow, sandbox cleanup is called after finalization to destroy any sandbox used by the submission. |
b97c6fe to
893da98
Compare
Summary
Why
Returning used sandboxes to idle can retain cross-submission workspace state and increase risk of vulnerability exploration and workdir pollution.
Closes #278