1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
use crate::{
  container::ContainerBuilder,
  error::{Error::Docker as DockerError, Result},
};
use async_std::prelude::StreamExt;
use bollard::{
  auth::DockerCredentials,
  image::{CreateImageOptions, ListImagesOptions},
  Docker,
};
use std::collections::HashMap;

#[derive(Default)]
pub struct Image {
  name: String,
  credentials: Option<DockerCredentials>,
}

impl Image {
  pub fn new(name: &str) -> Self {
    Image {
      name: name.to_string(),
      ..Default::default()
    }
  }

  pub fn with_credentials(self, credentials: DockerCredentials) -> Self {
    self.with_optional_credentials(Some(credentials))
  }

  pub fn with_optional_credentials(mut self, credentials: Option<DockerCredentials>) -> Self {
    self.credentials = credentials;
    self
  }

  async fn pull_image(&self, docker: &Docker) -> Result<()> {
    let filters = HashMap::from([("reference".to_string(), vec![self.name.to_string()])]);

    let list_options = Some(ListImagesOptions {
      filters,
      ..Default::default()
    });

    let image_list = docker.list_images(list_options).await?;

    if image_list.is_empty() {
      let image_options = Some(CreateImageOptions {
        from_image: self.name.to_string(),
        ..Default::default()
      });

      let mut stream = docker.create_image(image_options.clone(), None, None);

      let mut pull_completed = false;

      while !pull_completed {
        match stream.next().await {
          Some(Ok(info)) => {
            match info.progress {
              None => println!("{}", info.status.as_ref().unwrap()),
              _ => println!(
                "{}: {}",
                info.status.as_ref().unwrap(),
                info.progress.as_ref().unwrap()
              ),
            }

            if info.status
              == Some(format!(
                "Status: Downloaded newer image for {}",
                image_options.as_ref().unwrap().from_image
              ))
            {
              pull_completed = true;
            }
          }
          Some(Err(error)) => {
            return Err(DockerError(error));
          }
          _ => {
            return Ok(());
          }
        }
      }
    }
    Ok(())
  }

  pub async fn build(&self, docker: &Docker) -> Result<ContainerBuilder> {
    self.pull_image(docker).await?;
    Ok(ContainerBuilder::new(&self.name))
  }
}