use crate::error::{DockerError, Result};
use bollard::Docker;
use std::path::PathBuf;
use tracing::{debug, info};
#[derive(Debug, Clone)]
pub struct DockerClientConfig {
pub uri: Option<String>,
pub timeout: u64,
pub api_version: Option<String>,
}
impl Default for DockerClientConfig {
fn default() -> Self {
Self {
uri: None,
timeout: 120,
api_version: None,
}
}
}
impl DockerClientConfig {
pub fn new() -> Self {
Self::default()
}
pub fn uri(mut self, uri: impl Into<String>) -> Self {
self.uri = Some(uri.into());
self
}
pub fn timeout(mut self, timeout: u64) -> Self {
self.timeout = timeout;
self
}
pub fn api_version(mut self, version: impl Into<String>) -> Self {
self.api_version = Some(version.into());
self
}
}
pub struct DockerClient {
pub(crate) docker: Docker,
#[allow(dead_code)]
pub(crate) config: DockerClientConfig,
}
impl DockerClient {
pub fn new() -> Result<Self> {
Self::with_config(DockerClientConfig::default())
}
pub fn with_config(config: DockerClientConfig) -> Result<Self> {
debug!("Creating Docker client with config: {:?}", config);
debug!("DOCKER_HOST = {:?}", std::env::var("DOCKER_HOST").ok());
let docker =
Docker::connect_with_defaults().map_err(|e| DockerError::Connection(e.to_string()))?;
Ok(Self { docker, config })
}
pub fn connect_with_unix(path: impl Into<PathBuf>) -> Result<Self> {
let path = path.into();
let config = DockerClientConfig::new().uri(format!("unix://{}", path.display()));
Self::with_config(config)
}
pub fn connect_with_tcp(addr: impl Into<String>) -> Result<Self> {
let config = DockerClientConfig::new().uri(format!("tcp://{}", addr.into()));
Self::with_config(config)
}
pub async fn version(&self) -> Result<String> {
info!("Getting Docker version...");
let version =
self.docker.version().await.map_err(|e| {
DockerError::Connection(format!("Failed to get Docker version: {}", e))
})?;
let version_string = version.version.unwrap_or_else(|| "unknown".to_string());
info!("Docker version: {}", version_string);
Ok(version_string)
}
pub async fn ping(&self) -> Result<()> {
self.docker
.ping()
.await
.map_err(|e| DockerError::Connection(format!("Failed to ping Docker daemon: {}", e)))?;
Ok(())
}
pub async fn info(&self) -> Result<bollard::models::SystemInfo> {
self.docker
.info()
.await
.map_err(|e| DockerError::Connection(format!("Failed to get Docker info: {}", e)))
}
pub fn inner(&self) -> &Docker {
&self.docker
}
pub fn registry(&self) -> crate::registry::Registry<'_> {
crate::registry::Registry::new(self)
}
}
impl Default for DockerClient {
fn default() -> Self {
Self::new().expect("Failed to create default Docker client")
}
}