use serde::{Deserialize, Serialize};
pub const HANDOFF_SOCKET_PATH: &str = "/run/pact/handoff.sock";
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum NamespaceType {
Pid,
Net,
Mount,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NamespaceRequest {
pub allocation_id: String,
pub namespaces: Vec<NamespaceType>,
pub uenv_image: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NamespaceResponse {
pub allocation_id: String,
pub fd_types: Vec<NamespaceType>,
pub uenv_mount_path: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AllocationEnded {
pub allocation_id: String,
}
pub trait NamespaceProvider: Send + Sync {
fn create_namespaces(
&self,
request: &NamespaceRequest,
) -> Result<NamespaceResponse, NamespaceError>;
fn release_namespaces(&self, allocation_id: &str) -> Result<(), NamespaceError>;
}
pub trait NamespaceConsumer: Send + Sync {
fn request_namespaces(
&self,
request: &NamespaceRequest,
) -> Result<NamespaceResponse, NamespaceError>;
}
#[derive(Debug, thiserror::Error)]
pub enum NamespaceError {
#[error("handoff socket unavailable: {reason}")]
SocketUnavailable { reason: String },
#[error("namespace creation failed: {reason}")]
CreationFailed { reason: String },
#[error("allocation not found: {allocation_id}")]
AllocationNotFound { allocation_id: String },
#[error("FD passing failed: {0}")]
FdPassing(#[from] std::io::Error),
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn namespace_request_serialization() {
let req = NamespaceRequest {
allocation_id: "alloc-42".to_string(),
namespaces: vec![NamespaceType::Pid, NamespaceType::Net, NamespaceType::Mount],
uenv_image: Some("pytorch-2.5.sqfs".to_string()),
};
let json = serde_json::to_string(&req).unwrap();
let deser: NamespaceRequest = serde_json::from_str(&json).unwrap();
assert_eq!(deser.allocation_id, "alloc-42");
assert_eq!(deser.namespaces.len(), 3);
assert_eq!(deser.uenv_image.as_deref(), Some("pytorch-2.5.sqfs"));
}
#[test]
fn namespace_response_fd_order() {
let resp = NamespaceResponse {
allocation_id: "alloc-42".to_string(),
fd_types: vec![NamespaceType::Pid, NamespaceType::Net, NamespaceType::Mount],
uenv_mount_path: Some("/run/pact/uenv/pytorch-2.5".to_string()),
};
assert_eq!(resp.fd_types[0], NamespaceType::Pid);
assert_eq!(resp.fd_types[1], NamespaceType::Net);
assert_eq!(resp.fd_types[2], NamespaceType::Mount);
}
}