Skip to content

Commit 11a7b32

Browse files
committed
Correctly handle archiving sandboxes in Interactive
1 parent a64f33d commit 11a7b32

3 files changed

Lines changed: 48 additions & 18 deletions

File tree

cms/grading/Sandbox.py

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -712,6 +712,28 @@ def cleanup(self, delete: bool = False):
712712
# Delete the working directory.
713713
rmtree(self._outer_dir)
714714

715+
def archive_to_fobj(self, fobj: typing.IO[bytes]) -> bool:
716+
"""Archive the directory where the sandbox operated.
717+
718+
fobj: the file object the archive will be written to.
719+
return: whether archiving succeeded.
720+
"""
721+
metadata_path = self.get_root_path()
722+
box_dir = self._home
723+
with tarfile.open(fileobj=fobj, mode='w:gz') as tar_file:
724+
try:
725+
# Add metadata files
726+
for x in os.listdir(metadata_path):
727+
tar_file.add(os.path.join(metadata_path, x), x)
728+
# Add the box directory
729+
tar_file.add(box_dir, "box")
730+
except Exception:
731+
logger.warning(
732+
"Failed to add files to sandbox archive", exc_info=True
733+
)
734+
return False
735+
return True
736+
715737
def archive(self, file_cacher: FileCacher) -> str | None:
716738
"""Archive the directory where the sandbox operated.
717739
@@ -724,20 +746,9 @@ def archive(self, file_cacher: FileCacher) -> str | None:
724746

725747
with tempfile.TemporaryFile(dir=self.temp_dir) as sandbox_archive:
726748
# Archive the working directory
727-
metadata_path = self.get_root_path()
728-
box_dir = self._home
729-
with tarfile.open(fileobj=sandbox_archive, mode='w:gz') as tar_file:
730-
try:
731-
# Add metadata files
732-
for x in os.listdir(metadata_path):
733-
tar_file.add(os.path.join(metadata_path, x), x)
734-
# Add the box directory
735-
tar_file.add(box_dir, "box")
736-
except Exception:
737-
logger.warning(
738-
"Failed to add files to sandbox archive", exc_info=True
739-
)
740-
749+
ok = self.archive_to_fobj(sandbox_archive)
750+
if not ok:
751+
return None
741752
# Put archive to FS
742753
sandbox_archive.seek(0)
743754
return file_cacher.put_file_from_fobj(

cms/grading/tasktypes/Interactive.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ def evaluate(self, job, file_cacher):
240240
"concurrent": self.concurrent,
241241
"temp_dir": tempdir,
242242
"shard": file_cacher.service.shard if file_cacher.service else None,
243-
"delete_sandbox": True,
243+
"archive_sandbox": job.archive_sandbox,
244244
}
245245

246246
keeper_path = os.path.join(
@@ -284,6 +284,11 @@ def evaluate(self, job, file_cacher):
284284
job.text = result["text"]
285285
job.admin_text = result.get("admin_text")
286286
job.plus = result.get("stats", {})
287+
job.sandboxes = result["sandboxes"]
288+
for k,v in result["sandbox_archives"].items():
289+
digest = file_cacher.put_file_from_path(v, f"Sandbox {k}")
290+
job.sandbox_digests[k] = digest
291+
287292

288293
def _executable_filename(self, codenames, language):
289294
"""Return the filename of the executable."""

cms/grading/tasktypes/interactive_keeper.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import shutil
2424
import sys
2525
from functools import reduce
26+
import tempfile
2627

2728
from cms.grading.Sandbox import Sandbox, wait_without_std
2829
from cms.grading.steps.evaluation import (
@@ -81,7 +82,7 @@ def main():
8182
concurrent = config.get("concurrent")
8283
temp_dir = config.get("temp_dir")
8384
shard = config.get("shard")
84-
delete_sandbox = config.get("delete_sandbox")
85+
archive_sandbox = config.get("archive_sandbox")
8586

8687
pipes = []
8788
for i in range(process_limit):
@@ -259,19 +260,32 @@ def do_merge(a, b):
259260
outcome = score if score is not None else 0.0
260261
text = controller_text
261262

263+
job_sandboxes = []
264+
job_sandbox_archives = {}
265+
266+
for box in [controller_sandbox] + solution_sandboxes:
267+
job_sandboxes.append(box.get_root_path())
268+
if archive_sandbox or not success:
269+
with tempfile.NamedTemporaryFile('wb', dir=temp_dir, delete=False) as f:
270+
ok = box.archive_to_fobj(f)
271+
if ok:
272+
job_sandbox_archives[box.get_root_path()] = f.name
273+
262274
result = {
263275
"success": success,
264276
"outcome": outcome,
265277
"text": text,
266278
"admin_text": admin_text,
267279
"stats": stats_user,
280+
"sandboxes": job_sandboxes,
281+
"sandbox_archives": job_sandbox_archives,
268282
}
269283
# Communicate results back to the worker
270284
print(json.dumps(result), flush=True)
271285

272-
controller_sandbox.cleanup(delete=delete_sandbox)
286+
controller_sandbox.cleanup(delete=True)
273287
for s in solution_sandboxes:
274-
s.cleanup(delete=delete_sandbox)
288+
s.cleanup(delete=True)
275289

276290

277291
if __name__ == "__main__":

0 commit comments

Comments
 (0)