use futures_core::Stream;
use futures_util::{StreamExt, TryStreamExt};
use http::header::{CONNECTION, CONTENT_TYPE, UPGRADE};
use http::request::Builder;
use http_body_util::Full;
use hyper::{body::Bytes, Method};
use serde::Serialize;
use serde_derive::Deserialize;
use tokio::io::AsyncWrite;
use tokio_util::codec::FramedRead;
use std::cmp::Eq;
use std::collections::HashMap;
use std::fmt;
use std::hash::Hash;
use std::pin::Pin;
use super::Docker;
use crate::docker::{body_stream, BodyType};
use crate::errors::Error;
use crate::models::*;
use crate::read::NewlineLogOutputDecoder;
#[derive(Debug, Clone, Default, PartialEq, Serialize)]
pub struct ListContainersOptions<T>
where
T: Into<String> + Eq + Hash + Serialize,
{
pub all: bool,
pub limit: Option<isize>,
pub size: bool,
#[serde(serialize_with = "crate::docker::serialize_as_json")]
pub filters: HashMap<T, Vec<T>>,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct CreateContainerOptions<T>
where
T: Into<String> + Serialize,
{
pub name: T,
#[serde(skip_serializing_if = "Option::is_none")]
pub platform: Option<T>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
#[allow(missing_docs)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct NetworkingConfig<T: Into<String> + Hash + Eq> {
pub endpoints_config: HashMap<T, EndpointSettings>,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct Config {
#[serde(rename = "Hostname")]
#[serde(skip_serializing_if = "Option::is_none")]
pub hostname: Option<String>,
#[serde(rename = "Domainname")]
#[serde(skip_serializing_if = "Option::is_none")]
pub domainname: Option<String>,
#[serde(rename = "User")]
#[serde(skip_serializing_if = "Option::is_none")]
pub user: Option<String>,
#[serde(rename = "AttachStdin")]
#[serde(skip_serializing_if = "Option::is_none")]
pub attach_stdin: Option<bool>,
#[serde(rename = "AttachStdout")]
#[serde(skip_serializing_if = "Option::is_none")]
pub attach_stdout: Option<bool>,
#[serde(rename = "AttachStderr")]
#[serde(skip_serializing_if = "Option::is_none")]
pub attach_stderr: Option<bool>,
#[serde(rename = "ExposedPorts")]
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(feature = "utoipa", schema(value_type = Option<HashMap<String, HashMap<(), ()>>>))]
pub exposed_ports: Option<HashMap<String, EmptyObject>>,
#[serde(rename = "Tty")]
#[serde(skip_serializing_if = "Option::is_none")]
pub tty: Option<bool>,
#[serde(rename = "OpenStdin")]
#[serde(skip_serializing_if = "Option::is_none")]
pub open_stdin: Option<bool>,
#[serde(rename = "StdinOnce")]
#[serde(skip_serializing_if = "Option::is_none")]
pub stdin_once: Option<bool>,
#[serde(rename = "Env")]
#[serde(skip_serializing_if = "Option::is_none")]
pub env: Option<Vec<String>>,
#[serde(rename = "Cmd")]
#[serde(skip_serializing_if = "Option::is_none")]
pub cmd: Option<Vec<String>>,
#[serde(rename = "Healthcheck")]
#[serde(skip_serializing_if = "Option::is_none")]
pub healthcheck: Option<HealthConfig>,
#[serde(rename = "ArgsEscaped")]
#[serde(skip_serializing_if = "Option::is_none")]
pub args_escaped: Option<bool>,
#[serde(rename = "Image")]
#[serde(skip_serializing_if = "Option::is_none")]
pub image: Option<String>,
#[serde(rename = "Volumes")]
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(feature = "utoipa", schema(value_type = Option<HashMap<String, HashMap<(), ()>>>))]
pub volumes: Option<HashMap<String, EmptyObject>>,
#[serde(rename = "WorkingDir")]
#[serde(skip_serializing_if = "Option::is_none")]
pub working_dir: Option<String>,
#[serde(rename = "Entrypoint")]
#[serde(skip_serializing_if = "Option::is_none")]
pub entrypoint: Option<Vec<String>>,
#[serde(rename = "NetworkDisabled")]
#[serde(skip_serializing_if = "Option::is_none")]
pub network_disabled: Option<bool>,
#[serde(rename = "MacAddress")]
#[serde(skip_serializing_if = "Option::is_none")]
pub mac_address: Option<String>,
#[serde(rename = "OnBuild")]
#[serde(skip_serializing_if = "Option::is_none")]
pub on_build: Option<Vec<String>>,
#[serde(rename = "Labels")]
#[serde(skip_serializing_if = "Option::is_none")]
pub labels: Option<HashMap<String, String>>,
#[serde(rename = "StopSignal")]
#[serde(skip_serializing_if = "Option::is_none")]
pub stop_signal: Option<String>,
#[serde(rename = "StopTimeout")]
#[serde(skip_serializing_if = "Option::is_none")]
pub stop_timeout: Option<i64>,
#[serde(rename = "Shell")]
#[serde(skip_serializing_if = "Option::is_none")]
pub shell: Option<Vec<String>>,
#[serde(rename = "HostConfig")]
#[serde(skip_serializing_if = "Option::is_none")]
pub host_config: Option<HostConfig>,
#[serde(rename = "NetworkingConfig")]
#[serde(skip_serializing_if = "Option::is_none")]
pub networking_config: Option<NetworkingConfig<String>>,
}
impl From<ContainerConfig> for Config {
fn from(container: ContainerConfig) -> Self {
Config {
hostname: container.hostname,
domainname: container.domainname,
user: container.user,
attach_stdin: container.attach_stdin,
attach_stdout: container.attach_stdout,
attach_stderr: container.attach_stderr,
exposed_ports: container.exposed_ports,
tty: container.tty,
open_stdin: container.open_stdin,
stdin_once: container.stdin_once,
env: container.env,
cmd: container.cmd,
healthcheck: container.healthcheck,
args_escaped: container.args_escaped,
image: container.image,
volumes: container.volumes,
working_dir: container.working_dir,
entrypoint: container.entrypoint,
network_disabled: container.network_disabled,
mac_address: container.mac_address,
on_build: container.on_build,
labels: container.labels,
stop_signal: container.stop_signal,
stop_timeout: container.stop_timeout,
shell: container.shell,
host_config: None,
networking_config: None,
}
}
}
#[derive(Debug, Copy, Clone, Default, PartialEq, Serialize)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct StopContainerOptions {
pub t: i64,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct StartContainerOptions<T>
where
T: Into<String> + Serialize,
{
pub detach_keys: T,
}
#[derive(Debug, Copy, Clone, Default, PartialEq, Serialize)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct RemoveContainerOptions {
pub v: bool,
pub force: bool,
pub link: bool,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct WaitContainerOptions<T>
where
T: Into<String> + Serialize,
{
pub condition: T,
}
pub struct AttachContainerResults {
pub output: Pin<Box<dyn Stream<Item = Result<LogOutput, Error>> + Send>>,
pub input: Pin<Box<dyn AsyncWrite + Send>>,
}
impl fmt::Debug for AttachContainerResults {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "AttachContainerResults")
}
}
#[derive(Debug, Copy, Clone, Default, PartialEq, Serialize)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct AttachContainerOptions<T>
where
T: Into<String> + Serialize + Default,
{
pub stdin: Option<bool>,
pub stdout: Option<bool>,
pub stderr: Option<bool>,
pub stream: Option<bool>,
pub logs: Option<bool>,
#[serde(rename = "detachKeys")]
pub detach_keys: Option<T>,
}
#[derive(Debug, Copy, Clone, Default, PartialEq, Serialize)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct ResizeContainerTtyOptions {
#[serde(rename = "w")]
pub width: u16,
#[serde(rename = "h")]
pub height: u16,
}
#[derive(Debug, Copy, Clone, Default, PartialEq, Serialize)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct RestartContainerOptions {
pub t: isize,
}
#[derive(Debug, Copy, Clone, Default, PartialEq, Serialize)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct InspectContainerOptions {
pub size: bool,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct TopOptions<T>
where
T: Into<String> + Serialize,
{
pub ps_args: T,
}
fn is_zero(val: &i64) -> bool {
val == &0i64
}
#[derive(Debug, Clone, Default, PartialEq, Serialize)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct LogsOptions<T>
where
T: Into<String> + Serialize,
{
pub follow: bool,
pub stdout: bool,
pub stderr: bool,
pub since: i64,
#[serde(skip_serializing_if = "is_zero")]
pub until: i64,
pub timestamps: bool,
pub tail: T,
}
#[derive(Debug, Clone, PartialEq)]
#[allow(missing_docs)]
pub enum LogOutput {
StdErr { message: Bytes },
StdOut { message: Bytes },
StdIn { message: Bytes },
Console { message: Bytes },
}
impl fmt::Display for LogOutput {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let message = match &self {
LogOutput::StdErr { message } => message,
LogOutput::StdOut { message } => message,
LogOutput::StdIn { message } => message,
LogOutput::Console { message } => message,
};
write!(f, "{}", String::from_utf8_lossy(message))
}
}
impl AsRef<[u8]> for LogOutput {
fn as_ref(&self) -> &[u8] {
match self {
LogOutput::StdErr { message } => message.as_ref(),
LogOutput::StdOut { message } => message.as_ref(),
LogOutput::StdIn { message } => message.as_ref(),
LogOutput::Console { message } => message.as_ref(),
}
}
}
impl LogOutput {
pub fn into_bytes(self) -> Bytes {
match self {
LogOutput::StdErr { message } => message,
LogOutput::StdOut { message } => message,
LogOutput::StdIn { message } => message,
LogOutput::Console { message } => message,
}
}
}
#[derive(Debug, Copy, Clone, Default, PartialEq, Serialize)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct StatsOptions {
pub stream: bool,
#[serde(rename = "one-shot")]
pub one_shot: bool,
}
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
#[allow(missing_docs)]
#[serde(untagged)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub enum MemoryStatsStats {
V1(MemoryStatsStatsV1),
V2(MemoryStatsStatsV2),
}
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
#[allow(missing_docs)]
#[serde(deny_unknown_fields)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct MemoryStatsStatsV1 {
pub cache: u64,
pub dirty: u64,
pub mapped_file: u64,
pub total_inactive_file: u64,
pub pgpgout: u64,
pub rss: u64,
pub total_mapped_file: u64,
pub writeback: u64,
pub unevictable: u64,
pub pgpgin: u64,
pub total_unevictable: u64,
pub pgmajfault: u64,
pub total_rss: u64,
pub total_rss_huge: u64,
pub total_writeback: u64,
pub total_inactive_anon: u64,
pub rss_huge: u64,
pub hierarchical_memory_limit: u64,
pub total_pgfault: u64,
pub total_active_file: u64,
pub active_anon: u64,
pub total_active_anon: u64,
pub total_pgpgout: u64,
pub total_cache: u64,
pub total_dirty: u64,
pub inactive_anon: u64,
pub active_file: u64,
pub pgfault: u64,
pub inactive_file: u64,
pub total_pgmajfault: u64,
pub total_pgpgin: u64,
pub hierarchical_memsw_limit: Option<u64>, pub shmem: Option<u64>, pub total_shmem: Option<u64>, }
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
#[allow(missing_docs)]
#[serde(deny_unknown_fields)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct MemoryStatsStatsV2 {
pub anon: u64,
pub file: u64,
pub kernel_stack: u64,
pub slab: u64,
pub sock: u64,
pub shmem: u64,
pub file_mapped: u64,
pub file_dirty: u64,
pub file_writeback: u64,
pub anon_thp: u64,
pub inactive_anon: u64,
pub active_anon: u64,
pub inactive_file: u64,
pub active_file: u64,
pub unevictable: u64,
pub slab_reclaimable: u64,
pub slab_unreclaimable: u64,
pub pgfault: u64,
pub pgmajfault: u64,
pub workingset_refault: u64,
pub workingset_activate: u64,
pub workingset_nodereclaim: u64,
pub pgrefill: u64,
pub pgscan: u64,
pub pgsteal: u64,
pub pgactivate: u64,
pub pgdeactivate: u64,
pub pglazyfree: u64,
pub pglazyfreed: u64,
pub thp_fault_alloc: u64,
pub thp_collapse_alloc: u64,
}
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
#[allow(missing_docs)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct MemoryStats {
pub stats: Option<MemoryStatsStats>,
pub max_usage: Option<u64>,
pub usage: Option<u64>,
pub failcnt: Option<u64>,
pub limit: Option<u64>,
pub commit: Option<u64>,
pub commit_peak: Option<u64>,
pub commitbytes: Option<u64>,
pub commitpeakbytes: Option<u64>,
pub privateworkingset: Option<u64>,
}
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
#[allow(missing_docs)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct PidsStats {
pub current: Option<u64>,
pub limit: Option<u64>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[allow(missing_docs)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct BlkioStats {
pub io_service_bytes_recursive: Option<Vec<BlkioStatsEntry>>,
pub io_serviced_recursive: Option<Vec<BlkioStatsEntry>>,
pub io_queue_recursive: Option<Vec<BlkioStatsEntry>>,
pub io_service_time_recursive: Option<Vec<BlkioStatsEntry>>,
pub io_wait_time_recursive: Option<Vec<BlkioStatsEntry>>,
pub io_merged_recursive: Option<Vec<BlkioStatsEntry>>,
pub io_time_recursive: Option<Vec<BlkioStatsEntry>>,
pub sectors_recursive: Option<Vec<BlkioStatsEntry>>,
}
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
#[allow(missing_docs)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct StorageStats {
pub read_count_normalized: Option<u64>,
pub read_size_bytes: Option<u64>,
pub write_count_normalized: Option<u64>,
pub write_size_bytes: Option<u64>,
}
fn empty_string() -> String {
"".to_string()
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[allow(missing_docs)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct Stats {
#[cfg(feature = "time")]
#[serde(
deserialize_with = "crate::docker::deserialize_rfc3339",
serialize_with = "crate::docker::serialize_rfc3339"
)]
pub read: time::OffsetDateTime,
#[cfg(feature = "time")]
#[serde(
deserialize_with = "crate::docker::deserialize_rfc3339",
serialize_with = "crate::docker::serialize_rfc3339"
)]
pub preread: time::OffsetDateTime,
#[cfg(all(feature = "chrono", not(feature = "time")))]
pub read: chrono::DateTime<chrono::Utc>,
#[cfg(all(feature = "chrono", not(feature = "time")))]
pub preread: chrono::DateTime<chrono::Utc>,
#[cfg(not(any(feature = "chrono", feature = "time")))]
pub read: String,
#[cfg(not(any(feature = "chrono", feature = "time")))]
pub preread: String,
pub num_procs: u32,
pub pids_stats: PidsStats,
pub network: Option<NetworkStats>,
pub networks: Option<HashMap<String, NetworkStats>>,
pub memory_stats: MemoryStats,
pub blkio_stats: BlkioStats,
pub cpu_stats: CPUStats,
pub precpu_stats: CPUStats,
pub storage_stats: StorageStats,
#[serde(default = "empty_string")]
pub name: String,
#[serde(alias = "Id", default = "empty_string")]
pub id: String,
}
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
#[allow(missing_docs)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct NetworkStats {
pub rx_dropped: u64,
pub rx_bytes: u64,
pub rx_errors: u64,
pub tx_packets: u64,
pub tx_dropped: u64,
pub rx_packets: u64,
pub tx_errors: u64,
pub tx_bytes: u64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[allow(missing_docs)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct CPUUsage {
pub percpu_usage: Option<Vec<u64>>,
pub usage_in_usermode: u64,
pub total_usage: u64,
pub usage_in_kernelmode: u64,
}
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
#[allow(missing_docs)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct ThrottlingData {
pub periods: u64,
pub throttled_periods: u64,
pub throttled_time: u64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[allow(missing_docs)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct CPUStats {
pub cpu_usage: CPUUsage,
pub system_cpu_usage: Option<u64>,
pub online_cpus: Option<u64>,
pub throttling_data: ThrottlingData,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[allow(missing_docs)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct BlkioStatsEntry {
pub major: u64,
pub minor: u64,
pub op: String,
pub value: u64,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct KillContainerOptions<T>
where
T: Into<String> + Serialize,
{
pub signal: T,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize)]
#[serde(rename_all = "PascalCase")]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct UpdateContainerOptions<T>
where
T: Into<String> + Eq + Hash,
{
#[serde(rename = "CpuShares")]
#[serde(skip_serializing_if = "Option::is_none")]
pub cpu_shares: Option<isize>,
#[serde(rename = "Memory")]
#[serde(skip_serializing_if = "Option::is_none")]
pub memory: Option<i64>,
#[serde(rename = "CgroupParent")]
#[serde(skip_serializing_if = "Option::is_none")]
pub cgroup_parent: Option<T>,
#[serde(rename = "BlkioWeight")]
#[serde(skip_serializing_if = "Option::is_none")]
pub blkio_weight: Option<u16>,
#[serde(rename = "BlkioWeightDevice")]
#[serde(skip_serializing_if = "Option::is_none")]
pub blkio_weight_device: Option<Vec<ResourcesBlkioWeightDevice>>,
#[serde(rename = "BlkioDeviceReadBps")]
#[serde(skip_serializing_if = "Option::is_none")]
pub blkio_device_read_bps: Option<Vec<ThrottleDevice>>,
#[serde(rename = "BlkioDeviceWriteBps")]
#[serde(skip_serializing_if = "Option::is_none")]
pub blkio_device_write_bps: Option<Vec<ThrottleDevice>>,
#[serde(rename = "BlkioDeviceReadIOps")]
#[serde(skip_serializing_if = "Option::is_none")]
pub blkio_device_read_i_ops: Option<Vec<ThrottleDevice>>,
#[serde(rename = "BlkioDeviceWriteIOps")]
#[serde(skip_serializing_if = "Option::is_none")]
pub blkio_device_write_i_ops: Option<Vec<ThrottleDevice>>,
#[serde(rename = "CpuPeriod")]
#[serde(skip_serializing_if = "Option::is_none")]
pub cpu_period: Option<i64>,
#[serde(rename = "CpuQuota")]
#[serde(skip_serializing_if = "Option::is_none")]
pub cpu_quota: Option<i64>,
#[serde(rename = "CpuRealtimePeriod")]
#[serde(skip_serializing_if = "Option::is_none")]
pub cpu_realtime_period: Option<i64>,
#[serde(rename = "CpuRealtimeRuntime")]
#[serde(skip_serializing_if = "Option::is_none")]
pub cpu_realtime_runtime: Option<i64>,
#[serde(rename = "CpusetCpus")]
#[serde(skip_serializing_if = "Option::is_none")]
pub cpuset_cpus: Option<T>,
#[serde(rename = "CpusetMems")]
#[serde(skip_serializing_if = "Option::is_none")]
pub cpuset_mems: Option<T>,
#[serde(rename = "Devices")]
#[serde(skip_serializing_if = "Option::is_none")]
pub devices: Option<Vec<DeviceMapping>>,
#[serde(rename = "DeviceCgroupRules")]
#[serde(skip_serializing_if = "Option::is_none")]
pub device_cgroup_rules: Option<Vec<T>>,
#[serde(rename = "DeviceRequests")]
#[serde(skip_serializing_if = "Option::is_none")]
pub device_requests: Option<Vec<DeviceRequest>>,
#[serde(rename = "KernelMemoryTCP")]
#[serde(skip_serializing_if = "Option::is_none")]
pub kernel_memory_tcp: Option<i64>,
#[serde(rename = "MemoryReservation")]
#[serde(skip_serializing_if = "Option::is_none")]
pub memory_reservation: Option<i64>,
#[serde(rename = "MemorySwap")]
#[serde(skip_serializing_if = "Option::is_none")]
pub memory_swap: Option<i64>,
#[serde(rename = "MemorySwappiness")]
#[serde(skip_serializing_if = "Option::is_none")]
pub memory_swappiness: Option<i64>,
#[serde(rename = "NanoCpus")]
#[serde(skip_serializing_if = "Option::is_none")]
pub nano_cpus: Option<i64>,
#[serde(rename = "OomKillDisable")]
#[serde(skip_serializing_if = "Option::is_none")]
pub oom_kill_disable: Option<bool>,
#[serde(rename = "Init")]
#[serde(skip_serializing_if = "Option::is_none")]
pub init: Option<bool>,
#[serde(rename = "PidsLimit")]
#[serde(skip_serializing_if = "Option::is_none")]
pub pids_limit: Option<i64>,
#[serde(rename = "Ulimits")]
#[serde(skip_serializing_if = "Option::is_none")]
pub ulimits: Option<Vec<ResourcesUlimits>>,
#[serde(rename = "CpuCount")]
#[serde(skip_serializing_if = "Option::is_none")]
pub cpu_count: Option<i64>,
#[serde(rename = "CpuPercent")]
#[serde(skip_serializing_if = "Option::is_none")]
pub cpu_percent: Option<i64>,
#[serde(rename = "IOMaximumIOps")]
#[serde(skip_serializing_if = "Option::is_none")]
pub io_maximum_iops: Option<i64>,
#[serde(rename = "IOMaximumBandwidth")]
#[serde(skip_serializing_if = "Option::is_none")]
pub io_maximum_bandwidth: Option<i64>,
pub restart_policy: Option<RestartPolicy>,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct RenameContainerOptions<T>
where
T: Into<String> + Serialize,
{
pub name: T,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize)]
pub struct PruneContainersOptions<T>
where
T: Into<String> + Eq + Hash + Serialize,
{
#[serde(serialize_with = "crate::docker::serialize_as_json")]
pub filters: HashMap<T, Vec<T>>,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct UploadToContainerOptions<T>
where
T: Into<String> + Serialize,
{
pub path: T,
pub no_overwrite_dir_non_dir: T,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize)]
pub struct DownloadFromContainerOptions<T>
where
T: Into<String> + Serialize,
{
pub path: T,
}
impl Docker {
pub async fn list_containers<'de, T>(
&self,
options: Option<ListContainersOptions<T>>,
) -> Result<Vec<ContainerSummary>, Error>
where
T: Into<String> + Eq + Hash + Serialize,
{
let url = "/containers/json";
let req = self.build_request(
url,
Builder::new().method(Method::GET),
options,
Ok(BodyType::Left(Full::new(Bytes::new()))),
);
self.process_into_value(req).await
}
pub async fn create_container<T>(
&self,
options: Option<CreateContainerOptions<T>>,
config: Config,
) -> Result<ContainerCreateResponse, Error>
where
T: Into<String> + Serialize,
{
let url = "/containers/create";
let req = self.build_request(
url,
Builder::new().method(Method::POST),
options,
Docker::serialize_payload(Some(config)),
);
self.process_into_value(req).await
}
pub async fn start_container<T>(
&self,
container_name: &str,
options: Option<StartContainerOptions<T>>,
) -> Result<(), Error>
where
T: Into<String> + Serialize,
{
let url = format!("/containers/{container_name}/start");
let req = self.build_request(
&url,
Builder::new().method(Method::POST),
options,
Ok(BodyType::Left(Full::new(Bytes::new()))),
);
self.process_into_unit(req).await
}
pub async fn stop_container(
&self,
container_name: &str,
options: Option<StopContainerOptions>,
) -> Result<(), Error> {
let url = format!("/containers/{container_name}/stop");
let req = self.build_request(
&url,
Builder::new().method(Method::POST),
options,
Ok(BodyType::Left(Full::new(Bytes::new()))),
);
self.process_into_unit(req).await
}
pub async fn remove_container(
&self,
container_name: &str,
options: Option<RemoveContainerOptions>,
) -> Result<(), Error> {
let url = format!("/containers/{container_name}");
let req = self.build_request(
&url,
Builder::new().method(Method::DELETE),
options,
Ok(BodyType::Left(Full::new(Bytes::new()))),
);
self.process_into_unit(req).await
}
pub fn wait_container<T>(
&self,
container_name: &str,
options: Option<WaitContainerOptions<T>>,
) -> impl Stream<Item = Result<ContainerWaitResponse, Error>>
where
T: Into<String> + Serialize,
{
let url = format!("/containers/{container_name}/wait");
let req = self.build_request(
&url,
Builder::new().method(Method::POST),
options,
Ok(BodyType::Left(Full::new(Bytes::new()))),
);
self.process_into_stream(req).map(|res| match res {
Ok(ContainerWaitResponse {
status_code: code,
error:
Some(ContainerWaitExitError {
message: Some(error),
}),
}) if code > 0 => Err(Error::DockerContainerWaitError { error, code }),
Ok(ContainerWaitResponse {
status_code: code,
error: None,
}) if code > 0 => Err(Error::DockerContainerWaitError {
error: String::new(),
code,
}),
v => v,
})
}
pub async fn attach_container<T>(
&self,
container_name: &str,
options: Option<AttachContainerOptions<T>>,
) -> Result<AttachContainerResults, Error>
where
T: Into<String> + Serialize + Default,
{
let url = format!("/containers/{container_name}/attach");
let req = self.build_request(
&url,
Builder::new()
.method(Method::POST)
.header(CONNECTION, "Upgrade")
.header(UPGRADE, "tcp"),
options,
Ok(BodyType::Left(Full::new(Bytes::new()))),
);
let (read, write) = self.process_upgraded(req).await?;
let log = FramedRead::new(read, NewlineLogOutputDecoder::new(true)).map_err(|e| e.into());
Ok(AttachContainerResults {
output: Box::pin(log),
input: Box::pin(write),
})
}
pub async fn resize_container_tty(
&self,
container_name: &str,
options: ResizeContainerTtyOptions,
) -> Result<(), Error> {
let url = format!("/containers/{container_name}/resize");
let req = self.build_request(
&url,
Builder::new().method(Method::POST),
Some(options),
Ok(BodyType::Left(Full::new(Bytes::new()))),
);
self.process_into_unit(req).await
}
pub async fn restart_container(
&self,
container_name: &str,
options: Option<RestartContainerOptions>,
) -> Result<(), Error> {
let url = format!("/containers/{container_name}/restart");
let req = self.build_request(
&url,
Builder::new().method(Method::POST),
options,
Ok(BodyType::Left(Full::new(Bytes::new()))),
);
self.process_into_unit(req).await
}
pub async fn inspect_container(
&self,
container_name: &str,
options: Option<InspectContainerOptions>,
) -> Result<ContainerInspectResponse, Error> {
let url = format!("/containers/{container_name}/json");
let req = self.build_request(
&url,
Builder::new().method(Method::GET),
options,
Ok(BodyType::Left(Full::new(Bytes::new()))),
);
self.process_into_value(req).await
}
pub async fn top_processes<T>(
&self,
container_name: &str,
options: Option<TopOptions<T>>,
) -> Result<ContainerTopResponse, Error>
where
T: Into<String> + Serialize,
{
let url = format!("/containers/{container_name}/top");
let req = self.build_request(
&url,
Builder::new().method(Method::GET),
options,
Ok(BodyType::Left(Full::new(Bytes::new()))),
);
self.process_into_value(req).await
}
pub fn logs<T>(
&self,
container_name: &str,
options: Option<LogsOptions<T>>,
) -> impl Stream<Item = Result<LogOutput, Error>>
where
T: Into<String> + Serialize,
{
let url = format!("/containers/{container_name}/logs");
let req = self.build_request(
&url,
Builder::new().method(Method::GET),
options,
Ok(BodyType::Left(Full::new(Bytes::new()))),
);
self.process_into_stream_string(req)
}
pub async fn container_changes(
&self,
container_name: &str,
) -> Result<Option<Vec<FilesystemChange>>, Error> {
let url = format!("/containers/{container_name}/changes");
let req = self.build_request(
&url,
Builder::new().method(Method::GET),
None::<String>,
Ok(BodyType::Left(Full::new(Bytes::new()))),
);
self.process_into_value(req).await
}
pub fn stats(
&self,
container_name: &str,
options: Option<StatsOptions>,
) -> impl Stream<Item = Result<Stats, Error>> {
let url = format!("/containers/{container_name}/stats");
let req = self.build_request(
&url,
Builder::new().method(Method::GET),
options,
Ok(BodyType::Left(Full::new(Bytes::new()))),
);
self.process_into_stream(req)
}
pub async fn kill_container<T>(
&self,
container_name: &str,
options: Option<KillContainerOptions<T>>,
) -> Result<(), Error>
where
T: Into<String> + Serialize,
{
let url = format!("/containers/{container_name}/kill");
let req = self.build_request(
&url,
Builder::new().method(Method::POST),
options,
Ok(BodyType::Left(Full::new(Bytes::new()))),
);
self.process_into_unit(req).await
}
pub async fn update_container<T>(
&self,
container_name: &str,
config: UpdateContainerOptions<T>,
) -> Result<(), Error>
where
T: Into<String> + Eq + Hash + Serialize,
{
let url = format!("/containers/{container_name}/update");
let req = self.build_request(
&url,
Builder::new().method(Method::POST),
None::<String>,
Docker::serialize_payload(Some(config)),
);
self.process_into_unit(req).await
}
pub async fn rename_container<T>(
&self,
container_name: &str,
options: RenameContainerOptions<T>,
) -> Result<(), Error>
where
T: Into<String> + Serialize,
{
let url = format!("/containers/{container_name}/rename");
let req = self.build_request(
&url,
Builder::new().method(Method::POST),
Some(options),
Ok(BodyType::Left(Full::new(Bytes::new()))),
);
self.process_into_unit(req).await
}
pub async fn pause_container(&self, container_name: &str) -> Result<(), Error> {
let url = format!("/containers/{container_name}/pause");
let req = self.build_request(
&url,
Builder::new().method(Method::POST),
None::<String>,
Ok(BodyType::Left(Full::new(Bytes::new()))),
);
self.process_into_unit(req).await
}
pub async fn unpause_container(&self, container_name: &str) -> Result<(), Error> {
let url = format!("/containers/{container_name}/unpause");
let req = self.build_request(
&url,
Builder::new().method(Method::POST),
None::<String>,
Ok(BodyType::Left(Full::new(Bytes::new()))),
);
self.process_into_unit(req).await
}
pub async fn prune_containers<T>(
&self,
options: Option<PruneContainersOptions<T>>,
) -> Result<ContainerPruneResponse, Error>
where
T: Into<String> + Eq + Hash + Serialize,
{
let url = "/containers/prune";
let req = self.build_request(
url,
Builder::new().method(Method::POST),
options,
Ok(BodyType::Left(Full::new(Bytes::new()))),
);
self.process_into_value(req).await
}
pub async fn upload_to_container_streaming<T>(
&self,
container_name: &str,
options: Option<UploadToContainerOptions<T>>,
tar: impl Stream<Item = Bytes> + Send + 'static,
) -> Result<(), Error>
where
T: Into<String> + Serialize,
{
let url = format!("/containers/{container_name}/archive");
let req = self.build_request(
&url,
Builder::new()
.method(Method::PUT)
.header(CONTENT_TYPE, "application/x-tar"),
options,
Ok(body_stream(tar)),
);
self.process_into_unit(req).await
}
pub async fn upload_to_container<T>(
&self,
container_name: &str,
options: Option<UploadToContainerOptions<T>>,
tar: Bytes,
) -> Result<(), Error>
where
T: Into<String> + Serialize,
{
let url = format!("/containers/{container_name}/archive");
let req = self.build_request(
&url,
Builder::new()
.method(Method::PUT)
.header(CONTENT_TYPE, "application/x-tar"),
options,
Ok(BodyType::Left(Full::new(tar))),
);
self.process_into_unit(req).await
}
pub fn download_from_container<T>(
&self,
container_name: &str,
options: Option<DownloadFromContainerOptions<T>>,
) -> impl Stream<Item = Result<Bytes, Error>>
where
T: Into<String> + Serialize,
{
let url = format!("/containers/{container_name}/archive");
let req = self.build_request(
&url,
Builder::new().method(Method::GET),
options,
Ok(BodyType::Left(Full::new(Bytes::new()))),
);
self.process_into_body(req)
}
pub fn export_container(
&self,
container_name: &str,
) -> impl Stream<Item = Result<Bytes, Error>> {
let url = format!("/containers/{container_name}/export");
let req = self.build_request(
&url,
Builder::new()
.method(Method::GET)
.header(CONTENT_TYPE, "application/json"),
None::<String>,
Ok(BodyType::Left(Full::new(Bytes::new()))),
);
self.process_into_body(req)
}
}
#[cfg(not(windows))]
#[cfg(test)]
mod tests {
use futures_util::TryStreamExt;
use yup_hyper_mock::HostToReplyConnector;
use crate::{Docker, API_DEFAULT_VERSION};
use super::WaitContainerOptions;
#[tokio::test]
async fn test_container_wait_with_error() {
let mut connector = HostToReplyConnector::default();
connector.m.insert(
String::from("http://127.0.0.1"),
"HTTP/1.1 200 OK\r\nServer:mock1\r\nContent-Type:application/json\r\n\r\n{\"Error\":null,\"StatusCode\":1}".to_string(),
);
let docker =
Docker::connect_with_mock(connector, "127.0.0.1".to_string(), 5, API_DEFAULT_VERSION)
.unwrap();
let result = &docker
.wait_container("wait_container_test", None::<WaitContainerOptions<String>>)
.try_collect::<Vec<_>>()
.await;
assert!(matches!(
result,
Err(crate::errors::Error::DockerContainerWaitError { code: _, error: _ })
));
}
}