lmrc_docker/containers/
mod.rs1use crate::DockerClient;
4use crate::error::{DockerError, Result};
5use bollard::container::*;
6use bollard::exec::CreateExecOptions;
7use bollard::models::*;
8use futures_util::StreamExt;
9use std::collections::HashMap;
10use tracing::{debug, info};
11
12mod builder;
13pub use builder::*;
14
15pub struct Containers<'a> {
17 client: &'a DockerClient,
18}
19
20impl<'a> Containers<'a> {
21 pub(crate) fn new(client: &'a DockerClient) -> Self {
22 Self { client }
23 }
24
25 pub fn create(&self, image: impl Into<String>) -> ContainerBuilder<'a> {
48 ContainerBuilder::new(self.client, image)
49 }
50
51 pub fn get(&self, name_or_id: impl Into<String>) -> ContainerRef<'a> {
53 ContainerRef::new(self.client, name_or_id.into())
54 }
55
56 pub async fn list(&self, all: bool) -> Result<Vec<ContainerSummary>> {
74 let mut filters = HashMap::new();
75 if all {
76 filters.insert("all", vec!["true"]);
77 }
78
79 let options: Option<ListContainersOptions<String>> = Some(ListContainersOptions {
80 all,
81 ..Default::default()
82 });
83
84 self.client
85 .docker
86 .list_containers(options)
87 .await
88 .map_err(|e| DockerError::Other(format!("Failed to list containers: {}", e)))
89 }
90
91 pub async fn prune(&self) -> Result<ContainerPruneResponse> {
93 info!("Pruning stopped containers...");
94 self.client
95 .docker
96 .prune_containers(Some(
97 bollard::query_parameters::PruneContainersOptions::default(),
98 ))
99 .await
100 .map_err(|e| DockerError::Other(format!("Failed to prune containers: {}", e)))
101 }
102}
103
104pub struct ContainerRef<'a> {
106 client: &'a DockerClient,
107 id: String,
108}
109
110impl<'a> ContainerRef<'a> {
111 pub(crate) fn new(client: &'a DockerClient, id: String) -> Self {
112 Self { client, id }
113 }
114
115 pub fn id(&self) -> &str {
117 &self.id
118 }
119
120 pub async fn start(&self) -> Result<()> {
135 info!("Starting container: {}", self.id);
136 self.client
137 .docker
138 .start_container(
139 &self.id,
140 Some(bollard::query_parameters::StartContainerOptions::default()),
141 )
142 .await
143 .map_err(|e| DockerError::ContainerOperationFailed(format!("Failed to start: {}", e)))
144 }
145
146 pub async fn stop(&self, timeout: Option<i64>) -> Result<()> {
152 info!("Stopping container: {}", self.id);
153 let options = timeout.map(|t| StopContainerOptions { t });
154 self.client
155 .docker
156 .stop_container(&self.id, options)
157 .await
158 .map_err(|e| DockerError::ContainerOperationFailed(format!("Failed to stop: {}", e)))
159 }
160
161 pub async fn restart(&self, timeout: Option<i64>) -> Result<()> {
163 info!("Restarting container: {}", self.id);
164 let options = timeout.map(|t| RestartContainerOptions { t: t as isize });
165 self.client
166 .docker
167 .restart_container(&self.id, options)
168 .await
169 .map_err(|e| DockerError::ContainerOperationFailed(format!("Failed to restart: {}", e)))
170 }
171
172 pub async fn kill(&self, signal: Option<&str>) -> Result<()> {
174 info!("Killing container: {}", self.id);
175 let options = signal.map(|s| KillContainerOptions { signal: s });
176 self.client
177 .docker
178 .kill_container(&self.id, options)
179 .await
180 .map_err(|e| DockerError::ContainerOperationFailed(format!("Failed to kill: {}", e)))
181 }
182
183 pub async fn pause(&self) -> Result<()> {
185 info!("Pausing container: {}", self.id);
186 self.client
187 .docker
188 .pause_container(&self.id)
189 .await
190 .map_err(|e| DockerError::ContainerOperationFailed(format!("Failed to pause: {}", e)))
191 }
192
193 pub async fn unpause(&self) -> Result<()> {
195 info!("Unpausing container: {}", self.id);
196 self.client
197 .docker
198 .unpause_container(&self.id)
199 .await
200 .map_err(|e| DockerError::ContainerOperationFailed(format!("Failed to unpause: {}", e)))
201 }
202
203 pub async fn remove(&self, force: bool, remove_volumes: bool) -> Result<()> {
210 info!("Removing container: {}", self.id);
211 let options = Some(RemoveContainerOptions {
212 force,
213 v: remove_volumes,
214 ..Default::default()
215 });
216 self.client
217 .docker
218 .remove_container(&self.id, options)
219 .await
220 .map_err(|e| DockerError::ContainerOperationFailed(format!("Failed to remove: {}", e)))
221 }
222
223 pub async fn inspect(&self) -> Result<ContainerInspectResponse> {
225 debug!("Inspecting container: {}", self.id);
226 self.client
227 .docker
228 .inspect_container(
229 &self.id,
230 Some(bollard::query_parameters::InspectContainerOptions::default()),
231 )
232 .await
233 .map_err(|e| DockerError::ContainerNotFound(format!("{}: {}", self.id, e)))
234 }
235
236 pub async fn logs(
245 &self,
246 follow: bool,
247 stdout: bool,
248 stderr: bool,
249 tail: Option<&str>,
250 ) -> Result<Vec<String>> {
251 debug!("Getting logs for container: {}", self.id);
252 let options: Option<LogsOptions<String>> = Some(LogsOptions {
253 follow,
254 stdout,
255 stderr,
256 tail: tail.unwrap_or("all").to_string(),
257 ..Default::default()
258 });
259
260 let mut stream = self.client.docker.logs(&self.id, options);
261 let mut logs = Vec::new();
262
263 while let Some(chunk) = stream.next().await {
264 match chunk {
265 Ok(output) => {
266 logs.push(output.to_string());
267 }
268 Err(e) => {
269 return Err(DockerError::ContainerOperationFailed(format!(
270 "Failed to get logs: {}",
271 e
272 )));
273 }
274 }
275 }
276
277 Ok(logs)
278 }
279
280 pub async fn rename(&self, new_name: impl Into<String>) -> Result<()> {
284 let new_name = new_name.into();
285 info!("Renaming container {} to {}", self.id, new_name);
286 let options = RenameContainerOptions { name: new_name };
287 self.client
288 .docker
289 .rename_container(&self.id, options)
290 .await
291 .map_err(|e| DockerError::ContainerOperationFailed(format!("Failed to rename: {}", e)))
292 }
293
294 pub async fn exec(&self, cmd: Vec<String>, tty: bool) -> Result<String> {
296 debug!("Executing command in container {}: {:?}", self.id, cmd);
297 let config = CreateExecOptions {
298 cmd: Some(cmd),
299 attach_stdout: Some(true),
300 attach_stderr: Some(true),
301 tty: Some(tty),
302 ..Default::default()
303 };
304
305 let response = self
306 .client
307 .docker
308 .create_exec(&self.id, config)
309 .await
310 .map_err(|e| DockerError::ContainerOperationFailed(format!("Failed to exec: {}", e)))?;
311
312 Ok(response.id)
313 }
314
315 }
317
318impl DockerClient {
319 pub fn containers(&self) -> Containers<'_> {
334 Containers::new(self)
335 }
336}