rs_docker/
docker.rs

1use std;
2
3use crate::container::{Container, ContainerCreate, ContainerInfo};
4use crate::filesystem::FilesystemChange;
5use crate::image::{Image, ImageStatus};
6use crate::network::{Network, NetworkCreate};
7use crate::process::{Process, Top};
8use crate::stats::Stats;
9use crate::system::SystemInfo;
10use crate::version::Version;
11
12use crate::futures::FutureExt;
13use crate::futures::TryFutureExt;
14
15use hyper::{Body, Client, Method, Request, Uri};
16
17use hyper::client::HttpConnector;
18
19use hyperlocal::UnixConnector;
20
21use tokio::runtime::{Handle, Runtime};
22
23pub struct Docker {
24    protocol: Protocol,
25    path: String,
26    hyperlocal_client: Option<Client<UnixConnector, Body>>,
27    hyper_client: Option<Client<HttpConnector, Body>>,
28}
29
30enum Protocol {
31    UNIX,
32    TCP,
33}
34
35impl Docker {
36    pub fn connect(addr: &str) -> std::io::Result<Docker> {
37        let components: Vec<&str> = addr.split("://").collect();
38        if components.len() != 2 {
39            let err =
40                std::io::Error::new(std::io::ErrorKind::InvalidInput, "The address is invalid.");
41            return Err(err);
42        }
43
44        let protocol = components[0];
45        let path = components[1].to_string();
46
47        let protocol = match protocol {
48            "unix" => Protocol::UNIX,
49            "tcp" => Protocol::TCP,
50            _ => {
51                let err = std::io::Error::new(
52                    std::io::ErrorKind::InvalidInput,
53                    "The protocol is not supported.",
54                );
55                return Err(err);
56            }
57        };
58
59        let hyperlocal_client = match protocol {
60            Protocol::UNIX => {
61                let unix_connector = UnixConnector;
62                Some(Client::builder().build(unix_connector))
63            }
64            _ => None,
65        };
66
67        let hyper_client = match protocol {
68            Protocol::TCP => Some(Client::new()),
69            _ => None,
70        };
71
72        let docker = Docker {
73            protocol: protocol,
74            path: path,
75            hyperlocal_client: hyperlocal_client,
76            hyper_client: hyper_client,
77        };
78        return Ok(docker);
79    }
80
81    fn request_file(&self, method: Method, url: &str, file: Vec<u8>, content_type: &str) -> String {
82        let req = Request::builder()
83            .uri(match self.protocol {
84                Protocol::UNIX => hyperlocal::Uri::new(self.path.clone(), url).into(),
85                _ => format!("{}{}", self.path, url).parse::<Uri>().unwrap(),
86            })
87            .header("Content-Type", content_type)
88            .header("Accept", "application/json")
89            .method(method)
90            .body(Body::from(file))
91            .expect("failed to build request");
92        match Handle::try_current() {
93            Ok(handle) => handle.block_on(
94                match self.protocol {
95                    Protocol::UNIX => self.hyperlocal_client.as_ref().unwrap().request(req),
96                    Protocol::TCP => self.hyper_client.as_ref().unwrap().request(req),
97                }
98                .and_then(|res| hyper::body::to_bytes(res.into_body()))
99                .map(|body| String::from_utf8(body.expect("Body should not have an error").to_vec()).unwrap())
100                ),
101            Err(_) => Runtime::new().unwrap().block_on(
102                match self.protocol {
103                    Protocol::UNIX => self.hyperlocal_client.as_ref().unwrap().request(req),
104                    Protocol::TCP => self.hyper_client.as_ref().unwrap().request(req),
105                }
106                .and_then(|res| hyper::body::to_bytes(res.into_body()))
107                .map(|body| String::from_utf8(body.expect("Body should not have an error").to_vec()).unwrap()),
108            )
109        }
110    }
111
112    fn request(&self, method: Method, url: &str, body: String) -> String {
113        self.request_file(method, url, body.into_bytes(), "application/json")
114    }
115
116    //
117    // Networks
118    //
119
120    pub fn get_networks(&mut self) -> std::io::Result<Vec<Network>> {
121        let body = self.request(Method::GET, "/networks", "".to_string());
122
123        match serde_json::from_str(&body) {
124            Ok(networks) => Ok(networks),
125            Err(e) => Err(std::io::Error::new(
126                std::io::ErrorKind::InvalidInput,
127                e.to_string(),
128            )),
129        }
130    }
131
132    pub fn create_network(&mut self, network: NetworkCreate) -> std::io::Result<String> {
133        let body = self.request(
134            Method::POST,
135            "/networks/create",
136            serde_json::to_string(&network).unwrap(),
137        );
138
139        let status: serde_json::Value = match serde_json::from_str(&body) {
140            Ok(status) => status,
141            Err(e) => {
142                return Err(std::io::Error::new(
143                    std::io::ErrorKind::InvalidInput,
144                    e.to_string(),
145                ));
146            }
147        };
148        match status.get("Id") {
149            Some(id) => Ok(id.as_str().unwrap().to_string()),
150            _ => Err(std::io::Error::new(
151                std::io::ErrorKind::InvalidInput,
152                status.get("message").unwrap().to_string(),
153            )),
154        }
155    }
156
157    pub fn delete_network(&mut self, id_or_name: &str) -> std::io::Result<String> {
158        let body = self.request(
159            Method::DELETE,
160            &format!("/networks/{}", id_or_name),
161            "".to_string(),
162        );
163
164        match serde_json::from_str::<serde_json::Value>(&body) {
165            Ok(status) => Err(std::io::Error::new(
166                std::io::ErrorKind::InvalidInput,
167                status["message"].to_string(),
168            )),
169            Err(_e) => Ok("".to_string()),
170        }
171    }
172
173    //
174    // Containers
175    //
176
177    pub fn start_container(&mut self, id_or_name: &str) -> std::io::Result<String> {
178        let body = self.request(
179            Method::POST,
180            &format!("/containers/{}/start", id_or_name),
181            "".to_string(),
182        );
183
184        match serde_json::from_str::<serde_json::Value>(&body) {
185            Ok(status) => Err(std::io::Error::new(
186                std::io::ErrorKind::InvalidInput,
187                status["message"].to_string(),
188            )),
189            Err(_e) => Ok("".to_string()),
190        }
191    }
192
193    pub fn stop_container(&mut self, id_or_name: &str) -> std::io::Result<String> {
194        let body = self.request(
195            Method::POST,
196            &format!("/containers/{}/stop", id_or_name),
197            "".to_string(),
198        );
199
200        match serde_json::from_str::<serde_json::Value>(&body) {
201            Ok(status) => Err(std::io::Error::new(
202                std::io::ErrorKind::InvalidInput,
203                status["message"].to_string(),
204            )),
205            Err(_e) => Ok("".to_string()),
206        }
207    }
208
209    pub fn delete_container(&mut self, id_or_name: &str) -> std::io::Result<String> {
210        let body = self.request(
211            Method::DELETE,
212            &format!("/containers/{}", id_or_name),
213            "".to_string(),
214        );
215
216        match serde_json::from_str::<serde_json::Value>(&body) {
217            Ok(status) => Err(std::io::Error::new(
218                std::io::ErrorKind::InvalidInput,
219                status["message"].to_string(),
220            )),
221            Err(_e) => Ok("".to_string()),
222        }
223    }
224
225    pub fn create_container(
226        &mut self,
227        name: String,
228        container: ContainerCreate,
229    ) -> std::io::Result<String> {
230        let body = self.request(
231            Method::POST,
232            &format!("/containers/create?name={}", name),
233            serde_json::to_string(&container).unwrap(),
234        );
235
236        let status: serde_json::Value = match serde_json::from_str(&body) {
237            Ok(status) => status,
238            Err(e) => {
239                return Err(std::io::Error::new(
240                    std::io::ErrorKind::InvalidInput,
241                    e.to_string(),
242                ));
243            }
244        };
245        match status.get("Id") {
246            Some(id) => Ok(id.as_str().unwrap().to_string()),
247            _ => Err(std::io::Error::new(
248                std::io::ErrorKind::InvalidInput,
249                status.get("message").unwrap().to_string(),
250            )),
251        }
252    }
253
254    pub fn get_containers(&mut self, all: bool) -> std::io::Result<Vec<Container>> {
255        let a = match all {
256            true => "1",
257            false => "0",
258        };
259
260        let body = self.request(
261            Method::GET,
262            &format!("/containers/json?all={}&size=1", a),
263            "".to_string(),
264        );
265
266        let containers: Vec<Container> = match serde_json::from_str(&body) {
267            Ok(containers) => containers,
268            Err(e) => {
269                let err = std::io::Error::new(std::io::ErrorKind::InvalidInput, e.to_string());
270                return Err(err);
271            }
272        };
273
274        return Ok(containers);
275    }
276
277    pub fn get_processes(&mut self, container: &Container) -> std::io::Result<Vec<Process>> {
278        let body = self.request(
279            Method::GET,
280            &format!("/containers/{}/top", container.Id),
281            "".to_string(),
282        );
283
284        let top: Top = match serde_json::from_str(&body) {
285            Ok(top) => top,
286            Err(e) => {
287                let err = std::io::Error::new(std::io::ErrorKind::InvalidInput, e.to_string());
288                return Err(err);
289            }
290        };
291
292        let mut processes: Vec<Process> = Vec::new();
293        let mut process_iter = top.Processes.iter();
294        loop {
295            let process = match process_iter.next() {
296                Some(process) => process,
297                None => {
298                    break;
299                }
300            };
301
302            let mut p = Process {
303                user: String::new(),
304                pid: String::new(),
305                cpu: None,
306                memory: None,
307                vsz: None,
308                rss: None,
309                tty: None,
310                stat: None,
311                start: None,
312                time: None,
313                command: String::new(),
314            };
315
316            let mut value_iter = process.iter();
317            let mut i: usize = 0;
318            loop {
319                let value = match value_iter.next() {
320                    Some(value) => value,
321                    None => {
322                        break;
323                    }
324                };
325                let key = &top.Titles[i];
326                match key.as_ref() {
327                    "USER" => p.user = value.clone(),
328                    "PID" => p.pid = value.clone(),
329                    "%CPU" => p.cpu = Some(value.clone()),
330                    "%MEM" => p.memory = Some(value.clone()),
331                    "VSZ" => p.vsz = Some(value.clone()),
332                    "RSS" => p.rss = Some(value.clone()),
333                    "TTY" => p.tty = Some(value.clone()),
334                    "STAT" => p.stat = Some(value.clone()),
335                    "START" => p.start = Some(value.clone()),
336                    "TIME" => p.time = Some(value.clone()),
337                    "COMMAND" => p.command = value.clone(),
338                    _ => {}
339                }
340
341                i = i + 1;
342            }
343
344            processes.push(p);
345        }
346
347        return Ok(processes);
348    }
349
350    pub fn get_stats(&mut self, container: &Container) -> std::io::Result<Stats> {
351        if container.Status.contains("Up") == false {
352            let err = std::io::Error::new(
353                std::io::ErrorKind::InvalidInput,
354                "The container is already stopped.",
355            );
356            return Err(err);
357        }
358
359        let body = self.request(
360            Method::GET,
361            &format!("/containers/{}/stats", container.Id),
362            "".to_string(),
363        );
364
365        let stats: Stats = match serde_json::from_str(&body) {
366            Ok(stats) => stats,
367            Err(e) => {
368                let err = std::io::Error::new(std::io::ErrorKind::InvalidInput, e.to_string());
369                return Err(err);
370            }
371        };
372        return Ok(stats);
373    }
374
375    //
376    // Image
377    //
378
379    pub fn delete_image(&mut self, id_or_name: &str) -> std::io::Result<String> {
380        let body = self.request(
381            Method::DELETE,
382            &format!("/images/{}", id_or_name),
383            "".to_string(),
384        );
385
386        match serde_json::from_str::<serde_json::Value>(&body) {
387            Ok(data) => {
388                if data.is_array() {
389                    Ok("".to_string())
390                } else {
391                    Err(std::io::Error::new(
392                        std::io::ErrorKind::InvalidInput,
393                        data["message"].to_string(),
394                    ))
395                }
396            }
397            Err(e) => Err(std::io::Error::new(
398                std::io::ErrorKind::InvalidInput,
399                e.to_string(),
400            )),
401        }
402    }
403
404    pub fn build_image(&mut self, data: Vec<u8>, t: String) -> std::io::Result<String> {
405        let body = self.request_file(
406            Method::POST,
407            &format!("/build?t={}", t),
408            data,
409            "application/x-tar",
410        );
411        match serde_json::from_str::<serde_json::Value>(&body) {
412            Ok(status) => Err(std::io::Error::new(
413                std::io::ErrorKind::InvalidInput,
414                status["message"].to_string(),
415            )),
416            Err(_e) => Ok("".to_string()),
417        }
418    }
419
420    pub fn create_image(
421        &mut self,
422        image: String,
423        tag: String,
424    ) -> std::io::Result<Vec<ImageStatus>> {
425        let body = format!(
426            "[{}]",
427            self.request(
428                Method::POST,
429                &format!("/images/create?fromImage={}&tag={}", image, tag),
430                "".to_string()
431            )
432        );
433        let fixed = body.replace("}{", "},{");
434
435        let statuses: Vec<ImageStatus> = match serde_json::from_str(&fixed) {
436            Ok(statuses) => statuses,
437            Err(e) => {
438                let err = std::io::Error::new(std::io::ErrorKind::InvalidInput, e.to_string());
439                return Err(err);
440            }
441        };
442        return Ok(statuses);
443    }
444
445    pub fn get_images(&mut self, all: bool) -> std::io::Result<Vec<Image>> {
446        let a = match all {
447            true => "1",
448            false => "0",
449        };
450        let body = self.request(
451            Method::GET,
452            &format!("/images/json?all={}", a),
453            "".to_string(),
454        );
455
456        let images: Vec<Image> = match serde_json::from_str(&body) {
457            Ok(images) => images,
458            Err(e) => {
459                let err = std::io::Error::new(std::io::ErrorKind::InvalidInput, e.to_string());
460                return Err(err);
461            }
462        };
463        return Ok(images);
464    }
465
466    pub fn get_system_info(&mut self) -> std::io::Result<SystemInfo> {
467        let body = self.request(Method::GET, "/info", "".to_string());
468
469        let info: SystemInfo = match serde_json::from_str(&body) {
470            Ok(info) => info,
471            Err(e) => {
472                let err = std::io::Error::new(std::io::ErrorKind::InvalidInput, e.to_string());
473                return Err(err);
474            }
475        };
476        return Ok(info);
477    }
478
479    pub fn get_container_info(&mut self, container: &Container) -> std::io::Result<ContainerInfo> {
480        let body = self.request(
481            Method::GET,
482            &format!("/containers/{}/json", container.Id),
483            "".to_string(),
484        );
485
486        let container_info: ContainerInfo = match serde_json::from_str(&body) {
487            Ok(body) => body,
488            Err(e) => {
489                let err = std::io::Error::new(std::io::ErrorKind::InvalidInput, e.to_string());
490                return Err(err);
491            }
492        };
493        return Ok(container_info);
494    }
495
496    pub fn get_filesystem_changes(
497        &mut self,
498        container: &Container,
499    ) -> std::io::Result<Vec<FilesystemChange>> {
500        let body = self.request(
501            Method::GET,
502            &format!("/containers/{}/changes", container.Id),
503            "".to_string(),
504        );
505
506        let filesystem_changes: Vec<FilesystemChange> = match serde_json::from_str(&body) {
507            Ok(body) => body,
508            Err(e) => {
509                let err = std::io::Error::new(std::io::ErrorKind::InvalidInput, e.to_string());
510                return Err(err);
511            }
512        };
513        return Ok(filesystem_changes);
514    }
515
516    pub fn export_container(&mut self, container: &Container) -> std::io::Result<String> {
517        let body = self.request(
518            Method::GET,
519            &format!("/containers/{}/export", container.Id),
520            "".to_string(),
521        );
522
523        return Ok(body);
524    }
525
526    pub fn ping(&mut self) -> std::io::Result<String> {
527        let body = self.request(Method::GET, "/_ping", "".to_string());
528
529        return Ok(body);
530    }
531
532    pub fn get_version(&mut self) -> std::io::Result<Version> {
533        let body = self.request(Method::GET, "/version", "".to_string());
534
535        let version: Version = match serde_json::from_str(&body) {
536            Ok(r_body) => r_body,
537            Err(e) => {
538                let err = std::io::Error::new(std::io::ErrorKind::InvalidInput, e.to_string());
539                return Err(err);
540            }
541        };
542        return Ok(version);
543    }
544}