From 345eac3cdcf531534915ed31eec4457c2c54be62 Mon Sep 17 00:00:00 2001 From: Enrique Saurez Date: Thu, 22 Oct 2026 13:40:31 -0700 Subject: [PATCH 1/6] nvx-rs: move the CLI's sandbox lifecycle onto the store and supervisor The CLI kept runtime records, a lock, and spawn, wait, and kill code of its own. Its state directory is now the sandbox's store: the store's lock serializes the lifecycle transitions, and the store's launch journal and runtime record of format 4 replace the CLI's runtime record of format 1, while the CLI keeps its configuration record. start launches OpenVMM with supervisor::launch, under the runtime identity that the install metadata of the artifacts gives, every other transition finds the VM with supervisor::reattach, as an orchestrator does after it restarts, and stop runs the supervisor's graceful step through the control session, and reports a timeout without taking the forced step. A VM that another runtime identity started is reported and never driven. The first transition that finds the runtime record of an OpenVMM that exited clears it and reports it once, so start launches such a sandbox again, where it refused to before. Switching formats takes one stop of every running sandbox, with the build that started it: the store refuses a runtime record of format 1 with a message that names the format, and nothing is migrated. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: aa7b9fda-3774-4817-aa98-af05f8d50215 --- doc/run.md | 83 ++- nvx-rs/README.md | 89 ++- nvx-rs/src/host/cli/sandbox/identity.rs | 223 ------- nvx-rs/src/host/cli/sandbox/lifecycle.rs | 637 ++++++++----------- nvx-rs/src/host/cli/sandbox/lock.rs | 443 ++++--------- nvx-rs/src/host/cli/sandbox/mod.rs | 34 +- nvx-rs/src/host/cli/sandbox/state.rs | 4 +- nvx-rs/src/host/platform/windows.rs | 23 - nvx-rs/src/host/process.rs | 5 +- nvx-rs/src/host/python/fs.rs | 53 +- nvx-rs/src/host/python/mod.rs | 2 +- nvx-rs/src/host/python/windows.rs | 78 --- nvx-rs/src/host/store/mod.rs | 6 +- nvx-rs/src/host/supervisor/mod.rs | 11 + nvx-rs/src/testing/fake_openvmm.rs | 7 +- nvx-rs/tests/cli.rs | 232 ++++++- nvx-rs/tests/fixtures/cli.json | 41 +- scripts/nvx_tools/sandbox_lifecycle_tests.py | 55 +- scripts/test_microvm_tests.py | 72 ++- 19 files changed, 890 insertions(+), 1208 deletions(-) delete mode 100644 nvx-rs/src/host/cli/sandbox/identity.rs diff --git a/doc/run.md b/doc/run.md index 15c7b3508..666c4f829 100644 --- a/doc/run.md +++ b/doc/run.md @@ -1024,41 +1024,55 @@ that they cannot look up, as through a link loop, rather than take it for no manifest. Keep provisioned layers and their manifests read-only, and never rewrite them in place. -Lifecycle transitions fail closed: `start` rejects an already-running or stale -runtime record; `exec` and `stop` require a live OpenVMM process; and -`deprovision` refuses to remove a running sandbox or unknown files. A control -capability or control socket that remains without a runtime record, as after a -killed `start`, may belong to an OpenVMM process that still runs, so `start` and -`deprovision` refuse it too: end any OpenVMM process whose arguments name the -state directory, and then remove `control.capability` and `control.sock`. -`provision`, `start`, `stop`, and `deprovision` hold an exclusive lock on -`lifecycle.lock` in the state directory until they return, so the transitions +Lifecycle transitions fail closed: `start` rejects a sandbox that is already +running; `exec` and `stop` require a live OpenVMM process; and `deprovision` +refuses to remove a running sandbox or unknown files. The state directory is +the sandbox's store, whose runtime record identifies the running OpenVMM by its +process ID, its start time, and the boot of the host, so these checks treat +OpenVMM as gone once it exits, even if no process reaps it or another process +reuses its ID. Every transition but `provision` first finds the sandbox's VM as +an orchestrator does after it restarts: it ends a `start` that was interrupted, +such as a killed one, and clears the runtime record of an OpenVMM that exited +without a `stop`. The first `exec` or `stop` to find such a record fails +because the runtime state is stale, while `start` launches OpenVMM again and +`deprovision` removes the sandbox. + +`start` records the runtime identity of the artifacts with the VM: the SHA-256 +digests of OpenVMM, the kernel, and the Alpine initramfs, as the install +metadata lists them, without hashing an artifact. A release lists them in its +`SHA256SUMS`, and a checkout in the build provenance that `nvx.py` writes to +`build/` with each artifact that it builds, and that `nvx.py download` +installs. `exec`, `stop`, and `deprovision` drive only a VM that artifacts of +the same identity started: one that other artifacts started, as before they +were rebuilt or replaced, is reported, and must be stopped with the artifacts +that started it. The store reads runtime records of its own format only, so +stop every running sandbox with the NVX version that started it before you +switch versions: a version with another format fails every transition of such +a sandbox with a message that names the format of its record. + +`provision`, `start`, `stop`, and `deprovision` hold the store's lock, +`sandbox.lock` in the state directory, until they return, so the transitions of one sandbox take effect one at a time: each waits up to `--timeout` seconds for any other in progress, and of two overlapping starts, exactly one launches OpenVMM while the other then fails because the sandbox is already running. -`exec` does not take the lock, so a long workload does not delay `stop`. A -provisioned state directory keeps `lifecycle.lock`, and `deprovision` removes -it with NVX's other files. On NFS, and on Windows file systems without POSIX -deletion such as ReFS, a removed lock file keeps the emptied directory until -the transitions that wait for it close it, so `deprovision` then releases the -lock before it removes the directory, and a `provision` that waits for that -removal completes it once nothing keeps the directory. A transition that -waited while `deprovision` removed the sandbox then fails because the sandbox +`exec` holds the lock only while it finds the running OpenVMM, so a long +workload does not delay `stop`. A provisioned state directory keeps +`sandbox.lock`, and `deprovision` removes it with NVX's other files. On NFS, +and on Windows file systems without POSIX deletion such as ReFS, a removed lock +file keeps the emptied directory until its last handle closes, so `deprovision` +then releases the lock before it removes the directory. A transition that +waited while `deprovision` removed the sandbox then fails as for a sandbox that is not provisioned, except `provision`, which provisions the state directory -again once `deprovision` has removed it, unless another `provision` provisions -it first. Only a `provision` that starts just as `deprovision` removes the -emptied directory can provision it first, and `deprovision` then fails -because the sandbox was provisioned again. `--timeout` -must be finite and greater than 0. If the guest exits while `start` waits for -it to become ready, for example because it refuses +again. A `provision` that takes the lock after `deprovision` has removed the +lock file, but before it has removed the directory, provisions the directory +first, and `deprovision` then fails because the sandbox was provisioned again. +`--timeout` must be finite and greater than 0. If the guest exits while `start` +waits for it to become ready, for example because it refuses the workload identity, OpenVMM closes the control endpoint and shuts down. `start` then waits up to `--timeout` seconds for OpenVMM to publish `outcome.json` and exit, and fails with the report's category and status, such as `guest-exit status 125`. It terminates OpenVMM only if OpenVMM outlives that -wait. The runtime record identifies OpenVMM by its process ID and start time, so -these checks treat OpenVMM as gone once it exits, even if no process reaps it or -another process reuses its ID. A record that an earlier NVX version wrote lacks -the start time and identifies OpenVMM by its process ID alone. Managed +wait. Managed workload arguments use the bounded control protocol rather than the kernel command line and may contain whitespace. Managed execution can select an absolute working directory and either repeated inline `KEY=VALUE` entries or a @@ -1121,7 +1135,7 @@ named. It checks that: the guest refuses an identity that the Ubuntu image lacks before a one-shot workload starts, and in a managed `start`, which then fails with `guest-exit status 125` from OpenVMM's outcome report and leaves only - `config.json`, `lifecycle.lock`, `openvmm.log`, and that `outcome.json`, + `config.json`, `sandbox.lock`, `openvmm.log`, and that `outcome.json`, without a runtime record, capability, control socket, report staging file, or OpenVMM process; - every operation fails on a state directory that was never provisioned, a @@ -1143,14 +1157,16 @@ named. It checks that: `--outcome-report` path exists fails before its workload runs and leaves the file intact; - `stop` ends OpenVMM and its control socket or pipe, leaves only - `config.json`, `lifecycle.lock`, `openvmm.log`, and a successful + `config.json`, `sandbox.lock`, `openvmm.log`, and a successful `outcome.json`, and leaves a cleanly unmounted scratch file system, after which `exec` and `stop` fail; - two overlapping `start` calls then launch a single OpenVMM process: one succeeds, and the other fails with `sandbox is already running or has stale runtime state`; -- after OpenVMM exits without a `stop`, `exec` and `stop` report the stale - runtime state and `start` refuses it; and +- after OpenVMM exits without a `stop`, `exec` reports the stale runtime state + once and clears it, after which `stop` finds the sandbox not running, and the + state directory holds only `config.json`, `sandbox.lock`, and `openvmm.log`, + without a control socket or pipe; and - `deprovision` removes only NVX's files: it fails and keeps a foreign file in the state directory, and once that file is gone, it removes the directory, which a later `exec` does not recreate. @@ -1158,9 +1174,8 @@ named. It checks that: Throughout, no OpenVMM process whose arguments name the scenario's fixture outlives its sandbox, and the EROFS layer keeps its digest and the scratch image its file. On failure, the scenario stops and deprovisions what it -started, ends every OpenVMM process that names its fixture, removes any control -capability or socket that no runtime record names, and preserves the fixture -for recovery only if that cleanup fails. Its evidence, written to the +started, ends every OpenVMM process that names its fixture, and preserves the +fixture for recovery only if that cleanup fails. Its evidence, written to the output directory with a `sandbox-lifecycle-` prefix, holds bounded command observations (arguments, statuses, output sizes, and state-directory entries), the runtime record, the OpenVMM logs, the VM-level and exec outcome reports, the diff --git a/nvx-rs/README.md b/nvx-rs/README.md index a484f3bdb..cc9643fe7 100644 --- a/nvx-rs/README.md +++ b/nvx-rs/README.md @@ -68,7 +68,7 @@ nvx-rs/ | `nvx::gpt` | `Disk::read`: the protective MBR, primary header, and primary partition entry array of a GPT disk, read through a `ReadAt` source that the caller gives and checked strictly, as UEFI specifies them and as Linux requires them, without ever reading the backup: 128-byte entries, at most 1 MiB of them, used entries first and without holes, p1 and 1 to 254 layers, extents in the usable blocks without overlap, and unique GUIDs, with checked arithmetic in the disk's logical blocks. `Expected` adds the disk GUID and the partitions' unique GUIDs and sizes that a caller knows, and `Disk::read_p1` returns p1's opaque bytes, up to a bound and without trailing NUL bytes and whitespace. GUIDs are `nvx::uuid::Uuid`s. It has no dependencies, so the guest and the host's checks of the images that it stages share it | | `nvx::uuid` | `Uuid`: a UUID, such as the GUID of a GPT disk or partition or the UUID of a filesystem, in the order of its canonical text, the one text that it displays and parses: 32 lowercase hexadecimal digits in groups of 8, 4, 4, 4, and 12, joined by hyphens. `from_bytes_le` and `to_bytes_le` convert the layout in which GPT and other EFI GUIDs store their first three groups little-endian. It has no dependencies, so the host and the guest share it | | `nvx::Hypervisor` | KVM, MSHV, or WHP, with its serde form behind the `serde` feature | -| `nvx::host::artifacts` | `Artifacts` (also `nvx::Artifacts`): the OpenVMM executable, guest kernel, and Alpine initramfs, from a release, a repository checkout, the environment, a directory next to the executable, or the bundle. `ArtifactDir` and `Guest`: every artifact of the guests that `nvx.py` runs, the OpenVMM executable, the kernel, the Alpine, Ubuntu, and Azure Linux initramfs images and their package manifests, and the Ubuntu distro layer and its manifest, in a checkout's `build/` and `openvmm/target/release/` or a release's `guest/` and `bin/`, checked in the order and with the messages of `nvx.py`. `ArtifactPaths` lists them all and serializes as JSON | +| `nvx::host::artifacts` | `Artifacts` (also `nvx::Artifacts`): the OpenVMM executable, guest kernel, and Alpine initramfs, from a release, a repository checkout, the environment, a directory next to the executable, or the bundle. `ArtifactDir` and `Guest`: every artifact of the guests that `nvx.py` runs, the OpenVMM executable, the kernel, the Alpine, Ubuntu, and Azure Linux initramfs images and their package manifests, and the Ubuntu distro layer and its manifest, in a checkout's `build/` and `openvmm/target/release/` or a release's `guest/` and `bin/`, checked in the order and with the messages of `nvx.py`. `ArtifactPaths` lists them all and serializes as JSON. `ArtifactDir::runtime_identity` reads the `RuntimeIdentity` of the OpenVMM executable, kernel, and Alpine initramfs from the install metadata, a release's `SHA256SUMS` or a checkout's build provenance, without hashing an artifact | | `nvx::host::launch` | `LaunchSpec` and OpenVMM's command line: the managed lifecycle, the `WorkloadIdentity`, the 1024-byte kernel command line, its reserved parameters, the 32,767-character limit of a Windows command line, and `LaunchSpec::check`, which every command line applies to the memory size, the non-root identity, the hostname, the guest network, and the kernel parameters | | `nvx::host::network` | `NetworkPolicy` and `openvmm_arguments`: the egress default and rules, ingress, host-loopback access and forwards as OpenVMM options. Egress rules are lowered exactly into OpenVMM's IPv4 and IPv6 rules by the lowering of egress policy files, with at most 256 rules for each action | | `nvx::host::network::policy` | Egress policy files: `compile_policy_file`, `compile_policy_bytes`, and `compile_policy` compile one to a `CompiledEgressPolicy` of native rules; see [Egress policy files](#egress-policy-files) | @@ -76,7 +76,7 @@ nvx-rs/ | `nvx::host::store` | `SandboxStore`: the runtime records of one sandbox in a private directory with one lock, which every host that supervises the sandbox shares. The `RuntimeRecord` names the running VM: the `ProcessRef` of its VMM, its control endpoint, and the `RuntimeIdentity` that it was launched with, the identities of its OpenVMM, kernel, and initramfs from install or staging metadata and its boot and control contract versions, which decide whether another build of the host may reattach to it. The `LaunchJournal` of a launch holds a marker written before the spawn, the VMM's `ProcessRef` right after it, and an inherited claim on the VMM's log, which proves that a launch that never published its endpoint has exited, so that recovery ends an interrupted launch and signals only a VMM that it holds through a verified `Process`. Records are JSON in one versioned format, and records in any other format fail rather than being migrated | | `nvx::host::instance` | `Instance`: a VM in a directory of its own. It starts OpenVMM with a fresh capability and waits until the guest is ready and has mounted the mapped paths, reconciles recorded state, recovers interrupted launches, runs workloads and pumps their output, and stops the VM with a forced fallback | | `nvx::host::nvxs` | `HostConnection`: the host's role of the NVXS session over a platform transport, on which `nvx::host::session` and other host applications run their protocols. It attaches with the launch capability, waiting while the broker asks it to, sends application bytes in as many DATA records as they take, and returns each DATA payload from the guest, with deadlines; a receive that times out keeps a partial record | -| `nvx::host::process` | Verified process references, and the launch of OpenVMM for the CLI and `nvx::host::instance`, through `nvx::host::supervisor`. A `ProcessRef` names one process by its ID, its start time, and the `BootId` of the host boot during which it started. `Process::open` checks a reference and holds its process through a pidfd on Linux or a process handle on Windows, whether or not the caller started it, so that waits sleep until the process exits and termination never reaches a process that reuses its ID. A reference from an earlier boot names an exited process, and a process whose identity this user cannot check, such as another user's, makes `Process::open` fail rather than report an exit. Linux hosts without pidfds poll the reference instead, and cannot terminate the process | +| `nvx::host::process` | Verified process references, and the launch of OpenVMM for `nvx::host::instance`, through `nvx::host::supervisor`. A `ProcessRef` names one process by its ID, its start time, and the `BootId` of the host boot during which it started. `Process::open` checks a reference and holds its process through a pidfd on Linux or a process handle on Windows, whether or not the caller started it, so that waits sleep until the process exits and termination never reaches a process that reuses its ID. A reference from an earlier boot names an exited process, and a process whose identity this user cannot check, such as another user's, makes `Process::open` fail rather than report an exit. Linux hosts without pidfds poll the reference instead, and cannot terminate the process | | `nvx::host::supervisor` | The supervisor of a sandbox's VMM, beneath the CLI, `Instance`, and external orchestrators, which keep the configuration, the authorization, and the application protocol. `launch` runs the store's launch steps under the sandbox's lock: it writes the journal's marker, spawns OpenVMM with the claimed log, records its `ProcessRef` right after, and returns a `Starting` launch that the caller commits once the VMM serves its control endpoint, or abandons. A launch that fails never leaves an unrecorded VMM running. `VmmCommand::inherit` lists the descriptors (Linux) or handles (Windows) that the VMM inherits under their numbers, besides its standard streams, and it inherits nothing else of the caller's: on Linux, they must be close-on-exec, a flag that only the VMM's own process clears; on Windows, they join the VMM's explicit handle list. `Vmm::stop` runs one graceful step of the caller's and waits for the VMM's exit event until a deadline, and takes a bounded forced step through the VMM's verified identity only on request. `reattach` recovers an interrupted launch, and holds the recorded VMM again after the orchestrator restarts within the same host boot: `Live`, `Mismatched` with another runtime identity, `Exited`, or `Indeterminate` | | `nvx::host::seal` | `FileSeal`: what the host reports about an open file from its metadata alone, without reading it: the platform scheme, the device or volume serial number, the file ID, the kind, the length, the modification time, and on Linux the change time, with a versioned serde form. A seal detects accidental change, not tampering; see [Distro bindings](#distro-bindings) | | `nvx::host::session`, `platform`, `report` | The authenticated control session, which carries NVX's control protocol, `NVXC`, one frame per DATA record of a `HostConnection`; Linux and Windows primitives; and OpenVMM's outcome report | @@ -310,7 +310,9 @@ extracted release, with them in `guest/` and `bin/`. `SOURCE-MANIFEST.json`; otherwise the command fails and asks for `--root`. - The `NVX_OPENVMM` environment variable names an OpenVMM executable to run instead of the root's, such as `nvx-fake-openvmm` in tests. A relative path is - relative to the working directory. + relative to the working directory. It does not change the + [runtime identity](#runtime-identity), which the root's install metadata + gives. ### `artifacts` @@ -369,8 +371,9 @@ message. `nvx-rs sandbox` keeps the contract of `nvx.py sandbox`: every option of its parser, with the same names, defaults, and value checks, and the same -operations, checks, messages, outputs, exit statuses, outcome reports, and -state directories. The operation defaults to `run`. Options take their values +operations, checks, messages, outputs, exit statuses, and outcome reports, +except as [Other differences from nvx.py](#other-differences-from-nvxpy) lists. +The operation defaults to `run`. Options take their values as `--option VALUE` or `--option=VALUE`, repeated options accumulate, and `--mount-deny`, `--mount-allow`, and `--mount-write` apply to the `--mount` before them. @@ -389,22 +392,63 @@ before them. "exec", "status_code": STATUS}, "schema_version": 1}`. `stop` shuts the VM down and prints nothing. `deprovision` removes the state directory. -The state directory and its files are those that `nvx.py` wrote before it -delegated to `nvx-rs`, except that `config.json` also records the -[distro binding](#distro-bindings) of the sandbox. An older `nvx.py`, which -ignores the binding, can operate a sandbox that `nvx-rs` provisioned or -started, and `nvx-rs` one that `nvx.py` started, but `nvx-rs` refuses to start -a sandbox that `nvx.py` provisioned, which has no binding: +The state directory is the directory of the sandbox's +[store](src/host/store/mod.rs), the runtime records that every host of a +sandbox shares, and `config.json` is the CLI's own record beside them. It also +records the [distro binding](#distro-bindings) of the sandbox, and `nvx-rs` +refuses to start a sandbox that `nvx.py` provisioned, which has no binding: | File | Contents | | --- | --- | | `config.json` | The provisioned sandbox, in the lowest of formats 1 to 6 that holds it, with the binding of its distro layer under `distro_binding`, written as `json.dumps(value, indent=2, sort_keys=True)` writes it, with mode 0600, through a temporary file | -| `runtime.json` | Format 1: the `pid` and `start_time` of the running OpenVMM, as Python identifies a process, and its `control_endpoint`: `STATE/control.sock`, or `//./pipe/openvmm-microvm-HEX` on Windows | +| `sandbox.lock` | The store's lock, which serializes the lifecycle transitions | +| `launch.json` | The store's journal of a start in progress, or of one that was interrupted | +| `runtime.json` | The store's runtime record of the running VM, in its format 4: the OpenVMM process, by its ID, its start time, and the host's boot, its control endpoint, `STATE/control.sock` or `//./pipe/openvmm-microvm-HEX` on Windows, and the [runtime identity](#runtime-identity) of its start | | `control.capability` | The 32-byte capability of the running OpenVMM, with mode 0600 | | `control.sock` | The control endpoint on POSIX hosts | -| `openvmm.log` | OpenVMM's output, appended by each start | +| `openvmm.log` | OpenVMM's output, which each start replaces | | `outcome.json` | OpenVMM's outcome report of the last start | -| `lifecycle.lock` | The lock that serializes lifecycle transitions, an exclusive `flock` on POSIX and a lock of its first byte on Windows, as Python takes it; deprovision leaves `deprovisioned` in it while it removes the directory | + +`provision`, `start`, `stop`, and `deprovision` hold the store's lock for their +whole transition, and `exec` only while it finds the running VM, so that a long +workload cannot delay a `stop`. Every transition but `provision` finds the +sandbox's VM with `nvx::host::supervisor::reattach`, as an orchestrator does +after it restarts within the same boot of the host: it ends a start that was +interrupted, holds an OpenVMM that still runs through its verified process +identity, and clears the runtime record of one that exited without a `stop`. +The first `exec` or `stop` to find such a record fails because the runtime +state is stale, while `start` launches OpenVMM again and `deprovision` removes +the sandbox. `start` launches OpenVMM with `nvx::host::supervisor::launch`, and +`stop` asks the guest to shut down through the control session, waits up to +`--timeout` seconds for OpenVMM to exit, and reports one that does not, which +keeps running: it never ends OpenVMM otherwise. + +#### Runtime identity + +`start` records the runtime identity of its artifacts in the runtime record: +the SHA-256 digests of OpenVMM, the kernel, and the Alpine initramfs, and the +boot and control contract versions of this build. The digests come from the +root's install metadata, and no artifact is hashed: + +- A release lists them in its `SHA256SUMS`, under `bin/openvmm[.exe]`, + `guest/vmlinux`, and `guest/initramfs.cpio.gz`. +- A checkout has them in the build provenance that `nvx.py` writes to `build/` + with each artifact that it builds, and that `nvx.py download` installs with a + release's artifacts: the `executable_sha256` of `openvmm.provenance.json`, the + `kernel_sha256` of `vmlinux.provenance.json`, and the `initramfs_sha256` of + `initramfs.provenance.json`, each of format 1. + +`start` needs that metadata, and so does every other transition but +`provision` while the store records a start of the sandbox: each fails when +the metadata is missing or names no valid digest. They drive only a VM that the +same identity started: one that other artifacts started, as before they were +rebuilt or replaced, is reported with the parts that differ, and must be +stopped with the artifacts that started it. + +The store reads runtime records of its format alone. A sandbox that a build +with another format started, such as one whose `runtime.json` has format 1, +fails every transition with a message that names that format: stop every +running sandbox with the build that started it before you switch builds. #### Guest features @@ -437,6 +481,14 @@ guest agents of NVX advertise bits 0, 2, and 4 in sandbox mode. without a console window, as `nvx::host::supervisor` starts it. - `exec` refuses more than 1 MiB of workload output, which the guest agent never forwards. +- The state directory holds the store's files: `sandbox.lock` replaces the + `lifecycle.lock` of `nvx.py`, `runtime.json` has the store's format, and each + start replaces `openvmm.log`, which `nvx.py` appended to. `exec` holds the + lock while it finds the running VM, which `nvx.py` did not. A runtime record + of an OpenVMM that exited is cleared by the first transition that finds it, + so that `start` launches the sandbox again, where `nvx.py` refused to. +- `provision` refuses an existing state directory that is not private to the + current user, as the store refuses it, rather than make it private. - The `nvx_layer` token of the kernel command line names the image format of the distro layer, as in `nvx_layer=distro,0xd0003000,erofs:UUID`, and the guest refuses the form without a format that `nvx.py` wrote; see @@ -451,7 +503,9 @@ one-shot runs, which run `/bin/echo`, `/bin/sleep`, `/bin/true`, or `/bin/false`, write an outcome report if asked, and exit with the workload's status. Kernel command-line tokens make it fail: `fake_exit_on_start=STATUS`, `fake_exit_status=STATUS`, `fake_guest_features=MASK`, and -`fake_legacy_guest=1`, among others; see `nvx::testing::fake_openvmm`. +`fake_legacy_guest=1`, among others; see `nvx::testing::fake_openvmm`. A +managed `start` also reads the root's [runtime identity](#runtime-identity), so +a test root holds build provenance, as the roots of the golden scenarios do. ```bash cd nvx-rs @@ -479,7 +533,10 @@ recorded on each kind of host, where the script also played every scenario with `nvx-rs`, and each lifecycle with the Python implementation and `nvx-rs` taking turns both ways, and found no difference. `tests/cli.rs` replays the section of its host with `nvx-rs`, without Python; since `nvx.py sandbox` runs `nvx-rs`, -these scenarios are its reference. +these scenarios are its reference. Their state files have since followed the +sandbox store: its lock file, its runtime record, the log that each start +replaces, and the build provenance in the root from which `start` reads the +runtime identity. ## Features diff --git a/nvx-rs/src/host/cli/sandbox/identity.rs b/nvx-rs/src/host/cli/sandbox/identity.rs deleted file mode 100644 index 7175d45c8..000000000 --- a/nvx-rs/src/host/cli/sandbox/identity.rs +++ /dev/null @@ -1,223 +0,0 @@ -//! The identity of the OpenVMM process of a running sandbox, as the former `sandbox_lifecycle.py` -//! recorded and checked it: `_process_start_time` and `_process_running`. -//! -//! A runtime record names OpenVMM by its process ID and start time, which together never repeat: -//! on Linux the `starttime` field of `/proc/PID/stat`, in clock ticks since boot, and on Windows -//! the creation time of the process as a 64-bit `FILETIME`. Both implementations compute the same -//! value, so a record that either wrote identifies the process for the other. - -use crate::host::cli::{Exit, Result}; -use crate::host::python::builtins::Int; -#[cfg(any(target_os = "linux", windows))] -use crate::host::python::fs::os_error_message; - -/// Returns the start time of a live process, or `None` if no live process has the ID, as -/// `_process_start_time` does, or the message of the error with which the state of the process -/// cannot be determined. -/// -/// A process runs until its last thread exits, as [`crate::host::platform::process_start_time`] -/// holds, so a zombie counts as exited only once its other threads have exited too, where Python -/// counted every zombie as exited. On Windows, a process that the caller cannot open counts as -/// exited: OpenVMM runs as the caller, so such a process cannot be its VM. Other hosts have no -/// `/proc`, so every process counts as exited there, as in Python. -pub(crate) fn process_start_time(pid: &Int) -> std::result::Result, String> { - #[cfg(target_os = "linux")] - { - let path = format!("/proc/{pid}/stat"); - let stat = match std::fs::read(&path) { - Ok(stat) => stat, - Err(error) - if error.kind() == std::io::ErrorKind::NotFound - || error.raw_os_error() == Some(nix::errno::Errno::ESRCH as i32) => - { - return Ok(None); - } - Err(error) => return Err(os_error_message(&error, Some(&path))), - }; - linux_start_time(&stat, || { - pid.to_u32() - .map_or(Ok(false), crate::host::platform::other_threads_remain) - .map_err(|error| os_error_message(&error, Some(&format!("/proc/{pid}/task")))) - }) - } - #[cfg(windows)] - { - let Some(pid) = pid.to_u32() else { - return Ok(None); - }; - crate::host::platform::process_start_time(pid) - .map_err(|error| os_error_message(&error, None)) - } - #[cfg(not(any(target_os = "linux", windows)))] - { - let _ = pid; - Ok(None) - } -} - -/// Reads the start time from the `/proc/PID/stat` line of a process, as `_linux_start_time` -/// does: the command name may contain spaces and parentheses, so the fields after it start at -/// its last `)`, with the state as field 3 and `starttime` as field 22. Only for a zombie does it -/// ask `other_threads_remain` whether the process still runs. -#[cfg(any(target_os = "linux", test))] -fn linux_start_time( - stat: &[u8], - other_threads_remain: impl FnOnce() -> std::result::Result, -) -> std::result::Result, String> { - let unexpected = || "process status has an unexpected format".to_owned(); - let fields: Vec<&[u8]> = match stat.iter().rposition(|&byte| byte == b')') { - Some(end) => stat[end + 1..] - .split(|byte| matches!(byte, b' ' | b'\t' | b'\n' | b'\r' | 0x0b | 0x0c)) - .filter(|field| !field.is_empty()) - .collect(), - None => Vec::new(), - }; - let start = fields.get(19).copied().unwrap_or_default(); - if fields.len() < 20 || start.is_empty() || !start.iter().all(u8::is_ascii_digit) { - return Err(unexpected()); - } - let state = fields[0]; - if state == b"X" || (state == b"Z" && !other_threads_remain()?) { - return Ok(None); - } - std::str::from_utf8(start) - .ok() - .and_then(|start| start.parse().ok()) - .map(Some) - .ok_or_else(unexpected) -} - -/// Returns whether the OpenVMM process that a runtime record names still runs, as -/// `_process_running` does. A record without a start time, written by an earlier NVX version, -/// identifies OpenVMM by its process ID alone. -pub(crate) fn process_running(pid: &Int, start_time: Option<&Int>) -> Result { - if !pid.is_positive() { - return Ok(false); - } - let current = process_start_time(pid).map_err(|message| { - Exit::error(format!( - "cannot determine whether OpenVMM process {pid} is running: {message}" - )) - })?; - Ok(match start_time { - None => current.is_some(), - Some(start_time) => current.map(Int::from).as_ref() == Some(start_time), - }) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn stat_lines_give_the_start_time_of_live_processes() { - let line = |state: &str| { - format!( - "123 (my (odd) prog) {state} 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 4242 19 20\n" - ) - }; - let unasked = - || -> std::result::Result { panic!("only a zombie's threads matter") }; - let alone = || Ok(false); - let threads = || Ok(true); - assert_eq!( - linux_start_time(line("S").as_bytes(), unasked), - Ok(Some(4242)) - ); - assert_eq!( - linux_start_time(line("R").as_bytes(), unasked), - Ok(Some(4242)) - ); - assert_eq!(linux_start_time(line("Z").as_bytes(), alone), Ok(None)); - // A zombie main thread whose other threads still run is a live process. - assert_eq!( - linux_start_time(line("Z").as_bytes(), threads), - Ok(Some(4242)) - ); - assert_eq!( - linux_start_time(line("Z").as_bytes(), || Err("denied".to_owned())), - Err("denied".to_owned()) - ); - assert_eq!(linux_start_time(line("X").as_bytes(), unasked), Ok(None)); - for malformed in [ - &b"123 (prog) S 1 2"[..], - b"no parenthesis", - b"1 (p) S 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 x", - ] { - assert_eq!( - linux_start_time(malformed, unasked), - Err("process status has an unexpected format".to_owned()) - ); - } - } - - /// Kills and reaps a child when dropped, so that a failed assertion leaves no process. - #[cfg(target_os = "linux")] - struct Reaped(std::process::Child); - - #[cfg(target_os = "linux")] - impl Drop for Reaped { - fn drop(&mut self) { - let _ = self.0.kill(); - let _ = self.0.wait(); - } - } - - #[cfg(target_os = "linux")] - #[test] - fn a_vm_runs_until_its_last_thread_exits() { - // As in `process::tests::a_process_runs_until_its_last_thread_exits`, the main thread - // leaves through pthread_exit, which turns it into a zombie, while another thread sleeps. - use std::time::{Duration, Instant}; - let spawned = std::process::Command::new("python3") - .args([ - "-c", - "import ctypes, threading, time\n\ - threading.Thread(target=time.sleep, args=(60,)).start()\n\ - ctypes.CDLL(None).pthread_exit(None)\n", - ]) - .stdin(std::process::Stdio::null()) - .spawn(); - let mut child = match spawned { - Ok(child) => Reaped(child), - Err(error) if error.kind() == std::io::ErrorKind::NotFound => { - eprintln!("skipping: python3 is not installed"); - return; - } - Err(error) => panic!("cannot start python3: {error}"), - }; - let pid = Int::from(child.0.id()); - let start_time = Int::from(process_start_time(&pid).unwrap().expect("python3 runs")); - let deadline = Instant::now() + Duration::from_secs(10); - loop { - let stat = std::fs::read_to_string(format!("/proc/{pid}/stat")).unwrap(); - if stat[stat.rfind(')').unwrap() + 1..] - .trim_start() - .starts_with('Z') - { - break; - } - assert!(Instant::now() < deadline, "the main thread did not exit"); - std::thread::sleep(Duration::from_millis(10)); - } - assert!(process_running(&pid, Some(&start_time)).unwrap()); - child.0.kill().unwrap(); - child.0.wait().unwrap(); - assert!(!process_running(&pid, Some(&start_time)).unwrap()); - } - - #[test] - fn this_process_runs_and_others_are_identified_by_start_time() { - let pid = Int::from(std::process::id()); - let start_time = process_start_time(&pid).unwrap(); - if cfg!(any(target_os = "linux", windows)) { - let start_time = Int::from(start_time.expect("this process runs")); - assert!(process_running(&pid, Some(&start_time)).unwrap()); - assert!(!process_running(&pid, Some(&start_time.plus_one())).unwrap()); - assert!(process_running(&pid, None).unwrap()); - } - assert!(!process_running(&Int::zero(), None).unwrap()); - assert!(!process_running(&Int::parse("-5").unwrap(), None).unwrap()); - assert!(!process_running(&Int::parse("99999999999999999999").unwrap(), None).unwrap()); - } -} diff --git a/nvx-rs/src/host/cli/sandbox/lifecycle.rs b/nvx-rs/src/host/cli/sandbox/lifecycle.rs index 068eaef08..6e4c01567 100644 --- a/nvx-rs/src/host/cli/sandbox/lifecycle.rs +++ b/nvx-rs/src/host/cli/sandbox/lifecycle.rs @@ -1,47 +1,62 @@ //! The lifecycle of a managed sandbox, a port of the former -//! `scripts/nvx_tools/sandbox_lifecycle.py`, which it replaced (microsoft/nvx#359). +//! `scripts/nvx_tools/sandbox_lifecycle.py`, which it replaced (microsoft/nvx#359), on the +//! [`store`](crate::host::store), [`supervisor`], and [`session`](crate::host::session) that +//! every host of a sandbox shares. //! -//! A sandbox lives in its state directory: +//! A sandbox lives in its state directory, which is the directory of its store: //! //! ```text //! / //! config.json the launch that provision records, in formats 1 to 6, with the binding //! of its distro layer -//! lifecycle.lock the lock that serializes provision, start, stop, and deprovision -//! runtime.json format 1: the pid, start_time, and control_endpoint of OpenVMM +//! sandbox.lock the store's lock, which serializes the lifecycle transitions +//! launch.json the store's journal of a start in progress, or of an interrupted one +//! runtime.json the store's runtime record of the running VM, in its format 4 //! control.capability the 32-byte capability of the running OpenVMM //! control.sock the control endpoint of the running OpenVMM, on POSIX -//! openvmm.log OpenVMM's output, appended by each start +//! openvmm.log OpenVMM's output, which each start replaces //! outcome.json OpenVMM's outcome report of the latest start //! ``` //! +//! The CLI keeps its own record, the configuration, in its own formats; the store keeps the +//! runtime records, and reads no other format than its own: a sandbox that an earlier build +//! started must be stopped with that build. +//! //! [`provision`], [`start`], [`stop`], and [`deprovision`] hold the [`LifecycleLock`] for their -//! whole transition; [`exec_workload`] does not, so a long workload cannot delay a stop. Every -//! state file is written as `sandbox_lifecycle.py` wrote it, except that `config.json` also -//! records the binding of the distro layer that `provision` verified, which `start` checks from -//! metadata alone. An older `nvx.py` starts a sandbox that `nvx-rs` provisioned, without the -//! check, and `nvx-rs` refuses to start one that `nvx.py` provisioned, which has no binding. - +//! whole transition, and [`exec_workload`] only while it finds the running VM, so a long workload +//! cannot delay a stop. `config.json` also records the binding of the distro layer that +//! `provision` verified, which [`start`] checks from metadata alone: it refuses to start a sandbox +//! that `nvx.py` provisioned, which has no binding. +//! +//! Every transition but provision finds the sandbox's VM with [`supervisor::reattach`], as an +//! orchestrator does after it restarts: it ends a start that was interrupted, holds a recorded +//! OpenVMM that still runs through its verified identity, and clears the runtime record of one +//! that exited without a stop, which the first transition to find it reports, or which a start +//! replaces. A VM that another runtime identity than the artifacts' started, as after the +//! artifacts were replaced, is reported, and never driven. [`start`] launches OpenVMM with +//! [`supervisor::launch`], under the runtime identity that the install metadata of the artifacts +//! names, and [`stop`] asks the guest to shut down, waits for OpenVMM to exit, and reports a VM +//! that does not, without ending it otherwise. + +use std::ffi::OsString; use std::fs::{File, OpenOptions}; use std::io::Write; -use std::time::Duration; +use std::time::{Duration, Instant}; -use super::Artifacts; -use super::identity::{process_running, process_start_time}; use super::lock::{ LifecycleLock, OsErrorClass, directory_not_empty, directory_status, entries, lingers, os_error_class, }; use super::state::{ - CONFIG_FORMATS, RecordedDistro, STATE_FORMAT, config_int, config_text, deserialize_binding, + CONFIG_FORMATS, RecordedDistro, config_int, config_text, deserialize_binding, deserialize_launch, hex, member, random_bytes, read_json, serialize_launch, temporary_path, - uuid4_hex, write_json, + write_json, }; +use super::{Artifacts, error_with_cause}; use crate::ErrorKind; -use crate::host::cli::deadline::{Deadline, RETRY_INTERVAL, sleep}; +use crate::host::cli::deadline::{Deadline, RETRY_INTERVAL}; use crate::host::cli::session::{EXIT_CATEGORIES, ExecRequest, ExecResult, Session}; -use crate::host::cli::{Exit, Result}; -use crate::host::process::{self, Launched}; +use crate::host::cli::{Exit, Result, check_interrupt}; use crate::host::python::builtins::{Int, repr}; use crate::host::python::dumps::{self, Value, dict}; use crate::host::python::fs::{self, os_error_message}; @@ -50,32 +65,17 @@ use crate::host::python::path::PurePath; use crate::host::python::value::py_str; use crate::host::sandbox::distro::DISTRO_POLICY_REVISION; use crate::host::sandbox::{SandboxLaunch, require_file}; +use crate::host::store::{ + self, IdentityPart, LOCK_NAME, LockedStore, OUTCOME_NAME, RUNTIME_NAME, Recovery, SandboxStore, +}; +use crate::host::supervisor::{self, Launched, Reattached, StopOutcome, Vmm, VmmCommand}; use crate::protocol::{CAPABILITY_LEN, GuestFeatures}; -/// The launch configuration. +/// The launch configuration, the CLI's own record beside those of the store. pub(crate) const CONFIG_NAME: &str = "config.json"; -/// The record of the running OpenVMM process. -pub(crate) const RUNTIME_NAME: &str = "runtime.json"; -/// The control capability of the running OpenVMM process. -pub(crate) const CAPABILITY_NAME: &str = "control.capability"; -/// OpenVMM's output. -pub(crate) const LOG_NAME: &str = "openvmm.log"; -/// The control endpoint on POSIX. -pub(crate) const CONTROL_SOCKET_NAME: &str = "control.sock"; -/// OpenVMM's outcome report. -pub(crate) const OUTCOME_NAME: &str = "outcome.json"; -/// The lifecycle lock. -pub(crate) const LOCK_NAME: &str = "lifecycle.lock"; -/// The files that exist only from the start that launches an OpenVMM process until the stop or -/// failed start that ends it, whether or not the runtime record names it. -pub(crate) const RUNTIME_FILE_NAMES: [&str; 3] = - [RUNTIME_NAME, CAPABILITY_NAME, CONTROL_SOCKET_NAME]; /// How often a caller retries a lifecycle lock that another caller holds, and the removal of a /// state directory that a removed lock file still keeps. pub(crate) const LOCK_RETRY_INTERVAL: Duration = RETRY_INTERVAL; -/// What deprovision writes into the lock file before it removes the file, so that a waiter that -/// then takes the lock of the removed file knows that the directory goes away. -pub(crate) const LOCK_TOMBSTONE: &[u8] = b"deprovisioned\n"; /// The version of outcome reports. pub(crate) const OUTCOME_SCHEMA_VERSION: u64 = 1; @@ -163,28 +163,21 @@ fn make_directories_once(path: &PurePath, mode: u32) -> Result { } } -/// Gives a directory mode 0700, as `os.chmod(path, 0o700)` does: on Windows, that only clears the -/// read-only attribute. -fn make_private(path: &PurePath) -> Result { - #[cfg(unix)] - { - use std::os::unix::fs::PermissionsExt; - - std::fs::set_permissions(path.as_path(), std::fs::Permissions::from_mode(0o700)) - .map_err(|error| os_error(&error, path)) +/// Creates the state directory at `path`, as provision does. An existing path keeps the errors of +/// `Path.mkdir(exist_ok=True)`, which an existing directory does not raise, and stays as it is: the +/// store then refuses a directory that is not private to the current user. A new directory is +/// created as the store creates it, private to the current user, below parents that are created +/// as needed. +pub(crate) fn create_state_directory(path: &PurePath) -> Result { + if fs::lexists(path.as_path()) { + return make_directories(path, 0o700); } - #[cfg(not(unix))] - { - let metadata = std::fs::metadata(path.as_path()).map_err(|error| os_error(&error, path))?; - let mut permissions = metadata.permissions(); - if permissions.readonly() { - #[allow(clippy::permissions_set_readonly_false)] - permissions.set_readonly(false); - std::fs::set_permissions(path.as_path(), permissions) - .map_err(|error| os_error(&error, path))?; - } - Ok(()) + if let Some(parent) = path.parents().next().filter(|parent| parent != path) { + make_directories(&parent, 0o777)?; } + SandboxStore::create(path.as_path()) + .map(drop) + .map_err(error_with_cause) } /// Resolves the state directory at `path`, and creates it with `create`, as @@ -192,8 +185,7 @@ fn make_private(path: &PurePath) -> Result { pub(crate) fn prepare_state_directory(path: &str, create: bool) -> Result { let resolved = fs::resolve(&PurePath::native(path)).map_err(Exit::from_os_error)?; if create { - make_directories(&resolved, 0o700)?; - make_private(&resolved)?; + create_state_directory(&resolved)?; } if fs::is_symlink(resolved.as_path()) || !fs::is_dir(resolved.as_path()) { return Err(Exit::error(format!( @@ -349,115 +341,113 @@ pub(crate) fn provision( write_json(&config_path, &serialize_launch(launch, distro, config)?) } -/// The pid and start time that a runtime record names, as `_runtime_process` reads them. -struct RuntimeProcess { - /// The process ID, of any size. - pid: Int, - /// The start time, if the record has one. - start_time: Option, -} - -/// Reads the pid and start time of a runtime record, as `_runtime_process` does. -fn runtime_process(document: &Document) -> Result { - let invalid_pid = || Exit::error("sandbox runtime state has an invalid process ID"); - let root = document.root(); - let pid = member(document, root, "pid").ok_or_else(invalid_pid)?; - let pid = crate::host::python::value::py_int(document, pid).map_err(|error| match error { - crate::host::python::value::IntError::Invalid => invalid_pid(), - crate::host::python::value::IntError::Overflow => { - Exit::error("cannot convert float infinity to integer") - } - })?; - let start_time = match member(document, root, "start_time").map(|id| document.node(id)) { - None | Some(Node::Null) => None, - Some(Node::Integer(literal)) => { - let value = Int::parse(literal).map_err(|_| invalid_start_time())?; - if value.is_negative() { - return Err(invalid_start_time()); +/// What a transition finds of the sandbox's VM, through [`supervisor::reattach`]. +enum Found { + /// No VM is recorded, or a start that was interrupted is over. + Nothing, + /// The recorded OpenVMM exited without a stop, and its runtime record is cleared. + Exited, + /// The VM runs under the runtime identity of the artifacts. + Live(Vmm), + /// The VM runs under another runtime identity, which differs in these parts. + Mismatched(Vmm, Vec), +} + +/// Finds the sandbox's VM in the locked store, as an orchestrator does after it restarts within +/// the same host boot: with [`supervisor::reattach`], under the runtime identity of `artifacts`. +/// +/// Only a recorded launch needs that identity, so a sandbox that never started, or that stopped, +/// needs no install metadata. Fails for a record that the store does not read, such as one of an +/// earlier format, and when whether the VM runs cannot be determined. +fn find_vm(locked: &mut LockedStore, artifacts: &Artifacts<'_>) -> Result { + if locked.journal().map_err(error_with_cause)?.is_none() + && locked.runtime().map_err(error_with_cause)?.is_none() + { + return Ok(Found::Nothing); + } + let runtime = artifacts.runtime_identity()?; + Ok( + match supervisor::reattach(locked, &runtime).map_err(error_with_cause)? { + Reattached::Live(vmm) => Found::Live(vmm), + Reattached::Mismatched { vmm, differences } => Found::Mismatched(vmm, differences), + Reattached::Exited { + record: Some(_), .. + } => Found::Exited, + Reattached::Exited { record: None, .. } => Found::Nothing, + Reattached::Indeterminate(reason) => { + return Err(Exit::error(format!( + "cannot determine whether the OpenVMM process of the sandbox runs: {reason}" + ))); } - Some(value) - } - Some(_) => return Err(invalid_start_time()), - }; - Ok(RuntimeProcess { pid, start_time }) + }, + ) } -/// Returns the error of a runtime record with an invalid start time. -fn invalid_start_time() -> Exit { - Exit::error("sandbox runtime state has an invalid process start time") +/// Returns the error of a VM that another runtime identity than the artifacts' started, which +/// differs in `differences`: the CLI does not drive it, since it may speak other contracts. +fn mismatched(vmm: &Vmm, differences: &[IdentityPart]) -> Exit { + let parts: Vec<&str> = differences.iter().map(|part| part.name()).collect(); + Exit::error(format!( + "sandbox runs under another runtime identity than these artifacts, which differ in {}: \ + stop it with the artifacts that started it (OpenVMM process {})", + parts.join(", "), + vmm.record().process().pid + )) } -/// A running sandbox, as `_load_running` finds it. +/// The running VM of a sandbox, and the capability with which its control session attaches. struct Running { - /// The runtime record. - runtime: Document, - /// The control capability. + /// The VM. + vmm: Vmm, + /// Its control capability. capability: [u8; CAPABILITY_LEN], } -/// Reads the runtime record and capability of a running sandbox, as `_load_running` does. -fn load_running(state_dir: &PurePath) -> Result { - let runtime_path = state_dir.join(RUNTIME_NAME); - if !fs::is_file(runtime_path.as_path()) { - return Err(Exit::error("sandbox is not running")); - } - let runtime = read_json( - &runtime_path, - "sandbox runtime state", - "format", - &[STATE_FORMAT], - )?; - let process = runtime_process(&runtime)?; - if !process_running(&process.pid, process.start_time.as_ref())? { - return Err(Exit::error( - "sandbox runtime state is stale because the OpenVMM process is not running", - )); - } - let capability_path = state_dir.join(CAPABILITY_NAME); - require_file(capability_path.as_path(), "sandbox control capability")?; - let bytes = std::fs::read(capability_path.as_path()) - .map_err(|error| os_error(&error, &capability_path))?; - let capability: [u8; CAPABILITY_LEN] = bytes - .try_into() - .ok() - .filter(|capability| *capability != [0; CAPABILITY_LEN]) - .ok_or_else(|| Exit::error("sandbox control capability is invalid"))?; - Ok(Running { - runtime, - capability, - }) -} - -/// Returns the control endpoint of a runtime record, as `_endpoint` does. -fn endpoint(runtime: &Document) -> Result { - let value = member(runtime, runtime.root(), "control_endpoint") - .ok_or_else(|| Exit::error("sandbox runtime state has no control endpoint"))?; - py_str(runtime, value) - .ok_or_else(|| Exit::error("sandbox runtime state has no control endpoint")) +/// Returns the running VM of the sandbox in the locked store, as exec and stop require it. +fn running_vm(locked: &mut LockedStore, artifacts: &Artifacts<'_>) -> Result { + let vmm = match find_vm(locked, artifacts)? { + Found::Live(vmm) => vmm, + Found::Nothing => return Err(Exit::error("sandbox is not running")), + Found::Exited => { + return Err(Exit::error( + "sandbox runtime state is stale because the OpenVMM process is not running", + )); + } + Found::Mismatched(vmm, differences) => return Err(mismatched(&vmm, &differences)), + }; + let capability = locked.read_capability().map_err(error_with_cause)?; + Ok(Running { vmm, capability }) } -/// Connects to the running sandbox of `running`. -fn connect(running: &Running, timeout: f64) -> Result { - let endpoint = endpoint(&running.runtime)?; - let process = runtime_process(&running.runtime)?; - // A running process has an ID of 32 bits. - let pid = process.pid.to_u32().unwrap_or_default(); - Session::connect(&endpoint, pid, &running.capability, timeout) +/// Connects to the control endpoint of `vmm`, and attaches with `capability`. +fn connect(vmm: &Vmm, capability: &[u8; CAPABILITY_LEN], timeout: f64) -> Result { + Session::connect( + vmm.endpoint(), + vmm.record().process().pid, + capability, + timeout, + ) } /// Starts a provisioned sandbox, as `start` does. pub(crate) fn start(state_path: &str, artifacts: &Artifacts<'_>, timeout: f64) -> Result { let state_dir = prepare_state_directory(state_path, false)?; - let _lock = LifecycleLock::acquire(&state_dir, timeout, false)?; - start_locked(&state_dir, artifacts, timeout) + let mut lock = LifecycleLock::acquire(&state_dir, timeout, false)?; + start_locked(&state_dir, lock.locked(), artifacts, timeout) } /// The control features that the layered sandbox root must provide for the requests of /// `nvx-rs sandbox`. pub(crate) const REQUIRED_FEATURES: GuestFeatures = GuestFeatures::LAYERED_SANDBOX; -/// Starts the sandbox of a locked state directory, as `_start` does. -fn start_locked(state_dir: &PurePath, artifacts: &Artifacts<'_>, timeout: f64) -> Result { +/// Starts the sandbox of a locked state directory, as `_start` does, through the launch journal +/// of its store. +fn start_locked( + state_dir: &PurePath, + locked: &mut LockedStore, + artifacts: &Artifacts<'_>, + timeout: f64, +) -> Result { let config_path = state_dir.join(CONFIG_NAME); require_file(config_path.as_path(), "sandbox configuration")?; let config = read_json( @@ -466,16 +456,14 @@ fn start_locked(state_dir: &PurePath, artifacts: &Artifacts<'_>, timeout: f64) - "format", &CONFIG_FORMATS, )?; - // Any runtime file belongs to an OpenVMM process that may still run, even without a runtime - // record, as after a start that was killed. Without them, every runtime file that appears - // while this start holds the lifecycle lock is its own. - if RUNTIME_FILE_NAMES - .iter() - .any(|name| fs::lexists(state_dir.join(name).as_path())) - { - return Err(Exit::error( - "sandbox is already running or has stale runtime state", - )); + match find_vm(locked, artifacts)? { + Found::Nothing | Found::Exited => {} + Found::Live(_) => { + return Err(Exit::error( + "sandbox is already running or has stale runtime state", + )); + } + Found::Mismatched(vmm, differences) => return Err(mismatched(&vmm, &differences)), } let outcome_path = state_dir.join(OUTCOME_NAME); unlink_missing_ok(&outcome_path)?; @@ -490,21 +478,14 @@ fn start_locked(state_dir: &PurePath, artifacts: &Artifacts<'_>, timeout: f64) - // The plan takes the format and identity of the distro layer from its binding. let plan = launch.bound_plan(&binding)?; let [executable, kernel, initrd] = artifacts.require()?; + let runtime = artifacts.runtime_identity()?; let capability = random_bytes::()?; if capability == [0; CAPABILITY_LEN] { return Err(Exit::error( "the system returned an all-zero control capability", )); } - let endpoint = if cfg!(windows) { - format!("//./pipe/openvmm-microvm-{}", uuid4_hex()?) - } else { - state_dir - .join(super::lifecycle::CONTROL_SOCKET_NAME) - .as_str() - .to_owned() - }; - // The control endpoint goes between these arguments. + // The control endpoint, which the launch chooses, goes between these arguments. let mut head = vec![executable]; head.extend(plan.openvmm_arguments()?); head.extend( @@ -529,72 +510,49 @@ fn start_locked(state_dir: &PurePath, artifacts: &Artifacts<'_>, timeout: f64) - outcome_path.as_str().to_owned(), ]; extend_recorded_network_arguments(&mut tail, &config)?; - let mut command = head; - command.push(format!("listen={endpoint}")); - command.extend(tail); - - let capability_path = state_dir.join(CAPABILITY_NAME); - create_capability(&capability_path, &capability)?; - let log_path = state_dir.join(LOG_NAME); - let log = match OpenOptions::new() - .append(true) - .create(true) - .open(log_path.as_path()) - { - Ok(log) => log, - Err(error) => { - let _ = unlink_missing_ok(&capability_path); - return Err(os_error(&error, &log_path)); - } + let directory = std::env::current_dir().map_err(Exit::from_os_error)?; + let mut command = Vec::new(); + let mut starting = supervisor::launch(locked, &runtime, &capability, |endpoint| { + command = head; + command.push(format!("listen={endpoint}")); + command.extend(tail); + let arguments = command[1..].iter().map(OsString::from).collect(); + Ok(VmmCommand::new(&command[0], arguments, directory)) + }) + .map_err(error_with_cause)?; + let pid = starting.process().reference().pid; + let Err(error) = check_ready(starting.endpoint(), pid, &capability, timeout) else { + return starting.commit().map(drop).map_err(error_with_cause); }; - let mut launched: Option = None; - let started = launch_and_connect( - &command, - &capability, - log, - &endpoint, - state_dir, - timeout, - &mut launched, - ); - let Err(error) = started else { - if let Some(launched) = launched { - process::detach_child(launched); - } - return Ok(()); + // OpenVMM closes the control endpoint as it tears the VM down, before it publishes its + // outcome report and exits, so only a closed endpoint warrants waiting for it. + let grace = if matches!(error, Exit::EndpointClosed) { + timeout + } else { + 0.0 }; - let mut status = None; - let mut ended = launched.is_none(); - if let Some(launched) = launched.as_mut() { - // OpenVMM closes the control endpoint as it tears the VM down, before it publishes its - // outcome report and exits, so only a closed endpoint warrants waiting for it. - let grace = if matches!(error, Exit::EndpointClosed) { - timeout - } else { - 0.0 - }; - match end_failed_start(launched, grace, &command) { - Ok(exited) => { - status = exited; - ended = true; - } - Err(exit) => return Err(exit), - } - } - if ended { - // This start found none of these files and has held the lifecycle lock since, so they are - // its own. The runtime record goes last, so that an interrupted cleanup leaves a record - // that shows OpenVMM gone rather than runtime files that deprovision cannot vouch for. - unlink_missing_ok(&state_dir.join(CONTROL_SOCKET_NAME))?; - unlink_missing_ok(&capability_path)?; - unlink_missing_ok(&state_dir.join(RUNTIME_NAME))?; - } - match status { + let status = end_failed_start(starting.child(), grace, &command)?; + // OpenVMM has exited, so the launch ends: its capability, control socket, and journal go, + // and its log and outcome report stay. + let unended = match starting.abandon() { + Ok(Recovery::Indeterminate(reason)) => Some(reason), + Ok(_) => None, + Err(failure) => Some(failure.message().to_owned()), + }; + let error = match status { Some(status) if !matches!(error, Exit::Interrupted) => { - Err(Exit::error(startup_exit_message(&outcome_path, status)?)) + Exit::error(startup_exit_message(&outcome_path, status)?) } - _ => Err(error), + _ => error, + }; + if let Some(reason) = unended + && let Some(message) = error.message() + { + return Err(Exit::error(format!( + "{message}; the launch could not be ended, and remains for recovery: {reason}" + ))); } + Err(error) } /// Returns the error of a start whose distro layer fails the check of its binding: a binding that @@ -609,56 +567,6 @@ fn distro_error(error: crate::Error) -> Exit { } } -/// Launches OpenVMM, records it, and waits with [`check_ready`] until its guest is ready, leaving -/// the launched process in `launched` for the caller to end if that fails. -fn launch_and_connect( - command: &[String], - capability: &[u8; CAPABILITY_LEN], - log: File, - endpoint: &str, - state_dir: &PurePath, - timeout: f64, - launched: &mut Option, -) -> Result { - let arguments: Vec = command[1..].iter().map(Into::into).collect(); - let directory = std::env::current_dir().map_err(Exit::from_os_error)?; - let child = process::spawn( - std::path::Path::new(&command[0]), - false, - &arguments, - capability, - log, - &directory, - ) - .map_err(|error| Exit::error(super::spawn_error(&error, &command[0])))?; - let child = launched.insert(child); - // Until this process reaps the child or closes its handle, no other process can reuse its ID, - // so this identifies OpenVMM itself. - let pid = child.id(); - let start_time = process_start_time(&Int::from(pid)) - .map_err(|error| Exit::error(format!("cannot identify the OpenVMM process: {error}")))?; - let Some(start_time) = start_time else { - let exited = child - .wait_timeout(Duration::ZERO) - .map_err(Exit::from_os_error)?; - return Err(Exit::error(if exited.is_some() { - "OpenVMM exited during startup" - } else { - "cannot identify the OpenVMM process" - })); - }; - write_json( - &state_dir.join(RUNTIME_NAME), - &dict([ - ("format", Value::from(STATE_FORMAT)), - ("pid", Value::from(pid)), - ("start_time", Value::from(start_time)), - ("control_endpoint", Value::from(endpoint)), - ]), - )?; - check_ready(endpoint, pid, capability, timeout) -} - /// Waits until the guest of a VM that starts answers on `endpoint`, which OpenVMM process `pid` /// serves, and checks that it provides the features of [`REQUIRED_FEATURES`]. fn check_ready( @@ -726,30 +634,6 @@ fn extend_recorded_network_arguments(command: &mut Vec, config: &Documen Ok(()) } -/// Creates the control capability file of a start, which only it may hold, as -/// `_create_capability` does. -fn create_capability(path: &PurePath, capability: &[u8; CAPABILITY_LEN]) -> Result { - let mut options = OpenOptions::new(); - options.write(true).create_new(true); - #[cfg(unix)] - std::os::unix::fs::OpenOptionsExt::mode(&mut options, 0o600); - let mut file = match options.open(path.as_path()) { - Ok(file) => file, - Err(error) if error.kind() == std::io::ErrorKind::AlreadyExists => { - return Err(Exit::error( - "sandbox is already running or has stale runtime state", - )); - } - Err(error) => return Err(os_error(&error, path)), - }; - if let Err(error) = file.write_all(capability) { - drop(file); - let _ = unlink_missing_ok(path); - return Err(os_error(&error, path)); - } - Ok(()) -} - /// Ends the OpenVMM process of a failed start, as `_end_failed_start` does: returns OpenVMM's /// status if it exits on its own within `grace` seconds, or terminates it and returns `None`. /// Terminating OpenVMM while it publishes its outcome report would leave the report's staging @@ -843,85 +727,111 @@ fn startup_exit_message(outcome_path: &PurePath, status: i64) -> Result )) } -/// Runs a workload in a running sandbox, as `exec_workload` does. +/// Runs a workload in a running sandbox, as `exec_workload` does. The lock is held only while the +/// running VM is found, so a long workload cannot delay a stop. pub(crate) fn exec_workload( state_path: &str, + artifacts: &Artifacts<'_>, request: &ExecRequest, response_timeout: f64, ) -> Result { let state_dir = prepare_state_directory(state_path, false)?; - let running = load_running(&state_dir)?; - let mut session = connect(&running, response_timeout)?; + let running = { + let mut lock = LifecycleLock::acquire(&state_dir, response_timeout, false)?; + running_vm(lock.locked(), artifacts)? + }; + let mut session = connect(&running.vmm, &running.capability, response_timeout)?; session.exec(request, response_timeout) } /// Stops a running sandbox and returns OpenVMM's outcome report, as `stop` does. -pub(crate) fn stop(state_path: &str, timeout: f64) -> Result { +/// +/// The supervisor's stop runs one graceful step, which asks the guest to shut the VM down and +/// waits up to `timeout` seconds for OpenVMM to exit, so that Ctrl-C ends the wait. A VM whose +/// OpenVMM is still running then is reported, and keeps its runtime record: the stop never takes +/// the forced step. +pub(crate) fn stop(state_path: &str, artifacts: &Artifacts<'_>, timeout: f64) -> Result { let state_dir = prepare_state_directory(state_path, false)?; - let _lock = LifecycleLock::acquire(&state_dir, timeout, false)?; - let running = load_running(&state_dir)?; - let process = runtime_process(&running.runtime)?; - { - let mut session = connect(&running, timeout)?; - session.stop(timeout)?; + let mut lock = LifecycleLock::acquire(&state_dir, timeout, false)?; + let locked = lock.locked(); + let Running { vmm, capability } = running_vm(locked, artifacts)?; + // The graceful step waits for the exit itself, so the stop's own wait, until a deadline that + // has passed by then, only checks whether OpenVMM has exited. + let stopped = vmm + .stop(locked, Instant::now(), None, |vmm, _| { + { + let mut session = connect(vmm, &capability, timeout)?; + session.stop(timeout)?; + } + wait_for_exit(vmm, Deadline::after(timeout)) + }) + .map_err(error_with_cause)?; + if let Some(error) = stopped.graceful_error { + return Err(error); } - let deadline = Deadline::after(timeout); - while process_running(&process.pid, process.start_time.as_ref())? { - if deadline.passed() { - return Err(Exit::error("OpenVMM did not terminate after managed stop")); + if stopped.outcome == StopOutcome::TimedOut { + return Err(Exit::error("OpenVMM did not terminate after managed stop")); + } + read_openvmm_outcome(&state_dir.join(OUTCOME_NAME)) +} + +/// Waits until the OpenVMM of `vmm` exits or `deadline` passes, failing if Ctrl-C arrives first. +fn wait_for_exit(vmm: &Vmm, deadline: Deadline) -> Result { + loop { + check_interrupt()?; + let slice = deadline + .remaining() + .map_or(RETRY_INTERVAL, |left| left.min(RETRY_INTERVAL)); + if vmm + .wait(Instant::now() + slice) + .map_err(Exit::from_os_error)? + || deadline.passed() + { + return Ok(()); } - sleep(RETRY_INTERVAL)?; } - let outcome = read_openvmm_outcome(&state_dir.join(OUTCOME_NAME)); - // As in a failed start, the runtime record goes last. - unlink_missing_ok(&state_dir.join(CONTROL_SOCKET_NAME))?; - unlink_missing_ok(&state_dir.join(CAPABILITY_NAME))?; - unlink_missing_ok(&state_dir.join(RUNTIME_NAME))?; - outcome +} + +/// Returns whether `name` is a temporary file that the store leaves beside one of its files when a +/// replacement of the file is interrupted: `.NAME.NUMBER.tmp`. +fn is_store_temporary(name: &str) -> bool { + name.strip_prefix('.') + .and_then(|name| name.strip_suffix(".tmp")) + .and_then(|name| name.rsplit_once('.')) + .is_some_and(|(file, number)| { + store::FILE_NAMES.contains(&file) + && !number.is_empty() + && number.bytes().all(|byte| byte.is_ascii_digit()) + }) } /// Removes a stopped sandbox and its state directory, as `deprovision` does. -pub(crate) fn deprovision(state_path: &str, timeout: f64) -> Result { +pub(crate) fn deprovision(state_path: &str, artifacts: &Artifacts<'_>, timeout: f64) -> Result { let state_dir = prepare_state_directory(state_path, false)?; let mut lock = LifecycleLock::acquire(&state_dir, timeout, false)?; - let runtime_path = state_dir.join(RUNTIME_NAME); - if fs::is_file(runtime_path.as_path()) { - let runtime = read_json( - &runtime_path, - "sandbox runtime state", - "format", - &[STATE_FORMAT], - )?; - let process = runtime_process(&runtime)?; - if process_running(&process.pid, process.start_time.as_ref())? { + match find_vm(lock.locked(), artifacts)? { + Found::Nothing | Found::Exited => {} + Found::Live(_) | Found::Mismatched(..) => { return Err(Exit::error("sandbox must be stopped before deprovision")); } - } else if RUNTIME_FILE_NAMES - .iter() - .any(|name| fs::lexists(state_dir.join(name).as_path())) - { - // As start does, refuse runtime files that no record vouches for. - return Err(Exit::error(format!( - "sandbox has runtime files but no runtime record, so its OpenVMM process may still \ - run: end any OpenVMM process whose arguments name the state directory, then remove \ - {CAPABILITY_NAME} and {CONTROL_SOCKET_NAME}" - ))); } - for name in [ - RUNTIME_NAME, - CAPABILITY_NAME, - CONTROL_SOCKET_NAME, - OUTCOME_NAME, - LOG_NAME, - CONFIG_NAME, - ] { + // No OpenVMM of the sandbox runs, and a start that was interrupted is over, so every file of + // the store goes but its lock, which goes last. + for name in store::FILE_NAMES + .into_iter() + .filter(|&name| name != LOCK_NAME) + .chain([CONFIG_NAME]) + { unlink_missing_ok(&state_dir.join(name))?; } - let unknown: Vec = entries(&state_dir) + let mut unknown: Vec = entries(&state_dir) .map_err(|error| os_error(&error, &state_dir))? .into_iter() .filter(|name| name != LOCK_NAME) .collect(); + for name in unknown.extract_if(.., |name| is_store_temporary(name)) { + unlink_missing_ok(&state_dir.join(&name))?; + } if !unknown.is_empty() { // `iterdir` lists the names in the order of the directory, which the message keeps. return Err(Exit::error(format!( @@ -929,24 +839,20 @@ pub(crate) fn deprovision(state_path: &str, timeout: f64) -> Result { unknown.join(", ") ))); } - // Holding the lock until the directory is gone keeps a waiting transition from creating a new - // lock file in it. + // Holding the lock until the directory is gone keeps a transition that waits for it from + // finding the sandbox halfway removed. lock.remove()?; let lingering = match std::fs::remove_dir(state_dir.as_path()) { Ok(()) => false, Err(error) if directory_not_empty(&error) => true, - Err(error) => { - lock.retract(); - return Err(os_error(&error, &state_dir)); - } + Err(error) => return Err(os_error(&error, &state_dir)), }; if !lingering { - lock.retract(); return Ok(()); } // A removed lock file that a handle still keeps stays in the directory, as do the lock files of - // transitions that start meanwhile. Each waiter closes its handle once it finds the sandbox - // gone, so the directory empties after this call releases the lock. + // transitions that wait meanwhile. Each of them finds no sandbox, and removes its lock file as + // it releases the lock, so the directory empties after this call releases its own. let (removing, held) = hold_directory(&state_dir)?; lock.release(); let removed = remove_emptied_state_directory(&state_dir, removing, timeout); @@ -960,15 +866,16 @@ pub(crate) fn deprovision(state_path: &str, timeout: f64) -> Result { fn hold_directory(path: &PurePath) -> Result<((u64, u128), Option)> { #[cfg(unix)] { - use std::os::unix::fs::OpenOptionsExt; + use std::os::unix::fs::{MetadataExt, OpenOptionsExt}; let file = OpenOptions::new() .read(true) .custom_flags((nix::fcntl::OFlag::O_DIRECTORY | nix::fcntl::OFlag::O_NOFOLLOW).bits()) .open(path.as_path()) .map_err(|error| os_error(&error, path))?; - let status = fs::fstat(&file).map_err(Exit::from_os_error)?; - Ok((status.identity, Some(file))) + // `os.fstat` gives the identity that `os.lstat` gives for the directory's path. + let metadata = file.metadata().map_err(Exit::from_os_error)?; + Ok(((metadata.dev(), u128::from(metadata.ino())), Some(file))) } #[cfg(not(unix))] { diff --git a/nvx-rs/src/host/cli/sandbox/lock.rs b/nvx-rs/src/host/cli/sandbox/lock.rs index cd71874a4..fb2dab88d 100644 --- a/nvx-rs/src/host/cli/sandbox/lock.rs +++ b/nvx-rs/src/host/cli/sandbox/lock.rs @@ -1,31 +1,28 @@ -//! The lifecycle lock of a state directory, `_LifecycleLock` of the former `sandbox_lifecycle.py`, -//! with the file primitives that it shares with that Python implementation, so that an older -//! `nvx.py` and `nvx-rs` exclude each other: +//! The lifecycle lock of a state directory, which is the lock of the sandbox's +//! [`SandboxStore`]: its file `sandbox.lock`. //! -//! - `lifecycle.lock` is opened or created without following a link that it names: with -//! `O_NOFOLLOW` on POSIX, and on Windows with `FILE_FLAG_OPEN_REPARSE_POINT` and every sharing -//! mode, so that deprovision can delete it while others wait for it. It must be a regular file -//! with one link, and not a reparse point. -//! - The lock is `flock(LOCK_EX | LOCK_NB)` on POSIX, and on Windows the lock of the file's first -//! byte, as `msvcrt.locking(fd, LK_NBLCK, 1)` takes it at offset 0. A caller that cannot take -//! it retries every 25 ms within its timeout. -//! - Deprovision writes `deprovisioned\n` into the lock file before it removes the file, so that -//! a waiter that then takes the lock of the removed file knows that the directory goes away. -//! -//! [`LifecycleLock::acquire`] and [`LifecycleLock::release`] follow `__enter__` and `release` -//! step by step; see the comments of `_LifecycleLock`, in the history of -//! `scripts/nvx_tools/sandbox_lifecycle.py`, for the protocol. - -use std::fs::{File, OpenOptions}; -use std::io::{self, Read, Seek, SeekFrom, Write}; - -use super::lifecycle::{ - CONFIG_NAME, LOCK_NAME, LOCK_RETRY_INTERVAL, LOCK_TOMBSTONE, prepare_state_directory, -}; +//! - Provision, start, stop, and deprovision hold the lock for their whole transition, and exec +//! while it finds the running VM. A caller that cannot take the lock retries every 25 ms +//! within its timeout, and Ctrl-C ends the wait. +//! - The store takes the lock of the file that the path names when the lock is granted: a lock +//! file that its holder removed meanwhile locks nothing, so the store takes the lock of the +//! lock file at the path again, which it creates if needed. +//! - Deprovision removes the lock file before the directory. A transition that waited for it then +//! finds the directory gone, or takes the lock of a new lock file in the directory and finds no +//! sandbox there. Every transition that leaves the directory without a provisioned sandbox +//! removes its lock file as it releases the lock, so that the directory empties. + +use std::io; +use std::time::Instant; + +use super::error_with_cause; +use super::lifecycle::{CONFIG_NAME, LOCK_RETRY_INTERVAL, create_state_directory}; +use crate::ErrorKind; use crate::host::cli::deadline::Deadline; use crate::host::cli::{Exit, Result}; use crate::host::python::fs::{self, os_error_message}; use crate::host::python::path::PurePath; +use crate::host::store::{LOCK_NAME, LockedStore, SandboxStore}; /// How Python classifies an `OSError`, as far as the lifecycle tells the classes apart. #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -58,154 +55,11 @@ pub(crate) fn os_error_class(error: &io::Error) -> OsErrorClass { } } -/// Returns whether `error` is `ELOOP`, with which `O_NOFOLLOW` refuses a symbolic link. -fn is_loop(error: &io::Error) -> bool { - #[cfg(unix)] - return error.raw_os_error() == Some(nix::errno::Errno::ELOOP as i32); - #[cfg(not(unix))] - { - let _ = error; - false - } -} - -/// Returns the error of a lock file that is not a regular file with one name. -fn not_standalone(path: &PurePath) -> Exit { - Exit::error(format!( - "sandbox lifecycle lock is not a standalone regular file: {path}" - )) -} - -/// Returns the message of an `OSError` that opening the lock file at `path` raised: with the -/// file's name from `os.open` on POSIX, and without it from `ctypes.WinError` on Windows. -fn open_error(error: &io::Error, path: &PurePath) -> Exit { - let path = if cfg!(windows) { - None - } else { - Some(path.as_str()) - }; - Exit::error(os_error_message(error, path)) -} - -/// Opens or creates `path` without following a link that it names, as `_open_lock_path` does. -fn open_lock_path(path: &PurePath) -> io::Result { - let mut options = OpenOptions::new(); - options.read(true).write(true).create(true); - #[cfg(unix)] - { - use std::os::unix::fs::OpenOptionsExt; - - options - .mode(0o600) - .custom_flags(nix::fcntl::OFlag::O_NOFOLLOW.bits()); - } - #[cfg(windows)] - { - use std::os::windows::fs::OpenOptionsExt; - - /// `FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE`. - const SHARE_ALL: u32 = 0x7; - /// `FILE_ATTRIBUTE_NORMAL`. - const NORMAL: u32 = 0x80; - /// `FILE_FLAG_OPEN_REPARSE_POINT`. - const OPEN_REPARSE_POINT: u32 = 0x0020_0000; - options - .share_mode(SHARE_ALL) - .attributes(NORMAL) - .custom_flags(OPEN_REPARSE_POINT); - } - options.open(path.as_path()) -} - -/// Why opening the lock file failed. -enum OpenFailure { - /// An `OSError` that the caller may handle. - Os(io::Error), - /// Any other failure. - Exit(Exit), -} - -/// Opens the lock file of a state directory, creating it if needed, as `_open_lock_file` does: -/// it must be a regular file that no other name links, and not a reparse point. -fn open_lock_file(path: &PurePath) -> std::result::Result { - let file = match open_lock_path(path) { - Ok(file) => file, - Err(error) if is_loop(&error) => return Err(OpenFailure::Exit(not_standalone(path))), - Err(error) => return Err(OpenFailure::Os(error)), - }; - let status = fs::fstat(&file).map_err(|error| OpenFailure::Exit(Exit::from_os_error(error)))?; - if !status.regular || status.links != 1 || status.reparse_point { - return Err(OpenFailure::Exit(not_standalone(path))); - } - Ok(file) -} - -/// Takes the exclusive lock of an open lock file unless another caller holds it, and returns -/// whether it took it, as `_try_lock_file` does. -fn try_lock_file(file: &File) -> Result { - #[cfg(windows)] - return Ok(crate::host::platform::try_lock_first_byte(file)); - #[cfg(not(windows))] - match file.try_lock() { - Ok(()) => Ok(true), - Err(std::fs::TryLockError::WouldBlock) => Ok(false), - Err(std::fs::TryLockError::Error(error)) => Err(Exit::from_os_error(error)), - } -} - -/// Releases the lock of an open lock file, as `_unlock_file` does. -fn unlock_file(file: &File) -> io::Result<()> { - #[cfg(windows)] - return crate::host::platform::unlock_first_byte(file); - #[cfg(not(windows))] - file.unlock() -} - -/// Returns whether `path` still names the open `file`, as `_names_open_file` does. Windows keeps -/// a removed file in its directory until its last handle closes, and denies access to it -/// meanwhile, which counts as another file. -fn names_open_file(path: &PurePath, file: &File) -> io::Result { - let current = match fs::stat(path.as_path()) { - Ok(current) => current, - Err(error) => { - return match os_error_class(&error) { - OsErrorClass::NotFound => Ok(false), - OsErrorClass::PermissionDenied if cfg!(windows) => Ok(false), - _ => Err(error), - }; - } - }; - Ok(current.identity() == fs::fstat(file)?.identity) -} - -/// Reads up to `size` bytes from the start of the lock file, as `_read_lock_file` does. -fn read_lock_file(mut file: &File, size: usize) -> io::Result> { - file.seek(SeekFrom::Start(0))?; - let mut data = Vec::with_capacity(size); - let read = (&mut file).take(size as u64).read_to_end(&mut data); - file.seek(SeekFrom::Start(0))?; - read.map(|_| data) -} - -/// Writes `data` over the start of the lock file, as `_write_lock_file` does. -fn write_lock_file(mut file: &File, data: &[u8]) -> io::Result<()> { - file.seek(SeekFrom::Start(0))?; - let mut remaining = data; - let written = (|| { - while !remaining.is_empty() { - let count = file.write(remaining)?; - if count == 0 { - return Err(io::Error::other( - "sandbox lifecycle lock write made no progress", - )); - } - remaining = &remaining[count..]; - } - Ok(()) - })(); - // `msvcrt.locking()` unlocks from the file position, as it locked. - file.seek(SeekFrom::Start(0))?; - written +/// Returns the class of the I/O error that caused the store's `error`, if one did. +fn cause_class(error: &crate::Error) -> Option { + std::error::Error::source(error)? + .downcast_ref::() + .map(os_error_class) } /// Returns the error of a transition that still waits for another: Python's `TimeoutError`. @@ -223,7 +77,8 @@ pub(crate) fn directory_status(path: &PurePath) -> Option<(u64, u128)> { /// Returns whether `name` is a removed lock file that a handle still keeps, as `_lingers` does: /// NFS renames a removed file that is still open to `.nfs*`, and Windows file systems without -/// POSIX deletion keep it under its own name. +/// POSIX deletion keep it under its own name, as they keep the lock files that waiting +/// transitions create until they release them. pub(crate) fn lingers(name: &str) -> bool { name == LOCK_NAME || name.starts_with(".nfs") } @@ -254,165 +109,99 @@ pub(crate) fn entries(directory: &PurePath) -> io::Result> { .collect() } -/// Waits while lingering lock files keep the directory that deprovision removes, as -/// `_wait_for_handoff` does. The wait ends once the path names no directory or another one, or -/// the directory holds anything else; a directory that nothing keeps any longer is removed here, -/// in case deprovision has given up. -fn wait_for_handoff(state_dir: &PurePath, removing: (u64, u128), deadline: Deadline) -> Result { - loop { - if directory_status(state_dir) != Some(removing) { - return Ok(()); - } - let Ok(names) = entries(state_dir) else { - return Ok(()); - }; - if !names.iter().all(|name| lingers(name)) { - return Ok(()); - } - if names.is_empty() { - match std::fs::remove_dir(state_dir.as_path()) { - Ok(()) => return Ok(()), - Err(error) if !directory_not_empty(&error) => return Ok(()), - Err(_) => {} - } - } - if !deadline.pause(LOCK_RETRY_INTERVAL)? { - return Err(transition_in_progress()); - } - } -} - -/// The exclusive lock that serializes the lifecycle transitions of a state directory. +/// The exclusive lock that serializes the lifecycle transitions of a state directory: the lock +/// of its sandbox's store, through which the transition changes the store's records. pub(crate) struct LifecycleLock { /// The state directory. state_dir: PurePath, - /// Its lock file. - path: PurePath, - /// The locked file, until the lock is released. - file: Option, + /// The sandbox's locked store, until the lock is released. + locked: Option, + /// Whether the holder removed its lock file, after which the path may name another's. + removed: bool, } impl LifecycleLock { - /// Takes the lock of `state_dir` within `timeout` seconds, as `_LifecycleLock(state_dir, - /// timeout, provision=provision).__enter__()` does. + /// Takes the lock of `state_dir` within `timeout` seconds. /// - /// A caller that takes the lock of a lock file that its holder removed meanwhile holds no - /// lock. Unless it provisions, it retries only while the sandbox is still provisioned, and a - /// provision that finds the lock file or the directory removed waits until the old directory - /// is gone, then provisions a new one, unless another provision has done so first. + /// The directory must be private to the current user, as the store requires of its + /// directory. A provision whose directory deprovision removed meanwhile creates it again; any + /// other transition then fails, as for a path that names no directory. pub(crate) fn acquire(state_dir: &PurePath, timeout: f64, provision: bool) -> Result { if !(timeout > 0.0 && timeout < f64::INFINITY) { return Err(Exit::error( "lifecycle timeout must be finite and greater than 0", )); } - let path = state_dir.join(LOCK_NAME); let deadline = Deadline::after(timeout); loop { - let file = match open_lock_file(&path) { - Ok(file) => file, - Err(OpenFailure::Exit(exit)) => return Err(exit), - Err(OpenFailure::Os(error)) => match os_error_class(&error) { - // Deprovision removed the state directory meanwhile, which only a provision - // creates again. - OsErrorClass::NotFound => { - if !provision { - return Err(Exit::error(format!( - "sandbox state path is not a plain directory: {state_dir}" - ))); - } - if deadline.passed() { - return Err(transition_in_progress()); - } - prepare_state_directory(state_dir.as_str(), true)?; - continue; - } - // Without POSIX deletion, Windows denies opening a removed lock file until - // its last handle closes. - OsErrorClass::PermissionDenied - if provision && cfg!(windows) && deadline.pause(LOCK_RETRY_INTERVAL)? => - { - continue; - } - _ => return Err(open_error(&error, &path)), - }, + let opened = if provision { + SandboxStore::create(state_dir.as_path()) + } else { + SandboxStore::open(state_dir.as_path()) }; - while !try_lock_file(&file)? { - if !deadline.pause(LOCK_RETRY_INTERVAL)? { - return Err(transition_in_progress()); + // The store tries the lock once for a deadline that has passed, so that this wait + // checks for Ctrl-C between tries. + let error = match opened.and_then(|store| store.lock(Some(Instant::now()))) { + Ok(locked) => { + return Ok(Self { + state_dir: state_dir.clone(), + locked: Some(locked), + removed: false, + }); } - } - if names_open_file(&path, &file) - .map_err(|error| Exit::error(os_error_message(&error, Some(path.as_str()))))? - { - return Ok(Self { - state_dir: state_dir.clone(), - path, - file: Some(file), - }); - } - let tombstone = - read_lock_file(&file, LOCK_TOMBSTONE.len()).map_err(Exit::from_os_error)?; - // Deprovision leaves the mark only if lingering lock files, such as the one that this - // handle may keep, kept it from removing the directory, which the path then still - // names. - let removing = if provision && tombstone == LOCK_TOMBSTONE { - directory_status(state_dir) - } else { - None + Err(error) => error, }; - drop(file); - if !provision { - if !fs::lexists(state_dir.join(CONFIG_NAME).as_path()) { - return Err(Exit::error("sandbox is not provisioned")); + match (error.kind(), cause_class(&error)) { + (ErrorKind::TimedOut, _) => {} + // Deprovision removed the state directory meanwhile, which only a provision + // creates again. + (_, Some(OsErrorClass::NotFound)) if !provision => { + return Err(Exit::error(format!( + "sandbox state path is not a plain directory: {state_dir}" + ))); + } + (_, Some(OsErrorClass::NotFound)) => { + if deadline.passed() { + return Err(transition_in_progress()); + } + create_state_directory(state_dir)?; + continue; } - } else if let Some(removing) = removing { - wait_for_handoff(state_dir, removing, deadline)?; + // Without POSIX deletion, Windows denies opening a removed lock file until its + // last handle closes. + (_, Some(OsErrorClass::PermissionDenied)) if provision && cfg!(windows) => {} + _ => return Err(error_with_cause(error)), } - if deadline.passed() { + if !deadline.pause(LOCK_RETRY_INTERVAL)? { return Err(transition_in_progress()); } } } - /// Removes the held lock file, marking it so that waiters know why, as `remove` does. If the - /// removal fails, the mark goes again, as deprovision then keeps the directory. - pub(crate) fn remove(&mut self) -> Result { - let file = self.file.as_ref().expect("the lock is held"); - let removed = write_lock_file(file, LOCK_TOMBSTONE) - .map_err(Exit::from_os_error) - .and_then(|()| { - std::fs::remove_file(self.path.as_path()).map_err(|error| { - Exit::error(os_error_message(&error, Some(self.path.as_str()))) - }) - }); - if removed.is_err() { - self.retract(); - } - removed + /// Returns the sandbox's locked store. + pub(crate) fn locked(&mut self) -> &mut LockedStore { + self.locked.as_mut().expect("the lock is held") } - /// Clears the mark of the removed lock file, so that waiters retry at once, as `retract` - /// does. - pub(crate) fn retract(&mut self) { - if let Some(file) = &self.file { - let _ = write_lock_file(file, &[0; LOCK_TOMBSTONE.len()]); - } + /// Removes the held lock file, as deprovision does before it removes the directory. + pub(crate) fn remove(&mut self) -> Result { + let path = self.state_dir.join(LOCK_NAME); + std::fs::remove_file(path.as_path()) + .map_err(|error| Exit::error(os_error_message(&error, Some(path.as_str()))))?; + self.removed = true; + Ok(()) } - /// Releases the lock, as `release` does: the lock file of a directory that holds no - /// provisioned sandbox goes with it. + /// Releases the lock. The lock file of a directory that holds no provisioned sandbox goes + /// first, unless the holder removed it already, so that the directory can be removed. pub(crate) fn release(&mut self) { - let Some(file) = self.file.take() else { + let Some(locked) = self.locked.take() else { return; }; - if !fs::lexists(self.state_dir.join(CONFIG_NAME).as_path()) - && names_open_file(&self.path, &file).unwrap_or(false) - { - let _ = std::fs::remove_file(self.path.as_path()); + if !self.removed && !fs::lexists(self.state_dir.join(CONFIG_NAME).as_path()) { + let _ = std::fs::remove_file(self.state_dir.join(LOCK_NAME).as_path()); } - // Closing the file releases the lock even if the unlock fails. - let _ = unlock_file(&file); + drop(locked); } } @@ -427,7 +216,9 @@ mod tests { use super::*; fn state_dir(directory: &tempfile::TempDir) -> PurePath { - PurePath::native(directory.path().to_str().unwrap()) + let state = directory.path().join("state"); + SandboxStore::create(&state).unwrap(); + PurePath::native(state.to_str().unwrap()) } #[test] @@ -455,50 +246,44 @@ mod tests { let state = state_dir(&directory); drop(LifecycleLock::acquire(&state, 10.0, true).unwrap()); assert!(!state.join(LOCK_NAME).as_path().exists()); - let error = LifecycleLock::acquire(&state.join("missing"), 1.0, false) - .err() - .unwrap(); + let missing = state.join("missing"); + let error = LifecycleLock::acquire(&missing, 1.0, false).err().unwrap(); assert_eq!( error.message().unwrap(), - format!( - "sandbox state path is not a plain directory: {}", - state.join("missing") - ) + format!("sandbox state path is not a plain directory: {missing}") ); + // A provision creates the directory that deprovision removed meanwhile. + drop(LifecycleLock::acquire(&missing, 1.0, true).unwrap()); + assert!(missing.as_path().is_dir()); } #[test] - fn linked_lock_files_are_refused() { + fn a_removed_lock_file_is_not_removed_again_on_release() { let directory = tempfile::tempdir().unwrap(); let state = state_dir(&directory); - let other = state.join("other"); - std::fs::write(other.as_path(), "").unwrap(); - if std::fs::hard_link(other.as_path(), state.join(LOCK_NAME).as_path()).is_err() { - return; - } - let error = LifecycleLock::acquire(&state, 1.0, true).err().unwrap(); - assert_eq!( - error.message().unwrap(), - format!( - "sandbox lifecycle lock is not a standalone regular file: {}", - state.join(LOCK_NAME) - ) - ); + let mut lock = LifecycleLock::acquire(&state, 10.0, true).unwrap(); + lock.remove().unwrap(); + assert!(!state.join(LOCK_NAME).as_path().exists()); + // A transition that waited takes the lock of a new lock file, which stays its own. + let other = LifecycleLock::acquire(&state, 1.0, true).unwrap(); + lock.release(); + assert!(state.join(LOCK_NAME).as_path().is_file()); + drop(other); + assert!(!state.join(LOCK_NAME).as_path().exists()); } + #[cfg(unix)] #[test] - fn deprovision_marks_the_removed_lock_file() { + fn directories_that_others_may_access_are_refused() { + use std::os::unix::fs::PermissionsExt; + let directory = tempfile::tempdir().unwrap(); let state = state_dir(&directory); - let mut lock = LifecycleLock::acquire(&state, 10.0, true).unwrap(); - let file = lock.file.as_ref().unwrap().try_clone().unwrap(); - lock.remove().unwrap(); - assert_eq!(read_lock_file(&file, 64).unwrap(), LOCK_TOMBSTONE); - lock.retract(); + std::fs::set_permissions(state.as_path(), std::fs::Permissions::from_mode(0o755)).unwrap(); + let error = LifecycleLock::acquire(&state, 1.0, false).err().unwrap(); assert_eq!( - read_lock_file(&file, 64).unwrap(), - vec![0; LOCK_TOMBSTONE.len()] + error.message().unwrap(), + format!("the sandbox store {state} is accessible to other users (mode 755)") ); - assert!(!state.join(LOCK_NAME).as_path().exists()); } } diff --git a/nvx-rs/src/host/cli/sandbox/mod.rs b/nvx-rs/src/host/cli/sandbox/mod.rs index 4d29b62b4..d1278dabb 100644 --- a/nvx-rs/src/host/cli/sandbox/mod.rs +++ b/nvx-rs/src/host/cli/sandbox/mod.rs @@ -10,7 +10,6 @@ //! choices, and fails as `argparse` fails, with status 2. The `--mount-deny`, `--mount-allow`, //! and `--mount-write` options name paths of the `--mount` before them. -mod identity; mod lifecycle; mod lock; mod state; @@ -26,6 +25,7 @@ use crate::host::artifacts::{Guest, KERNEL_NAME}; use crate::host::network::policy::compile_policy_file; use crate::host::platform; use crate::host::python::builtins::{Int, parse_float}; +use crate::host::python::fs::os_error_message; use crate::host::python::path::PurePath; use crate::host::python::shell::format_command; use crate::host::sandbox::mount::{MountOwner, PolicyKind}; @@ -37,6 +37,7 @@ use crate::host::sandbox::{ DEFAULT_ENTRYPOINT, DEFAULT_HOSTNAME, DEFAULT_WORKLOAD_IDENTITY, SandboxLaunch, SandboxLayer, SandboxSettings, parse_workload_identity, require_file, }; +use crate::host::store::RuntimeIdentity; pub(crate) use self::lifecycle::ProvisionConfig; use self::state::RecordedDistro; @@ -556,6 +557,32 @@ impl Artifacts<'_> { path_string(&initrd)?, ]) } + + /// Returns the runtime identity of the artifacts, which the install metadata of the root + /// names, as [`ArtifactDir::runtime_identity`](crate::host::artifacts::ArtifactDir::runtime_identity) + /// reads it. `NVX_OPENVMM` replaces the OpenVMM executable that runs, but not its identity, + /// which stays the root's. + pub(crate) fn runtime_identity(&self) -> Result { + locate(self.root)? + .dir + .runtime_identity() + .map_err(error_with_cause) + } +} + +/// Returns the error that prints the message of `error`, followed by the I/O error or other +/// cause that it keeps, if any, as Python prints an `OSError`. +pub(crate) fn error_with_cause(error: crate::Error) -> Exit { + let cause = std::error::Error::source(&error).map(|source| { + match source.downcast_ref::() { + Some(cause) => os_error_message(cause, None), + None => source.to_string(), + } + }); + match cause { + Some(cause) => Exit::error(format!("{}: {cause}", error.message())), + None => Exit::from(error), + } } /// Runs the operation of `options`, as `command_sandbox` does. @@ -663,6 +690,7 @@ pub(super) fn run(root: Option<&str>, options: Options) -> Result { arguments.extend(options.args.iter().cloned()); let result = lifecycle::exec_workload( state_dir, + &artifacts, &super::session::ExecRequest { arguments, timeout_ms: options.exec_timeout_ms.clone(), @@ -681,10 +709,10 @@ pub(super) fn run(root: Option<&str>, options: Options) -> Result { Err(Exit::Status(result.returncode)) } (SandboxOperation::Stop, _) => { - lifecycle::stop(state_dir("stop")?, options.timeout).map(drop) + lifecycle::stop(state_dir("stop")?, &artifacts, options.timeout).map(drop) } (SandboxOperation::Deprovision, _) => { - lifecycle::deprovision(state_dir("deprovision")?, options.timeout) + lifecycle::deprovision(state_dir("deprovision")?, &artifacts, options.timeout) } (SandboxOperation::Run, Some((launch, rules, binding))) => { if options.state_dir.is_some() { diff --git a/nvx-rs/src/host/cli/sandbox/state.rs b/nvx-rs/src/host/cli/sandbox/state.rs index b87c5f5f3..162ff7c77 100644 --- a/nvx-rs/src/host/cli/sandbox/state.rs +++ b/nvx-rs/src/host/cli/sandbox/state.rs @@ -1,4 +1,4 @@ -//! The files of a state directory: `config.json` in formats 1 to 6, `runtime.json`, and the JSON +//! The CLI's own record in a state directory, `config.json` in formats 1 to 6, and the JSON //! reading and writing of the former `sandbox_lifecycle.py`. //! //! - [`write_json`] writes `json.dumps(value, indent=2, sort_keys=True) + "\n"` as @@ -36,8 +36,6 @@ use crate::host::sandbox::mount::{ }; use crate::host::sandbox::{SandboxLaunch, SandboxSettings}; -/// The format of `runtime.json`, and the version of the files that `_read_json` reads by default. -pub(crate) const STATE_FORMAT: u64 = 1; /// The configuration of a sandbox without shares. pub(crate) const CONFIG_FORMAT: u64 = 1; /// The configuration of a sandbox with one share, which format-1 readers would start without it. diff --git a/nvx-rs/src/host/platform/windows.rs b/nvx-rs/src/host/platform/windows.rs index 5721e8246..ff92da84a 100644 --- a/nvx-rs/src/host/platform/windows.rs +++ b/nvx-rs/src/host/platform/windows.rs @@ -34,8 +34,6 @@ use windows_sys::Win32::Storage::FileSystem::{ READ_CONTROL, ReadFile, SECURITY_IDENTIFICATION, SECURITY_SQOS_PRESENT, WriteFile, }; #[cfg(feature = "cli")] -use windows_sys::Win32::Storage::FileSystem::{LockFile, UnlockFile}; -#[cfg(feature = "cli")] use windows_sys::Win32::System::Console::{CTRL_C_EVENT, SetConsoleCtrlHandler}; use windows_sys::Win32::System::IO::{CancelIoEx, GetOverlappedResult, OVERLAPPED}; use windows_sys::Win32::System::LibraryLoader::{ @@ -1278,27 +1276,6 @@ pub(crate) fn interrupted() -> bool { INTERRUPTED.load(std::sync::atomic::Ordering::SeqCst) } -/// Locks the first byte of `file` unless another handle holds a lock on it, as -/// `msvcrt.locking(fd, LK_NBLCK, 1)` does at offset 0, and returns whether it took the lock. -/// -/// Like the C runtime, which reports every failure of `LockFile` as `EACCES` in this mode, any -/// failure counts as a lock that another handle holds. -#[cfg(feature = "cli")] -pub(crate) fn try_lock_first_byte(file: &fs::File) -> bool { - // SAFETY: the handle is open for as long as `file` is borrowed. - unsafe { LockFile(file.as_raw_handle() as HANDLE, 0, 0, 1, 0) != 0 } -} - -/// Unlocks the first byte of `file`, as `msvcrt.locking(fd, LK_UNLCK, 1)` does at offset 0. -#[cfg(feature = "cli")] -pub(crate) fn unlock_first_byte(file: &fs::File) -> io::Result<()> { - // SAFETY: the handle is open for as long as `file` is borrowed. - if unsafe { UnlockFile(file.as_raw_handle() as HANDLE, 0, 0, 1, 0) } == 0 { - return Err(io::Error::last_os_error()); - } - Ok(()) -} - #[cfg(test)] mod tests { use std::io::Read; diff --git a/nvx-rs/src/host/process.rs b/nvx-rs/src/host/process.rs index b45186445..97e560bc1 100644 --- a/nvx-rs/src/host/process.rs +++ b/nvx-rs/src/host/process.rs @@ -17,9 +17,8 @@ //! //! The [`supervisor`] launches OpenVMM, and its [`Launched::process`] holds the child that it //! started through a handle of its own. [`spawn`], [`detach_child`], and [`kill_child`] launch and -//! release OpenVMM for the CLI and [`instance`](super::instance), which keep records of their own: -//! they go through the supervisor, and remain until those callers move to it, as do -//! [`wait_for_exit`] and [`kill`]. +//! release OpenVMM for [`instance`](super::instance), which keeps records of its own: they go +//! through the supervisor, and remain until it moves to it, as do [`wait_for_exit`] and [`kill`]. use std::ffi::OsString; use std::fmt; diff --git a/nvx-rs/src/host/python/fs.rs b/nvx-rs/src/host/python/fs.rs index e6be18b36..6a25c9769 100644 --- a/nvx-rs/src/host/python/fs.rs +++ b/nvx-rs/src/host/python/fs.rs @@ -5,11 +5,10 @@ //! - [`is_file`], [`is_dir`], [`is_symlink`], and [`exists`]: `os.path.isfile`, `isdir`, `islink`, //! and `exists`, which follow symbolic links, except `islink`, and are false on any error. On //! Windows, only `IO_REPARSE_TAG_SYMLINK` reparse points are links, not junctions. -//! - [`stat`], [`lstat`], and [`fstat`]: the identity and owner that `os.stat` and `os.lstat` -//! report for a path, and the identity, type, and link count that `os.fstat` reports for an -//! open file. On Windows, the identity is the 64-bit volume serial number and the 128-bit file -//! ID of `FILE_ID_INFO`, as Python 3.12 and later report them, which also tell apart the files -//! of ReFS volumes. [`lexists`] is `os.path.lexists`. +//! - [`stat`] and [`lstat`]: the identity and owner that `os.stat` and `os.lstat` report for a +//! path. On Windows, the identity is the 64-bit volume serial number and the 128-bit file ID +//! of `FILE_ID_INFO`, as Python 3.12 and later report them, which also tell apart the files of +//! ReFS volumes. [`lexists`] is `os.path.lexists`. //! - [`resolve`]: `Path.resolve()`, which follows `os.path.realpath`: it resolves symbolic links //! component by component on POSIX, keeping the components that do not exist, and asks Windows //! for the final path of the longest existing prefix, dropping the `\\?\` prefix again. @@ -92,26 +91,6 @@ pub(crate) fn lexists(path: &Path) -> bool { lstat(path).is_ok() } -/// The fields of `os.fstat` on an open file that NVX reads. -#[cfg(all(feature = "cli", any(unix, windows)))] -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub(crate) struct FileStat { - /// `st_dev` and `st_ino`, the identity that [`Stat::identity`] gives for the same file. - pub(crate) identity: (u64, u128), - /// Whether `st_mode` is a regular file. - pub(crate) regular: bool, - /// `st_nlink`: the number of names that link the file. - pub(crate) links: u64, - /// Whether the file is a reparse point, from `st_file_attributes` on Windows; never on POSIX. - pub(crate) reparse_point: bool, -} - -/// Returns `os.fstat` of an open file, as CPython's `_Py_fstat_noraise` computes it. -#[cfg(all(feature = "cli", any(unix, windows)))] -pub(crate) fn fstat(file: &std::fs::File) -> io::Result { - imp::fstat(file) -} - /// Returns `path.resolve()`: the absolute path of `path`, with symbolic links resolved and `..` /// components removed, as `os.path.realpath(path)` gives it. pub(crate) fn resolve(path: &PurePath) -> io::Result { @@ -196,18 +175,6 @@ mod imp { }) } - /// See [`super::fstat`]. - #[cfg(feature = "cli")] - pub(super) fn fstat(file: &fs::File) -> io::Result { - let metadata = file.metadata()?; - Ok(super::FileStat { - identity: (metadata.dev(), u128::from(metadata.ino())), - regular: metadata.file_type().is_file(), - links: metadata.nlink(), - reparse_point: false, - }) - } - /// Returns `posixpath.realpath(path)` in its non-strict mode: each component is resolved in /// turn, a symbolic link is replaced by its target, and a component that cannot be examined /// is kept as it is. A symbolic-link loop stops resolution at the link. @@ -361,18 +328,6 @@ mod imp { }) } - /// See [`super::fstat`]. - #[cfg(feature = "cli")] - pub(super) fn fstat(file: &std::fs::File) -> io::Result { - let status = win32::fstat(file)?; - Ok(super::FileStat { - identity: (status.device, status.inode), - regular: status.mode == StatMode::Regular, - links: u64::from(status.links), - reparse_point: status.reparse_point, - }) - } - /// Returns `ntpath.normcase(path)`: lowercase, with `\` separators. fn normcase(path: &str) -> String { path.replace('/', "\\").to_lowercase() diff --git a/nvx-rs/src/host/python/mod.rs b/nvx-rs/src/host/python/mod.rs index 9d5814a4c..e344cf9df 100644 --- a/nvx-rs/src/host/python/mod.rs +++ b/nvx-rs/src/host/python/mod.rs @@ -17,7 +17,7 @@ //! for it, and the pure paths of `pathlib` with the `os.path` operations that they use. //! - [`uuid`]: the UUIDs that `uuid.UUID` reads. //! - [`fs`]: the filesystem queries of `os`, `os.path`, and `pathlib.Path`, among them -//! `Path.resolve()` and the file identities of `os.stat`, `os.lstat`, and `os.fstat`. +//! `Path.resolve()` and the file identities of `os.stat` and `os.lstat`. //! - [`shell`]: `subprocess.list2cmdline` and `shlex.join`, with which `nvx.py` prints the //! commands that it runs. //! - [`value`]: `str()`, `int()`, and `==` of the values that `json.loads` returns, as `nvx.py` diff --git a/nvx-rs/src/host/python/windows.rs b/nvx-rs/src/host/python/windows.rs index d8804eb64..86e886658 100644 --- a/nvx-rs/src/host/python/windows.rs +++ b/nvx-rs/src/host/python/windows.rs @@ -360,84 +360,6 @@ fn stat_wide(path: &[u16], mut traverse: bool) -> io::Result { }) } -/// Returns `os.fstat` of an open file, as CPython's `_Py_fstat_noraise` computes it: a disk file's -/// type, identity, and link count come from `GetFileInformationByHandle` and `FILE_ID_INFO`, as -/// [`stat`] reads them, and its attributes tell whether the handle is a reparse point that was -/// opened as itself. -#[cfg(feature = "cli")] -pub(crate) fn fstat(file: &impl AsRawHandle) -> io::Result { - let handle = file.as_raw_handle() as HANDLE; - // SAFETY: the handle is open for as long as `file` is borrowed. - let file_type = unsafe { GetFileType(handle) }; - if file_type != FILE_TYPE_DISK { - if file_type == FILE_TYPE_UNKNOWN && last_error() != 0 { - return Err(io::Error::last_os_error()); - } - let mode = match file_type { - FILE_TYPE_CHAR => StatMode::Character, - FILE_TYPE_PIPE => StatMode::Fifo, - _ => StatMode::Unknown, - }; - return Ok(HandleStatus { - mode, - device: 0, - inode: 0, - links: 0, - reparse_point: false, - }); - } - // SAFETY: BY_HANDLE_FILE_INFORMATION is plain data, for which all-zero bytes are valid. - let mut information: BY_HANDLE_FILE_INFORMATION = unsafe { mem::zeroed() }; - // SAFETY: the handle is open and the output pointer is valid. - if unsafe { GetFileInformationByHandle(handle, &mut information) } == 0 { - return Err(io::Error::last_os_error()); - } - // SAFETY: FILE_ID_INFO is plain data, for which all-zero bytes are valid. - let mut id: FILE_ID_INFO = unsafe { mem::zeroed() }; - // SAFETY: the handle is open, and the buffer is a FILE_ID_INFO of the size that is passed. - let has_id = unsafe { - GetFileInformationByHandleEx( - handle, - FileIdInfo, - (&raw mut id).cast::(), - mem::size_of::() as u32, - ) - } != 0; - let device = if has_id { - id.VolumeSerialNumber - } else { - u64::from(information.dwVolumeSerialNumber) - }; - let inode = match has_id.then(|| u128::from_le_bytes(id.FileId.Identifier)) { - Some(inode) if inode != 0 => inode, - _ => (u128::from(information.nFileIndexHigh) << 32) | u128::from(information.nFileIndexLow), - }; - Ok(HandleStatus { - mode: mode(information.dwFileAttributes, 0), - device, - inode, - links: information.nNumberOfLinks, - reparse_point: information.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT != 0, - }) -} - -/// The fields of `os.fstat` that NVX reads. -#[cfg(feature = "cli")] -#[derive(Clone, Copy, Debug)] -pub(crate) struct HandleStatus { - /// The type of file. `os.fstat` reads no reparse tag, so a link opened as itself is no - /// [`StatMode::Link`]. - pub(crate) mode: StatMode, - /// `st_dev`: the volume serial number. - pub(crate) device: u64, - /// `st_ino`: the file ID. - pub(crate) inode: u128, - /// `st_nlink`: the number of names that link the file. - pub(crate) links: u32, - /// Whether `st_file_attributes` has `FILE_ATTRIBUTE_REPARSE_POINT`. - pub(crate) reparse_point: bool, -} - /// Returns `nt._getfinalpathname(path)`: the path that Windows gives for the file, with its /// `\\?\` prefix. pub(crate) fn final_path_name(path: &Path) -> io::Result { diff --git a/nvx-rs/src/host/store/mod.rs b/nvx-rs/src/host/store/mod.rs index cf3ae3892..9fa7f65fe 100644 --- a/nvx-rs/src/host/store/mod.rs +++ b/nvx-rs/src/host/store/mod.rs @@ -173,9 +173,9 @@ use crate::protocol::CAPABILITY_LEN; /// Version of the store's records. /// -/// It follows the versions of the runtime state that the CLI and [`instance`](super::instance) -/// keep on their own, up to 3, so that a record of theirs fails as unsupported rather than being -/// read as one of the store's. +/// It follows the versions of the runtime state that the CLI kept on its own, 1, and that +/// [`instance`](super::instance) keeps, up to 3, so that a record of theirs fails as unsupported +/// rather than being read as one of the store's. pub const FORMAT: u32 = 4; /// Name of the sandbox's lock. pub const LOCK_NAME: &str = "sandbox.lock"; diff --git a/nvx-rs/src/host/supervisor/mod.rs b/nvx-rs/src/host/supervisor/mod.rs index 413f35e51..f3c685a2a 100644 --- a/nvx-rs/src/host/supervisor/mod.rs +++ b/nvx-rs/src/host/supervisor/mod.rs @@ -261,6 +261,17 @@ impl<'s> Starting<'s> { &self.inner().process } + /// Returns the VMM's child, through which the CLI waits for a VMM whose start failed, reads + /// its exit status, and ends it as `subprocess` ends it, before it abandons the launch. + #[cfg(feature = "cli")] + pub(crate) fn child(&mut self) -> &mut Launched { + &mut self + .inner + .as_mut() + .expect("a launch is in progress until it is committed or abandoned") + .child + } + /// Commits the launch of a VMM that serves its control endpoint: writes its runtime record, /// as [`Launch::commit`] does, releases the VMM's child, which keeps running, and returns the /// VM. diff --git a/nvx-rs/src/testing/fake_openvmm.rs b/nvx-rs/src/testing/fake_openvmm.rs index 1af037916..58792fe3a 100644 --- a/nvx-rs/src/testing/fake_openvmm.rs +++ b/nvx-rs/src/testing/fake_openvmm.rs @@ -41,7 +41,8 @@ //! //! Kernel command-line tokens adjust the emulation: `fake_exit_on_start=CODE` fails the launch, //! `fake_boot_delay_ms=MS` delays the control endpoint, `fake_crash_after_ms=MS` makes the VM -//! die, `fake_ignore_stop=1` ignores stop requests, `fake_ignore_cancel=1` ignores cancellation, +//! die, `fake_ignore_stop=1` ignores stop requests, `fake_exit_delay_ms=MS` delays the exit after +//! an acknowledged stop, `fake_ignore_cancel=1` ignores cancellation, //! `fake_guest_features=MASK` advertises the decimal feature mask `MASK` instead of the current //! guest's, `fake_legacy_guest=1` refuses the features request like a guest agent that //! predates it, and `fake_refuse_maps=1` fails every `MAPS` request like a guest that cannot @@ -195,6 +196,8 @@ struct Options { crash_after: Option, /// Whether stop requests are deliberately left unanswered. ignore_stop: bool, + /// Artificial delay between the acknowledgement of a stop and the exit. + exit_delay: Duration, /// Whether cancellation requests are deliberately ignored. ignore_cancel: bool, /// Whether the guest omits feature negotiation like an older image. @@ -605,6 +608,7 @@ fn parse(program: &str, arguments: &[String]) -> Result { boot_delay: millis("fake_boot_delay_ms").unwrap_or_default(), crash_after: millis("fake_crash_after_ms"), ignore_stop: knob("fake_ignore_stop") == Some("1"), + exit_delay: millis("fake_exit_delay_ms").unwrap_or_default(), ignore_cancel: knob("fake_ignore_cancel") == Some("1"), legacy_guest: knob("fake_legacy_guest") == Some("1"), refuse_maps: knob("fake_refuse_maps") == Some("1"), @@ -1357,6 +1361,7 @@ fn serve_client( match session.run(options, guest) { Ok(Flow::Exit) => { write_report(options, "managed", "success", 0); + thread::sleep(options.exit_delay); Flow::Exit } _ => Flow::Continue, diff --git a/nvx-rs/tests/cli.rs b/nvx-rs/tests/cli.rs index 5f84ee72b..fae746eac 100644 --- a/nvx-rs/tests/cli.rs +++ b/nvx-rs/tests/cli.rs @@ -22,8 +22,9 @@ use std::fs; use std::path::{Path, PathBuf}; use std::process::{Command, Output, Stdio}; use std::thread; -use std::time::Duration; +use std::time::{Duration, Instant}; +use nvx::host::process::{Process, ProcessRef}; use serde_json::{Value, json}; /// The recorded scenarios. @@ -41,7 +42,7 @@ const MODE_NAMES: [&str; 5] = [ "config.json", "runtime.json", "control.capability", - "lifecycle.lock", + "sandbox.lock", ]; /// How many scenarios run at once. const PARALLELISM: usize = 4; @@ -111,6 +112,7 @@ impl Root { for prefix in [ "\"pid\": ", "\"start_time\": ", + "\"startTime\": ", "\"device\": ", "\"seconds\": ", "\"nanoseconds\": ", @@ -122,6 +124,8 @@ impl Root { "\"operation_id\": \"", "\"instance_id\": \"", "\"fileId\": \"", + "\"launch\": \"", + "\"boot\": \"", ] { text = replace_after(&text, prefix, is_hex, Some(32), "{hex32}"); } @@ -178,8 +182,8 @@ impl Root { Value::Object(entry) } - /// Ends the fake OpenVMM processes that the runtime records below the root name, so that the - /// root can be removed after a failure. + /// Ends the fake OpenVMM processes that the runtime records and launch journals below the root + /// name, so that the root can be removed after a failure. fn end_leftovers(&self) { let mut pending = vec![self.path.clone()]; while let Some(directory) = pending.pop() { @@ -190,11 +194,12 @@ impl Root { let path = entry.path(); if path.is_dir() { pending.push(path); - } else if entry.file_name() == "runtime.json" + } else if (entry.file_name() == "runtime.json" + || entry.file_name() == "launch.json") && let Some(pid) = fs::read_to_string(&path) .ok() .and_then(|text| serde_json::from_str::(&text).ok()) - .and_then(|record| record["pid"].as_u64()) + .and_then(|record| record["process"]["pid"].as_u64()) { kill(pid); } @@ -530,7 +535,7 @@ fn start_requires_the_guest_features_of_layered_sandboxes() { .map(|entry| entry.unwrap().file_name().into_string().unwrap()) .collect(); names.sort(); - assert_eq!(names, ["config.json", "lifecycle.lock", "openvmm.log"]); + assert_eq!(names, ["config.json", "openvmm.log", "sandbox.lock"]); let output = run(&root, &lifecycle("deprovision", &root, state)); assert!(output.status.success(), "{}", text(&output.stderr)); } @@ -623,10 +628,7 @@ fn refused_start(root: &Root, state: &str, message: &str) { let output = run(root, &lifecycle("start", root, state)); assert_eq!(output.status.code(), Some(1), "{}", text(&output.stderr)); assert_eq!(text(&output.stderr), format!("error: {message}\n")); - assert_eq!( - state_entries(root, state), - ["config.json", "lifecycle.lock"] - ); + assert_eq!(state_entries(root, state), ["config.json", "sandbox.lock"]); let output = run(root, &lifecycle("deprovision", root, state)); assert!(output.status.success(), "{}", text(&output.stderr)); } @@ -752,7 +754,7 @@ fn a_distro_manifest_path_whose_lookup_fails_refuses_provision_and_start() { failed(&run(&root, &lifecycle("start", &root, "absent"))); assert_eq!( state_entries(&root, "absent"), - ["config.json", "lifecycle.lock"] + ["config.json", "sandbox.lock"] ); let output = run(&root, &lifecycle("deprovision", &root, "absent")); assert!(output.status.success(), "{}", text(&output.stderr)); @@ -930,6 +932,25 @@ fn start_refuses_a_configuration_without_a_valid_distro_binding() { } } +/// Runs the lifecycle `operation` on `state` below the root, with `extra` arguments, and returns +/// its exit status and standard error. +fn transition(root: &Root, operation: &str, state: &str, extra: &[&str]) -> (Option, String) { + let mut arguments = lifecycle(operation, root, state); + arguments.extend(extra.iter().map(|argument| (*argument).to_owned())); + let output = run(root, &arguments); + (output.status.code(), text(&output.stderr)) +} + +/// Returns the exit status and standard error of a transition that succeeds. +fn succeeded() -> (Option, String) { + (Some(0), String::new()) +} + +/// Returns the exit status and standard error of a transition that fails with `message`. +fn failed(message: &str) -> (Option, String) { + (Some(1), format!("error: {message}\n")) +} + /// Provisions a minimal sandbox in `state` below the root, with `command_line` for the fake /// OpenVMM. fn provisioned_with(root: &Root, state: &str, command_line: &str) { @@ -937,6 +958,19 @@ fn provisioned_with(root: &Root, state: &str, command_line: &str) { assert!(output.status.success(), "{}", text(&output.stderr)); } +/// Returns the runtime record of the sandbox in `state` below the root, if it has one. +fn runtime_record(root: &Root, state: &str) -> Option { + let text = fs::read_to_string(root.path.join(state).join("runtime.json")).ok()?; + Some(serde_json::from_str(&text).unwrap()) +} + +/// Returns the OpenVMM process that the runtime record of `state` names, while it runs. +fn recorded_process(root: &Root, state: &str) -> Option { + let record = runtime_record(root, state)?; + let reference: ProcessRef = serde_json::from_value(record["process"].clone()).unwrap(); + Process::open(&reference).unwrap() +} + /// Returns the names in the state directory `state` below the root, sorted. fn state_entries(root: &Root, state: &str) -> Vec { let mut names: Vec = fs::read_dir(root.path.join(state)) @@ -947,6 +981,180 @@ fn state_entries(root: &Root, state: &str) -> Vec { names } +/// The error of exec and stop for a sandbox whose OpenVMM exited without a stop. +const STALE: &str = "sandbox runtime state is stale because the OpenVMM process is not running"; + +#[test] +fn a_restarted_cli_drives_only_a_vm_of_its_runtime_identity() { + // Each run of the CLI is a new orchestrator, which finds the VM that an earlier one started + // within the same boot through the runtime record of the store. + let root = populated_root(); + provisioned(&root, "state"); + assert_eq!(transition(&root, "start", "state", &[]), succeeded()); + let record = runtime_record(&root, "state").unwrap(); + // The record names the artifacts by the digests that their build provenance records. + for (part, name, field) in [ + ("openvmm", "openvmm", "executable_sha256"), + ("kernel", "vmlinux", "kernel_sha256"), + ("initramfs", "initramfs", "initramfs_sha256"), + ] { + let path = root.below(&format!("build/{name}.provenance.json")); + let provenance: Value = serde_json::from_str(&fs::read_to_string(path).unwrap()).unwrap(); + assert_eq!( + record["runtime"][part].as_str().unwrap(), + format!("sha256:{}", provenance[field].as_str().unwrap()) + ); + } + let mut exec = lifecycle("exec", &root, "state"); + exec.extend(["--entrypoint", "/bin/echo", "--arg", "reattached"].map(str::to_owned)); + let output = run(&root, &exec); + assert_eq!( + (output.status.code(), text(&output.stdout)), + (Some(0), "reattached\n".to_owned()), + "{}", + text(&output.stderr) + ); + + // Artifacts of another kernel did not start the VM, so no transition drives it, and it runs + // on. + let kernel = root.below("build/vmlinux.provenance.json"); + let provenance = fs::read_to_string(&kernel).unwrap(); + let other = format!( + "{{\"format\": 1, \"kernel_sha256\": \"{}\"}}\n", + "0".repeat(64) + ); + fs::write(&kernel, other).unwrap(); + let pid = record["process"]["pid"].as_u64().unwrap(); + let mismatched = format!( + "sandbox runs under another runtime identity than these artifacts, which differ in \ + kernel: stop it with the artifacts that started it (OpenVMM process {pid})" + ); + for operation in ["start", "exec", "stop"] { + assert_eq!( + transition(&root, operation, "state", &[]), + failed(&mismatched), + "{operation}" + ); + } + assert_eq!( + transition(&root, "deprovision", "state", &[]), + failed("sandbox must be stopped before deprovision") + ); + assert_eq!(runtime_record(&root, "state").unwrap(), record); + assert!(recorded_process(&root, "state").is_some()); + + // The artifacts that started it drive it again. + fs::write(&kernel, provenance).unwrap(); + assert_eq!(transition(&root, "stop", "state", &[]), succeeded()); + assert_eq!(runtime_record(&root, "state"), None); + assert_eq!(transition(&root, "deprovision", "state", &[]), succeeded()); +} + +#[test] +fn runtime_records_of_earlier_formats_fail_and_name_their_format() { + let root = populated_root(); + provisioned(&root, "state"); + // The runtime record that the CLI wrote before the store, in its format 1. + let path = root.path.join("state").join("runtime.json"); + let endpoint = root.path.join("state").join("control.sock"); + let record = format!( + "{{\n \"control_endpoint\": {},\n \"format\": 1,\n \"pid\": 4242,\n \ + \"start_time\": 1\n}}\n", + serde_json::to_string(endpoint.to_str().unwrap()).unwrap() + ); + fs::write(&path, &record).unwrap(); + let unsupported = format!( + "the sandbox runtime record {} has the unsupported format 1; this build reads format 4 \ + only and migrates no other, so stop the sandbox with the build that started it", + path.display() + ); + for operation in ["start", "exec", "stop", "deprovision"] { + assert_eq!( + transition(&root, operation, "state", &[]), + failed(&unsupported), + "{operation}" + ); + } + assert_eq!(fs::read_to_string(&path).unwrap(), record); + fs::remove_file(&path).unwrap(); + assert_eq!(transition(&root, "deprovision", "state", &[]), succeeded()); +} + +#[test] +fn stop_reports_its_timeout_and_leaves_the_vm_running() { + let root = populated_root(); + // A guest that never answers the stop. + provisioned_with(&root, "unanswered", "fake_ignore_stop=1"); + assert_eq!(transition(&root, "start", "unanswered", &[]), succeeded()); + let record = runtime_record(&root, "unanswered").unwrap(); + assert_eq!( + transition(&root, "stop", "unanswered", &["--timeout", "1"]), + failed("managed control response timed out") + ); + assert_eq!(runtime_record(&root, "unanswered").unwrap(), record); + let process = recorded_process(&root, "unanswered").expect("the VM still runs"); + process.kill().unwrap(); + assert!( + process + .wait(Instant::now() + Duration::from_secs(10)) + .unwrap() + ); + assert_eq!( + transition(&root, "deprovision", "unanswered", &[]), + succeeded() + ); + + // A guest that shuts down, whose OpenVMM takes longer to exit than the stop waits. + provisioned_with(&root, "slow", "fake_exit_delay_ms=4000"); + assert_eq!(transition(&root, "start", "slow", &[]), succeeded()); + let record = runtime_record(&root, "slow").unwrap(); + assert_eq!( + transition(&root, "stop", "slow", &["--timeout", "1"]), + failed("OpenVMM did not terminate after managed stop") + ); + assert_eq!(runtime_record(&root, "slow").unwrap(), record); + let process = recorded_process(&root, "slow").expect("the VM still runs"); + assert!( + process + .wait(Instant::now() + Duration::from_secs(30)) + .unwrap() + ); + assert_eq!(transition(&root, "stop", "slow", &[]), failed(STALE)); + assert_eq!(runtime_record(&root, "slow"), None); + assert_eq!(transition(&root, "deprovision", "slow", &[]), succeeded()); +} + +#[test] +fn a_sandbox_whose_openvmm_exited_reports_it_once_and_starts_again() { + let root = populated_root(); + provisioned(&root, "state"); + let crash = || { + assert_eq!(transition(&root, "start", "state", &[]), succeeded()); + let process = recorded_process(&root, "state").expect("the VM runs"); + process.kill().unwrap(); + assert!( + process + .wait(Instant::now() + Duration::from_secs(10)) + .unwrap() + ); + }; + crash(); + assert_eq!(transition(&root, "exec", "state", &[]), failed(STALE)); + assert_eq!( + state_entries(&root, "state"), + ["config.json", "openvmm.log", "sandbox.lock"] + ); + assert_eq!( + transition(&root, "stop", "state", &[]), + failed("sandbox is not running") + ); + // A start replaces the record of an OpenVMM that exited. + crash(); + assert_eq!(transition(&root, "start", "state", &[]), succeeded()); + assert_eq!(transition(&root, "stop", "state", &[]), succeeded()); + assert_eq!(transition(&root, "deprovision", "state", &[]), succeeded()); +} + #[test] fn artifacts_default_to_the_checkout_that_holds_the_executable() { let output = Command::new(NVX_RS) diff --git a/nvx-rs/tests/fixtures/cli.json b/nvx-rs/tests/fixtures/cli.json index 8351a4959..7dcfcdaed 100644 --- a/nvx-rs/tests/fixtures/cli.json +++ b/nvx-rs/tests/fixtures/cli.json @@ -4,6 +4,9 @@ "tree": [ {"path":"build/vmlinux","text":"kernel\n"}, {"path":"build/initramfs.cpio.gz","text":"initramfs\n"}, +{"path":"build/openvmm.provenance.json","text":"{\n \"format\": 1,\n \"executable_sha256\": \"1e8c924cdaac227bf74b4a4a9bcdb9e8836a1355fd0408cc81dab5d9aa71eb4d\"\n}\n"}, +{"path":"build/vmlinux.provenance.json","text":"{\n \"format\": 1,\n \"kernel_sha256\": \"a0c936696eb7d5ee3192bf53b9d281cecbb40ca9db520de72cb95817ad92ac72\"\n}\n"}, +{"path":"build/initramfs.provenance.json","text":"{\n \"format\": 1,\n \"initramfs_sha256\": \"2f77c2c97621e8bf69794e9d12815bb7c46338638f126bea8b18dfe02a684d62\"\n}\n"}, {"path":"distro.erofs","text":"distro\n"}, {"path":"runtime.erofs","text":"runtime\n"}, {"path":"custom.erofs","text":"custom\n"}, @@ -144,18 +147,18 @@ {"name":"run: missing OpenVMM","steps":[{"argv":["sandbox","run","--layer","distro,{root}/distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}/scratch.ext4"],"remove":["openvmm/target/release/openvmm{exe}"],"status":1,"stdout":"","stderr":"error: OpenVMM release binary not found: {root}/openvmm/target/release/openvmm\n"}]}, {"name":"run: missing kernel","steps":[{"argv":["sandbox","run","--layer","distro,{root}/distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}/scratch.ext4"],"remove":["build/vmlinux"],"status":1,"stdout":"","stderr":"error: Linux direct kernel not found: {root}/build/vmlinux\n"}]}, {"name":"run: missing initramfs","steps":[{"argv":["sandbox","run","--layer","distro,{root}/distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}/scratch.ext4"],"remove":["build/initramfs.cpio.gz"],"status":1,"stdout":"","stderr":"error: initramfs not found: {root}/build/initramfs.cpio.gz\n"}]}, -{"name":"lifecycle: minimal","steps":[{"argv":["sandbox","provision","--state-dir","{root}/state","--layer","distro,{root}/distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}/scratch.ext4"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","lifecycle.lock"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 1,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mount\": null,\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":null,"state/openvmm.log":null}},{"argv":["sandbox","start","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log","build/fake-openvmm-state.json"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","control.sock","lifecycle.lock","openvmm.log","runtime.json"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 1,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mount\": null,\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":{"text":"{\n \"control_endpoint\": \"{root}/state/control.sock\",\n \"format\": 1,\n \"pid\": {int},\n \"start_time\": {int}\n}\n","mode":384},"state/control.capability":{"size":32,"mode":384},"state/outcome.json":null,"state/openvmm.log":{"text":""},"build/fake-openvmm-state.json":{"text":"[\"--machine\",\"microvm\",\"--microvm-sandbox-block\",\"distro:file:{root}/distro.erofs,ro\",\"--microvm-sandbox-block\",\"scratch:file:{root}/scratch.ext4\",\"--microvm-workload-identity\",\"65534:65534\",\"--microvm-lifecycle\",\"managed\",\"--single-process\",\"--hypervisor\",\"kvm\",\"--memory\",\"256M\",\"--kernel\",\"{root}/build/vmlinux\",\"--initrd\",\"{root}/build/initramfs.cpio.gz\",\"--cmdline\",\"nvx_sandbox=1 nvx_layer=distro,0xd0003000,erofs:11111111-1111-1111-1111-111111111111 nvx_scratch=0xd0006000,ext4 nvx_entrypoint=/bin/sh nvx_hostname=nvx-sandbox\",\"--virtio-console\",\"none\",\"--microvm-control-console\",\"listen={root}/state/control.sock\",\"--microvm-control-auth-stdin\",\"--microvm-report\",\"{root}/state/outcome.json\"]"}}},{"argv":["sandbox","exec","--state-dir","{root}/state","--entrypoint","/bin/echo","--arg","hello","--arg","world"],"status":0,"stdout":"hello world\n","stderr":""},{"argv":["sandbox","exec","--state-dir","{root}/state","--entrypoint","/bin/pwd","--cwd","/tmp"],"status":0,"stdout":"/tmp\n","stderr":""},{"argv":["sandbox","exec","--state-dir","{root}/state","--entrypoint","/bin/sh","--arg=-c","--arg","echo out; echoerr err; exit 3","--outcome-report","{root}/reports/exec.json"],"check":["reports/exec.json"],"status":3,"stdout":"out\n","stderr":"err\n","files":{"reports/exec.json":{"text":"{\n \"operation_id\": \"{hex32}\",\n \"outcome\": {\n \"category\": \"exit\",\n \"operation\": \"exec\",\n \"status_code\": 3\n },\n \"schema_version\": 1\n}\n","mode":384}}},{"argv":["sandbox","exec","--state-dir","{root}/state","--entrypoint","/usr/bin/env","--environment","A=1","--environment","B=two"],"status":0,"stdout":"A=1\nB=two\n","stderr":""},{"argv":["sandbox","exec","--state-dir","{root}/state","--entrypoint","/usr/bin/printenv","--arg","PATH","--inherit-default-environment","--environment","A=1"],"status":0,"stdout":"/usr/sbin:/usr/bin:/sbin:/bin\n","stderr":""},{"argv":["sandbox","exec","--state-dir","{root}/state","--entrypoint","/usr/bin/env","--environment-file","{root}/env.json"],"write":{"env.json":"[\"X=1\", \"Y=two words\"]"},"status":0,"stdout":"X=1\nY=two words\n","stderr":""},{"argv":["sandbox","exec","--state-dir","{root}/state","--entrypoint","/usr/bin/env","--environment-file","{root}/empty.json"],"write":{"empty.json":"[]"},"status":0,"stdout":"","stderr":""},{"argv":["sandbox","exec","--state-dir","{root}/state","--entrypoint","/bin/sh","--arg=-c","--arg","sleep 5000","--exec-timeout-ms","50","--outcome-report","{root}/reports/exec-timeout.json"],"check":["reports/exec-timeout.json"],"status":124,"stdout":"","stderr":"","files":{"reports/exec-timeout.json":{"text":"{\n \"operation_id\": \"{hex32}\",\n \"outcome\": {\n \"category\": \"timeout\",\n \"operation\": \"exec\",\n \"status_code\": 124\n },\n \"schema_version\": 1\n}\n","mode":384}}},{"argv":["sandbox","exec","--state-dir","{root}/state","--entrypoint","/bin/sh","--arg=-c","--arg","signal 9"],"status":137,"stdout":"","stderr":""},{"argv":["sandbox","exec","--state-dir","{root}/state","--entrypoint","/bin/missing"],"status":127,"stdout":"","stderr":"openvmm: /bin/missing: not found\n"},{"argv":["sandbox","exec","--state-dir","{root}/state","--entrypoint","/bin/pwd","--cwd","/missing"],"status":1,"stdout":"","stderr":"error: managed guest rejected exec (status=2, category=cwd-failed)\n"},{"argv":["sandbox","exec","--state-dir","{root}/state","--entrypoint","/bin/pwd","--cwd","relative"],"status":1,"stdout":"","stderr":"error: managed exec working directory must be an absolute path of at most 4096 bytes\n"},{"argv":["sandbox","exec","--state-dir","{root}/state","--entrypoint","/bin/echo","--outcome-report","{root}/reports/exec.json"],"status":1,"stdout":"","stderr":"error: outcome report already exists: {root}/reports/exec.json\n"},{"argv":["sandbox","exec","--state-dir","{root}/state","--entrypoint","/bin/echo","--outcome-report","{root}/missing/exec.json"],"status":1,"stdout":"","stderr":"error: outcome report parent is not a plain directory: {root}/missing\n"},{"argv":["sandbox","stop","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","lifecycle.lock","openvmm.log","outcome.json"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 1,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mount\": null,\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"kvm\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 0,\n \"deny_rule_count\": 0,\n \"host_loopback\": \"none\",\n \"mode\": \"none\",\n \"status\": \"not-requested\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","start","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","control.sock","lifecycle.lock","openvmm.log","runtime.json"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 1,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mount\": null,\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":{"text":"{\n \"control_endpoint\": \"{root}/state/control.sock\",\n \"format\": 1,\n \"pid\": {int},\n \"start_time\": {int}\n}\n","mode":384},"state/control.capability":{"size":32,"mode":384},"state/outcome.json":null,"state/openvmm.log":{"text":""}}},{"argv":["sandbox","stop","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","lifecycle.lock","openvmm.log","outcome.json"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 1,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mount\": null,\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"kvm\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 0,\n \"deny_rule_count\": 0,\n \"host_loopback\": \"none\",\n \"mode\": \"none\",\n \"status\": \"not-requested\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","deprovision","--state-dir","{root}/state"],"check":["state"],"status":0,"stdout":"","stderr":"","files":{"state":null}}],"interop":true}, -{"name":"lifecycle: one share","steps":[{"argv":["sandbox","provision","--state-dir","{root}/state","--layer","distro,{root}/distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}/scratch.ext4","--mount","/data,{root}/share"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","lifecycle.lock"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 2,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mount\": {\n \"access\": \"ro\",\n \"denied_paths\": [],\n \"guest_target\": \"/data\",\n \"host_path\": \"{root}/share\",\n \"owner\": \"vmm\"\n },\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":null,"state/openvmm.log":null}},{"argv":["sandbox","start","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log","build/fake-openvmm-state.json"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","control.sock","lifecycle.lock","openvmm.log","runtime.json"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 2,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mount\": {\n \"access\": \"ro\",\n \"denied_paths\": [],\n \"guest_target\": \"/data\",\n \"host_path\": \"{root}/share\",\n \"owner\": \"vmm\"\n },\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":{"text":"{\n \"control_endpoint\": \"{root}/state/control.sock\",\n \"format\": 1,\n \"pid\": {int},\n \"start_time\": {int}\n}\n","mode":384},"state/control.capability":{"size":32,"mode":384},"state/outcome.json":null,"state/openvmm.log":{"text":""},"build/fake-openvmm-state.json":{"text":"[\"--machine\",\"microvm\",\"--microvm-sandbox-block\",\"distro:file:{root}/distro.erofs,ro\",\"--microvm-sandbox-block\",\"scratch:file:{root}/scratch.ext4\",\"--microvm-workload-identity\",\"65534:65534\",\"--mount\",\"/data,{root}/share,ro\",\"--microvm-lifecycle\",\"managed\",\"--single-process\",\"--hypervisor\",\"kvm\",\"--memory\",\"256M\",\"--kernel\",\"{root}/build/vmlinux\",\"--initrd\",\"{root}/build/initramfs.cpio.gz\",\"--cmdline\",\"nvx_sandbox=1 nvx_layer=distro,0xd0003000,erofs:11111111-1111-1111-1111-111111111111 nvx_scratch=0xd0006000,ext4 nvx_entrypoint=/bin/sh nvx_hostname=nvx-sandbox\",\"--virtio-console\",\"none\",\"--microvm-control-console\",\"listen={root}/state/control.sock\",\"--microvm-control-auth-stdin\",\"--microvm-report\",\"{root}/state/outcome.json\"]"}}},{"argv":["sandbox","exec","--state-dir","{root}/state","--entrypoint","/bin/pwd","--cwd","/data"],"status":0,"stdout":"/data\n","stderr":""},{"argv":["sandbox","stop","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","lifecycle.lock","openvmm.log","outcome.json"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 2,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mount\": {\n \"access\": \"ro\",\n \"denied_paths\": [],\n \"guest_target\": \"/data\",\n \"host_path\": \"{root}/share\",\n \"owner\": \"vmm\"\n },\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"kvm\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 0,\n \"deny_rule_count\": 0,\n \"host_loopback\": \"none\",\n \"mode\": \"none\",\n \"status\": \"not-requested\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","start","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","control.sock","lifecycle.lock","openvmm.log","runtime.json"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 2,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mount\": {\n \"access\": \"ro\",\n \"denied_paths\": [],\n \"guest_target\": \"/data\",\n \"host_path\": \"{root}/share\",\n \"owner\": \"vmm\"\n },\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":{"text":"{\n \"control_endpoint\": \"{root}/state/control.sock\",\n \"format\": 1,\n \"pid\": {int},\n \"start_time\": {int}\n}\n","mode":384},"state/control.capability":{"size":32,"mode":384},"state/outcome.json":null,"state/openvmm.log":{"text":""}}},{"argv":["sandbox","stop","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","lifecycle.lock","openvmm.log","outcome.json"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 2,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mount\": {\n \"access\": \"ro\",\n \"denied_paths\": [],\n \"guest_target\": \"/data\",\n \"host_path\": \"{root}/share\",\n \"owner\": \"vmm\"\n },\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"kvm\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 0,\n \"deny_rule_count\": 0,\n \"host_loopback\": \"none\",\n \"mode\": \"none\",\n \"status\": \"not-requested\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","deprovision","--state-dir","{root}/state"],"check":["state"],"status":0,"stdout":"","stderr":"","files":{"state":null}}],"interop":true}, -{"name":"lifecycle: caller-owned share","steps":[{"argv":["sandbox","provision","--state-dir","{root}/state","--layer","distro,{root}/distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}/scratch.ext4","--mount","/data,{root}/share,rw","--mount-owner","caller"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","lifecycle.lock"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 3,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mount\": {\n \"access\": \"rw\",\n \"denied_paths\": [],\n \"guest_target\": \"/data\",\n \"host_path\": \"{root}/share\",\n \"owner\": \"caller\"\n },\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":null,"state/openvmm.log":null}},{"argv":["sandbox","start","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log","build/fake-openvmm-state.json"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","control.sock","lifecycle.lock","openvmm.log","runtime.json"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 3,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mount\": {\n \"access\": \"rw\",\n \"denied_paths\": [],\n \"guest_target\": \"/data\",\n \"host_path\": \"{root}/share\",\n \"owner\": \"caller\"\n },\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":{"text":"{\n \"control_endpoint\": \"{root}/state/control.sock\",\n \"format\": 1,\n \"pid\": {int},\n \"start_time\": {int}\n}\n","mode":384},"state/control.capability":{"size":32,"mode":384},"state/outcome.json":null,"state/openvmm.log":{"text":""},"build/fake-openvmm-state.json":{"text":"[\"--machine\",\"microvm\",\"--microvm-sandbox-block\",\"distro:file:{root}/distro.erofs,ro\",\"--microvm-sandbox-block\",\"scratch:file:{root}/scratch.ext4\",\"--microvm-workload-identity\",\"65534:65534\",\"--mount\",\"/data,{root}/share,rw\",\"--mount-owner\",\"caller\",\"--microvm-lifecycle\",\"managed\",\"--single-process\",\"--hypervisor\",\"kvm\",\"--memory\",\"256M\",\"--kernel\",\"{root}/build/vmlinux\",\"--initrd\",\"{root}/build/initramfs.cpio.gz\",\"--cmdline\",\"nvx_sandbox=1 nvx_layer=distro,0xd0003000,erofs:11111111-1111-1111-1111-111111111111 nvx_scratch=0xd0006000,ext4 nvx_entrypoint=/bin/sh nvx_hostname=nvx-sandbox\",\"--virtio-console\",\"none\",\"--microvm-control-console\",\"listen={root}/state/control.sock\",\"--microvm-control-auth-stdin\",\"--microvm-report\",\"{root}/state/outcome.json\"]"}}},{"argv":["sandbox","stop","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","lifecycle.lock","openvmm.log","outcome.json"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 3,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mount\": {\n \"access\": \"rw\",\n \"denied_paths\": [],\n \"guest_target\": \"/data\",\n \"host_path\": \"{root}/share\",\n \"owner\": \"caller\"\n },\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"kvm\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 0,\n \"deny_rule_count\": 0,\n \"host_loopback\": \"none\",\n \"mode\": \"none\",\n \"status\": \"not-requested\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","start","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","control.sock","lifecycle.lock","openvmm.log","runtime.json"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 3,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mount\": {\n \"access\": \"rw\",\n \"denied_paths\": [],\n \"guest_target\": \"/data\",\n \"host_path\": \"{root}/share\",\n \"owner\": \"caller\"\n },\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":{"text":"{\n \"control_endpoint\": \"{root}/state/control.sock\",\n \"format\": 1,\n \"pid\": {int},\n \"start_time\": {int}\n}\n","mode":384},"state/control.capability":{"size":32,"mode":384},"state/outcome.json":null,"state/openvmm.log":{"text":""}}},{"argv":["sandbox","stop","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","lifecycle.lock","openvmm.log","outcome.json"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 3,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mount\": {\n \"access\": \"rw\",\n \"denied_paths\": [],\n \"guest_target\": \"/data\",\n \"host_path\": \"{root}/share\",\n \"owner\": \"caller\"\n },\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"kvm\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 0,\n \"deny_rule_count\": 0,\n \"host_loopback\": \"none\",\n \"mode\": \"none\",\n \"status\": \"not-requested\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","deprovision","--state-dir","{root}/state"],"check":["state"],"status":0,"stdout":"","stderr":"","files":{"state":null}}],"interop":true}, -{"name":"lifecycle: several shares","steps":[{"argv":["sandbox","provision","--state-dir","{root}/state","--layer","distro,{root}/distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}/scratch.ext4","--mount","/data,{root}/share","--mount","/other,{root}/share2,rw"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","lifecycle.lock"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 4,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mounts\": [\n {\n \"access\": \"ro\",\n \"denied_paths\": [],\n \"guest_target\": \"/data\",\n \"host_path\": \"{root}/share\",\n \"owner\": \"vmm\"\n },\n {\n \"access\": \"rw\",\n \"denied_paths\": [],\n \"guest_target\": \"/other\",\n \"host_path\": \"{root}/share2\",\n \"owner\": \"vmm\"\n }\n ],\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":null,"state/openvmm.log":null}},{"argv":["sandbox","start","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log","build/fake-openvmm-state.json"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","control.sock","lifecycle.lock","openvmm.log","runtime.json"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 4,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mounts\": [\n {\n \"access\": \"ro\",\n \"denied_paths\": [],\n \"guest_target\": \"/data\",\n \"host_path\": \"{root}/share\",\n \"owner\": \"vmm\"\n },\n {\n \"access\": \"rw\",\n \"denied_paths\": [],\n \"guest_target\": \"/other\",\n \"host_path\": \"{root}/share2\",\n \"owner\": \"vmm\"\n }\n ],\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":{"text":"{\n \"control_endpoint\": \"{root}/state/control.sock\",\n \"format\": 1,\n \"pid\": {int},\n \"start_time\": {int}\n}\n","mode":384},"state/control.capability":{"size":32,"mode":384},"state/outcome.json":null,"state/openvmm.log":{"text":""},"build/fake-openvmm-state.json":{"text":"[\"--machine\",\"microvm\",\"--microvm-sandbox-block\",\"distro:file:{root}/distro.erofs,ro\",\"--microvm-sandbox-block\",\"scratch:file:{root}/scratch.ext4\",\"--microvm-workload-identity\",\"65534:65534\",\"--mount-aggregate\",\"/run/nvx/shares\",\"--mount-child\",\"0-bd47413b5c03dfc6e4b5b8143fe205ea,{root}/share,ro\",\"--mount-child\",\"1-bf2faee2596026620b5cbc997c2d7d45,{root}/share2,rw\",\"--microvm-lifecycle\",\"managed\",\"--single-process\",\"--hypervisor\",\"kvm\",\"--memory\",\"256M\",\"--kernel\",\"{root}/build/vmlinux\",\"--initrd\",\"{root}/build/initramfs.cpio.gz\",\"--cmdline\",\"nvx_sandbox=1 nvx_layer=distro,0xd0003000,erofs:11111111-1111-1111-1111-111111111111 nvx_scratch=0xd0006000,ext4 nvx_entrypoint=/bin/sh nvx_hostname=nvx-sandbox nvx_share=0-bd47413b5c03dfc6e4b5b8143fe205ea,/data,ro nvx_share=1-bf2faee2596026620b5cbc997c2d7d45,/other,rw\",\"--virtio-console\",\"none\",\"--microvm-control-console\",\"listen={root}/state/control.sock\",\"--microvm-control-auth-stdin\",\"--microvm-report\",\"{root}/state/outcome.json\"]"}}},{"argv":["sandbox","exec","--state-dir","{root}/state","--entrypoint","/bin/pwd","--cwd","/other"],"status":0,"stdout":"/other\n","stderr":""},{"argv":["sandbox","stop","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","lifecycle.lock","openvmm.log","outcome.json"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 4,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mounts\": [\n {\n \"access\": \"ro\",\n \"denied_paths\": [],\n \"guest_target\": \"/data\",\n \"host_path\": \"{root}/share\",\n \"owner\": \"vmm\"\n },\n {\n \"access\": \"rw\",\n \"denied_paths\": [],\n \"guest_target\": \"/other\",\n \"host_path\": \"{root}/share2\",\n \"owner\": \"vmm\"\n }\n ],\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"kvm\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 0,\n \"deny_rule_count\": 0,\n \"host_loopback\": \"none\",\n \"mode\": \"none\",\n \"status\": \"not-requested\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","start","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","control.sock","lifecycle.lock","openvmm.log","runtime.json"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 4,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mounts\": [\n {\n \"access\": \"ro\",\n \"denied_paths\": [],\n \"guest_target\": \"/data\",\n \"host_path\": \"{root}/share\",\n \"owner\": \"vmm\"\n },\n {\n \"access\": \"rw\",\n \"denied_paths\": [],\n \"guest_target\": \"/other\",\n \"host_path\": \"{root}/share2\",\n \"owner\": \"vmm\"\n }\n ],\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":{"text":"{\n \"control_endpoint\": \"{root}/state/control.sock\",\n \"format\": 1,\n \"pid\": {int},\n \"start_time\": {int}\n}\n","mode":384},"state/control.capability":{"size":32,"mode":384},"state/outcome.json":null,"state/openvmm.log":{"text":""}}},{"argv":["sandbox","stop","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","lifecycle.lock","openvmm.log","outcome.json"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 4,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mounts\": [\n {\n \"access\": \"ro\",\n \"denied_paths\": [],\n \"guest_target\": \"/data\",\n \"host_path\": \"{root}/share\",\n \"owner\": \"vmm\"\n },\n {\n \"access\": \"rw\",\n \"denied_paths\": [],\n \"guest_target\": \"/other\",\n \"host_path\": \"{root}/share2\",\n \"owner\": \"vmm\"\n }\n ],\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"kvm\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 0,\n \"deny_rule_count\": 0,\n \"host_loopback\": \"none\",\n \"mode\": \"none\",\n \"status\": \"not-requested\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","deprovision","--state-dir","{root}/state"],"check":["state"],"status":0,"stdout":"","stderr":"","files":{"state":null}}],"interop":true}, -{"name":"lifecycle: share policies","steps":[{"argv":["sandbox","provision","--state-dir","{root}/state","--layer","distro,{root}/distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}/scratch.ext4","--mount","/data,{root}/share,rw","--mount-deny","{root}/share/hidden","--mount-allow","{root}/share/hidden/shown","--mount-write","{root}/share/writable"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","lifecycle.lock"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 5,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mounts\": [\n {\n \"access\": \"rw\",\n \"allowed_paths\": [\n \"{root}/share/hidden/shown\"\n ],\n \"denied_paths\": [\n \"{root}/share/hidden\"\n ],\n \"guest_target\": \"/data\",\n \"host_path\": \"{root}/share\",\n \"owner\": \"vmm\",\n \"writable_paths\": [\n \"{root}/share/writable\"\n ]\n }\n ],\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":null,"state/openvmm.log":null}},{"argv":["sandbox","start","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log","build/fake-openvmm-state.json"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","control.sock","lifecycle.lock","openvmm.log","runtime.json"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 5,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mounts\": [\n {\n \"access\": \"rw\",\n \"allowed_paths\": [\n \"{root}/share/hidden/shown\"\n ],\n \"denied_paths\": [\n \"{root}/share/hidden\"\n ],\n \"guest_target\": \"/data\",\n \"host_path\": \"{root}/share\",\n \"owner\": \"vmm\",\n \"writable_paths\": [\n \"{root}/share/writable\"\n ]\n }\n ],\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":{"text":"{\n \"control_endpoint\": \"{root}/state/control.sock\",\n \"format\": 1,\n \"pid\": {int},\n \"start_time\": {int}\n}\n","mode":384},"state/control.capability":{"size":32,"mode":384},"state/outcome.json":null,"state/openvmm.log":{"text":""},"build/fake-openvmm-state.json":{"text":"[\"--machine\",\"microvm\",\"--microvm-sandbox-block\",\"distro:file:{root}/distro.erofs,ro\",\"--microvm-sandbox-block\",\"scratch:file:{root}/scratch.ext4\",\"--microvm-workload-identity\",\"65534:65534\",\"--mount\",\"/data,{root}/share,rw\",\"--mount-deny\",\"{root}/share/hidden\",\"--mount-allow\",\"{root}/share/hidden/shown\",\"--mount-write\",\"{root}/share/writable\",\"--microvm-lifecycle\",\"managed\",\"--single-process\",\"--hypervisor\",\"kvm\",\"--memory\",\"256M\",\"--kernel\",\"{root}/build/vmlinux\",\"--initrd\",\"{root}/build/initramfs.cpio.gz\",\"--cmdline\",\"nvx_sandbox=1 nvx_layer=distro,0xd0003000,erofs:11111111-1111-1111-1111-111111111111 nvx_scratch=0xd0006000,ext4 nvx_entrypoint=/bin/sh nvx_hostname=nvx-sandbox\",\"--virtio-console\",\"none\",\"--microvm-control-console\",\"listen={root}/state/control.sock\",\"--microvm-control-auth-stdin\",\"--microvm-report\",\"{root}/state/outcome.json\"]"}}},{"argv":["sandbox","stop","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","lifecycle.lock","openvmm.log","outcome.json"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 5,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mounts\": [\n {\n \"access\": \"rw\",\n \"allowed_paths\": [\n \"{root}/share/hidden/shown\"\n ],\n \"denied_paths\": [\n \"{root}/share/hidden\"\n ],\n \"guest_target\": \"/data\",\n \"host_path\": \"{root}/share\",\n \"owner\": \"vmm\",\n \"writable_paths\": [\n \"{root}/share/writable\"\n ]\n }\n ],\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"kvm\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 0,\n \"deny_rule_count\": 0,\n \"host_loopback\": \"none\",\n \"mode\": \"none\",\n \"status\": \"not-requested\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","start","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","control.sock","lifecycle.lock","openvmm.log","runtime.json"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 5,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mounts\": [\n {\n \"access\": \"rw\",\n \"allowed_paths\": [\n \"{root}/share/hidden/shown\"\n ],\n \"denied_paths\": [\n \"{root}/share/hidden\"\n ],\n \"guest_target\": \"/data\",\n \"host_path\": \"{root}/share\",\n \"owner\": \"vmm\",\n \"writable_paths\": [\n \"{root}/share/writable\"\n ]\n }\n ],\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":{"text":"{\n \"control_endpoint\": \"{root}/state/control.sock\",\n \"format\": 1,\n \"pid\": {int},\n \"start_time\": {int}\n}\n","mode":384},"state/control.capability":{"size":32,"mode":384},"state/outcome.json":null,"state/openvmm.log":{"text":""}}},{"argv":["sandbox","stop","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","lifecycle.lock","openvmm.log","outcome.json"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 5,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mounts\": [\n {\n \"access\": \"rw\",\n \"allowed_paths\": [\n \"{root}/share/hidden/shown\"\n ],\n \"denied_paths\": [\n \"{root}/share/hidden\"\n ],\n \"guest_target\": \"/data\",\n \"host_path\": \"{root}/share\",\n \"owner\": \"vmm\",\n \"writable_paths\": [\n \"{root}/share/writable\"\n ]\n }\n ],\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"kvm\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 0,\n \"deny_rule_count\": 0,\n \"host_loopback\": \"none\",\n \"mode\": \"none\",\n \"status\": \"not-requested\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","deprovision","--state-dir","{root}/state"],"check":["state"],"status":0,"stdout":"","stderr":"","files":{"state":null}}],"interop":true}, -{"name":"lifecycle: file share","steps":[{"argv":["sandbox","provision","--state-dir","{root}/state","--layer","distro,{root}/distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}/scratch.ext4","--mount","/etc/single.txt,{root}/single.txt,ro"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","lifecycle.lock"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 6,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mounts\": [\n {\n \"access\": \"ro\",\n \"allowed_paths\": [],\n \"denied_paths\": [],\n \"guest_target\": \"/etc/single.txt\",\n \"host_path\": \"{root}/single.txt\",\n \"kind\": \"file\",\n \"owner\": \"vmm\",\n \"writable_paths\": []\n }\n ],\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":null,"state/openvmm.log":null}},{"argv":["sandbox","start","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log","build/fake-openvmm-state.json"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","control.sock","lifecycle.lock","openvmm.log","runtime.json"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 6,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mounts\": [\n {\n \"access\": \"ro\",\n \"allowed_paths\": [],\n \"denied_paths\": [],\n \"guest_target\": \"/etc/single.txt\",\n \"host_path\": \"{root}/single.txt\",\n \"kind\": \"file\",\n \"owner\": \"vmm\",\n \"writable_paths\": []\n }\n ],\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":{"text":"{\n \"control_endpoint\": \"{root}/state/control.sock\",\n \"format\": 1,\n \"pid\": {int},\n \"start_time\": {int}\n}\n","mode":384},"state/control.capability":{"size":32,"mode":384},"state/outcome.json":null,"state/openvmm.log":{"text":""},"build/fake-openvmm-state.json":{"text":"[\"--machine\",\"microvm\",\"--microvm-sandbox-block\",\"distro:file:{root}/distro.erofs,ro\",\"--microvm-sandbox-block\",\"scratch:file:{root}/scratch.ext4\",\"--microvm-workload-identity\",\"65534:65534\",\"--mount-aggregate\",\"/run/nvx/shares\",\"--mount-child\",\"0-a80a05e9cb129bc27b7ba103bf2f5ac0,{root}/single.txt,ro,file\",\"--microvm-lifecycle\",\"managed\",\"--single-process\",\"--hypervisor\",\"kvm\",\"--memory\",\"256M\",\"--kernel\",\"{root}/build/vmlinux\",\"--initrd\",\"{root}/build/initramfs.cpio.gz\",\"--cmdline\",\"nvx_sandbox=1 nvx_layer=distro,0xd0003000,erofs:11111111-1111-1111-1111-111111111111 nvx_scratch=0xd0006000,ext4 nvx_entrypoint=/bin/sh nvx_hostname=nvx-sandbox nvx_share=0-a80a05e9cb129bc27b7ba103bf2f5ac0,/etc/single.txt,ro,file\",\"--virtio-console\",\"none\",\"--microvm-control-console\",\"listen={root}/state/control.sock\",\"--microvm-control-auth-stdin\",\"--microvm-report\",\"{root}/state/outcome.json\"]"}}},{"argv":["sandbox","stop","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","lifecycle.lock","openvmm.log","outcome.json"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 6,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mounts\": [\n {\n \"access\": \"ro\",\n \"allowed_paths\": [],\n \"denied_paths\": [],\n \"guest_target\": \"/etc/single.txt\",\n \"host_path\": \"{root}/single.txt\",\n \"kind\": \"file\",\n \"owner\": \"vmm\",\n \"writable_paths\": []\n }\n ],\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"kvm\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 0,\n \"deny_rule_count\": 0,\n \"host_loopback\": \"none\",\n \"mode\": \"none\",\n \"status\": \"not-requested\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","start","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","control.sock","lifecycle.lock","openvmm.log","runtime.json"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 6,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mounts\": [\n {\n \"access\": \"ro\",\n \"allowed_paths\": [],\n \"denied_paths\": [],\n \"guest_target\": \"/etc/single.txt\",\n \"host_path\": \"{root}/single.txt\",\n \"kind\": \"file\",\n \"owner\": \"vmm\",\n \"writable_paths\": []\n }\n ],\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":{"text":"{\n \"control_endpoint\": \"{root}/state/control.sock\",\n \"format\": 1,\n \"pid\": {int},\n \"start_time\": {int}\n}\n","mode":384},"state/control.capability":{"size":32,"mode":384},"state/outcome.json":null,"state/openvmm.log":{"text":""}}},{"argv":["sandbox","stop","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","lifecycle.lock","openvmm.log","outcome.json"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 6,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mounts\": [\n {\n \"access\": \"ro\",\n \"allowed_paths\": [],\n \"denied_paths\": [],\n \"guest_target\": \"/etc/single.txt\",\n \"host_path\": \"{root}/single.txt\",\n \"kind\": \"file\",\n \"owner\": \"vmm\",\n \"writable_paths\": []\n }\n ],\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"kvm\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 0,\n \"deny_rule_count\": 0,\n \"host_loopback\": \"none\",\n \"mode\": \"none\",\n \"status\": \"not-requested\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","deprovision","--state-dir","{root}/state"],"check":["state"],"status":0,"stdout":"","stderr":"","files":{"state":null}}],"interop":true}, -{"name":"lifecycle: network and launch options","steps":[{"argv":["sandbox","provision","--state-dir","{root}/state","--layer","distro,{root}/distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}/scratch.ext4","--entrypoint","/bin/server","--arg=--serve","--hostname","worker","--workload-user","1000:1000","--memory-max","268435456","--pids-max","64","--memory-mib","384","--hypervisor","auto","--cmdline","quiet","--net","10.0.0.2/24","--network-profile","portable","--network-egress","deny","--network-ingress","deny","--network-egress-policy-file","{root}/policy.json","--host-loopback","allow","--network-proxy","10.0.0.1:3128","--host-loopback-forward","8080","--host-loopback-forward","9090"],"write":{"policy.json":"{\"allow\": [{\"cidr\": \"192.0.2.0/24\", \"protocol\": \"tcp\", \"port\": 443}], \"deny\": [{\"cidr\": \"192.0.2.128/25\"}]}"},"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","lifecycle.lock"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"quiet\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 1,\n \"host_loopback\": \"allow\",\n \"host_loopback_forward\": [\n \"8080\",\n \"9090\"\n ],\n \"hostname\": \"worker\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": 268435456,\n \"memory_mib\": 384,\n \"mount\": null,\n \"net\": \"10.0.0.2/24\",\n \"network_egress\": \"deny\",\n \"network_egress_allow\": [\n \"192.0.2.0/24:tcp:443\"\n ],\n \"network_egress_deny\": [\n \"192.0.2.128/25\"\n ],\n \"network_ingress\": \"deny\",\n \"network_profile\": \"portable\",\n \"network_proxy\": \"10.0.0.1:3128\",\n \"pids_max\": 64,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 1000,\n \"workload_uid\": 1000\n}\n","mode":384},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":null,"state/openvmm.log":null}},{"argv":["sandbox","start","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log","build/fake-openvmm-state.json"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","control.sock","lifecycle.lock","openvmm.log","runtime.json"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"quiet\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 1,\n \"host_loopback\": \"allow\",\n \"host_loopback_forward\": [\n \"8080\",\n \"9090\"\n ],\n \"hostname\": \"worker\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": 268435456,\n \"memory_mib\": 384,\n \"mount\": null,\n \"net\": \"10.0.0.2/24\",\n \"network_egress\": \"deny\",\n \"network_egress_allow\": [\n \"192.0.2.0/24:tcp:443\"\n ],\n \"network_egress_deny\": [\n \"192.0.2.128/25\"\n ],\n \"network_ingress\": \"deny\",\n \"network_profile\": \"portable\",\n \"network_proxy\": \"10.0.0.1:3128\",\n \"pids_max\": 64,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 1000,\n \"workload_uid\": 1000\n}\n","mode":384},"state/runtime.json":{"text":"{\n \"control_endpoint\": \"{root}/state/control.sock\",\n \"format\": 1,\n \"pid\": {int},\n \"start_time\": {int}\n}\n","mode":384},"state/control.capability":{"size":32,"mode":384},"state/outcome.json":null,"state/openvmm.log":{"text":""},"build/fake-openvmm-state.json":{"text":"[\"--machine\",\"microvm\",\"--microvm-sandbox-block\",\"distro:file:{root}/distro.erofs,ro\",\"--microvm-sandbox-block\",\"scratch:file:{root}/scratch.ext4\",\"--microvm-workload-identity\",\"1000:1000\",\"--microvm-lifecycle\",\"managed\",\"--single-process\",\"--hypervisor\",\"kvm\",\"--memory\",\"384M\",\"--kernel\",\"{root}/build/vmlinux\",\"--initrd\",\"{root}/build/initramfs.cpio.gz\",\"--cmdline\",\"quiet nvx_sandbox=1 nvx_layer=distro,0xd0003000,erofs:11111111-1111-1111-1111-111111111111 nvx_scratch=0xd0006000,ext4 nvx_entrypoint=/bin/sh nvx_hostname=worker nvx_memory_max=268435456 nvx_pids_max=65\",\"--virtio-console\",\"none\",\"--microvm-control-console\",\"listen={root}/state/control.sock\",\"--microvm-control-auth-stdin\",\"--microvm-report\",\"{root}/state/outcome.json\",\"--net\",\"10.0.0.2/24\",\"--network-profile\",\"portable\",\"--network-egress\",\"deny\",\"--network-ingress\",\"deny\",\"--host-loopback\",\"allow\",\"--network-egress-allow\",\"192.0.2.0/24:tcp:443\",\"--network-egress-deny\",\"192.0.2.128/25\",\"--host-loopback-forward\",\"8080\",\"--host-loopback-forward\",\"9090\",\"--network-proxy\",\"10.0.0.1:3128\"]"}}},{"argv":["sandbox","stop","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","lifecycle.lock","openvmm.log","outcome.json"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"quiet\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 1,\n \"host_loopback\": \"allow\",\n \"host_loopback_forward\": [\n \"8080\",\n \"9090\"\n ],\n \"hostname\": \"worker\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": 268435456,\n \"memory_mib\": 384,\n \"mount\": null,\n \"net\": \"10.0.0.2/24\",\n \"network_egress\": \"deny\",\n \"network_egress_allow\": [\n \"192.0.2.0/24:tcp:443\"\n ],\n \"network_egress_deny\": [\n \"192.0.2.128/25\"\n ],\n \"network_ingress\": \"deny\",\n \"network_profile\": \"portable\",\n \"network_proxy\": \"10.0.0.1:3128\",\n \"pids_max\": 64,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 1000,\n \"workload_uid\": 1000\n}\n","mode":384},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"kvm\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 1,\n \"deny_rule_count\": 1,\n \"host_loopback\": \"allow\",\n \"mode\": \"rules\",\n \"status\": \"applied\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","start","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","control.sock","lifecycle.lock","openvmm.log","runtime.json"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"quiet\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 1,\n \"host_loopback\": \"allow\",\n \"host_loopback_forward\": [\n \"8080\",\n \"9090\"\n ],\n \"hostname\": \"worker\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": 268435456,\n \"memory_mib\": 384,\n \"mount\": null,\n \"net\": \"10.0.0.2/24\",\n \"network_egress\": \"deny\",\n \"network_egress_allow\": [\n \"192.0.2.0/24:tcp:443\"\n ],\n \"network_egress_deny\": [\n \"192.0.2.128/25\"\n ],\n \"network_ingress\": \"deny\",\n \"network_profile\": \"portable\",\n \"network_proxy\": \"10.0.0.1:3128\",\n \"pids_max\": 64,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 1000,\n \"workload_uid\": 1000\n}\n","mode":384},"state/runtime.json":{"text":"{\n \"control_endpoint\": \"{root}/state/control.sock\",\n \"format\": 1,\n \"pid\": {int},\n \"start_time\": {int}\n}\n","mode":384},"state/control.capability":{"size":32,"mode":384},"state/outcome.json":null,"state/openvmm.log":{"text":""}}},{"argv":["sandbox","stop","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","lifecycle.lock","openvmm.log","outcome.json"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"quiet\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 1,\n \"host_loopback\": \"allow\",\n \"host_loopback_forward\": [\n \"8080\",\n \"9090\"\n ],\n \"hostname\": \"worker\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": 268435456,\n \"memory_mib\": 384,\n \"mount\": null,\n \"net\": \"10.0.0.2/24\",\n \"network_egress\": \"deny\",\n \"network_egress_allow\": [\n \"192.0.2.0/24:tcp:443\"\n ],\n \"network_egress_deny\": [\n \"192.0.2.128/25\"\n ],\n \"network_ingress\": \"deny\",\n \"network_profile\": \"portable\",\n \"network_proxy\": \"10.0.0.1:3128\",\n \"pids_max\": 64,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 1000,\n \"workload_uid\": 1000\n}\n","mode":384},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"kvm\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 1,\n \"deny_rule_count\": 1,\n \"host_loopback\": \"allow\",\n \"mode\": \"rules\",\n \"status\": \"applied\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","deprovision","--state-dir","{root}/state"],"check":["state"],"status":0,"stdout":"","stderr":"","files":{"state":null}}],"interop":true}, +{"name":"lifecycle: minimal","steps":[{"argv":["sandbox","provision","--state-dir","{root}/state","--layer","distro,{root}/distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}/scratch.ext4"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","sandbox.lock"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 1,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mount\": null,\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":null,"state/openvmm.log":null}},{"argv":["sandbox","start","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log","build/fake-openvmm-state.json"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","control.sock","openvmm.log","runtime.json","sandbox.lock"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 1,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mount\": null,\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":{"text":"{\n \"format\": 4,\n \"launch\": \"{hex32}\",\n \"process\": {\n \"pid\": {int},\n \"startTime\": {int},\n \"boot\": \"{hex32}\"\n },\n \"endpoint\": \"{root}/state/control.sock\",\n \"runtime\": {\n \"openvmm\": \"sha256:1e8c924cdaac227bf74b4a4a9bcdb9e8836a1355fd0408cc81dab5d9aa71eb4d\",\n \"kernel\": \"sha256:a0c936696eb7d5ee3192bf53b9d281cecbb40ca9db520de72cb95817ad92ac72\",\n \"initramfs\": \"sha256:2f77c2c97621e8bf69794e9d12815bb7c46338638f126bea8b18dfe02a684d62\",\n \"bootContract\": 2,\n \"controlContract\": \"nvx-microvm-v2-control-v2\",\n \"controlSession\": 1\n }\n}\n","mode":384},"state/control.capability":{"size":32,"mode":384},"state/outcome.json":null,"state/openvmm.log":{"text":""},"build/fake-openvmm-state.json":{"text":"[\"--machine\",\"microvm\",\"--microvm-sandbox-block\",\"distro:file:{root}/distro.erofs,ro\",\"--microvm-sandbox-block\",\"scratch:file:{root}/scratch.ext4\",\"--microvm-workload-identity\",\"65534:65534\",\"--microvm-lifecycle\",\"managed\",\"--single-process\",\"--hypervisor\",\"kvm\",\"--memory\",\"256M\",\"--kernel\",\"{root}/build/vmlinux\",\"--initrd\",\"{root}/build/initramfs.cpio.gz\",\"--cmdline\",\"nvx_sandbox=1 nvx_layer=distro,0xd0003000,erofs:11111111-1111-1111-1111-111111111111 nvx_scratch=0xd0006000,ext4 nvx_entrypoint=/bin/sh nvx_hostname=nvx-sandbox\",\"--virtio-console\",\"none\",\"--microvm-control-console\",\"listen={root}/state/control.sock\",\"--microvm-control-auth-stdin\",\"--microvm-report\",\"{root}/state/outcome.json\"]"}}},{"argv":["sandbox","exec","--state-dir","{root}/state","--entrypoint","/bin/echo","--arg","hello","--arg","world"],"status":0,"stdout":"hello world\n","stderr":""},{"argv":["sandbox","exec","--state-dir","{root}/state","--entrypoint","/bin/pwd","--cwd","/tmp"],"status":0,"stdout":"/tmp\n","stderr":""},{"argv":["sandbox","exec","--state-dir","{root}/state","--entrypoint","/bin/sh","--arg=-c","--arg","echo out; echoerr err; exit 3","--outcome-report","{root}/reports/exec.json"],"check":["reports/exec.json"],"status":3,"stdout":"out\n","stderr":"err\n","files":{"reports/exec.json":{"text":"{\n \"operation_id\": \"{hex32}\",\n \"outcome\": {\n \"category\": \"exit\",\n \"operation\": \"exec\",\n \"status_code\": 3\n },\n \"schema_version\": 1\n}\n","mode":384}}},{"argv":["sandbox","exec","--state-dir","{root}/state","--entrypoint","/usr/bin/env","--environment","A=1","--environment","B=two"],"status":0,"stdout":"A=1\nB=two\n","stderr":""},{"argv":["sandbox","exec","--state-dir","{root}/state","--entrypoint","/usr/bin/printenv","--arg","PATH","--inherit-default-environment","--environment","A=1"],"status":0,"stdout":"/usr/sbin:/usr/bin:/sbin:/bin\n","stderr":""},{"argv":["sandbox","exec","--state-dir","{root}/state","--entrypoint","/usr/bin/env","--environment-file","{root}/env.json"],"write":{"env.json":"[\"X=1\", \"Y=two words\"]"},"status":0,"stdout":"X=1\nY=two words\n","stderr":""},{"argv":["sandbox","exec","--state-dir","{root}/state","--entrypoint","/usr/bin/env","--environment-file","{root}/empty.json"],"write":{"empty.json":"[]"},"status":0,"stdout":"","stderr":""},{"argv":["sandbox","exec","--state-dir","{root}/state","--entrypoint","/bin/sh","--arg=-c","--arg","sleep 5000","--exec-timeout-ms","50","--outcome-report","{root}/reports/exec-timeout.json"],"check":["reports/exec-timeout.json"],"status":124,"stdout":"","stderr":"","files":{"reports/exec-timeout.json":{"text":"{\n \"operation_id\": \"{hex32}\",\n \"outcome\": {\n \"category\": \"timeout\",\n \"operation\": \"exec\",\n \"status_code\": 124\n },\n \"schema_version\": 1\n}\n","mode":384}}},{"argv":["sandbox","exec","--state-dir","{root}/state","--entrypoint","/bin/sh","--arg=-c","--arg","signal 9"],"status":137,"stdout":"","stderr":""},{"argv":["sandbox","exec","--state-dir","{root}/state","--entrypoint","/bin/missing"],"status":127,"stdout":"","stderr":"openvmm: /bin/missing: not found\n"},{"argv":["sandbox","exec","--state-dir","{root}/state","--entrypoint","/bin/pwd","--cwd","/missing"],"status":1,"stdout":"","stderr":"error: managed guest rejected exec (status=2, category=cwd-failed)\n"},{"argv":["sandbox","exec","--state-dir","{root}/state","--entrypoint","/bin/pwd","--cwd","relative"],"status":1,"stdout":"","stderr":"error: managed exec working directory must be an absolute path of at most 4096 bytes\n"},{"argv":["sandbox","exec","--state-dir","{root}/state","--entrypoint","/bin/echo","--outcome-report","{root}/reports/exec.json"],"status":1,"stdout":"","stderr":"error: outcome report already exists: {root}/reports/exec.json\n"},{"argv":["sandbox","exec","--state-dir","{root}/state","--entrypoint","/bin/echo","--outcome-report","{root}/missing/exec.json"],"status":1,"stdout":"","stderr":"error: outcome report parent is not a plain directory: {root}/missing\n"},{"argv":["sandbox","stop","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","openvmm.log","outcome.json","sandbox.lock"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 1,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mount\": null,\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"kvm\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 0,\n \"deny_rule_count\": 0,\n \"host_loopback\": \"none\",\n \"mode\": \"none\",\n \"status\": \"not-requested\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","start","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","control.sock","openvmm.log","runtime.json","sandbox.lock"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 1,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mount\": null,\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":{"text":"{\n \"format\": 4,\n \"launch\": \"{hex32}\",\n \"process\": {\n \"pid\": {int},\n \"startTime\": {int},\n \"boot\": \"{hex32}\"\n },\n \"endpoint\": \"{root}/state/control.sock\",\n \"runtime\": {\n \"openvmm\": \"sha256:1e8c924cdaac227bf74b4a4a9bcdb9e8836a1355fd0408cc81dab5d9aa71eb4d\",\n \"kernel\": \"sha256:a0c936696eb7d5ee3192bf53b9d281cecbb40ca9db520de72cb95817ad92ac72\",\n \"initramfs\": \"sha256:2f77c2c97621e8bf69794e9d12815bb7c46338638f126bea8b18dfe02a684d62\",\n \"bootContract\": 2,\n \"controlContract\": \"nvx-microvm-v2-control-v2\",\n \"controlSession\": 1\n }\n}\n","mode":384},"state/control.capability":{"size":32,"mode":384},"state/outcome.json":null,"state/openvmm.log":{"text":""}}},{"argv":["sandbox","stop","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","openvmm.log","outcome.json","sandbox.lock"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 1,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mount\": null,\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"kvm\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 0,\n \"deny_rule_count\": 0,\n \"host_loopback\": \"none\",\n \"mode\": \"none\",\n \"status\": \"not-requested\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","deprovision","--state-dir","{root}/state"],"check":["state"],"status":0,"stdout":"","stderr":"","files":{"state":null}}],"interop":true}, +{"name":"lifecycle: one share","steps":[{"argv":["sandbox","provision","--state-dir","{root}/state","--layer","distro,{root}/distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}/scratch.ext4","--mount","/data,{root}/share"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","sandbox.lock"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 2,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mount\": {\n \"access\": \"ro\",\n \"denied_paths\": [],\n \"guest_target\": \"/data\",\n \"host_path\": \"{root}/share\",\n \"owner\": \"vmm\"\n },\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":null,"state/openvmm.log":null}},{"argv":["sandbox","start","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log","build/fake-openvmm-state.json"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","control.sock","openvmm.log","runtime.json","sandbox.lock"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 2,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mount\": {\n \"access\": \"ro\",\n \"denied_paths\": [],\n \"guest_target\": \"/data\",\n \"host_path\": \"{root}/share\",\n \"owner\": \"vmm\"\n },\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":{"text":"{\n \"format\": 4,\n \"launch\": \"{hex32}\",\n \"process\": {\n \"pid\": {int},\n \"startTime\": {int},\n \"boot\": \"{hex32}\"\n },\n \"endpoint\": \"{root}/state/control.sock\",\n \"runtime\": {\n \"openvmm\": \"sha256:1e8c924cdaac227bf74b4a4a9bcdb9e8836a1355fd0408cc81dab5d9aa71eb4d\",\n \"kernel\": \"sha256:a0c936696eb7d5ee3192bf53b9d281cecbb40ca9db520de72cb95817ad92ac72\",\n \"initramfs\": \"sha256:2f77c2c97621e8bf69794e9d12815bb7c46338638f126bea8b18dfe02a684d62\",\n \"bootContract\": 2,\n \"controlContract\": \"nvx-microvm-v2-control-v2\",\n \"controlSession\": 1\n }\n}\n","mode":384},"state/control.capability":{"size":32,"mode":384},"state/outcome.json":null,"state/openvmm.log":{"text":""},"build/fake-openvmm-state.json":{"text":"[\"--machine\",\"microvm\",\"--microvm-sandbox-block\",\"distro:file:{root}/distro.erofs,ro\",\"--microvm-sandbox-block\",\"scratch:file:{root}/scratch.ext4\",\"--microvm-workload-identity\",\"65534:65534\",\"--mount\",\"/data,{root}/share,ro\",\"--microvm-lifecycle\",\"managed\",\"--single-process\",\"--hypervisor\",\"kvm\",\"--memory\",\"256M\",\"--kernel\",\"{root}/build/vmlinux\",\"--initrd\",\"{root}/build/initramfs.cpio.gz\",\"--cmdline\",\"nvx_sandbox=1 nvx_layer=distro,0xd0003000,erofs:11111111-1111-1111-1111-111111111111 nvx_scratch=0xd0006000,ext4 nvx_entrypoint=/bin/sh nvx_hostname=nvx-sandbox\",\"--virtio-console\",\"none\",\"--microvm-control-console\",\"listen={root}/state/control.sock\",\"--microvm-control-auth-stdin\",\"--microvm-report\",\"{root}/state/outcome.json\"]"}}},{"argv":["sandbox","exec","--state-dir","{root}/state","--entrypoint","/bin/pwd","--cwd","/data"],"status":0,"stdout":"/data\n","stderr":""},{"argv":["sandbox","stop","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","openvmm.log","outcome.json","sandbox.lock"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 2,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mount\": {\n \"access\": \"ro\",\n \"denied_paths\": [],\n \"guest_target\": \"/data\",\n \"host_path\": \"{root}/share\",\n \"owner\": \"vmm\"\n },\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"kvm\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 0,\n \"deny_rule_count\": 0,\n \"host_loopback\": \"none\",\n \"mode\": \"none\",\n \"status\": \"not-requested\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","start","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","control.sock","openvmm.log","runtime.json","sandbox.lock"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 2,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mount\": {\n \"access\": \"ro\",\n \"denied_paths\": [],\n \"guest_target\": \"/data\",\n \"host_path\": \"{root}/share\",\n \"owner\": \"vmm\"\n },\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":{"text":"{\n \"format\": 4,\n \"launch\": \"{hex32}\",\n \"process\": {\n \"pid\": {int},\n \"startTime\": {int},\n \"boot\": \"{hex32}\"\n },\n \"endpoint\": \"{root}/state/control.sock\",\n \"runtime\": {\n \"openvmm\": \"sha256:1e8c924cdaac227bf74b4a4a9bcdb9e8836a1355fd0408cc81dab5d9aa71eb4d\",\n \"kernel\": \"sha256:a0c936696eb7d5ee3192bf53b9d281cecbb40ca9db520de72cb95817ad92ac72\",\n \"initramfs\": \"sha256:2f77c2c97621e8bf69794e9d12815bb7c46338638f126bea8b18dfe02a684d62\",\n \"bootContract\": 2,\n \"controlContract\": \"nvx-microvm-v2-control-v2\",\n \"controlSession\": 1\n }\n}\n","mode":384},"state/control.capability":{"size":32,"mode":384},"state/outcome.json":null,"state/openvmm.log":{"text":""}}},{"argv":["sandbox","stop","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","openvmm.log","outcome.json","sandbox.lock"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 2,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mount\": {\n \"access\": \"ro\",\n \"denied_paths\": [],\n \"guest_target\": \"/data\",\n \"host_path\": \"{root}/share\",\n \"owner\": \"vmm\"\n },\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"kvm\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 0,\n \"deny_rule_count\": 0,\n \"host_loopback\": \"none\",\n \"mode\": \"none\",\n \"status\": \"not-requested\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","deprovision","--state-dir","{root}/state"],"check":["state"],"status":0,"stdout":"","stderr":"","files":{"state":null}}],"interop":true}, +{"name":"lifecycle: caller-owned share","steps":[{"argv":["sandbox","provision","--state-dir","{root}/state","--layer","distro,{root}/distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}/scratch.ext4","--mount","/data,{root}/share,rw","--mount-owner","caller"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","sandbox.lock"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 3,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mount\": {\n \"access\": \"rw\",\n \"denied_paths\": [],\n \"guest_target\": \"/data\",\n \"host_path\": \"{root}/share\",\n \"owner\": \"caller\"\n },\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":null,"state/openvmm.log":null}},{"argv":["sandbox","start","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log","build/fake-openvmm-state.json"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","control.sock","openvmm.log","runtime.json","sandbox.lock"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 3,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mount\": {\n \"access\": \"rw\",\n \"denied_paths\": [],\n \"guest_target\": \"/data\",\n \"host_path\": \"{root}/share\",\n \"owner\": \"caller\"\n },\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":{"text":"{\n \"format\": 4,\n \"launch\": \"{hex32}\",\n \"process\": {\n \"pid\": {int},\n \"startTime\": {int},\n \"boot\": \"{hex32}\"\n },\n \"endpoint\": \"{root}/state/control.sock\",\n \"runtime\": {\n \"openvmm\": \"sha256:1e8c924cdaac227bf74b4a4a9bcdb9e8836a1355fd0408cc81dab5d9aa71eb4d\",\n \"kernel\": \"sha256:a0c936696eb7d5ee3192bf53b9d281cecbb40ca9db520de72cb95817ad92ac72\",\n \"initramfs\": \"sha256:2f77c2c97621e8bf69794e9d12815bb7c46338638f126bea8b18dfe02a684d62\",\n \"bootContract\": 2,\n \"controlContract\": \"nvx-microvm-v2-control-v2\",\n \"controlSession\": 1\n }\n}\n","mode":384},"state/control.capability":{"size":32,"mode":384},"state/outcome.json":null,"state/openvmm.log":{"text":""},"build/fake-openvmm-state.json":{"text":"[\"--machine\",\"microvm\",\"--microvm-sandbox-block\",\"distro:file:{root}/distro.erofs,ro\",\"--microvm-sandbox-block\",\"scratch:file:{root}/scratch.ext4\",\"--microvm-workload-identity\",\"65534:65534\",\"--mount\",\"/data,{root}/share,rw\",\"--mount-owner\",\"caller\",\"--microvm-lifecycle\",\"managed\",\"--single-process\",\"--hypervisor\",\"kvm\",\"--memory\",\"256M\",\"--kernel\",\"{root}/build/vmlinux\",\"--initrd\",\"{root}/build/initramfs.cpio.gz\",\"--cmdline\",\"nvx_sandbox=1 nvx_layer=distro,0xd0003000,erofs:11111111-1111-1111-1111-111111111111 nvx_scratch=0xd0006000,ext4 nvx_entrypoint=/bin/sh nvx_hostname=nvx-sandbox\",\"--virtio-console\",\"none\",\"--microvm-control-console\",\"listen={root}/state/control.sock\",\"--microvm-control-auth-stdin\",\"--microvm-report\",\"{root}/state/outcome.json\"]"}}},{"argv":["sandbox","stop","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","openvmm.log","outcome.json","sandbox.lock"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 3,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mount\": {\n \"access\": \"rw\",\n \"denied_paths\": [],\n \"guest_target\": \"/data\",\n \"host_path\": \"{root}/share\",\n \"owner\": \"caller\"\n },\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"kvm\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 0,\n \"deny_rule_count\": 0,\n \"host_loopback\": \"none\",\n \"mode\": \"none\",\n \"status\": \"not-requested\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","start","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","control.sock","openvmm.log","runtime.json","sandbox.lock"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 3,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mount\": {\n \"access\": \"rw\",\n \"denied_paths\": [],\n \"guest_target\": \"/data\",\n \"host_path\": \"{root}/share\",\n \"owner\": \"caller\"\n },\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":{"text":"{\n \"format\": 4,\n \"launch\": \"{hex32}\",\n \"process\": {\n \"pid\": {int},\n \"startTime\": {int},\n \"boot\": \"{hex32}\"\n },\n \"endpoint\": \"{root}/state/control.sock\",\n \"runtime\": {\n \"openvmm\": \"sha256:1e8c924cdaac227bf74b4a4a9bcdb9e8836a1355fd0408cc81dab5d9aa71eb4d\",\n \"kernel\": \"sha256:a0c936696eb7d5ee3192bf53b9d281cecbb40ca9db520de72cb95817ad92ac72\",\n \"initramfs\": \"sha256:2f77c2c97621e8bf69794e9d12815bb7c46338638f126bea8b18dfe02a684d62\",\n \"bootContract\": 2,\n \"controlContract\": \"nvx-microvm-v2-control-v2\",\n \"controlSession\": 1\n }\n}\n","mode":384},"state/control.capability":{"size":32,"mode":384},"state/outcome.json":null,"state/openvmm.log":{"text":""}}},{"argv":["sandbox","stop","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","openvmm.log","outcome.json","sandbox.lock"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 3,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mount\": {\n \"access\": \"rw\",\n \"denied_paths\": [],\n \"guest_target\": \"/data\",\n \"host_path\": \"{root}/share\",\n \"owner\": \"caller\"\n },\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"kvm\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 0,\n \"deny_rule_count\": 0,\n \"host_loopback\": \"none\",\n \"mode\": \"none\",\n \"status\": \"not-requested\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","deprovision","--state-dir","{root}/state"],"check":["state"],"status":0,"stdout":"","stderr":"","files":{"state":null}}],"interop":true}, +{"name":"lifecycle: several shares","steps":[{"argv":["sandbox","provision","--state-dir","{root}/state","--layer","distro,{root}/distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}/scratch.ext4","--mount","/data,{root}/share","--mount","/other,{root}/share2,rw"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","sandbox.lock"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 4,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mounts\": [\n {\n \"access\": \"ro\",\n \"denied_paths\": [],\n \"guest_target\": \"/data\",\n \"host_path\": \"{root}/share\",\n \"owner\": \"vmm\"\n },\n {\n \"access\": \"rw\",\n \"denied_paths\": [],\n \"guest_target\": \"/other\",\n \"host_path\": \"{root}/share2\",\n \"owner\": \"vmm\"\n }\n ],\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":null,"state/openvmm.log":null}},{"argv":["sandbox","start","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log","build/fake-openvmm-state.json"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","control.sock","openvmm.log","runtime.json","sandbox.lock"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 4,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mounts\": [\n {\n \"access\": \"ro\",\n \"denied_paths\": [],\n \"guest_target\": \"/data\",\n \"host_path\": \"{root}/share\",\n \"owner\": \"vmm\"\n },\n {\n \"access\": \"rw\",\n \"denied_paths\": [],\n \"guest_target\": \"/other\",\n \"host_path\": \"{root}/share2\",\n \"owner\": \"vmm\"\n }\n ],\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":{"text":"{\n \"format\": 4,\n \"launch\": \"{hex32}\",\n \"process\": {\n \"pid\": {int},\n \"startTime\": {int},\n \"boot\": \"{hex32}\"\n },\n \"endpoint\": \"{root}/state/control.sock\",\n \"runtime\": {\n \"openvmm\": \"sha256:1e8c924cdaac227bf74b4a4a9bcdb9e8836a1355fd0408cc81dab5d9aa71eb4d\",\n \"kernel\": \"sha256:a0c936696eb7d5ee3192bf53b9d281cecbb40ca9db520de72cb95817ad92ac72\",\n \"initramfs\": \"sha256:2f77c2c97621e8bf69794e9d12815bb7c46338638f126bea8b18dfe02a684d62\",\n \"bootContract\": 2,\n \"controlContract\": \"nvx-microvm-v2-control-v2\",\n \"controlSession\": 1\n }\n}\n","mode":384},"state/control.capability":{"size":32,"mode":384},"state/outcome.json":null,"state/openvmm.log":{"text":""},"build/fake-openvmm-state.json":{"text":"[\"--machine\",\"microvm\",\"--microvm-sandbox-block\",\"distro:file:{root}/distro.erofs,ro\",\"--microvm-sandbox-block\",\"scratch:file:{root}/scratch.ext4\",\"--microvm-workload-identity\",\"65534:65534\",\"--mount-aggregate\",\"/run/nvx/shares\",\"--mount-child\",\"0-bd47413b5c03dfc6e4b5b8143fe205ea,{root}/share,ro\",\"--mount-child\",\"1-bf2faee2596026620b5cbc997c2d7d45,{root}/share2,rw\",\"--microvm-lifecycle\",\"managed\",\"--single-process\",\"--hypervisor\",\"kvm\",\"--memory\",\"256M\",\"--kernel\",\"{root}/build/vmlinux\",\"--initrd\",\"{root}/build/initramfs.cpio.gz\",\"--cmdline\",\"nvx_sandbox=1 nvx_layer=distro,0xd0003000,erofs:11111111-1111-1111-1111-111111111111 nvx_scratch=0xd0006000,ext4 nvx_entrypoint=/bin/sh nvx_hostname=nvx-sandbox nvx_share=0-bd47413b5c03dfc6e4b5b8143fe205ea,/data,ro nvx_share=1-bf2faee2596026620b5cbc997c2d7d45,/other,rw\",\"--virtio-console\",\"none\",\"--microvm-control-console\",\"listen={root}/state/control.sock\",\"--microvm-control-auth-stdin\",\"--microvm-report\",\"{root}/state/outcome.json\"]"}}},{"argv":["sandbox","exec","--state-dir","{root}/state","--entrypoint","/bin/pwd","--cwd","/other"],"status":0,"stdout":"/other\n","stderr":""},{"argv":["sandbox","stop","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","openvmm.log","outcome.json","sandbox.lock"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 4,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mounts\": [\n {\n \"access\": \"ro\",\n \"denied_paths\": [],\n \"guest_target\": \"/data\",\n \"host_path\": \"{root}/share\",\n \"owner\": \"vmm\"\n },\n {\n \"access\": \"rw\",\n \"denied_paths\": [],\n \"guest_target\": \"/other\",\n \"host_path\": \"{root}/share2\",\n \"owner\": \"vmm\"\n }\n ],\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"kvm\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 0,\n \"deny_rule_count\": 0,\n \"host_loopback\": \"none\",\n \"mode\": \"none\",\n \"status\": \"not-requested\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","start","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","control.sock","openvmm.log","runtime.json","sandbox.lock"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 4,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mounts\": [\n {\n \"access\": \"ro\",\n \"denied_paths\": [],\n \"guest_target\": \"/data\",\n \"host_path\": \"{root}/share\",\n \"owner\": \"vmm\"\n },\n {\n \"access\": \"rw\",\n \"denied_paths\": [],\n \"guest_target\": \"/other\",\n \"host_path\": \"{root}/share2\",\n \"owner\": \"vmm\"\n }\n ],\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":{"text":"{\n \"format\": 4,\n \"launch\": \"{hex32}\",\n \"process\": {\n \"pid\": {int},\n \"startTime\": {int},\n \"boot\": \"{hex32}\"\n },\n \"endpoint\": \"{root}/state/control.sock\",\n \"runtime\": {\n \"openvmm\": \"sha256:1e8c924cdaac227bf74b4a4a9bcdb9e8836a1355fd0408cc81dab5d9aa71eb4d\",\n \"kernel\": \"sha256:a0c936696eb7d5ee3192bf53b9d281cecbb40ca9db520de72cb95817ad92ac72\",\n \"initramfs\": \"sha256:2f77c2c97621e8bf69794e9d12815bb7c46338638f126bea8b18dfe02a684d62\",\n \"bootContract\": 2,\n \"controlContract\": \"nvx-microvm-v2-control-v2\",\n \"controlSession\": 1\n }\n}\n","mode":384},"state/control.capability":{"size":32,"mode":384},"state/outcome.json":null,"state/openvmm.log":{"text":""}}},{"argv":["sandbox","stop","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","openvmm.log","outcome.json","sandbox.lock"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 4,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mounts\": [\n {\n \"access\": \"ro\",\n \"denied_paths\": [],\n \"guest_target\": \"/data\",\n \"host_path\": \"{root}/share\",\n \"owner\": \"vmm\"\n },\n {\n \"access\": \"rw\",\n \"denied_paths\": [],\n \"guest_target\": \"/other\",\n \"host_path\": \"{root}/share2\",\n \"owner\": \"vmm\"\n }\n ],\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"kvm\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 0,\n \"deny_rule_count\": 0,\n \"host_loopback\": \"none\",\n \"mode\": \"none\",\n \"status\": \"not-requested\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","deprovision","--state-dir","{root}/state"],"check":["state"],"status":0,"stdout":"","stderr":"","files":{"state":null}}],"interop":true}, +{"name":"lifecycle: share policies","steps":[{"argv":["sandbox","provision","--state-dir","{root}/state","--layer","distro,{root}/distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}/scratch.ext4","--mount","/data,{root}/share,rw","--mount-deny","{root}/share/hidden","--mount-allow","{root}/share/hidden/shown","--mount-write","{root}/share/writable"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","sandbox.lock"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 5,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mounts\": [\n {\n \"access\": \"rw\",\n \"allowed_paths\": [\n \"{root}/share/hidden/shown\"\n ],\n \"denied_paths\": [\n \"{root}/share/hidden\"\n ],\n \"guest_target\": \"/data\",\n \"host_path\": \"{root}/share\",\n \"owner\": \"vmm\",\n \"writable_paths\": [\n \"{root}/share/writable\"\n ]\n }\n ],\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":null,"state/openvmm.log":null}},{"argv":["sandbox","start","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log","build/fake-openvmm-state.json"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","control.sock","openvmm.log","runtime.json","sandbox.lock"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 5,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mounts\": [\n {\n \"access\": \"rw\",\n \"allowed_paths\": [\n \"{root}/share/hidden/shown\"\n ],\n \"denied_paths\": [\n \"{root}/share/hidden\"\n ],\n \"guest_target\": \"/data\",\n \"host_path\": \"{root}/share\",\n \"owner\": \"vmm\",\n \"writable_paths\": [\n \"{root}/share/writable\"\n ]\n }\n ],\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":{"text":"{\n \"format\": 4,\n \"launch\": \"{hex32}\",\n \"process\": {\n \"pid\": {int},\n \"startTime\": {int},\n \"boot\": \"{hex32}\"\n },\n \"endpoint\": \"{root}/state/control.sock\",\n \"runtime\": {\n \"openvmm\": \"sha256:1e8c924cdaac227bf74b4a4a9bcdb9e8836a1355fd0408cc81dab5d9aa71eb4d\",\n \"kernel\": \"sha256:a0c936696eb7d5ee3192bf53b9d281cecbb40ca9db520de72cb95817ad92ac72\",\n \"initramfs\": \"sha256:2f77c2c97621e8bf69794e9d12815bb7c46338638f126bea8b18dfe02a684d62\",\n \"bootContract\": 2,\n \"controlContract\": \"nvx-microvm-v2-control-v2\",\n \"controlSession\": 1\n }\n}\n","mode":384},"state/control.capability":{"size":32,"mode":384},"state/outcome.json":null,"state/openvmm.log":{"text":""},"build/fake-openvmm-state.json":{"text":"[\"--machine\",\"microvm\",\"--microvm-sandbox-block\",\"distro:file:{root}/distro.erofs,ro\",\"--microvm-sandbox-block\",\"scratch:file:{root}/scratch.ext4\",\"--microvm-workload-identity\",\"65534:65534\",\"--mount\",\"/data,{root}/share,rw\",\"--mount-deny\",\"{root}/share/hidden\",\"--mount-allow\",\"{root}/share/hidden/shown\",\"--mount-write\",\"{root}/share/writable\",\"--microvm-lifecycle\",\"managed\",\"--single-process\",\"--hypervisor\",\"kvm\",\"--memory\",\"256M\",\"--kernel\",\"{root}/build/vmlinux\",\"--initrd\",\"{root}/build/initramfs.cpio.gz\",\"--cmdline\",\"nvx_sandbox=1 nvx_layer=distro,0xd0003000,erofs:11111111-1111-1111-1111-111111111111 nvx_scratch=0xd0006000,ext4 nvx_entrypoint=/bin/sh nvx_hostname=nvx-sandbox\",\"--virtio-console\",\"none\",\"--microvm-control-console\",\"listen={root}/state/control.sock\",\"--microvm-control-auth-stdin\",\"--microvm-report\",\"{root}/state/outcome.json\"]"}}},{"argv":["sandbox","stop","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","openvmm.log","outcome.json","sandbox.lock"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 5,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mounts\": [\n {\n \"access\": \"rw\",\n \"allowed_paths\": [\n \"{root}/share/hidden/shown\"\n ],\n \"denied_paths\": [\n \"{root}/share/hidden\"\n ],\n \"guest_target\": \"/data\",\n \"host_path\": \"{root}/share\",\n \"owner\": \"vmm\",\n \"writable_paths\": [\n \"{root}/share/writable\"\n ]\n }\n ],\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"kvm\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 0,\n \"deny_rule_count\": 0,\n \"host_loopback\": \"none\",\n \"mode\": \"none\",\n \"status\": \"not-requested\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","start","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","control.sock","openvmm.log","runtime.json","sandbox.lock"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 5,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mounts\": [\n {\n \"access\": \"rw\",\n \"allowed_paths\": [\n \"{root}/share/hidden/shown\"\n ],\n \"denied_paths\": [\n \"{root}/share/hidden\"\n ],\n \"guest_target\": \"/data\",\n \"host_path\": \"{root}/share\",\n \"owner\": \"vmm\",\n \"writable_paths\": [\n \"{root}/share/writable\"\n ]\n }\n ],\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":{"text":"{\n \"format\": 4,\n \"launch\": \"{hex32}\",\n \"process\": {\n \"pid\": {int},\n \"startTime\": {int},\n \"boot\": \"{hex32}\"\n },\n \"endpoint\": \"{root}/state/control.sock\",\n \"runtime\": {\n \"openvmm\": \"sha256:1e8c924cdaac227bf74b4a4a9bcdb9e8836a1355fd0408cc81dab5d9aa71eb4d\",\n \"kernel\": \"sha256:a0c936696eb7d5ee3192bf53b9d281cecbb40ca9db520de72cb95817ad92ac72\",\n \"initramfs\": \"sha256:2f77c2c97621e8bf69794e9d12815bb7c46338638f126bea8b18dfe02a684d62\",\n \"bootContract\": 2,\n \"controlContract\": \"nvx-microvm-v2-control-v2\",\n \"controlSession\": 1\n }\n}\n","mode":384},"state/control.capability":{"size":32,"mode":384},"state/outcome.json":null,"state/openvmm.log":{"text":""}}},{"argv":["sandbox","stop","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","openvmm.log","outcome.json","sandbox.lock"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 5,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mounts\": [\n {\n \"access\": \"rw\",\n \"allowed_paths\": [\n \"{root}/share/hidden/shown\"\n ],\n \"denied_paths\": [\n \"{root}/share/hidden\"\n ],\n \"guest_target\": \"/data\",\n \"host_path\": \"{root}/share\",\n \"owner\": \"vmm\",\n \"writable_paths\": [\n \"{root}/share/writable\"\n ]\n }\n ],\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"kvm\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 0,\n \"deny_rule_count\": 0,\n \"host_loopback\": \"none\",\n \"mode\": \"none\",\n \"status\": \"not-requested\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","deprovision","--state-dir","{root}/state"],"check":["state"],"status":0,"stdout":"","stderr":"","files":{"state":null}}],"interop":true}, +{"name":"lifecycle: file share","steps":[{"argv":["sandbox","provision","--state-dir","{root}/state","--layer","distro,{root}/distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}/scratch.ext4","--mount","/etc/single.txt,{root}/single.txt,ro"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","sandbox.lock"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 6,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mounts\": [\n {\n \"access\": \"ro\",\n \"allowed_paths\": [],\n \"denied_paths\": [],\n \"guest_target\": \"/etc/single.txt\",\n \"host_path\": \"{root}/single.txt\",\n \"kind\": \"file\",\n \"owner\": \"vmm\",\n \"writable_paths\": []\n }\n ],\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":null,"state/openvmm.log":null}},{"argv":["sandbox","start","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log","build/fake-openvmm-state.json"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","control.sock","openvmm.log","runtime.json","sandbox.lock"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 6,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mounts\": [\n {\n \"access\": \"ro\",\n \"allowed_paths\": [],\n \"denied_paths\": [],\n \"guest_target\": \"/etc/single.txt\",\n \"host_path\": \"{root}/single.txt\",\n \"kind\": \"file\",\n \"owner\": \"vmm\",\n \"writable_paths\": []\n }\n ],\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":{"text":"{\n \"format\": 4,\n \"launch\": \"{hex32}\",\n \"process\": {\n \"pid\": {int},\n \"startTime\": {int},\n \"boot\": \"{hex32}\"\n },\n \"endpoint\": \"{root}/state/control.sock\",\n \"runtime\": {\n \"openvmm\": \"sha256:1e8c924cdaac227bf74b4a4a9bcdb9e8836a1355fd0408cc81dab5d9aa71eb4d\",\n \"kernel\": \"sha256:a0c936696eb7d5ee3192bf53b9d281cecbb40ca9db520de72cb95817ad92ac72\",\n \"initramfs\": \"sha256:2f77c2c97621e8bf69794e9d12815bb7c46338638f126bea8b18dfe02a684d62\",\n \"bootContract\": 2,\n \"controlContract\": \"nvx-microvm-v2-control-v2\",\n \"controlSession\": 1\n }\n}\n","mode":384},"state/control.capability":{"size":32,"mode":384},"state/outcome.json":null,"state/openvmm.log":{"text":""},"build/fake-openvmm-state.json":{"text":"[\"--machine\",\"microvm\",\"--microvm-sandbox-block\",\"distro:file:{root}/distro.erofs,ro\",\"--microvm-sandbox-block\",\"scratch:file:{root}/scratch.ext4\",\"--microvm-workload-identity\",\"65534:65534\",\"--mount-aggregate\",\"/run/nvx/shares\",\"--mount-child\",\"0-a80a05e9cb129bc27b7ba103bf2f5ac0,{root}/single.txt,ro,file\",\"--microvm-lifecycle\",\"managed\",\"--single-process\",\"--hypervisor\",\"kvm\",\"--memory\",\"256M\",\"--kernel\",\"{root}/build/vmlinux\",\"--initrd\",\"{root}/build/initramfs.cpio.gz\",\"--cmdline\",\"nvx_sandbox=1 nvx_layer=distro,0xd0003000,erofs:11111111-1111-1111-1111-111111111111 nvx_scratch=0xd0006000,ext4 nvx_entrypoint=/bin/sh nvx_hostname=nvx-sandbox nvx_share=0-a80a05e9cb129bc27b7ba103bf2f5ac0,/etc/single.txt,ro,file\",\"--virtio-console\",\"none\",\"--microvm-control-console\",\"listen={root}/state/control.sock\",\"--microvm-control-auth-stdin\",\"--microvm-report\",\"{root}/state/outcome.json\"]"}}},{"argv":["sandbox","stop","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","openvmm.log","outcome.json","sandbox.lock"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 6,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mounts\": [\n {\n \"access\": \"ro\",\n \"allowed_paths\": [],\n \"denied_paths\": [],\n \"guest_target\": \"/etc/single.txt\",\n \"host_path\": \"{root}/single.txt\",\n \"kind\": \"file\",\n \"owner\": \"vmm\",\n \"writable_paths\": []\n }\n ],\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"kvm\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 0,\n \"deny_rule_count\": 0,\n \"host_loopback\": \"none\",\n \"mode\": \"none\",\n \"status\": \"not-requested\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","start","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","control.sock","openvmm.log","runtime.json","sandbox.lock"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 6,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mounts\": [\n {\n \"access\": \"ro\",\n \"allowed_paths\": [],\n \"denied_paths\": [],\n \"guest_target\": \"/etc/single.txt\",\n \"host_path\": \"{root}/single.txt\",\n \"kind\": \"file\",\n \"owner\": \"vmm\",\n \"writable_paths\": []\n }\n ],\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":{"text":"{\n \"format\": 4,\n \"launch\": \"{hex32}\",\n \"process\": {\n \"pid\": {int},\n \"startTime\": {int},\n \"boot\": \"{hex32}\"\n },\n \"endpoint\": \"{root}/state/control.sock\",\n \"runtime\": {\n \"openvmm\": \"sha256:1e8c924cdaac227bf74b4a4a9bcdb9e8836a1355fd0408cc81dab5d9aa71eb4d\",\n \"kernel\": \"sha256:a0c936696eb7d5ee3192bf53b9d281cecbb40ca9db520de72cb95817ad92ac72\",\n \"initramfs\": \"sha256:2f77c2c97621e8bf69794e9d12815bb7c46338638f126bea8b18dfe02a684d62\",\n \"bootContract\": 2,\n \"controlContract\": \"nvx-microvm-v2-control-v2\",\n \"controlSession\": 1\n }\n}\n","mode":384},"state/control.capability":{"size":32,"mode":384},"state/outcome.json":null,"state/openvmm.log":{"text":""}}},{"argv":["sandbox","stop","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","openvmm.log","outcome.json","sandbox.lock"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 6,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mounts\": [\n {\n \"access\": \"ro\",\n \"allowed_paths\": [],\n \"denied_paths\": [],\n \"guest_target\": \"/etc/single.txt\",\n \"host_path\": \"{root}/single.txt\",\n \"kind\": \"file\",\n \"owner\": \"vmm\",\n \"writable_paths\": []\n }\n ],\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"kvm\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 0,\n \"deny_rule_count\": 0,\n \"host_loopback\": \"none\",\n \"mode\": \"none\",\n \"status\": \"not-requested\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","deprovision","--state-dir","{root}/state"],"check":["state"],"status":0,"stdout":"","stderr":"","files":{"state":null}}],"interop":true}, +{"name":"lifecycle: network and launch options","steps":[{"argv":["sandbox","provision","--state-dir","{root}/state","--layer","distro,{root}/distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}/scratch.ext4","--entrypoint","/bin/server","--arg=--serve","--hostname","worker","--workload-user","1000:1000","--memory-max","268435456","--pids-max","64","--memory-mib","384","--hypervisor","auto","--cmdline","quiet","--net","10.0.0.2/24","--network-profile","portable","--network-egress","deny","--network-ingress","deny","--network-egress-policy-file","{root}/policy.json","--host-loopback","allow","--network-proxy","10.0.0.1:3128","--host-loopback-forward","8080","--host-loopback-forward","9090"],"write":{"policy.json":"{\"allow\": [{\"cidr\": \"192.0.2.0/24\", \"protocol\": \"tcp\", \"port\": 443}], \"deny\": [{\"cidr\": \"192.0.2.128/25\"}]}"},"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","sandbox.lock"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"quiet\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 1,\n \"host_loopback\": \"allow\",\n \"host_loopback_forward\": [\n \"8080\",\n \"9090\"\n ],\n \"hostname\": \"worker\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": 268435456,\n \"memory_mib\": 384,\n \"mount\": null,\n \"net\": \"10.0.0.2/24\",\n \"network_egress\": \"deny\",\n \"network_egress_allow\": [\n \"192.0.2.0/24:tcp:443\"\n ],\n \"network_egress_deny\": [\n \"192.0.2.128/25\"\n ],\n \"network_ingress\": \"deny\",\n \"network_profile\": \"portable\",\n \"network_proxy\": \"10.0.0.1:3128\",\n \"pids_max\": 64,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 1000,\n \"workload_uid\": 1000\n}\n","mode":384},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":null,"state/openvmm.log":null}},{"argv":["sandbox","start","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log","build/fake-openvmm-state.json"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","control.sock","openvmm.log","runtime.json","sandbox.lock"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"quiet\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 1,\n \"host_loopback\": \"allow\",\n \"host_loopback_forward\": [\n \"8080\",\n \"9090\"\n ],\n \"hostname\": \"worker\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": 268435456,\n \"memory_mib\": 384,\n \"mount\": null,\n \"net\": \"10.0.0.2/24\",\n \"network_egress\": \"deny\",\n \"network_egress_allow\": [\n \"192.0.2.0/24:tcp:443\"\n ],\n \"network_egress_deny\": [\n \"192.0.2.128/25\"\n ],\n \"network_ingress\": \"deny\",\n \"network_profile\": \"portable\",\n \"network_proxy\": \"10.0.0.1:3128\",\n \"pids_max\": 64,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 1000,\n \"workload_uid\": 1000\n}\n","mode":384},"state/runtime.json":{"text":"{\n \"format\": 4,\n \"launch\": \"{hex32}\",\n \"process\": {\n \"pid\": {int},\n \"startTime\": {int},\n \"boot\": \"{hex32}\"\n },\n \"endpoint\": \"{root}/state/control.sock\",\n \"runtime\": {\n \"openvmm\": \"sha256:1e8c924cdaac227bf74b4a4a9bcdb9e8836a1355fd0408cc81dab5d9aa71eb4d\",\n \"kernel\": \"sha256:a0c936696eb7d5ee3192bf53b9d281cecbb40ca9db520de72cb95817ad92ac72\",\n \"initramfs\": \"sha256:2f77c2c97621e8bf69794e9d12815bb7c46338638f126bea8b18dfe02a684d62\",\n \"bootContract\": 2,\n \"controlContract\": \"nvx-microvm-v2-control-v2\",\n \"controlSession\": 1\n }\n}\n","mode":384},"state/control.capability":{"size":32,"mode":384},"state/outcome.json":null,"state/openvmm.log":{"text":""},"build/fake-openvmm-state.json":{"text":"[\"--machine\",\"microvm\",\"--microvm-sandbox-block\",\"distro:file:{root}/distro.erofs,ro\",\"--microvm-sandbox-block\",\"scratch:file:{root}/scratch.ext4\",\"--microvm-workload-identity\",\"1000:1000\",\"--microvm-lifecycle\",\"managed\",\"--single-process\",\"--hypervisor\",\"kvm\",\"--memory\",\"384M\",\"--kernel\",\"{root}/build/vmlinux\",\"--initrd\",\"{root}/build/initramfs.cpio.gz\",\"--cmdline\",\"quiet nvx_sandbox=1 nvx_layer=distro,0xd0003000,erofs:11111111-1111-1111-1111-111111111111 nvx_scratch=0xd0006000,ext4 nvx_entrypoint=/bin/sh nvx_hostname=worker nvx_memory_max=268435456 nvx_pids_max=65\",\"--virtio-console\",\"none\",\"--microvm-control-console\",\"listen={root}/state/control.sock\",\"--microvm-control-auth-stdin\",\"--microvm-report\",\"{root}/state/outcome.json\",\"--net\",\"10.0.0.2/24\",\"--network-profile\",\"portable\",\"--network-egress\",\"deny\",\"--network-ingress\",\"deny\",\"--host-loopback\",\"allow\",\"--network-egress-allow\",\"192.0.2.0/24:tcp:443\",\"--network-egress-deny\",\"192.0.2.128/25\",\"--host-loopback-forward\",\"8080\",\"--host-loopback-forward\",\"9090\",\"--network-proxy\",\"10.0.0.1:3128\"]"}}},{"argv":["sandbox","stop","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","openvmm.log","outcome.json","sandbox.lock"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"quiet\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 1,\n \"host_loopback\": \"allow\",\n \"host_loopback_forward\": [\n \"8080\",\n \"9090\"\n ],\n \"hostname\": \"worker\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": 268435456,\n \"memory_mib\": 384,\n \"mount\": null,\n \"net\": \"10.0.0.2/24\",\n \"network_egress\": \"deny\",\n \"network_egress_allow\": [\n \"192.0.2.0/24:tcp:443\"\n ],\n \"network_egress_deny\": [\n \"192.0.2.128/25\"\n ],\n \"network_ingress\": \"deny\",\n \"network_profile\": \"portable\",\n \"network_proxy\": \"10.0.0.1:3128\",\n \"pids_max\": 64,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 1000,\n \"workload_uid\": 1000\n}\n","mode":384},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"kvm\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 1,\n \"deny_rule_count\": 1,\n \"host_loopback\": \"allow\",\n \"mode\": \"rules\",\n \"status\": \"applied\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","start","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","control.sock","openvmm.log","runtime.json","sandbox.lock"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"quiet\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 1,\n \"host_loopback\": \"allow\",\n \"host_loopback_forward\": [\n \"8080\",\n \"9090\"\n ],\n \"hostname\": \"worker\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": 268435456,\n \"memory_mib\": 384,\n \"mount\": null,\n \"net\": \"10.0.0.2/24\",\n \"network_egress\": \"deny\",\n \"network_egress_allow\": [\n \"192.0.2.0/24:tcp:443\"\n ],\n \"network_egress_deny\": [\n \"192.0.2.128/25\"\n ],\n \"network_ingress\": \"deny\",\n \"network_profile\": \"portable\",\n \"network_proxy\": \"10.0.0.1:3128\",\n \"pids_max\": 64,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 1000,\n \"workload_uid\": 1000\n}\n","mode":384},"state/runtime.json":{"text":"{\n \"format\": 4,\n \"launch\": \"{hex32}\",\n \"process\": {\n \"pid\": {int},\n \"startTime\": {int},\n \"boot\": \"{hex32}\"\n },\n \"endpoint\": \"{root}/state/control.sock\",\n \"runtime\": {\n \"openvmm\": \"sha256:1e8c924cdaac227bf74b4a4a9bcdb9e8836a1355fd0408cc81dab5d9aa71eb4d\",\n \"kernel\": \"sha256:a0c936696eb7d5ee3192bf53b9d281cecbb40ca9db520de72cb95817ad92ac72\",\n \"initramfs\": \"sha256:2f77c2c97621e8bf69794e9d12815bb7c46338638f126bea8b18dfe02a684d62\",\n \"bootContract\": 2,\n \"controlContract\": \"nvx-microvm-v2-control-v2\",\n \"controlSession\": 1\n }\n}\n","mode":384},"state/control.capability":{"size":32,"mode":384},"state/outcome.json":null,"state/openvmm.log":{"text":""}}},{"argv":["sandbox","stop","--state-dir","{root}/state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","openvmm.log","outcome.json","sandbox.lock"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"quiet\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 1,\n \"host_loopback\": \"allow\",\n \"host_loopback_forward\": [\n \"8080\",\n \"9090\"\n ],\n \"hostname\": \"worker\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": 268435456,\n \"memory_mib\": 384,\n \"mount\": null,\n \"net\": \"10.0.0.2/24\",\n \"network_egress\": \"deny\",\n \"network_egress_allow\": [\n \"192.0.2.0/24:tcp:443\"\n ],\n \"network_egress_deny\": [\n \"192.0.2.128/25\"\n ],\n \"network_ingress\": \"deny\",\n \"network_profile\": \"portable\",\n \"network_proxy\": \"10.0.0.1:3128\",\n \"pids_max\": 64,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 1000,\n \"workload_uid\": 1000\n}\n","mode":384},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"kvm\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 1,\n \"deny_rule_count\": 1,\n \"host_loopback\": \"allow\",\n \"mode\": \"rules\",\n \"status\": \"applied\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","deprovision","--state-dir","{root}/state"],"check":["state"],"status":0,"stdout":"","stderr":"","files":{"state":null}}],"interop":true}, {"name":"state: operations without a sandbox","steps":[{"argv":["sandbox","start","--state-dir","{root}/state"],"status":1,"stdout":"","stderr":"error: sandbox state path is not a plain directory: {root}/state\n"},{"argv":["sandbox","exec","--state-dir","{root}/state"],"status":1,"stdout":"","stderr":"error: sandbox state path is not a plain directory: {root}/state\n"},{"argv":["sandbox","stop","--state-dir","{root}/state"],"status":1,"stdout":"","stderr":"error: sandbox state path is not a plain directory: {root}/state\n"},{"argv":["sandbox","deprovision","--state-dir","{root}/state"],"status":1,"stdout":"","stderr":"error: sandbox state path is not a plain directory: {root}/state\n"}]}, {"name":"state: refusals of a running sandbox","steps":[{"argv":["sandbox","provision","--state-dir","{root}/state","--layer","distro,{root}/distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}/scratch.ext4"],"status":0,"stdout":"","stderr":""},{"argv":["sandbox","provision","--state-dir","{root}/state","--layer","distro,{root}/distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}/scratch.ext4"],"status":1,"stdout":"","stderr":"error: sandbox is already provisioned\n"},{"argv":["sandbox","start","--state-dir","{root}/state"],"status":0,"stdout":"","stderr":""},{"argv":["sandbox","start","--state-dir","{root}/state"],"status":1,"stdout":"","stderr":"error: sandbox is already running or has stale runtime state\n"},{"argv":["sandbox","deprovision","--state-dir","{root}/state"],"status":1,"stdout":"","stderr":"error: sandbox must be stopped before deprovision\n"},{"argv":["sandbox","stop","--state-dir","{root}/state"],"status":0,"stdout":"","stderr":""},{"argv":["sandbox","stop","--state-dir","{root}/state"],"status":1,"stdout":"","stderr":"error: sandbox is not running\n"},{"argv":["sandbox","exec","--state-dir","{root}/state"],"status":1,"stdout":"","stderr":"error: sandbox is not running\n"},{"argv":["sandbox","deprovision","--state-dir","{root}/state"],"check":["state"],"status":0,"stdout":"","stderr":"","files":{"state":null}}],"interop":true}, -{"name":"state: relative and nested state directory","steps":[{"argv":["sandbox","provision","--state-dir","nested/state","--layer","distro,{root}/distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}/scratch.ext4"],"check":["nested/state","nested/state/config.json"],"status":0,"stdout":"","stderr":"","files":{"nested/state":{"entries":["config.json","lifecycle.lock"],"mode":448},"nested/state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 1,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mount\": null,\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384}}},{"argv":["sandbox","deprovision","--state-dir","nested/state"],"check":["nested/state"],"status":0,"stdout":"","stderr":"","files":{"nested/state":null}}]}, -{"name":"state: failed start","steps":[{"argv":["sandbox","provision","--state-dir","{root}/state","--layer","distro,{root}/distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}/scratch.ext4","--cmdline","fake_exit_on_start=5"],"status":0,"stdout":"","stderr":""},{"argv":["sandbox","start","--state-dir","{root}/state","--timeout","0.5"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":1,"stdout":"","stderr":"error: OpenVMM exited during startup with status 5\n","files":{"state":{"entries":["config.json","lifecycle.lock","openvmm.log"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"fake_exit_on_start=5\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 1,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mount\": null,\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":null,"state/openvmm.log":{"text":"openvmm: failing the launch as requested\n"}}},{"argv":["sandbox","start","--state-dir","{root}/state","--timeout","0.5"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":1,"stdout":"","stderr":"error: OpenVMM exited during startup with status 5\n","files":{"state":{"entries":["config.json","lifecycle.lock","openvmm.log"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"fake_exit_on_start=5\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 1,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mount\": null,\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":null,"state/openvmm.log":{"text":"openvmm: failing the launch as requested\nopenvmm: failing the launch as requested\n"}}},{"argv":["sandbox","deprovision","--state-dir","{root}/state"],"check":["state"],"status":0,"stdout":"","stderr":"","files":{"state":null}}],"interop":true}, -{"name":"state: start without OpenVMM","steps":[{"argv":["sandbox","provision","--state-dir","{root}/state","--layer","distro,{root}/distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}/scratch.ext4"],"status":0,"stdout":"","stderr":""},{"argv":["sandbox","start","--state-dir","{root}/state"],"remove":["openvmm/target/release/openvmm{exe}"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":1,"stdout":"","stderr":"error: OpenVMM release binary not found: {root}/openvmm/target/release/openvmm\n","files":{"state":{"entries":["config.json","lifecycle.lock"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 1,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mount\": null,\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":null,"state/openvmm.log":null}},{"argv":["sandbox","deprovision","--state-dir","{root}/state"],"status":0,"stdout":"","stderr":""}]}, +{"name":"state: relative and nested state directory","steps":[{"argv":["sandbox","provision","--state-dir","nested/state","--layer","distro,{root}/distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}/scratch.ext4"],"check":["nested/state","nested/state/config.json"],"status":0,"stdout":"","stderr":"","files":{"nested/state":{"entries":["config.json","sandbox.lock"],"mode":448},"nested/state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 1,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mount\": null,\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384}}},{"argv":["sandbox","deprovision","--state-dir","nested/state"],"check":["nested/state"],"status":0,"stdout":"","stderr":"","files":{"nested/state":null}}]}, +{"name":"state: failed start","steps":[{"argv":["sandbox","provision","--state-dir","{root}/state","--layer","distro,{root}/distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}/scratch.ext4","--cmdline","fake_exit_on_start=5"],"status":0,"stdout":"","stderr":""},{"argv":["sandbox","start","--state-dir","{root}/state","--timeout","0.5"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":1,"stdout":"","stderr":"error: OpenVMM exited during startup with status 5\n","files":{"state":{"entries":["config.json","openvmm.log","sandbox.lock"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"fake_exit_on_start=5\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 1,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mount\": null,\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":null,"state/openvmm.log":{"text":"openvmm: failing the launch as requested\n"}}},{"argv":["sandbox","start","--state-dir","{root}/state","--timeout","0.5"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":1,"stdout":"","stderr":"error: OpenVMM exited during startup with status 5\n","files":{"state":{"entries":["config.json","openvmm.log","sandbox.lock"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"fake_exit_on_start=5\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 1,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mount\": null,\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":null,"state/openvmm.log":{"text":"openvmm: failing the launch as requested\n"}}},{"argv":["sandbox","deprovision","--state-dir","{root}/state"],"check":["state"],"status":0,"stdout":"","stderr":"","files":{"state":null}}],"interop":true}, +{"name":"state: start without OpenVMM","steps":[{"argv":["sandbox","provision","--state-dir","{root}/state","--layer","distro,{root}/distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}/scratch.ext4"],"status":0,"stdout":"","stderr":""},{"argv":["sandbox","start","--state-dir","{root}/state"],"remove":["openvmm/target/release/openvmm{exe}"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":1,"stdout":"","stderr":"error: OpenVMM release binary not found: {root}/openvmm/target/release/openvmm\n","files":{"state":{"entries":["config.json","sandbox.lock"],"mode":448},"state/config.json":{"text":"{\n \"cmdline\": \"\",\n \"distro_binding\": {\n \"format\": \"erofs\",\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\n \"image\": {\n \"changed\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"device\": {int},\n \"fileId\": \"{hex32}\",\n \"kind\": \"regular\",\n \"length\": 7,\n \"modified\": {\n \"nanoseconds\": {int},\n \"seconds\": {int}\n },\n \"scheme\": \"linux\",\n \"version\": 1\n },\n \"manifest\": null,\n \"policy\": {\n \"result\": \"accepted\",\n \"revision\": 1\n },\n \"version\": 1\n },\n \"format\": 1,\n \"host_loopback\": null,\n \"host_loopback_forward\": [],\n \"hostname\": \"nvx-sandbox\",\n \"hypervisor\": \"kvm\",\n \"layers\": [\n {\n \"path\": \"{root}/distro.erofs\",\n \"role\": \"distro\",\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\n }\n ],\n \"memory_max\": null,\n \"memory_mib\": 256,\n \"mount\": null,\n \"net\": null,\n \"network_egress\": null,\n \"network_egress_allow\": [],\n \"network_egress_deny\": [],\n \"network_ingress\": null,\n \"network_profile\": null,\n \"network_proxy\": null,\n \"pids_max\": null,\n \"scratch\": \"{root}/scratch.ext4\",\n \"workload_gid\": 65534,\n \"workload_uid\": 65534\n}\n","mode":384},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":null,"state/openvmm.log":null}},{"argv":["sandbox","deprovision","--state-dir","{root}/state"],"status":0,"stdout":"","stderr":""}]}, {"name":"state: malformed configuration","steps":[{"argv":["sandbox","provision","--state-dir","{root}/state","--layer","distro,{root}/distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}/scratch.ext4"],"status":0,"stdout":"","stderr":""},{"argv":["sandbox","start","--state-dir","{root}/state"],"write":{"state/config.json":"{\"format\": 7}"},"status":1,"stdout":"","stderr":"error: sandbox configuration has an unsupported format: {root}/state/config.json\n"},{"argv":["sandbox","start","--state-dir","{root}/state"],"write":{"state/config.json":"[]"},"status":1,"stdout":"","stderr":"error: sandbox configuration has an unsupported format: {root}/state/config.json\n"},{"argv":["sandbox","start","--state-dir","{root}/state"],"write":{"state/config.json":"{\"format\": 1}"},"status":1,"stdout":"","stderr":"error: sandbox configuration is malformed\n"},{"argv":["sandbox","deprovision","--state-dir","{root}/state"],"check":["state"],"status":0,"stdout":"","stderr":"","files":{"state":null}}]}, {"name":"state: not a directory","steps":[{"argv":["sandbox","start","--state-dir","{root}/single.txt"],"status":1,"stdout":"","stderr":"error: sandbox state path is not a plain directory: {root}/single.txt\n"},{"argv":["sandbox","provision","--state-dir","{root}/single.txt","--layer","distro,{root}/distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}/scratch.ext4"],"status":1,"stdout":"","stderr":"error: [Errno 17] File exists: '{root}/single.txt'\n"},{"argv":["sandbox","deprovision","--state-dir","{root}/single.txt"],"status":1,"stdout":"","stderr":"error: sandbox state path is not a plain directory: {root}/single.txt\n"}]}, {"name":"state: foreign file","steps":[{"argv":["sandbox","provision","--state-dir","{root}/state","--layer","distro,{root}/distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}/scratch.ext4"],"status":0,"stdout":"","stderr":""},{"argv":["sandbox","deprovision","--state-dir","{root}/state"],"write":{"state/foreign.txt":"x"},"check":["state"],"status":1,"stdout":"","stderr":"error: sandbox state directory contains files not owned by NVX: foreign.txt\n","files":{"state":{"entries":["foreign.txt"],"mode":448}}}]} @@ -287,17 +290,17 @@ {"name":"run: missing OpenVMM","steps":[{"argv":["sandbox","run","--layer","distro,{root}\\distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}\\scratch.ext4"],"remove":["openvmm/target/release/openvmm{exe}"],"status":1,"stdout":"","stderr":"error: OpenVMM release binary not found: {root}\\openvmm\\target\\release\\openvmm.exe\r\n"}]}, {"name":"run: missing kernel","steps":[{"argv":["sandbox","run","--layer","distro,{root}\\distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}\\scratch.ext4"],"remove":["build/vmlinux"],"status":1,"stdout":"","stderr":"error: Linux direct kernel not found: {root}\\build\\vmlinux\r\n"}]}, {"name":"run: missing initramfs","steps":[{"argv":["sandbox","run","--layer","distro,{root}\\distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}\\scratch.ext4"],"remove":["build/initramfs.cpio.gz"],"status":1,"stdout":"","stderr":"error: initramfs not found: {root}\\build\\initramfs.cpio.gz\r\n"}]}, -{"name":"lifecycle: minimal","steps":[{"argv":["sandbox","provision","--state-dir","{root}\\state","--layer","distro,{root}\\distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}\\scratch.ext4"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","lifecycle.lock"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 1,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mount\": null,\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":null,"state/openvmm.log":null}},{"argv":["sandbox","start","--state-dir","{root}\\state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log","build/fake-openvmm-state.json"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","lifecycle.lock","openvmm.log","runtime.json"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 1,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mount\": null,\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":{"text":"{\r\n \"control_endpoint\": \"//./pipe/openvmm-microvm-{hex32}\",\r\n \"format\": 1,\r\n \"pid\": {int},\r\n \"start_time\": {int}\r\n}\r\n"},"state/control.capability":{"size":32},"state/outcome.json":null,"state/openvmm.log":{"text":""},"build/fake-openvmm-state.json":{"text":"[\"--machine\",\"microvm\",\"--microvm-sandbox-block\",\"distro:file:{root_json}\\\\distro.erofs,ro\",\"--microvm-sandbox-block\",\"scratch:file:{root_json}\\\\scratch.ext4\",\"--microvm-workload-identity\",\"65534:65534\",\"--microvm-lifecycle\",\"managed\",\"--single-process\",\"--hypervisor\",\"whp\",\"--memory\",\"256M\",\"--kernel\",\"{root_json}\\\\build\\\\vmlinux\",\"--initrd\",\"{root_json}\\\\build\\\\initramfs.cpio.gz\",\"--cmdline\",\"nvx_sandbox=1 nvx_layer=distro,0xd0003000,erofs:11111111-1111-1111-1111-111111111111 nvx_scratch=0xd0006000,ext4 nvx_entrypoint=/bin/sh nvx_hostname=nvx-sandbox\",\"--virtio-console\",\"none\",\"--microvm-control-console\",\"listen=//./pipe/openvmm-microvm-{hex32}\",\"--microvm-control-auth-stdin\",\"--microvm-report\",\"{root_json}\\\\state\\\\outcome.json\"]"}}},{"argv":["sandbox","exec","--state-dir","{root}\\state","--entrypoint","/bin/echo","--arg","hello","--arg","world"],"status":0,"stdout":"hello world\n","stderr":""},{"argv":["sandbox","exec","--state-dir","{root}\\state","--entrypoint","/bin/pwd","--cwd","/tmp"],"status":0,"stdout":"/tmp\n","stderr":""},{"argv":["sandbox","exec","--state-dir","{root}\\state","--entrypoint","/bin/sh","--arg=-c","--arg","echo out; echoerr err; exit 3","--outcome-report","{root}\\reports\\exec.json"],"check":["reports/exec.json"],"status":3,"stdout":"out\n","stderr":"err\n","files":{"reports/exec.json":{"text":"{\n \"operation_id\": \"{hex32}\",\n \"outcome\": {\n \"category\": \"exit\",\n \"operation\": \"exec\",\n \"status_code\": 3\n },\n \"schema_version\": 1\n}\n"}}},{"argv":["sandbox","exec","--state-dir","{root}\\state","--entrypoint","/usr/bin/env","--environment","A=1","--environment","B=two"],"status":0,"stdout":"A=1\nB=two\n","stderr":""},{"argv":["sandbox","exec","--state-dir","{root}\\state","--entrypoint","/usr/bin/printenv","--arg","PATH","--inherit-default-environment","--environment","A=1"],"status":0,"stdout":"/usr/sbin:/usr/bin:/sbin:/bin\n","stderr":""},{"argv":["sandbox","exec","--state-dir","{root}\\state","--entrypoint","/usr/bin/env","--environment-file","{root}\\env.json"],"write":{"env.json":"[\"X=1\", \"Y=two words\"]"},"status":0,"stdout":"X=1\nY=two words\n","stderr":""},{"argv":["sandbox","exec","--state-dir","{root}\\state","--entrypoint","/usr/bin/env","--environment-file","{root}\\empty.json"],"write":{"empty.json":"[]"},"status":0,"stdout":"","stderr":""},{"argv":["sandbox","exec","--state-dir","{root}\\state","--entrypoint","/bin/sh","--arg=-c","--arg","sleep 5000","--exec-timeout-ms","50","--outcome-report","{root}\\reports\\exec-timeout.json"],"check":["reports/exec-timeout.json"],"status":124,"stdout":"","stderr":"","files":{"reports/exec-timeout.json":{"text":"{\n \"operation_id\": \"{hex32}\",\n \"outcome\": {\n \"category\": \"timeout\",\n \"operation\": \"exec\",\n \"status_code\": 124\n },\n \"schema_version\": 1\n}\n"}}},{"argv":["sandbox","exec","--state-dir","{root}\\state","--entrypoint","/bin/sh","--arg=-c","--arg","signal 9"],"status":137,"stdout":"","stderr":""},{"argv":["sandbox","exec","--state-dir","{root}\\state","--entrypoint","/bin/missing"],"status":127,"stdout":"","stderr":"openvmm: /bin/missing: not found\n"},{"argv":["sandbox","exec","--state-dir","{root}\\state","--entrypoint","/bin/pwd","--cwd","/missing"],"status":1,"stdout":"","stderr":"error: managed guest rejected exec (status=2, category=cwd-failed)\r\n"},{"argv":["sandbox","exec","--state-dir","{root}\\state","--entrypoint","/bin/pwd","--cwd","relative"],"status":1,"stdout":"","stderr":"error: managed exec working directory must be an absolute path of at most 4096 bytes\r\n"},{"argv":["sandbox","exec","--state-dir","{root}\\state","--entrypoint","/bin/echo","--outcome-report","{root}\\reports\\exec.json"],"status":1,"stdout":"","stderr":"error: outcome report already exists: {root}\\reports\\exec.json\r\n"},{"argv":["sandbox","exec","--state-dir","{root}\\state","--entrypoint","/bin/echo","--outcome-report","{root}\\missing\\exec.json"],"status":1,"stdout":"","stderr":"error: outcome report parent is not a plain directory: {root}\\missing\r\n"},{"argv":["sandbox","stop","--state-dir","{root}\\state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","lifecycle.lock","openvmm.log","outcome.json"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 1,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mount\": null,\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"whp\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 0,\n \"deny_rule_count\": 0,\n \"host_loopback\": \"none\",\n \"mode\": \"none\",\n \"status\": \"not-requested\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","start","--state-dir","{root}\\state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","lifecycle.lock","openvmm.log","runtime.json"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 1,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mount\": null,\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":{"text":"{\r\n \"control_endpoint\": \"//./pipe/openvmm-microvm-{hex32}\",\r\n \"format\": 1,\r\n \"pid\": {int},\r\n \"start_time\": {int}\r\n}\r\n"},"state/control.capability":{"size":32},"state/outcome.json":null,"state/openvmm.log":{"text":""}}},{"argv":["sandbox","stop","--state-dir","{root}\\state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","lifecycle.lock","openvmm.log","outcome.json"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 1,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mount\": null,\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"whp\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 0,\n \"deny_rule_count\": 0,\n \"host_loopback\": \"none\",\n \"mode\": \"none\",\n \"status\": \"not-requested\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","deprovision","--state-dir","{root}\\state"],"check":["state"],"status":0,"stdout":"","stderr":"","files":{"state":null}}],"interop":true}, -{"name":"lifecycle: one share","steps":[{"argv":["sandbox","provision","--state-dir","{root}\\state","--layer","distro,{root}\\distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}\\scratch.ext4","--mount","/data,{root}\\share"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","lifecycle.lock"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 2,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mount\": {\r\n \"access\": \"ro\",\r\n \"denied_paths\": [],\r\n \"guest_target\": \"/data\",\r\n \"host_path\": \"{root_json}\\\\share\",\r\n \"owner\": \"vmm\"\r\n },\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":null,"state/openvmm.log":null}},{"argv":["sandbox","start","--state-dir","{root}\\state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log","build/fake-openvmm-state.json"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","lifecycle.lock","openvmm.log","runtime.json"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 2,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mount\": {\r\n \"access\": \"ro\",\r\n \"denied_paths\": [],\r\n \"guest_target\": \"/data\",\r\n \"host_path\": \"{root_json}\\\\share\",\r\n \"owner\": \"vmm\"\r\n },\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":{"text":"{\r\n \"control_endpoint\": \"//./pipe/openvmm-microvm-{hex32}\",\r\n \"format\": 1,\r\n \"pid\": {int},\r\n \"start_time\": {int}\r\n}\r\n"},"state/control.capability":{"size":32},"state/outcome.json":null,"state/openvmm.log":{"text":""},"build/fake-openvmm-state.json":{"text":"[\"--machine\",\"microvm\",\"--microvm-sandbox-block\",\"distro:file:{root_json}\\\\distro.erofs,ro\",\"--microvm-sandbox-block\",\"scratch:file:{root_json}\\\\scratch.ext4\",\"--microvm-workload-identity\",\"65534:65534\",\"--mount\",\"/data,{root_json}\\\\share,ro\",\"--microvm-lifecycle\",\"managed\",\"--single-process\",\"--hypervisor\",\"whp\",\"--memory\",\"256M\",\"--kernel\",\"{root_json}\\\\build\\\\vmlinux\",\"--initrd\",\"{root_json}\\\\build\\\\initramfs.cpio.gz\",\"--cmdline\",\"nvx_sandbox=1 nvx_layer=distro,0xd0003000,erofs:11111111-1111-1111-1111-111111111111 nvx_scratch=0xd0006000,ext4 nvx_entrypoint=/bin/sh nvx_hostname=nvx-sandbox\",\"--virtio-console\",\"none\",\"--microvm-control-console\",\"listen=//./pipe/openvmm-microvm-{hex32}\",\"--microvm-control-auth-stdin\",\"--microvm-report\",\"{root_json}\\\\state\\\\outcome.json\"]"}}},{"argv":["sandbox","exec","--state-dir","{root}\\state","--entrypoint","/bin/pwd","--cwd","/data"],"status":0,"stdout":"/data\n","stderr":""},{"argv":["sandbox","stop","--state-dir","{root}\\state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","lifecycle.lock","openvmm.log","outcome.json"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 2,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mount\": {\r\n \"access\": \"ro\",\r\n \"denied_paths\": [],\r\n \"guest_target\": \"/data\",\r\n \"host_path\": \"{root_json}\\\\share\",\r\n \"owner\": \"vmm\"\r\n },\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"whp\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 0,\n \"deny_rule_count\": 0,\n \"host_loopback\": \"none\",\n \"mode\": \"none\",\n \"status\": \"not-requested\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","start","--state-dir","{root}\\state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","lifecycle.lock","openvmm.log","runtime.json"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 2,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mount\": {\r\n \"access\": \"ro\",\r\n \"denied_paths\": [],\r\n \"guest_target\": \"/data\",\r\n \"host_path\": \"{root_json}\\\\share\",\r\n \"owner\": \"vmm\"\r\n },\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":{"text":"{\r\n \"control_endpoint\": \"//./pipe/openvmm-microvm-{hex32}\",\r\n \"format\": 1,\r\n \"pid\": {int},\r\n \"start_time\": {int}\r\n}\r\n"},"state/control.capability":{"size":32},"state/outcome.json":null,"state/openvmm.log":{"text":""}}},{"argv":["sandbox","stop","--state-dir","{root}\\state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","lifecycle.lock","openvmm.log","outcome.json"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 2,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mount\": {\r\n \"access\": \"ro\",\r\n \"denied_paths\": [],\r\n \"guest_target\": \"/data\",\r\n \"host_path\": \"{root_json}\\\\share\",\r\n \"owner\": \"vmm\"\r\n },\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"whp\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 0,\n \"deny_rule_count\": 0,\n \"host_loopback\": \"none\",\n \"mode\": \"none\",\n \"status\": \"not-requested\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","deprovision","--state-dir","{root}\\state"],"check":["state"],"status":0,"stdout":"","stderr":"","files":{"state":null}}],"interop":true}, -{"name":"lifecycle: several shares","steps":[{"argv":["sandbox","provision","--state-dir","{root}\\state","--layer","distro,{root}\\distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}\\scratch.ext4","--mount","/data,{root}\\share","--mount","/other,{root}\\share2,rw"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","lifecycle.lock"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 4,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mounts\": [\r\n {\r\n \"access\": \"ro\",\r\n \"denied_paths\": [],\r\n \"guest_target\": \"/data\",\r\n \"host_path\": \"{root_json}\\\\share\",\r\n \"owner\": \"vmm\"\r\n },\r\n {\r\n \"access\": \"rw\",\r\n \"denied_paths\": [],\r\n \"guest_target\": \"/other\",\r\n \"host_path\": \"{root_json}\\\\share2\",\r\n \"owner\": \"vmm\"\r\n }\r\n ],\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":null,"state/openvmm.log":null}},{"argv":["sandbox","start","--state-dir","{root}\\state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log","build/fake-openvmm-state.json"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","lifecycle.lock","openvmm.log","runtime.json"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 4,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mounts\": [\r\n {\r\n \"access\": \"ro\",\r\n \"denied_paths\": [],\r\n \"guest_target\": \"/data\",\r\n \"host_path\": \"{root_json}\\\\share\",\r\n \"owner\": \"vmm\"\r\n },\r\n {\r\n \"access\": \"rw\",\r\n \"denied_paths\": [],\r\n \"guest_target\": \"/other\",\r\n \"host_path\": \"{root_json}\\\\share2\",\r\n \"owner\": \"vmm\"\r\n }\r\n ],\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":{"text":"{\r\n \"control_endpoint\": \"//./pipe/openvmm-microvm-{hex32}\",\r\n \"format\": 1,\r\n \"pid\": {int},\r\n \"start_time\": {int}\r\n}\r\n"},"state/control.capability":{"size":32},"state/outcome.json":null,"state/openvmm.log":{"text":""},"build/fake-openvmm-state.json":{"text":"[\"--machine\",\"microvm\",\"--microvm-sandbox-block\",\"distro:file:{root_json}\\\\distro.erofs,ro\",\"--microvm-sandbox-block\",\"scratch:file:{root_json}\\\\scratch.ext4\",\"--microvm-workload-identity\",\"65534:65534\",\"--mount-aggregate\",\"/run/nvx/shares\",\"--mount-child\",\"0-bd47413b5c03dfc6e4b5b8143fe205ea,{root_json}\\\\share,ro\",\"--mount-child\",\"1-bf2faee2596026620b5cbc997c2d7d45,{root_json}\\\\share2,rw\",\"--microvm-lifecycle\",\"managed\",\"--single-process\",\"--hypervisor\",\"whp\",\"--memory\",\"256M\",\"--kernel\",\"{root_json}\\\\build\\\\vmlinux\",\"--initrd\",\"{root_json}\\\\build\\\\initramfs.cpio.gz\",\"--cmdline\",\"nvx_sandbox=1 nvx_layer=distro,0xd0003000,erofs:11111111-1111-1111-1111-111111111111 nvx_scratch=0xd0006000,ext4 nvx_entrypoint=/bin/sh nvx_hostname=nvx-sandbox nvx_share=0-bd47413b5c03dfc6e4b5b8143fe205ea,/data,ro nvx_share=1-bf2faee2596026620b5cbc997c2d7d45,/other,rw\",\"--virtio-console\",\"none\",\"--microvm-control-console\",\"listen=//./pipe/openvmm-microvm-{hex32}\",\"--microvm-control-auth-stdin\",\"--microvm-report\",\"{root_json}\\\\state\\\\outcome.json\"]"}}},{"argv":["sandbox","exec","--state-dir","{root}\\state","--entrypoint","/bin/pwd","--cwd","/other"],"status":0,"stdout":"/other\n","stderr":""},{"argv":["sandbox","stop","--state-dir","{root}\\state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","lifecycle.lock","openvmm.log","outcome.json"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 4,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mounts\": [\r\n {\r\n \"access\": \"ro\",\r\n \"denied_paths\": [],\r\n \"guest_target\": \"/data\",\r\n \"host_path\": \"{root_json}\\\\share\",\r\n \"owner\": \"vmm\"\r\n },\r\n {\r\n \"access\": \"rw\",\r\n \"denied_paths\": [],\r\n \"guest_target\": \"/other\",\r\n \"host_path\": \"{root_json}\\\\share2\",\r\n \"owner\": \"vmm\"\r\n }\r\n ],\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"whp\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 0,\n \"deny_rule_count\": 0,\n \"host_loopback\": \"none\",\n \"mode\": \"none\",\n \"status\": \"not-requested\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","start","--state-dir","{root}\\state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","lifecycle.lock","openvmm.log","runtime.json"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 4,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mounts\": [\r\n {\r\n \"access\": \"ro\",\r\n \"denied_paths\": [],\r\n \"guest_target\": \"/data\",\r\n \"host_path\": \"{root_json}\\\\share\",\r\n \"owner\": \"vmm\"\r\n },\r\n {\r\n \"access\": \"rw\",\r\n \"denied_paths\": [],\r\n \"guest_target\": \"/other\",\r\n \"host_path\": \"{root_json}\\\\share2\",\r\n \"owner\": \"vmm\"\r\n }\r\n ],\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":{"text":"{\r\n \"control_endpoint\": \"//./pipe/openvmm-microvm-{hex32}\",\r\n \"format\": 1,\r\n \"pid\": {int},\r\n \"start_time\": {int}\r\n}\r\n"},"state/control.capability":{"size":32},"state/outcome.json":null,"state/openvmm.log":{"text":""}}},{"argv":["sandbox","stop","--state-dir","{root}\\state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","lifecycle.lock","openvmm.log","outcome.json"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 4,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mounts\": [\r\n {\r\n \"access\": \"ro\",\r\n \"denied_paths\": [],\r\n \"guest_target\": \"/data\",\r\n \"host_path\": \"{root_json}\\\\share\",\r\n \"owner\": \"vmm\"\r\n },\r\n {\r\n \"access\": \"rw\",\r\n \"denied_paths\": [],\r\n \"guest_target\": \"/other\",\r\n \"host_path\": \"{root_json}\\\\share2\",\r\n \"owner\": \"vmm\"\r\n }\r\n ],\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"whp\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 0,\n \"deny_rule_count\": 0,\n \"host_loopback\": \"none\",\n \"mode\": \"none\",\n \"status\": \"not-requested\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","deprovision","--state-dir","{root}\\state"],"check":["state"],"status":0,"stdout":"","stderr":"","files":{"state":null}}],"interop":true}, -{"name":"lifecycle: share policies","steps":[{"argv":["sandbox","provision","--state-dir","{root}\\state","--layer","distro,{root}\\distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}\\scratch.ext4","--mount","/data,{root}\\share,rw","--mount-deny","{root}\\share\\hidden","--mount-allow","{root}\\share\\hidden\\shown","--mount-write","{root}\\share\\writable"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","lifecycle.lock"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 5,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mounts\": [\r\n {\r\n \"access\": \"rw\",\r\n \"allowed_paths\": [\r\n \"{root_json}\\\\share\\\\hidden\\\\shown\"\r\n ],\r\n \"denied_paths\": [\r\n \"{root_json}\\\\share\\\\hidden\"\r\n ],\r\n \"guest_target\": \"/data\",\r\n \"host_path\": \"{root_json}\\\\share\",\r\n \"owner\": \"vmm\",\r\n \"writable_paths\": [\r\n \"{root_json}\\\\share\\\\writable\"\r\n ]\r\n }\r\n ],\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":null,"state/openvmm.log":null}},{"argv":["sandbox","start","--state-dir","{root}\\state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log","build/fake-openvmm-state.json"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","lifecycle.lock","openvmm.log","runtime.json"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 5,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mounts\": [\r\n {\r\n \"access\": \"rw\",\r\n \"allowed_paths\": [\r\n \"{root_json}\\\\share\\\\hidden\\\\shown\"\r\n ],\r\n \"denied_paths\": [\r\n \"{root_json}\\\\share\\\\hidden\"\r\n ],\r\n \"guest_target\": \"/data\",\r\n \"host_path\": \"{root_json}\\\\share\",\r\n \"owner\": \"vmm\",\r\n \"writable_paths\": [\r\n \"{root_json}\\\\share\\\\writable\"\r\n ]\r\n }\r\n ],\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":{"text":"{\r\n \"control_endpoint\": \"//./pipe/openvmm-microvm-{hex32}\",\r\n \"format\": 1,\r\n \"pid\": {int},\r\n \"start_time\": {int}\r\n}\r\n"},"state/control.capability":{"size":32},"state/outcome.json":null,"state/openvmm.log":{"text":""},"build/fake-openvmm-state.json":{"text":"[\"--machine\",\"microvm\",\"--microvm-sandbox-block\",\"distro:file:{root_json}\\\\distro.erofs,ro\",\"--microvm-sandbox-block\",\"scratch:file:{root_json}\\\\scratch.ext4\",\"--microvm-workload-identity\",\"65534:65534\",\"--mount\",\"/data,{root_json}\\\\share,rw\",\"--mount-deny\",\"{root_json}\\\\share\\\\hidden\",\"--mount-allow\",\"{root_json}\\\\share\\\\hidden\\\\shown\",\"--mount-write\",\"{root_json}\\\\share\\\\writable\",\"--microvm-lifecycle\",\"managed\",\"--single-process\",\"--hypervisor\",\"whp\",\"--memory\",\"256M\",\"--kernel\",\"{root_json}\\\\build\\\\vmlinux\",\"--initrd\",\"{root_json}\\\\build\\\\initramfs.cpio.gz\",\"--cmdline\",\"nvx_sandbox=1 nvx_layer=distro,0xd0003000,erofs:11111111-1111-1111-1111-111111111111 nvx_scratch=0xd0006000,ext4 nvx_entrypoint=/bin/sh nvx_hostname=nvx-sandbox\",\"--virtio-console\",\"none\",\"--microvm-control-console\",\"listen=//./pipe/openvmm-microvm-{hex32}\",\"--microvm-control-auth-stdin\",\"--microvm-report\",\"{root_json}\\\\state\\\\outcome.json\"]"}}},{"argv":["sandbox","stop","--state-dir","{root}\\state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","lifecycle.lock","openvmm.log","outcome.json"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 5,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mounts\": [\r\n {\r\n \"access\": \"rw\",\r\n \"allowed_paths\": [\r\n \"{root_json}\\\\share\\\\hidden\\\\shown\"\r\n ],\r\n \"denied_paths\": [\r\n \"{root_json}\\\\share\\\\hidden\"\r\n ],\r\n \"guest_target\": \"/data\",\r\n \"host_path\": \"{root_json}\\\\share\",\r\n \"owner\": \"vmm\",\r\n \"writable_paths\": [\r\n \"{root_json}\\\\share\\\\writable\"\r\n ]\r\n }\r\n ],\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"whp\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 0,\n \"deny_rule_count\": 0,\n \"host_loopback\": \"none\",\n \"mode\": \"none\",\n \"status\": \"not-requested\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","start","--state-dir","{root}\\state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","lifecycle.lock","openvmm.log","runtime.json"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 5,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mounts\": [\r\n {\r\n \"access\": \"rw\",\r\n \"allowed_paths\": [\r\n \"{root_json}\\\\share\\\\hidden\\\\shown\"\r\n ],\r\n \"denied_paths\": [\r\n \"{root_json}\\\\share\\\\hidden\"\r\n ],\r\n \"guest_target\": \"/data\",\r\n \"host_path\": \"{root_json}\\\\share\",\r\n \"owner\": \"vmm\",\r\n \"writable_paths\": [\r\n \"{root_json}\\\\share\\\\writable\"\r\n ]\r\n }\r\n ],\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":{"text":"{\r\n \"control_endpoint\": \"//./pipe/openvmm-microvm-{hex32}\",\r\n \"format\": 1,\r\n \"pid\": {int},\r\n \"start_time\": {int}\r\n}\r\n"},"state/control.capability":{"size":32},"state/outcome.json":null,"state/openvmm.log":{"text":""}}},{"argv":["sandbox","stop","--state-dir","{root}\\state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","lifecycle.lock","openvmm.log","outcome.json"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 5,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mounts\": [\r\n {\r\n \"access\": \"rw\",\r\n \"allowed_paths\": [\r\n \"{root_json}\\\\share\\\\hidden\\\\shown\"\r\n ],\r\n \"denied_paths\": [\r\n \"{root_json}\\\\share\\\\hidden\"\r\n ],\r\n \"guest_target\": \"/data\",\r\n \"host_path\": \"{root_json}\\\\share\",\r\n \"owner\": \"vmm\",\r\n \"writable_paths\": [\r\n \"{root_json}\\\\share\\\\writable\"\r\n ]\r\n }\r\n ],\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"whp\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 0,\n \"deny_rule_count\": 0,\n \"host_loopback\": \"none\",\n \"mode\": \"none\",\n \"status\": \"not-requested\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","deprovision","--state-dir","{root}\\state"],"check":["state"],"status":0,"stdout":"","stderr":"","files":{"state":null}}],"interop":true}, -{"name":"lifecycle: file share","steps":[{"argv":["sandbox","provision","--state-dir","{root}\\state","--layer","distro,{root}\\distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}\\scratch.ext4","--mount","/etc/single.txt,{root}\\single.txt,ro"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","lifecycle.lock"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 6,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mounts\": [\r\n {\r\n \"access\": \"ro\",\r\n \"allowed_paths\": [],\r\n \"denied_paths\": [],\r\n \"guest_target\": \"/etc/single.txt\",\r\n \"host_path\": \"{root_json}\\\\single.txt\",\r\n \"kind\": \"file\",\r\n \"owner\": \"vmm\",\r\n \"writable_paths\": []\r\n }\r\n ],\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":null,"state/openvmm.log":null}},{"argv":["sandbox","start","--state-dir","{root}\\state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log","build/fake-openvmm-state.json"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","lifecycle.lock","openvmm.log","runtime.json"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 6,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mounts\": [\r\n {\r\n \"access\": \"ro\",\r\n \"allowed_paths\": [],\r\n \"denied_paths\": [],\r\n \"guest_target\": \"/etc/single.txt\",\r\n \"host_path\": \"{root_json}\\\\single.txt\",\r\n \"kind\": \"file\",\r\n \"owner\": \"vmm\",\r\n \"writable_paths\": []\r\n }\r\n ],\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":{"text":"{\r\n \"control_endpoint\": \"//./pipe/openvmm-microvm-{hex32}\",\r\n \"format\": 1,\r\n \"pid\": {int},\r\n \"start_time\": {int}\r\n}\r\n"},"state/control.capability":{"size":32},"state/outcome.json":null,"state/openvmm.log":{"text":""},"build/fake-openvmm-state.json":{"text":"[\"--machine\",\"microvm\",\"--microvm-sandbox-block\",\"distro:file:{root_json}\\\\distro.erofs,ro\",\"--microvm-sandbox-block\",\"scratch:file:{root_json}\\\\scratch.ext4\",\"--microvm-workload-identity\",\"65534:65534\",\"--mount-aggregate\",\"/run/nvx/shares\",\"--mount-child\",\"0-a80a05e9cb129bc27b7ba103bf2f5ac0,{root_json}\\\\single.txt,ro,file\",\"--microvm-lifecycle\",\"managed\",\"--single-process\",\"--hypervisor\",\"whp\",\"--memory\",\"256M\",\"--kernel\",\"{root_json}\\\\build\\\\vmlinux\",\"--initrd\",\"{root_json}\\\\build\\\\initramfs.cpio.gz\",\"--cmdline\",\"nvx_sandbox=1 nvx_layer=distro,0xd0003000,erofs:11111111-1111-1111-1111-111111111111 nvx_scratch=0xd0006000,ext4 nvx_entrypoint=/bin/sh nvx_hostname=nvx-sandbox nvx_share=0-a80a05e9cb129bc27b7ba103bf2f5ac0,/etc/single.txt,ro,file\",\"--virtio-console\",\"none\",\"--microvm-control-console\",\"listen=//./pipe/openvmm-microvm-{hex32}\",\"--microvm-control-auth-stdin\",\"--microvm-report\",\"{root_json}\\\\state\\\\outcome.json\"]"}}},{"argv":["sandbox","stop","--state-dir","{root}\\state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","lifecycle.lock","openvmm.log","outcome.json"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 6,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mounts\": [\r\n {\r\n \"access\": \"ro\",\r\n \"allowed_paths\": [],\r\n \"denied_paths\": [],\r\n \"guest_target\": \"/etc/single.txt\",\r\n \"host_path\": \"{root_json}\\\\single.txt\",\r\n \"kind\": \"file\",\r\n \"owner\": \"vmm\",\r\n \"writable_paths\": []\r\n }\r\n ],\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"whp\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 0,\n \"deny_rule_count\": 0,\n \"host_loopback\": \"none\",\n \"mode\": \"none\",\n \"status\": \"not-requested\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","start","--state-dir","{root}\\state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","lifecycle.lock","openvmm.log","runtime.json"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 6,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mounts\": [\r\n {\r\n \"access\": \"ro\",\r\n \"allowed_paths\": [],\r\n \"denied_paths\": [],\r\n \"guest_target\": \"/etc/single.txt\",\r\n \"host_path\": \"{root_json}\\\\single.txt\",\r\n \"kind\": \"file\",\r\n \"owner\": \"vmm\",\r\n \"writable_paths\": []\r\n }\r\n ],\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":{"text":"{\r\n \"control_endpoint\": \"//./pipe/openvmm-microvm-{hex32}\",\r\n \"format\": 1,\r\n \"pid\": {int},\r\n \"start_time\": {int}\r\n}\r\n"},"state/control.capability":{"size":32},"state/outcome.json":null,"state/openvmm.log":{"text":""}}},{"argv":["sandbox","stop","--state-dir","{root}\\state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","lifecycle.lock","openvmm.log","outcome.json"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 6,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mounts\": [\r\n {\r\n \"access\": \"ro\",\r\n \"allowed_paths\": [],\r\n \"denied_paths\": [],\r\n \"guest_target\": \"/etc/single.txt\",\r\n \"host_path\": \"{root_json}\\\\single.txt\",\r\n \"kind\": \"file\",\r\n \"owner\": \"vmm\",\r\n \"writable_paths\": []\r\n }\r\n ],\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"whp\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 0,\n \"deny_rule_count\": 0,\n \"host_loopback\": \"none\",\n \"mode\": \"none\",\n \"status\": \"not-requested\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","deprovision","--state-dir","{root}\\state"],"check":["state"],"status":0,"stdout":"","stderr":"","files":{"state":null}}],"interop":true}, -{"name":"lifecycle: network and launch options","steps":[{"argv":["sandbox","provision","--state-dir","{root}\\state","--layer","distro,{root}\\distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}\\scratch.ext4","--entrypoint","/bin/server","--arg=--serve","--hostname","worker","--workload-user","1000:1000","--memory-max","268435456","--pids-max","64","--memory-mib","384","--hypervisor","auto","--cmdline","quiet","--net","10.0.0.2/24","--network-profile","portable","--network-egress","deny","--network-ingress","deny","--network-egress-policy-file","{root}\\policy.json","--host-loopback","allow","--network-proxy","10.0.0.1:3128","--host-loopback-forward","8080","--host-loopback-forward","9090"],"write":{"policy.json":"{\"allow\": [{\"cidr\": \"192.0.2.0/24\", \"protocol\": \"tcp\", \"port\": 443}], \"deny\": [{\"cidr\": \"192.0.2.128/25\"}]}"},"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","lifecycle.lock"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"quiet\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 1,\r\n \"host_loopback\": \"allow\",\r\n \"host_loopback_forward\": [\r\n \"8080\",\r\n \"9090\"\r\n ],\r\n \"hostname\": \"worker\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": 268435456,\r\n \"memory_mib\": 384,\r\n \"mount\": null,\r\n \"net\": \"10.0.0.2/24\",\r\n \"network_egress\": \"deny\",\r\n \"network_egress_allow\": [\r\n \"192.0.2.0/24:tcp:443\"\r\n ],\r\n \"network_egress_deny\": [\r\n \"192.0.2.128/25\"\r\n ],\r\n \"network_ingress\": \"deny\",\r\n \"network_profile\": \"portable\",\r\n \"network_proxy\": \"10.0.0.1:3128\",\r\n \"pids_max\": 64,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 1000,\r\n \"workload_uid\": 1000\r\n}\r\n"},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":null,"state/openvmm.log":null}},{"argv":["sandbox","start","--state-dir","{root}\\state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log","build/fake-openvmm-state.json"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","lifecycle.lock","openvmm.log","runtime.json"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"quiet\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 1,\r\n \"host_loopback\": \"allow\",\r\n \"host_loopback_forward\": [\r\n \"8080\",\r\n \"9090\"\r\n ],\r\n \"hostname\": \"worker\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": 268435456,\r\n \"memory_mib\": 384,\r\n \"mount\": null,\r\n \"net\": \"10.0.0.2/24\",\r\n \"network_egress\": \"deny\",\r\n \"network_egress_allow\": [\r\n \"192.0.2.0/24:tcp:443\"\r\n ],\r\n \"network_egress_deny\": [\r\n \"192.0.2.128/25\"\r\n ],\r\n \"network_ingress\": \"deny\",\r\n \"network_profile\": \"portable\",\r\n \"network_proxy\": \"10.0.0.1:3128\",\r\n \"pids_max\": 64,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 1000,\r\n \"workload_uid\": 1000\r\n}\r\n"},"state/runtime.json":{"text":"{\r\n \"control_endpoint\": \"//./pipe/openvmm-microvm-{hex32}\",\r\n \"format\": 1,\r\n \"pid\": {int},\r\n \"start_time\": {int}\r\n}\r\n"},"state/control.capability":{"size":32},"state/outcome.json":null,"state/openvmm.log":{"text":""},"build/fake-openvmm-state.json":{"text":"[\"--machine\",\"microvm\",\"--microvm-sandbox-block\",\"distro:file:{root_json}\\\\distro.erofs,ro\",\"--microvm-sandbox-block\",\"scratch:file:{root_json}\\\\scratch.ext4\",\"--microvm-workload-identity\",\"1000:1000\",\"--microvm-lifecycle\",\"managed\",\"--single-process\",\"--hypervisor\",\"whp\",\"--memory\",\"384M\",\"--kernel\",\"{root_json}\\\\build\\\\vmlinux\",\"--initrd\",\"{root_json}\\\\build\\\\initramfs.cpio.gz\",\"--cmdline\",\"quiet nvx_sandbox=1 nvx_layer=distro,0xd0003000,erofs:11111111-1111-1111-1111-111111111111 nvx_scratch=0xd0006000,ext4 nvx_entrypoint=/bin/sh nvx_hostname=worker nvx_memory_max=268435456 nvx_pids_max=65\",\"--virtio-console\",\"none\",\"--microvm-control-console\",\"listen=//./pipe/openvmm-microvm-{hex32}\",\"--microvm-control-auth-stdin\",\"--microvm-report\",\"{root_json}\\\\state\\\\outcome.json\",\"--net\",\"10.0.0.2/24\",\"--network-profile\",\"portable\",\"--network-egress\",\"deny\",\"--network-ingress\",\"deny\",\"--host-loopback\",\"allow\",\"--network-egress-allow\",\"192.0.2.0/24:tcp:443\",\"--network-egress-deny\",\"192.0.2.128/25\",\"--host-loopback-forward\",\"8080\",\"--host-loopback-forward\",\"9090\",\"--network-proxy\",\"10.0.0.1:3128\"]"}}},{"argv":["sandbox","stop","--state-dir","{root}\\state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","lifecycle.lock","openvmm.log","outcome.json"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"quiet\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 1,\r\n \"host_loopback\": \"allow\",\r\n \"host_loopback_forward\": [\r\n \"8080\",\r\n \"9090\"\r\n ],\r\n \"hostname\": \"worker\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": 268435456,\r\n \"memory_mib\": 384,\r\n \"mount\": null,\r\n \"net\": \"10.0.0.2/24\",\r\n \"network_egress\": \"deny\",\r\n \"network_egress_allow\": [\r\n \"192.0.2.0/24:tcp:443\"\r\n ],\r\n \"network_egress_deny\": [\r\n \"192.0.2.128/25\"\r\n ],\r\n \"network_ingress\": \"deny\",\r\n \"network_profile\": \"portable\",\r\n \"network_proxy\": \"10.0.0.1:3128\",\r\n \"pids_max\": 64,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 1000,\r\n \"workload_uid\": 1000\r\n}\r\n"},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"whp\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 1,\n \"deny_rule_count\": 1,\n \"host_loopback\": \"allow\",\n \"mode\": \"rules\",\n \"status\": \"applied\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","start","--state-dir","{root}\\state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","lifecycle.lock","openvmm.log","runtime.json"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"quiet\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 1,\r\n \"host_loopback\": \"allow\",\r\n \"host_loopback_forward\": [\r\n \"8080\",\r\n \"9090\"\r\n ],\r\n \"hostname\": \"worker\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": 268435456,\r\n \"memory_mib\": 384,\r\n \"mount\": null,\r\n \"net\": \"10.0.0.2/24\",\r\n \"network_egress\": \"deny\",\r\n \"network_egress_allow\": [\r\n \"192.0.2.0/24:tcp:443\"\r\n ],\r\n \"network_egress_deny\": [\r\n \"192.0.2.128/25\"\r\n ],\r\n \"network_ingress\": \"deny\",\r\n \"network_profile\": \"portable\",\r\n \"network_proxy\": \"10.0.0.1:3128\",\r\n \"pids_max\": 64,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 1000,\r\n \"workload_uid\": 1000\r\n}\r\n"},"state/runtime.json":{"text":"{\r\n \"control_endpoint\": \"//./pipe/openvmm-microvm-{hex32}\",\r\n \"format\": 1,\r\n \"pid\": {int},\r\n \"start_time\": {int}\r\n}\r\n"},"state/control.capability":{"size":32},"state/outcome.json":null,"state/openvmm.log":{"text":""}}},{"argv":["sandbox","stop","--state-dir","{root}\\state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","lifecycle.lock","openvmm.log","outcome.json"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"quiet\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 1,\r\n \"host_loopback\": \"allow\",\r\n \"host_loopback_forward\": [\r\n \"8080\",\r\n \"9090\"\r\n ],\r\n \"hostname\": \"worker\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": 268435456,\r\n \"memory_mib\": 384,\r\n \"mount\": null,\r\n \"net\": \"10.0.0.2/24\",\r\n \"network_egress\": \"deny\",\r\n \"network_egress_allow\": [\r\n \"192.0.2.0/24:tcp:443\"\r\n ],\r\n \"network_egress_deny\": [\r\n \"192.0.2.128/25\"\r\n ],\r\n \"network_ingress\": \"deny\",\r\n \"network_profile\": \"portable\",\r\n \"network_proxy\": \"10.0.0.1:3128\",\r\n \"pids_max\": 64,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 1000,\r\n \"workload_uid\": 1000\r\n}\r\n"},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"whp\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 1,\n \"deny_rule_count\": 1,\n \"host_loopback\": \"allow\",\n \"mode\": \"rules\",\n \"status\": \"applied\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","deprovision","--state-dir","{root}\\state"],"check":["state"],"status":0,"stdout":"","stderr":"","files":{"state":null}}],"interop":true}, +{"name":"lifecycle: minimal","steps":[{"argv":["sandbox","provision","--state-dir","{root}\\state","--layer","distro,{root}\\distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}\\scratch.ext4"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","sandbox.lock"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 1,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mount\": null,\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":null,"state/openvmm.log":null}},{"argv":["sandbox","start","--state-dir","{root}\\state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log","build/fake-openvmm-state.json"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","openvmm.log","runtime.json","sandbox.lock"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 1,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mount\": null,\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":{"text":"{\n \"format\": 4,\n \"launch\": \"{hex32}\",\n \"process\": {\n \"pid\": {int},\n \"startTime\": {int},\n \"boot\": \"{hex32}\"\n },\n \"endpoint\": \"//./pipe/openvmm-microvm-{hex32}\",\n \"runtime\": {\n \"openvmm\": \"sha256:1e8c924cdaac227bf74b4a4a9bcdb9e8836a1355fd0408cc81dab5d9aa71eb4d\",\n \"kernel\": \"sha256:a0c936696eb7d5ee3192bf53b9d281cecbb40ca9db520de72cb95817ad92ac72\",\n \"initramfs\": \"sha256:2f77c2c97621e8bf69794e9d12815bb7c46338638f126bea8b18dfe02a684d62\",\n \"bootContract\": 2,\n \"controlContract\": \"nvx-microvm-v2-control-v2\",\n \"controlSession\": 1\n }\n}\n"},"state/control.capability":{"size":32},"state/outcome.json":null,"state/openvmm.log":{"text":""},"build/fake-openvmm-state.json":{"text":"[\"--machine\",\"microvm\",\"--microvm-sandbox-block\",\"distro:file:{root_json}\\\\distro.erofs,ro\",\"--microvm-sandbox-block\",\"scratch:file:{root_json}\\\\scratch.ext4\",\"--microvm-workload-identity\",\"65534:65534\",\"--microvm-lifecycle\",\"managed\",\"--single-process\",\"--hypervisor\",\"whp\",\"--memory\",\"256M\",\"--kernel\",\"{root_json}\\\\build\\\\vmlinux\",\"--initrd\",\"{root_json}\\\\build\\\\initramfs.cpio.gz\",\"--cmdline\",\"nvx_sandbox=1 nvx_layer=distro,0xd0003000,erofs:11111111-1111-1111-1111-111111111111 nvx_scratch=0xd0006000,ext4 nvx_entrypoint=/bin/sh nvx_hostname=nvx-sandbox\",\"--virtio-console\",\"none\",\"--microvm-control-console\",\"listen=//./pipe/openvmm-microvm-{hex32}\",\"--microvm-control-auth-stdin\",\"--microvm-report\",\"{root_json}\\\\state\\\\outcome.json\"]"}}},{"argv":["sandbox","exec","--state-dir","{root}\\state","--entrypoint","/bin/echo","--arg","hello","--arg","world"],"status":0,"stdout":"hello world\n","stderr":""},{"argv":["sandbox","exec","--state-dir","{root}\\state","--entrypoint","/bin/pwd","--cwd","/tmp"],"status":0,"stdout":"/tmp\n","stderr":""},{"argv":["sandbox","exec","--state-dir","{root}\\state","--entrypoint","/bin/sh","--arg=-c","--arg","echo out; echoerr err; exit 3","--outcome-report","{root}\\reports\\exec.json"],"check":["reports/exec.json"],"status":3,"stdout":"out\n","stderr":"err\n","files":{"reports/exec.json":{"text":"{\n \"operation_id\": \"{hex32}\",\n \"outcome\": {\n \"category\": \"exit\",\n \"operation\": \"exec\",\n \"status_code\": 3\n },\n \"schema_version\": 1\n}\n"}}},{"argv":["sandbox","exec","--state-dir","{root}\\state","--entrypoint","/usr/bin/env","--environment","A=1","--environment","B=two"],"status":0,"stdout":"A=1\nB=two\n","stderr":""},{"argv":["sandbox","exec","--state-dir","{root}\\state","--entrypoint","/usr/bin/printenv","--arg","PATH","--inherit-default-environment","--environment","A=1"],"status":0,"stdout":"/usr/sbin:/usr/bin:/sbin:/bin\n","stderr":""},{"argv":["sandbox","exec","--state-dir","{root}\\state","--entrypoint","/usr/bin/env","--environment-file","{root}\\env.json"],"write":{"env.json":"[\"X=1\", \"Y=two words\"]"},"status":0,"stdout":"X=1\nY=two words\n","stderr":""},{"argv":["sandbox","exec","--state-dir","{root}\\state","--entrypoint","/usr/bin/env","--environment-file","{root}\\empty.json"],"write":{"empty.json":"[]"},"status":0,"stdout":"","stderr":""},{"argv":["sandbox","exec","--state-dir","{root}\\state","--entrypoint","/bin/sh","--arg=-c","--arg","sleep 5000","--exec-timeout-ms","50","--outcome-report","{root}\\reports\\exec-timeout.json"],"check":["reports/exec-timeout.json"],"status":124,"stdout":"","stderr":"","files":{"reports/exec-timeout.json":{"text":"{\n \"operation_id\": \"{hex32}\",\n \"outcome\": {\n \"category\": \"timeout\",\n \"operation\": \"exec\",\n \"status_code\": 124\n },\n \"schema_version\": 1\n}\n"}}},{"argv":["sandbox","exec","--state-dir","{root}\\state","--entrypoint","/bin/sh","--arg=-c","--arg","signal 9"],"status":137,"stdout":"","stderr":""},{"argv":["sandbox","exec","--state-dir","{root}\\state","--entrypoint","/bin/missing"],"status":127,"stdout":"","stderr":"openvmm: /bin/missing: not found\n"},{"argv":["sandbox","exec","--state-dir","{root}\\state","--entrypoint","/bin/pwd","--cwd","/missing"],"status":1,"stdout":"","stderr":"error: managed guest rejected exec (status=2, category=cwd-failed)\r\n"},{"argv":["sandbox","exec","--state-dir","{root}\\state","--entrypoint","/bin/pwd","--cwd","relative"],"status":1,"stdout":"","stderr":"error: managed exec working directory must be an absolute path of at most 4096 bytes\r\n"},{"argv":["sandbox","exec","--state-dir","{root}\\state","--entrypoint","/bin/echo","--outcome-report","{root}\\reports\\exec.json"],"status":1,"stdout":"","stderr":"error: outcome report already exists: {root}\\reports\\exec.json\r\n"},{"argv":["sandbox","exec","--state-dir","{root}\\state","--entrypoint","/bin/echo","--outcome-report","{root}\\missing\\exec.json"],"status":1,"stdout":"","stderr":"error: outcome report parent is not a plain directory: {root}\\missing\r\n"},{"argv":["sandbox","stop","--state-dir","{root}\\state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","openvmm.log","outcome.json","sandbox.lock"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 1,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mount\": null,\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"whp\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 0,\n \"deny_rule_count\": 0,\n \"host_loopback\": \"none\",\n \"mode\": \"none\",\n \"status\": \"not-requested\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","start","--state-dir","{root}\\state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","openvmm.log","runtime.json","sandbox.lock"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 1,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mount\": null,\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":{"text":"{\n \"format\": 4,\n \"launch\": \"{hex32}\",\n \"process\": {\n \"pid\": {int},\n \"startTime\": {int},\n \"boot\": \"{hex32}\"\n },\n \"endpoint\": \"//./pipe/openvmm-microvm-{hex32}\",\n \"runtime\": {\n \"openvmm\": \"sha256:1e8c924cdaac227bf74b4a4a9bcdb9e8836a1355fd0408cc81dab5d9aa71eb4d\",\n \"kernel\": \"sha256:a0c936696eb7d5ee3192bf53b9d281cecbb40ca9db520de72cb95817ad92ac72\",\n \"initramfs\": \"sha256:2f77c2c97621e8bf69794e9d12815bb7c46338638f126bea8b18dfe02a684d62\",\n \"bootContract\": 2,\n \"controlContract\": \"nvx-microvm-v2-control-v2\",\n \"controlSession\": 1\n }\n}\n"},"state/control.capability":{"size":32},"state/outcome.json":null,"state/openvmm.log":{"text":""}}},{"argv":["sandbox","stop","--state-dir","{root}\\state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","openvmm.log","outcome.json","sandbox.lock"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 1,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mount\": null,\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"whp\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 0,\n \"deny_rule_count\": 0,\n \"host_loopback\": \"none\",\n \"mode\": \"none\",\n \"status\": \"not-requested\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","deprovision","--state-dir","{root}\\state"],"check":["state"],"status":0,"stdout":"","stderr":"","files":{"state":null}}],"interop":true}, +{"name":"lifecycle: one share","steps":[{"argv":["sandbox","provision","--state-dir","{root}\\state","--layer","distro,{root}\\distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}\\scratch.ext4","--mount","/data,{root}\\share"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","sandbox.lock"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 2,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mount\": {\r\n \"access\": \"ro\",\r\n \"denied_paths\": [],\r\n \"guest_target\": \"/data\",\r\n \"host_path\": \"{root_json}\\\\share\",\r\n \"owner\": \"vmm\"\r\n },\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":null,"state/openvmm.log":null}},{"argv":["sandbox","start","--state-dir","{root}\\state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log","build/fake-openvmm-state.json"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","openvmm.log","runtime.json","sandbox.lock"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 2,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mount\": {\r\n \"access\": \"ro\",\r\n \"denied_paths\": [],\r\n \"guest_target\": \"/data\",\r\n \"host_path\": \"{root_json}\\\\share\",\r\n \"owner\": \"vmm\"\r\n },\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":{"text":"{\n \"format\": 4,\n \"launch\": \"{hex32}\",\n \"process\": {\n \"pid\": {int},\n \"startTime\": {int},\n \"boot\": \"{hex32}\"\n },\n \"endpoint\": \"//./pipe/openvmm-microvm-{hex32}\",\n \"runtime\": {\n \"openvmm\": \"sha256:1e8c924cdaac227bf74b4a4a9bcdb9e8836a1355fd0408cc81dab5d9aa71eb4d\",\n \"kernel\": \"sha256:a0c936696eb7d5ee3192bf53b9d281cecbb40ca9db520de72cb95817ad92ac72\",\n \"initramfs\": \"sha256:2f77c2c97621e8bf69794e9d12815bb7c46338638f126bea8b18dfe02a684d62\",\n \"bootContract\": 2,\n \"controlContract\": \"nvx-microvm-v2-control-v2\",\n \"controlSession\": 1\n }\n}\n"},"state/control.capability":{"size":32},"state/outcome.json":null,"state/openvmm.log":{"text":""},"build/fake-openvmm-state.json":{"text":"[\"--machine\",\"microvm\",\"--microvm-sandbox-block\",\"distro:file:{root_json}\\\\distro.erofs,ro\",\"--microvm-sandbox-block\",\"scratch:file:{root_json}\\\\scratch.ext4\",\"--microvm-workload-identity\",\"65534:65534\",\"--mount\",\"/data,{root_json}\\\\share,ro\",\"--microvm-lifecycle\",\"managed\",\"--single-process\",\"--hypervisor\",\"whp\",\"--memory\",\"256M\",\"--kernel\",\"{root_json}\\\\build\\\\vmlinux\",\"--initrd\",\"{root_json}\\\\build\\\\initramfs.cpio.gz\",\"--cmdline\",\"nvx_sandbox=1 nvx_layer=distro,0xd0003000,erofs:11111111-1111-1111-1111-111111111111 nvx_scratch=0xd0006000,ext4 nvx_entrypoint=/bin/sh nvx_hostname=nvx-sandbox\",\"--virtio-console\",\"none\",\"--microvm-control-console\",\"listen=//./pipe/openvmm-microvm-{hex32}\",\"--microvm-control-auth-stdin\",\"--microvm-report\",\"{root_json}\\\\state\\\\outcome.json\"]"}}},{"argv":["sandbox","exec","--state-dir","{root}\\state","--entrypoint","/bin/pwd","--cwd","/data"],"status":0,"stdout":"/data\n","stderr":""},{"argv":["sandbox","stop","--state-dir","{root}\\state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","openvmm.log","outcome.json","sandbox.lock"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 2,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mount\": {\r\n \"access\": \"ro\",\r\n \"denied_paths\": [],\r\n \"guest_target\": \"/data\",\r\n \"host_path\": \"{root_json}\\\\share\",\r\n \"owner\": \"vmm\"\r\n },\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"whp\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 0,\n \"deny_rule_count\": 0,\n \"host_loopback\": \"none\",\n \"mode\": \"none\",\n \"status\": \"not-requested\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","start","--state-dir","{root}\\state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","openvmm.log","runtime.json","sandbox.lock"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 2,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mount\": {\r\n \"access\": \"ro\",\r\n \"denied_paths\": [],\r\n \"guest_target\": \"/data\",\r\n \"host_path\": \"{root_json}\\\\share\",\r\n \"owner\": \"vmm\"\r\n },\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":{"text":"{\n \"format\": 4,\n \"launch\": \"{hex32}\",\n \"process\": {\n \"pid\": {int},\n \"startTime\": {int},\n \"boot\": \"{hex32}\"\n },\n \"endpoint\": \"//./pipe/openvmm-microvm-{hex32}\",\n \"runtime\": {\n \"openvmm\": \"sha256:1e8c924cdaac227bf74b4a4a9bcdb9e8836a1355fd0408cc81dab5d9aa71eb4d\",\n \"kernel\": \"sha256:a0c936696eb7d5ee3192bf53b9d281cecbb40ca9db520de72cb95817ad92ac72\",\n \"initramfs\": \"sha256:2f77c2c97621e8bf69794e9d12815bb7c46338638f126bea8b18dfe02a684d62\",\n \"bootContract\": 2,\n \"controlContract\": \"nvx-microvm-v2-control-v2\",\n \"controlSession\": 1\n }\n}\n"},"state/control.capability":{"size":32},"state/outcome.json":null,"state/openvmm.log":{"text":""}}},{"argv":["sandbox","stop","--state-dir","{root}\\state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","openvmm.log","outcome.json","sandbox.lock"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 2,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mount\": {\r\n \"access\": \"ro\",\r\n \"denied_paths\": [],\r\n \"guest_target\": \"/data\",\r\n \"host_path\": \"{root_json}\\\\share\",\r\n \"owner\": \"vmm\"\r\n },\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"whp\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 0,\n \"deny_rule_count\": 0,\n \"host_loopback\": \"none\",\n \"mode\": \"none\",\n \"status\": \"not-requested\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","deprovision","--state-dir","{root}\\state"],"check":["state"],"status":0,"stdout":"","stderr":"","files":{"state":null}}],"interop":true}, +{"name":"lifecycle: several shares","steps":[{"argv":["sandbox","provision","--state-dir","{root}\\state","--layer","distro,{root}\\distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}\\scratch.ext4","--mount","/data,{root}\\share","--mount","/other,{root}\\share2,rw"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","sandbox.lock"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 4,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mounts\": [\r\n {\r\n \"access\": \"ro\",\r\n \"denied_paths\": [],\r\n \"guest_target\": \"/data\",\r\n \"host_path\": \"{root_json}\\\\share\",\r\n \"owner\": \"vmm\"\r\n },\r\n {\r\n \"access\": \"rw\",\r\n \"denied_paths\": [],\r\n \"guest_target\": \"/other\",\r\n \"host_path\": \"{root_json}\\\\share2\",\r\n \"owner\": \"vmm\"\r\n }\r\n ],\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":null,"state/openvmm.log":null}},{"argv":["sandbox","start","--state-dir","{root}\\state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log","build/fake-openvmm-state.json"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","openvmm.log","runtime.json","sandbox.lock"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 4,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mounts\": [\r\n {\r\n \"access\": \"ro\",\r\n \"denied_paths\": [],\r\n \"guest_target\": \"/data\",\r\n \"host_path\": \"{root_json}\\\\share\",\r\n \"owner\": \"vmm\"\r\n },\r\n {\r\n \"access\": \"rw\",\r\n \"denied_paths\": [],\r\n \"guest_target\": \"/other\",\r\n \"host_path\": \"{root_json}\\\\share2\",\r\n \"owner\": \"vmm\"\r\n }\r\n ],\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":{"text":"{\n \"format\": 4,\n \"launch\": \"{hex32}\",\n \"process\": {\n \"pid\": {int},\n \"startTime\": {int},\n \"boot\": \"{hex32}\"\n },\n \"endpoint\": \"//./pipe/openvmm-microvm-{hex32}\",\n \"runtime\": {\n \"openvmm\": \"sha256:1e8c924cdaac227bf74b4a4a9bcdb9e8836a1355fd0408cc81dab5d9aa71eb4d\",\n \"kernel\": \"sha256:a0c936696eb7d5ee3192bf53b9d281cecbb40ca9db520de72cb95817ad92ac72\",\n \"initramfs\": \"sha256:2f77c2c97621e8bf69794e9d12815bb7c46338638f126bea8b18dfe02a684d62\",\n \"bootContract\": 2,\n \"controlContract\": \"nvx-microvm-v2-control-v2\",\n \"controlSession\": 1\n }\n}\n"},"state/control.capability":{"size":32},"state/outcome.json":null,"state/openvmm.log":{"text":""},"build/fake-openvmm-state.json":{"text":"[\"--machine\",\"microvm\",\"--microvm-sandbox-block\",\"distro:file:{root_json}\\\\distro.erofs,ro\",\"--microvm-sandbox-block\",\"scratch:file:{root_json}\\\\scratch.ext4\",\"--microvm-workload-identity\",\"65534:65534\",\"--mount-aggregate\",\"/run/nvx/shares\",\"--mount-child\",\"0-bd47413b5c03dfc6e4b5b8143fe205ea,{root_json}\\\\share,ro\",\"--mount-child\",\"1-bf2faee2596026620b5cbc997c2d7d45,{root_json}\\\\share2,rw\",\"--microvm-lifecycle\",\"managed\",\"--single-process\",\"--hypervisor\",\"whp\",\"--memory\",\"256M\",\"--kernel\",\"{root_json}\\\\build\\\\vmlinux\",\"--initrd\",\"{root_json}\\\\build\\\\initramfs.cpio.gz\",\"--cmdline\",\"nvx_sandbox=1 nvx_layer=distro,0xd0003000,erofs:11111111-1111-1111-1111-111111111111 nvx_scratch=0xd0006000,ext4 nvx_entrypoint=/bin/sh nvx_hostname=nvx-sandbox nvx_share=0-bd47413b5c03dfc6e4b5b8143fe205ea,/data,ro nvx_share=1-bf2faee2596026620b5cbc997c2d7d45,/other,rw\",\"--virtio-console\",\"none\",\"--microvm-control-console\",\"listen=//./pipe/openvmm-microvm-{hex32}\",\"--microvm-control-auth-stdin\",\"--microvm-report\",\"{root_json}\\\\state\\\\outcome.json\"]"}}},{"argv":["sandbox","exec","--state-dir","{root}\\state","--entrypoint","/bin/pwd","--cwd","/other"],"status":0,"stdout":"/other\n","stderr":""},{"argv":["sandbox","stop","--state-dir","{root}\\state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","openvmm.log","outcome.json","sandbox.lock"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 4,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mounts\": [\r\n {\r\n \"access\": \"ro\",\r\n \"denied_paths\": [],\r\n \"guest_target\": \"/data\",\r\n \"host_path\": \"{root_json}\\\\share\",\r\n \"owner\": \"vmm\"\r\n },\r\n {\r\n \"access\": \"rw\",\r\n \"denied_paths\": [],\r\n \"guest_target\": \"/other\",\r\n \"host_path\": \"{root_json}\\\\share2\",\r\n \"owner\": \"vmm\"\r\n }\r\n ],\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"whp\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 0,\n \"deny_rule_count\": 0,\n \"host_loopback\": \"none\",\n \"mode\": \"none\",\n \"status\": \"not-requested\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","start","--state-dir","{root}\\state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","openvmm.log","runtime.json","sandbox.lock"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 4,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mounts\": [\r\n {\r\n \"access\": \"ro\",\r\n \"denied_paths\": [],\r\n \"guest_target\": \"/data\",\r\n \"host_path\": \"{root_json}\\\\share\",\r\n \"owner\": \"vmm\"\r\n },\r\n {\r\n \"access\": \"rw\",\r\n \"denied_paths\": [],\r\n \"guest_target\": \"/other\",\r\n \"host_path\": \"{root_json}\\\\share2\",\r\n \"owner\": \"vmm\"\r\n }\r\n ],\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":{"text":"{\n \"format\": 4,\n \"launch\": \"{hex32}\",\n \"process\": {\n \"pid\": {int},\n \"startTime\": {int},\n \"boot\": \"{hex32}\"\n },\n \"endpoint\": \"//./pipe/openvmm-microvm-{hex32}\",\n \"runtime\": {\n \"openvmm\": \"sha256:1e8c924cdaac227bf74b4a4a9bcdb9e8836a1355fd0408cc81dab5d9aa71eb4d\",\n \"kernel\": \"sha256:a0c936696eb7d5ee3192bf53b9d281cecbb40ca9db520de72cb95817ad92ac72\",\n \"initramfs\": \"sha256:2f77c2c97621e8bf69794e9d12815bb7c46338638f126bea8b18dfe02a684d62\",\n \"bootContract\": 2,\n \"controlContract\": \"nvx-microvm-v2-control-v2\",\n \"controlSession\": 1\n }\n}\n"},"state/control.capability":{"size":32},"state/outcome.json":null,"state/openvmm.log":{"text":""}}},{"argv":["sandbox","stop","--state-dir","{root}\\state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","openvmm.log","outcome.json","sandbox.lock"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 4,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mounts\": [\r\n {\r\n \"access\": \"ro\",\r\n \"denied_paths\": [],\r\n \"guest_target\": \"/data\",\r\n \"host_path\": \"{root_json}\\\\share\",\r\n \"owner\": \"vmm\"\r\n },\r\n {\r\n \"access\": \"rw\",\r\n \"denied_paths\": [],\r\n \"guest_target\": \"/other\",\r\n \"host_path\": \"{root_json}\\\\share2\",\r\n \"owner\": \"vmm\"\r\n }\r\n ],\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"whp\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 0,\n \"deny_rule_count\": 0,\n \"host_loopback\": \"none\",\n \"mode\": \"none\",\n \"status\": \"not-requested\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","deprovision","--state-dir","{root}\\state"],"check":["state"],"status":0,"stdout":"","stderr":"","files":{"state":null}}],"interop":true}, +{"name":"lifecycle: share policies","steps":[{"argv":["sandbox","provision","--state-dir","{root}\\state","--layer","distro,{root}\\distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}\\scratch.ext4","--mount","/data,{root}\\share,rw","--mount-deny","{root}\\share\\hidden","--mount-allow","{root}\\share\\hidden\\shown","--mount-write","{root}\\share\\writable"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","sandbox.lock"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 5,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mounts\": [\r\n {\r\n \"access\": \"rw\",\r\n \"allowed_paths\": [\r\n \"{root_json}\\\\share\\\\hidden\\\\shown\"\r\n ],\r\n \"denied_paths\": [\r\n \"{root_json}\\\\share\\\\hidden\"\r\n ],\r\n \"guest_target\": \"/data\",\r\n \"host_path\": \"{root_json}\\\\share\",\r\n \"owner\": \"vmm\",\r\n \"writable_paths\": [\r\n \"{root_json}\\\\share\\\\writable\"\r\n ]\r\n }\r\n ],\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":null,"state/openvmm.log":null}},{"argv":["sandbox","start","--state-dir","{root}\\state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log","build/fake-openvmm-state.json"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","openvmm.log","runtime.json","sandbox.lock"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 5,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mounts\": [\r\n {\r\n \"access\": \"rw\",\r\n \"allowed_paths\": [\r\n \"{root_json}\\\\share\\\\hidden\\\\shown\"\r\n ],\r\n \"denied_paths\": [\r\n \"{root_json}\\\\share\\\\hidden\"\r\n ],\r\n \"guest_target\": \"/data\",\r\n \"host_path\": \"{root_json}\\\\share\",\r\n \"owner\": \"vmm\",\r\n \"writable_paths\": [\r\n \"{root_json}\\\\share\\\\writable\"\r\n ]\r\n }\r\n ],\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":{"text":"{\n \"format\": 4,\n \"launch\": \"{hex32}\",\n \"process\": {\n \"pid\": {int},\n \"startTime\": {int},\n \"boot\": \"{hex32}\"\n },\n \"endpoint\": \"//./pipe/openvmm-microvm-{hex32}\",\n \"runtime\": {\n \"openvmm\": \"sha256:1e8c924cdaac227bf74b4a4a9bcdb9e8836a1355fd0408cc81dab5d9aa71eb4d\",\n \"kernel\": \"sha256:a0c936696eb7d5ee3192bf53b9d281cecbb40ca9db520de72cb95817ad92ac72\",\n \"initramfs\": \"sha256:2f77c2c97621e8bf69794e9d12815bb7c46338638f126bea8b18dfe02a684d62\",\n \"bootContract\": 2,\n \"controlContract\": \"nvx-microvm-v2-control-v2\",\n \"controlSession\": 1\n }\n}\n"},"state/control.capability":{"size":32},"state/outcome.json":null,"state/openvmm.log":{"text":""},"build/fake-openvmm-state.json":{"text":"[\"--machine\",\"microvm\",\"--microvm-sandbox-block\",\"distro:file:{root_json}\\\\distro.erofs,ro\",\"--microvm-sandbox-block\",\"scratch:file:{root_json}\\\\scratch.ext4\",\"--microvm-workload-identity\",\"65534:65534\",\"--mount\",\"/data,{root_json}\\\\share,rw\",\"--mount-deny\",\"{root_json}\\\\share\\\\hidden\",\"--mount-allow\",\"{root_json}\\\\share\\\\hidden\\\\shown\",\"--mount-write\",\"{root_json}\\\\share\\\\writable\",\"--microvm-lifecycle\",\"managed\",\"--single-process\",\"--hypervisor\",\"whp\",\"--memory\",\"256M\",\"--kernel\",\"{root_json}\\\\build\\\\vmlinux\",\"--initrd\",\"{root_json}\\\\build\\\\initramfs.cpio.gz\",\"--cmdline\",\"nvx_sandbox=1 nvx_layer=distro,0xd0003000,erofs:11111111-1111-1111-1111-111111111111 nvx_scratch=0xd0006000,ext4 nvx_entrypoint=/bin/sh nvx_hostname=nvx-sandbox\",\"--virtio-console\",\"none\",\"--microvm-control-console\",\"listen=//./pipe/openvmm-microvm-{hex32}\",\"--microvm-control-auth-stdin\",\"--microvm-report\",\"{root_json}\\\\state\\\\outcome.json\"]"}}},{"argv":["sandbox","stop","--state-dir","{root}\\state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","openvmm.log","outcome.json","sandbox.lock"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 5,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mounts\": [\r\n {\r\n \"access\": \"rw\",\r\n \"allowed_paths\": [\r\n \"{root_json}\\\\share\\\\hidden\\\\shown\"\r\n ],\r\n \"denied_paths\": [\r\n \"{root_json}\\\\share\\\\hidden\"\r\n ],\r\n \"guest_target\": \"/data\",\r\n \"host_path\": \"{root_json}\\\\share\",\r\n \"owner\": \"vmm\",\r\n \"writable_paths\": [\r\n \"{root_json}\\\\share\\\\writable\"\r\n ]\r\n }\r\n ],\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"whp\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 0,\n \"deny_rule_count\": 0,\n \"host_loopback\": \"none\",\n \"mode\": \"none\",\n \"status\": \"not-requested\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","start","--state-dir","{root}\\state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","openvmm.log","runtime.json","sandbox.lock"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 5,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mounts\": [\r\n {\r\n \"access\": \"rw\",\r\n \"allowed_paths\": [\r\n \"{root_json}\\\\share\\\\hidden\\\\shown\"\r\n ],\r\n \"denied_paths\": [\r\n \"{root_json}\\\\share\\\\hidden\"\r\n ],\r\n \"guest_target\": \"/data\",\r\n \"host_path\": \"{root_json}\\\\share\",\r\n \"owner\": \"vmm\",\r\n \"writable_paths\": [\r\n \"{root_json}\\\\share\\\\writable\"\r\n ]\r\n }\r\n ],\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":{"text":"{\n \"format\": 4,\n \"launch\": \"{hex32}\",\n \"process\": {\n \"pid\": {int},\n \"startTime\": {int},\n \"boot\": \"{hex32}\"\n },\n \"endpoint\": \"//./pipe/openvmm-microvm-{hex32}\",\n \"runtime\": {\n \"openvmm\": \"sha256:1e8c924cdaac227bf74b4a4a9bcdb9e8836a1355fd0408cc81dab5d9aa71eb4d\",\n \"kernel\": \"sha256:a0c936696eb7d5ee3192bf53b9d281cecbb40ca9db520de72cb95817ad92ac72\",\n \"initramfs\": \"sha256:2f77c2c97621e8bf69794e9d12815bb7c46338638f126bea8b18dfe02a684d62\",\n \"bootContract\": 2,\n \"controlContract\": \"nvx-microvm-v2-control-v2\",\n \"controlSession\": 1\n }\n}\n"},"state/control.capability":{"size":32},"state/outcome.json":null,"state/openvmm.log":{"text":""}}},{"argv":["sandbox","stop","--state-dir","{root}\\state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","openvmm.log","outcome.json","sandbox.lock"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 5,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mounts\": [\r\n {\r\n \"access\": \"rw\",\r\n \"allowed_paths\": [\r\n \"{root_json}\\\\share\\\\hidden\\\\shown\"\r\n ],\r\n \"denied_paths\": [\r\n \"{root_json}\\\\share\\\\hidden\"\r\n ],\r\n \"guest_target\": \"/data\",\r\n \"host_path\": \"{root_json}\\\\share\",\r\n \"owner\": \"vmm\",\r\n \"writable_paths\": [\r\n \"{root_json}\\\\share\\\\writable\"\r\n ]\r\n }\r\n ],\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"whp\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 0,\n \"deny_rule_count\": 0,\n \"host_loopback\": \"none\",\n \"mode\": \"none\",\n \"status\": \"not-requested\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","deprovision","--state-dir","{root}\\state"],"check":["state"],"status":0,"stdout":"","stderr":"","files":{"state":null}}],"interop":true}, +{"name":"lifecycle: file share","steps":[{"argv":["sandbox","provision","--state-dir","{root}\\state","--layer","distro,{root}\\distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}\\scratch.ext4","--mount","/etc/single.txt,{root}\\single.txt,ro"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","sandbox.lock"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 6,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mounts\": [\r\n {\r\n \"access\": \"ro\",\r\n \"allowed_paths\": [],\r\n \"denied_paths\": [],\r\n \"guest_target\": \"/etc/single.txt\",\r\n \"host_path\": \"{root_json}\\\\single.txt\",\r\n \"kind\": \"file\",\r\n \"owner\": \"vmm\",\r\n \"writable_paths\": []\r\n }\r\n ],\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":null,"state/openvmm.log":null}},{"argv":["sandbox","start","--state-dir","{root}\\state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log","build/fake-openvmm-state.json"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","openvmm.log","runtime.json","sandbox.lock"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 6,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mounts\": [\r\n {\r\n \"access\": \"ro\",\r\n \"allowed_paths\": [],\r\n \"denied_paths\": [],\r\n \"guest_target\": \"/etc/single.txt\",\r\n \"host_path\": \"{root_json}\\\\single.txt\",\r\n \"kind\": \"file\",\r\n \"owner\": \"vmm\",\r\n \"writable_paths\": []\r\n }\r\n ],\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":{"text":"{\n \"format\": 4,\n \"launch\": \"{hex32}\",\n \"process\": {\n \"pid\": {int},\n \"startTime\": {int},\n \"boot\": \"{hex32}\"\n },\n \"endpoint\": \"//./pipe/openvmm-microvm-{hex32}\",\n \"runtime\": {\n \"openvmm\": \"sha256:1e8c924cdaac227bf74b4a4a9bcdb9e8836a1355fd0408cc81dab5d9aa71eb4d\",\n \"kernel\": \"sha256:a0c936696eb7d5ee3192bf53b9d281cecbb40ca9db520de72cb95817ad92ac72\",\n \"initramfs\": \"sha256:2f77c2c97621e8bf69794e9d12815bb7c46338638f126bea8b18dfe02a684d62\",\n \"bootContract\": 2,\n \"controlContract\": \"nvx-microvm-v2-control-v2\",\n \"controlSession\": 1\n }\n}\n"},"state/control.capability":{"size":32},"state/outcome.json":null,"state/openvmm.log":{"text":""},"build/fake-openvmm-state.json":{"text":"[\"--machine\",\"microvm\",\"--microvm-sandbox-block\",\"distro:file:{root_json}\\\\distro.erofs,ro\",\"--microvm-sandbox-block\",\"scratch:file:{root_json}\\\\scratch.ext4\",\"--microvm-workload-identity\",\"65534:65534\",\"--mount-aggregate\",\"/run/nvx/shares\",\"--mount-child\",\"0-a80a05e9cb129bc27b7ba103bf2f5ac0,{root_json}\\\\single.txt,ro,file\",\"--microvm-lifecycle\",\"managed\",\"--single-process\",\"--hypervisor\",\"whp\",\"--memory\",\"256M\",\"--kernel\",\"{root_json}\\\\build\\\\vmlinux\",\"--initrd\",\"{root_json}\\\\build\\\\initramfs.cpio.gz\",\"--cmdline\",\"nvx_sandbox=1 nvx_layer=distro,0xd0003000,erofs:11111111-1111-1111-1111-111111111111 nvx_scratch=0xd0006000,ext4 nvx_entrypoint=/bin/sh nvx_hostname=nvx-sandbox nvx_share=0-a80a05e9cb129bc27b7ba103bf2f5ac0,/etc/single.txt,ro,file\",\"--virtio-console\",\"none\",\"--microvm-control-console\",\"listen=//./pipe/openvmm-microvm-{hex32}\",\"--microvm-control-auth-stdin\",\"--microvm-report\",\"{root_json}\\\\state\\\\outcome.json\"]"}}},{"argv":["sandbox","stop","--state-dir","{root}\\state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","openvmm.log","outcome.json","sandbox.lock"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 6,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mounts\": [\r\n {\r\n \"access\": \"ro\",\r\n \"allowed_paths\": [],\r\n \"denied_paths\": [],\r\n \"guest_target\": \"/etc/single.txt\",\r\n \"host_path\": \"{root_json}\\\\single.txt\",\r\n \"kind\": \"file\",\r\n \"owner\": \"vmm\",\r\n \"writable_paths\": []\r\n }\r\n ],\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"whp\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 0,\n \"deny_rule_count\": 0,\n \"host_loopback\": \"none\",\n \"mode\": \"none\",\n \"status\": \"not-requested\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","start","--state-dir","{root}\\state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","openvmm.log","runtime.json","sandbox.lock"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 6,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mounts\": [\r\n {\r\n \"access\": \"ro\",\r\n \"allowed_paths\": [],\r\n \"denied_paths\": [],\r\n \"guest_target\": \"/etc/single.txt\",\r\n \"host_path\": \"{root_json}\\\\single.txt\",\r\n \"kind\": \"file\",\r\n \"owner\": \"vmm\",\r\n \"writable_paths\": []\r\n }\r\n ],\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":{"text":"{\n \"format\": 4,\n \"launch\": \"{hex32}\",\n \"process\": {\n \"pid\": {int},\n \"startTime\": {int},\n \"boot\": \"{hex32}\"\n },\n \"endpoint\": \"//./pipe/openvmm-microvm-{hex32}\",\n \"runtime\": {\n \"openvmm\": \"sha256:1e8c924cdaac227bf74b4a4a9bcdb9e8836a1355fd0408cc81dab5d9aa71eb4d\",\n \"kernel\": \"sha256:a0c936696eb7d5ee3192bf53b9d281cecbb40ca9db520de72cb95817ad92ac72\",\n \"initramfs\": \"sha256:2f77c2c97621e8bf69794e9d12815bb7c46338638f126bea8b18dfe02a684d62\",\n \"bootContract\": 2,\n \"controlContract\": \"nvx-microvm-v2-control-v2\",\n \"controlSession\": 1\n }\n}\n"},"state/control.capability":{"size":32},"state/outcome.json":null,"state/openvmm.log":{"text":""}}},{"argv":["sandbox","stop","--state-dir","{root}\\state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","openvmm.log","outcome.json","sandbox.lock"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 6,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mounts\": [\r\n {\r\n \"access\": \"ro\",\r\n \"allowed_paths\": [],\r\n \"denied_paths\": [],\r\n \"guest_target\": \"/etc/single.txt\",\r\n \"host_path\": \"{root_json}\\\\single.txt\",\r\n \"kind\": \"file\",\r\n \"owner\": \"vmm\",\r\n \"writable_paths\": []\r\n }\r\n ],\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"whp\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 0,\n \"deny_rule_count\": 0,\n \"host_loopback\": \"none\",\n \"mode\": \"none\",\n \"status\": \"not-requested\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","deprovision","--state-dir","{root}\\state"],"check":["state"],"status":0,"stdout":"","stderr":"","files":{"state":null}}],"interop":true}, +{"name":"lifecycle: network and launch options","steps":[{"argv":["sandbox","provision","--state-dir","{root}\\state","--layer","distro,{root}\\distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}\\scratch.ext4","--entrypoint","/bin/server","--arg=--serve","--hostname","worker","--workload-user","1000:1000","--memory-max","268435456","--pids-max","64","--memory-mib","384","--hypervisor","auto","--cmdline","quiet","--net","10.0.0.2/24","--network-profile","portable","--network-egress","deny","--network-ingress","deny","--network-egress-policy-file","{root}\\policy.json","--host-loopback","allow","--network-proxy","10.0.0.1:3128","--host-loopback-forward","8080","--host-loopback-forward","9090"],"write":{"policy.json":"{\"allow\": [{\"cidr\": \"192.0.2.0/24\", \"protocol\": \"tcp\", \"port\": 443}], \"deny\": [{\"cidr\": \"192.0.2.128/25\"}]}"},"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","sandbox.lock"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"quiet\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 1,\r\n \"host_loopback\": \"allow\",\r\n \"host_loopback_forward\": [\r\n \"8080\",\r\n \"9090\"\r\n ],\r\n \"hostname\": \"worker\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": 268435456,\r\n \"memory_mib\": 384,\r\n \"mount\": null,\r\n \"net\": \"10.0.0.2/24\",\r\n \"network_egress\": \"deny\",\r\n \"network_egress_allow\": [\r\n \"192.0.2.0/24:tcp:443\"\r\n ],\r\n \"network_egress_deny\": [\r\n \"192.0.2.128/25\"\r\n ],\r\n \"network_ingress\": \"deny\",\r\n \"network_profile\": \"portable\",\r\n \"network_proxy\": \"10.0.0.1:3128\",\r\n \"pids_max\": 64,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 1000,\r\n \"workload_uid\": 1000\r\n}\r\n"},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":null,"state/openvmm.log":null}},{"argv":["sandbox","start","--state-dir","{root}\\state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log","build/fake-openvmm-state.json"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","openvmm.log","runtime.json","sandbox.lock"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"quiet\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 1,\r\n \"host_loopback\": \"allow\",\r\n \"host_loopback_forward\": [\r\n \"8080\",\r\n \"9090\"\r\n ],\r\n \"hostname\": \"worker\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": 268435456,\r\n \"memory_mib\": 384,\r\n \"mount\": null,\r\n \"net\": \"10.0.0.2/24\",\r\n \"network_egress\": \"deny\",\r\n \"network_egress_allow\": [\r\n \"192.0.2.0/24:tcp:443\"\r\n ],\r\n \"network_egress_deny\": [\r\n \"192.0.2.128/25\"\r\n ],\r\n \"network_ingress\": \"deny\",\r\n \"network_profile\": \"portable\",\r\n \"network_proxy\": \"10.0.0.1:3128\",\r\n \"pids_max\": 64,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 1000,\r\n \"workload_uid\": 1000\r\n}\r\n"},"state/runtime.json":{"text":"{\n \"format\": 4,\n \"launch\": \"{hex32}\",\n \"process\": {\n \"pid\": {int},\n \"startTime\": {int},\n \"boot\": \"{hex32}\"\n },\n \"endpoint\": \"//./pipe/openvmm-microvm-{hex32}\",\n \"runtime\": {\n \"openvmm\": \"sha256:1e8c924cdaac227bf74b4a4a9bcdb9e8836a1355fd0408cc81dab5d9aa71eb4d\",\n \"kernel\": \"sha256:a0c936696eb7d5ee3192bf53b9d281cecbb40ca9db520de72cb95817ad92ac72\",\n \"initramfs\": \"sha256:2f77c2c97621e8bf69794e9d12815bb7c46338638f126bea8b18dfe02a684d62\",\n \"bootContract\": 2,\n \"controlContract\": \"nvx-microvm-v2-control-v2\",\n \"controlSession\": 1\n }\n}\n"},"state/control.capability":{"size":32},"state/outcome.json":null,"state/openvmm.log":{"text":""},"build/fake-openvmm-state.json":{"text":"[\"--machine\",\"microvm\",\"--microvm-sandbox-block\",\"distro:file:{root_json}\\\\distro.erofs,ro\",\"--microvm-sandbox-block\",\"scratch:file:{root_json}\\\\scratch.ext4\",\"--microvm-workload-identity\",\"1000:1000\",\"--microvm-lifecycle\",\"managed\",\"--single-process\",\"--hypervisor\",\"whp\",\"--memory\",\"384M\",\"--kernel\",\"{root_json}\\\\build\\\\vmlinux\",\"--initrd\",\"{root_json}\\\\build\\\\initramfs.cpio.gz\",\"--cmdline\",\"quiet nvx_sandbox=1 nvx_layer=distro,0xd0003000,erofs:11111111-1111-1111-1111-111111111111 nvx_scratch=0xd0006000,ext4 nvx_entrypoint=/bin/sh nvx_hostname=worker nvx_memory_max=268435456 nvx_pids_max=65\",\"--virtio-console\",\"none\",\"--microvm-control-console\",\"listen=//./pipe/openvmm-microvm-{hex32}\",\"--microvm-control-auth-stdin\",\"--microvm-report\",\"{root_json}\\\\state\\\\outcome.json\",\"--net\",\"10.0.0.2/24\",\"--network-profile\",\"portable\",\"--network-egress\",\"deny\",\"--network-ingress\",\"deny\",\"--host-loopback\",\"allow\",\"--network-egress-allow\",\"192.0.2.0/24:tcp:443\",\"--network-egress-deny\",\"192.0.2.128/25\",\"--host-loopback-forward\",\"8080\",\"--host-loopback-forward\",\"9090\",\"--network-proxy\",\"10.0.0.1:3128\"]"}}},{"argv":["sandbox","stop","--state-dir","{root}\\state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","openvmm.log","outcome.json","sandbox.lock"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"quiet\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 1,\r\n \"host_loopback\": \"allow\",\r\n \"host_loopback_forward\": [\r\n \"8080\",\r\n \"9090\"\r\n ],\r\n \"hostname\": \"worker\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": 268435456,\r\n \"memory_mib\": 384,\r\n \"mount\": null,\r\n \"net\": \"10.0.0.2/24\",\r\n \"network_egress\": \"deny\",\r\n \"network_egress_allow\": [\r\n \"192.0.2.0/24:tcp:443\"\r\n ],\r\n \"network_egress_deny\": [\r\n \"192.0.2.128/25\"\r\n ],\r\n \"network_ingress\": \"deny\",\r\n \"network_profile\": \"portable\",\r\n \"network_proxy\": \"10.0.0.1:3128\",\r\n \"pids_max\": 64,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 1000,\r\n \"workload_uid\": 1000\r\n}\r\n"},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"whp\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 1,\n \"deny_rule_count\": 1,\n \"host_loopback\": \"allow\",\n \"mode\": \"rules\",\n \"status\": \"applied\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","start","--state-dir","{root}\\state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","control.capability","openvmm.log","runtime.json","sandbox.lock"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"quiet\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 1,\r\n \"host_loopback\": \"allow\",\r\n \"host_loopback_forward\": [\r\n \"8080\",\r\n \"9090\"\r\n ],\r\n \"hostname\": \"worker\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": 268435456,\r\n \"memory_mib\": 384,\r\n \"mount\": null,\r\n \"net\": \"10.0.0.2/24\",\r\n \"network_egress\": \"deny\",\r\n \"network_egress_allow\": [\r\n \"192.0.2.0/24:tcp:443\"\r\n ],\r\n \"network_egress_deny\": [\r\n \"192.0.2.128/25\"\r\n ],\r\n \"network_ingress\": \"deny\",\r\n \"network_profile\": \"portable\",\r\n \"network_proxy\": \"10.0.0.1:3128\",\r\n \"pids_max\": 64,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 1000,\r\n \"workload_uid\": 1000\r\n}\r\n"},"state/runtime.json":{"text":"{\n \"format\": 4,\n \"launch\": \"{hex32}\",\n \"process\": {\n \"pid\": {int},\n \"startTime\": {int},\n \"boot\": \"{hex32}\"\n },\n \"endpoint\": \"//./pipe/openvmm-microvm-{hex32}\",\n \"runtime\": {\n \"openvmm\": \"sha256:1e8c924cdaac227bf74b4a4a9bcdb9e8836a1355fd0408cc81dab5d9aa71eb4d\",\n \"kernel\": \"sha256:a0c936696eb7d5ee3192bf53b9d281cecbb40ca9db520de72cb95817ad92ac72\",\n \"initramfs\": \"sha256:2f77c2c97621e8bf69794e9d12815bb7c46338638f126bea8b18dfe02a684d62\",\n \"bootContract\": 2,\n \"controlContract\": \"nvx-microvm-v2-control-v2\",\n \"controlSession\": 1\n }\n}\n"},"state/control.capability":{"size":32},"state/outcome.json":null,"state/openvmm.log":{"text":""}}},{"argv":["sandbox","stop","--state-dir","{root}\\state"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":0,"stdout":"","stderr":"","files":{"state":{"entries":["config.json","openvmm.log","outcome.json","sandbox.lock"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"quiet\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 1,\r\n \"host_loopback\": \"allow\",\r\n \"host_loopback_forward\": [\r\n \"8080\",\r\n \"9090\"\r\n ],\r\n \"hostname\": \"worker\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": 268435456,\r\n \"memory_mib\": 384,\r\n \"mount\": null,\r\n \"net\": \"10.0.0.2/24\",\r\n \"network_egress\": \"deny\",\r\n \"network_egress_allow\": [\r\n \"192.0.2.0/24:tcp:443\"\r\n ],\r\n \"network_egress_deny\": [\r\n \"192.0.2.128/25\"\r\n ],\r\n \"network_ingress\": \"deny\",\r\n \"network_profile\": \"portable\",\r\n \"network_proxy\": \"10.0.0.1:3128\",\r\n \"pids_max\": 64,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 1000,\r\n \"workload_uid\": 1000\r\n}\r\n"},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":{"text":"{\n \"backend\": \"whp\",\n \"instance_id\": \"{hex32}\",\n \"network_policy\": {\n \"allow_rule_count\": 1,\n \"deny_rule_count\": 1,\n \"host_loopback\": \"allow\",\n \"mode\": \"rules\",\n \"status\": \"applied\",\n \"status_code\": 0\n },\n \"outcome\": {\n \"category\": \"success\",\n \"operation\": \"managed\",\n \"status_code\": 0\n },\n \"schema_version\": 1,\n \"teardown\": {\n \"control_channels_closed\": true,\n \"guest_workload_stopped\": true,\n \"network_released\": true,\n \"openvmm_process_terminated\": true,\n \"temporary_storage_removed\": true,\n \"virtiofs_released\": true,\n \"vm_stopped\": true\n }\n}\n"},"state/openvmm.log":{"text":""}}},{"argv":["sandbox","deprovision","--state-dir","{root}\\state"],"check":["state"],"status":0,"stdout":"","stderr":"","files":{"state":null}}],"interop":true}, {"name":"state: operations without a sandbox","steps":[{"argv":["sandbox","start","--state-dir","{root}\\state"],"status":1,"stdout":"","stderr":"error: sandbox state path is not a plain directory: {root}\\state\r\n"},{"argv":["sandbox","exec","--state-dir","{root}\\state"],"status":1,"stdout":"","stderr":"error: sandbox state path is not a plain directory: {root}\\state\r\n"},{"argv":["sandbox","stop","--state-dir","{root}\\state"],"status":1,"stdout":"","stderr":"error: sandbox state path is not a plain directory: {root}\\state\r\n"},{"argv":["sandbox","deprovision","--state-dir","{root}\\state"],"status":1,"stdout":"","stderr":"error: sandbox state path is not a plain directory: {root}\\state\r\n"}]}, {"name":"state: refusals of a running sandbox","steps":[{"argv":["sandbox","provision","--state-dir","{root}\\state","--layer","distro,{root}\\distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}\\scratch.ext4"],"status":0,"stdout":"","stderr":""},{"argv":["sandbox","provision","--state-dir","{root}\\state","--layer","distro,{root}\\distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}\\scratch.ext4"],"status":1,"stdout":"","stderr":"error: sandbox is already provisioned\r\n"},{"argv":["sandbox","start","--state-dir","{root}\\state"],"status":0,"stdout":"","stderr":""},{"argv":["sandbox","start","--state-dir","{root}\\state"],"status":1,"stdout":"","stderr":"error: sandbox is already running or has stale runtime state\r\n"},{"argv":["sandbox","deprovision","--state-dir","{root}\\state"],"status":1,"stdout":"","stderr":"error: sandbox must be stopped before deprovision\r\n"},{"argv":["sandbox","stop","--state-dir","{root}\\state"],"status":0,"stdout":"","stderr":""},{"argv":["sandbox","stop","--state-dir","{root}\\state"],"status":1,"stdout":"","stderr":"error: sandbox is not running\r\n"},{"argv":["sandbox","exec","--state-dir","{root}\\state"],"status":1,"stdout":"","stderr":"error: sandbox is not running\r\n"},{"argv":["sandbox","deprovision","--state-dir","{root}\\state"],"check":["state"],"status":0,"stdout":"","stderr":"","files":{"state":null}}],"interop":true}, -{"name":"state: relative and nested state directory","steps":[{"argv":["sandbox","provision","--state-dir","nested/state","--layer","distro,{root}\\distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}\\scratch.ext4"],"check":["nested/state","nested/state/config.json"],"status":0,"stdout":"","stderr":"","files":{"nested/state":{"entries":["config.json","lifecycle.lock"]},"nested/state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 1,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mount\": null,\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"}}},{"argv":["sandbox","deprovision","--state-dir","nested/state"],"check":["nested/state"],"status":0,"stdout":"","stderr":"","files":{"nested/state":null}}]}, -{"name":"state: failed start","steps":[{"argv":["sandbox","provision","--state-dir","{root}\\state","--layer","distro,{root}\\distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}\\scratch.ext4","--cmdline","fake_exit_on_start=5"],"status":0,"stdout":"","stderr":""},{"argv":["sandbox","start","--state-dir","{root}\\state","--timeout","0.5"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":1,"stdout":"","stderr":"error: OpenVMM exited during startup with status 5\r\n","files":{"state":{"entries":["config.json","lifecycle.lock","openvmm.log"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"fake_exit_on_start=5\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 1,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mount\": null,\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":null,"state/openvmm.log":{"text":"openvmm: failing the launch as requested\n"}}},{"argv":["sandbox","start","--state-dir","{root}\\state","--timeout","0.5"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":1,"stdout":"","stderr":"error: OpenVMM exited during startup with status 5\r\n","files":{"state":{"entries":["config.json","lifecycle.lock","openvmm.log"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"fake_exit_on_start=5\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 1,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mount\": null,\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":null,"state/openvmm.log":{"text":"openvmm: failing the launch as requested\nopenvmm: failing the launch as requested\n"}}},{"argv":["sandbox","deprovision","--state-dir","{root}\\state"],"check":["state"],"status":0,"stdout":"","stderr":"","files":{"state":null}}],"interop":true}, -{"name":"state: start without OpenVMM","steps":[{"argv":["sandbox","provision","--state-dir","{root}\\state","--layer","distro,{root}\\distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}\\scratch.ext4"],"status":0,"stdout":"","stderr":""},{"argv":["sandbox","start","--state-dir","{root}\\state"],"remove":["openvmm/target/release/openvmm{exe}"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":1,"stdout":"","stderr":"error: OpenVMM release binary not found: {root}\\openvmm\\target\\release\\openvmm.exe\r\n","files":{"state":{"entries":["config.json","lifecycle.lock"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 1,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mount\": null,\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":null,"state/openvmm.log":null}},{"argv":["sandbox","deprovision","--state-dir","{root}\\state"],"status":0,"stdout":"","stderr":""}]}, +{"name":"state: relative and nested state directory","steps":[{"argv":["sandbox","provision","--state-dir","nested/state","--layer","distro,{root}\\distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}\\scratch.ext4"],"check":["nested/state","nested/state/config.json"],"status":0,"stdout":"","stderr":"","files":{"nested/state":{"entries":["config.json","sandbox.lock"]},"nested/state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 1,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mount\": null,\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"}}},{"argv":["sandbox","deprovision","--state-dir","nested/state"],"check":["nested/state"],"status":0,"stdout":"","stderr":"","files":{"nested/state":null}}]}, +{"name":"state: failed start","steps":[{"argv":["sandbox","provision","--state-dir","{root}\\state","--layer","distro,{root}\\distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}\\scratch.ext4","--cmdline","fake_exit_on_start=5"],"status":0,"stdout":"","stderr":""},{"argv":["sandbox","start","--state-dir","{root}\\state","--timeout","0.5"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":1,"stdout":"","stderr":"error: OpenVMM exited during startup with status 5\r\n","files":{"state":{"entries":["config.json","openvmm.log","sandbox.lock"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"fake_exit_on_start=5\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 1,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mount\": null,\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":null,"state/openvmm.log":{"text":"openvmm: failing the launch as requested\n"}}},{"argv":["sandbox","start","--state-dir","{root}\\state","--timeout","0.5"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":1,"stdout":"","stderr":"error: OpenVMM exited during startup with status 5\r\n","files":{"state":{"entries":["config.json","openvmm.log","sandbox.lock"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"fake_exit_on_start=5\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 1,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mount\": null,\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":null,"state/openvmm.log":{"text":"openvmm: failing the launch as requested\n"}}},{"argv":["sandbox","deprovision","--state-dir","{root}\\state"],"check":["state"],"status":0,"stdout":"","stderr":"","files":{"state":null}}],"interop":true}, +{"name":"state: start without OpenVMM","steps":[{"argv":["sandbox","provision","--state-dir","{root}\\state","--layer","distro,{root}\\distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}\\scratch.ext4"],"status":0,"stdout":"","stderr":""},{"argv":["sandbox","start","--state-dir","{root}\\state"],"remove":["openvmm/target/release/openvmm{exe}"],"check":["state","state/config.json","state/runtime.json","state/control.capability","state/outcome.json","state/openvmm.log"],"status":1,"stdout":"","stderr":"error: OpenVMM release binary not found: {root}\\openvmm\\target\\release\\openvmm.exe\r\n","files":{"state":{"entries":["config.json","sandbox.lock"]},"state/config.json":{"text":"{\r\n \"cmdline\": \"\",\r\n \"distro_binding\": {\r\n \"format\": \"erofs\",\r\n \"identity\": \"11111111-1111-1111-1111-111111111111\",\r\n \"image\": {\r\n \"changed\": null,\r\n \"device\": {int},\r\n \"fileId\": \"{hex32}\",\r\n \"kind\": \"regular\",\r\n \"length\": 7,\r\n \"modified\": {\r\n \"nanoseconds\": {int},\r\n \"seconds\": {int}\r\n },\r\n \"scheme\": \"windows\",\r\n \"version\": 1\r\n },\r\n \"manifest\": null,\r\n \"policy\": {\r\n \"result\": \"accepted\",\r\n \"revision\": 1\r\n },\r\n \"version\": 1\r\n },\r\n \"format\": 1,\r\n \"host_loopback\": null,\r\n \"host_loopback_forward\": [],\r\n \"hostname\": \"nvx-sandbox\",\r\n \"hypervisor\": \"whp\",\r\n \"layers\": [\r\n {\r\n \"path\": \"{root_json}\\\\distro.erofs\",\r\n \"role\": \"distro\",\r\n \"uuid\": \"11111111-1111-1111-1111-111111111111\"\r\n }\r\n ],\r\n \"memory_max\": null,\r\n \"memory_mib\": 256,\r\n \"mount\": null,\r\n \"net\": null,\r\n \"network_egress\": null,\r\n \"network_egress_allow\": [],\r\n \"network_egress_deny\": [],\r\n \"network_ingress\": null,\r\n \"network_profile\": null,\r\n \"network_proxy\": null,\r\n \"pids_max\": null,\r\n \"scratch\": \"{root_json}\\\\scratch.ext4\",\r\n \"workload_gid\": 65534,\r\n \"workload_uid\": 65534\r\n}\r\n"},"state/runtime.json":null,"state/control.capability":null,"state/outcome.json":null,"state/openvmm.log":null}},{"argv":["sandbox","deprovision","--state-dir","{root}\\state"],"status":0,"stdout":"","stderr":""}]}, {"name":"state: malformed configuration","steps":[{"argv":["sandbox","provision","--state-dir","{root}\\state","--layer","distro,{root}\\distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}\\scratch.ext4"],"status":0,"stdout":"","stderr":""},{"argv":["sandbox","start","--state-dir","{root}\\state"],"write":{"state/config.json":"{\"format\": 7}"},"status":1,"stdout":"","stderr":"error: sandbox configuration has an unsupported format: {root}\\state\\config.json\r\n"},{"argv":["sandbox","start","--state-dir","{root}\\state"],"write":{"state/config.json":"[]"},"status":1,"stdout":"","stderr":"error: sandbox configuration has an unsupported format: {root}\\state\\config.json\r\n"},{"argv":["sandbox","start","--state-dir","{root}\\state"],"write":{"state/config.json":"{\"format\": 1}"},"status":1,"stdout":"","stderr":"error: sandbox configuration is malformed\r\n"},{"argv":["sandbox","deprovision","--state-dir","{root}\\state"],"check":["state"],"status":0,"stdout":"","stderr":"","files":{"state":null}}]}, {"name":"state: not a directory","steps":[{"argv":["sandbox","start","--state-dir","{root}\\single.txt"],"status":1,"stdout":"","stderr":"error: sandbox state path is not a plain directory: {root}\\single.txt\r\n"},{"argv":["sandbox","provision","--state-dir","{root}\\single.txt","--layer","distro,{root}\\distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}\\scratch.ext4"],"status":1,"stdout":"","stderr":"error: [WinError 183] Cannot create a file when that file already exists: '{root_json}\\\\single.txt'\r\n"},{"argv":["sandbox","deprovision","--state-dir","{root}\\single.txt"],"status":1,"stdout":"","stderr":"error: sandbox state path is not a plain directory: {root}\\single.txt\r\n"}]}, {"name":"state: foreign file","steps":[{"argv":["sandbox","provision","--state-dir","{root}\\state","--layer","distro,{root}\\distro.erofs,11111111-1111-1111-1111-111111111111","--scratch","{root}\\scratch.ext4"],"status":0,"stdout":"","stderr":""},{"argv":["sandbox","deprovision","--state-dir","{root}\\state"],"write":{"state/foreign.txt":"x"},"check":["state"],"status":1,"stdout":"","stderr":"error: sandbox state directory contains files not owned by NVX: foreign.txt\r\n","files":{"state":{"entries":["foreign.txt"]}}}]} diff --git a/scripts/nvx_tools/sandbox_lifecycle_tests.py b/scripts/nvx_tools/sandbox_lifecycle_tests.py index 62aa4c602..08c8be37f 100644 --- a/scripts/nvx_tools/sandbox_lifecycle_tests.py +++ b/scripts/nvx_tools/sandbox_lifecycle_tests.py @@ -75,7 +75,9 @@ LOG = "openvmm.log" SOCKET = "control.sock" OUTCOME = "outcome.json" -LOCK = "lifecycle.lock" +LOCK = "sandbox.lock" +# The fields of the store's runtime record. +RUNTIME_FIELDS = frozenset({"format", "launch", "process", "endpoint", "runtime"}) PROVISIONED_ENTRIES = frozenset({CONFIG, LOCK}) # Windows hosts serve the control endpoint as a named pipe outside the state. RUNNING_ENTRIES = frozenset( @@ -84,7 +86,11 @@ else {CONFIG, LOCK, RUNTIME, CAPABILITY, LOG, SOCKET} ) STOPPED_ENTRIES = frozenset({CONFIG, LOCK, LOG, OUTCOME}) +# OpenVMM that exits without a stop publishes no outcome report, and the +# transition that finds it exited clears its runtime record. +CRASHED_ENTRIES = frozenset({CONFIG, LOCK, LOG}) ALREADY_RUNNING = b"sandbox is already running or has stale runtime state" +STALE = b"sandbox runtime state is stale because the OpenVMM process is not running" def openvmm_processes(marker: str) -> tuple[int, ...]: @@ -125,15 +131,20 @@ def openvmm_processes(marker: str) -> tuple[int, ...]: return tuple(sorted(processes)) +def recorded_pid(runtime: dict[str, Any]) -> int: + """Returns the ID of the OpenVMM process that a runtime record names.""" + return int(runtime["process"]["pid"]) + + def openvmm_running(runtime: dict[str, Any], marker: str) -> bool: """Returns whether the OpenVMM process of a runtime record still runs: an OpenVMM process with its ID whose arguments contain marker, the name of the fixture, which no other process names.""" - return int(runtime["pid"]) in openvmm_processes(marker) + return recorded_pid(runtime) in openvmm_processes(marker) def control_endpoint_present(runtime: dict[str, Any]) -> bool: - endpoint = str(runtime["control_endpoint"]) + endpoint = str(runtime["endpoint"]) if sys.platform == "win32": return endpoint.rsplit("/", 1)[-1] in os.listdir("//./pipe/") return os.path.lexists(endpoint) @@ -151,7 +162,7 @@ def kill_openvmm(runtime: dict[str, Any], timeout: float, marker: str) -> None: names the fixture, as for openvmm_running.""" if openvmm_running(runtime, marker): try: - terminate_process(int(runtime["pid"])) + terminate_process(recorded_pid(runtime)) except OSError: # The process may have exited since the check. if openvmm_running(runtime, marker): @@ -159,7 +170,9 @@ def kill_openvmm(runtime: dict[str, Any], timeout: float, marker: str) -> None: deadline = time.monotonic() + timeout while openvmm_running(runtime, marker): if time.monotonic() >= deadline: - raise RuntimeError(f"OpenVMM process {runtime['pid']} survived its kill") + raise RuntimeError( + f"OpenVMM process {recorded_pid(runtime)} survived its kill" + ) time.sleep(0.05) @@ -250,7 +263,7 @@ def read_runtime(state: Path) -> dict[str, Any]: if not isinstance(value, dict): raise RuntimeError(f"sandbox runtime record is not an object: {path}") runtime = cast(dict[str, Any], value) - if set(runtime) != {"format", "pid", "start_time", "control_endpoint"}: + if frozenset(runtime) != RUNTIME_FIELDS: raise RuntimeError(f"sandbox runtime record has unexpected fields: {path}") return runtime @@ -597,7 +610,7 @@ def require_started(self, description: str) -> dict[str, Any]: raise RuntimeError( f"the control endpoint is absent after the {description}" ) - self.require_processes((int(runtime["pid"]),), f"after the {description}") + self.require_processes((recorded_pid(runtime),), f"after the {description}") return runtime def check_running_transitions(self, runtime: dict[str, Any]) -> None: @@ -617,7 +630,7 @@ def capability_digest() -> bytes: if capability_digest() != capability: raise RuntimeError("a repeated start changed the control capability") self.require_entries(self.state, RUNNING_ENTRIES, "after a repeated start") - self.require_processes((int(runtime["pid"]),), "after a repeated start") + self.require_processes((recorded_pid(runtime),), "after a repeated start") def check_warm_state(self) -> None: # Managed arguments keep their whitespace, and a later request sees the @@ -709,7 +722,7 @@ def check_deprovision_while_running(self, runtime: dict[str, Any]) -> None: self.require_entries(self.state, RUNNING_ENTRIES, "after deprovision refused") if (self.state / RUNTIME).read_bytes() != record: raise RuntimeError("a refused deprovision changed the runtime record") - self.require_processes((int(runtime["pid"]),), "after deprovision refused") + self.require_processes((recorded_pid(runtime),), "after deprovision refused") result = self.exec("/bin/cat", f"--arg={STATE_PATH}", expected=None) self.require_output( result, STATE_CONTENT, "the state after deprovision refused" @@ -743,22 +756,23 @@ def stop(self, runtime: dict[str, Any], layer_digest: str) -> None: ) def check_crash(self, runtime: dict[str, Any]) -> None: - # OpenVMM exits without a stop, so its runtime record goes stale. + # OpenVMM exits without a stop. The next transition finds that it + # exited, reports it once, and clears its runtime record. kill_openvmm(runtime, self.timeout, self.root.name) self.require_processes((), "after OpenVMM was killed") if sys.platform == "win32" and control_endpoint_present(runtime): raise RuntimeError("the control pipe outlived OpenVMM") - stale = b"sandbox runtime state is stale because the OpenVMM process" for operation, diagnostic in ( - ("exec", stale), - ("stop", stale), - ("start", ALREADY_RUNNING), + ("exec", STALE), + ("stop", b"sandbox is not running"), ): self.invoke(operation, state=self.state, expected=1, diagnostic=diagnostic) self.require_entries( - self.state, RUNNING_ENTRIES, f"after {operation} with stale state" + self.state, CRASHED_ENTRIES, f"after {operation} of a crashed sandbox" ) - self.require_processes((), "after the stale-state requests") + if control_endpoint_present(runtime): + raise RuntimeError("the crashed sandbox kept its control endpoint") + self.require_processes((), "after the requests of a crashed sandbox") def check_deprovision(self) -> None: self.copy_evidence(self.state / LOG, "openvmm.log") @@ -811,7 +825,6 @@ def clean_up(self) -> list[Exception]: errors.append(error) # Every OpenVMM process whose arguments name the fixture is the # acceptance's own, even one that no runtime record names. - ended = False try: for pid in openvmm_processes(self.root.name): with contextlib.suppress(OSError): @@ -821,18 +834,14 @@ def clean_up(self) -> list[Exception]: if time.monotonic() >= deadline: raise RuntimeError(f"OpenVMM processes {list(strays)} survived") time.sleep(0.05) - ended = True except Exception as error: errors.append(error) for state, label in states: try: self.copy_evidence(state / LOG, f"{label}openvmm.log") (state / FOREIGN_NAME).unlink(missing_ok=True) - if ended and not (state / RUNTIME).exists(): - # With no fixture OpenVMM left, runtime files that no record - # names are stale, and deprovision refuses them. - (state / CAPABILITY).unlink(missing_ok=True) - (state / SOCKET).unlink(missing_ok=True) + # Deprovision ends a start that was interrupted, and clears the + # runtime record of an OpenVMM that exited. result = self.run_cli("deprovision", state=state) if result.returncode != 0: raise RuntimeError( diff --git a/scripts/test_microvm_tests.py b/scripts/test_microvm_tests.py index 282d9a127..c3dfe0fac 100644 --- a/scripts/test_microvm_tests.py +++ b/scripts/test_microvm_tests.py @@ -971,13 +971,21 @@ def _running(self, state: Path) -> tuple[dict[str, object], _CliResult | None]: if not (state / "runtime.json").is_file(): return {}, self._error("sandbox is not running") runtime = json.loads((state / "runtime.json").read_text(encoding="utf-8")) - if not self.running.get(runtime["pid"], False): + if not self.running.get(runtime["process"]["pid"], False): + # The transition that finds that OpenVMM exited clears its record. + if "crash-keeps-runtime" not in self.faults: + self._clear_runtime(state) return {}, self._error( "sandbox runtime state is stale because the OpenVMM process is " "not running" ) return runtime, None + @staticmethod + def _clear_runtime(state: Path) -> None: + for name in ("control.capability", "control.sock", "runtime.json"): + (state / name).unlink(missing_ok=True) + def _run( self, state: Path | None, values: dict[str, str], _: list[str] ) -> _CliResult: @@ -1005,7 +1013,7 @@ def _provision( if (state / "config.json").exists(): return self._error("sandbox is already provisioned") state.mkdir(parents=True, exist_ok=True) - (state / "lifecycle.lock").touch() + (state / "sandbox.lock").touch() (state / "config.json").write_text( json.dumps( { @@ -1024,8 +1032,15 @@ def _start( refusal = self._missing(state) if refusal is not None: return refusal - if (state / "runtime.json").exists() and "duplicate-start" not in self.faults: - return self._error("sandbox is already running or has stale runtime state") + runtime_path = state / "runtime.json" + if runtime_path.exists() and "duplicate-start" not in self.faults: + runtime = json.loads(runtime_path.read_text(encoding="utf-8")) + if self.running.get(runtime["process"]["pid"], False): + return self._error( + "sandbox is already running or has stale runtime state" + ) + # A start replaces the record of an OpenVMM that exited. + self._clear_runtime(state) if "unserialized-starts" in self.faults: # Without the lock, both overlapping starts pass the check above # before either records its OpenVMM, and both launch one (#464). @@ -1086,10 +1101,18 @@ def _launch(self, state: Path) -> _CliResult: (state / "runtime.json").write_text( json.dumps( { - "format": 1, - "pid": pid, - "start_time": pid * 7, - "control_endpoint": endpoint, + "format": 4, + "launch": f"{pid:032x}", + "process": {"pid": pid, "startTime": pid * 7, "boot": "b" * 32}, + "endpoint": endpoint, + "runtime": { + "openvmm": "sha256:" + "1" * 64, + "kernel": "sha256:" + "2" * 64, + "initramfs": "sha256:" + "3" * 64, + "bootContract": 2, + "controlContract": "nvx-microvm-v2-control-v2", + "controlSession": 1, + }, } ), encoding="utf-8", @@ -1174,7 +1197,7 @@ def _stop(self, state: Path | None, _: dict[str, str], __: list[str]) -> _CliRes runtime, refusal = self._running(state) if refusal is not None: return refusal - pid = cast(int, runtime["pid"]) + pid = cast(int, cast(dict[str, object], runtime["process"])["pid"]) if "stop-leaks-process" not in self.faults: self.terminate(pid) (state / "runtime.json").unlink() @@ -1223,18 +1246,19 @@ def _deprovision( runtime_path = state / "runtime.json" if runtime_path.is_file(): runtime = json.loads(runtime_path.read_text(encoding="utf-8")) - if self.running.get(runtime.get("pid"), False): + if self.running.get(runtime.get("process", {}).get("pid"), False): return self._error("sandbox must be stopped before deprovision") if "cleanup-deprovision-fails" in self.faults and self.probed: return self._error("injected deprovision failure") for name in ( + "launch.json", "runtime.json", "control.capability", "control.sock", "outcome.json", "openvmm.log", "config.json", - "lifecycle.lock", + "sandbox.lock", ): (state / name).unlink(missing_ok=True) unknown = sorted(path.name for path in state.iterdir()) @@ -1251,7 +1275,7 @@ def processes(self, _: str) -> tuple[int, ...]: return tuple(sorted(pid for pid, running in self.running.items() if running)) def endpoint_present(self, runtime: dict[str, object]) -> bool: - return runtime["control_endpoint"] in self.endpoints.values() + return runtime["endpoint"] in self.endpoints.values() def terminate(self, pid: int) -> None: self.running[pid] = False @@ -1291,13 +1315,13 @@ class PublicSandboxLifecycleTests(unittest.TestCase): "stop", "exec", "stop", - # Restart with overlapping starts, crash, and stale runtime state. + # Restart with overlapping starts, then a crash, which the next + # transition reports. "start", "start", "exec", "exec", "stop", - "start", # Deprovision. "deprovision", "deprovision", @@ -1423,8 +1447,8 @@ def test_acceptance_detects_lifecycle_regressions(self): "run-starts-workload": "run returned 0, expected 125", "failed-start-keeps-runtime": ( "after a refused managed start: the state directory holds " - "['config.json', 'lifecycle.lock', 'openvmm.log', 'outcome.json', " - "'runtime.json']" + "['config.json', 'openvmm.log', 'outcome.json', 'runtime.json', " + "'sandbox.lock']" ), "failed-start-hides-status": ( "start returned 1, expected 1 with 'error: OpenVMM exited during " @@ -1450,6 +1474,9 @@ def test_acceptance_detects_lifecycle_regressions(self): ), "restart-loses-state": "the restarted sandbox's state returned status 1", "unserialized-starts": "overlapping public starts returned status 0", + "crash-keeps-runtime": ( + "after exec of a crashed sandbox: the state directory holds" + ), "deprovision-removes-foreign": "deprovision returned 0, expected 1", } for fault, message in regressions.items(): @@ -1482,7 +1509,7 @@ def test_acceptance_detects_a_leaked_report_staging_file(self): message = str(raised.exception) for fragment in ( "after a refused managed start: the state directory holds " - "['.tmpAbC123', 'config.json', 'lifecycle.lock', 'openvmm.log']", + "['.tmpAbC123', 'config.json', 'openvmm.log', 'sandbox.lock']", "sandbox state directory contains files not owned by NVX: .tmpAbC123", "fixture preserved for recovery", ): @@ -1553,10 +1580,9 @@ def processes(marker: str) -> tuple[int, ...]: return (child.pid,) if child.poll() is None else () runtime: dict[str, object] = { - "format": 1, - "pid": child.pid, - "start_time": 1, - "control_endpoint": "unused", + "format": 4, + "process": {"pid": child.pid, "startTime": 1, "boot": "b" * 32}, + "endpoint": "unused", } try: with patch.object( @@ -1586,7 +1612,7 @@ def test_control_endpoint_presence_follows_the_endpoint(self): from multiprocessing.connection import Listener name = f"openvmm-microvm-test-{os.getpid()}" - runtime: dict[str, object] = {"control_endpoint": f"//./pipe/{name}"} + runtime: dict[str, object] = {"endpoint": f"//./pipe/{name}"} listener = Listener(rf"\\.\pipe\{name}", family="AF_PIPE") try: self.assertTrue( @@ -1598,7 +1624,7 @@ def test_control_endpoint_presence_follows_the_endpoint(self): return with tempfile.TemporaryDirectory() as temporary: endpoint = Path(temporary) / "control.sock" - runtime = {"control_endpoint": str(endpoint)} + runtime = {"endpoint": str(endpoint)} self.assertFalse(sandbox_lifecycle_tests.control_endpoint_present(runtime)) endpoint.touch() self.assertTrue(sandbox_lifecycle_tests.control_endpoint_present(runtime)) From aaf8f6084fbb02a9753aee459df00c774502c88c Mon Sep 17 00:00:00 2001 From: Enrique Saurez Date: Thu, 22 Oct 2026 13:40:31 -0700 Subject: [PATCH 2/6] scripts: carry guest build provenance in setup bundles A managed start reads the runtime identity of the kernel and the Alpine initramfs from their build provenance, so a host whose guest artifacts come from a guest bundle, as a Windows host's do, needs it too. The bundle now carries vmlinux.provenance.json and initramfs.provenance.json, with their checksums, and the Windows setup requires and installs them. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: aa7b9fda-3774-4817-aa98-af05f8d50215 --- scripts/setup/README.md | 4 +++- scripts/setup/setup-linux-mshv.sh | 12 ++++++------ scripts/setup/setup-windows-whp.ps1 | 2 ++ 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/scripts/setup/README.md b/scripts/setup/README.md index e08ac5912..535f890e8 100644 --- a/scripts/setup/README.md +++ b/scripts/setup/README.md @@ -222,7 +222,9 @@ sh scripts/setup/setup-linux-mshv.sh \ ``` The destination must be absent or empty. The bundle contains the kernel, -initramfs, package manifest, checkout revision, and SHA-256 checksums. +initramfs, package manifest, the build provenance of the kernel and the Alpine +initramfs, which managed sandboxes read as their runtime identity, the checkout +revision, and SHA-256 checksums. Use `--bundle-only` with `--guest-bundle` to package artifacts produced by a separate `build-guest` invocation without provisioning or rebuilding the host. diff --git a/scripts/setup/setup-linux-mshv.sh b/scripts/setup/setup-linux-mshv.sh index 0ec44d67c..cb7ae3932 100644 --- a/scripts/setup/setup-linux-mshv.sh +++ b/scripts/setup/setup-linux-mshv.sh @@ -337,8 +337,8 @@ build_nvx() { check_guest_bundle() { bundle_path=$1 [ -d "$bundle_path" ] || die "guest bundle not found: $bundle_path" - for name in vmlinux vmlinux.config initramfs.cpio.gz \ - initramfs.cpio.gz.packages.json initramfs-ubuntu.cpio.gz \ + for name in vmlinux vmlinux.config vmlinux.provenance.json initramfs.cpio.gz \ + initramfs.cpio.gz.packages.json initramfs.provenance.json initramfs-ubuntu.cpio.gz \ initramfs-ubuntu.cpio.gz.packages.json ubuntu-distro.erofs \ ubuntu-distro.erofs.manifest.json initramfs-azurelinux.cpio.gz \ initramfs-azurelinux.cpio.gz.packages.json REVISION SHA256SUMS; do @@ -362,8 +362,8 @@ create_guest_bundle() { die "guest bundle directory is not empty: $bundle_path" fi mkdir -p "$bundle_path" - for name in vmlinux vmlinux.config initramfs.cpio.gz \ - initramfs.cpio.gz.packages.json initramfs-ubuntu.cpio.gz \ + for name in vmlinux vmlinux.config vmlinux.provenance.json initramfs.cpio.gz \ + initramfs.cpio.gz.packages.json initramfs.provenance.json initramfs-ubuntu.cpio.gz \ initramfs-ubuntu.cpio.gz.packages.json ubuntu-distro.erofs \ ubuntu-distro.erofs.manifest.json initramfs-azurelinux.cpio.gz \ initramfs-azurelinux.cpio.gz.packages.json; do @@ -374,8 +374,8 @@ create_guest_bundle() { git -C "$workspace" rev-parse HEAD >"${bundle_path}/REVISION" ( cd "$bundle_path" - sha256sum vmlinux vmlinux.config initramfs.cpio.gz \ - initramfs.cpio.gz.packages.json initramfs-ubuntu.cpio.gz \ + sha256sum vmlinux vmlinux.config vmlinux.provenance.json initramfs.cpio.gz \ + initramfs.cpio.gz.packages.json initramfs.provenance.json initramfs-ubuntu.cpio.gz \ initramfs-ubuntu.cpio.gz.packages.json ubuntu-distro.erofs \ ubuntu-distro.erofs.manifest.json initramfs-azurelinux.cpio.gz \ initramfs-azurelinux.cpio.gz.packages.json REVISION >SHA256SUMS diff --git a/scripts/setup/setup-windows-whp.ps1 b/scripts/setup/setup-windows-whp.ps1 index f5d2416c5..71f5ef2fd 100644 --- a/scripts/setup/setup-windows-whp.ps1 +++ b/scripts/setup/setup-windows-whp.ps1 @@ -198,8 +198,10 @@ $RustupHome = Join-Path $ToolRoot "rustup" $RequiredGuestArtifacts = @( "vmlinux", "vmlinux.config", + "vmlinux.provenance.json", "initramfs.cpio.gz", "initramfs.cpio.gz.packages.json", + "initramfs.provenance.json", "initramfs-ubuntu.cpio.gz", "initramfs-ubuntu.cpio.gz.packages.json", "ubuntu-distro.erofs", From 4e1cccecde50fb6c9f13b1345a74d17d98b85278 Mon Sep 17 00:00:00 2001 From: Enrique Saurez Date: Thu, 22 Oct 2026 13:40:31 -0700 Subject: [PATCH 3/6] nvx-rs: open the store's files without waiting on a FIFO The lock file was opened for writing only, which waits for a reader when the path names a FIFO, before the deadline of the lock applies. The runtime record, the launch journal, and the capability were opened for reading, which waits for a writer, and read until the writer closed them, while the sandbox was locked. The opens no longer wait, and a lock, a record, or a capability that is not a regular file is refused before anything is read from it, as the CLI checked of its own records before it moved onto the store. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: aa7b9fda-3774-4817-aa98-af05f8d50215 --- nvx-rs/src/host/store/files.rs | 81 +++++++++++++++++++++------ nvx-rs/src/host/store/mod.rs | 4 +- nvx-rs/src/host/store/record.rs | 5 +- nvx-rs/src/host/store/tests.rs | 97 +++++++++++++++++++++++++++++++++ 4 files changed, 166 insertions(+), 21 deletions(-) diff --git a/nvx-rs/src/host/store/files.rs b/nvx-rs/src/host/store/files.rs index 23f786bd3..5e0581d48 100644 --- a/nvx-rs/src/host/store/files.rs +++ b/nvx-rs/src/host/store/files.rs @@ -12,6 +12,11 @@ pub(super) fn io_error(context: String) -> impl FnOnce(io::Error) -> Error { move |error| Error::io(context).with_source(error) } +/// Returns the error of a lock, a record, or a capability that is not a regular file. +fn not_regular() -> io::Error { + io::Error::other("is not a regular file") +} + /// Mode of the store's files on Unix: readable and writable by the current user only. #[cfg(unix)] const PRIVATE_FILE_MODE: u32 = 0o600; @@ -51,36 +56,57 @@ pub(super) fn create_new(path: &Path) -> io::Result { /// exist. /// /// On Unix, a lock and the checks of the locked file need no read access, so the file is opened -/// only for writing. The umask may have cleared even the current user's permission to write it, -/// whether this or another call created it, so a lock file of the current user that the user can -/// open for writing no more is given its mode again. +/// only for writing. The open does not wait, as it would for a FIFO without a reader, and a lock +/// file that is not a regular file is refused. The umask may have cleared even the current user's +/// permission to write it, whether this or another call created it, so a lock file of the current +/// user that the user can open for writing no more is given its mode again. pub(super) fn open_lock(path: &Path) -> io::Result { let open = || { - private_options() - .read(cfg!(windows)) - .create(true) - .truncate(false) - .open(path) + let mut options = private_options(); + options.read(cfg!(windows)).create(true).truncate(false); + #[cfg(unix)] + { + use std::os::unix::fs::OpenOptionsExt; + + // These flags replace those of `private_options`. + options.custom_flags( + (nix::fcntl::OFlag::O_NOFOLLOW | nix::fcntl::OFlag::O_NONBLOCK).bits(), + ); + } + options.open(path) }; #[cfg(target_os = "linux")] - let file = match open() { + let opened = match open() { Err(error) if error.kind() == io::ErrorKind::PermissionDenied => { platform::restore_private_file_mode(path, PRIVATE_FILE_MODE)?; - open()? + open() } - opened => opened?, + opened => opened, }; #[cfg(not(target_os = "linux"))] - let file = open()?; + let opened = open(); #[cfg(unix)] { use std::os::unix::fs::PermissionsExt; - if file.metadata()?.permissions().mode() & PRIVATE_FILE_MODE != PRIVATE_FILE_MODE { + // A FIFO without a reader, or a socket, refuses an open that does not wait. + let file = match opened { + Err(error) if error.raw_os_error() == Some(nix::errno::Errno::ENXIO as i32) => { + return Err(not_regular()); + } + opened => opened?, + }; + let metadata = file.metadata()?; + if !metadata.file_type().is_file() { + return Err(not_regular()); + } + if metadata.permissions().mode() & PRIVATE_FILE_MODE != PRIVATE_FILE_MODE { file.set_permissions(fs::Permissions::from_mode(PRIVATE_FILE_MODE))?; } + Ok(file) } - Ok(file) + #[cfg(not(unix))] + opened } /// Atomically replaces `path` with `contents`, readable only by the current user. @@ -112,16 +138,35 @@ pub(super) fn write_private(path: &Path, contents: &[u8]) -> Result<()> { /// Reads the file at `path`, the store's `description`, or returns `None` if it does not exist. /// -/// A file that is not private to the current user is refused, since others could have written -/// it: on Windows, a file's own access control list decides who may open it, whatever the store's -/// directory allows. +/// The open does not wait, as it would on Unix for a FIFO without a writer, and a file that is +/// not a regular file is refused before anything is read from it, as a FIFO whose writer keeps +/// it open would keep a read waiting. A file that is not private to the current user is refused, +/// since others could have written it: on Windows, a file's own access control list decides who +/// may open it, whatever the store's directory allows. pub(super) fn read_private(path: &Path, description: &str) -> Result>> { let failed = || io_error(format!("cannot read the {description} {}", path.display())); - let mut file = match File::open(path) { + let mut options = OpenOptions::new(); + options.read(true); + #[cfg(unix)] + { + use std::os::unix::fs::OpenOptionsExt; + + // Reads of a regular file do not depend on the flag. + options.custom_flags(nix::fcntl::OFlag::O_NONBLOCK.bits()); + } + let mut file = match options.open(path) { Ok(file) => file, Err(error) if error.kind() == io::ErrorKind::NotFound => return Ok(None), + // A socket refuses an open. + #[cfg(unix)] + Err(error) if error.raw_os_error() == Some(nix::errno::Errno::ENXIO as i32) => { + return Err(failed()(not_regular())); + } Err(error) => return Err(failed()(error)), }; + if !file.metadata().map_err(failed())?.file_type().is_file() { + return Err(failed()(not_regular())); + } if let Some(problem) = platform::private_file_problem(&file).map_err(failed())? { return Err(Error::io(format!( "the {description} {} {problem}", diff --git a/nvx-rs/src/host/store/mod.rs b/nvx-rs/src/host/store/mod.rs index 9fa7f65fe..b2787f9d0 100644 --- a/nvx-rs/src/host/store/mod.rs +++ b/nvx-rs/src/host/store/mod.rs @@ -25,7 +25,9 @@ //! whose list allows anyone else. [`SandboxStore::create`] and [`SandboxStore::open`] refuse an //! existing directory that is not private, rather than change it, since others could have placed //! records in it, or still hold its files open. The store names its files by their paths, so the -//! directories above it must be ones that only the user, or an administrator, may change. +//! directories above it must be ones that only the user, or an administrator, may change. A lock, +//! a record, or a capability that is not a regular file, such as a FIFO, is refused at once, +//! rather than waited on while the sandbox is locked. //! Records are replaced atomically, so readers that hold no lock, such as an execution, see whole //! records. The store holds runtime records only: configuration belongs to its caller, which may //! keep it in the same directory under a name of its own. diff --git a/nvx-rs/src/host/store/record.rs b/nvx-rs/src/host/store/record.rs index a14e62350..990f8c812 100644 --- a/nvx-rs/src/host/store/record.rs +++ b/nvx-rs/src/host/store/record.rs @@ -313,8 +313,9 @@ impl Record for LaunchJournal { /// /// A record of another format fails before its fields are read, with a message that names its /// format: the store reads format [`FORMAT`] only, and migrates no other. A record file that is -/// not private to the current user fails as well, since others could have written it. Every -/// failure to read or check the record is an [`ErrorKind::Io`](crate::ErrorKind::Io) error. +/// not private to the current user fails as well, since others could have written it, and so +/// does one that is not a regular file, without waiting on it. Every failure to read or check the +/// record is an [`ErrorKind::Io`](crate::ErrorKind::Io) error. pub(super) fn read(path: &Path) -> Result> { let description = T::DESCRIPTION; let Some(text) = read_private(path, description)? else { diff --git a/nvx-rs/src/host/store/tests.rs b/nvx-rs/src/host/store/tests.rs index af2b5b3da..a99e1ab7d 100644 --- a/nvx-rs/src/host/store/tests.rs +++ b/nvx-rs/src/host/store/tests.rs @@ -335,6 +335,103 @@ fn a_lock_file_replaced_while_waiting_locks_nothing() { waiter.join().unwrap(); } +#[cfg(unix)] +#[test] +fn a_lock_file_that_is_not_a_regular_file_fails_at_once() { + use std::os::unix::fs::OpenOptionsExt; + + use nix::sys::stat::Mode; + + let (_directory, store) = store(); + let path = store.path(LOCK_NAME); + nix::unistd::mkfifo(&path, Mode::S_IRUSR | Mode::S_IWUSR).unwrap(); + let refused = |error: Error| { + assert_eq!(error.message(), format!("cannot lock {}", path.display())); + let cause = std::error::Error::source(&error).map(ToString::to_string); + assert_eq!(cause.as_deref(), Some("is not a regular file"), "{error}"); + }; + // An open of a FIFO for writing that waits would wait for a reader, so the lock is taken on a + // thread that the test can leave behind. + let (sender, receiver) = mpsc::channel(); + thread::spawn({ + let store = store.clone(); + move || sender.send(store.lock(None).map(drop)).unwrap() + }); + let locked = receiver + .recv_timeout(Duration::from_secs(10)) + .expect("the lock waits for a reader of the FIFO"); + refused(locked.unwrap_err()); + // A FIFO that has a reader opens, and is refused all the same. + let _reader = fs::OpenOptions::new() + .read(true) + .custom_flags(nix::fcntl::OFlag::O_NONBLOCK.bits()) + .open(&path) + .unwrap(); + refused(store.lock(Some(Instant::now())).unwrap_err()); +} + +#[cfg(unix)] +#[test] +fn records_and_capabilities_that_are_not_regular_files_fail_at_once() { + use std::os::unix::fs::OpenOptionsExt; + + use nix::sys::stat::Mode; + + let (_directory, store) = store(); + for (name, description) in [ + (RUNTIME_NAME, "sandbox runtime record"), + (JOURNAL_NAME, "sandbox launch journal"), + (CAPABILITY_NAME, "sandbox control capability"), + ] { + let path = store.path(name); + nix::unistd::mkfifo(&path, Mode::S_IRUSR | Mode::S_IWUSR).unwrap(); + // An open of a FIFO for reading that waits would wait for a writer, and a read of one would + // wait until its writer closes it, so the file is read on a thread that the test can leave + // behind. + let refused = || { + let (sender, receiver) = mpsc::channel(); + thread::spawn({ + let store = store.clone(); + move || { + let read = match name { + RUNTIME_NAME => store.runtime().map(drop), + JOURNAL_NAME => store.journal().map(drop), + _ => store.read_capability().map(drop), + }; + sender.send(read).unwrap(); + } + }); + let error = receiver + .recv_timeout(Duration::from_secs(10)) + .unwrap_or_else(|_| panic!("the read of the {description} waits on the FIFO")) + .unwrap_err(); + assert_eq!( + error.message(), + format!("cannot read the {description} {}", path.display()) + ); + let cause = std::error::Error::source(&error).map(ToString::to_string); + assert_eq!(cause.as_deref(), Some("is not a regular file"), "{error}"); + }; + refused(); + // A FIFO that has a writer opens at once, and is refused all the same, before a read waits + // for the writer to close it. + let reader = fs::OpenOptions::new() + .read(true) + .custom_flags(nix::fcntl::OFlag::O_NONBLOCK.bits()) + .open(&path) + .unwrap(); + let writer = fs::OpenOptions::new() + .write(true) + .custom_flags(nix::fcntl::OFlag::O_NONBLOCK.bits()) + .open(&path) + .unwrap(); + drop(reader); + refused(); + drop(writer); + fs::remove_file(&path).unwrap(); + } +} + #[cfg(target_os = "linux")] #[test] fn the_store_is_private_to_the_current_user() { From 4be119d6fa384252de155568794c3c9902d04e17 Mon Sep 17 00:00:00 2001 From: Enrique Saurez Date: Thu, 22 Oct 2026 13:40:31 -0700 Subject: [PATCH 4/6] nvx-rs: start only the root's OpenVMM, and create state directories in the store The install metadata of the artifact root identifies the root's own OpenVMM, so a managed start refuses another one that NVX_OPENVMM names, which the runtime record would otherwise misname. Provision creates a missing state directory only through the store, even when an existing one is removed meanwhile, so that it is private to the user on every host. The documentation of the format switch also asks to end an OpenVMM that an interrupted start of an earlier version left without a record. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: aa7b9fda-3774-4817-aa98-af05f8d50215 --- doc/run.md | 4 +++- nvx-rs/README.md | 23 ++++++++++++------- nvx-rs/src/host/cli/sandbox/lifecycle.rs | 28 ++++++++++++++++++------ nvx-rs/src/host/cli/sandbox/mod.rs | 20 ++++++++++++++--- nvx-rs/tests/cli.rs | 26 ++++++++++++++++++++++ 5 files changed, 82 insertions(+), 19 deletions(-) diff --git a/doc/run.md b/doc/run.md index 666c4f829..d4572ba68 100644 --- a/doc/run.md +++ b/doc/run.md @@ -1048,7 +1048,9 @@ were rebuilt or replaced, is reported, and must be stopped with the artifacts that started it. The store reads runtime records of its own format only, so stop every running sandbox with the NVX version that started it before you switch versions: a version with another format fails every transition of such -a sandbox with a message that names the format of its record. +a sandbox with a message that names the format of its record. An OpenVMM that +an interrupted `start` of an earlier version left behind has no record, and +that version asked for it to be ended by hand: end it before the switch too. `provision`, `start`, `stop`, and `deprovision` hold the store's lock, `sandbox.lock` in the state directory, until they return, so the transitions diff --git a/nvx-rs/README.md b/nvx-rs/README.md index cc9643fe7..79d34d208 100644 --- a/nvx-rs/README.md +++ b/nvx-rs/README.md @@ -310,9 +310,9 @@ extracted release, with them in `guest/` and `bin/`. `SOURCE-MANIFEST.json`; otherwise the command fails and asks for `--root`. - The `NVX_OPENVMM` environment variable names an OpenVMM executable to run instead of the root's, such as `nvx-fake-openvmm` in tests. A relative path is - relative to the working directory. It does not change the - [runtime identity](#runtime-identity), which the root's install metadata - gives. + relative to the working directory. A managed `start` refuses it, since the + root's install metadata identifies the root's own OpenVMM alone; see + [Runtime identity](#runtime-identity). ### `artifacts` @@ -440,15 +440,20 @@ root's install metadata, and no artifact is hashed: `start` needs that metadata, and so does every other transition but `provision` while the store records a start of the sandbox: each fails when -the metadata is missing or names no valid digest. They drive only a VM that the -same identity started: one that other artifacts started, as before they were -rebuilt or replaced, is reported with the parts that differ, and must be -stopped with the artifacts that started it. +the metadata is missing or names no valid digest. `start` runs the root's own +OpenVMM, which the metadata identifies, and refuses another one that +`NVX_OPENVMM` names. The transitions drive only a VM that the same identity +started: one that other artifacts started, as before they were rebuilt or +replaced, is reported with the parts that differ, and must be stopped with the +artifacts that started it. The store reads runtime records of its format alone. A sandbox that a build with another format started, such as one whose `runtime.json` has format 1, fails every transition with a message that names that format: stop every -running sandbox with the build that started it before you switch builds. +running sandbox with the build that started it before you switch builds. An +OpenVMM that a start of such a build left behind when it was interrupted has no +record, and that build asked for it to be ended by hand: end it before the +switch too, since this build finds no VM in that sandbox. #### Guest features @@ -489,6 +494,8 @@ guest agents of NVX advertise bits 0, 2, and 4 in sandbox mode. so that `start` launches the sandbox again, where `nvx.py` refused to. - `provision` refuses an existing state directory that is not private to the current user, as the store refuses it, rather than make it private. +- A managed `start` refuses an OpenVMM that `NVX_OPENVMM` names instead of the + root's; see [Runtime identity](#runtime-identity). - The `nvx_layer` token of the kernel command line names the image format of the distro layer, as in `nvx_layer=distro,0xd0003000,erofs:UUID`, and the guest refuses the form without a format that `nvx.py` wrote; see diff --git a/nvx-rs/src/host/cli/sandbox/lifecycle.rs b/nvx-rs/src/host/cli/sandbox/lifecycle.rs index 6e4c01567..1c80c9e0a 100644 --- a/nvx-rs/src/host/cli/sandbox/lifecycle.rs +++ b/nvx-rs/src/host/cli/sandbox/lifecycle.rs @@ -163,14 +163,17 @@ fn make_directories_once(path: &PurePath, mode: u32) -> Result { } } -/// Creates the state directory at `path`, as provision does. An existing path keeps the errors of -/// `Path.mkdir(exist_ok=True)`, which an existing directory does not raise, and stays as it is: the -/// store then refuses a directory that is not private to the current user. A new directory is -/// created as the store creates it, private to the current user, below parents that are created -/// as needed. +/// Creates the state directory at `path`, as provision does. An existing directory stays as it is, +/// and the store then refuses one that is not private to the current user; any other existing +/// path fails as `Path.mkdir(exist_ok=True)` fails for it. A new directory is created as the +/// store creates it, private to the current user, below parents that are created as needed, even +/// when an existing one goes meanwhile: nothing else creates the directory itself. pub(crate) fn create_state_directory(path: &PurePath) -> Result { + if fs::is_dir(path.as_path()) { + return Ok(()); + } if fs::lexists(path.as_path()) { - return make_directories(path, 0o700); + return Err(os_error(&already_exists(), path)); } if let Some(parent) = path.parents().next().filter(|parent| parent != path) { make_directories(&parent, 0o777)?; @@ -180,6 +183,17 @@ pub(crate) fn create_state_directory(path: &PurePath) -> Result { .map_err(error_with_cause) } +/// Returns the error with which `os.mkdir` fails for a path that exists. +fn already_exists() -> std::io::Error { + #[cfg(windows)] + let error = std::io::Error::from_raw_os_error(183); + #[cfg(unix)] + let error = std::io::Error::from_raw_os_error(nix::errno::Errno::EEXIST as i32); + #[cfg(not(any(windows, unix)))] + let error = std::io::Error::from(std::io::ErrorKind::AlreadyExists); + error +} + /// Resolves the state directory at `path`, and creates it with `create`, as /// `_prepare_state_directory` does. It must be a directory and not a symbolic link. pub(crate) fn prepare_state_directory(path: &str, create: bool) -> Result { @@ -478,7 +492,7 @@ fn start_locked( // The plan takes the format and identity of the distro layer from its binding. let plan = launch.bound_plan(&binding)?; let [executable, kernel, initrd] = artifacts.require()?; - let runtime = artifacts.runtime_identity()?; + let runtime = artifacts.launch_identity()?; let capability = random_bytes::()?; if capability == [0; CAPABILITY_LEN] { return Err(Exit::error( diff --git a/nvx-rs/src/host/cli/sandbox/mod.rs b/nvx-rs/src/host/cli/sandbox/mod.rs index d1278dabb..c6bfd840c 100644 --- a/nvx-rs/src/host/cli/sandbox/mod.rs +++ b/nvx-rs/src/host/cli/sandbox/mod.rs @@ -19,7 +19,7 @@ use std::thread; use std::time::Duration; use super::args::{Parser, Receiver, Spec, Syntax, invalid_choice, invalid_value}; -use super::artifacts::{Located, locate}; +use super::artifacts::{Located, OPENVMM_VARIABLE, locate}; use super::{Exit, Result, output}; use crate::host::artifacts::{Guest, KERNEL_NAME}; use crate::host::network::policy::compile_policy_file; @@ -560,14 +560,28 @@ impl Artifacts<'_> { /// Returns the runtime identity of the artifacts, which the install metadata of the root /// names, as [`ArtifactDir::runtime_identity`](crate::host::artifacts::ArtifactDir::runtime_identity) - /// reads it. `NVX_OPENVMM` replaces the OpenVMM executable that runs, but not its identity, - /// which stays the root's. + /// reads it. pub(crate) fn runtime_identity(&self) -> Result { locate(self.root)? .dir .runtime_identity() .map_err(error_with_cause) } + + /// Returns the runtime identity under which a managed start launches OpenVMM, as + /// [`runtime_identity`](Self::runtime_identity) does. The install metadata names the root's + /// own OpenVMM alone, so the start refuses another one that `NVX_OPENVMM` names, which the + /// runtime record would otherwise misname. + pub(crate) fn launch_identity(&self) -> Result { + let Located { dir, openvmm } = locate(self.root)?; + if openvmm != dir.openvmm() { + return Err(Exit::error(format!( + "a managed sandbox runs the OpenVMM of the artifact root, which its install \ + metadata identifies, and not the one that {OPENVMM_VARIABLE} names" + ))); + } + dir.runtime_identity().map_err(error_with_cause) + } } /// Returns the error that prints the message of `error`, followed by the I/O error or other diff --git a/nvx-rs/tests/cli.rs b/nvx-rs/tests/cli.rs index fae746eac..094f2a2d8 100644 --- a/nvx-rs/tests/cli.rs +++ b/nvx-rs/tests/cli.rs @@ -1080,6 +1080,32 @@ fn runtime_records_of_earlier_formats_fail_and_name_their_format() { assert_eq!(transition(&root, "deprovision", "state", &[]), succeeded()); } +#[test] +fn a_managed_start_runs_only_the_openvmm_of_the_root() { + // The install metadata of the root identifies its own OpenVMM alone, so a start refuses + // another one, which the runtime record would misname. + let root = populated_root(); + provisioned(&root, "state"); + let environment = BTreeMap::from([("NVX_OPENVMM".to_owned(), FAKE_OPENVMM.to_owned())]); + let output = nvx_rs(&root, &lifecycle("start", &root, "state"), &environment); + assert_eq!( + (output.status.code(), text(&output.stderr)), + failed( + "a managed sandbox runs the OpenVMM of the artifact root, which its install metadata \ + identifies, and not the one that NVX_OPENVMM names" + ) + ); + assert_eq!( + state_entries(&root, "state"), + ["config.json", "sandbox.lock"] + ); + // The transitions that launch nothing do not run the OpenVMM that it names. + assert_eq!(transition(&root, "start", "state", &[]), succeeded()); + let output = nvx_rs(&root, &lifecycle("stop", &root, "state"), &environment); + assert!(output.status.success(), "{}", text(&output.stderr)); + assert_eq!(transition(&root, "deprovision", "state", &[]), succeeded()); +} + #[test] fn stop_reports_its_timeout_and_leaves_the_vm_running() { let root = populated_root(); From 0e458e5e24080ed39e39b56b038de1a1cb6f6430 Mon Sep 17 00:00:00 2001 From: Enrique Saurez Date: Thu, 22 Oct 2026 14:57:49 -0700 Subject: [PATCH 5/6] nvx-rs: read install metadata without waiting on a FIFO The runtime identity was read from a release's SHA256SUMS or a checkout's build provenance with an open that waits for a writer when the path names a FIFO, and with a read that waits until the writer closes it. A sandbox's transitions read the identity while they hold the store's lock, so the open no longer waits, and a file of metadata that is not a regular file is refused before anything is read from it, as the store's own files are. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: aa7b9fda-3774-4817-aa98-af05f8d50215 --- nvx-rs/src/host/artifacts.rs | 108 ++++++++++++++++++++++++++++++++++- 1 file changed, 106 insertions(+), 2 deletions(-) diff --git a/nvx-rs/src/host/artifacts.rs b/nvx-rs/src/host/artifacts.rs index 109b13cac..0de8de471 100644 --- a/nvx-rs/src/host/artifacts.rs +++ b/nvx-rs/src/host/artifacts.rs @@ -626,7 +626,8 @@ impl ArtifactDir { /// The artifacts of a release therefore have the same identity in the release and in a /// checkout that installed it. Fails with /// [`ErrorKind::Unavailable`](crate::ErrorKind::Unavailable) when the metadata is missing or - /// unreadable, or names no valid digest of an artifact. + /// unreadable, or names no valid digest of an artifact. A file of metadata that is not a + /// regular file, such as a FIFO, is refused without being waited on. pub fn runtime_identity(&self) -> Result { let [openvmm, kernel, initramfs] = match self.layout { Layout::Release => self.listed_identities()?, @@ -711,6 +712,11 @@ fn sha256_identity(digest: &str) -> Option { /// Reads the file of install metadata at `path`, which messages call `description`, as text of /// at most [`MAX_METADATA_LEN`] bytes. A missing file fails with `advice` on how to provide it. +/// +/// The open does not wait, as it would on Unix for a FIFO without a writer, and a file that is +/// not a regular file is refused before anything is read from it, as a FIFO whose writer keeps it +/// open would keep a read waiting: a sandbox's transitions read the metadata while they hold its +/// lock. fn read_metadata(path: &Path, description: &str, advice: &str) -> Result { let unreadable = |error: io::Error| { if error.kind() == io::ErrorKind::NotFound { @@ -723,7 +729,35 @@ fn read_metadata(path: &Path, description: &str, advice: &str) -> Result .with_source(error) } }; - let file = fs::File::open(path).map_err(unreadable)?; + let not_regular = || { + Error::unavailable(format!( + "the {description} {} is not a regular file", + path.display() + )) + }; + let mut options = fs::OpenOptions::new(); + options.read(true); + #[cfg(unix)] + { + use std::os::unix::fs::OpenOptionsExt; + + // Reads of a regular file do not depend on the flag. + options.custom_flags(nix::fcntl::OFlag::O_NONBLOCK.bits()); + } + let opened = options.open(path); + #[cfg(unix)] + { + // A socket refuses an open. + if let Err(error) = &opened + && error.raw_os_error() == Some(nix::errno::Errno::ENXIO as i32) + { + return Err(not_regular()); + } + } + let file = opened.map_err(unreadable)?; + if !file.metadata().map_err(unreadable)?.file_type().is_file() { + return Err(not_regular()); + } let mut text = String::new(); file.take(MAX_METADATA_LEN + 1) .read_to_string(&mut text) @@ -1398,6 +1432,76 @@ mod tests { ); } + #[cfg(unix)] + #[test] + fn metadata_that_is_not_a_regular_file_fails_at_once() { + use std::os::unix::fs::OpenOptionsExt; + use std::sync::mpsc; + use std::thread; + use std::time::Duration; + + use nix::sys::stat::Mode; + + let installed = tempfile::tempdir().unwrap(); + checkout(installed.path()); + record_provenance( + installed.path(), + &["a", "b", "c"].map(|digit| digit.repeat(64)), + ); + let repository = ArtifactDir::repository(installed.path()).unwrap(); + let extracted = tempfile::tempdir().unwrap(); + release(extracted.path()); + let released = ArtifactDir::release(extracted.path()).unwrap(); + for (artifacts, path, description) in [ + ( + &repository, + repository.artifact(KERNEL_PROVENANCE_NAME), + "kernel build provenance", + ), + ( + &released, + released.root().join(CHECKSUMS_NAME), + "release checksum list", + ), + ] { + let _ = fs::remove_file(&path); + nix::unistd::mkfifo(&path, Mode::S_IRUSR | Mode::S_IWUSR).unwrap(); + // An open of a FIFO for reading that waits would wait for a writer, and a read of one + // would wait until its writer closes it, so the identity is read on a thread that the + // test can leave behind. + let refused = || { + let (sender, receiver) = mpsc::channel(); + let artifacts = artifacts.clone(); + thread::spawn(move || sender.send(artifacts.runtime_identity()).unwrap()); + let error = receiver + .recv_timeout(Duration::from_secs(10)) + .unwrap_or_else(|_| panic!("the read of the {description} waits on the FIFO")) + .unwrap_err(); + assert_eq!(error.kind(), ErrorKind::Unavailable); + assert_eq!( + error.message(), + format!("the {description} {} is not a regular file", path.display()) + ); + }; + refused(); + // A FIFO that has a writer opens at once, and is refused all the same, before a read + // waits for the writer to close it. + let reader = fs::OpenOptions::new() + .read(true) + .custom_flags(nix::fcntl::OFlag::O_NONBLOCK.bits()) + .open(&path) + .unwrap(); + let writer = fs::OpenOptions::new() + .write(true) + .custom_flags(nix::fcntl::OFlag::O_NONBLOCK.bits()) + .open(&path) + .unwrap(); + drop(reader); + refused(); + drop(writer); + } + } + #[cfg(feature = "bundled")] #[test] fn the_bundle_is_a_compatible_release() { From be28d6310ee1a89bccf248a1046dfd8bf7cbe459 Mon Sep 17 00:00:00 2001 From: Enrique Saurez Date: Thu, 22 Oct 2026 15:27:31 -0700 Subject: [PATCH 6/6] nvx-rs: refuse a store lock file that is a link On Windows, the store opened its lock file through a link, and checked and locked the file that the link names. A lock taken through a link to a file that a launch replaces, such as the capability, held the replaced file, so the next transition locked the replacement and ran beside the first. The CLI's own lock refused reparse points before the CLI moved onto the store. The lock file is now opened as itself on Windows, and a reparse point, or a file that is not a regular file, is refused through the opened handle on every platform. On Unix, where O_NOFOLLOW refuses a link, the link is reported as not a regular file too. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: aa7b9fda-3774-4817-aa98-af05f8d50215 --- nvx-rs/src/host/filesystem.rs | 4 +-- nvx-rs/src/host/store/files.rs | 57 ++++++++++++++++++++++------------ nvx-rs/src/host/store/mod.rs | 7 +++-- nvx-rs/src/host/store/tests.rs | 31 ++++++++++++++++++ 4 files changed, 76 insertions(+), 23 deletions(-) diff --git a/nvx-rs/src/host/filesystem.rs b/nvx-rs/src/host/filesystem.rs index 7347d9e87..77a5b1fec 100644 --- a/nvx-rs/src/host/filesystem.rs +++ b/nvx-rs/src/host/filesystem.rs @@ -773,14 +773,14 @@ fn check_policy_paths(root: &Path, paths: &[PathBuf], action: &str) -> Result bool { +pub(crate) fn is_reparse_point(metadata: &fs::Metadata) -> bool { use std::os::windows::fs::MetadataExt; metadata.file_attributes() & 0x400 != 0 } #[cfg(not(windows))] /// Reports no reparse points on hosts whose metadata has no such Windows attribute. -fn is_reparse_point(_metadata: &fs::Metadata) -> bool { +pub(crate) fn is_reparse_point(_metadata: &fs::Metadata) -> bool { false } diff --git a/nvx-rs/src/host/store/files.rs b/nvx-rs/src/host/store/files.rs index 5e0581d48..42a0c18a1 100644 --- a/nvx-rs/src/host/store/files.rs +++ b/nvx-rs/src/host/store/files.rs @@ -5,7 +5,7 @@ use std::io::{self, Read, Write}; use std::path::Path; use crate::error::{Error, Result}; -use crate::host::platform; +use crate::host::{filesystem, platform}; /// Builds an error mapper that keeps an I/O source under `context`. pub(super) fn io_error(context: String) -> impl FnOnce(io::Error) -> Error { @@ -55,11 +55,16 @@ pub(super) fn create_new(path: &Path) -> io::Result { /// Opens the lock file at `path`, creating it readable only by the current user if it does not /// exist. /// +/// A lock file that is not a regular file is refused, and so is a link, which is not followed, or +/// on Windows any other reparse point: a lock taken through a link would hold the file that the +/// link names, which a launch may replace while the lock is held, so that the next transition +/// would lock the replacement. +/// /// On Unix, a lock and the checks of the locked file need no read access, so the file is opened -/// only for writing. The open does not wait, as it would for a FIFO without a reader, and a lock -/// file that is not a regular file is refused. The umask may have cleared even the current user's -/// permission to write it, whether this or another call created it, so a lock file of the current -/// user that the user can open for writing no more is given its mode again. +/// only for writing. The open does not wait, as it would for a FIFO without a reader. The umask +/// may have cleared even the current user's permission to write it, whether this or another call +/// created it, so a lock file of the current user that the user can open for writing no more is +/// given its mode again. pub(super) fn open_lock(path: &Path) -> io::Result { let open = || { let mut options = private_options(); @@ -73,6 +78,14 @@ pub(super) fn open_lock(path: &Path) -> io::Result { (nix::fcntl::OFlag::O_NOFOLLOW | nix::fcntl::OFlag::O_NONBLOCK).bits(), ); } + #[cfg(windows)] + { + use std::os::windows::fs::OpenOptionsExt; + + /// `FILE_FLAG_OPEN_REPARSE_POINT`, which opens a link as itself. + const OPEN_REPARSE_POINT: u32 = 0x0020_0000; + options.custom_flags(OPEN_REPARSE_POINT); + } options.open(path) }; #[cfg(target_os = "linux")] @@ -87,26 +100,32 @@ pub(super) fn open_lock(path: &Path) -> io::Result { let opened = open(); #[cfg(unix)] { - use std::os::unix::fs::PermissionsExt; - - // A FIFO without a reader, or a socket, refuses an open that does not wait. - let file = match opened { - Err(error) if error.raw_os_error() == Some(nix::errno::Errno::ENXIO as i32) => { - return Err(not_regular()); - } - opened => opened?, - }; - let metadata = file.metadata()?; - if !metadata.file_type().is_file() { + use nix::errno::Errno; + + // `O_NOFOLLOW` refuses a link, and an open that does not wait refuses a FIFO without a + // reader, or a socket. + if let Err(error) = &opened + && [Errno::ELOOP, Errno::ENXIO] + .into_iter() + .any(|errno| error.raw_os_error() == Some(errno as i32)) + { return Err(not_regular()); } + } + let file = opened?; + let metadata = file.metadata()?; + if !metadata.file_type().is_file() || filesystem::is_reparse_point(&metadata) { + return Err(not_regular()); + } + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + if metadata.permissions().mode() & PRIVATE_FILE_MODE != PRIVATE_FILE_MODE { file.set_permissions(fs::Permissions::from_mode(PRIVATE_FILE_MODE))?; } - Ok(file) } - #[cfg(not(unix))] - opened + Ok(file) } /// Atomically replaces `path` with `contents`, readable only by the current user. diff --git a/nvx-rs/src/host/store/mod.rs b/nvx-rs/src/host/store/mod.rs index b2787f9d0..d8272751b 100644 --- a/nvx-rs/src/host/store/mod.rs +++ b/nvx-rs/src/host/store/mod.rs @@ -27,7 +27,9 @@ //! records in it, or still hold its files open. The store names its files by their paths, so the //! directories above it must be ones that only the user, or an administrator, may change. A lock, //! a record, or a capability that is not a regular file, such as a FIFO, is refused at once, -//! rather than waited on while the sandbox is locked. +//! rather than waited on while the sandbox is locked. So is a lock file that is a link, or on +//! Windows any other reparse point, since a lock taken through it would hold the file that it +//! names, which a launch may replace while the lock is held. //! Records are replaced atomically, so readers that hold no lock, such as an execution, see whole //! records. The store holds runtime records only: configuration belongs to its caller, which may //! keep it in the same directory under a name of its own. @@ -357,7 +359,8 @@ impl SandboxStore { /// Fails with [`ErrorKind::TimedOut`](crate::ErrorKind::TimedOut) when the deadline passes /// first. A lock file that is removed or replaced while this waits locks nothing, so the /// lock is then taken again. A lock file that is not private to the current user is refused, - /// as a record is, since others could hold its lock. + /// as a record is, since others could hold its lock, and one that is a link, or not a regular + /// file, is refused at once. pub fn lock(&self, deadline: Option) -> Result { let path = self.path(LOCK_NAME); let failed = |error| io_error(format!("cannot lock {}", path.display()))(error); diff --git a/nvx-rs/src/host/store/tests.rs b/nvx-rs/src/host/store/tests.rs index a99e1ab7d..4ce0fb309 100644 --- a/nvx-rs/src/host/store/tests.rs +++ b/nvx-rs/src/host/store/tests.rs @@ -370,6 +370,37 @@ fn a_lock_file_that_is_not_a_regular_file_fails_at_once() { refused(store.lock(Some(Instant::now())).unwrap_err()); } +#[test] +fn a_lock_file_that_is_a_link_fails_at_once() { + let (_directory, store) = store(); + let path = store.path(LOCK_NAME); + // A lock taken through a link to a file that a launch replaces, such as the capability, would + // hold the replaced file, and the next transition would lock its replacement. + let target = store.capability_path(); + fs::write(&target, CAPABILITY).unwrap(); + #[cfg(unix)] + let linked = std::os::unix::fs::symlink(&target, &path); + #[cfg(windows)] + let linked = std::os::windows::fs::symlink_file(&target, &path); + if linked.is_err() { + // Windows lets only Developer Mode or a privilege create a symbolic link. + eprintln!("skipped: this host does not let the test create a symbolic link"); + return; + } + let error = store.lock(None).unwrap_err(); + assert_eq!(error.message(), format!("cannot lock {}", path.display())); + let cause = std::error::Error::source(&error).map(ToString::to_string); + assert_eq!(cause.as_deref(), Some("is not a regular file"), "{error}"); + // The link and the file that it names stay as they are. + assert!( + fs::symlink_metadata(&path) + .unwrap() + .file_type() + .is_symlink() + ); + assert_eq!(fs::read(&target).unwrap(), CAPABILITY); +} + #[cfg(unix)] #[test] fn records_and_capabilities_that_are_not_regular_files_fail_at_once() {