opencode_cloud_core/docker/
mod.rs

1//! Docker operations module
2//!
3//! This module provides Docker container management functionality including:
4//! - Docker client wrapper with connection handling
5//! - Docker-specific error types
6//! - Embedded Dockerfile for building the opencode image
7//! - Progress reporting for build and pull operations
8//! - Image build and pull operations
9//! - Volume management for persistent storage
10//! - Container lifecycle (create, start, stop, remove)
11
12mod client;
13pub mod container;
14mod dockerfile;
15mod error;
16pub mod image;
17pub mod progress;
18pub mod volume;
19
20// Core types
21pub use client::DockerClient;
22pub use error::DockerError;
23pub use progress::ProgressReporter;
24
25// Dockerfile constants
26pub use dockerfile::{DOCKERFILE, IMAGE_NAME_DOCKERHUB, IMAGE_NAME_GHCR, IMAGE_TAG_DEFAULT};
27
28// Image operations
29pub use image::{build_image, image_exists, pull_image};
30
31// Volume management
32pub use volume::{
33    MOUNT_CONFIG, MOUNT_PROJECTS, MOUNT_SESSION, VOLUME_CONFIG, VOLUME_NAMES, VOLUME_PROJECTS,
34    VOLUME_SESSION, ensure_volumes_exist, remove_all_volumes, remove_volume, volume_exists,
35};
36
37// Container lifecycle
38pub use container::{
39    CONTAINER_NAME, OPENCODE_WEB_PORT, container_exists, container_is_running, container_state,
40    create_container, remove_container, start_container, stop_container,
41};
42
43/// Full setup: ensure volumes exist, create container if needed, start it
44///
45/// This is the primary entry point for starting the opencode service.
46/// Returns the container ID on success.
47///
48/// # Arguments
49/// * `client` - Docker client
50/// * `opencode_web_port` - Port to bind on host for opencode web UI (defaults to OPENCODE_WEB_PORT)
51/// * `env_vars` - Additional environment variables (optional)
52pub async fn setup_and_start(
53    client: &DockerClient,
54    opencode_web_port: Option<u16>,
55    env_vars: Option<Vec<String>>,
56) -> Result<String, DockerError> {
57    // Ensure volumes exist first
58    volume::ensure_volumes_exist(client).await?;
59
60    // Check if container already exists
61    let container_id = if container::container_exists(client, container::CONTAINER_NAME).await? {
62        // Get existing container ID
63        let info = client
64            .inner()
65            .inspect_container(container::CONTAINER_NAME, None)
66            .await
67            .map_err(|e| {
68                DockerError::Container(format!("Failed to inspect existing container: {}", e))
69            })?;
70        info.id
71            .unwrap_or_else(|| container::CONTAINER_NAME.to_string())
72    } else {
73        // Create new container
74        container::create_container(client, None, None, opencode_web_port, env_vars).await?
75    };
76
77    // Start if not running
78    if !container::container_is_running(client, container::CONTAINER_NAME).await? {
79        container::start_container(client, container::CONTAINER_NAME).await?;
80    }
81
82    Ok(container_id)
83}
84
85/// Stop and optionally remove the opencode container
86///
87/// # Arguments
88/// * `client` - Docker client
89/// * `remove` - Also remove the container after stopping
90pub async fn stop_service(client: &DockerClient, remove: bool) -> Result<(), DockerError> {
91    let name = container::CONTAINER_NAME;
92
93    // Check if container exists
94    if !container::container_exists(client, name).await? {
95        return Err(DockerError::Container(format!(
96            "Container '{}' does not exist",
97            name
98        )));
99    }
100
101    // Stop if running
102    if container::container_is_running(client, name).await? {
103        container::stop_container(client, name, None).await?;
104    }
105
106    // Remove if requested
107    if remove {
108        container::remove_container(client, name, false).await?;
109    }
110
111    Ok(())
112}