Capabilities
The parts of the Endpoints layer a server holds out, stated in the capabilities object of its info response: the client image store, the registries it pulls from, and each volume mode with what may hold a volume of it; and, for every capability, the endpoints it governs and exactly what the server sends for a request past a capability it does not hold out.
A server serves every endpoint of the Endpoints
layer: it reads every request and answers it as that layer provides.
Some of what a request may ask, a server may not do: keep a client
image store, pull from a registry, keep volumes of a mode, let more
than one party hold a volume at once. Each such part is a
capability. The server states the capabilities it holds out in
the capabilities object of its info response,
and this layer defines that object and binds the server by it: for
every capability, the endpoints it governs, what the server does
where it holds the capability out, and exactly what the server sends
for a request past a capability it does not hold out.
- The object is a fact of the server. The server sends the same
capabilitiesobject in everyinforesponse it sends, on every connection, for as long as it runs. The object does not depend on the identity under which a connection was authorized, on the request, or on the time. - A capability held out is served. A server that holds out a capability serves the endpoints it governs as the pages of the Endpoints layer state, in full, and sends none of the responses this layer defines for the capability’s absence.
- A capability not held out is refused as this layer states. A server that does not hold out a capability answers every request past it with exactly the response this layer names for that endpoint, as the first and only response on the scope, followed by the response finish, serves no channel on the scope, looks nothing up for the request, holds nothing, fetches nothing, deploys nothing and changes nothing. The server sends no error, no other refusal and no bare finish in place of the response named.
- Every page of the Endpoints layer is read with this one. A requirement of an endpoint page that a capability governs holds where the server holds the capability out; where it does not, the response this layer names is the whole of the server’s answer, and the endpoint page’s requirements on what follows the request do not arise.
The object#
The capabilities member of the info response is one JSON object,
read as Notation provides, with the members
tabulated. Every member is optional. An absent member has the meaning
the table gives its absence, and a server that does not hold out a
capability may send the member with that value or omit it; the two
are one statement. The server sends no member this revision does not
define.
| Member | Type | Absent | Governs |
|---|---|---|---|
images_store | boolean | false | images::list, images::stat, images::build, images::delete, images::oci, and the client source of images::check |
images_registries | array of strings | [] | the registries source of images::check, and where a run obtains its image |
volumes_persistent | object, the mode object | every member false: no volume of the mode | the eleven volume endpoints, and the volume mounts of the two runs, for a volume whose mode is persistent |
volumes_ephemeral | object, the mode object | every member false: no volume of the mode | the same, for a volume whose mode is ephemeral |
volumes_read_only | object, the mode object | every member false: no volume of the mode | the same, for a volume whose mode is read_only |
The object is defined by
diverge-sdk-rs/src/provider/endpoints/info/server/response/capabilities.rs:
//! What a provider does, of everything this specification lets a
//! provider do.
use serde::{Deserialize, Serialize};
use super::VolumeCapabilities;
use crate::provider::endpoints::volumes::Mode;
/// Which of the specification's optional parts this provider serves.
///
/// Every provider serves every endpoint's wire — it reads every
/// request and answers it — but not every provider does everything a
/// request may ask: one has no image store, one pulls from no
/// registry, one has no FUSE on its host, one keeps volumes of some
/// modes only, and keeps them more narrowly than the mode allows.
/// Each such part is one member here, and a request past it is not
/// a failure but a refusal the endpoint has a variant of its own for,
/// named below member by member. A caller that read these composes
/// no such request; one that did not reads the refusal.
///
/// # Flat, and tolerant
///
/// One object, every member at the top, each a boolean, a list or an
/// object of booleans; no member nests a capability inside another
/// but the three volume modes, whose object says what may be done
/// with a volume of the mode. A member absent is the capability
/// absent — `false`, the empty list, the object of every member
/// `false` — so a provider states only what it does. A member a
/// reader does not know is ignored: see [`Frame`](super::Frame).
///
/// # No mode is no volumes
///
/// A provider with none of the three volume modes keeps no volumes,
/// and every endpoint of
/// [`volumes`](crate::provider::endpoints::volumes) — the eleven —
/// answers `Unsupported`: nothing is listed, nothing is made, and
/// there is no volume for a run or a build to name. [`volumes`](Self::volumes)
/// is that test.
///
/// # What is not here
///
/// The volume endpoints themselves, which every provider that keeps
/// volumes serves;
/// [`daemons`](crate::provider::endpoints::daemons), which is the
/// provider's configuration and refused as such; and every quantity
/// — how many containers, how much disk — which is a limit, not a
/// capability, and answered where it binds.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
#[derive(schemars::JsonSchema)]
pub struct Capabilities {
/// The provider keeps an image store for its callers: it answers
/// [`images::list`](crate::provider::endpoints::images::list),
/// [`stat`](crate::provider::endpoints::images::stat),
/// [`build`](crate::provider::endpoints::images::build),
/// [`delete`](crate::provider::endpoints::images::delete) and
/// [`oci`](crate::provider::endpoints::images::oci), and an
/// [`images::check`](crate::provider::endpoints::images::check)
/// asking the `client` source looks in it. Absent — `false` —
/// each of the five answers `Unsupported`, and a check asking only
/// `client` answers `NoSource`.
#[serde(default)]
pub images_store: bool,
/// The registries the provider pulls from: each a host as a
/// [`Reference`](crate::shared::containers::request::Reference)
/// names it — `docker.io`, `ghcr.io`, `registry.example.com:5000`
/// — and the whole of them. An
/// [`images::check`](crate::provider::endpoints::images::check)
/// asking the `registries` source consults exactly these, and a
/// reference naming any other is ignored; a run's image whose
/// references name none of these, and that no store the provider
/// has holds, cannot be supplied. Absent — empty — the provider
/// pulls from no registry, and a check asking only `registries`
/// answers `NoSource`.
#[serde(default)]
pub images_registries: Vec<String>,
/// What may be done with a volume in the
/// [`Persistent`](Mode::Persistent) mode. Every member `false` —
/// the object absent — the provider keeps no volume in the mode:
/// no listed volume is in it, and a
/// [`create`](crate::provider::endpoints::volumes::create) or an
/// [`edit`](crate::provider::endpoints::volumes::edit) naming it
/// is answered `ModeUnsupported`. See
/// [`kept`](VolumeCapabilities::kept).
#[serde(default)]
pub volumes_persistent: VolumeCapabilities,
/// What may be done with a volume in the
/// [`Ephemeral`](Mode::Ephemeral) mode. Every member `false`, as
/// for [`volumes_persistent`](Self::volumes_persistent).
#[serde(default)]
pub volumes_ephemeral: VolumeCapabilities,
/// What may be done with a volume in the
/// [`ReadOnly`](Mode::ReadOnly) mode. Every member `false`, as
/// for [`volumes_persistent`](Self::volumes_persistent).
#[serde(default)]
pub volumes_read_only: VolumeCapabilities,
}
impl Capabilities {
/// Whether the provider keeps volumes at all: any mode among the
/// three. `false` is every volume endpoint answering
/// `Unsupported`.
pub fn volumes(&self) -> bool {
self.volumes_persistent.kept() || self.volumes_ephemeral.kept() || self.volumes_read_only.kept()
}
/// What may be done with a volume of `mode` here: `None` for a
/// mode the provider keeps no volumes in — its object every
/// member `false`.
pub fn volume(&self, mode: Mode) -> Option<&VolumeCapabilities> {
let object = match mode {
Mode::Persistent => &self.volumes_persistent,
Mode::Ephemeral => &self.volumes_ephemeral,
Mode::ReadOnly => &self.volumes_read_only,
};
object.kept().then_some(object)
}
}
images_store#
The server keeps a client image store: images the client builds on it by images::build, under names of the client’s, held for the client and for no other identity, as the pages of those endpoints define.
- Held out. The server serves images::list,
images::stat,
images::build,
images::delete and
images::oci as their pages state, and
the
clientsource of images::check is a source the server has. - Not held out. The server has no client image, and the
clientsource of images::check is not a source the server has. The server answers:- an images::list request with exactly
one response, the byte
4and nothing else, and the response finish; it sends no image, no byte2and no byte3; - an images::stat request with exactly
one response, the byte
1and nothing else, and the response finish; - an images::build request with exactly
one response, the byte
4and nothing else, and the response finish; it sends no line of output before it, and builds nothing; - an images::delete request with
exactly one response, the byte
2and nothing else, and the response finish; - an images::oci request with exactly one
response on the main stream, the byte
0and nothing else, and the response finish; it answers no channel the client opens on the scope; - an images::check request that asks
for the
clientsource and not for theregistriessource, or that asks for theregistriessource whereimages_registriesis empty, with exactly one response, the byte2and nothing else, and the response finish; a request that also asks for theregistriessource whereimages_registriesis not empty is answered from that source alone.
- an images::list request with exactly
one response, the byte
- The
images_diskmember of a capacity response. The server sends theimages_diskmember of every capacity response whether or not it holds outimages_store. Where it does not, the member carries any value, and a client does not read it.
images_registries#
The registries the server pulls images from: an array of strings,
each the host of one registry as a reference’s registry member
names it — docker.io, ghcr.io, registry.example.com:5000 — and
every registry the server pulls from is in the array.
- The
registriessource. An images::check request that asks for theregistriessource is answered by consulting exactly the registries in the array that a reference of the request names, as<registry>/<name>@<digest>. A reference naming a registry not in the array is ignored. The source is one the server has when the array is not empty. - A run’s image. A server obtains the image of a containers::agents::run or a containers::tools::run from a registry only where the registry is in the array and a reference of the request names it; it obtains an image from no registry the array does not list.
- Empty. An empty array, or an absent member, states that the
server pulls from no registry. The
registriessource is not a source the server has: an images::check request that asks for it and for no source the server has is answered with exactly one response, the byte2and nothing else, and the response finish; one that also asks for theclientsource whereimages_storeistrueis answered from that source alone. A run’s image is obtained from what the server holds or from the client, and from no registry.
What every server does#
Every server makes every FUSE mount a run request names, and every
mount a client asks for on the fuse-mount channel of a run, and
serves every containers::fuse_serve
request, as those pages state: no capability governs them, and the
capabilities object has no member for them.
The volume modes#
Each of volumes_persistent, volumes_ephemeral and
volumes_read_only is the mode object for
volumes whose mode is, respectively, persistent, ephemeral and
read_only, as volumes::create defines
the three modes: what the server does with a volume of the mode. A
mode object with at least one member true is a mode the server
holds out, and keeps volumes in; a mode object with every member
false — or absent, which is the same statement — is a mode the
server does not hold out.
- Held out. The server keeps volumes of the mode. A volumes::list listing of the server may contain a volume of the mode, a volumes::create request may state the mode, and a volumes::edit request may change a volume into the mode. What a running container, a FUSE serve and an operation may do with a volume of the mode is the mode object.
- Not held out. The server keeps no volume of the mode. No listing of
the server contains a volume of the mode. The server answers a
volumes::create request whose
modeis the mode with exactly one response, the byte2and nothing else, and the response finish, and creates nothing. The server answers a volumes::edit request whose change states the mode — asMode, or asBoth— and names a volume in the client’s listing with exactly one response, the byte3and nothing else, and the response finish, and changes nothing; a request naming a volume not in the listing is answered with the error the edit page states, whatever the change. - None held out. A server that holds out none of the three keeps
no volume at all, and the eleven volume endpoints are not served.
The server answers every request of each with exactly one
response, the byte tabulated and nothing else, and the response
finish; it sends nothing else on the scope, serves no channel, and
neither lists, examines, reads, writes, walks, serves, measures,
creates, edits nor deletes anything for the request. Such a server
has no volume a run can name: a run request whose
volume_mountshas an entry is answered with the error the run pages state for a volume not in the client’s listing, and an images::build request naming a context is answered with the error its page states for a volume not in the client’s listing.
- The
volumes_diskmember of a capacity response. The server sends thevolumes_diskmember of every capacity response whether or not it holds out a mode object. Where it holds out none, the member carries any value, and a client does not read it.
The mode object#
The value of each of volumes_persistent, volumes_ephemeral and
volumes_read_only is one JSON object of five boolean members, each
optional, an absent member being false. The object with every
member false is the mode not held out, and is what an absent mode
member means; every rule below that speaks of a mode object absent
speaks of that object too.
| Member | true states that |
|---|---|
mount | a running container of the client may mount a volume of the mode |
fuse_serve | a volumes::fuse_serve scope of the client may hold a volume of the mode |
mount_many | more than one running container may mount a volume of the mode at once |
fuse_serve_many | more than one FUSE serve may hold a volume of the mode at once |
mount_and_fuse_serve | a running container and a FUSE serve may hold a volume of the mode at once |
- The mode allows at most. A mode allows at most what
volumes::create states for it: a
persistentvolume may be mounted in any number of running containers at once, is held by at most one FUSE serve at a time, and is never held by a FUSE serve and a running container at once; anephemeralor aread_onlyvolume may be held by any number of running containers and FUSE serves at once. A server holds out no more than the mode allows: thevolumes_persistentobject hasfuse_serve_manyandmount_and_fuse_servefalse, absent or present. - Consistency. A server that sends
mount_manytruesendsmounttrue. A server that sendsfuse_serve_manytruesendsfuse_servetrue. A server that sendsmount_and_fuse_servetruesendsmounttrueandfuse_servetrue. - One running container is one holder. A running container that
mounts a volume at several
container_paths of one request is one running container holding the volume. An images::build that names a directory of the volume as its context holds the volume as one running container does, for the life of the build.
The hold#
The server holds a volume for a running container from the moment it
accepts the run request that names the volume until the run ends;
for a FUSE serve from the moment it sends Serving until the
response finish; for a build naming the volume as its context from
the moment it accepts the build request until the response finish;
and to itself for a volumes::stat,
volumes::read,
volumes::write,
volumes::filetree,
volumes::edit or
volumes::delete, an operation,
from the moment it accepts the request until the response finish, a
delete never giving the hold back. Whether a request that would hold
a volume is served is decided by the volume’s mode, by the mode
object the server holds out for the mode, and by who holds the
volume at the time of the request, in the order stated below, the
first rule that applies deciding; the decision and the hold it takes
are one act, between which no other hold of the volume is taken or
given back and no edit of the volume lands.
- A run’s volume mount. For each distinct
volume_namea run request names, in the order first named: (1) a volume not in the client’s listing is the run’s error; (2) a volume whosemodeis not themodeof every mount naming it isVolumeMode, carrying the name and the mode the volume is in; (3) a volume of a mode whose object is absent, or whosemountisfalse, isVolumeMountUnsupported, carrying the name; (4) a volume an operation holds isVolumeHeld, carrying the name; (5) a volume a running container holds, wheremount_manyisfalse, isVolumeMounted, carrying the name; (6) a volume a FUSE serve holds, wheremount_and_fuse_serveisfalse, isVolumeFuseServed, carrying the name; (7) otherwise the server holds the volume for the run. A refusal of any volume gives back every volume held for the request before it, and the server fetches nothing and deploys nothing for the request. The run pages state each response’s form. - A FUSE serve. For the volume a volumes::fuse_serve
request names: (1) a volume not in the client’s listing is the
error; (2) a volume whose
modeis not the request’smodeisVolumeMode, carrying the name and the mode the volume is in; (3) a volume of a mode whose object is absent, or whosefuse_serveisfalse, isUnsupported; (4) a volume an operation holds isHeld; (5) a volume another FUSE serve holds, wherefuse_serve_manyisfalse, isFuseServed; (6) a volume a running container holds, wheremount_and_fuse_serveisfalse, isMounted; (7) otherwise the server holds the volume for the FUSE serve. That page states each response’s form. - An operation. For the volume an operation names: (1) a volume
not in the client’s listing is the error; (2) a volume another
operation holds is
Held; (3) a volume a running container holds isMounted; (4) a volume a FUSE serve holds isFuseServed; (5) otherwise the server holds the volume to itself for the operation. Each operation’s page states each response’s form. - A build’s context. For the volume an images::build
request names as its context: (1) a volume not in the client’s
listing is the error; (2) a volume an operation holds, a volume a
running container holds where the mode object is absent or its
mount_manyisfalse, and a volume a FUSE serve holds where the mode object is absent or itsmount_and_fuse_serveisfalse, are eachContextHeld; (3) otherwise the server holds the volume for the build. That page states each response’s form. - Who holds, as of the request. A running container holds a
volume from the moment the server holds it for the run until the
run ends, whether or not the id has been sent. A FUSE serve holds a
volume from
Servinguntil the response finish. An operation holds a volume from the moment the server accepts the request until its response finish.
The mode object is defined by
diverge-sdk-rs/src/provider/endpoints/info/server/response/volume_capabilities.rs:
//! What may be done with a volume of one mode, on one provider.
use serde::{Deserialize, Serialize};
/// What a provider does with a volume of one
/// [`Mode`](crate::provider::endpoints::volumes::Mode): whether a run
/// may mount it, whether a FUSE serve may hold it, and how many of
/// each, and both, at once.
///
/// The mode itself says the MOST that may be done with a volume in
/// it — any number of running containers but one FUSE serve, never
/// both, for a persistent volume; any number of each, beside one
/// another, for an ephemeral or a read-only one — and this says how
/// much of that the provider does. Each member is a boolean, absent being `false`;
/// the object of every member `false` — the member absent, the same
/// statement — is a mode the provider keeps no volume in, see
/// [`kept`](Self::kept); and
/// the later ones extend the earlier: `mount_many` means nothing
/// without `mount`, `fuse_serve_many` nothing without `fuse_serve`,
/// `mount_and_fuse_serve` nothing without both; a provider states
/// them consistently, and a reader reads each as the plain fact it
/// is. A request past one is answered by the endpoint's own variant,
/// named below.
///
/// # What a mode allows, at most
///
/// | mode | `mount_many` | `fuse_serve_many` | `mount_and_fuse_serve` |
/// |------|--------------|-------------------|------------------------|
/// | persistent | may | never | never |
/// | ephemeral | may | may | may |
/// | read only | may | may | may |
///
/// A provider holds out no more than the mode allows: a persistent
/// volume's object has `fuse_serve_many` and `mount_and_fuse_serve`
/// `false`.
#[derive(
Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize, schemars::JsonSchema,
)]
pub struct VolumeCapabilities {
/// A run may mount a volume of the mode — a
/// [`VolumeMount`](crate::shared::containers::request::VolumeMount)
/// naming one is honored. Absent — `false` — a run naming one is
/// answered
/// [`VolumeMountUnsupported`](crate::shared::containers::response::VolumeMountUnsupported).
#[serde(default)]
pub mount: bool,
/// A [`fuse_serve`](crate::provider::endpoints::volumes::fuse_serve)
/// may hold a volume of the mode. Absent — `false` — one asking
/// is answered `Unsupported`.
#[serde(default)]
pub fuse_serve: bool,
/// More than one running container may mount a volume of the
/// mode at once. Absent — `false` — a run naming one that a
/// running container has is answered
/// [`VolumeMounted`](crate::shared::containers::response::VolumeMounted).
#[serde(default)]
pub mount_many: bool,
/// More than one FUSE serve may hold a volume of the mode at
/// once. Absent — `false` — a FUSE serve of one that another FUSE
/// serve holds is answered `FuseServed`.
#[serde(default)]
pub fuse_serve_many: bool,
/// A running container and a FUSE serve may hold a volume of the
/// mode at once. Absent — `false` — a run naming one that a FUSE
/// serve holds is answered
/// [`VolumeFuseServed`](crate::shared::containers::response::VolumeFuseServed),
/// and a FUSE serve of one that a running container has is
/// answered `Mounted`.
#[serde(default)]
pub mount_and_fuse_serve: bool,
}
impl VolumeCapabilities {
/// Whether the provider keeps volumes of the mode at all: any
/// member `true`. Every member `false` is the mode not kept, and
/// is what an absent member means.
pub fn kept(&self) -> bool {
self.mount || self.fuse_serve || self.mount_many || self.fuse_serve_many || self.mount_and_fuse_serve
}
}