test(e2e): full virtualized build+boot+unlock QEMU harness
Opt-in via LIM_E2E_QEMU=1. Models the whole process on a virtio VM (the Pi's USB-gadget net can't be emulated, so it models the software stack, not the board): builds a LUKS image carrying the real lim initcpio Tor artifacts, boots it in QEMU rootless, lets the real netconf/tor/dropbear/ encryptssh chain publish the onion, delivers the passphrase over Tor, and asserts the boot-ok marker on the serial console. Supports a private offline Tor network via chutney. The pure command builders (config.qemu_argv/kernel_cmdline/ssh_argv, qemu_binary), the env parser, and drift guards that keep build_image.sh aligned with the harness run in the normal suite — no QEMU/root/network. The build stage needs root; harness.py primes sudo up front, keeps the credential warm, and reclaims work-dir ownership on every exit path so the pytest tmp cleanup never trips on root-owned files. QEMU stderr is captured so an early exit is debuggable, and each teardown step is fault-isolated so none masks the real error. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
72
tests/e2e/test_qemu_unlock_e2e.py
Normal file
72
tests/e2e/test_qemu_unlock_e2e.py
Normal file
@@ -0,0 +1,72 @@
|
||||
"""Full virtualized end-to-end test: build -> boot in QEMU -> unlock over Tor.
|
||||
|
||||
Models the whole process the way lim runs it, on a QEMU-bootable virtio
|
||||
machine (the Raspberry Pi's USB-gadget net can't be emulated, so we model
|
||||
the software stack, not the board):
|
||||
|
||||
1. build a LUKS image carrying the REAL lim initcpio tor artifacts,
|
||||
2. boot it in QEMU (rootless, user-mode net),
|
||||
3. its initramfs runs the real netconf/tor/dropbear/encryptssh chain and
|
||||
publishes the onion service,
|
||||
4. deliver the LUKS passphrase by SSHing to that onion through Tor,
|
||||
5. assert the boot-ok marker appears on the serial console — i.e. the real
|
||||
root actually unlocked and booted.
|
||||
|
||||
Heavy and privileged (QEMU + a tor network; the build needs root, ideally in
|
||||
a throwaway VM). Opt-in and skipped unless LIM_E2E_QEMU=1:
|
||||
|
||||
LIM_E2E_QEMU=1 pytest tests/e2e/test_qemu_unlock_e2e.py -v -s
|
||||
|
||||
Environment knobs: LIM_E2E_ARCH (x86_64|aarch64), CHUTNEY_PATH (use a private
|
||||
chutney network instead of the public one). See tests/e2e/qemu/README.md.
|
||||
"""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from tests.e2e.qemu import config as qemu_config
|
||||
from tests.e2e.qemu.harness import HarnessConfig, run
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parents[2]
|
||||
|
||||
# The QEMU binary is arch-specific, so derive it from LIM_E2E_ARCH instead of
|
||||
# hardcoding x86_64 — otherwise the documented aarch64 run is never reachable.
|
||||
_ARCH = os.environ.get("LIM_E2E_ARCH", "x86_64")
|
||||
_REQUIRED = (
|
||||
qemu_config.qemu_binary(_ARCH),
|
||||
"cryptsetup", "pacstrap", "tor", "ssh", "ncat",
|
||||
)
|
||||
|
||||
|
||||
def _missing_tools() -> list[str]:
|
||||
return [tool for tool in _REQUIRED if shutil.which(tool) is None]
|
||||
|
||||
|
||||
pytestmark = pytest.mark.skipif(
|
||||
os.environ.get("LIM_E2E_QEMU") != "1" or bool(_missing_tools()),
|
||||
reason=(
|
||||
"needs LIM_E2E_QEMU=1 and "
|
||||
+ ", ".join(_REQUIRED)
|
||||
+ " (build stage needs root; slow)."
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def test_full_build_boot_and_tor_unlock(tmp_path):
|
||||
chutney_path = os.environ.get("CHUTNEY_PATH")
|
||||
cfg = HarnessConfig(
|
||||
repo_root=REPO_ROOT,
|
||||
work_dir=tmp_path / "qemu-e2e",
|
||||
arch=_ARCH,
|
||||
chutney_path=Path(chutney_path) if chutney_path else None,
|
||||
)
|
||||
|
||||
spec = run(cfg)
|
||||
|
||||
# run() only returns after the marker was observed; re-assert for clarity.
|
||||
assert spec.onion_address.endswith(".onion")
|
||||
serial = spec.serial_log.read_text(errors="replace")
|
||||
assert qemu_config.BOOT_OK_MARKER in serial
|
||||
Reference in New Issue
Block a user