passivized_docker_engine_client/client/container.rs
1use hyper::StatusCode;
2
3use crate::client::container_files::DecContainerFiles;
4use crate::client::shared::parse_container_log;
5use crate::DockerEngineClient;
6use crate::errors::DecUseError;
7use crate::requests::{CreateExecRequest, InspectContainerArgs, LogsArgs, RemoveContainerArgs, WaitCondition};
8use crate::responses::{CreateExecResponse, InspectContainerResponse, TopResponse, WaitResponse};
9use crate::model::{StreamLine, TsStreamLine};
10
11pub struct DecContainer<'a> {
12 pub(super) client: &'a DockerEngineClient,
13 pub(super) container_id: String
14}
15
16impl <'a> DecContainer<'a> {
17
18 /// Create a command to run within a container, but don't start or execute the command.
19 pub async fn create_exec(&self, request: CreateExecRequest) -> Result<CreateExecResponse, DecUseError> {
20 let uri = self.client.url.containers().create_exec(&self.container_id);
21 let response = self.client.http.post_json(uri, &request)?.execute().await?;
22
23 response
24 .assert_item_status(StatusCode::CREATED)?
25 .parse()
26 }
27
28 /// Work with files inside a container.
29 pub fn files(&'_ self) -> DecContainerFiles<'_> {
30 DecContainerFiles {
31 client: self.client,
32 container_id: &self.container_id
33 }
34 }
35
36 /// Get the console output (stdout and/or stderr) of a container.
37 ///
38 /// # Example
39 ///
40 /// ```rust
41 /// use passivized_docker_engine_client::DockerEngineClient;
42 /// use passivized_docker_engine_client::errors::DecError;
43 ///
44 /// async fn example() -> Result<(), DecError> {
45 /// let dec = DockerEngineClient::new()?;
46 /// let log = dec.container("example").logs().await?;
47 ///
48 /// println!("Container log:");
49 /// for line in log {
50 /// println!("{}", line.text);
51 /// }
52 ///
53 /// Ok(())
54 /// }
55 /// ```
56 pub async fn logs(&self) -> Result<Vec<StreamLine>, DecUseError> {
57 self.logs_with(LogsArgs::default()).await
58 }
59
60 pub async fn logs_timestamped(&self) -> Result<Vec<Result<TsStreamLine, String>>, DecUseError> {
61 let lines = self.logs_with(LogsArgs::default().timestamps()).await?;
62
63 let results = lines
64 .iter()
65 .map(TsStreamLine::try_from)
66 .collect();
67
68 Ok(results)
69 }
70
71 async fn logs_with(&self, args: LogsArgs) -> Result<Vec<StreamLine>, DecUseError> {
72 let uri = self.client.url.containers().logs(&self.container_id, args)?;
73 let response = self.client.http.get(uri)?.execute().await?;
74
75 response
76 .assert_item_status(StatusCode::OK)?
77 .parse_with(parse_container_log)
78 }
79
80 /// Inspect a container.
81 ///
82 /// # Example
83 ///
84 /// ```rust
85 /// use passivized_docker_engine_client::DockerEngineClient;
86 /// use passivized_docker_engine_client::errors::DecError;
87 ///
88 /// async fn example() -> Result<(), DecError> {
89 /// let dec = DockerEngineClient::new()?;
90 /// let container = dec.container("example").inspect().await?;
91 ///
92 /// println!("Container status: {}", container.state.status);
93 ///
94 /// Ok(())
95 /// }
96 /// ```
97 pub async fn inspect(&self) -> Result<InspectContainerResponse, DecUseError> {
98 self.inspect_with(InspectContainerArgs::default()).await
99 }
100
101 /// Inspect a container, with additional request arguments.
102 ///
103 /// # Example
104 ///
105 /// ```rust
106 /// use passivized_docker_engine_client::DockerEngineClient;
107 /// use passivized_docker_engine_client::requests::InspectContainerArgs;
108 /// use passivized_docker_engine_client::errors::DecError;
109 ///
110 /// async fn example() -> Result<(), DecError> {
111 /// let dec = DockerEngineClient::new()?;
112 /// let container = dec.container("example")
113 /// .inspect_with(InspectContainerArgs::default().size(true))
114 /// .await?;
115 ///
116 /// let container_size = container.size_root_fs
117 /// .map(|size| size.to_string())
118 /// .unwrap_or("unknown".into());
119 ///
120 /// println!("Container status: {}", container.state.status);
121 /// println!("Container root file system size: {}", container_size);
122 ///
123 /// Ok(())
124 /// }
125 /// ```
126 pub async fn inspect_with(&self, args: InspectContainerArgs) -> Result<InspectContainerResponse, DecUseError> {
127 let uri = self.client.url.containers().inspect(&self.container_id, args.size.unwrap_or_default());
128 let response = self.client.http.get(uri)?.execute().await?;
129
130 response
131 .assert_item_status(StatusCode::OK)?
132 .parse()
133 }
134
135 /// Abort a running container, using the default process signal (as determined
136 /// by the Docker Engine and/or the container's configuration or the configuration
137 /// of the image the container is based on).
138 pub async fn kill<S: Into<String>>(&self) -> Result<(), DecUseError> {
139 self.kill_opt(None).await
140 }
141
142 /// Send a signal to a container.
143 ///
144 /// Multiple signals are supported, not just SIGKILL.
145 ///
146 /// # Arguments
147 /// * `signal` - name of signal to send
148 ///
149 /// # Example
150 ///
151 /// ```rust
152 /// use passivized_docker_engine_client::DockerEngineClient;
153 /// use passivized_docker_engine_client::errors::DecError;
154 ///
155 /// async fn example() -> Result<(), DecError> {
156 /// let dec = DockerEngineClient::new()?;
157 ///
158 /// dec.container("example").kill_with("SIGTERM").await?;
159 /// Ok(())
160 /// }
161 /// ```
162 pub async fn kill_with<S: Into<String>>(&self, signal: S) -> Result<(), DecUseError> {
163 self.kill_opt(Some(signal.into())).await
164 }
165
166 async fn kill_opt(&self, signal: Option<String>) -> Result<(), DecUseError> {
167 let uri = self.client.url.containers().kill(&self.container_id, signal)?;
168 let response = self.client.http.post(uri)?.execute().await?;
169
170 response
171 .assert_unit_status(StatusCode::NO_CONTENT)
172 }
173
174 /// Pause a running container.
175 ///
176 /// # Example
177 ///
178 /// ```rust
179 /// use passivized_docker_engine_client::DockerEngineClient;
180 /// use passivized_docker_engine_client::errors::DecError;
181 ///
182 /// async fn example() -> Result<(), DecError> {
183 /// let dec = DockerEngineClient::new()?;
184 ///
185 /// dec.container("example").pause().await?;
186 /// Ok(())
187 /// }
188 /// ```
189 #[cfg(not(windows))] // Docker for Windows does not support pausing containers.
190 pub async fn pause(&self) -> Result<(), DecUseError> {
191 let uri = self.client.url.containers().pause(&self.container_id);
192 let response = self.client.http.post(uri)?.execute().await?;
193
194 response
195 .assert_unit_status(StatusCode::NO_CONTENT)
196 }
197
198 /// Delete a stopped container.
199 ///
200 /// Remove requests are NOT idempotent.
201 pub async fn remove(&self) -> Result<(), DecUseError> {
202 self.remove_with(RemoveContainerArgs::default()).await
203 }
204
205 /// Delete a container.
206 ///
207 /// Remove requests are NOT idempotent; attempting to remove a removed container will return an error.
208 ///
209 /// # Arguments
210 /// * `args` are additional request arguments for the removal.
211 ///
212 /// # Example
213 ///
214 /// ```rust
215 /// use passivized_docker_engine_client::DockerEngineClient;
216 /// use passivized_docker_engine_client::errors::DecError;
217 /// use passivized_docker_engine_client::requests::RemoveContainerArgs;
218 ///
219 /// async fn example() -> Result<(), DecError> {
220 /// let dec = DockerEngineClient::new()?;
221 ///
222 /// let args = RemoveContainerArgs::default()
223 /// .remove_volumes(true);
224 ///
225 /// dec.container("example").remove_with(args).await?;
226 ///
227 /// Ok(())
228 /// }
229 /// ```
230 pub async fn remove_with(&self, args: RemoveContainerArgs) -> Result<(), DecUseError> {
231 let uri = self.client.url.containers().remove(&self.container_id, args)?;
232 let response = self.client.http.delete(uri)?.execute().await?;
233
234 response
235 .assert_unit_status(StatusCode::NO_CONTENT)
236 }
237
238 /// Rename an existing container.
239 ///
240 /// # Arguments
241 /// * `new_name` - New name for the container.
242 ///
243 /// # Example
244 ///
245 /// ```rust
246 /// use passivized_docker_engine_client::DockerEngineClient;
247 /// use passivized_docker_engine_client::errors::DecError;
248 ///
249 /// async fn example() -> Result<(), DecError> {
250 /// let dec = DockerEngineClient::new()?;
251 ///
252 /// dec.container("example").rename("example2").await?;
253 ///
254 /// Ok(())
255 /// }
256 /// ```
257 pub async fn rename<NN: Into<String>>(&self, new_name: NN) -> Result<(), DecUseError> {
258 let uri = self.client.url.containers().rename(&self.container_id, new_name.into());
259 let response = self.client.http.post(uri)?.execute().await?;
260
261 response
262 .assert_unit_status(StatusCode::NO_CONTENT)
263 }
264
265 /// Start an existing container.
266 ///
267 /// This is idempotent.
268 ///
269 /// # Example
270 ///
271 /// ```rust
272 /// use passivized_docker_engine_client::DockerEngineClient;
273 /// use passivized_docker_engine_client::errors::DecError;
274 ///
275 /// async fn example() -> Result<(), DecError> {
276 /// let dec = DockerEngineClient::new()?;
277 ///
278 /// dec.container("example").start().await?;
279 ///
280 /// Ok(())
281 /// }
282 /// ```
283 pub async fn start(&self) -> Result<(), DecUseError> {
284 let uri = self.client.url.containers().start(&self.container_id);
285 let response = self.client.http.post(uri)?.execute().await?;
286
287 response
288 // See https://docs.docker.com/engine/api/v1.41/#tag/Container/operation/ContainerStart
289 .assert_unit_status_in(&[
290 // No error
291 StatusCode::NO_CONTENT,
292 // Already started
293 StatusCode::NOT_MODIFIED
294 ])
295 }
296
297 /// Stop a running container.
298 ///
299 /// This is idempotent.
300 ///
301 /// # Example
302 ///
303 /// ```rust
304 /// use passivized_docker_engine_client::DockerEngineClient;
305 /// use passivized_docker_engine_client::errors::DecError;
306 ///
307 /// async fn example() -> Result<(), DecError> {
308 /// let dec = DockerEngineClient::new()?;
309 ///
310 /// dec.container("example").stop().await?;
311 ///
312 /// Ok(())
313 /// }
314 /// ```
315 pub async fn stop(&self) -> Result<(), DecUseError> {
316 let uri = self.client.url.containers().stop(&self.container_id);
317 let response = self.client.http.post(uri)?.execute().await?;
318
319 response
320 // See https://docs.docker.com/engine/api/v1.41/#tag/Container/operation/ContainerStop
321 .assert_unit_status_in(&[
322 // No error
323 StatusCode::NO_CONTENT,
324 // Already stopped
325 StatusCode::NOT_MODIFIED
326 ])
327 }
328
329 /// Get a list of processes running inside the container.
330 pub async fn top(&self) -> Result<TopResponse, DecUseError> {
331 self.top_opt(None).await
332 }
333
334 /// Get a list of processes running inside the container, customizing the output.
335 pub async fn top_with(&self, ps_args: String) -> Result<TopResponse, DecUseError> {
336 self.top_opt(Some(ps_args)).await
337 }
338
339 async fn top_opt(&self, ps_args: Option<String>) -> Result<TopResponse, DecUseError> {
340 let uri = self.client.url.containers().top(&self.container_id, ps_args)?;
341 let response = self.client.http.get(uri)?.execute().await?;
342
343 response
344 .assert_item_status(StatusCode::OK)?
345 .parse()
346 }
347
348 /// Unpause a paused container.
349 ///
350 /// # Example
351 ///
352 /// ```rust
353 /// use passivized_docker_engine_client::DockerEngineClient;
354 /// use passivized_docker_engine_client::errors::DecError;
355 ///
356 /// async fn example() -> Result<(), DecError> {
357 /// let dec = DockerEngineClient::new()?;
358 ///
359 /// dec.container("example").unpause().await?;
360 ///
361 /// Ok(())
362 /// }
363 /// ```
364 #[cfg(not(windows))] // Docker for Windows does not support pausing containers.
365 pub async fn unpause(&self) -> Result<(), DecUseError> {
366 let uri = self.client.url.containers().unpause(&self.container_id);
367 let response = self.client.http.post(uri)?.execute().await?;
368
369 response
370 .assert_unit_status(StatusCode::NO_CONTENT)
371 }
372
373 /// Wait for a container to reach a specific state.
374 ///
375 /// For process stop states, blocks until the container stops, then returns the exit code.
376 ///
377 /// # Arguments
378 /// `condition` - Desired condition to wait for.
379 ///
380 /// # Example
381 ///
382 /// ```rust
383 /// use passivized_docker_engine_client::DockerEngineClient;
384 /// use passivized_docker_engine_client::errors::DecError;
385 /// use passivized_docker_engine_client::requests::WaitCondition;
386 ///
387 /// async fn example() -> Result<(), DecError> {
388 /// let dec = DockerEngineClient::new()?;
389 ///
390 /// dec.container("example").wait(WaitCondition::NotRunning).await?;
391 ///
392 /// Ok(())
393 /// }
394 /// ```
395 pub async fn wait(&self, condition: WaitCondition) -> Result<WaitResponse, DecUseError> {
396 let uri = self.client.url.containers().wait(&self.container_id, condition);
397 let response = self.client.http.post(uri)?.execute().await?;
398
399 response
400 .assert_item_status(StatusCode::OK)?
401 .parse()
402 }
403
404}