opencode_cloud_core/docker/
client.rs

1//! Docker client wrapper with connection handling
2//!
3//! This module provides a wrapped Docker client that handles connection
4//! errors gracefully and provides clear error messages.
5
6use bollard::Docker;
7
8use super::error::DockerError;
9
10/// Docker client wrapper with connection handling
11pub struct DockerClient {
12    inner: Docker,
13}
14
15impl DockerClient {
16    /// Create new client connecting to local Docker daemon
17    ///
18    /// Uses platform-appropriate socket (Unix socket on Linux/macOS).
19    /// Returns a clear error if Docker is not running or accessible.
20    pub fn new() -> Result<Self, DockerError> {
21        let docker = Docker::connect_with_local_defaults()
22            .map_err(|e| DockerError::Connection(e.to_string()))?;
23
24        Ok(Self { inner: docker })
25    }
26
27    /// Create client with custom timeout (in seconds)
28    ///
29    /// Use for long-running operations like image builds.
30    /// Default timeout is 120 seconds; build timeout should be 600+ seconds.
31    pub fn with_timeout(timeout_secs: u64) -> Result<Self, DockerError> {
32        let docker = Docker::connect_with_local_defaults()
33            .map_err(|e| DockerError::Connection(e.to_string()))?
34            .with_timeout(std::time::Duration::from_secs(timeout_secs));
35
36        Ok(Self { inner: docker })
37    }
38
39    /// Verify connection to Docker daemon
40    ///
41    /// Returns Ok(()) if connected, descriptive error otherwise.
42    pub async fn verify_connection(&self) -> Result<(), DockerError> {
43        self.inner.ping().await.map_err(DockerError::from)?;
44        Ok(())
45    }
46
47    /// Get Docker version info (useful for debugging)
48    pub async fn version(&self) -> Result<String, DockerError> {
49        let version = self.inner.version().await.map_err(DockerError::from)?;
50
51        let version_str = format!(
52            "Docker {} (API {})",
53            version.version.unwrap_or_else(|| "unknown".to_string()),
54            version.api_version.unwrap_or_else(|| "unknown".to_string())
55        );
56
57        Ok(version_str)
58    }
59
60    /// Access inner Bollard client for advanced operations
61    pub fn inner(&self) -> &Docker {
62        &self.inner
63    }
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69
70    #[test]
71    fn docker_client_creation_does_not_panic() {
72        // This test just verifies the code compiles and doesn't panic
73        // Actual connection test requires Docker to be running
74        let result = DockerClient::new();
75        // We don't assert success because Docker may not be running in CI
76        drop(result);
77    }
78
79    #[test]
80    fn docker_client_with_timeout_does_not_panic() {
81        let result = DockerClient::with_timeout(600);
82        drop(result);
83    }
84}