Skip to content
Merged
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
32 changes: 21 additions & 11 deletions nvx-rs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1203,9 +1203,10 @@ with the record's image, an EROFS image whose UUID it checks if the record
expects one, or a GPT disk whose GUID it checks if the record expects one, its
upper, a tmpfs or the scratch with the record's journal policy, and its overlay
mode, and with the `RootTemplate` that the guest's own configuration holds,
which its template captured: the runtime directory, the block devices, and the
timeout, but waits for no device past the activation's deadline, which the
adapter passes, so that the application ends by then.
which its template captured: the runtime directory, the block devices, the
timeout, and the host filesystem device's virtio-fs tag, but waits for no device
past the activation's deadline, which the adapter passes, so that the
application ends by then.

For a GPT disk, it first asks the kernel to reread the disk's partition table
with `BLKRRPART`, since the kernel created the block devices of partitions for
Expand All @@ -1219,15 +1220,24 @@ every execution runs, while without one each execution names its own identity,
which `Entry::check` checks. With the record's generated machine ID, it then
generates one, once, with `MachineId::generate`, after the repair's reseed, so
that no two clones share it, to `MACHINE_ID_FILE` in the template's runtime
directory, and it binds the runtime's files for every execution with
`Entry::bind_runtime` in the caller's mount namespace: that file read-only over
the root's `/etc/machine-id`, or, with the image's own machine ID, nothing; the
entry checks the record's hostname, which every execution's entry takes with
`Entry::hostname`. It returns the `Applied` root and workload, whose `teardown`
directory. With the record's export, it mounts the host export through that
filesystem device with `Root::mount_export`, read-write, at `hostfs/root` in the
runtime directory, outside the root, since the record names no mode and OpenVMM
limits each child to its own access; with its network's DNS server, it writes a
resolver file that names it, mode 0444, to `RESOLVER_FILE` in the runtime
directory. It then binds the runtime's files for every execution with one
`Entry::bind_runtime` in the caller's mount namespace: the machine ID's file
read-only over the root's `/etc/machine-id` and the resolver file over its
`/etc/resolv.conf`, or, with neither, nothing; the entry checks the record's
hostname, which every execution's entry takes with `Entry::hostname`. It returns
the `Applied` root and workload, from which `take_export` hands the adapter the
export's `SourceRoot`, to map the host's mapping table from once the clone is
ready, and `proxy` the network's proxy endpoint, which the adapter tells its
workloads as at a cold boot, and whose `teardown` closes the export's root and
removes the binds before it tears the root down, as a drop does, or an
`ApplyError`, after which nothing that it mounted or bound stays mounted unless
the error reports the unmounts that failed, and applies none of the record's
export or network.
`ApplyError`, `Export` or `Network` among them, after which nothing that it
mounted or bound stays mounted unless the error reports the unmounts that
failed, and applies none of the record's listener publication.

Once the adapter passed the activation what the session received while the
application ran, which came before Ready, `Activation::complete` opens the fence
Expand Down
63 changes: 36 additions & 27 deletions nvx-rs/src/guest/clone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,33 +147,41 @@
//! record expects one, or a GPT disk whose GUID it checks if the record expects one, its upper, a
//! tmpfs or the scratch, with the record's journal policy, and its overlay mode, and with the
//! inputs that the guest takes from its own configuration, which its template captured, a
//! [`RootTemplate`]: the runtime directory, the block devices, and how long the assembly waits for
//! each, but never past the activation's deadline, which the adapter passes, so that the
//! application ends by then. For a GPT disk, it first asks the kernel to reread the disk's
//! partition table, once the distro device appeared, by the same deadline: the kernel created the
//! block devices of partitions from the template's placeholder, when it found it, and creates those
//! of the clone's disk then, from the same read-only bytes that the assembly reads and checks, as
//! at a cold boot. The upper must be fresh: the assembly refuses one whose `upper` or `work`
//! directory exists and is not empty, since a clone's root starts as its image alone, while a cold
//! sandbox's scratch keeps its upper across boots; the check reads at most one entry of each, and
//! no file. If the record has a workload identity, it then checks its account in the assembled
//! root, before Ready, as NVX's init checks it at boot: the identity is
//! [`Identity::new`](super::entry::Identity::new) of the record's user and group IDs, without
//! supplementary groups or capabilities, in existing-account mode, and every execution runs as
//! it. Without one, the adapter gives each execution its own identity, which
//! [`RootTemplate`]: the runtime directory, the block devices, the host filesystem device's
//! virtio-fs tag, and how long the assembly waits for each block device, but never past the
//! activation's deadline, which the adapter passes, so that the application ends by then. For a GPT
//! disk, it first asks the kernel to reread the disk's partition table, once the distro device
//! appeared, by the same deadline: the kernel created the block devices of partitions from the
//! template's placeholder, when it found it, and creates those of the clone's disk then, from the
//! same read-only bytes that the assembly reads and checks, as at a cold boot. The upper must be
//! fresh: the assembly refuses one whose `upper` or `work` directory exists and is not empty, since
//! a clone's root starts as its image alone, while a cold sandbox's scratch keeps its upper across
//! boots; the check reads at most one entry of each, and no file. If the record has a workload
//! identity, it then checks its account in the assembled root, before Ready, as NVX's init checks
//! it at boot: the identity is [`Identity::new`](super::entry::Identity::new) of the record's user
//! and group IDs, without supplementary groups or capabilities, in existing-account mode, and every
//! execution runs as it. Without one, the adapter gives each execution its own identity, which
//! [`Entry::check`](super::entry::Entry::check) checks. If the record asks for a generated machine
//! ID, it then generates one, once, with
//! [`MachineId::generate`](super::entry::MachineId::generate), after the repair's reseed, so that
//! no two clones of a template share it, to [`MACHINE_ID_FILE`] in the template's runtime
//! directory, and it binds the runtime's files into the root for every execution, with
//! [`Entry::bind_runtime`](super::entry::Entry::bind_runtime), in the caller's mount namespace: the
//! generated machine ID's file, read-only over the root's `/etc/machine-id`, or, with the image's
//! own machine ID, nothing. The entry checks the record's hostname first, which every execution's
//! entry then takes. It returns the [`Applied`] root and workload, which the adapter keeps, and
//! whose [`teardown`](Applied::teardown) removes the binds before it tears the root down, or an
//! [`ApplyError`], after which nothing that it mounted or bound stays mounted, unless the error
//! holds the unmounts that failed. It applies none of the record's other settings: its export,
//! and its network.
//! directory. If the record has an export, it mounts the host export through the template's
//! filesystem device, with [`Root::mount_export`](root::Root::mount_export), read-write, at
//! `hostfs/root` in the runtime directory, outside the root, where only the guest's root user
//! reaches it: the record names no mode, and OpenVMM limits each of the export's children to its
//! own access. If the record's network names a DNS server, it writes a resolver file that names
//! it to [`RESOLVER_FILE`] in the runtime directory. It then binds the runtime's files into the
//! root for every execution, with one [`Entry::bind_runtime`](super::entry::Entry::bind_runtime),
//! in the caller's mount namespace: the generated machine ID's file, read-only over the root's
//! `/etc/machine-id`, and the resolver file, read-only over the root's `/etc/resolv.conf`, or,
//! with neither, nothing. The entry checks the record's hostname first, which every execution's
//! entry then takes. It returns the [`Applied`] root and workload, which the adapter keeps, with
//! the export's root, from which the adapter maps the host's mapping table once the clone is
//! ready, and the network's proxy endpoint, which the adapter tells its workloads as at a cold
//! boot, and whose [`teardown`](Applied::teardown) closes the export's root and removes the binds
//! before it tears the root down, or an [`ApplyError`], after which nothing that it mounted or
//! bound stays mounted, unless the error holds the unmounts that failed. It applies none of the
//! record's other settings: the publication of the workload's listeners.
//!
//! # Example
//!
Expand Down Expand Up @@ -260,8 +268,9 @@
//! namespace, as [`root::assemble`] does: for a GPT disk, it asks the kernel to reread the disk's
//! partition table, which needs `CAP_SYS_ADMIN`; it mounts the root, reads the account databases
//! below it, and, if it fails, tears it down. It starts no process, and generates at most one ID,
//! a machine ID that the record asks for, whose bind over the root's `/etc/machine-id` needs
//! Linux 6.8 or later.
//! a machine ID that the record asks for. It mounts the host export of a record that has one, and
//! writes the machine ID's and the resolver's files in the runtime directory, whose binds over
//! the root's `/etc/machine-id` and `/etc/resolv.conf` need Linux 6.8 or later.

mod activation;
mod mounts;
Expand All @@ -285,8 +294,8 @@ use super::time::{

pub use self::activation::{
Activation, ActivationAction, ActivationCause, ActivationFailure, ActivationInput,
ActivationState, Applied, AppliedTeardownError, ApplyError, MACHINE_ID_FILE, RootTemplate,
apply,
ActivationState, Applied, AppliedTeardownError, ApplyError, MACHINE_ID_FILE, NetworkError,
RESOLVER_FILE, RootTemplate, apply,
};
pub use self::mounts::{Mounted, Origin};
pub use self::repair::{
Expand Down
9 changes: 6 additions & 3 deletions nvx-rs/src/guest/clone/activation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ use crate::activation::{
};

pub use self::apply::{
Applied, AppliedTeardownError, ApplyError, MACHINE_ID_FILE, RootTemplate, apply,
Applied, AppliedTeardownError, ApplyError, MACHINE_ID_FILE, NetworkError, RESOLVER_FILE,
RootTemplate, apply,
};

/// The activation of a repaired clone: the state machine, without I/O and independent of any
Expand Down Expand Up @@ -88,8 +89,10 @@ pub use self::apply::{
/// /// work.
/// fn acknowledge(&mut self);
/// /// Keeps the clone's activated root and workload: the identity, if any, in which it runs
/// /// every execution, with the hostname of every execution, and the root, with the runtime's
/// /// binds on it, which `Applied::teardown` removes before it tears the root down.
/// /// every execution, with the hostname of every execution, the root, with the runtime's
/// /// binds on it, which `Applied::teardown` removes before it tears the root down, and the
/// /// export's root, which `Applied::take_export` hands over, from which it maps the host's
/// /// mapping table.
/// fn keep(&mut self, applied: Applied);
/// /// Queues Ready, which reports `activation` beside the agent's own identities, such as its
/// /// build.
Expand Down
Loading
Loading