mcai-docker 0.2.2

Library to start and stop MCAI workers images with Docker.
Documentation
use crate::{
  container::ContainerBuilder,
  error::{Error::Docker as DockerError, Result},
};
use async_std::prelude::StreamExt;
use bollard::{
  auth::DockerCredentials,
  image::{CreateImageOptions, ListImagesOptions},
  Docker,
};
use log::info;
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, self.credentials.clone());

      let mut pull_completed = false;

      while !pull_completed {
        match stream.next().await {
          Some(Ok(info)) => {
            match info.progress {
              None => info!("{}", info.status.as_ref().unwrap()),
              _ => info!(
                "{}: {:?}kB/{:?}kB",
                info.status.as_ref().unwrap(),
                info
                  .progress_detail
                  .clone()
                  .unwrap_or_default()
                  .current
                  .unwrap_or_default(),
                info
                  .progress_detail
                  .unwrap_or_default()
                  .total
                  .unwrap_or_default()
              ),
            }

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