Skip to content

nvx-rs: set a clone's hostname and machine ID for every execution - #959

Merged
Pedro Henrique Penna (ppenna) merged 2 commits into
devfrom
esaurez/845-pr2-workload-view-20261024
Oct 29, 2026
Merged

Pedro Henrique Penna (ppenna) merged 2 commits into
devfrom
esaurez/845-pr2-workload-view-20261024

Conversation

@esaurez

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

Copy link
Copy Markdown
Contributor

Part of #845 (PR 2 of 3: the workload view).

Stacked on #957: this PR targets its branch, esaurez/845-pr1-gpt-fresh-scratch-20261024, and is rebased and retargeted with it.

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

It builds on #876, merged: the resolver bind and the removable runtime binds.

What and why

A clone's activation assembled its root and checked its workload identity, but left the record's hostname and machine ID unapplied. It now applies the workload's view before Ready.

nvx-rs: set a clone's hostname and machine ID for every execution

  • If the record asks for a generated machine ID, nvx::guest::clone::apply generates one once, after the account check, with MachineId::generate, from the random number generator that the repair reseeded before the activation accepted the record, so that no two clones of a template share it. It writes it to the new MACHINE_ID_FILE, workload-machine-id, in the template's runtime directory, as the managed agent writes /run/nvx/workload-machine-id, and binds it read-only over the root's /etc/machine-id with Entry::bind_runtime, in the caller's mount namespace, of which each execution's starts as a copy. With the image's own machine ID, it binds nothing. A retry of the Activate never applies the record again, so every execution and every retry sees the same ID.
  • The entry that binds checks the record's hostname first, and Applied::hostname returns it, which the test adapter gives every execution's entry through Entry::hostname.
  • Applied now holds the RuntimeBinds: Applied::teardown removes them with RuntimeBinds::remove before Root::teardown, since they keep the root's mount busy, and reports both through the new AppliedTeardownError; dropping an Applied removes them before the root drops. into_parts returns them for a caller that keeps them apart.

A generation or a bind that fails is ApplyError::MachineId or ApplyError::View, once the root is torn down. The test adapter maps each application failure to its class explicitly: the GPT reread and the assembly to 54, ACTIVATION_ROOT, and the account, the machine ID and the view to 55, ACTIVATION_WORKLOAD, leaving 56 and 57 to the export and the network. It keeps the Applied, whose teardown its stop runs. The cold path is unchanged: it generates and binds nothing.

runtime-adapter: start clones with generated machine IDs, in VMs

runtime-adapter-host start takes --machine-id (image | generated), the image's own by default, and clone::Request passes the mode to the clone's activation record.

In vm_clone, one template serves three clones at once: over a scratch image and in RAM, each with a generated machine ID, and in RAM with the image's own. Two executions in each, as user ID 0, see the hostname, none of the markers that the other clones' executions wrote in their roots, and the clone's machine ID: a generated one, distinct between the clones, the same in both executions, held by one bind that is read-only, nosuid and nodev, which a write cannot change; or, with the image's own, none, since the image has none, and then the one that the first execution wrote. Each stop then tears its root down, binds included.

A clone of a GPT disk whose layer holds a regular file etc starts with the image's own machine ID, which needs no bind, and ends before it is ready with a generated one, which no bind can hold below a file: status 8, by the evidence of invalid settings, with the guest's status 55. The template of that scenario is of the EROFS image, so it also restores a GPT clone of an EROFS placeholder.

Not in this PR

Validation

Each commit of this PR, on #954's head 21b9983, with OpenVMM at 832d5bb4, the pin of its base: fmt, Clippy with -D warnings in every configuration of the library and of the test adapter, and the check that nvx-rs/README.md gives each module a short row and a section. At this PR's head:

  • On Windows: tests/host_modules.rs; the test adapter's protocol-only tests, and its host tests of the library, the binaries, host_session and clone_start.
  • On Linux x86_64: the aarch64-apple-darwin check; the library's guest::clone and guest::root tests; the test adapter's musl guest library tests and guest_session; and, as root, the ignored test that checks the partition-table reread against the kernel with a loop device, and, in a process of their own, the privileged tests of guest::mappings.
  • The documentation with -D warnings, the MSRV checks of the library and of the test adapter's three configurations, and the Markdown links.

At #960's head, the top of the stack, with OpenVMM at 832d5bb4:

  • On Windows and on Linux x86_64, every step of check-nvx-rs: fmt; Clippy with -D warnings with all features and on the library with and without default features; the tests with all features and with the default ones; the library without default features; the documentation with -D warnings; and the MSRV check, with the aarch64-apple-darwin check on Linux.
  • The test adapter's fmt, Clippy, build, tests and MSRV check, in its protocol-only and host configurations on both, and in its musl guest configuration on Linux.
  • On Linux, the guest binaries' check, build and tests, and the Markdown links.
  • On Linux, as root, three times each, the ignored tests of guest::mappings, of guest::clone::activation::apply, and of the partition-table reread with a loop device, each in a process of its own: guest::mappings' privileged tests can find a mount busy at their teardown when another test of the same process starts a child, as they do at runtime-adapter: start clones of templates #954's head.
  • CI at the same head, in run 40829337538: 36 jobs passed, among them the microVM tests and the platform checks on KVM, MSHV and WHP, and OpenVMM's unit and VM tests; the six that run only for a pull request or on dev were skipped.
  • The test adapter's VM suites, on Windows (WHP) and on Linux (MSHV), with OpenVMM's release build of 832d5bb4: vm_clone, 16 scenarios, with this PR's two: the generated machine IDs, and the view that cannot be set; vm_layered, 43; vm_restart, 3; vm_steps, 3; and vm_template, 9.

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

Per-clone entropy uniqueness, execution mount inheritance, and teardown require verification of the VM scenarios isolated in #961 and KVM/MSHV/WHP results after the final restack.

No open issues 🎉

What changed in this PR

This PR implements the workload-view portion of #845: clone activation prepares the machine ID once, and every execution receives the record’s hostname. It also manages runtime binds through teardown, while leaving cold-boot behavior unchanged.

Changes:

  • Generate and bind a clone’s machine ID after activation validation and account checks, reusing it across executions and retries.
  • Preserve runtime binds with the activated root, remove them before teardown, and classify workload-view failures as status 55.
  • Extend tests and documentation for hostname propagation, machine-ID modes, activation ordering, and cleanup. VM scenarios are tracked separately in #961.
File Description
tests/​runtime-adapter/​tests/​guest_session.rs Tests workload-view failures and execution hostnames.
tests/​runtime-adapter/​src/​worker/​executions.rs Adds per-sandbox hostname configuration.
tests/​runtime-adapter/​src/​worker/​activation.rs Propagates clone hostnames and maps application failure statuses.
tests/​runtime-adapter/​src/​worker.rs Retains activated roots and removes runtime binds during teardown.
tests/​runtime-adapter/​README.md Documents clone workload views, cleanup, and failure statuses.
nvx-rs/​src/​guest/​mappings.rs Adds a test-only teardown-error constructor.
nvx-rs/​src/​guest/​clone/​activation/​apply/​tests.rs Tests generation, binding, retries, failures, and teardown ordering.
nvx-rs/​src/​guest/​clone/​activation/​apply.rs Implements workload-view application and runtime-bind ownership.
nvx-rs/​src/​guest/​clone/​activation.rs Exports the new teardown error and machine-ID filename constant.
nvx-rs/​src/​guest/​clone.rs Exposes and documents the expanded activation API.
nvx-rs/​README.md Updates the clone API overview with workload-view behavior.

Review effort: Balanced

@esaurez
Enrique Saurez (esaurez) force-pushed the esaurez/845-pr2-workload-view-20261024 branch from f3ebadd to d5c55de Compare October 29, 2026 03:37
@esaurez
Enrique Saurez (esaurez) changed the base branch from esaurez/845-pr2-base-snapshot-20261028 to esaurez/845-pr1-gpt-fresh-scratch-20261024 October 29, 2026 03:37
@esaurez
Enrique Saurez (esaurez) requested a balanced review from Copilot October 29, 2026 03:38

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

Persistent runtime binds cross execution mount namespaces and alter privileged teardown ordering, requiring confirmation of the privileged test and VM scenarios on KVM, MSHV, and WHP that this review could not execute.

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.

🔵 Needs a closer look

Pending privileged testing and KVM/MSHV/WHP clone-suite validation leave restored mount-namespace inheritance and machine-ID bind cleanup unverified at the rebased head.

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.

🔵 Needs a closer look

Privileged VM validation on KVM, MSHV, and WHP remains pending, leaving mount-namespace isolation, read-only machine-ID binds, and teardown behavior unverified.

No open issues 🎉

Review effort: Balanced

Enrique Saurez and others added 2 commits October 29, 2026 07:09
A clone's activation assembled its root and checked its workload
identity, but left the record's hostname and machine ID unapplied. It
now applies the workload's view before Ready:

- If the record asks for a generated machine ID,
  nvx::guest::clone::apply generates one once, after the account check,
  with MachineId::generate, from the random number generator that the
  repair reseeded before the activation accepted the record, so that no
  two clones of a template share it. It writes it to the new
  MACHINE_ID_FILE, workload-machine-id, in the template's runtime
  directory, as the managed agent writes /run/nvx/workload-machine-id,
  and binds it read-only over the root's /etc/machine-id with
  Entry::bind_runtime, in the caller's mount namespace, of which each
  execution's starts as a copy. With the image's own machine ID, it
  binds nothing. A retry of the Activate never applies the record
  again, so every execution and every retry sees the same ID.
- The entry that binds checks the record's hostname first, and
  Applied::hostname returns it, which the runtime adapter gives every
  execution's entry through Entry::hostname.
- Applied now holds the RuntimeBinds: Applied::teardown removes them
  with RuntimeBinds::remove before Root::teardown, since they keep the
  root's mount busy, and reports both through the new
  AppliedTeardownError; dropping an Applied removes them before the
  root drops. into_parts returns them for a caller that keeps them
  apart.

A generation or a bind that fails is ApplyError::MachineId or
ApplyError::View, once the root is torn down. The runtime adapter maps
each application failure to its class explicitly: the GPT reread and
the assembly to 54, ACTIVATION_ROOT, and the account, the machine ID
and the view to 55, ACTIVATION_WORKLOAD, leaving 56 and 57 to the
export and the network. It keeps the Applied, whose teardown its stop
runs. The cold path is unchanged: it generates and binds nothing.

The tests cover, without a VM, that no ID is generated before the
gate, and one only once after it, the hostname and both machine-ID
modes, the binds' removal before the root's teardown, and each
failure's status. An ignored test checks the generation and the
read-only bind against the kernel, as root. The VM tests of concurrent
clones, the image's own machine ID, and the failure statuses come with
the restore of clones in VMs.

Part of #845

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80932e09-af82-4b79-b417-70a241303cda
A clone's guest sets the hostname and the machine ID of the record's
workload view for every execution: the image's own machine ID, or one
that it generates once for the clone, which a read-only bind holds at
/etc/machine-id. These VM tests check that view in clones that run at
once.

start takes --machine-id image|generated, the image's own by default,
and clone::Request passes the mode to the clone's activation record.

In vm_clone, one template serves three clones at once: over a scratch
image and in RAM, each with a generated machine ID, and in RAM with the
image's own. Two executions in each, as user ID 0, see the hostname,
none of the markers that the other clones' executions wrote in their
roots, and the clone's machine ID: a generated one, distinct between
the clones, the same in both executions, held by one bind that is
read-only, nosuid and nodev, which a write cannot change; or, with the
image's own, none, since the image has none, and then the one that the
first execution wrote. Each stop then tears its root down, binds
included.

A clone of a GPT disk whose layer holds a regular file etc starts with
the image's own machine ID, which needs no bind, and ends with status
55 before it is ready with a generated one, which no bind can hold
below a file. The template of that scenario is of the EROFS image, so it
also restores a GPT clone of an EROFS placeholder.

The scenarios' GPT disks may hold regular files in their top layer.

Part of #845

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80932e09-af82-4b79-b417-70a241303cda
@ppenna
Pedro Henrique Penna (ppenna) force-pushed the esaurez/845-pr2-workload-view-20261024 branch from ff1efdf to c5abb73 Compare October 29, 2026 14:11
@ppenna
Pedro Henrique Penna (ppenna) merged commit e4ca7ac into dev Oct 29, 2026
45 checks passed
@ppenna
Pedro Henrique Penna (ppenna) deleted the esaurez/845-pr2-workload-view-20261024 branch October 29, 2026 14:57
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