Skip to content

nvx-rs: build the plain spawn descriptors from the managed ones - #611

Merged
Pedro Henrique Penna (ppenna) merged 1 commit into
devfrom
nvx-rs-quality/plain-spawn-descriptors
Oct 19, 2026
Merged

Pedro Henrique Penna (ppenna) merged 1 commit into
devfrom
nvx-rs-quality/plain-spawn-descriptors

Conversation

@ppenna

Copy link
Copy Markdown
Contributor

Summary

SpawnDescriptors, in nvx-rs/src/guest/sys/spawn.rs, tells the forked child what to do with each descriptor of a workload. It has two constructors:

  • managed, with which the managed agent spawns every workload;
  • plain, compiled only into test builds with the host feature, with which the loopback test spawns its workloads without privileges.

plain repeated managed's construction of all seven descriptor fields and differed from it only in inherit_config. It is now managed's action set with inherit_config: false, written with struct update syntax, and its comment names that one difference: the child closes the configuration descriptor instead of keeping it across exec. A field added to the struct now needs one constructor updated, and the difference no longer hides among the repeated fields.

This is the seventh nvx-rs-quality/ pull request. #603, #605, #606, #607, and #608 merged, #608 while this run was underway, and #609 is open; all had approving Copilot reviews and no other feedback to follow, and none was closed without merging. Those runs changed protocol/exec.rs, the managed agent's exec.rs, loopback.rs, and seam.rs, host/filesystem.rs, the time tools, and guest/sys/socket.rs. This run surveyed the crate by reading and with a throwaway duplicate-window script, exact and with names and literals abstracted, and skipped every file that an open pull request to dev changed when it started: #608 (guest/time/commands.rs, daemon.rs, and system.rs), #609 (guest/sys/socket.rs), #522 (tests/cli.rs), and #585 (aci_edge_sandboxes/src/async_api.rs and tests/async_api.rs). Of the candidates that it found, this one, a follow-up that #609 listed, removes the most duplicated code per changed line, in a file that no earlier run touched. The host-side candidates, in the part of the crate that earlier runs covered least, removed less per changed line; they are listed under Follow-ups.

Changes

All changes are in nvx-rs/src/guest/sys/spawn.rs: 4 insertions and 8 deletions.

  • SpawnDescriptors::plain returns Self { inherit_config: false, ..Self::managed(config, stdout, stderr, launch) } instead of listing every field, and its doc comment says how it differs from managed.
  • Sites that now share managed's construction of the descriptor fields:
    • SpawnDescriptors::managed, which guest/managed_agent/launch.rs calls for every managed workload, as do the tests of guest/sys/spawn.rs;
    • SpawnDescriptors::plain, which spawn_unprivileged in guest/managed_agent/loopback.rs calls.

No signature, public item, message, error kind, protocol byte, fixture, or other file changes.

Behavior

  • managed is unchanged. plain returns the same value in every field as before: the same seven descriptors from the same arguments, and inherit_config: false. child_exec therefore still closes the configuration descriptor in a plain child, and still clears its close-on-exec flag in a managed one.

  • plain is #[cfg(all(test, feature = "host"))], so no library, CLI, or guest binary build includes it. The command lines, exit statuses, and byte output of every guest binary are unchanged, and the --no-default-features build does not change.

  • Tests that cover the change:

    • guest::managed_agent::loopback::tests::control_session_broker_and_real_agent_loop_back spawns its workloads through plain;
    • guest::sys::spawn::tests build their descriptors with managed.

    Both ran and passed in the Linux test run with all features on prometheus32, and the spawn tests also in the musl run of check-guest-binaries.

Follow-ups

The survey turned these up, and this change leaves them alone. I found no suspected bug.

  • Managed-agent test fixtures: cgroup::tests::setup and dispatch::tests::direct_system are identical, dispatch::tests::mapping_system builds the host-export tree of mappings::tests::setup, and dispatch::tests::maps_payload and mappings::tests::payload are the same MAPS encoder. Constructors next to TestSystem in seam.rs could serve them, in about 160 changed lines.
  • The main of each of the nine guest programs restores SIGPIPE and converts its outcome with ExitStatus::from(...).into(); four of them are identical. One entry helper in guest/status.rs could hold that convention.
  • In host/:
    • process::tests::a_process_runs_until_its_last_thread_exits and cli::sandbox::identity::tests::a_vm_runs_until_its_last_thread_exits repeat the fixture of a Python process whose main thread exits first, with identical Reaped guards and wait loops.
    • In session.rs, features, map_host_paths, and next_exec_event repeat the request-ID check, map_host_paths and next_exec_event truncate an error category to 64 characters the same way, and the tests' Scripted and Responder transports have identical read methods.
    • filesystem.rs, network/mod.rs, and sandbox/options/tests.rs each define the same option-values test helper.
  • In testing/fake_openvmm.rs, sandbox_shares and mapped_children parse --mount-child values with the same code.
  • These follow-ups of earlier runs remain:
    • Instance::runtime and Instance::launch in host/instance.rs;
    • the failure-reporting closures of console_pending.rs and mmio_write.rs;
    • run_file and run_network in guest/device_io.rs;
    • the device_io helper in tests/guest_tools.rs, which repeats the spawn and output collection of run;
    • the time tools' follow-ups of nvx-rs: report time-ABI repair failures through one Runtime method #608, which has now landed: the three watcher-violation sites in daemon.rs, and the port-window lock failures that cmd_sample and cmd_generation_id in commands.rs report with the same four lines.

Validation

  • BASE: 77f84a4d89d7dc9996c76ed71a5d1b2cc6f917e0, which was origin/dev when the run started: the merge of Repin OpenVMM to microVM v2.2 #600, which moved the OpenVMM pin. Before this pull request was opened, origin/dev had advanced by the merge of nvx-rs: report time-ABI repair failures through one Runtime method #608 and a performance-baseline update, neither of which changes nvx-rs/src/guest/sys/spawn.rs, and no open pull request to dev changed it.

  • TREE: 76b62eaccfcd0ecbcac0b735678890916c3a5de7, the tree of this commit. Every host applied the patch to BASE and checked that git write-tree equals TREE.

  • Release on every host: v0.1.0-dev.77f84a4d89d7, BASE's own release. The newest release when the run started predated Repin OpenVMM to microVM v2.2 #600's pin, so the hosts waited until CI published this one. Its OpenVMM, d0bdbed8f6e3b86271277dc22658109e2e633b2d, is the pinned revision.

  • Each host rebuilt nvx-rs from TREE and recorded its provenance, since nvx.py runs the nvx-rs/target/release/nvx-rs that it finds, and pre-built the managed_lifecycle and openvmm_e2e test binaries, so that no Cargo build overlapped its hypervisor tests.

  • Guest artifacts were rebuilt from TREE on prometheus30, with these steps:

    • build-guest-binaries --output;
    • test-guest-binaries --binaries, where 34 tests passed;
    • the Alpine build-initramfs with NVX_GUEST_BINARIES.

    initramfs.cpio.gz (SHA-256 5b5c7d8aada348d371b3321e5794cd9c90e39348f3f0c8e22e8f97ce1923a080), its packages manifest, and its provenance went to the other two hosts, where the SHA-256 digests matched.

  • Local checks on Windows with Rust 1.95.0:

    • rustfmt;
    • Clippy with -D warnings: for x86_64-unknown-linux-gnu, which builds the guest module, with all targets and features and for the library without default features; for the guest build on x86_64-unknown-linux-musl, as check-guest-binaries lints it; and natively, in both configurations;
    • the steps that no host could run, because the hosts lack the toolchain or target: the Windows MSRV (1.89) checks and the macOS (aarch64-apple-darwin) checks, for both nvx-rs and aci_edge_sandboxes.
Host profile Backend Host type check-nvx-rs and check-aci-edge-sandboxes without a hypervisor Guest artifacts test-aci-edge-sandboxes test-microvm, default suite
prometheus32 kvm baremetal Pass: every Linux step, including MSRV 1.89, check-guest-binaries (318 tests), 607 nvx-rs tests with all features and 263 with the default ones, artifact bundling, 157 and 85 aci_edge_sandboxes tests, and agent bundling; the macOS checks ran locally Copied in, digests match Pass (18/18) Pass (41 scenario runs)
prometheus30 mshv baremetal None; prometheus32 and prometheus28 ran the gates Built and tested here Pass (18/18) Pass (41 scenario runs)
prometheus28 whp baremetal Pass: every Windows step, including 331 nvx-rs tests with all features and 263 with the default ones, artifact bundling, 157 and 85 aci_edge_sandboxes tests, and agent bundling; the MSRV checks ran locally Copied in, digests match Pass (18/18) Pass (42 scenario runs)

The Linux gates and tests ran in the foreground of their SSH sessions, with every signal at its default disposition (SigIgn: 0), so the CLI's Ctrl-C tests ran as intended. Every gate and test passed at its first attempt, no host fell back to a host CPU profile, and each host's time-ABI evidence line counted no boot, capture, or restore over its CPU-time budget.

The run lock and prometheus28's host lock held the ID of an earlier run that had stopped without releasing them, which its operator confirmed. Both locks were younger than the four hours after which a run takes one over, so I renamed them aside and took them over only after checking that no process ran from that host's worktree and that the other two hosts held no lock.

Left to CI:

  • the OpenVMM unit and VMM suites, test-openvmm-unit and test-openvmm;
  • the microVM scenarios that run only when named: smp-lapic, managed-exec-config, and sandbox-lifecycle;
  • the debug-kernel scenarios, the Ubuntu and Azure Linux guests, and guest determinism;
  • the benchmarks;
  • the virtual-machine runners;
  • check-quality and validate-nvx, whose inputs this change does not touch.

SpawnDescriptors, in guest/sys/spawn.rs, has two constructors: managed,
with which the managed agent spawns every workload, and plain, with
which only the loopback test spawns its workloads without privileges.
plain repeated managed's construction of all seven descriptor fields
and differed from it only in inherit_config, so a field added to the
struct had to be added to both, and the one difference hid among the
repeated fields.

plain now takes managed's action set and overrides inherit_config with
struct update syntax, and its comment names that difference: the child
closes the configuration descriptor instead of keeping it across exec.
The two constructors can no longer drift apart.

Behavior is unchanged: managed is untouched, and plain returns the same
value in every field as before. plain is compiled only into test builds
with the host feature, so no library, CLI, or guest binary build
changes. The loopback test that spawns its workloads through plain,
control_session_broker_and_real_agent_loop_back, and the spawn tests,
which use managed, cover both constructors.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 19, 2026 22:04

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.

🟢 Approval recommended

The narrow, test-only refactor preserves descriptor values and configuration-close behavior, with no unresolved findings.

No open issues 🎉

What changed in this PR

Simplifies test-only spawn descriptor construction by sharing the managed constructor while preserving plain-child behavior.

Changes:

  • Reuses managed through struct update syntax, retaining inherit_config: false.
  • Clarifies the configuration descriptor’s handling in the doc comment.
File Description
nvx-rs/​src/​guest/​sys/​spawn.rs Removes duplicated descriptor initialization from plain.

Review effort: Balanced

@ppenna
Pedro Henrique Penna (ppenna) merged commit f10b7ba into dev Oct 19, 2026
33 checks passed
@ppenna
Pedro Henrique Penna (ppenna) deleted the nvx-rs-quality/plain-spawn-descriptors branch October 19, 2026 23:26
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