Skip to content

runtime-adapter: start and run from a template, and run the stage's scenarios as one suite - #963

Merged
Pedro Henrique Penna (ppenna) merged 5 commits into
devfrom
esaurez/847-pr2-start-run-suite-20261024
Oct 29, 2026
Merged

Pedro Henrique Penna (ppenna) merged 5 commits into
devfrom
esaurez/847-pr2-start-run-suite-20261024

Conversation

@esaurez

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

Copy link
Copy Markdown
Contributor

Part of #847 (PR 2 of 2: start and run from a template, and the stage's suite).

Stacked on #962.

Chain: #951 → #954 → #957 → #959 → #960 → #955 → #958 → #962 → this PR, on dev; #879, #881, #885, #886, #877, #932, #884, #887, #933, #880, #936, #937, #939, #940, #941, #888, #949, and #950 have merged.

What and why

#847 asks the runtime adapter to implement every template and clone operation through public APIs and its own framing, and to run the stage's scenarios as one suite. Below this PR, the adapter's host program creates, refreshes, lists, inspects, and removes templates (#962), and starts clones of them (#954), with exports and networks (#955, #958). This PR completes the operations table: typed outcomes for a start's selection, and a run command. It also runs every scenario of the stage by name in CI.

Start: the selections that the template store refuses

A start whose template store refuses its selection ends with status 10, before it leases or launches anything. It prints the refusal's record, in the form of a clone failure's, at the stage selection:

  • no-template: a template store that does not exist (no-store), or a generation that the store never published (unpublished) or no longer holds (retired);
  • expired: a generation whose capture the restore window of its time contract no longer admits at the host's time;
  • ineligible: a generation that a tombstone quarantines (tombstone), or whose capture is after the host's time (early).

The record's code is the code of NVX's error. Without --memory, the start reads the generation's compatibility key before the lease, for the template's RAM, but a key that it cannot read gives no memory target rather than a failure, and the lease decides. A selection that races a retirement, or follows one that a crash interrupted, then ends as the store's refusal rather than as a failed read. A clone that NVX's plan refuses after the lease, such as one whose key cannot be read or whose network policy is not the template's, still ends with status 8.

Run: one execution in a clone

runtime-adapter-host run takes the options of start, then one execution, as exec takes it:

  1. It plans the clone, its network, and the export of its shares, then stages the clone's images in a visible preflight (preflight=staged), so the start hashes nothing.
  2. It starts the clone as start does, through the same code. It ends as a start ends when the store refuses the selection (10) or the clone fails (8, or 9 while the teardown is unresolved).
  3. It runs the execution in the clone's root, through authenticated execution control: the program, its arguments, and its environment reach the guest in the execution's start, never in the template's boot tokens.
  4. Whatever the execution did, it stops the clone as stop does and releases the lease. It prints the execution's result and the teardown's outcome.
  5. An execution that failed to run, such as one whose session ended, fails the run after the teardown. Since the session may have ended with the VM, as when its VMM crashed, the run first waits for the VMM to exit, as long as a stop waits for one that it terminated, so that the stop records the clone's failure rather than take the exit for its own. The run then exits with the stop's status: 8, or 9 while the teardown is unresolved, or 4 once the root failed, and 1 otherwise.

It never captures, boots cold, rehashes, or falls back to another format. Its executions find the policy's proxy, and the clone's shares beneath its root, as the executions of a started clone do.

The stage's suite, by name

tests/runtime-adapter/scenarios.txt names the stage's consolidated suite: 155 scenarios in 11 test targets. They cover every operation's outcomes, from create to run, without a hypervisor and in VMs, and every scenario of vm_template, vm_clone, vm_layered, vm_restart, and vm_steps. The new nvx.py test-runtime-adapter --backend {kvm,mshv,whp} runs it, in place of cargo test -- --ignored, in the microVM workflow's runtime adapter steps. It fails when:

  • a line names a test that its target does not have, or a named target has a test that no line names;
  • the library, or a test target that no line names, has an ignored test, with the targets as Cargo's metadata lists them;
  • a named test does not report ok, or a target passes fewer tests than it names, so a run of no test fails.

--check-only checks the names alone. The README's "Stage scenarios" table lists every scenario with what it proves, in the manifest's order, which a unit test keeps in step.

Commits

  1. runtime-adapter: type the selections that a template store refuses
  2. runtime-adapter: run one execution in a clone of a template
  3. runtime-adapter: give a run the network and shares of its start
  4. runtime-adapter: run executions in clones of network templates and with shares, in VMs
  5. runtime-adapter: run the stage's scenarios as one suite, by name

Validation

At this PR's head f43428e, the same five commits as the earlier head 918e6fc, restacked onto #962's head 43879d7: its tree, 82135bf4, is the merge of 918e6fc into dev at fbeb563, which holds #951, #954 and #957, plus #955's fix that treats a path whose links loop as a changed object. That merge, without the fix, passed the documentation gates of nvx-rs and of the test adapter, Clippy, validate-nvx on Linux and on Windows, and the Markdown link check; the fix has its own tests in #955. The rest of this section describes 918e6fc, on #962's earlier head 32942c3.

Pending at 918e6fc.

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.

🟡 Changes recommended

Relative NVX_ARTIFACTS_DIR paths remain unresolved when Cargo runs in tests/runtime-adapter, causing VM tests to look in the wrong directory and potentially fail despite artifacts being present.

1 open issue
What changed in this PR

Completes the runtime adapter’s template and clone operations for #847 by adding a one-execution run command, typed selection refusals, and a named acceptance suite.

Changes:

  • Reports template-selection refusals separately from clone failures.
  • Runs one authenticated execution in a clone, preserving its network and shares, then tears it down and releases its lease.
  • Adds a manifest of 153 scenarios across 10 targets and integrates name and result checks into CI.
File Description
tests/​runtime-adapter/​tests/​vm_clone.rs Tests selection refusals and clone runs, including networks and shares.
tests/​runtime-adapter/​tests/​host_session.rs Tests shutdown with late execution messages.
tests/​runtime-adapter/​tests/​clone_start.rs Tests run options, preflight, and selection refusals.
tests/​runtime-adapter/​src/​lib.rs Documents running an execution in a clone.
tests/​runtime-adapter/​src/​host/​executions.rs Identifies messages belonging to tracked executions.
tests/​runtime-adapter/​src/​host.rs Allows shutdown to skip accepted late messages.
tests/​runtime-adapter/​src/​bin/​runtime-adapter-host.rs Implements selection outcomes and the shared start/run lifecycle.
tests/​runtime-adapter/​scenarios.txt Names the consolidated stage suite.
tests/​runtime-adapter/​README.md Documents operations, outcomes, and every stage scenario.
scripts/​test_nvx_tools.py Tests suite validation, execution checks, and CI wiring.
scripts/​nvx.py Registers test-runtime-adapter.
scripts/​nvx_tools/​runtime_adapter_tests.py Validates scenario coverage and runs named tests.
scripts/​nvx_tools/​ci.py Makes suite-runner changes trigger microVM tests.
doc/​usage.md Documents the new suite command.
doc/​contribute.md Updates contributor guidance for running the suite.
doc/​ci.md Describes named runtime-adapter coverage in CI.
.github/​workflows/​run-nvx-microvm-tests.yml Runs the consolidated suite on Linux and Windows.
.github/​actions/​validate-nvx/​action.yml Checks the new command’s help on both platforms.

Review effort: Balanced

Comment thread scripts/nvx_tools/runtime_adapter_tests.py Outdated
@esaurez
Enrique Saurez (esaurez) requested a balanced review from Copilot October 28, 2026 18:32

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.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

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

The stacked base has five acknowledged OpenVMM-pin-dependent failures, and the final rebased head still needs the full 153-scenario suite validated on KVM, MSHV, and WHP.

No open issues 🎉

Review effort: Balanced

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.

🟡 Changes recommended

The execution-error path discards teardown status 8 and exits with 1, losing the typed clone-failure outcome; it should preserve that status and add coverage for a clone dying during execution.

1 open issue

Review effort: Balanced

Comment thread tests/runtime-adapter/src/bin/runtime-adapter-host.rs

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

Static review confirmed suite coverage but could not establish teardown, lease-release, and VM-exit race correctness without fresh exact-head runtime validation on KVM, MSHV, and WHP.

No open issues 🎉

1 resolved since last review

Review effort: Balanced

@esaurez
Enrique Saurez (esaurez) force-pushed the esaurez/847-pr1-template-operations-20261024 branch from 83fdc81 to 43879d7 Compare October 29, 2026 15:34
@esaurez
Enrique Saurez (esaurez) force-pushed the esaurez/847-pr2-start-run-suite-20261024 branch from 6863d47 to f43428e Compare October 29, 2026 15:34
@esaurez Enrique Saurez (esaurez) removed their assignment Oct 29, 2026
Base automatically changed from esaurez/847-pr1-template-operations-20261024 to dev October 29, 2026 19:32
Enrique Saurez and others added 5 commits October 29, 2026 12:47
A start whose template store refuses its selection now ends with the
refusal's record, in the form of a clone failure's, at the stage
`selection`, and with the exit status 10, before it leases or launches
anything. A template store that does not exist, and a generation that the
store never published or no longer holds, name no template; a generation
whose capture the restore window of its time contract no longer admits at
the host's time is expired; and one that a tombstone quarantines, or whose
capture is after the host's time, is ineligible. The record's code is that
of NVX's error, and the diagnostic stays on standard error.

A selection that races a retirement, or follows one that a crash
interrupted, ends as the store's refusal of the lease too: a retirement
tombstones a generation before it deletes its payload, and deletes none
that a lease keeps, and the start reads the generation's compatibility key
before the lease only for a memory target that the request does not name,
leaving a key that it cannot read to NVX's plan.

The clone tests check each refusal through the record, including a
generation that `remove` retired, and the unit tests the refusals of a
selection that names no template, which need no hypervisor.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 7d3db585-441c-495b-901f-ab7203c0f36b
`runtime-adapter-host run` runs one execution in a clone of a generation
of a template store, then tears the clone down, in one command.

- Its preflight stages the clone's images in the sandbox's store, as
  `stage` does, and prints `preflight=staged`, before the clone's start,
  which hashes nothing. A start that the guest would refuse fails the
  command, as a usage error, before it stages anything.
- It starts the clone as `start` does, through the same code, and ends as
  a start ends once the template store refuses the selection (status 10)
  or the clone fails (8, or 9 while the teardown is unresolved).
- It runs the execution in the clone's root, through `host::Executions`,
  in the session of the clone order in which the clone's sandbox started,
  as `exec` runs one: the program, its arguments, and its environment
  reach the guest in the execution's start, never in the template's boot
  tokens.
- Whatever the execution did, it stops the clone as `stop` does, and
  releases the lease once the VM exited. It holds the sandbox's lock from
  the lease to the teardown. It exits 0 once the execution finished,
  whatever its status, and the clone stopped.
- An execution that failed to run, as one whose session ended, fails the
  command after the teardown. Since the session may have ended with the
  VM, as when its VMM crashed, the run first waits up to 10 seconds for
  the VMM to exit, as a stop waits for one that it terminated: the stop
  then finds that the VM exited without it, and ends the clone as a
  reattach that finds it so does, with the record of its failure. The
  run exits with the stop's status once it is not a success (8, or 9,
  for a clone that failed, and 4 for a root that failed), and with 1
  otherwise.

The stop takes the same session, in which the guest may still send
messages about an execution that the driver finished without them, as
when it gave up waiting for its outcome. `Client::stop_skipping` stops
the guest agent as `stop` does, but skips the messages that its caller
accepts, and `Executions::is_about` accepts the guest's messages about
the driver's own executions; `stop` keeps its behavior.

The parsing of an execution's options, the checks of its starts, the run
of the executions, and the stop of a VM are factored out of `exec` and
`reattach`, which keep their behavior.

The README gains the table of the template and clone operations, and
documents `run`, its key, and its statuses. The unit tests cover run's
command lines and its refused selections after the preflight, and a stop
that skips the guest's late messages about the executions that the
driver gave up on; the VM tests cover a run with one execution, an
execution that the guest refuses, and runs whose clone does not start.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 7d3db585-441c-495b-901f-ab7203c0f36b
The stage's integration of network templates and exports gives `start`
the options `--network` and `--share`, with `--deny`, `--reserved`, and
`--share-root`, beside `--first-partition-bound` and `--machine-id`, and
`run` takes the options of a start. A run read its network only in its
start, though, once its preflight staged the images, and checked the
execution's start without the proxy of the network.

So `CloneOptions::plan` now reads the clone's network, and plans the
clone and the export of its shares, as the start did first, and both
commands plan before anything else: a start before it reads the
template store, and a run before its preflight stages anything. A
network, an image, or shares that the plan refuses then fail a run with
nothing created, and the run checks the execution's start with the URL
of the network's proxy in its environment, as `exec` gives it to an
execution of a clone, before it stages anything. The start's behavior
is unchanged.

The host program's documentation and the README give `run` the start's
options, and say that the execution finds the clone's shares beneath
its root. `clone_start` checks the run's new usage errors, and that a
run refuses a network or shares that the plan refuses before it stages
anything.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 7d3db585-441c-495b-901f-ab7203c0f36b
…th shares, in VMs

`run` now takes the start's network and shares, so two rows check that
the run's execution gets them in its clone, in one command:

- a run of a clone of a proxy-only template gives its execution the URL
  of the network's proxy in `http_proxy`, `https_proxy`, `HTTP_PROXY`,
  and `HTTPS_PROXY`, after the execution's own entries, as the
  executions of a started clone find it, then tears the clone down; a
  run of a network that lets nothing through finds no template there,
  and NVX's plan refuses its clone, once the preflight staged the
  images, before any VMM starts, so the run runs nothing;
- a run's execution reads a host file through the run's share, which
  the restore sequence mapped beneath the clone's root before the
  clone's sandbox started, and writes another through it, which the
  host's directory keeps once the run tore the clone down.

A third row checks a run whose clone's VM exits during its execution.
The execution, as user ID 0, tells the host through the run's share that
it runs, then waits, while the test terminates the clone's VMM. The run
waits for the VMM's exit, so its stop finds that the VM exited, and the
clone fails, as NVX attributes the loss of its status, which quarantines
nothing. The run ends with that failure's status 8, after the diagnostic
of the executions' failure, once it released the lease, and the template
still serves clones.

`Cloned::assert_ran_mapped` checks a run whose clone applied mappings,
`execution_facts` returns what a run, or an exec, printed of its first
execution, and `Template::begin_in` begins a start or a run that the
test ends itself.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 7d3db585-441c-495b-901f-ab7203c0f36b
The adapter's CI step ran its ignored tests, so a VM scenario that was
renamed or gone dropped out of it without a failure, and the scenarios
without a hypervisor ran only in the nvx-rs job.

tests/runtime-adapter/scenarios.txt now names the stage's consolidated
suite, each scenario by its test target and test: every operation's
outcomes, from create to run, without a hypervisor and in VMs, and every
scenario of vm_template, vm_clone, vm_layered, vm_restart and vm_steps,
155 in all.

nvx.py test-runtime-adapter runs it. It takes the adapter's test targets
from Cargo's metadata and lists the tests of each named one, and fails
unless the lines name exactly those tests and neither the library nor
another test target has an ignored test. It then runs each target's
scenarios by name with --exact, the ignored ones included, and fails
unless each of them reported ok and the target passed as many tests as
it names. --check-only checks the names alone. The microVM workflow's
runtime adapter steps run it on each backend in place of
cargo test -- --ignored, and validate-nvx checks its help.

The README lists every scenario with what it proves, in the order of the
manifest, which a unit test keeps in step. The usage, CI and
contribution documents describe the command.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 7d3db585-441c-495b-901f-ab7203c0f36b
@ppenna
Pedro Henrique Penna (ppenna) force-pushed the esaurez/847-pr2-start-run-suite-20261024 branch from f43428e to 41a4181 Compare October 29, 2026 19:49
Copilot AI balanced review requested due to automatic review settings October 29, 2026 19:49

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.

🟡 Changes recommended

The validator incorrectly rejects an explicitly selected KVM backend on hosts exposing both /dev/mshv and accessible /dev/kvm, preventing the suite from running despite explicit backend support.

1 open issue

Review effort: Balanced

targets = read_scenarios()
environment = os.environ.copy()
if not check_only:
validate_openvmm_test_backend(backend)
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.

3 participants