//! Create and manage images.
//!
//! API Reference: <https://docs.docker.com/engine/api/v1.41/#tag/Image>
use std::{collections::HashMap, io::Read, iter};
use futures_util::{stream::Stream, TryFutureExt, TryStreamExt};
use hyper::Body;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use url::form_urlencoded;
use crate::{docker::Docker, errors::Result, tarball, transport::tar};
#[cfg(feature = "chrono")]
use crate::datetime::datetime_from_unix_timestamp;
#[cfg(feature = "chrono")]
use chrono::{DateTime, Utc};
/// Interface for accessing and manipulating a named docker image
///
/// [Api Reference](https://docs.docker.com/engine/api/v1.41/#tag/Image)
pub struct Image<'docker> {
docker: &'docker Docker,
name: String,
}
impl<'docker> Image<'docker> {
/// Exports an interface for operations that may be performed against a named image
pub fn new<S>(
docker: &'docker Docker,
name: S,
) -> Self
where
S: Into<String>,
{
Image {
docker,
name: name.into(),
}
}
/// Inspects a named image's details
///
/// [Api Reference](https://docs.docker.com/engine/api/v1.41/#operation/ImageInspect)
pub async fn inspect(&self) -> Result<ImageDetails> {
self.docker
.get_json(&format!("/images/{}/json", self.name)[..])
.await
}
/// Lists the history of the images set of changes
///
/// [Api Reference](https://docs.docker.com/engine/api/v1.41/#operation/ImageHistory)
pub async fn history(&self) -> Result<Vec<History>> {
self.docker
.get_json(&format!("/images/{}/history", self.name)[..])
.await
}
/// Deletes an image
///
/// [Api Reference](https://docs.docker.com/engine/api/v1.41/#operation/ImagePrune)
pub async fn delete(&self) -> Result<Vec<Status>> {
self.docker
.delete_json::<Vec<Status>>(&format!("/images/{}", self.name)[..])
.await
}
/// Export this image to a tarball
///
/// [Api Reference](https://docs.docker.com/engine/api/v1.41/#operation/ImageGet)
pub fn export(&self) -> impl Stream<Item = Result<Vec<u8>>> + Unpin + 'docker {
Box::pin(
self.docker
.stream_get(format!("/images/{}/get", self.name))
.map_ok(|c| c.to_vec()),
)
}
/// Adds a tag to an image
///
/// [Api Reference](https://docs.docker.com/engine/api/v1.41/#operation/ImageTag)
pub async fn tag(
&self,
opts: &TagOptions,
) -> Result<()> {
let mut path = vec![format!("/images/{}/tag", self.name)];
if let Some(query) = opts.serialize() {
path.push(query)
}
let _ = self.docker.post(&path.join("?"), None).await?;
Ok(())
}
}
/// Interface for docker images
pub struct Images<'docker> {
docker: &'docker Docker,
}
impl<'docker> Images<'docker> {
/// Exports an interface for interacting with docker images
pub fn new(docker: &'docker Docker) -> Self {
Images { docker }
}
/// Builds a new image build by reading a Dockerfile in a target directory
///
/// [Api Reference](https://docs.docker.com/engine/api/v1.41/#operation/ImageBuild)
pub fn build(
&self,
opts: &BuildOptions,
) -> impl Stream<Item = Result<Value>> + Unpin + 'docker {
let mut endpoint = vec!["/build".to_owned()];
if let Some(query) = opts.serialize() {
endpoint.push(query)
}
// To not tie the lifetime of `opts` to the 'stream, we do the tarring work outside of the
// stream. But for backwards compatability, we have to return the error inside of the
// stream.
let mut bytes = Vec::default();
let tar_result = tarball::dir(&mut bytes, opts.path.as_str());
// We must take ownership of the Docker reference. If we don't then the lifetime of 'stream
// is incorrectly tied to `self`.
let docker = self.docker;
Box::pin(
async move {
// Bubble up error inside the stream for backwards compatability
tar_result?;
let value_stream = docker.stream_post_into_values(
endpoint.join("?"),
Some((Body::from(bytes), tar())),
None::<iter::Empty<_>>,
);
Ok(value_stream)
}
.try_flatten_stream(),
)
}
/// Lists the docker images on the current docker host
///
/// [Api Reference](https://docs.docker.com/engine/api/v1.41/#operation/ImageList)
pub async fn list(
&self,
opts: &ImageListOptions,
) -> Result<Vec<ImageInfo>> {
let mut path = vec!["/images/json".to_owned()];
if let Some(query) = opts.serialize() {
path.push(query);
}
self.docker
.get_json::<Vec<ImageInfo>>(&path.join("?"))
.await
}
<