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