Response
The server sends exactly one response: the byte 0 alone, Serving, after which the scope stays open until the client stops it; the byte 1, 2 or 3 alone when a running container, another FUSE serve or an operation holds the volume; the byte 4 followed by the volume and the mode it is in as JSON, when the request meant another; the byte 5 alone when the server does not FUSE serve a volume of its mode, or keeps no volume; or the byte 6 followed by an error as one JSON value; every refusal and an error are followed by the response finish.
The server sends exactly one response on the scope, and sends it before it serves any channel.
[0] serving: the volume is held, and the asks are answered
[1] not served: a running container holds the volume
[2] not served: another FUSE serve holds the volume
[3] not served: an operation holds the volume
[4][volume-mode JSON …] not served: the volume is not in the mode the request means
[5] not served: the server does not FUSE serve a volume of its mode, or keeps no volume
[6][error JSON …] not served
- The sequence. Exactly one response, first. After a response
whose payload is exactly the one byte
0, the scope stays open: the server sends no other response on the scope, answers every channel the client opens as Client Channels states, and sends the response finish after the last answer when the client stops. After a response whose first byte is1to6, the response finish follows the response, and no frame follows the finish. A response finish that no response precedes states that the request was not served, as Endpoints provides. - The refusals, and their order. The server decides the bytes
1to5for the volume the request names in the order Capabilities states for a FUSE serve, the first rule that applies deciding; on a server that keeps no volume at all, the byte5is the only response, sent before anything is looked up. Every refusal states that the volume is not served, that nothing is held, and that nothing changed. - Mounted. A payload that is the byte
1and nothing else states that a running container of the client holds the volume at the time of the request, on a server whose mode object for the volume’s mode hasmount_and_fuse_servefalse. - FUSE served. A payload that is the byte
2and nothing else states that another FUSE serve of the client holds the volume at the time of the request, on a server whose mode object for the volume’s mode hasfuse_serve_manyfalse. - Held. A payload that is the byte
3and nothing else states that the volume is under a volumes::stat, volumes::read, volumes::write, volumes::filetree, volumes::edit or volumes::delete at the time of the request. - Volume mode. A payload whose first byte is
4carries, after that byte as JSON, an object with the membersname, a string, andmode, a string: the name the request gave, and the mode the volume is in, as the client’s listing reports it, one ofpersistent,ephemeralandread_only. The server sends it when the request’smodeis not the volume’s; the volume is not served and is as it was. - Unsupported. A payload that is the byte
5and nothing else states that the server holds out no mode object for the volume’s mode, or one whosefuse_serveisfalse; or that the server holds out none of the three mode objects and keeps no volume, as Capabilities provides. - The error. A payload whose first byte is
6carries an error after that byte, running to the end of the payload. The error is exactly one JSON value, in the form defined on the volumes::list response page. This revision prescribes nothing about its content. - Malformed. A payload with no byte, a payload whose first byte
is none of
0to6, a payload whose bytes after4do not parse as the refusal, and a payload whose bytes after6are not one JSON value are malformed. A payload whose first byte is0,1,2,3or5is read by that byte alone; bytes after it are not read.
The response is defined by diverge-sdk-rs/src/provider/endpoints/volumes/fuse_serve/server/response/frame.rs:
//! What a server's response frame carries for a volume FUSE serve.
use std::fmt;
use crate::wire::decode::Decode;
use crate::wire::encode::{Encode, Writer};
use crate::shared::containers::response::VolumeMode;
use crate::shared::error::Error;
/// The volume is served, or the news that it will not be.
///
/// A payload leads with one byte saying which — `0` for
/// [`Serving`](Self::Serving), `1` for [`Mounted`](Self::Mounted),
/// `2` for [`FuseServed`](Self::FuseServed), `3` for
/// [`Held`](Self::Held), `4` for [`VolumeMode`](Self::VolumeMode),
/// `5` for [`Unsupported`](Self::Unsupported), `6` for
/// [`Error`](Self::Error) — and the rest is that variant's own JSON,
/// of which only the mode refusal and the error have any.
///
/// # How the scope runs
///
/// | the scope | means |
/// |-----------|-------|
/// | a serving, then quiet, and stays open | the volume is held, and every ask the caller opens is answered on its channel |
/// | a serving, then a finish | the caller stopped, and the volume is given back |
/// | a mounted, then a finish | it was not served: a running container has the volume, and the provider does not FUSE serve a volume of its mode beside one |
/// | a FUSE served, then a finish | it was not served: another FUSE serve holds the volume, and the provider FUSE serves a volume of its mode once |
/// | a held, then a finish | it was not served: another operation has the volume to itself |
/// | a volume mode, then a finish | it was not served: the volume is not in the mode the request means |
/// | an unsupported, then a finish | it was not served: the provider does not FUSE serve a volume of its mode |
/// | an error, then a finish | it was not served: no volume of the caller's has the name |
///
/// Exactly one response, first. Nothing else comes on channel `0`;
/// the answers to asks come on the asks' channels.
#[derive(Debug, Clone, PartialEq)]
pub enum Frame {
/// The volume is held, and asks may be opened. Tag `0`.
Serving,
/// A running container has the volume, and the provider does not
/// FUSE serve a volume of its mode beside one — its
/// `mount_and_fuse_serve` for the mode is `false`. Tag `1`.
///
/// Nothing is held and nothing changed; a caller stops the
/// container, or asks another provider, and asks again.
Mounted,
/// Another FUSE serve holds the volume, and the provider FUSE
/// serves a volume of its mode once — its `fuse_serve_many` for
/// the mode is `false`. Tag `2`.
///
/// Nothing is held and nothing changed; a caller stops that FUSE
/// serve, or asks another provider, and asks again.
FuseServed,
/// Another operation — a stat, a read, a write, a filetree, an
/// edit or a delete — has the volume to itself. Tag `3`.
///
/// Nothing is held and nothing changed; a caller waits for it to
/// end and asks again.
Held,
/// The volume is not in the mode the request means it to have.
/// Tag `4`.
///
/// Nothing is held and nothing changed; the volume is in the mode
/// reported. See [`VolumeMode`].
VolumeMode(VolumeMode),
/// The provider does not FUSE serve a volume of the mode the
/// volume is in — its `fuse_serve` for the mode is `false`, see
/// [`info`](crate::provider::endpoints::info) — or keeps no
/// volumes at all, no mode being among its capabilities, which
/// every volume endpoint answers so. Tag `5`.
///
/// Nothing is held and nothing changed; a caller asks another
/// provider, or has the volume edited into a mode this one
/// serves.
Unsupported,
/// A failure. Tag `6`.
///
/// See [`shared::error::Error`](crate::shared::error::Error) for
/// why it says so little.
Error(Error),
}
/// Tag for [`Frame::Serving`].
const SERVING: u8 = 0;
/// Tag for [`Frame::Mounted`].
const MOUNTED: u8 = 1;
/// Tag for [`Frame::FuseServed`].
const FUSE_SERVED: u8 = 2;
/// Tag for [`Frame::Held`].
const HELD: u8 = 3;
/// Tag for [`Frame::VolumeMode`].
const VOLUME_MODE: u8 = 4;
/// Tag for [`Frame::Unsupported`].
const UNSUPPORTED: u8 = 5;
/// Tag for [`Frame::Error`].
const ERROR: u8 = 6;
impl Encode for Frame {
/// The ordinary JSON failure, from the half that has one.
type Error = serde_json::Error;
// Spelled out rather than `Self::Error`: this enum has a variant
// called `Error`, so the associated type is ambiguous by that name.
fn encode(&self, out: &mut Writer<'_>) -> Result<(), serde_json::Error> {
match self {
Frame::Serving => {
out.extend_from_slice(&[SERVING]);
Ok(())
}
Frame::Mounted => {
out.extend_from_slice(&[MOUNTED]);
Ok(())
}
Frame::FuseServed => {
out.extend_from_slice(&[FUSE_SERVED]);
Ok(())
}
Frame::Held => {
out.extend_from_slice(&[HELD]);
Ok(())
}
Frame::VolumeMode(refused) => {
out.extend_from_slice(&[VOLUME_MODE]);
serde_json::to_writer(out, refused)
}
Frame::Unsupported => {
out.extend_from_slice(&[UNSUPPORTED]);
Ok(())
}
Frame::Error(error) => {
out.extend_from_slice(&[ERROR]);
error.encode(out)
}
}
}
}
impl Decode<'_> for Frame {
/// Four ways to fail, and two of them are parses.
type Error = FrameError;
// Spelled out for the same reason as `encode` above.
fn decode(bytes: &[u8]) -> Result<Self, FrameError> {
let (tag, rest) = bytes.split_first().ok_or(FrameError::Empty)?;
match *tag {
SERVING => Ok(Frame::Serving),
MOUNTED => Ok(Frame::Mounted),
FUSE_SERVED => Ok(Frame::FuseServed),
HELD => Ok(Frame::Held),
VOLUME_MODE => serde_json::from_slice(rest).map(Frame::VolumeMode).map_err(FrameError::VolumeMode),
UNSUPPORTED => Ok(Frame::Unsupported),
ERROR => Error::decode(rest).map(Frame::Error).map_err(FrameError::Error),
tag => Err(FrameError::UnknownTag(tag)),
}
}
}
/// A volume FUSE serve response frame that could not be read.
#[derive(Debug)]
pub enum FrameError {
/// No bytes at all, so not even a tag.
Empty,
/// A tag that is none of this frame's seven.
UnknownTag(u8),
/// The mode refusal did not parse.
VolumeMode(serde_json::Error),
/// The error did not parse.
Error(serde_json::Error),
}
impl fmt::Display for FrameError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FrameError::Empty => f.write_str("volume FUSE serve response frame is empty"),
FrameError::UnknownTag(tag) => write!(f, "unknown volume FUSE serve response frame tag {tag}"),
FrameError::VolumeMode(error) => write!(f, "volume FUSE serve mode refusal did not parse: {error}"),
FrameError::Error(error) => write!(f, "volume FUSE serve error did not parse: {error}"),
}
}
}
impl std::error::Error for FrameError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
FrameError::VolumeMode(error) | FrameError::Error(error) => Some(error),
FrameError::Empty | FrameError::UnknownTag(_) => None,
}
}
}
and the volume mode by diverge-sdk-rs/src/shared/containers/response/volume_mode.rs:
//! A run, or a FUSE serve, refused because a volume it names is not in
//! the mode it meant.
use serde::{Deserialize, Serialize};
use crate::provider::endpoints::volumes::Mode;
/// The volume a request named under one mode that is in another, and
/// so refused the request: a run, whose every mount states the mode it
/// means the volume to have, or a FUSE serve, whose request does.
///
/// Nothing changed: the volume is in the mode reported here, as its
/// listing reports it, and only an
/// [`edit`](crate::provider::endpoints::volumes::edit) changes it. The
/// provider answers this after it has held the volume and given the
/// hold back, so that an edit cannot land between the look and the
/// answer — the first and only response of its scope, then the
/// finish — with nothing fetched and nothing deployed for it. The
/// mode the request meant is the request's to read back.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct VolumeMode {
/// The `volume_name` of the volume, as the request named it.
pub name: String,
/// The mode the volume is in.
pub mode: Mode,
}