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 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 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 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}