Skip to main content

MeshRuntime

Struct MeshRuntime 

Source
pub struct MeshRuntime { /* private fields */ }
Expand description

The runtime: the registry (always there, so a surface can answer before activation) and the active machinery once a declaration switched it on.

Implementations§

Source§

impl MeshRuntime

Source

pub fn new() -> Self

An inactive runtime: an empty registry and the reason “nothing activated it”.

Source

pub fn decided(&self) -> bool

Whether a shared server activated or declined this runtime. false in every process that is not a server — the command line, a test — where nothing activates the mesh and MeshRuntime::status reports an absence, not a decision.

The distinction is what lets mesh doctor judge the server: an enabled declaration and an inactive mesh is a failure when a server decided it and nothing at all when nobody did.

use majordomus_cli::mesh::manager::MeshRuntime;

let mesh = MeshRuntime::new();
assert!(!mesh.decided(), "nothing has decided the command line's runtime");
assert!(!mesh.status().active);

// a server that could not activate the mesh says why, and that is a decision
mesh.decline("the node identity did not load");
assert!(mesh.decided());
assert!(mesh.status().reason.unwrap().contains("did not load"));
Source

pub fn attach_cooperation(&self, cooperation: Arc<Cooperation>)

Attach the cooperation runtime a server built for this mesh. The runtime is already started; a second attachment replaces nothing.

Discovery and cooperation are built separately — one finds runtimes, the other links to them — and this is where the surfaces find both behind one handle. The first attachment wins, so a second server in one process cannot quietly take over the journal the first one is already writing to.

use majordomus_cli::mesh::manager::MeshRuntime;

let mesh = MeshRuntime::new();
let first = built("0000000000000001");
mesh.attach_cooperation(Arc::clone(&first));
mesh.attach_cooperation(built("0000000000000002"));

let attached = mesh.cooperation().expect("a cooperation runtime");
assert_eq!(attached.runtime_key(), first.runtime_key(), "the first one keeps the slot");
Source

pub fn decline_cooperation(&self, reason: &str)

Record why cooperation is not running while the mesh is.

A mesh that discovers runtimes and links to none is the confusing case: everything looks switched on and nothing is shared. The server that decided not to start cooperation says so here, so the answer reaches whoever asks rather than living in the log of a process they cannot see.

use majordomus_cli::mesh::manager::MeshRuntime;

let mesh = MeshRuntime::new();
mesh.decline_cooperation("no endpoint to be dialed at");

let status = mesh.cooperation_status();
assert!(!status.active);
assert_eq!(status.reason.as_deref(), Some("no endpoint to be dialed at"));
Source

pub fn cooperation(&self) -> Option<Arc<Cooperation>>

The cooperation runtime, while one is still running. A stopped runtime is not handed out: a caller that got it would write to a journal nobody is replicating and believe it had told the mesh something. None here means the same to every caller — do this locally, or say it cannot be done — however cooperation came to be absent.

use majordomus_cli::mesh::manager::MeshRuntime;

let mesh = MeshRuntime::new();
assert!(mesh.cooperation().is_none(), "nothing has attached one");

mesh.attach_cooperation(Arc::clone(&cooperation));
assert!(mesh.cooperation().is_some());

cooperation.stop();
assert!(mesh.cooperation().is_none(), "a stopped runtime is not handed out");
Source

pub fn cooperation_status(&self) -> CooperationStatus

Cooperation’s status, or why there is none: the mesh’s own reason when the mesh is off, cooperation’s reason when only cooperation is.

The two reasons are kept apart because they call for different acts. A mesh that was never switched on is a declaration to write; cooperation declined under a running mesh is something about this server — no endpoint, no trusted key — and the more specific reason is the one worth telling a person, so it wins when both exist.

use majordomus_cli::mesh::manager::MeshRuntime;

let mesh = MeshRuntime::new();
let untouched = mesh.cooperation_status();
assert!(untouched.reason.unwrap().contains("no shared server"), "the mesh's reason");

mesh.decline_cooperation("the declaration enables the mesh but not cooperation");
let declined = mesh.cooperation_status();
assert!(declined.reason.unwrap().contains("not cooperation"), "the nearer reason wins");
assert!(declined.peers.is_empty(), "and the shape is the one a live runtime answers");
Source

pub fn registry(&self) -> &Arc<MeshRegistry> ⓘ

The registry behind this runtime, for surfaces that project it directly.

Source

pub fn decline(&self, reason: &str)

Record why the mesh is not running, without activating anything: a disabled or malformed declaration is an answer mesh.status must be able to give.

Source

pub fn activate( &self, config: &MeshConfig, identity: NodeIdentity, endpoints: Vec<String>, repos: Vec<String>, version: &str, ) -> Result<(), MeshError>

Activate the mesh from a declaration. Idempotent: a second activation of an active runtime is a no-op. A disabled declaration records the reason and starts nothing. Never blocks: every socket lives on its own thread.

Source

pub fn activate_as( &self, config: &MeshConfig, identity: Arc<NodeIdentity>, runtime: &str, endpoints: Vec<String>, repos: Vec<String>, version: &str, ) -> Result<(), MeshError>

MeshRuntime::activate for one runtime slot of the node: what a shared server does, naming its checkout’s runtime so that two servers of one machine announce, hear and link to each other as the two runtimes they are.

Without the slot, two worktrees of one repository on one machine would announce under one identity and each would take the other’s announcements for its own echo. The slot is what makes them two peers that cooperate, which is the ordinary case on a developer’s machine and not an exotic one.

use std::sync::Arc;

use majordomus_cli::mesh::config::MeshConfig;
use majordomus_cli::mesh::identity::NodeIdentity;
use majordomus_cli::mesh::manager::MeshRuntime;

// a declaration with every discovery mechanism off: nothing opens a socket
let config: MeshConfig = serde_json::from_value(serde_json::json!({
    "schema": "mesh/v1", "kind": "mesh", "id": "majordomus", "enabled": true,
    "multicast": { "enabled": false },
})).unwrap();

let mesh = MeshRuntime::new();
let identity = Arc::new(NodeIdentity::ephemeral().unwrap());
// a machine whose key the declaration does not list stays closed (see `closed_to`)
let mut config = config;
config.trust.allow = vec![identity.public.public_key.clone()];
mesh.activate_as(&config, Arc::clone(&identity), "0000000000000001",
    vec!["127.0.0.1:8742".into()], vec!["root".into()], "doc").unwrap();

let status = mesh.status();
assert!(status.active);
assert_eq!(status.identity.unwrap().node_id, identity.public.node_id);

// idempotent: the second server of this process does not restart the first's mesh
mesh.activate_as(&config, Arc::clone(&identity), "0000000000000002",
    vec!["127.0.0.1:8743".into()], vec!["root".into()], "doc").unwrap();
assert_eq!(mesh.status().started_at, status.started_at);
Source

pub fn attach(&self, provider: Box<dyn MeshProvider>) -> Result<(), MeshError>

Attach one more provider to an active mesh. This is the entire registration a new discovery mechanism needs: no surface, consumer or registry changes with it.

Source

pub fn begin_stop(&self)

Tell the mesh to stop, without waiting for anything: every loop and handler reads the flag at once, and the draining that needs locks is left to stop. A process that is shutting down calls this first, does the work that must not be delayed — closing its listeners, releasing its lease — and only then drains.

use majordomus_cli::mesh::manager::MeshRuntime;

// a mesh that never activated has nothing to tell, and says so rather than failing:
// a shutdown path must not care whether the thing it is stopping ever started
let mesh = MeshRuntime::new();
mesh.begin_stop();
assert!(!mesh.status().active);
Source

pub fn stop(&self)

Stop the mesh. Every provider thread and the manager end at their next bounded wait; the registry keeps what it saw for whoever still asks.

Source

pub fn status(&self) -> MeshStatus

The status, as every surface answers it.

Source

pub fn nodes(&self) -> Vec<NodeRecord>

Every observed node, in the registry’s canonical node-id order.

Source

pub fn register(&self, envelope: &Value, path: &str) -> RegisterAnswer

Handle one rendezvous registration: verify the presented envelope exactly as a datagram, record it, and answer with this node’s envelope and the candidates the registry holds. Registration grants nothing — the caller becomes a record, not a peer with rights.

Trait Implementations§

Source§

impl Default for MeshRuntime

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more