use crate::error::Result;
use crate::generated::{
container_service_server::ContainerServiceServer,
image_service_server::ImageServiceServer,
machine_service_server::MachineServiceServer,
system_service_server::SystemServiceServer,
};
use crate::grpc::{ContainerServiceImpl, ImageServiceImpl, MachineServiceImpl, SystemServiceImpl};
use arcbox_core::{Config, Runtime};
use arcbox_docker::{DockerApiServer, ServerConfig as DockerConfig};
use std::path::PathBuf;
use std::sync::Arc;
use tonic::transport::Server;
#[derive(Debug, Clone)]
pub struct ApiServerConfig {
pub grpc_addr: String,
pub docker_socket: PathBuf,
}
impl Default for ApiServerConfig {
fn default() -> Self {
Self {
grpc_addr: "[::1]:50051".to_string(),
docker_socket: PathBuf::from("/var/run/arcbox-docker.sock"),
}
}
}
pub struct ApiServer {
config: ApiServerConfig,
runtime: Arc<Runtime>,
}
impl ApiServer {
pub fn new(api_config: ApiServerConfig, core_config: Config) -> Result<Self> {
let runtime = Arc::new(Runtime::new(core_config)?);
Ok(Self {
config: api_config,
runtime,
})
}
#[must_use]
pub fn runtime(&self) -> &Arc<Runtime> {
&self.runtime
}
pub async fn run(&self) -> Result<()> {
self.runtime.init().await?;
let docker_config = DockerConfig {
socket_path: self.config.docker_socket.clone(),
};
let docker_server = DockerApiServer::new(docker_config, Arc::clone(&self.runtime));
let docker_handle = tokio::spawn(async move {
if let Err(e) = docker_server.run().await {
tracing::error!("Docker API server error: {}", e);
}
});
let container_service = ContainerServiceImpl::new(Arc::clone(&self.runtime));
let machine_service = MachineServiceImpl::new(Arc::clone(&self.runtime));
let image_service = ImageServiceImpl::new(Arc::clone(&self.runtime));
let system_service = SystemServiceImpl::new(Arc::clone(&self.runtime));
let grpc_addr = self
.config
.grpc_addr
.parse()
.map_err(|e| crate::error::ApiError::Config(format!("invalid gRPC address: {}", e)))?;
tracing::info!("ArcBox API server starting");
tracing::info!(" gRPC: {}", self.config.grpc_addr);
tracing::info!(
" Docker API: {}",
self.config.docker_socket.display()
);
let grpc_server = Server::builder()
.add_service(ContainerServiceServer::new(container_service))
.add_service(MachineServiceServer::new(machine_service))
.add_service(ImageServiceServer::new(image_service))
.add_service(SystemServiceServer::new(system_service))
.serve_with_shutdown(grpc_addr, async {
tokio::signal::ctrl_c()
.await
.expect("failed to listen for ctrl-c");
tracing::info!("Received shutdown signal");
});
tracing::info!("ArcBox API server running");
grpc_server.await.map_err(|e| {
crate::error::ApiError::Transport(format!("gRPC server error: {}", e))
})?;
tracing::info!("Shutting down...");
docker_handle.abort();
self.runtime.shutdown().await?;
Ok(())
}
}