/agent/register

The agent, sent exactly once for the life of the container; the proxy sends exactly one answer, registered or an error, and closes the connection.

The server opens /agent/register and sends exactly one message, the request defined below as JSON. The proxy sends exactly one message and closes the connection. In the message, a first byte of 0 states that the agent is registered; a first byte of 1 is followed by an error as JSON.

  • Once. The agent is fixed for the life of the container. A registration after a successful registration is answered with an error. A loop asked for before a registration is answered with an error on /agent/run.
  • No repetition. A registration whose path ended abruptly has an answer the server did not receive. No party retries it.

The request is defined by diverge-provider-sdk/src/container_proxy/agent/register/request/request.rs:

//! The agent, as the request that made the container carried it.

use serde::{Deserialize, Serialize};
use serde_json::Value;

use crate::decode::Decode;
use crate::encode::{Encode, Writer};

/// The agent, handed to the container once: the
/// [`agent`](crate::endpoints::containers::agents::run::client::request::Frame::agent)
/// of the request that made it, typed to the same depth for the same
/// reason — a JSON value, because the image defines what an agent
/// is, and what the value may be is what
/// [`agent_schema`](crate::shared::containers::agent_schema) answers.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct Request {
    /// The agent, as the image defines it.
    pub agent: Value,
}

impl Encode for Request {
    /// The ordinary JSON failure.
    type Error = serde_json::Error;

    fn encode(&self, out: &mut Writer<'_>) -> Result<(), Self::Error> {
        serde_json::to_writer(out, self)
    }
}

impl Decode<'_> for Request {
    /// The ordinary JSON failure.
    type Error = serde_json::Error;

    fn decode(bytes: &[u8]) -> Result<Self, Self::Error> {
        serde_json::from_slice(bytes)
    }
}

The answer is defined by diverge-provider-sdk/src/container_proxy/agent/register/response/frame.rs:

//! The one message on `/agent/register`.

use std::error;
use std::fmt;

use crate::decode::Decode;
use crate::encode::{Encode, Writer};
use crate::shared;

/// One frame, then the close: the agent taken, or not.
///
/// A payload leads with one byte saying which; only
/// [`Error`](Self::Error) carries anything after it.
#[derive(Debug, Clone, PartialEq)]
pub enum Frame {
    /// The container holds the agent now, for its life. Tag `0`.
    Registered,
    /// The container refused it. Tag `1`. The agent's server's own
    /// words: a value the image will not take, or an agent already
    /// registered.
    Error(shared::error::Error),
}

/// Tag for [`Frame::Registered`].
const REGISTERED: u8 = 0;

/// Tag for [`Frame::Error`].
const ERROR: u8 = 1;

/// A tag, and — for the error alone — that variant's own JSON.
impl Encode for Frame {
    /// The ordinary JSON failure. `Registered` cannot fail at all.
    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::Registered => {
                out.extend_from_slice(&[REGISTERED]);
                Ok(())
            }
            Frame::Error(error) => {
                out.extend_from_slice(&[ERROR]);
                error.encode(out)
            }
        }
    }
}

impl Decode<'_> for Frame {
    /// Three ways to fail, and only one of them is JSON.
    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 {
            REGISTERED => Ok(Frame::Registered),
            ERROR => shared::error::Error::decode(rest)
                .map(Frame::Error)
                .map_err(FrameError::Body),
            tag => Err(FrameError::UnknownTag(tag)),
        }
    }
}

/// A registration answer 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 two.
    UnknownTag(u8),
    /// The error payload after the tag did not parse.
    Body(serde_json::Error),
}

impl fmt::Display for FrameError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            FrameError::Empty => f.write_str("register answer frame is empty"),
            FrameError::UnknownTag(tag) => {
                write!(f, "unknown register answer tag {tag}")
            }
            FrameError::Body(error) => {
                write!(f, "register answer error did not parse: {error}")
            }
        }
    }
}

impl error::Error for FrameError {
    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
        match self {
            FrameError::Body(error) => Some(error),
            FrameError::Empty | FrameError::UnknownTag(_) => None,
        }
    }
}