opencode_cloud_core/docker/
volume.rs1use super::{DockerClient, DockerError};
7use bollard::models::VolumeCreateRequest;
8use bollard::query_parameters::RemoveVolumeOptions;
9use std::collections::HashMap;
10use tracing::debug;
11
12pub const VOLUME_SESSION: &str = "opencode-data";
14
15pub const VOLUME_STATE: &str = "opencode-state";
17
18pub const VOLUME_CACHE: &str = "opencode-cache";
20
21pub const VOLUME_PROJECTS: &str = "opencode-workspace";
23
24pub const VOLUME_CONFIG: &str = "opencode-config";
26
27pub const VOLUME_USERS: &str = "opencode-users";
29
30pub const VOLUME_NAMES: [&str; 6] = [
32 VOLUME_SESSION,
33 VOLUME_STATE,
34 VOLUME_CACHE,
35 VOLUME_PROJECTS,
36 VOLUME_CONFIG,
37 VOLUME_USERS,
38];
39
40pub const MOUNT_SESSION: &str = "/home/opencode/.local/share/opencode";
42
43pub const MOUNT_STATE: &str = "/home/opencode/.local/state/opencode";
45
46pub const MOUNT_CACHE: &str = "/home/opencode/.cache/opencode";
48
49pub const MOUNT_PROJECTS: &str = "/home/opencode/workspace";
51
52pub const MOUNT_CONFIG: &str = "/home/opencode/.config/opencode";
54
55pub const MOUNT_USERS: &str = "/var/lib/opencode-users";
57
58pub async fn ensure_volumes_exist(client: &DockerClient) -> Result<(), DockerError> {
63 debug!("Ensuring all required volumes exist");
64
65 for volume_name in VOLUME_NAMES {
66 ensure_volume_exists(client, volume_name).await?;
67 }
68
69 debug!("All volumes verified/created");
70 Ok(())
71}
72
73async fn ensure_volume_exists(client: &DockerClient, name: &str) -> Result<(), DockerError> {
75 debug!("Checking volume: {}", name);
76
77 let options = VolumeCreateRequest {
79 name: Some(name.to_string()),
80 driver: Some("local".to_string()),
81 driver_opts: Some(HashMap::new()),
82 labels: Some(HashMap::from([(
83 "managed-by".to_string(),
84 "opencode-cloud".to_string(),
85 )])),
86 cluster_volume_spec: None,
87 };
88
89 client
91 .inner()
92 .create_volume(options)
93 .await
94 .map_err(|e| DockerError::Volume(format!("Failed to create volume {name}: {e}")))?;
95
96 debug!("Volume {} ready", name);
97 Ok(())
98}
99
100pub async fn volume_exists(client: &DockerClient, name: &str) -> Result<bool, DockerError> {
102 debug!("Checking if volume exists: {}", name);
103
104 match client.inner().inspect_volume(name).await {
105 Ok(_) => Ok(true),
106 Err(bollard::errors::Error::DockerResponseServerError {
107 status_code: 404, ..
108 }) => Ok(false),
109 Err(e) => Err(DockerError::Volume(format!(
110 "Failed to inspect volume {name}: {e}"
111 ))),
112 }
113}
114
115pub async fn remove_volume(client: &DockerClient, name: &str) -> Result<(), DockerError> {
120 debug!("Removing volume: {}", name);
121
122 client
123 .inner()
124 .remove_volume(name, None::<RemoveVolumeOptions>)
125 .await
126 .map_err(|e| DockerError::Volume(format!("Failed to remove volume {name}: {e}")))?;
127
128 debug!("Volume {} removed", name);
129 Ok(())
130}
131
132pub async fn remove_all_volumes(client: &DockerClient) -> Result<(), DockerError> {
136 debug!("Removing all opencode-cloud volumes");
137
138 for volume_name in VOLUME_NAMES {
139 if volume_exists(client, volume_name).await? {
141 remove_volume(client, volume_name).await?;
142 }
143 }
144
145 debug!("All volumes removed");
146 Ok(())
147}
148
149#[cfg(test)]
150mod tests {
151 use super::*;
152
153 #[test]
154 fn volume_constants_are_correct() {
155 assert_eq!(VOLUME_SESSION, "opencode-data");
156 assert_eq!(VOLUME_STATE, "opencode-state");
157 assert_eq!(VOLUME_CACHE, "opencode-cache");
158 assert_eq!(VOLUME_PROJECTS, "opencode-workspace");
159 assert_eq!(VOLUME_CONFIG, "opencode-config");
160 assert_eq!(VOLUME_USERS, "opencode-users");
161 }
162
163 #[test]
164 fn volume_names_array_has_all_volumes() {
165 assert_eq!(VOLUME_NAMES.len(), 6);
166 assert!(VOLUME_NAMES.contains(&VOLUME_SESSION));
167 assert!(VOLUME_NAMES.contains(&VOLUME_STATE));
168 assert!(VOLUME_NAMES.contains(&VOLUME_CACHE));
169 assert!(VOLUME_NAMES.contains(&VOLUME_PROJECTS));
170 assert!(VOLUME_NAMES.contains(&VOLUME_CONFIG));
171 assert!(VOLUME_NAMES.contains(&VOLUME_USERS));
172 }
173
174 #[test]
175 fn mount_points_are_correct() {
176 assert_eq!(MOUNT_SESSION, "/home/opencode/.local/share/opencode");
177 assert_eq!(MOUNT_STATE, "/home/opencode/.local/state/opencode");
178 assert_eq!(MOUNT_CACHE, "/home/opencode/.cache/opencode");
179 assert_eq!(MOUNT_PROJECTS, "/home/opencode/workspace");
180 assert_eq!(MOUNT_CONFIG, "/home/opencode/.config/opencode");
181 assert_eq!(MOUNT_USERS, "/var/lib/opencode-users");
182 }
183}