aion-server 0.31.0

Aion workflow server library: HTTP, gRPC, WebSocket, and worker endpoints. Run it with the `aion` binary from the aion-cli crate.
Documentation
//! Shared record fixtures for the control modules' specimens.
//!
//! Every live-incarnation case in these tests is proven against the ONE
//! process a test can trust to exist and to be alive: itself. A record naming
//! this process's real pid and real start instant is a genuinely live
//! incarnation by the same instrument the production code probes with, so a
//! specimen built on it exercises the real verification path rather than a
//! stubbed one.

use std::net::SocketAddr;
use std::path::Path;

use super::claim::IntendedAddresses;
use super::incarnation;
use super::pid_file::{IncarnationState, PidRecord, pid_file_path};

/// The boxed-error result shape every control specimen uses.
pub(super) type TestResult = Result<(), Box<dyn std::error::Error>>;

/// The HTTP address the fixtures record and intend by default.
pub(super) const HTTP: &str = "127.0.0.1:8080";

/// The gRPC address the fixtures record and intend by default.
pub(super) const GRPC: &str = "127.0.0.1:50051";

/// The addresses a fixture boot intends to bind.
pub(super) fn intended() -> Result<IntendedAddresses, std::net::AddrParseError> {
    Ok(IntendedAddresses {
        http: HTTP.parse()?,
        grpc: GRPC.parse()?,
    })
}

/// Addresses no fixture record ever carries, for the no-collision case.
pub(super) fn other_intended() -> Result<IntendedAddresses, std::net::AddrParseError> {
    Ok(IntendedAddresses {
        http: "127.0.0.1:18080".parse()?,
        grpc: "127.0.0.1:15005".parse()?,
    })
}

/// A BIRTH record for THIS process: live, booting, nothing bound.
pub(super) fn own_birth_record() -> Result<PidRecord, Box<dyn std::error::Error>> {
    let me = incarnation::self_identity()?;
    Ok(PidRecord {
        pid: me.pid,
        started_at_unix_secs: me.started_at_unix_secs,
        binary_sha256: me.binary_sha256,
        version: env!("CARGO_PKG_VERSION").to_owned(),
        commit: "test-commit".to_owned(),
        state: IncarnationState::Booting,
        http_address: None,
        grpc_address: None,
        intended_http_address: None,
        intended_grpc_address: None,
        stage: None,
        stage_detail: None,
        stage_seq: 0,
        stage_updated_at_unix_secs: 0,
        drain_timeout_seconds: 0,
    })
}

/// A SERVING record for THIS process, bound to the default fixture
/// addresses (or the ones given).
pub(super) fn own_serving_record(
    http: Option<SocketAddr>,
    grpc: Option<SocketAddr>,
) -> Result<PidRecord, Box<dyn std::error::Error>> {
    let mut record = own_birth_record()?;
    record.state = IncarnationState::Serving;
    record.http_address = Some(match http {
        Some(address) => address,
        None => HTTP.parse()?,
    });
    record.grpc_address = Some(match grpc {
        Some(address) => address,
        None => GRPC.parse()?,
    });
    record.drain_timeout_seconds = 30;
    Ok(record)
}

/// A record whose pid is proven vacated: a reaped child, with a start
/// instant no live process reports, so a probe cannot accidentally verify it.
pub(super) fn dead_record() -> Result<PidRecord, Box<dyn std::error::Error>> {
    let mut child = std::process::Command::new("true").spawn()?;
    let pid = child.id();
    child.wait()?;
    let mut record = own_serving_record(None, None)?;
    record.pid = pid;
    record.started_at_unix_secs = 0;
    Ok(record)
}

/// Write `record` where the control modules read, creating `run/` first.
pub(super) fn plant(home: &Path, record: &PidRecord) -> TestResult {
    let path = pid_file_path(home);
    std::fs::create_dir_all(path.parent().ok_or("pid path must have a parent")?)?;
    std::fs::write(&path, format!("{}\n", serde_json::to_string(record)?))?;
    Ok(())
}