1use crate::container::{Container, ContainerInfo};
2use crate::event::Event;
3use crate::filesystem::FilesystemChange;
4use crate::image::Image;
5use crate::network::{Network, NetworkCreate};
6use crate::process::{Process, Top};
7use crate::stats::Stats;
8use crate::system::SystemInfo;
9use crate::version::Version;
10use http::method::Method;
11use isahc::{config::Dialer, prelude::*, send, Body, Request};
12use std::io::{Error, ErrorKind, Read};
13use std::path::Path;
14
15pub struct Docker {
16 dialer: Dialer,
17}
18
19impl Docker {
20 pub fn connect() -> std::io::Result<Docker> {
21 let path = String::from("/var/run/docker.sock");
22 let file = Path::new(&path);
23 if !file.exists() {
24 return Err(Error::new(
25 ErrorKind::NotFound,
26 format!("{} not found.", path),
27 ));
28 }
29 let dialer = Dialer::unix_socket(path);
30 Ok(Docker { dialer })
31 }
32
33 fn request(&self, method: Method, url: &str, body: String) -> std::io::Result<String> {
34 #[cfg(unix)]
35 let req = Request::builder()
36 .uri(format!("http://localhost{}", url))
37 .header("Content-Type", "application/json")
38 .header("Accept", "application/json")
39 .method(method)
40 .dial(self.dialer.clone())
41 .body(Body::from(body))
42 .expect("failed to build request");
43
44 let mut res = send(req)?;
45
46 if !res.status().is_success() {
47 let mut reason = String::new();
48 if res.status().is_client_error() {
49 reason.push_str("Client error:");
50 } else if res.status().is_server_error() {
51 reason.push_str("Server error:");
52 }
53 reason.push_str(res.status().canonical_reason().unwrap());
54 return Err(Error::new(ErrorKind::Other, reason));
55 }
56
57 let body = res.body_mut();
58 let mut buf = String::new();
59 if let Err(e) = body.read_to_string(&mut buf) {
60 panic!("{}", e);
61 }
62 Ok(buf)
63 }
64
65 pub fn get_networks(&mut self) -> std::io::Result<Vec<Network>> {
70 let body = self.request(Method::GET, "/networks", "".to_string())?;
71
72 match serde_json::from_str(&body) {
73 Ok(networks) => Ok(networks),
74 Err(e) => Err(std::io::Error::new(
75 std::io::ErrorKind::InvalidInput,
76 e.to_string(),
77 )),
78 }
79 }
80
81 pub fn create_network(&mut self, network: NetworkCreate) -> std::io::Result<String> {
82 let body = self.request(
83 Method::POST,
84 "/networks/create",
85 serde_json::to_string(&network).unwrap(),
86 )?;
87
88 let status: serde_json::Value = match serde_json::from_str(&body) {
89 Ok(status) => status,
90 Err(e) => {
91 return Err(std::io::Error::new(
92 std::io::ErrorKind::InvalidInput,
93 e.to_string(),
94 ));
95 }
96 };
97 match status.get("Id") {
98 Some(id) => Ok(id.as_str().unwrap().to_string()),
99 _ => Err(std::io::Error::new(
100 std::io::ErrorKind::InvalidInput,
101 status.get("message").unwrap().to_string(),
102 )),
103 }
104 }
105
106 pub fn delete_network(&mut self, id_or_name: &str) -> std::io::Result<String> {
107 let body = self.request(
108 Method::DELETE,
109 &format!("/networks/{}", id_or_name),
110 "".to_string(),
111 )?;
112
113 match serde_json::from_str::<serde_json::Value>(&body) {
114 Ok(status) => Err(std::io::Error::new(
115 std::io::ErrorKind::InvalidInput,
116 status["message"].to_string(),
117 )),
118 Err(_e) => Ok("".to_string()),
119 }
120 }
121
122 pub fn get_containers(&mut self, all: bool) -> std::io::Result<Vec<Container>> {
127 let a = match all {
128 true => "1",
129 false => "0",
130 };
131
132 let body = self.request(
133 Method::GET,
134 &format!("/containers/json?all={}&size=1", a),
135 "".to_string(),
136 )?;
137
138 match serde_json::from_str(&body) {
139 Ok(containers) => Ok(containers),
140 Err(e) => Err(std::io::Error::new(
141 std::io::ErrorKind::InvalidInput,
142 e.to_string(),
143 )),
144 }
145 }
146
147 pub fn get_processes(&mut self, container: &Container) -> std::io::Result<Vec<Process>> {
148 let body = self.request(
149 Method::GET,
150 &format!("/containers/{}/top", container.Id),
151 "".to_string(),
152 )?;
153
154 let top: Top = match serde_json::from_str(&body) {
155 Ok(top) => top,
156 Err(e) => {
157 let err = std::io::Error::new(std::io::ErrorKind::InvalidInput, e.to_string());
158 return Err(err);
159 }
160 };
161
162 let mut processes: Vec<Process> = Vec::new();
163 let process_iter = top.Processes.iter();
164
165 for process in process_iter {
166 let mut p = Process {
167 user: String::new(),
168 pid: String::new(),
169 cpu: None,
170 memory: None,
171 vsz: None,
172 rss: None,
173 tty: None,
174 stat: None,
175 start: None,
176 time: None,
177 command: String::new(),
178 };
179
180 let i: usize = 0;
181 let value_iter = process.iter();
182 for value in value_iter {
183 let key = &top.Titles[i];
184 match key.as_ref() {
185 "USER" => p.user = value.clone(),
186 "PID" => p.pid = value.clone(),
187 "%CPU" => p.cpu = Some(value.clone()),
188 "%MEM" => p.memory = Some(value.clone()),
189 "VSZ" => p.vsz = Some(value.clone()),
190 "RSS" => p.rss = Some(value.clone()),
191 "TTY" => p.tty = Some(value.clone()),
192 "STAT" => p.stat = Some(value.clone()),
193 "START" => p.start = Some(value.clone()),
194 "TIME" => p.time = Some(value.clone()),
195 "COMMAND" => p.command = value.clone(),
196 _ => {}
197 }
198 }
199
200 processes.push(p);
201 }
202
203 Ok(processes)
204 }
205
206 pub fn get_stats(&mut self, container: &Container) -> std::io::Result<Stats> {
207 if !container.Status.contains("Up") {
208 let err = std::io::Error::new(
209 std::io::ErrorKind::InvalidInput,
210 "The container is already stopped.",
211 );
212 return Err(err);
213 }
214
215 let body = self.request(
216 Method::GET,
217 &format!("/containers/{}/stats", container.Id),
218 "".to_string(),
219 )?;
220
221 match serde_json::from_str(&body) {
222 Ok(stats) => Ok(stats),
223 Err(e) => Err(std::io::Error::new(
224 std::io::ErrorKind::InvalidInput,
225 e.to_string(),
226 )),
227 }
228 }
229
230 pub fn get_images(&mut self, all: bool) -> std::io::Result<Vec<Image>> {
235 let a = match all {
236 true => "1",
237 false => "0",
238 };
239 let body = self.request(
240 Method::GET,
241 &format!("/images/json?all={}", a),
242 "".to_string(),
243 )?;
244
245 match serde_json::from_str(&body) {
246 Ok(images) => Ok(images),
247 Err(e) => Err(std::io::Error::new(
248 std::io::ErrorKind::InvalidInput,
249 e.to_string(),
250 )),
251 }
252 }
253
254 pub fn get_system_info(&mut self) -> std::io::Result<SystemInfo> {
255 let body = self.request(Method::GET, "/info", "".to_string())?;
256
257 match serde_json::from_str(&body) {
258 Ok(info) => Ok(info),
259 Err(e) => Err(std::io::Error::new(
260 std::io::ErrorKind::InvalidInput,
261 e.to_string(),
262 )),
263 }
264 }
265
266 pub fn get_container_info(&mut self, container: &Container) -> std::io::Result<ContainerInfo> {
267 let body = self.request(
268 Method::GET,
269 &format!("/containers/{}/json", container.Id),
270 "".to_string(),
271 )?;
272
273 match serde_json::from_str(&body) {
274 Ok(body) => Ok(body),
275 Err(e) => Err(std::io::Error::new(
276 std::io::ErrorKind::InvalidInput,
277 e.to_string(),
278 )),
279 }
280 }
281
282 pub fn get_filesystem_changes(
283 &mut self,
284 container: &Container,
285 ) -> std::io::Result<Vec<FilesystemChange>> {
286 let body = self.request(
287 Method::GET,
288 &format!("/containers/{}/changes", container.Id),
289 "".to_string(),
290 )?;
291
292 match serde_json::from_str(&body) {
293 Ok(body) => Ok(body),
294 Err(e) => Err(std::io::Error::new(
295 std::io::ErrorKind::InvalidInput,
296 e.to_string(),
297 )),
298 }
299 }
300
301 pub fn ping(&mut self) -> std::io::Result<String> {
302 let result = self.request(Method::GET, "/_ping", "".to_string())?;
303 Ok(result)
304 }
305
306 pub fn get_version(&mut self) -> std::io::Result<Version> {
307 let body = self.request(Method::GET, "/version", "".to_string())?;
308
309 match serde_json::from_str(&body) {
310 Ok(r_body) => Ok(r_body),
311 Err(e) => Err(std::io::Error::new(
312 std::io::ErrorKind::InvalidInput,
313 e.to_string(),
314 )),
315 }
316 }
317
318 pub fn get_events(
319 &mut self,
320 since: Option<String>,
321 until: Option<String>,
322 ) -> std::io::Result<Vec<Event>> {
323 let mut url = "/events".to_string();
324 let mut options = "".to_string();
325 if let Some(since_val) = since {
326 options.push_str("since=");
327 options.push_str(&since_val);
328 }
329 if let Some(until_val) = until {
330 if !options.is_empty() {
331 options.push('&');
332 }
333 options.push_str("until=");
334 options.push_str(&until_val);
335 }
336 if !options.is_empty() {
337 url.push('?');
338 url.push_str(&options);
339 }
340 let body = self.request(Method::GET, &url, "".to_string())?;
341 match serde_json::from_str(&body) {
342 Ok(r_body) => Ok(r_body),
343 Err(e) => Err(std::io::Error::new(
344 std::io::ErrorKind::InvalidInput,
345 e.to_string(),
346 )),
347 }
348 }
349}