use std::path::PathBuf;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum ZLayerError {
#[error("spec error: {0}")]
Spec(#[from] zlayer_spec::SpecError),
#[error("container error: {0}")]
Container(#[from] ContainerError),
#[error("network error: {0}")]
Network(#[from] NetworkError),
#[error("runtime error: {0}")]
Runtime(#[from] RuntimeError),
#[error("config error: {0}")]
Config(#[from] ConfigError),
#[error("registry error: {0}")]
Registry(#[from] RegistryError),
#[error("IO error at {path}: {source}")]
Io {
path: PathBuf,
#[source]
source: std::io::Error,
},
}
#[derive(Debug, Error)]
pub enum ContainerError {
#[error("failed to pull image {image}: {reason}")]
PullFailed { image: String, reason: String },
#[error("failed to create container: {0}")]
CreateFailed(String),
#[error("failed to start container {id}: {reason}")]
StartFailed { id: String, reason: String },
#[error("container {id} exited with code {code}")]
Exited { id: String, code: i32 },
#[error("container {id} not found")]
NotFound { id: String },
#[error("health check failed for {id}: {reason}")]
HealthCheckFailed { id: String, reason: String },
#[error("init action {action} failed for {id}: {reason}")]
InitActionFailed {
id: String,
action: String,
reason: String,
},
}
#[derive(Debug, Error)]
pub enum NetworkError {
#[error("failed to create overlay interface {name}: {reason}")]
OverlayCreateFailed { name: String, reason: String },
#[error("failed to configure overlay peer: {0}")]
OverlayPeerFailed(String),
#[error("DNS resolution failed for {name}: {reason}")]
DnsFailed { name: String, reason: String },
#[error("connection timeout to {address}")]
ConnectionTimeout { address: String },
#[error("failed to bind port {port}: {reason}")]
BindFailed { port: u16, reason: String },
}
#[derive(Debug, Error)]
pub enum RuntimeError {
#[error("failed to join deployment {name}: {reason}")]
JoinFailed { name: String, reason: String },
#[error("scheduler error: {0}")]
Scheduler(String),
#[error("service discovery error: {0}")]
ServiceDiscovery(String),
#[error("autoscaling error for {service}: {reason}")]
AutoscalingFailed { service: String, reason: String },
#[error("leadership lost for deployment {name}")]
LeadershipLost { name: String },
}
#[derive(Debug, Error)]
pub enum ConfigError {
#[error("missing required configuration: {0}")]
Missing(String),
#[error("invalid configuration for {key}: {reason}")]
Invalid { key: String, reason: String },
#[error("failed to load config from {path}: {reason}")]
LoadFailed { path: PathBuf, reason: String },
#[error("{0}")]
Other(String),
}
impl ConfigError {
pub fn other(msg: impl Into<String>) -> ZLayerError {
ZLayerError::Config(ConfigError::Other(msg.into()))
}
}
#[derive(Debug, Error)]
pub enum RegistryError {
#[error("failed to pull manifest for {image}: {reason}")]
ManifestFailed { image: String, reason: String },
#[error("failed to pull blob {digest}: {reason}")]
BlobFailed { digest: String, reason: String },
#[error("authentication failed for registry {registry}")]
AuthFailed { registry: String },
#[error("image {image} not found")]
NotFound { image: String },
}
pub type Result<T, E = ZLayerError> = std::result::Result<T, E>;
pub use ZLayerError as Error;
impl Error {
pub fn config(msg: impl Into<String>) -> Self {
ZLayerError::Config(ConfigError::Other(msg.into()))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_display() {
let err = ContainerError::NotFound {
id: "test-id".to_string(),
};
assert!(err.to_string().contains("test-id"));
}
}