108 lines
3.7 KiB
Python
108 lines
3.7 KiB
Python
#!/usr/bin/env python3
|
|
"""The project folder is mounted by the session, not by the image.
|
|
|
|
python3 tests/test_vm_share_mount.py
|
|
|
|
WHY
|
|
|
|
The share is one virtiofs tag. The image built by the autoinstall carries
|
|
an fstab line for it; an image installed by hand does not, and the
|
|
failure that produces is the worst shape there is - the machine boots,
|
|
the agent runs its commands, files are written, the host commits an empty
|
|
directory, and the Gitea repository is simply empty. No error anywhere.
|
|
|
|
So the session mounts it over the guest channel every time, and these
|
|
cases cover the three things that has to get right: do nothing when there
|
|
is no share, do nothing on Windows (where the mount is WinFsp's job and
|
|
there is no command to run from here), and fail LOUDLY rather than carry
|
|
on when the mount does not take.
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
|
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "vm", "vmd"))
|
|
|
|
import session as S # noqa: E402
|
|
|
|
FAILS: list[str] = []
|
|
|
|
|
|
def check(label: str, condition: bool, detail: str = "") -> None:
|
|
if condition:
|
|
print(f"ok {label}")
|
|
else:
|
|
FAILS.append(f"{label}: {detail}")
|
|
print(f"FAIL {label}\n {detail}")
|
|
|
|
|
|
class FakeGuest:
|
|
def __init__(self, result):
|
|
self.result = result
|
|
self.scripts: list[str] = []
|
|
|
|
def __enter__(self):
|
|
return self
|
|
|
|
def __exit__(self, *_exc):
|
|
return False
|
|
|
|
def shell(self, script, timeout_s=0):
|
|
self.scripts.append(script)
|
|
return self.result
|
|
|
|
|
|
class FakeSession:
|
|
def __init__(self, os_name="ubuntu", share="/Pool/vmprojects/p", result=None):
|
|
self.os_name = os_name
|
|
self.share = share
|
|
self._guest = FakeGuest(result or {"exit_code": 0, "stdout": "", "stderr": ""})
|
|
|
|
def guest(self, timeout=0):
|
|
return self._guest
|
|
|
|
|
|
# --- nothing to mount ---------------------------------------------------
|
|
s = FakeSession(share=None)
|
|
S._mount_share(s)
|
|
check("no share: nothing is run in the guest", s._guest.scripts == [],
|
|
f"ran {s._guest.scripts}")
|
|
|
|
# --- Windows ------------------------------------------------------------
|
|
s = FakeSession(os_name="windows")
|
|
S._mount_share(s)
|
|
check("windows: no linux mount command is sent", s._guest.scripts == [],
|
|
f"ran {s._guest.scripts}")
|
|
|
|
# --- the normal case ----------------------------------------------------
|
|
s = FakeSession()
|
|
S._mount_share(s)
|
|
script = s._guest.scripts[0] if s._guest.scripts else ""
|
|
check("ubuntu: the share is mounted", "mount -t virtiofs project /mnt/project" in script,
|
|
f"script was: {script!r}")
|
|
check("and the mountpoint is created first", "mkdir -p /mnt/project" in script, script)
|
|
# Idempotence is not a nicety here: an image that DOES carry the fstab
|
|
# line is already mounted, and a second mount over the top would hide it.
|
|
check("and an existing mount is left alone", "mountpoint -q /mnt/project ||" in script, script)
|
|
check("and the result is checked rather than assumed",
|
|
script.strip().endswith("mountpoint -q /mnt/project"), script)
|
|
|
|
# --- the failure that must not be silent --------------------------------
|
|
s = FakeSession(result={"exit_code": 32, "stdout": "",
|
|
"stderr": "unknown filesystem type 'virtiofs'"})
|
|
try:
|
|
S._mount_share(s)
|
|
check("a failed mount stops the session", False, "it carried on regardless")
|
|
except S.SessionError as exc:
|
|
text = str(exc)
|
|
check("a failed mount stops the session", True)
|
|
check("and says what would have been lost",
|
|
"would be kept" in text or "nothing written" in text, text)
|
|
check("and quotes what the guest actually said", "virtiofs" in text, text)
|
|
|
|
print()
|
|
if FAILS:
|
|
print(f"FAILED {len(FAILS)}")
|
|
raise SystemExit(1)
|
|
print("the project folder is mounted by the session, and loudly if not")
|