Repository navigation
tests: launch the runtime adapter's VMs with a layered root - #773
Merged
Enrique Saurez (esaurez) merged 2 commits intoOct 23, 2026
Merged
Conversation
Base automatically changed from
esaurez/a1-host-h5-adapter-vm-20261020
to
dev
October 22, 2026 22:58
Contributor
There was a problem hiding this comment.
🔵 Needs a closer look
PID 1 supervision, privileged root assembly and teardown, and lifecycle behavior across three backends warrant final human review alongside backend CI results.
No open issues 🎉
What changed in this PR
Extends the independent runtime adapter to launch layered-root VMs through NVX’s public APIs. This advances #621 by exercising guest root assembly outside PID 1, before NVX’s Rust layered initializer lands.
Changes:
- Adds typed launch planning, persisted launch records, root configuration messages, and host probe commands.
- Introduces PID 1 supervision and a separate worker for root assembly, inspection, teardown, and stage-specific failure reporting.
- Adds layered VM scenarios and protocol tests, reuses restart-test helpers, and documents coverage on KVM, MSHV, and WHP.
| File | Description |
|---|---|
| tests/runtime-adapter/tests/vm_restart.rs | Reuses shared VM lifecycle helpers. |
| tests/runtime-adapter/tests/vm_layered.rs | Adds layered-root success and failure scenarios. |
| tests/runtime-adapter/tests/layered_launch.rs | Tests launch plans, records, arguments, and option validation. |
| tests/runtime-adapter/tests/initramfs.rs | Checks both guest-agent launcher replacements. |
| tests/runtime-adapter/tests/host_session.rs | Tests root configuration, probes, and failure replies. |
| tests/runtime-adapter/tests/common/mod.rs | Extracts shared VM command and cleanup helpers. |
| tests/runtime-adapter/src/worker/probes.rs | Inspects mounts, contents, cgroups, and processes. |
| tests/runtime-adapter/src/worker.rs | Adds supervision and worker-owned root operations. |
| tests/runtime-adapter/src/root.rs | Defines root configuration and stage-failure encodings. |
| tests/runtime-adapter/src/probe.rs | Defines probe requests and escaped fact reports. |
| tests/runtime-adapter/src/lib.rs | Exposes layered modules and guest command constants. |
| tests/runtime-adapter/src/layered.rs | Builds typed launch plans and persists launch records. |
| tests/runtime-adapter/src/initramfs.rs | Adds the layered launcher and init-agent replacement. |
| tests/runtime-adapter/src/host.rs | Adds root configuration, probe, and teardown-failure handling. |
| tests/runtime-adapter/src/guest.rs | Generalizes session handling for both root modes. |
| tests/runtime-adapter/src/frame.rs | Adds six message kinds and body-size limits. |
| tests/runtime-adapter/src/bin/runtime-adapter-host.rs | Adds layered launch options, probes, and outcome checks. |
| tests/runtime-adapter/src/bin/runtime-adapter-guest.rs | Dispatches layered init, worker, and idle commands. |
| tests/runtime-adapter/Cargo.toml | Adds feature-scoped JSON and process-control dependencies. |
| tests/runtime-adapter/Cargo.lock | Records the adapter’s JSON dependency. |
| doc/project-structure.md | Describes the layered worker and scenario structure. |
| doc/ci.md | Documents layered-root tests and backend coverage. |
| .github/workflows/run-nvx-microvm-tests.yml | Updates comments only to describe layered scenarios. |
Review effort: Balanced
The runtime adapter now launches VMs with a real layered root, which its own guest assembles through nvx::guest::root in a worker process, never PID 1. The guest library's root operations get real-kernel coverage before NVX has a Rust PID 1, and later root modes get a harness for their scenarios. - The host program plans the VM with NVX's typed SandboxPlan, launches it through NVX's supervisor with the OpenVMM arguments of NVX's sandbox start, and records the launch in its store. The boot tokens carry only what the boot contract defines; the rest of what the root needs goes in one root configuration message of the adapter's framing, after attach. - The appended initramfs archive also replaces /sbin/nvx-init-agent with a launcher that names the control tty itself, so that NVX's init agent never assembles the root, and keeps the blockless launcher. - PID 1 starts an unrelated child and the worker, collects every child's status, and ends the VM with the worker's. The worker attaches, checks the root configuration, reads the boot layout, bootstraps and assembles the root, answers RootReady or RootFailed with the stage that failed, serves pings, echoes and probes, and on Stop tears the root down and reports a teardown that failed. - A table of VM scenarios checks EROFS over an ext4 scratch through the worker's probes, and that a wrong image identity, an invalid root configuration, and a busy root's teardown each reach the host program with their stage while the VM still ends cleanly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: ddd9400a-9806-4d3d-b2cb-4320252cb090
Enrique Saurez (esaurez)
force-pushed
the
esaurez/adapter-layered-vms-20261021
branch
from
October 22, 2026 23:26
867fdd9 to
0a89f7b
Compare
Enrique Saurez (esaurez)
marked this pull request as ready for review
October 22, 2026 23:34
The layered scenarios whose launch fails returned before they checked the launch's record, although the adapter's README says that each scenario checks it. They now check it too: the record holds the plan, with the image identity that the launch expected, and the root configuration that the host sent, whether or not the guest accepted it. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: ddd9400a-9806-4d3d-b2cb-4320252cb090
Enrique Saurez (esaurez)
deleted the
esaurez/adapter-layered-vms-20261021
branch
October 23, 2026 03:00
This was referenced Oct 23, 2026
Open
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #621
Refs #620, #622, #535
The runtime adapter now launches VMs with a real layered root, which its own guest assembles through
nvx::guest::rootin a worker process, never PID 1. Until NVX's Rust PID 1 lands, the guest library's root operations get real-kernel coverage on KVM, MSHV and WHP through public APIs only, and the next root modes get a scenario table to extend.What changed
Host program:
runtime-adapter-host launch ... --distro FORMAT:PATH [--distro-identity UUID] --upper (scratch:PATH | ram) [--runtime-dir DIR]plans the VM with NVX's typedSandboxPlan, launches it through NVX's supervisor with the OpenVMM arguments of NVX's own sandbox start, and records what it launched in the sandbox's store (runtime-adapter.json). The boot tokens carry only what the boot contract defines. Everything else that the root needs goes in one root configuration message, versionedNAME=VALUEtext, over the adapter's framing after attach. A newprobe STORE PROBE...command runs the worker's probes. A command whose guest reported a failed stage exits with 4, once the VM ended with the stage's status.Framing: kinds 6 to 11:
RootConfig,RootReady,RootFailed(the stage, then the problem),Probe,Report(the probe's facts), andProbeFailed.Initramfs overlay: NVX's
/initrunsexec /sbin/nvx-init-agentwithout arguments fornvx_sandbox=1. Besides the blockless launcher for/sbin/nvx-managed-agent, the appended archive now replaces/sbin/nvx-init-agentwith/sbin/runtime-adapter-layered,exec /sbin/runtime-adapter-guest init /dev/hvc2 layered, so that NVX's init agent never assembles the root in the adapter's VMs.PID 1 and the worker: PID 1 starts an unrelated child and the worker, collects every child's status, and ends the VM with the worker's status through
/sbin/nvx-exit. The worker opens the control tty throughnvx::guest::terminal, attaches, checks the root configuration, reads the boot layout withRootLayout::parse, runsbootstrapandassemble, and answersRootReady, orRootFailedwith the stage that failed: configuration, layout, bootstrap, assembly, or teardown, which end the VM with 11 to 15. It then servesPing,EchoandProbe, and onStoptears the root down, reporting a teardown that failed before it confirms.Scenarios:
tests/vm_layered.rsdeclares a table of scenarios, each an ignored test:erofs_over_scratch: the mounts and their flags, the merged root's contents and writes, the agent's and workload's cgroups, PID 1's unrelated child, untouched, and a clean teardown;other_identity_fails_the_assembly,invalid_configuration_fails_its_stageandbusy_root_fails_the_teardown: each failure reaches the host program with its stage, and the VM still ends cleanly.The fixtures are those of NVX's own layered scenarios: the Ubuntu EROFS image, its manifest, and a copy of the scratch template.
Guest tests: each guest binary that a test starts runs in a process group of its own. The test collects the binary's status only after it has killed the group, so the group keeps its ID even after the binary exits, and PID 1's children never outlive the test, even if PID 1 dies first. A binary that exits but leaves processes of its group running fails its test.
Dependencies: the host-only configuration takes
serde_jsonfor the launch record, and the guest-only one takesnix'sprocessandsignalfeatures for PID 1, at the releases thatnvxalready uses.CI: the microVM workflow already runs the adapter's ignored tests on KVM, MSHV and WHP, in the job that downloads the Ubuntu distro layer and the scratch template into
build/; only its comment changes. The adapter's README,doc/ci.mdanddoc/project-structure.mddescribe the layered mode.Validation
cargo fmt --all --check; Clippy with warnings denied, the build, and the tests of the protocol-only and host-only configurations; their checks with the minimum Rust version, 1.89.cargo fmt --all --check; Clippy with warnings denied, and the tests, of all three configurations, the guest-only one forx86_64-unknown-linux-musl, which stays a static executable; the guest session tests three times, with no guest process left behind; their checks with Rust 1.89; innvx-rs,cargo test --locked --all-featureswithNVX_BUNDLE=skip.cargo test --locked --features host -- --ignored, the 4 layered and 2 restart scenarios with parallel test threads, passed three runs each on WHP (Windows 11) and on a Linux/MSHV host, as root, with the host program and guest agent built from this head and NVX's artifacts from release packages of an earlierdev. KVM passed six runs on a Linux/KVM host on an earlier base of the same change. The Linux hosts ran OpenVMM with--cpu-profile host, because their CPUs have no built-in profile.python scripts/nvx.py verify;python -m compileall -q scripts; the unittest modules thatvalidate-nvxruns, those that run shell scripts with Git Bash, as on CI's Windows runners;ruff checkandruff format --check;python scripts/check_markdown_links.py.