use std::path::PathBuf;
use std::time::Duration;
fn format_stderr_tail(lines: &[String]) -> String {
if lines.is_empty() {
String::new()
} else {
format!(" — last stderr: {}", lines.join(" | "))
}
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum SupervisorError {
#[error("resolve the spawn command for {service} instance {key}: {source}")]
SpawnSpec {
service: String,
key: String,
#[source]
source: Box<dyn std::error::Error + Send + Sync + 'static>,
},
#[error("create directory {path} for {service} instance {key}: {source}")]
CreateDir {
service: String,
key: String,
path: PathBuf,
#[source]
source: std::io::Error,
},
#[error("spawn {program} for {service} instance {key}: {source}")]
Spawn {
service: String,
key: String,
program: PathBuf,
#[source]
source: std::io::Error,
},
#[error(
"{service} instance {key} did not bind {socket} within {budget:?} — \
if this service loads a model at startup, its ServiceTimeouts::spawn_probe \
is too small{}",
format_stderr_tail(.stderr)
)]
SpawnTimeout {
service: String,
key: String,
socket: PathBuf,
budget: Duration,
stderr: Vec<String>,
},
#[error(
"{service} instance {key} exited before binding {socket}: {status}{}",
format_stderr_tail(.stderr)
)]
ChildExited {
service: String,
key: String,
socket: PathBuf,
status: std::process::ExitStatus,
stderr: Vec<String>,
},
#[error("refusing to adopt {socket} for {service} instance {key}: {source}")]
UntrustedSocket {
service: String,
key: String,
socket: PathBuf,
#[source]
source: Box<crate::uds::UdsSecurityError>,
},
#[error(
"SIGTERM patience {sigterm_patience:?} must strictly exceed the supervised \
child's shutdown-flush budget {shutdown_flush:?}, or the SIGKILL lands \
mid-flush and discards acked writes"
)]
InvalidTimeouts {
sigterm_patience: Duration,
shutdown_flush: Duration,
},
#[error("socket path for {service} instance {key} is unusable: {source}")]
SocketPath {
service: String,
key: String,
#[source]
source: Box<crate::uds::UdsSecurityError>,
},
}