Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Make the dashboard follow what each solver can actually do: panels and action bu
- `csauto doctor` reports the panels and capabilities derived for the configured solver

### Fixed
- Opening the solver GUI on a case whose shared dirs are symlinks (the default since `mesh_mode = "symlink"` became the default in 0.4.1) left those symlinks dangling inside the container: `build_gui_command` (docker) and the singularity branch of `build_runtime_gui_command` mounted only the runs dir, unlike their `run` counterparts which also bind the symlink targets. Both now bind them the same way, `MESH` read-only and `POST` writable
- Requesting a restart on a solver without restart support returned HTTP 500 "Launch error", a client error reported as a server fault; it now returns HTTP 400 naming the solver
- Live control on a solver declaring no control action reported "Invalid action (expected one of [])"; both the API and the CLI now name the solver
- code_aster's Compare panel offered an empty file selector; it now offers `doe_row.csv`
Expand Down
4 changes: 4 additions & 0 deletions csauto/docker.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,11 @@ def build_gui_command(
except ValueError:
setup_rel = Path(setup_path.name)
container_setup = f"{container_case}/{setup_rel.as_posix()}"
from .execution import shared_dir_symlink_mounts

cmd: list[str] = ["docker", "run", "-v", f"{runs_root}:{container_root}"]
for target, readonly in shared_dir_symlink_mounts(runs_root, adapter.shared_dir_names):
cmd.extend(["-v", f"{target}:{target}:ro" if readonly else f"{target}:{target}"])
if display:
cmd.extend(
[
Expand Down
2 changes: 2 additions & 0 deletions csauto/execution.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,8 @@ def build_runtime_gui_command(
"--pwd",
container_case,
]
for target, readonly in shared_dir_symlink_mounts(runs_root, adapter.shared_dir_names):
cmd.extend(["--bind", f"{target}:{target}:ro" if readonly else f"{target}:{target}"])
if os.environ.get("DISPLAY") and Path("/tmp/.X11-unix").exists():
cmd.extend(["--bind", "/tmp/.X11-unix:/tmp/.X11-unix"])
cmd.append(selection.singularity_image)
Expand Down
35 changes: 34 additions & 1 deletion tests/unit/test_docker.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from pathlib import Path

from csauto.docker import build_run_command, read_container_id
from csauto.docker import build_gui_command, build_run_command, read_container_id


def test_build_run_command_without_display(monkeypatch, tmp_path: Path) -> None:
Expand Down Expand Up @@ -71,3 +71,36 @@ def test_build_run_command_mounts_symlinked_shared_dirs(monkeypatch, tmp_path: P
assert f"-v {mesh.resolve()}:{mesh.resolve()}:ro" in joined
assert f"-v {post.resolve()}:{post.resolve()}" in joined
assert f"{post.resolve()}:ro" not in joined


def test_build_gui_command_mounts_shared_dir_symlinks(monkeypatch, tmp_path: Path) -> None:
"""The GUI must see the same shared dirs as a run, or symlinked meshes break inside the container."""
monkeypatch.delenv("DISPLAY", raising=False)
runs_dir = tmp_path / "RUNS"
case_dir = runs_dir / "case0001"
(case_dir / "DATA").mkdir(parents=True)
(case_dir / "DATA" / "setup.xml").write_text("<root/>", encoding="utf-8")
mesh = tmp_path / "study" / "MESH"
post = tmp_path / "study" / "POST"
mesh.mkdir(parents=True)
post.mkdir(parents=True)
(runs_dir / "MESH").symlink_to(mesh, target_is_directory=True)
(runs_dir / "POST").symlink_to(post, target_is_directory=True)

cmd = build_gui_command(case_dir, "my_image")

assert f"{mesh.resolve()}:{mesh.resolve()}:ro" in cmd
assert f"{post.resolve()}:{post.resolve()}" in cmd


def test_build_gui_command_adds_no_mount_without_symlinks(monkeypatch, tmp_path: Path) -> None:
monkeypatch.delenv("DISPLAY", raising=False)
runs_dir = tmp_path / "RUNS"
case_dir = runs_dir / "case0001"
(case_dir / "DATA").mkdir(parents=True)
(case_dir / "DATA" / "setup.xml").write_text("<root/>", encoding="utf-8")
(runs_dir / "MESH").mkdir()

cmd = build_gui_command(case_dir, "my_image")

assert cmd.count("-v") == 1
26 changes: 26 additions & 0 deletions tests/unit/test_execution.py
Original file line number Diff line number Diff line change
Expand Up @@ -309,3 +309,29 @@ def test_build_singularity_slurm_script_binds_symlinked_shared_dirs(tmp_path: Pa
script = CodeSaturneAdapter().build_slurm_script(case_dir, nprocs=2, nt=1, selection=selection)

assert f"--bind {mesh.resolve()}:{mesh.resolve()}:ro" in script


def test_build_runtime_gui_command_singularity_mounts_shared_dir_symlinks(tmp_path: Path, monkeypatch) -> None:
"""The GUI must see the same shared dirs as a run, or symlinked meshes break inside the container."""
monkeypatch.delenv("DISPLAY", raising=False)
runs_dir = tmp_path / "RUNS"
case_dir = runs_dir / "case0001"
(case_dir / "DATA").mkdir(parents=True)
(case_dir / "DATA" / "setup.xml").write_text("<root/>", encoding="utf-8")
mesh = tmp_path / "study" / "MESH"
post = tmp_path / "study" / "POST"
mesh.mkdir(parents=True)
post.mkdir(parents=True)
(runs_dir / "MESH").symlink_to(mesh, target_is_directory=True)
(runs_dir / "POST").symlink_to(post, target_is_directory=True)
selection = RuntimeSelection(
runtime=RUNTIME_SINGULARITY,
docker_image="img",
singularity_bin="/usr/bin/apptainer",
singularity_image="/images/code_saturne.sif",
)

cmd = build_runtime_gui_command(case_dir, selection)

assert f"{mesh.resolve()}:{mesh.resolve()}:ro" in cmd
assert f"{post.resolve()}:{post.resolve()}" in cmd
Loading