diff --git a/l4d2host/instances.py b/l4d2host/instances.py index 59a293e..8128114 100644 --- a/l4d2host/instances.py +++ b/l4d2host/instances.py @@ -135,14 +135,17 @@ def stop_instance( passthrough=passthrough, should_cancel=should_cancel, ) - emit_step("unmounting runtime overlay...", on_stdout, passthrough) - run_command( - ["fusermount3", "-u", str(root / "runtime" / name / "merged")], - on_stdout=on_stdout, - on_stderr=on_stderr, - passthrough=passthrough, - should_cancel=should_cancel, - ) + emit_step("unmounting runtime overlay (if mounted)...", on_stdout, passthrough) + try: + run_command( + ["fusermount3", "-u", str(root / "runtime" / name / "merged")], + on_stdout=on_stdout, + on_stderr=on_stderr, + passthrough=passthrough, + should_cancel=should_cancel, + ) + except subprocess.CalledProcessError: + pass emit_step("stop complete.", on_stdout, passthrough) diff --git a/l4d2host/tests/test_lifecycle.py b/l4d2host/tests/test_lifecycle.py index 84d6bd4..5c369d1 100644 --- a/l4d2host/tests/test_lifecycle.py +++ b/l4d2host/tests/test_lifecycle.py @@ -96,6 +96,27 @@ def test_delete_stopped_instance_removes_dirs(tmp_path: Path, monkeypatch: pytes assert ["sudo", "-n", "/usr/local/libexec/left4me/left4me-systemctl", "stop", "alpha"] in calls +def test_stop_succeeds_when_unmount_fails(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: + fusermount_calls: list[list[str]] = [] + + def fake_run_command(cmd, **kwargs): + del kwargs + if cmd and cmd[0] == "fusermount3": + fusermount_calls.append(list(cmd)) + raise subprocess.CalledProcessError( + returncode=1, + cmd=list(cmd), + stderr="fusermount3: failed to unmount /var/lib/left4me/runtime/alpha/merged: Invalid argument", + ) + + monkeypatch.setattr("l4d2host.instances.run_command", fake_run_command) + monkeypatch.setattr("l4d2host.service_control.run_command", fake_run_command) + + stop_instance("alpha", root=tmp_path) + + assert fusermount_calls, "stop must always attempt fusermount3 -u (no preflight)" + + def test_delete_succeeds_when_unmount_fails(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: fusermount_calls: list[list[str]] = []