Spaces:
Sleeping
Sleeping
File size: 10,913 Bytes
f52c7b1 1610381 f52c7b1 1610381 f52c7b1 1610381 362a3d0 1610381 f52c7b1 1610381 f52c7b1 1610381 f52c7b1 1610381 f52c7b1 362a3d0 f52c7b1 362a3d0 f52c7b1 1610381 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 | """Tests for the Docker execution engine (keyless; no Docker daemon needed).
The engine's *selection* logic and *command construction* are tested
here; actually executing a container is verified manually and by the
experiment harness, for the same reason live-LLM calls are excluded
from the unit suite.
"""
import json
from pathlib import Path
import pytest
from autocodabench.runner import execution as ex
# -- engine resolution --------------------------------------------------------
def test_auto_prefers_docker_when_available(monkeypatch):
monkeypatch.setattr(ex, "_docker_available", lambda: True)
r = ex.resolve_execution_engine("auto")
assert r["engine"] == "docker" and r["error"] is None and r["note"] is None
def test_auto_errors_without_docker(monkeypatch):
# Docker-only: with no daemon there is no fallback — it is a hard error.
monkeypatch.setattr(ex, "_docker_available", lambda: False)
r = ex.resolve_execution_engine("auto")
assert r["engine"] is None
assert "Docker" in r["error"] and "exclusively through Docker" in r["error"]
def test_conda_engine_removed(monkeypatch):
# The conda engine has been removed; requesting it returns a clear error.
monkeypatch.setattr(ex, "_docker_available", lambda: True)
r = ex.resolve_execution_engine("conda")
assert r["engine"] is None
assert "removed" in r["error"]
def test_explicit_docker_errors_without_daemon(monkeypatch):
monkeypatch.setattr(ex, "_docker_available", lambda: False)
r = ex.resolve_execution_engine("docker")
assert r["engine"] is None and "Docker" in r["error"]
def test_unknown_engine_rejected():
r = ex.resolve_execution_engine("podman")
assert r["engine"] is None and "unknown engine" in r["error"]
# -- docker command construction ----------------------------------------------
def test_docker_run_mirrors_worker_contract(tmp_path):
# stage the minimum the mount builder inspects
(tmp_path / "program" / "scoring_program").mkdir(parents=True)
(tmp_path / "input").mkdir()
(tmp_path / "output").mkdir()
cmd = ex._docker_run(
"codalab/codalab-legacy:py37", tmp_path, "scoring_program",
"python3 $program/score.py $input $output",
{"OMP_NUM_THREADS": "2"}, has_ingestion=False,
)
assert cmd.startswith("docker run --rm ")
# the active program dir is mounted at /app/program, as the worker does
assert f"-v {tmp_path / 'program' / 'scoring_program'}:/app/program:rw" in cmd
assert f"-v {tmp_path / 'input'}:/app/input:rw" in cmd
assert f"-v {tmp_path / 'output'}:/app/output:rw" in cmd
assert "-w /app/program" in cmd # worker's working directory
assert "codalab/codalab-legacy:py37" in cmd
# canonical $variables resolved to container paths
assert "/app/program/score.py /app/input /app/output" in cmd
assert "$program" not in cmd and "$input" not in cmd
assert "-e PYTHONUNBUFFERED=1" in cmd
assert "-e OMP_NUM_THREADS=2" in cmd
# the platform never installs requirements — neither may the engine
assert "pip install" not in cmd
def test_resolve_command_substitutes_worker_variables(tmp_path):
# $variables become the worker's absolute container paths; literal /app/...
# paths are already correct and left untouched.
out = ex._resolve_command(
"python3 $program/score.py $input_data $input $output",
"docker", tmp_path, "scoring_program")
assert "/app/program/score.py /app/input_data /app/input /app/output" in out
assert "$" not in out
# -- docker_image resolution from competition.yaml ------------------------------
def test_bundle_docker_image_reads_declared_image(tmp_path):
bundle = tmp_path / "demo"
bundle.mkdir()
(bundle / "competition.yaml").write_text(
"title: t\ndocker_image: myorg/myimage:1.2\n", encoding="utf-8")
assert ex.bundle_docker_image("demo", str(tmp_path)) == "myorg/myimage:1.2"
def test_bundle_docker_image_defaults_to_autocodabench_base(tmp_path):
bundle = tmp_path / "demo"
bundle.mkdir()
(bundle / "competition.yaml").write_text("title: t\n", encoding="utf-8")
# With no declared image, the runner falls back to the autocodabench CPU
# base image (overridable via AUTOCODABENCH_DOCKER_IMAGE).
got = ex.bundle_docker_image("demo", str(tmp_path))
assert got == ex._DEFAULT_DOCKER_IMAGE
assert "autocodabench-base-cpu" in got
# -- engine plumbing through the sandbox runner ---------------------------------
def test_run_user_submission_requires_daemon_for_explicit_docker(tmp_path, monkeypatch):
monkeypatch.setattr(ex, "_docker_available", lambda: False)
bundle = tmp_path / "demo"
(bundle / "scoring_program").mkdir(parents=True)
sub = tmp_path / "sub"
sub.mkdir()
monkeypatch.setenv("AUTOCODABENCH_BUNDLES_ROOT", str(tmp_path))
res = ex.run_user_submission("demo", env_name="unused",
submission_dir=str(sub), label="t",
engine="docker")
assert res["ok"] is False and "Docker daemon" in res["error"]
# -- arch normalization -------------------------------------------------------
@pytest.mark.parametrize("raw,want", [
("aarch64", "arm64"), ("arm64", "arm64"), ("arm64/v8", "arm64"),
("x86_64", "amd64"), ("amd64", "amd64"),
("", "unknown"), (None, "unknown"), ("ppc64le", "ppc64le"),
])
def test_normalize_arch(raw, want):
assert ex._normalize_arch(raw) == want
# -- image architecture detection (registry/local, no daemon needed) ----------
def _patch_query(monkeypatch, handler):
"""Replace `_docker_query(args, timeout=...)` with a canned dispatcher."""
monkeypatch.setattr(ex, "_docker_query", lambda args, timeout=20: handler(args))
def test_image_arch_status_local_inspect(monkeypatch):
def handler(args):
if args[:2] == ["image", "inspect"]:
return "arm64", None
return None, "should not reach the registry for a local image"
_patch_query(monkeypatch, handler)
s = ex.image_arch_status("local/img:1")
assert s["present_locally"] is True
assert s["arch"] == "arm64" and s["available_arches"] == ["arm64"]
assert s["multi_arch"] is False and s["source"] == "local image"
def test_image_arch_status_remote_multiarch(monkeypatch):
# `docker manifest inspect --verbose` returns a JSON array for a manifest
# list; attestation entries (architecture == "unknown") are dropped.
manifest = json.dumps([
{"Descriptor": {"platform": {"architecture": "amd64", "os": "linux"}}},
{"Descriptor": {"platform": {"architecture": "arm64", "os": "linux"}}},
{"Descriptor": {"platform": {"architecture": "unknown", "os": "unknown"}}},
])
def handler(args):
if args[:2] == ["image", "inspect"]:
return None, "No such image"
return manifest, None
_patch_query(monkeypatch, handler)
s = ex.image_arch_status("codalab/codalab-legacy:py312")
assert s["present_locally"] is False
assert s["available_arches"] == ["amd64", "arm64"]
assert s["multi_arch"] is True and s["arch"] is None
assert s["source"] == "remote manifest"
def test_image_arch_status_remote_singlearch(monkeypatch):
# A single-arch image: `--verbose` returns a JSON object, not a list.
manifest = json.dumps(
{"Descriptor": {"platform": {"architecture": "amd64", "os": "linux"}}})
def handler(args):
if args[:2] == ["image", "inspect"]:
return None, "No such image"
return manifest, None
_patch_query(monkeypatch, handler)
s = ex.image_arch_status("codalab/codalab-legacy:py39")
assert s["available_arches"] == ["amd64"] and s["arch"] == "amd64"
assert s["multi_arch"] is False
def test_image_arch_status_unavailable_tidies_error(monkeypatch):
# Not local and not in any registry: multi-line auth errors collapse to one.
def handler(args):
if args[:2] == ["image", "inspect"]:
return None, "No such image"
return None, "errors:\n denied: requested access\n unauthorized: auth required"
_patch_query(monkeypatch, handler)
s = ex.image_arch_status("autocodabench/autocodabench-base-cpu:latest")
assert s["arch"] is None and s["available_arches"] == []
assert "\n" not in s["error"] and "denied" in s["error"]
# -- docker_preflight: native vs emulated fit ---------------------------------
def _patch_preflight(monkeypatch, *, host, daemon_arch, img):
monkeypatch.setattr(ex, "_host_arch", lambda: host)
monkeypatch.setattr(ex, "docker_daemon_status", lambda: {
"cli_installed": True, "daemon_running": True,
"os": "linux", "arch": daemon_arch, "server_version": "27.0"})
monkeypatch.setattr(ex, "image_arch_status", lambda image: img)
def test_preflight_native_when_host_arch_available(monkeypatch):
_patch_preflight(monkeypatch, host="arm64", daemon_arch="arm64", img={
"present_locally": True, "arch": "arm64", "available_arches": ["arm64"],
"multi_arch": False, "source": "local image", "error": None})
p = ex.docker_preflight("local/img:1")
assert p["ready"] is True
assert p["runs_natively"] is True and p["emulated"] is False
def test_preflight_emulated_when_arch_mismatch(monkeypatch):
_patch_preflight(monkeypatch, host="arm64", daemon_arch="arm64", img={
"present_locally": False, "arch": "amd64", "available_arches": ["amd64"],
"multi_arch": False, "source": "remote manifest", "error": None})
p = ex.docker_preflight("codalab/codalab-legacy:py39")
assert p["runs_natively"] is False and p["emulated"] is True
def test_preflight_multiarch_runs_natively(monkeypatch):
_patch_preflight(monkeypatch, host="arm64", daemon_arch="arm64", img={
"present_locally": False, "arch": None,
"available_arches": ["amd64", "arm64"], "multi_arch": True,
"source": "remote manifest", "error": None})
p = ex.docker_preflight("codalab/codalab-legacy:py312")
assert p["runs_natively"] is True and p["emulated"] is False
def test_preflight_unknown_when_arch_undetermined(monkeypatch):
_patch_preflight(monkeypatch, host="arm64", daemon_arch="arm64", img={
"present_locally": False, "arch": None, "available_arches": [],
"multi_arch": False, "source": None, "error": "image not available"})
p = ex.docker_preflight("private/img:1")
assert p["runs_natively"] is None and p["emulated"] is None
def test_preflight_not_ready_without_daemon(monkeypatch):
monkeypatch.setattr(ex, "_host_arch", lambda: "arm64")
monkeypatch.setattr(ex, "docker_daemon_status", lambda: {
"cli_installed": True, "daemon_running": False,
"os": None, "arch": None, "server_version": None})
p = ex.docker_preflight("any/img:1")
assert p["ready"] is False and p["runs_natively"] is None
|