opencode_cloud_core/docker/
mod.rs1mod client;
16pub mod container;
17mod dockerfile;
18mod error;
19pub mod exec;
20mod health;
21pub mod image;
22pub mod mount;
23pub mod progress;
24pub mod state;
25pub mod update;
26pub mod users;
27mod version;
28pub mod volume;
29
30pub use client::DockerClient;
32pub use error::DockerError;
33pub use progress::ProgressReporter;
34
35pub use health::{
37 ExtendedHealthResponse, HealthError, HealthResponse, check_health, check_health_extended,
38};
39
40pub use dockerfile::{DOCKERFILE, IMAGE_NAME_DOCKERHUB, IMAGE_NAME_GHCR, IMAGE_TAG_DEFAULT};
42
43pub use image::{build_image, image_exists, pull_image};
45
46pub use update::{UpdateResult, has_previous_image, rollback_image, update_image};
48
49pub use version::{VERSION_LABEL, get_cli_version, get_image_version, versions_compatible};
51
52pub use exec::{
54 exec_command, exec_command_exit_code, exec_command_with_status, exec_command_with_stdin,
55};
56
57pub use users::{
59 UserInfo, create_user, delete_user, list_users, lock_user, persist_user, remove_persisted_user,
60 restore_persisted_users, set_user_password, unlock_user, user_exists,
61};
62
63pub use volume::{
65 MOUNT_CACHE, MOUNT_CONFIG, MOUNT_PROJECTS, MOUNT_SESSION, MOUNT_STATE, MOUNT_USERS,
66 VOLUME_CACHE, VOLUME_CONFIG, VOLUME_NAMES, VOLUME_PROJECTS, VOLUME_SESSION, VOLUME_STATE,
67 VOLUME_USERS, ensure_volumes_exist, remove_all_volumes, remove_volume, volume_exists,
68};
69
70pub use mount::{MountError, ParsedMount, check_container_path_warning, validate_mount_path};
72
73pub use container::{
75 CONTAINER_NAME, ContainerBindMount, ContainerPorts, OPENCODE_WEB_PORT, container_exists,
76 container_is_running, container_state, create_container, get_container_bind_mounts,
77 get_container_ports, remove_container, start_container, stop_container,
78};
79
80pub use state::{ImageState, clear_state, get_state_path, load_state, save_state};
82
83pub async fn setup_and_start(
97 client: &DockerClient,
98 opencode_web_port: Option<u16>,
99 env_vars: Option<Vec<String>>,
100 bind_address: Option<&str>,
101 cockpit_port: Option<u16>,
102 cockpit_enabled: Option<bool>,
103 bind_mounts: Option<Vec<mount::ParsedMount>>,
104) -> Result<String, DockerError> {
105 volume::ensure_volumes_exist(client).await?;
107
108 let container_id = if container::container_exists(client, container::CONTAINER_NAME).await? {
110 let info = client
112 .inner()
113 .inspect_container(container::CONTAINER_NAME, None)
114 .await
115 .map_err(|e| {
116 DockerError::Container(format!("Failed to inspect existing container: {e}"))
117 })?;
118 info.id
119 .unwrap_or_else(|| container::CONTAINER_NAME.to_string())
120 } else {
121 container::create_container(
123 client,
124 None,
125 None,
126 opencode_web_port,
127 env_vars,
128 bind_address,
129 cockpit_port,
130 cockpit_enabled,
131 bind_mounts,
132 )
133 .await?
134 };
135
136 if !container::container_is_running(client, container::CONTAINER_NAME).await? {
138 container::start_container(client, container::CONTAINER_NAME).await?;
139 }
140
141 users::restore_persisted_users(client, container::CONTAINER_NAME).await?;
143
144 Ok(container_id)
145}
146
147pub const DEFAULT_STOP_TIMEOUT_SECS: i64 = 30;
149
150pub async fn stop_service(
157 client: &DockerClient,
158 remove: bool,
159 timeout_secs: Option<i64>,
160) -> Result<(), DockerError> {
161 let name = container::CONTAINER_NAME;
162 let timeout = timeout_secs.unwrap_or(DEFAULT_STOP_TIMEOUT_SECS);
163
164 if !container::container_exists(client, name).await? {
166 return Err(DockerError::Container(format!(
167 "Container '{name}' does not exist"
168 )));
169 }
170
171 if container::container_is_running(client, name).await? {
173 container::stop_container(client, name, Some(timeout)).await?;
174 }
175
176 if remove {
178 container::remove_container(client, name, false).await?;
179 }
180
181 Ok(())
182}