diff options
-rw-r--r-- | src/container.rs | 184 | ||||
-rw-r--r-- | src/docker.rs | 8 | ||||
-rw-r--r-- | src/image.rs | 76 | ||||
-rw-r--r-- | src/lib.rs | 2 |
4 files changed, 222 insertions, 48 deletions
diff --git a/src/container.rs b/src/container.rs index dfd65d2..60f7d61 100644 --- a/src/container.rs +++ b/src/container.rs @@ -19,7 +19,7 @@ use crate::{ docker::Docker, errors::{Error, Result}, exec::{Exec, ExecContainerOptions}, - image::Config, + image::ContainerConfig, network::NetworkSettings, transport::Payload, tty::{self, Multiplexer as TtyMultiPlexer}, @@ -365,9 +365,8 @@ impl<'docker> Container<'docker> { .skip(1) .collect::<std::path::PathBuf>(), bytes, - ) - .unwrap(); - let data = ar.into_inner().unwrap(); + )?; + let data = ar.into_inner()?; self.copy_to(Path::new("/"), data.into()).await?; Ok(()) @@ -1195,37 +1194,47 @@ pub struct ContainerInfo { pub ports: Vec<Port>, pub state: String, pub status: String, - pub size_rw: Option<u64>, - pub size_root_fs: Option<u64>, + pub size_rw: Option<i64>, + pub size_root_fs: Option<i64>, } #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "PascalCase")] pub struct ContainerDetails { - pub app_armor_profile: String, - pub args: Vec<String>, - pub config: Config, + pub id: String, #[cfg(feature = "chrono")] pub created: DateTime<Utc>, #[cfg(not(feature = "chrono"))] pub created: String, - pub driver: String, - // pub ExecIDs: ?? - pub host_config: HostConfig, + pub path: String, + pub args: Vec<String>, + pub state: State, + pub image: String, + pub resolv_conf_path: String, pub hostname_path: String, pub hosts_path: String, pub log_path: String, - pub id: String, - pub image: String, - pub mount_label: String, pub name: String, - pub network_settings: NetworkSettings, - pub path: String, + pub restart_count: i64, + pub driver: String, + pub platform: String, + pub mount_label: String, pub process_label: String, - pub resolv_conf_path: String, - pub restart_count: u64, - pub state: State, + pub app_armor_profile: String, + #[serde(rename = "ExecIDs")] + pub exec_ids: Option<Vec<String>>, + pub host_config: HostConfig, + pub graph_driver: GraphDriverData, pub mounts: Vec<Mount>, + pub config: ContainerConfig, + pub network_settings: NetworkSettings, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "PascalCase")] +pub struct GraphDriverData { + pub name: String, + pub data: HashMap<String, String>, } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -1263,22 +1272,139 @@ pub struct State { #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "PascalCase")] pub struct HostConfig { + pub cpu_shares: Option<i64>, + pub memory: Option<i64>, pub cgroup_parent: Option<String>, - #[serde(rename = "ContainerIDFile")] - pub container_id_file: String, - pub cpu_shares: Option<u64>, + pub blkio_weight_device: Option<Vec<ThrottleDevice>>, + pub blkio_device_read_bps: Option<Vec<ThrottleDevice>>, + pub blkio_device_write_bps: Option<Vec<ThrottleDevice>>, + #[serde(rename = "BlkioDeviceReadIOps")] + pub blkio_device_read_iops: Option<Vec<ThrottleDevice>>, + #[serde(rename = "BlkioDeviceWriteIOps")] + pub blkio_device_write_iops: Option<Vec<ThrottleDevice>>, + pub cpu_period: Option<i64>, + pub cpu_quota: Option<i64>, + pub cpu_realtime_period: Option<i64>, + pub cpu_realtime_runtime: Option<i64>, pub cpuset_cpus: Option<String>, - pub memory: Option<u64>, + pub cpuset_mems: Option<String>, + pub devices: Option<Vec<DeviceMapping>>, + pub device_cgroup_rules: Option<String>, + pub device_requests: Option<Vec<DeviceRequest>>, + #[serde(rename = "KernelMemoryTCP")] + pub kernel_memory_tcp: i64, + pub memory_reservation: Option<i64>, pub memory_swap: Option<i64>, + pub memory_swappiness: Option<i64>, + #[serde(rename = "NanoCPUs")] + pub nano_cpus: Option<i64>, + pub oom_kill_disable: bool, + pub init: Option<bool>, + pub pids_limit: Option<i64>, + pub ulimits: Option<Vec<Ulimit>>, + pub cpu_count: i64, + pub cpu_percent: i64, + #[serde(rename = "IOMaximumIOps")] + pub io_maximum_iops: u64, + #[serde(rename = "IOMaximumBandwith")] + pub io_maximum_bandwith: Option<u64>, + pub binds: Option<Vec<String>>, + #[serde(rename = "ContainerIDFile")] + pub container_id_file: String, + pub log_config: LogConfig, pub network_mode: String, + pub port_bindings: Option<PortMap>, + pub restart_policy: RestartPolicy, + pub auto_remove: bool, + pub volume_driver: String, + pub volumes_from: Option<Vec<String>>, + pub mounts: Option<Vec<Mount>>, + pub cap_add: Option<Vec<String>>, + pub cap_drop: Option<Vec<String>>, + pub dns: Option<Vec<String>>, + pub dns_options: Option<Vec<String>>, + pub dns_search: Option<Vec<String>>, + pub extra_hosts: Option<Vec<String>>, + pub group_add: Option<Vec<String>>, + pub ipc_mode: String, + pub cgroup: String, + pub links: Option<Vec<String>>, + pub oom_score_adj: i64, pub pid_mode: Option<String>, - pub port_bindings: Option<HashMap<String, Vec<HashMap<String, String>>>>, pub privileged: bool, pub publish_all_ports: bool, - pub readonly_rootfs: Option<bool>, /* pub RestartPolicy: ??? - * pub SecurityOpt: Option<???>, - * pub Ulimits: Option<???> - * pub VolumesFrom: Option<??/> */ + pub readonly_rootfs: Option<bool>, + pub security_opt: Option<Vec<String>>, + pub storage_opt: Option<HashMap<String, String>>, + pub tmpfs: Option<HashMap<String, String>>, + #[serde(rename = "UTSMode")] + pub uts_mode: String, + pub userns_mode: String, + pub shm_size: u64, + pub sysctls: Option<HashMap<String, String>>, + pub runtime: String, + pub console_size: Option<Vec<u64>>, + pub isolation: String, + pub masked_paths: Option<Vec<String>>, + pub readonly_paths: Option<Vec<String>>, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "PascalCase")] +pub struct ThrottleDevice { + pub path: String, + pub rate: u64, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "PascalCase")] +pub struct RestartPolicy { + pub name: String, + pub maximum_retry_count: u64, +} + +pub type PortMap = HashMap<String, Option<Vec<PortBinding>>>; + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "PascalCase")] +pub struct PortBinding { + pub host_ip: String, + pub host_port: String, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct LogConfig { + #[serde(rename = "Type")] + pub type_: String, + #[serde(rename = "Config")] + pub config: HashMap<String, String>, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "PascalCase")] +pub struct Ulimit { + pub name: String, + pub soft: u64, + pub hard: u64, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "PascalCase")] +pub struct DeviceMapping { + pub path_on_host: Option<String>, + pub path_in_container: Option<String>, + pub cgroup_permissions: Option<String>, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "PascalCase")] +pub struct DeviceRequest { + pub driver: String, + pub count: u64, + #[serde(rename = "DeviceIDs")] + pub device_ids: Vec<String>, + pub capabilities: Vec<String>, + pub options: Option<serde_json::Value>, } #[derive(Clone, Debug, Serialize, Deserialize)] diff --git a/src/docker.rs b/src/docker.rs index edb97e4..1b7f603 100644 --- a/src/docker.rs +++ b/src/docker.rs @@ -7,8 +7,7 @@ use std::{collections::HashMap, env, io, path::Path}; use futures_util::{stream::Stream, TryStreamExt}; use hyper::{client::HttpConnector, Body, Client, Method}; use mime::Mime; -use serde::{Deserialize, Serialize}; -use serde_json::Value; +use serde::{de, Deserialize, Serialize}; use url::form_urlencoded; use crate::{ @@ -355,14 +354,15 @@ impl Docker { /// Send a streaming post request that returns a stream of JSON values /// /// Assumes that each received chunk contains one or more JSON values - pub(crate) fn stream_post_into_values<'a, H>( + pub(crate) fn stream_post_into<'a, H, T>( &'a self, endpoint: impl AsRef<str> + 'a, body: Option<(Body, Mime)>, headers: Option<H>, - ) -> impl Stream<Item = Result<Value>> + 'a + ) -> impl Stream<Item = Result<T>> + 'a where H: IntoIterator<Item = (&'static str, String)> + 'a, + T: de::DeserializeOwned, { self.stream_post(endpoint, body, headers) .and_then(|chunk| async move { diff --git a/src/image.rs b/src/image.rs index addf4ee..6df8cb7 100644 --- a/src/image.rs +++ b/src/image.rs @@ -7,7 +7,6 @@ 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}; @@ -111,7 +110,7 @@ impl<'docker> Images<'docker> { pub fn build( &self, opts: &BuildOptions, - ) -> impl Stream<Item = Result<Value>> + Unpin + 'docker { + ) -> impl Stream<Item = Result<ImageBuildChunk>> + Unpin + 'docker { let mut endpoint = vec!["/build".to_owned()]; if let Some(query) = opts.serialize() { endpoint.push(query) @@ -131,7 +130,7 @@ impl<'docker> Images<'docker> { // Bubble up error inside the stream for backwards compatability tar_result?; - let value_stream = docker.stream_post_into_values( + let value_stream = docker.stream_post_into( endpoint.join("?"), Some((Body::from(bytes), tar())), None::<iter::Empty<_>>, @@ -191,7 +190,7 @@ impl<'docker> Images<'docker> { pub fn pull( &self, opts: &PullOptions, - ) -> impl Stream<Item = Result<Value>> + Unpin + 'docker { + ) -> impl Stream<Item = Result<ImageBuildChunk>> + Unpin + 'docker { let mut path = vec!["/images/create".to_owned()]; if let Some(query) = opts.serialize() { path.push(query); @@ -200,10 +199,7 @@ impl<'docker> Images<'docker> { .auth_header() .map(|a| iter::once(("X-Registry-Auth", a))); - Box::pin( - self.docker - .stream_post_into_values(path.join("?"), None, headers), - ) + Box::pin(self.docker.stream_post_into(path.join("?"), None, headers)) } /// exports a collection of named images, @@ -230,7 +226,7 @@ impl<'docker> Images<'docker> { pub fn import<R>( self, mut tarball: R, - ) -> impl Stream<Item = Result<Value>> + Unpin + 'docker + ) -> impl Stream<Item = Result<ImageBuildChunk>> + Unpin + 'docker where R: Read + Send + 'docker, { @@ -240,7 +236,7 @@ impl<'docker> Images<'docker> { tarball.read_to_end(&mut bytes)?; - let value_stream = self.docker.stream_post_into_values( + let value_stream = self.docker.stream_post_into( "/images/load", Some((Body::from(bytes), tar())), None::<iter::Empty<_>>, @@ -449,12 +445,20 @@ impl PullOptions { } } -#[derive(Default)] pub struct PullOptionsBuilder { auth: Option<RegistryAuth>, params: HashMap<&'static str, String>, } +impl Default for PullOptionsBuilder { + fn default() -> Self { + let mut params = HashMap::new(); + params.insert("tag", "latest".to_string()); + + PullOptionsBuilder { auth: None, params } + } +} + impl PullOptionsBuilder { /// Name of the image to pull. The name may include a tag or digest. /// This parameter may only be used when pulling an image. @@ -485,6 +489,9 @@ impl PullOptionsBuilder { /// Repository name given to an image when it is imported. The repo may include a tag. /// This parameter may only be used when importing an image. + /// + /// By default a `latest` tag is added when calling + /// [PullOptionsBuilder::default](PullOptionsBuilder::default]. pub fn repo<R>( &mut self, r: R, @@ -792,7 +799,7 @@ pub struct ImageDetails { pub architecture: String, pub author: String, pub comment: String, - pub config: Config, + pub config: ContainerConfig, #[cfg(feature = "chrono")] pub created: DateTime<Utc>, #[cfg(not(feature = "chrono"))] @@ -809,7 +816,7 @@ pub struct ImageDetails { #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "PascalCase")] -pub struct Config { +pub struct ContainerConfig { pub attach_stderr: bool, pub attach_stdin: bool, pub attach_stdout: bool, @@ -831,7 +838,7 @@ pub struct Config { pub working_dir: String, } -impl Config { +impl ContainerConfig { pub fn env(&self) -> HashMap<String, String> { let mut map = HashMap::new(); if let Some(ref vars) = self.env { @@ -862,6 +869,47 @@ pub enum Status { Deleted(String), } +#[derive(Serialize, Deserialize, Debug)] +#[serde(untagged)] +/// Represents a response chunk from Docker api when building, pulling or importing an image. +pub enum ImageBuildChunk { + Update { + stream: String, + }, + Error { + error: String, + #[serde(rename = "errorDetail")] + error_detail: ErrorDetail, + }, + Digest { + aux: Aux, + }, + PullStatus { + status: String, + id: Option<String>, + progress: Option<String>, + #[serde(rename = "progressDetail")] + progress_detail: Option<ProgressDetail>, + }, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct Aux { + #[serde(rename = "ID")] + id: String, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct ErrorDetail { + message: String, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct ProgressDetail { + current: Option<u64>, + total: Option<u64>, +} + #[cfg(test)] mod tests { use super::*; @@ -119,7 +119,7 @@ reexport! { docker::{Version, Info, Event, Actor}; exec::{ExecDetails, ProcessConfig}; image::{ - SearchResult, ImageInfo as Image, ImageDetails, Config, History, Status, + SearchResult, ImageInfo as Image, ImageDetails, ContainerConfig, History, Status, }; network::{ NetworkSettings, NetworkEntry, Ipam, NetworkDetails, |