Repository navigation
tests: launch the runtime adapter's VMs and reattach after a restart - #718
Merged
Enrique Saurez (esaurez) merged 2 commits intoOct 22, 2026
Merged
Conversation
Contributor
There was a problem hiding this comment.
🔵 Needs a closer look
Cross-platform VM recovery and guest PID 1 shutdown warrant final human review alongside backend CI results.
No open issues 🎉
What changed in this PR
Extends the independent runtime adapter to exercise NVX’s public supervisor APIs for VM launch, reattachment after an orchestrator restart, and shutdown, advancing #620.
Changes:
- Adds guest control-terminal serving, initramfs customization, and provenance-based runtime identity.
- Adds host lifecycle commands and tests for restart, identity mismatch, and graceful shutdown.
- Wires VM scenarios into backend CI and documents their use.
| File | Description |
|---|---|
| tests/runtime-adapter/tests/vm_restart.rs | Tests cross-process reattachment, mismatch handling, and shutdown. |
| tests/runtime-adapter/tests/runtime.rs | Tests provenance-based runtime identities. |
| tests/runtime-adapter/tests/initramfs.rs | Tests archive contents, alignment, and copying. |
| tests/runtime-adapter/tests/guest_session.rs | Adds terminal tests and child-process cleanup. |
| tests/runtime-adapter/src/runtime.rs | Reads artifact identities from provenance. |
| tests/runtime-adapter/src/lib.rs | Exposes new modules and the guest init command. |
| tests/runtime-adapter/src/initramfs.rs | Builds the guest-agent initramfs overlay. |
| tests/runtime-adapter/src/guest.rs | Adds raw-terminal session serving. |
| tests/runtime-adapter/src/bin/runtime-adapter-host.rs | Adds launch, reattach, and stop commands. |
| tests/runtime-adapter/src/bin/runtime-adapter-guest.rs | Adds terminal init and PID 1 shutdown handling. |
| tests/runtime-adapter/README.md | Documents lifecycle commands and tests. |
| tests/runtime-adapter/Cargo.toml | Enables feature-scoped JSON and terminal dependencies. |
| tests/runtime-adapter/Cargo.lock | Records direct adapter dependencies. |
| scripts/test_nvx_tools.py | Checks guest-agent CI wiring. |
| doc/project-structure.md | Describes adapter lifecycle coverage. |
| doc/ci.md | Documents new validation and backend scenarios. |
| .github/workflows/run-nvx-microvm-tests.yml | Supplies guest agents and artifacts to scenarios. |
| .github/workflows/ci.yml | Builds and uploads the guest agent for Windows jobs. |
Review effort: Balanced
Enrique Saurez (esaurez)
force-pushed
the
esaurez/a1-host-h5-adapter-vm-20261020
branch
from
October 22, 2026 03:51
ec06e90 to
0d3909c
Compare
Base automatically changed from
esaurez/a1-host-h5-adapter-host-20261020
to
dev
October 22, 2026 21:53
`nvx::guest::terminal::Terminal` opens the terminal on which a guest agent serves its control session, waiting while the kernel sets it up during boot, puts it in raw mode, and drains it, so that a guest agent outside NVX serves its session on that terminal as NVX's managed agent does, rather than repeat its settings. The managed agent opens its control terminal through it, and configures and drains it through the same functions. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 76c7c1bb-20fe-4724-bbc0-dfb01bbec828
Enrique Saurez (esaurez)
force-pushed
the
esaurez/a1-host-h5-adapter-vm-20261020
branch
from
October 22, 2026 22:31
0d3909c to
73ee141
Compare
The runtime adapter's host program now launches a VM through NVX's supervisor, with the adapter's guest agent in place of NVX's managed agent, and later processes of it reattach to the VM and stop it, as an orchestrator does after it restarts, through public APIs only. - `runtime-adapter-guest init TTY` serves the control tty, which `guest::serve_terminal` opens with `nvx::guest::terminal`, as the managed agent does, and as PID 1 ends the VM through `/sbin/nvx-exit`. - `runtime_adapter::initramfs` copies NVX's initramfs and appends a `newc` archive with the guest agent, a launcher script, and a symbolic link that replaces `/sbin/nvx-managed-agent`. NVX's own images don't change. - `runtime_adapter::runtime` takes the runtime identity of NVX's artifacts from `ArtifactDir::runtime_identity`, which reads the install metadata beside them and never hashes them, and names the guest agent's build in the identity of the initramfs. - `runtime-adapter-host launch`, `reattach`, and `stop` work on a sandbox store: the launch attaches, exchanges a Ping and an Echo, and commits; a later process reattaches to the VM, which is not its child, attaches in a new epoch, and stops it with the adapter's Stop as the graceful step. A different runtime identity finds the VM mismatched and signals nothing. - `tests/vm_restart.rs` runs these scenarios on a real hypervisor, as ignored tests that the microVM guest and sandbox jobs run on every backend. The Linux jobs build the guest agent, and distro-artifacts builds it on Linux for the Windows job. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 76c7c1bb-20fe-4724-bbc0-dfb01bbec828
Enrique Saurez (esaurez)
force-pushed
the
esaurez/a1-host-h5-adapter-vm-20261020
branch
from
October 22, 2026 22:32
73ee141 to
631406b
Compare
Enrique Saurez (esaurez)
deleted the
esaurez/a1-host-h5-adapter-vm-20261020
branch
October 22, 2026 22:58
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.
The runtime adapter's host program now drives a VM's life through NVX's supervisor, as an orchestrator outside NVX does: it launches a VM whose initramfs runs the adapter's guest agent in place of NVX's managed agent, and later processes of it reattach to the VM after a simulated restart, attach a fresh session, and stop it, through public APIs only. Ignored tests run these scenarios on a real hypervisor, and the microVM guest and sandbox jobs run them on every backend.
Follows #671, which added the adapter's host program.
Part of #620
Refs #535
What changed
nvx::guest::terminal, in its own commit. A public guest library for the terminal on which a guest agent serves its control session:Terminal::openopens it for reading and writing, waiting while the kernel sets it up during boot (it retriesENOENT,ENXIO,ENODEV, andEIOevery 50 ms for 30 s),configure_rawputs it in raw mode withCLOCAL, anddrainwaits until it has sent what the agent wrote. NVX's managed agent now opens its control terminal through it, and puts it in raw mode and drains it through the functions that the library wraps, so a guest agent outside NVX no longer repeats those settings. Unit tests open pseudo-terminals: one that the kernel has not set up yet, every byte value through raw mode, a file that is no terminal, and the errors that end the retries. The crate docs,nvx-rs/README.md, anddoc/project-structure.mdlist it with the other guest libraries.guest::serve_terminalopens a tty withnvx::guest::terminal, as the managed agent does, serves the session across resets, and drains the tty once serving ends.runtime-adapter-guest init TTYruns it, and as PID 1 ends the VM through/sbin/nvx-exit, with 0 once the host stopped it and 1 otherwise, as the managed agent does. The command's name is defined once, asruntime_adapter::GUEST_INIT_COMMANDbesideGUEST_SERVE_COMMAND, and the binary, its usage message, the launcher below, and the tests use it./initrunsexec /sbin/nvx-managed-agent /dev/hvc2 - nvx-managed UID GID NAME HOMEas PID 1.runtime_adapter::initramfs::writecopies NVX's initramfs and appends anewcarchive that Linux unpacks over it: the guest agent, a/bin/shlauncher that runsruntime-adapter-guest init "$1", and a symbolic link from/sbin/nvx-managed-agentto the launcher. It is a link because Linux unlinks a file whose type an archive changes, rather than truncating an inode that other names could share. The archive starts at a multiple of 4 bytes, after zero padding. NVX's own images don't change.runtime_adapter::runtime::Runtime::from_dirfinds the artifacts withnvx::host::artifacts::Artifacts::from_dir, and takes their runtime identity fromArtifactDir::runtime_identity(nvx-rs: prepare the CLI's move onto the store and supervisor #675), which names each artifact by the SHA-256 digest that the install metadata beside it records, never from its contents: a release'sSHA256SUMS, or the build provenance in a checkout'sbuild/, wherenvx.py downloadand CI put it. The adapter changes only the identity of the initramfs, which names NVX's initramfs and the guest agent's build,sha256:DIGEST+runtime-adapter-guest/BUILD, with the crate's version unless--guest-buildnames another, so a host program that ships another build of the guest agent runs another runtime.runtime-adapter-host launch,reattach, andstopon a sandbox store, with NVX's artifacts inNVX_ARTIFACTS_DIR:launchbuilds aLaunchSpecfor the blockless managed root with the guest agent's initramfs, which it writes once the launch has checked that no VM of the sandbox runs, launches throughsupervisor::launch, connects withnvx::host::session::connect, attaches, exchanges aPingand the longestEcho, which spans five records each way, commits, and exits.reattachtakes the lock and callssupervisor::reattach, which holds the VMM although it is not the process's child. It reads the capability from the store, attaches a fresh session, in a new epoch, and exchanges aPing.stopdoes the same, then callsVmm::stopwith the adapter'sStop, answered withStopped, as the graceful step, waits for the exit, and checks the outcome report:success, status 0, and a complete teardown. A VM whose guest agent doesn't confirm is terminated, and the command fails.mismatchedwith the parts that differ, exits with 3, and signals nothing.tests/vm_restart.rs, ignored, reads the backend throughnvx::Hypervisor::from_env_or_default. One test launches, then reattaches from a second process (the same VMM and broker instance, a later epoch), then stops from a third (graceful,success 0, a complete teardown, and no runtime record left). The other reattaches with another guest build:mismatchedininitramfs, the VM still runs under the same runtime record, and the right runtime then stops it gracefully. Each command is a separate process with a timeout. A failed test ends the VM that it left, and keeps the sandbox's directory, whose path it prints, unless it can confirm that the VM ended.tests/initramfs.rsandtests/runtime.rs, host-only, run on Linux and Windows: the archive's entries, modes, owners, and padding; the copy and its alignment for every base length modulo 4; and the runtime identity of a checkout and of a release, which differs from NVX's in the initramfs alone, of another build of the guest agent, which differs there too, of builds that no artifact identity can hold, and of missing install metadata, which fails as NVX reports it.tests/guest_session.rs: through a relay to a pseudo-terminal,serve_terminalandruntime-adapter-guest initpass anEchoof every byte value, so the tty is in raw mode, andinitfails at once on a path that is no tty. A failed test kills the guest binary that it started, and the pseudo-terminal's descriptors are closed on exec, so that no process holds its master end.nvx-microvm-guests-{kvm,mshv,whp}, run the scenarios, and the default and debug-kernel suites neither build nor run them. The hook inrun-nvx-microvm-tests.ymlsetsNVX_ARTIFACTS_DIRto the checkout, into which the job downloads the artifacts and their provenance, andRUNTIME_ADAPTER_GUESTto the guest agent. The Linux jobs build the guest agent as a static musl executable first.distro-artifacts, which uploads the Ubuntu and Azure Linux images that the Windows job takes, also builds the guest agent on Linux and uploads it asruntime-adapter-guest, which that job downloads. In 8 of 10 recent runs that I sampled,distro-artifactsfinished 3.5 to 8.4 minutes beforenvx-cli-windows, which the Windows job also needs, so this build, about a minute, seldom delays it. The build runs wheneverdistro-artifactsruns, because a job may read only its own change detector's output, astest_ci_jobs_follow_their_change_detector_outputchecks.scripts/test_nvx_tools.pychecks this wiring, anddoc/ci.mddescribes it.nix(term), a Linux dev-dependency thatnvxalready pulls in, and the guest-only build still pulls in no host-only dependency.nvx-rs/README.md, thenvxcrate docs,doc/ci.md, anddoc/project-structure.md.Validation
All of the following ran on this head's tree; the last push only restored a blank line between two jobs in
ci.yml, after which the workflow tests ofscripts/test_nvx_tools.pyran again.On Windows 11, in
tests/runtime-adapter, for the protocol-only and host-only configurations:cargo fmt --all --checkcargo clippy --locked --all-targets -- -D warnings, and the same with--features hostcargo build --lockedandcargo test --locked, and the same with--features hostcargo +1.89 check --locked --all-targets, and the same with--features hostRUSTDOCFLAGS="-D warnings" cargo doc --locked --no-deps --features hostOn an Azure Linux 3.0 host, the Linux steps of
check-nvx-rsandcheck-aci-edge-sandboxes:nvx-rs: formatting, both Clippy runs,cargo test --locked --all-features(886 tests) andcargo test --locked(424), the bundled-artifacts test, rustdoc, the MSRV check, theaarch64-apple-darwincheck, andnvx.py check-guest-binaries,build-guest-binaries, andtest-guest-binariestests/runtime-adapter: Clippy, the build, the tests, and the MSRV check of all three configurations, the guest-only one forx86_64-unknown-linux-musl; formatting; rustdoc of the host-only and guest-only configurations; and the release build of the guest agentaci_edge_sandboxes, which builds onnvx: formatting, both Clippy runs, both test runs,nvx.py test-agent-bundling, rustdoc, the MSRV check, and theaarch64-apple-darwincheckFrom the checkout's root, on Windows 11, with Git for Windows'
bashfirst onPATH, as on the hosted runners:validate-nvxsteps:compileall, theunittestrun of its eleven suites (1250 tests, 39 skipped),test_hosts.py, and eachnvx.py --helpthat it runspython -m ruff checkandpython -m ruff format --checkonscripts .github/specula, andpython -m pyright --pythonplatform Linuxand--pythonplatform Windows, withrequirements-dev.txtpython scripts/check_markdown_links.pyandpython scripts/nvx.py verifyscripts/test_adversarial.pyfails four of its process-containment tests on this machine atdev's head too, so it is left to CI.On real hypervisors,
cargo test --locked --features host --test vm_restart -- --ignoredpassed both tests, and left no VM or sandbox directory behind, with the guest agent fromcargo build --locked --release --features guest --target x86_64-unknown-linux-musl --bin runtime-adapter-guest:v0.1.0-dev.8483e3757469, whichpython scripts\nvx.py downloadinstalled in the checkout, so the identity came from their build provenance.SHA256SUMS. That host's CPU cannot present NVX's built-in CPU profiles, so a local wrapper aroundopenvmmadded--cpu-profile host. The CI runners use the built-in profiles.dev, with the adapter's earlier identity code. CI runs this head on KVM.With injected failures, on an earlier head whose code for them is unchanged:
cfmakerawin the raw-mode settings, the library's test that passes every byte value fails, and so do both of the guest agent's tty tests.Left to CI: the microVM guest and sandbox jobs on KVM, MSHV, and WHP with the built-in CPU profiles, and
distro-artifacts' build and upload of the guest agent.