docker_sync/
docker.rs

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    //
66    // Networks
67    //
68
69    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    //
123    // Containers
124    //
125
126    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    //
231    // Image
232    //
233
234    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}