Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 29 additions & 4 deletions nvx-rs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ nvx-rs/
│ ├── error.rs nvx::Error, its ErrorKind, and its stable ErrorCode
│ ├── exec/ the execution engine: the streams, credit, and outcome of an execution's two
│ │ sides, without I/O, threads, or features, for any framing and host
│ ├── host/ feature `host` (default): artifacts, launch, network and egress policy files,
│ │ filesystem, NVXS connection, session, process, platform, VM instances,
│ │ sandbox stores, templates' manifests, compatibility keys, store, and leases,
│ │ outcome reports, file seals, and the sandbox launch profile of
│ ├── host/ feature `host` (default): per-domain errors, artifacts, launch, network and
│ │ egress policy files, filesystem, NVXS connection, session, process, platform,
│ │ VM instances, sandbox stores, templates' manifests, compatibility keys, store,
│ │ and leases, outcome reports, file seals, and the sandbox launch profile of
│ │ `nvx-rs sandbox`, its distro and scratch bindings, and its prepared launches;
│ │ with feature `cli`, the `nvx-rs` CLI
│ ├── testing/ feature `testing`: the fake OpenVMM
Expand Down Expand Up @@ -117,6 +117,7 @@ changes only when the module's purpose does.
| [`nvx::uuid`](#nvxuuid) | UUIDs, such as those of GPT disks and filesystems, in their canonical text and in GPT's layout |
| [`nvx::Hypervisor`](#nvxhypervisor) | KVM, MSHV, or WHP, with its serde form behind the `serde` feature |
| [`nvx::exec`](#nvxexec) | The execution engine: the streams, credit, and outcome of an execution's two sides, without I/O, for any framing |
| [`nvx::host::error`](#nvxhosterror) | Per-domain errors of the host's operations, each with kinds of its own: configuration, launch, transport, snapshot, restore, and report |
| [`nvx::host::artifacts`](#nvxhostartifacts) | Locating the OpenVMM executable and the guest artifacts that a VM runs, and their runtime identity |
| [`nvx::host::launch`](#nvxhostlaunch) | `LaunchSpec` and `VmmLaunch`: the OpenVMM command line of a managed microVM, its checks, and the inputs that it shares with an adapter's launch |
| [`nvx::host::network`](#nvxhostnetwork) | The one lowering of a network posture into OpenVMM options, for the CLI and external orchestrators |
Expand Down Expand Up @@ -378,6 +379,27 @@ The adapter chooses one byte window per stream, or frames charged against one
pool that stdout and stderr share, and the maximum chunk, in `Limits`; a `Key`'s
generation tells stale messages from foreign ones.

### `nvx::host::error`

The errors of the host's operations, one for each domain, which share no
classification with `nvx::Error` or with one another:

| Error | Domain | Kinds |
| --- | --- | --- |
| `ConfigError` | The checks of a VM's configuration and of the values in it, which read nothing on the host | `Invalid`, `Unsupported` |
| `LaunchError` | A VM's launch, stop, verification, and reattachment, and the probe of its hypervisor | `Unavailable`, `Refused`, `Io`, `Exited`, `RuntimeMismatch`, `StillRunning` |
| `TransportError` | The connection to a VM's control endpoint, and the session over it | `TimedOut`, `Exited`, `Refused`, `Closed`, `Reset`, `Protocol`, `Io` |
| `SnapshotError` | A snapshot's capture, opening, and acceptance of a VM | `Io`, `Malformed`, `Incompatible`, `CaptureFailed` |
| `RestoreError` | A VM's restore from a snapshot | `OperationFailed`, `Expired`, `Cancelled`, `Exited`, `Protocol` |
| `ReportError` | The reading of OpenVMM's outcome report of a VM | `Io`, `Malformed` |

Each error has a kind from an enum of its own domain, such as
`ConfigErrorKind`, which its `kind` method returns; a message that callers can
show as is, which it displays; and an optional source, which
`std::error::Error::source` returns. The kinds are non-exhaustive, so that each
domain can classify new failures. The host's other operations return
`nvx::Error`, as [Errors](#errors) describes.

### `nvx::host::artifacts`

`Artifacts` (also `nvx::Artifacts`): the OpenVMM executable, guest kernel, and
Expand Down Expand Up @@ -2504,6 +2526,9 @@ optional cause.
Messages call an instance `sandbox NAME`, after the name that `Instance::create`
or `Instance::open` gives it.

The per-domain errors of [`nvx::host::error`](#nvxhosterror) are separate: each
has the kinds of its own domain, and none has a code.

### Error codes

An `ErrorCode` is finer than an `ErrorKind`: callers tell errors apart by their
Expand Down
57 changes: 57 additions & 0 deletions nvx-rs/public-api/linux.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1680,6 +1680,63 @@ fn nvx::host::artifacts::check_manifest
fn nvx::host::artifacts::openvmm_executable
mod nvx::host::cli
fn nvx::host::cli::main
mod nvx::host::error
struct nvx::host::error::ConfigError
method nvx::host::error::ConfigError::kind
method nvx::host::error::ConfigError::new
method nvx::host::error::ConfigError::with_source
enum nvx::host::error::ConfigErrorKind
variant nvx::host::error::ConfigErrorKind::Invalid
variant nvx::host::error::ConfigErrorKind::Unsupported
struct nvx::host::error::LaunchError
method nvx::host::error::LaunchError::kind
method nvx::host::error::LaunchError::new
method nvx::host::error::LaunchError::with_source
enum nvx::host::error::LaunchErrorKind
variant nvx::host::error::LaunchErrorKind::Exited
variant nvx::host::error::LaunchErrorKind::Io
variant nvx::host::error::LaunchErrorKind::Refused
variant nvx::host::error::LaunchErrorKind::RuntimeMismatch
variant nvx::host::error::LaunchErrorKind::StillRunning
variant nvx::host::error::LaunchErrorKind::Unavailable
struct nvx::host::error::ReportError
method nvx::host::error::ReportError::kind
method nvx::host::error::ReportError::new
method nvx::host::error::ReportError::with_source
enum nvx::host::error::ReportErrorKind
variant nvx::host::error::ReportErrorKind::Io
variant nvx::host::error::ReportErrorKind::Malformed
struct nvx::host::error::RestoreError
method nvx::host::error::RestoreError::kind
method nvx::host::error::RestoreError::new
method nvx::host::error::RestoreError::with_source
enum nvx::host::error::RestoreErrorKind
variant nvx::host::error::RestoreErrorKind::Cancelled
variant nvx::host::error::RestoreErrorKind::Exited
variant nvx::host::error::RestoreErrorKind::Expired
variant nvx::host::error::RestoreErrorKind::OperationFailed
variant nvx::host::error::RestoreErrorKind::Protocol
struct nvx::host::error::SnapshotError
method nvx::host::error::SnapshotError::kind
method nvx::host::error::SnapshotError::new
method nvx::host::error::SnapshotError::with_source
enum nvx::host::error::SnapshotErrorKind
variant nvx::host::error::SnapshotErrorKind::CaptureFailed
variant nvx::host::error::SnapshotErrorKind::Incompatible
variant nvx::host::error::SnapshotErrorKind::Io
variant nvx::host::error::SnapshotErrorKind::Malformed
struct nvx::host::error::TransportError
method nvx::host::error::TransportError::kind
method nvx::host::error::TransportError::new
method nvx::host::error::TransportError::with_source
enum nvx::host::error::TransportErrorKind
variant nvx::host::error::TransportErrorKind::Closed
variant nvx::host::error::TransportErrorKind::Exited
variant nvx::host::error::TransportErrorKind::Io
variant nvx::host::error::TransportErrorKind::Protocol
variant nvx::host::error::TransportErrorKind::Refused
variant nvx::host::error::TransportErrorKind::Reset
variant nvx::host::error::TransportErrorKind::TimedOut
mod nvx::host::filesystem
struct nvx::host::filesystem::Bind
field nvx::host::filesystem::Bind::child
Expand Down
57 changes: 57 additions & 0 deletions nvx-rs/public-api/windows.txt
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,63 @@ fn nvx::host::artifacts::check_manifest
fn nvx::host::artifacts::openvmm_executable
mod nvx::host::cli
fn nvx::host::cli::main
mod nvx::host::error
struct nvx::host::error::ConfigError
method nvx::host::error::ConfigError::kind
method nvx::host::error::ConfigError::new
method nvx::host::error::ConfigError::with_source
enum nvx::host::error::ConfigErrorKind
variant nvx::host::error::ConfigErrorKind::Invalid
variant nvx::host::error::ConfigErrorKind::Unsupported
struct nvx::host::error::LaunchError
method nvx::host::error::LaunchError::kind
method nvx::host::error::LaunchError::new
method nvx::host::error::LaunchError::with_source
enum nvx::host::error::LaunchErrorKind
variant nvx::host::error::LaunchErrorKind::Exited
variant nvx::host::error::LaunchErrorKind::Io
variant nvx::host::error::LaunchErrorKind::Refused
variant nvx::host::error::LaunchErrorKind::RuntimeMismatch
variant nvx::host::error::LaunchErrorKind::StillRunning
variant nvx::host::error::LaunchErrorKind::Unavailable
struct nvx::host::error::ReportError
method nvx::host::error::ReportError::kind
method nvx::host::error::ReportError::new
method nvx::host::error::ReportError::with_source
enum nvx::host::error::ReportErrorKind
variant nvx::host::error::ReportErrorKind::Io
variant nvx::host::error::ReportErrorKind::Malformed
struct nvx::host::error::RestoreError
method nvx::host::error::RestoreError::kind
method nvx::host::error::RestoreError::new
method nvx::host::error::RestoreError::with_source
enum nvx::host::error::RestoreErrorKind
variant nvx::host::error::RestoreErrorKind::Cancelled
variant nvx::host::error::RestoreErrorKind::Exited
variant nvx::host::error::RestoreErrorKind::Expired
variant nvx::host::error::RestoreErrorKind::OperationFailed
variant nvx::host::error::RestoreErrorKind::Protocol
struct nvx::host::error::SnapshotError
method nvx::host::error::SnapshotError::kind
method nvx::host::error::SnapshotError::new
method nvx::host::error::SnapshotError::with_source
enum nvx::host::error::SnapshotErrorKind
variant nvx::host::error::SnapshotErrorKind::CaptureFailed
variant nvx::host::error::SnapshotErrorKind::Incompatible
variant nvx::host::error::SnapshotErrorKind::Io
variant nvx::host::error::SnapshotErrorKind::Malformed
struct nvx::host::error::TransportError
method nvx::host::error::TransportError::kind
method nvx::host::error::TransportError::new
method nvx::host::error::TransportError::with_source
enum nvx::host::error::TransportErrorKind
variant nvx::host::error::TransportErrorKind::Closed
variant nvx::host::error::TransportErrorKind::Exited
variant nvx::host::error::TransportErrorKind::Io
variant nvx::host::error::TransportErrorKind::Protocol
variant nvx::host::error::TransportErrorKind::Refused
variant nvx::host::error::TransportErrorKind::Reset
variant nvx::host::error::TransportErrorKind::TimedOut
mod nvx::host::filesystem
struct nvx::host::filesystem::Bind
field nvx::host::filesystem::Bind::child
Expand Down
74 changes: 74 additions & 0 deletions nvx-rs/src/host/error/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
//! [`ConfigError`]: an error of the checks of a VM's configuration and of the values in it.

use std::fmt;

/// What a [`ConfigError`] found wrong with a value.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum ConfigErrorKind {
/// The value breaks its own rules: it is malformed, such as a label with a space or an address
/// that is not of its form, it lies outside its range, or it combines fields that cannot go
/// together.
Invalid,
/// The value is well formed, but NVX cannot represent or enforce it, such as more egress rules
/// than OpenVMM takes, or a command line longer than its limit.
Unsupported,
}

/// An error of the checks of a VM's configuration, or of a value in it, which read nothing on the
/// host: its [`kind`](Self::kind), a message, which it displays, and an optional source.
///
/// The message is a complete sentence fragment, without a trailing period, which callers can show
/// as is.
#[derive(Debug)]
pub struct ConfigError {
/// What the check found wrong.
kind: ConfigErrorKind,
/// Complete, user-facing description without a trailing period.
message: String,
/// Underlying error, when retaining it aids diagnostics.
source: Option<Box<dyn std::error::Error + Send + Sync + 'static>>,
}

impl ConfigError {
/// Creates an error of `kind`, which `message` describes.
pub fn new(kind: ConfigErrorKind, message: impl Into<String>) -> Self {
Self {
kind,
message: message.into(),
source: None,
}
}

/// Attaches the underlying error that caused this one.
#[must_use]
pub fn with_source(
mut self,
source: impl Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
) -> Self {
self.source = Some(source.into());
self
}

/// Returns what the check found wrong.
pub fn kind(&self) -> ConfigErrorKind {
self.kind
}
}

impl fmt::Display for ConfigError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(&self.message)
}
}

impl std::error::Error for ConfigError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.source
.as_deref()
.map(|source| source as &(dyn std::error::Error + 'static))
}
}

#[cfg(test)]
mod tests;
25 changes: 25 additions & 0 deletions nvx-rs/src/host/error/config/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use std::net::{AddrParseError, Ipv4Addr};

use super::*;

#[test]
fn an_error_has_the_kind_that_it_was_created_with() {
for kind in [ConfigErrorKind::Invalid, ConfigErrorKind::Unsupported] {
assert_eq!(ConfigError::new(kind, "a refused value").kind(), kind);
}
}

#[test]
fn an_error_displays_its_message_and_exposes_its_source() {
let message = "the guest network 10.0.0.256/24 is not an IPv4 address with a prefix length";
let error = ConfigError::new(ConfigErrorKind::Invalid, message);
assert_eq!(error.to_string(), message);
assert!(std::error::Error::source(&error).is_none());

let cause = "10.0.0.256".parse::<Ipv4Addr>().unwrap_err();
let error = error.with_source(cause.clone());
assert_eq!(error.kind(), ConfigErrorKind::Invalid);
assert_eq!(error.to_string(), message);
let source = std::error::Error::source(&error).unwrap();
assert_eq!(source.downcast_ref::<AddrParseError>(), Some(&cause));
}
85 changes: 85 additions & 0 deletions nvx-rs/src/host/error/launch.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
//! [`LaunchError`]: an error of a VM's launch, stop, verification, or reattachment, or of the probe
//! of its hypervisor.

use std::fmt;

/// What a [`LaunchError`] stopped at.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum LaunchErrorKind {
/// A runtime dependency is missing or unusable: the hypervisor, an artifact that the runtime
/// names, or a host that NVX does not support.
Unavailable,
/// The launch refuses its request as given, such as a VM directory that is not private, or a
/// configuration or a snapshot that it cannot launch.
Refused,
/// A host I/O operation failed, such as the creation of the VM's files or the start of its
/// process.
Io,
/// The VM's process exited, as when it fails to start, or when the process that a caller saved
/// is gone.
Exited,
/// The VM runs under another runtime identity than the caller's, so the caller may only stop
/// it, through its verified process.
RuntimeMismatch,
/// The VM's exit was not confirmed by the deadline, so its process may still run.
StillRunning,
}

/// An error of a VM's launch, stop, verification, or reattachment, or of the probe of its
/// hypervisor: its [`kind`](Self::kind), a message, which it displays, and an optional source.
///
/// The message is a complete sentence fragment, without a trailing period, which callers can show
/// as is.
#[derive(Debug)]
pub struct LaunchError {
/// What the operation stopped at.
kind: LaunchErrorKind,
/// Complete, user-facing description without a trailing period.
message: String,
/// Underlying error, when retaining it aids diagnostics.
source: Option<Box<dyn std::error::Error + Send + Sync + 'static>>,
}

impl LaunchError {
/// Creates an error of `kind`, which `message` describes.
pub fn new(kind: LaunchErrorKind, message: impl Into<String>) -> Self {
Self {
kind,
message: message.into(),
source: None,
}
}

/// Attaches the underlying error that caused this one.
#[must_use]
pub fn with_source(
mut self,
source: impl Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
) -> Self {
self.source = Some(source.into());
self
}

/// Returns what the operation stopped at.
pub fn kind(&self) -> LaunchErrorKind {
self.kind
}
}

impl fmt::Display for LaunchError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(&self.message)
}
}

impl std::error::Error for LaunchError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.source
.as_deref()
.map(|source| source as &(dyn std::error::Error + 'static))
}
}

#[cfg(test)]
mod tests;
Loading
Loading