Skip to main content

chord_util/docker/
container.rs

1use std::sync::Arc;
2
3use log::{trace, warn};
4use reqwest::Method;
5
6use chord_core::future::task::spawn;
7use chord_core::value::{Map, Value};
8
9use crate::docker::engine::Engine;
10use crate::docker::Error;
11use crate::docker::Error::*;
12
13#[derive(Default)]
14pub struct Arg {
15    image: String,
16    host_config: Option<Map>,
17    env: Option<Vec<String>>,
18    cmd: Option<Vec<String>>,
19}
20
21impl Arg {
22    pub fn image(mut self, image: String) -> Arg {
23        self.image = image;
24        self
25    }
26
27    pub fn env(mut self, env: Vec<String>) -> Arg {
28        self.env = Some(env);
29        self
30    }
31
32    pub fn cmd(mut self, cmd: Vec<String>) -> Arg {
33        self.cmd = Some(cmd);
34        self
35    }
36
37    pub fn host_config(mut self, host_config: Map) -> Arg {
38        self.host_config = Some(host_config);
39        self
40    }
41
42    fn into_value(self) -> Value {
43        let mut v = Map::new();
44        v.insert("Image".to_string(), Value::String(self.image));
45        if let Some(a) = self.host_config {
46            v.insert("HostConfig".to_string(), Value::Object(a));
47        }
48        if let Some(a) = self.env {
49            v.insert(
50                "Env".to_string(),
51                Value::Array(a.into_iter().map(|b| Value::String(b)).collect()),
52            );
53        }
54        if let Some(a) = self.cmd {
55            v.insert(
56                "Cmd".to_string(),
57                Value::Array(a.into_iter().map(|b| Value::String(b)).collect()),
58            );
59        }
60        Value::Object(v)
61    }
62}
63
64pub struct Container {
65    engine: Arc<Engine>,
66    name: String,
67}
68
69impl Container {
70    pub async fn new(docker: Arc<Engine>, name: &str, arg: Arg) -> Result<Container, Error> {
71        let arg = arg.into_value();
72        trace!("container create {}, {}", name, arg);
73        docker
74            .call(
75                format!("containers/create?name={}", name).as_str(),
76                Method::POST,
77                Some(arg),
78                1,
79            )
80            .await
81            .map(|_| Container {
82                name: name.into(),
83                engine: docker,
84            })
85    }
86
87    pub async fn start(&mut self) -> Result<Vec<String>, Error> {
88        trace!("container start {}", self.name);
89        self.engine
90            .call(
91                format!("containers/{}/start", self.name).as_str(),
92                Method::POST,
93                None,
94                1,
95            )
96            .await
97    }
98
99    pub async fn wait(&self) -> Result<Vec<String>, Error> {
100        trace!("container wait {}", self.name);
101        let res = self
102            .engine
103            .call(
104                format!("containers/{}/wait", self.name).as_str(),
105                Method::POST,
106                None,
107                1,
108            )
109            .await?;
110        if res.len() == 1 && res[0].contains("\"StatusCode\":0") {
111            return Ok(res);
112        } else {
113            return Err(Container(res[0].clone()));
114        }
115    }
116
117    pub async fn tail(&self, stderr: bool, tail: usize) -> Result<Vec<String>, Error> {
118        trace!("container log {}", self.name);
119        if stderr {
120            self.engine
121                .call_with_op(
122                    format!("containers/{}/logs?stderr=true&tail={}", self.name, tail).as_str(),
123                    Method::GET,
124                    None,
125                    tail,
126                    |buf| String::from_utf8_lossy(&buf[8..]).to_string(),
127                )
128                .await
129        } else {
130            self.engine
131                .call_with_op(
132                    format!("containers/{}/logs?stdout=true&tail={}", self.name, tail).as_str(),
133                    Method::GET,
134                    None,
135                    tail,
136                    |buf| String::from_utf8_lossy(&buf[8..]).to_string(),
137                )
138                .await
139        }
140    }
141}
142
143impl Drop for Container {
144    fn drop(&mut self) {
145        let uri = format!("containers/{}?force=true", self.name);
146        let engine = self.engine.clone();
147        let name = self.name.clone();
148        let _ = spawn(async move {
149            engine
150                .call(uri.as_str(), Method::DELETE, None, 1)
151                .await
152                .map_err(|_| {
153                    warn!("container remove fail {}", name);
154                })
155                .map(|_| {
156                    trace!("container remove {}", name);
157                })
158        });
159    }
160}