1#![allow(non_snake_case)]
2use std::collections::HashMap;
3
4use api::api_utils;
5use api::DockerApiClient;
6use utils::Response;
7
8use serde_json;
9
10#[derive(Serialize, Deserialize, Debug)]
11pub struct Container {
12 pub Id: String,
13 pub Names: Vec<String>,
14 pub Image: String,
15 pub ImageID: String,
16 pub Command: String,
17 pub State: String,
18 pub Status: String,
19 pub Ports: Vec<Port>,
20 pub Labels: Option<HashMap<String, String>>,
21
22 #[serde(default)]
23 pub SizeRw: Option<u64>,
24
25 #[serde(default)]
26 pub SizeRootFs: u64,
27 pub HostConfig: HostConfig,
28 pub Mounts: Vec<Mounts>,
29}
30
31#[derive(Serialize, Deserialize, Debug)]
32pub struct Port {
33 pub PrivatePort: u32,
34 pub PublicPort: u32,
35 pub Type: String,
36}
37
38#[derive(Serialize, Deserialize, Debug)]
39pub struct HostConfig {
40 pub NetworkMode: String,
41}
42
43#[derive(Serialize, Deserialize, Debug)]
44pub struct Mounts {
45 pub Name: String,
46 pub Source: String,
47 pub Destination: String,
48 pub Driver: String,
49 pub Mode: String,
50 pub RW: bool,
51 pub Propagation: String,
52}
53
54#[derive(Serialize, Deserialize, Debug, Default)]
58pub struct ContainerConfig {
59 pub Image: String,
60 pub Cmd: Vec<String>,
61
62 pub Hostname: String,
63 pub Domainname: String,
64 pub User: String,
65 pub AttachStdin: bool,
66 pub AttachStdout: bool,
67 pub AttachStderr: bool,
68 pub Tty: bool,
69 pub OpenStdin: bool,
70 pub StdinOnce: bool,
71 pub Env: Vec<String>,
72 pub Entrypoint: Option<String>,
73 pub Labels: Option<HashMap<String, String>>,
74 pub WorkingDir: String,
75}
76
77#[derive(Serialize, Deserialize, Debug)]
78pub struct CreateContainerResponse {
79 pub Id: String,
80}
81
82#[derive(Serialize, Deserialize, Debug, Default)]
83pub struct ContainerState {
84 pub Status: String,
85 pub Running: bool,
86 pub Paused: bool,
87 pub Restarting: bool,
88 pub OOMKilled: bool,
89 pub Dead: bool,
90 pub Pid: u64,
91 pub ExitCode: u64,
92 pub Error: String,
93 pub StartedAt: String,
94 pub FinishedAt: String,
95}
96
97#[derive(Serialize, Deserialize, Debug, Default)]
99pub struct ContainerDetails {
100 pub Id: String,
101 pub Created: String,
102 pub Path: String,
103 pub Platform: Option<String>,
104 pub Args: Vec<String>,
105 pub State: ContainerState,
106 pub Image: String,
107 pub ResolvConfPath: String,
108 pub Name: String,
109 pub HostnamePath: String,
110 pub HostsPath: String,
111 pub LogPath: String,
112 pub RestartCount: u64,
113 pub Driver: String,
114 pub MountLabel: String,
115 pub ProcessLabel: String,
116 pub AppArmorProfile: String,
117 pub ExecIDs: Option<String>,
118 pub HostConfig: serde_json::Value,
119 pub Config: ContainerConfig,
120}
121
122#[derive(Serialize, Deserialize, Debug, Default)]
123pub struct ContainerFsChange {
124 Path: String,
125 Kind: u8,
126}
127
128pub trait Containers: DockerApiClient {
129 fn get_response_from_api(
134 &self,
135 api_endpoint: &str,
136 method: &str,
137 body: &str,
138 ) -> Result<Response, String> {
139 let req = match api_utils::get_formatted_api_request(
140 api_endpoint,
141 method,
142 body,
143 ) {
144 Some(req) => req,
145 None => return Err("Error while preparing request".to_string()),
146 };
147
148 match self.request(&req) {
149 Some(resp) => match Response::parse_http_response(resp) {
150 Ok(response) => Ok(response),
151 Err(err) => {
152 Err(format!("Response body was not valid : {}", err))
153 }
154 },
155 None => Err("Got no response from docker host.".to_string()),
156 }
157 }
158
159 fn get_containers(
162 &self,
163 api_endpoint: &str,
164 method: &str,
165 query_param: &str,
166 ) -> Result<Vec<Container>, String> {
167 let json_resp =
168 match self.get_response_from_api(api_endpoint, method, query_param)
169 {
170 Ok(resp) => {
171 if resp.status_code == 200 {
172 resp.body
173 } else {
174 return Err(format!(
175 "Invalid Response : {} :: {}",
176 resp.status_code, resp.body
177 ));
178 }
179 }
180 Err(err) => return Err(err),
181 };
182
183 let containers: Vec<Container> = match serde_json::from_str(&json_resp)
184 {
185 Ok(info) => info,
186 Err(err) => {
187 return Err(format!(
188 "Error while deserializing JSON response : {}",
189 err
190 ))
191 }
192 };
193
194 return Ok(containers);
195 }
196
197 fn list_running_containers(
222 &self,
223 limit: Option<u32>,
224 ) -> Result<Vec<Container>, String> {
225 let api_endpoint = "/containers/json";
226 let method = "GET";
227
228 let query_params = match limit {
229 Some(limit) => format!("?size=true&limit={}", limit),
230 None => "?size=true".to_string(),
231 };
232
233 self.get_containers(api_endpoint, method, &query_params)
234 }
235
236 fn list_all_containers(
238 &self,
239 limit: Option<u32>,
240 ) -> Result<Vec<Container>, String> {
241 let api_endpoint = "/containers/json";
242 let method = "GET";
243
244 let query_params = match limit {
245 Some(limit) => format!("?all=true&size=true&limit={}", limit),
246 None => "?all=true&size=true".to_string(),
247 };
248
249 self.get_containers(api_endpoint, method, &query_params)
250 }
251
252 fn get_container_details_with_filter(
256 &self,
257 filter: &str,
258 limit: Option<u32>,
259 ) -> Result<Vec<Container>, String> {
260 let api_endpoint = "/containers/json";
261 let method = "GET";
262
263 let query_params = match limit {
264 Some(limit) => format!(
265 "?all=true&size=true&limit={}&filter={}",
266 limit, filter
267 ),
268 None => format!("?all=true&size=true&filter={}", filter),
269 };
270
271 self.get_containers(api_endpoint, method, &query_params)
272 }
273
274 fn create_container(
278 &self,
279 name: &str,
280 config: ContainerConfig,
281 ) -> Result<CreateContainerResponse, String> {
282 let api_endpoint = format!("/containers/create?name={}", name);
283 let method = "POST";
284 let body = match serde_json::to_string(&config) {
285 Ok(body) => body,
286 Err(err) => {
287 return Err(format!(
288 "Error while serialize Cotainer config : {}",
289 err
290 ))
291 }
292 };
293
294 match self.get_response_from_api(&api_endpoint, method, &body) {
295 Ok(resp) => {
296 if resp.status_code != 201 {
297 return Err(format!(
298 "Invalid Request : {} :: {}",
299 resp.status_code, resp.body
300 ));
301 }
302 match serde_json::from_str(&resp.body) {
303 Ok(info) => return Ok(info),
304 Err(err) => {
305 return Err(format!(
306 "Error while deserializing JSON response : {}",
307 err
308 ))
309 }
310 };
311 }
312 Err(err) => Err(err),
313 }
314 }
315
316 fn create_container_minimal(
347 &self,
348 name: &str,
349 image: &str,
350 cmd: Vec<String>,
351 ) -> Result<CreateContainerResponse, String> {
352 let config = ContainerConfig {
353 Image: image.to_string(),
354 Cmd: cmd,
355 ..Default::default()
356 };
357
358 self.create_container(name, config)
359 }
360
361 fn inspect_container(&self, id: &str) -> Result<ContainerDetails, String> {
387 let api_endpoint = format!("/containers/{id}/json", id = id);
388 let method = "GET";
389
390 match self.get_response_from_api(&api_endpoint, method, "") {
391 Ok(resp) => {
392 if resp.status_code != 200 {
393 return Err(format!(
394 "Invalid Request : {} :: {}",
395 resp.status_code, resp.body
396 ));
397 }
398 match serde_json::from_str(&resp.body) {
399 Ok(info) => return Ok(info),
400 Err(err) => {
401 return Err(format!(
402 "Error while deserializing JSON response : {}",
403 err
404 ))
405 }
406 };
407 }
408 Err(err) => Err(err),
409 }
410 }
411
412 fn get_container_filesystem_changes(
415 &self,
416 id: &str,
417 ) -> Result<Vec<ContainerFsChange>, String> {
418 let api_endpoint = format!("/containers/{id}/changes", id = id);
419 let method = "GET";
420
421 match self.get_response_from_api(&api_endpoint, method, "") {
422 Ok(resp) => {
423 if resp.status_code != 200 {
427 return Err(format!(
428 "Invalid Request : {} :: {}",
429 resp.status_code, resp.body
430 ));
431 }
432 if resp.body == "null" {
433 return Ok(Vec::new());
434 }
435
436 match serde_json::from_str(&resp.body) {
437 Ok(info) => return Ok(info),
438 Err(err) => {
439 return Err(format!(
440 "Error while deserializing JSON response : {}",
441 err
442 ))
443 }
444 };
445 }
446 Err(err) => Err(err),
447 }
448 }
449
450 fn manipulate_container_status(
496 &self,
497 action: &str,
498 id: &str,
499 params: &str,
500 ) -> Result<String, String> {
501 let api_endpoint = format!(
502 "/containers/{id}/{action}",
503 id = id,
504 action = action
505 );
506 let method = "GET";
507
508 match self.get_response_from_api(&api_endpoint, method, params) {
509 Ok(resp) => {
510 if resp.status_code == 204 {
511 Ok(format!("Container {} successful", action))
512 } else if resp.status_code == 304 {
513 Err(format!("Container already {}ed", action))
514 } else {
515 Err(format!(
516 "Error while requesting Docker API : {} :: {}",
517 resp.status_code, resp.body
518 ))
519 }
520 }
521 Err(err) => return Err(err),
522 }
523 }
524
525 fn start_container(&self, id: &str) -> Result<String, String> {
526 self.manipulate_container_status("start", id, "")
527 }
528
529 fn stop_container(
530 &self,
531 id: &str,
532 delay: Option<&str>,
533 ) -> Result<String, String> {
534 let param = match delay {
535 Some(d) => format!("t={}", d),
536 None => String::new(),
537 };
538 self.manipulate_container_status("stop", id, ¶m)
539 }
540
541 fn pause_container(&self, id: &str) -> Result<String, String> {
542 self.manipulate_container_status("pause", id, "")
543 }
544
545 fn unpause_container(&self, id: &str) -> Result<String, String> {
546 self.manipulate_container_status("unpause", id, "")
547 }
548
549 fn restart_container(
550 &self,
551 id: &str,
552 delay: Option<&str>,
553 ) -> Result<String, String> {
554 let param = match delay {
555 Some(d) => format!("t={}", d),
556 None => String::new(),
557 };
558 self.manipulate_container_status("restart", id, ¶m)
559 }
560
561 fn kill_container(
562 &self,
563 id: &str,
564 signal: Option<&str>,
565 ) -> Result<String, String> {
566 let param = match signal {
567 Some(sig) => format!("signal={}", sig),
568 None => String::new(),
569 };
570 self.manipulate_container_status("kill", id, ¶m)
571 }
572
573 fn rename_container(&self, id: &str, name: &str) -> Result<String, String> {
574 let name_param = &format!("name={}", name);
575 self.manipulate_container_status("rename", id, name_param)
576 }
577}