chord_util/docker/
container.rs1use 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}