Skip to content

tests: launch the runtime adapter's VMs with a layered root - #773

Merged
Enrique Saurez (esaurez) merged 2 commits into
devfrom
esaurez/adapter-layered-vms-20261021
Oct 23, 2026
Merged

Enrique Saurez (esaurez) merged 2 commits into
devfrom
esaurez/adapter-layered-vms-20261021

Conversation

@esaurez

@esaurez Enrique Saurez (esaurez) commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

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::root in 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 typed SandboxPlan, 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, versioned NAME=VALUE text, over the adapter's framing after attach. A new probe 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), and ProbeFailed.

  • Initramfs overlay: NVX's /init runs exec /sbin/nvx-init-agent without arguments for nvx_sandbox=1. Besides the blockless launcher for /sbin/nvx-managed-agent, the appended archive now replaces /sbin/nvx-init-agent with /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 through nvx::guest::terminal, attaches, checks the root configuration, reads the boot layout with RootLayout::parse, runs bootstrap and assemble, and answers RootReady, or RootFailed with the stage that failed: configuration, layout, bootstrap, assembly, or teardown, which end the VM with 11 to 15. It then serves Ping, Echo and Probe, and on Stop tears the root down, reporting a teardown that failed before it confirms.

  • Scenarios: tests/vm_layered.rs declares 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_stage and busy_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_json for the launch record, and the guest-only one takes nix's process and signal features for PID 1, at the releases that nvx already 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.md and doc/project-structure.md describe the layered mode.

Validation

  • Windows 11 (MSVC): 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.
  • Linux, as a regular user: cargo fmt --all --check; Clippy with warnings denied, and the tests, of all three configurations, the guest-only one for x86_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; in nvx-rs, cargo test --locked --all-features with NVX_BUNDLE=skip.
  • Real VMs: 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 earlier dev. 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 that validate-nvx runs, those that run shell scripts with Git Bash, as on CI's Windows runners; ruff check and ruff format --check; python scripts/check_markdown_links.py.
  • Left to CI: the microVM jobs on the shared KVM, MSHV and WHP runners, with their built-in CPU profiles, and the quality checks of files that this change doesn't touch, such as pyright, shellcheck and shfmt.

@esaurez
Enrique Saurez (esaurez) requested a balanced review from Copilot October 22, 2026 22:54
Base automatically changed from esaurez/a1-host-h5-adapter-vm-20261020 to dev October 22, 2026 22:58

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 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
@esaurez
Enrique Saurez (esaurez) force-pushed the esaurez/adapter-layered-vms-20261021 branch from 867fdd9 to 0a89f7b Compare October 22, 2026 23:26
Copilot AI balanced review requested due to automatic review settings October 22, 2026 23:26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Privileged PID 1 supervision and mount teardown warrant human review alongside exact-head KVM, MSHV, and WHP CI results.

No open issues 🎉

Review effort: Balanced

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
Copilot AI balanced review requested due to automatic review settings October 23, 2026 00:14

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Privileged mount and cgroup operations, PID 1 supervision, and cross-backend teardown need final human review alongside the backend validation results.

No open issues 🎉

Review effort: Balanced

@esaurez
Enrique Saurez (esaurez) merged commit 3951066 into dev Oct 23, 2026
42 checks passed
@esaurez
Enrique Saurez (esaurez) deleted the esaurez/adapter-layered-vms-20261021 branch October 23, 2026 03:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants