summaryrefslogtreecommitdiffstats
path: root/src/container.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/container.rs')
-rw-r--r--src/container.rs152
1 files changed, 117 insertions, 35 deletions
diff --git a/src/container.rs b/src/container.rs
index ec2033d..ad26238 100644
--- a/src/container.rs
+++ b/src/container.rs
@@ -20,7 +20,7 @@ use crate::{
errors::{Error, Result},
exec::{Exec, ExecContainerOptions},
image::Config,
- network::{NetworkInfo, NetworkSettings},
+ network::NetworkSettings,
transport::Payload,
tty::{self, Multiplexer as TtyMultiPlexer},
};
@@ -32,7 +32,7 @@ use chrono::{DateTime, Utc};
/// Interface for accessing and manipulating a docker container
///
-/// Api Reference: <https://docs.docker.com/engine/api/v1.41/#tag/Container>
+/// [Api Reference](https://docs.docker.com/engine/api/v1.41/#tag/Container)
pub struct Container<'docker> {
docker: &'docker Docker,
id: String,
@@ -60,7 +60,7 @@ impl<'docker> Container<'docker> {
/// Inspects the current docker container instance's details
///
- /// Api Reference: <https://docs.docker.com/engine/api/v1.41/#operation/ContainerInspect>
+ /// [Api Reference](https://docs.docker.com/engine/api/v1.41/#operation/ContainerInspect)
pub async fn inspect(&self) -> Result<ContainerDetails> {
self.docker
.get_json::<ContainerDetails>(&format!("/containers/{}/json", self.id)[..])
@@ -69,7 +69,7 @@ impl<'docker> Container<'docker> {
/// Returns a `top` view of information about the container process
///
- /// Api Reference: <https://docs.docker.com/engine/api/v1.41/#operation/ContainerTop>
+ /// [Api Reference](https://docs.docker.com/engine/api/v1.41/#operation/ContainerTop)
pub async fn top(
&self,
psargs: Option<&str>,
@@ -86,7 +86,7 @@ impl<'docker> Container<'docker> {
/// Returns a stream of logs emitted but the container instance
///
- /// Api Reference: <https://docs.docker.com/engine/api/v1.41/#operation/ContainerLogs>
+ /// [Api Reference](https://docs.docker.com/engine/api/v1.41/#operation/ContainerLogs)
pub fn logs(
&self,
opts: &LogsOptions,
@@ -114,13 +114,15 @@ impl<'docker> Container<'docker> {
.await
}
- /// Attaches a `[TtyMultiplexer]` to the container.
+ /// Attaches a [Multiplexer](crate::tty::Multiplexer) to the container.
///
- /// The `[TtyMultiplexer]` implements Stream for returning Stdout and Stderr chunks. It also implements `[AsyncWrite]` for writing to Stdin.
+ /// The [Multiplexer](crate::tty::Multiplexer) implements Stream for returning Stdout and
+ /// Stderr chunks. It also implements `[AsyncWrite]` for writing to Stdin.
///
- /// The multiplexer can be split into its read and write halves with the `[split](TtyMultiplexer::split)` method
+ /// The multiplexer can be split into its read and write halves with the
+ /// [split](crate::tty::Multiplexer::split) method
///
- /// Api Reference: <https://docs.docker.com/engine/api/v1.41/#operation/ContainerAttach>
+ /// [Api Reference](https://docs.docker.com/engine/api/v1.41/#operation/ContainerAttach)
pub async fn attach(&self) -> Result<TtyMultiPlexer<'docker>> {
let tcp_stream = self.attach_raw().await?;
@@ -129,7 +131,7 @@ impl<'docker> Container<'docker> {
/// Returns a set of changes made to the container instance
///
- /// Api Reference: <https://docs.docker.com/engine/api/v1.41/#operation/ContainerChanges>
+ /// [Api Reference](https://docs.docker.com/engine/api/v1.41/#operation/ContainerChanges)
pub async fn changes(&self) -> Result<Vec<Change>> {
self.docker
.get_json::<Vec<Change>>(&format!("/containers/{}/changes", self.id)[..])
@@ -138,7 +140,7 @@ impl<'docker> Container<'docker> {
/// Exports the current docker container into a tarball
///
- /// Api Reference: <https://docs.docker.com/engine/api/v1.41/#operation/ContainerExport>
+ /// [Api Reference](https://docs.docker.com/engine/api/v1.41/#operation/ContainerExport)
pub fn export(&self) -> impl Stream<Item = Result<Vec<u8>>> + 'docker {
self.docker
.stream_get(format!("/containers/{}/export", self.id))
@@ -147,7 +149,7 @@ impl<'docker> Container<'docker> {
/// Returns a stream of stats specific to this container instance
///
- /// Api Reference: <https://docs.docker.com/engine/api/v1.41/#operation/ContainerStats>
+ /// [Api Reference](https://docs.docker.com/engine/api/v1.41/#operation/ContainerStats)
pub fn stats(&self) -> impl Stream<Item = Result<Stats>> + Unpin + 'docker {
let codec = futures_codec::LinesCodec {};
@@ -169,7 +171,7 @@ impl<'docker> Container<'docker> {
/// Start the container instance
///
- /// Api Reference: <https://docs.docker.com/engine/api/v1.41/#operation/ContainerStart>
+ /// [Api Reference](https://docs.docker.com/engine/api/v1.41/#operation/ContainerStart)
pub async fn start(&self) -> Result<()> {
self.docker
.post(&format!("/containers/{}/start", self.id)[..], None)
@@ -179,7 +181,7 @@ impl<'docker> Container<'docker> {
/// Stop the container instance
///
- /// Api Reference: <https://docs.docker.com/engine/api/v1.41/#operation/ContainerStop>
+ /// [Api Reference](https://docs.docker.com/engine/api/v1.41/#operation/ContainerStop)
pub async fn stop(
&self,
wait: Option<Duration>,
@@ -198,7 +200,7 @@ impl<'docker> Container<'docker> {
/// Restart the container instance
///
- /// Api Reference: <https://docs.docker.com/engine/api/v1.41/#operation/ContainerRestart>
+ /// [Api Reference](https://docs.docker.com/engine/api/v1.41/#operation/ContainerRestart)
pub async fn restart(
&self,
wait: Option<Duration>,
@@ -216,7 +218,7 @@ impl<'docker> Container<'docker> {
/// Kill the container instance
///
- /// Api Reference: <https://docs.docker.com/engine/api/v1.41/#operation/ContainerKill>
+ /// [Api Reference](https://docs.docker.com/engine/api/v1.41/#operation/ContainerKill)
pub async fn kill(
&self,
signal: Option<&str>,
@@ -234,7 +236,7 @@ impl<'docker> Container<'docker> {
/// Rename the container instance
///
- /// Api Reference: <https://docs.docker.com/engine/api/v1.41/#operation/ContainerRename>
+ /// [Api Reference](https://docs.docker.com/engine/api/v1.41/#operation/ContainerRename)
pub async fn rename(
&self,
name: &str,
@@ -253,7 +255,7 @@ impl<'docker> Container<'docker> {
/// Pause the container instance
///
- /// Api Reference: <https://docs.docker.com/engine/api/v1.41/#operation/ContainerPause>
+ /// [Api Reference](https://docs.docker.com/engine/api/v1.41/#operation/ContainerPause)
pub async fn pause(&self) -> Result<()> {
self.docker
.post(&format!("/containers/{}/pause", self.id)[..], None)
@@ -263,7 +265,7 @@ impl<'docker> Container<'docker> {
/// Unpause the container instance
///
- /// Api Reference: <https://docs.docker.com/engine/api/v1.41/#operation/ContainerUnpause>
+ /// [Api Reference](https://docs.docker.com/engine/api/v1.41/#operation/ContainerUnpause)
pub async fn unpause(&self) -> Result<()> {
self.docker
.post(&format!("/containers/{}/unpause", self.id)[..], None)
@@ -273,7 +275,7 @@ impl<'docker> Container<'docker> {
/// Wait until the container stops
///
- /// Api Reference: <https://docs.docker.com/engine/api/v1.41/#operation/ContainerWait>
+ /// [Api Reference](https://docs.docker.com/engine/api/v1.41/#operation/ContainerWait)
pub async fn wait(&self) -> Result<Exit> {
self.docker
.post_json(format!("/containers/{}/wait", self.id), Payload::None)
@@ -284,7 +286,7 @@ impl<'docker> Container<'docker> {
///
/// Use remove instead to use the force/v options.
///
- /// Api Reference: <https://docs.docker.com/engine/api/v1.41/#operation/ContainerDelete>
+ /// [Api Reference](https://docs.docker.com/engine/api/v1.41/#operation/ContainerDelete)
pub async fn delete(&self) -> Result<()> {
self.docker
.delete(&format!("/containers/{}", self.id)[..])
@@ -294,7 +296,7 @@ impl<'docker> Container<'docker> {
/// Delete the container instance (todo: force/v)
///
- /// Api Reference: <https://docs.docker.com/engine/api/v1.41/#operation/ContainerRemove>
+ /// [Api Reference](https://docs.docker.com/engine/api/v1.41/#operation/ContainerRemove)
pub async fn remove(
&self,
opts: RmContainerOptions,
@@ -309,7 +311,7 @@ impl<'docker> Container<'docker> {
/// Execute a command in this container
///
- /// Api Reference: <https://docs.docker.com/engine/api/v1.41/#tag/Exec>
+ /// [Api Reference](https://docs.docker.com/engine/api/v1.41/#tag/Exec)
pub fn exec(
&self,
opts: &ExecContainerOptions,
@@ -326,7 +328,7 @@ impl<'docker> Container<'docker> {
/// ends in `/.` then this indicates that only the contents of the path directory should be
/// copied. A symlink is always resolved to its target.
///
- /// Api Reference: <https://docs.docker.com/engine/api/v1.41/#operation/ContainerArchive>
+ /// [Api Reference](https://docs.docker.com/engine/api/v1.41/#operation/ContainerArchive)
pub fn copy_from(
&self,
path: &Path,
@@ -344,7 +346,7 @@ impl<'docker> Container<'docker> {
/// The file will be copied at the given location (see `path`) and will be owned by root
/// with access mask 644.
///
- /// Api Reference: <https://docs.docker.com/engine/api/v1.41/#operation/PutContainerArchive>
+ /// [Api Reference](https://docs.docker.com/engine/api/v1.41/#operation/PutContainerArchive)
pub async fn copy_file_into<P: AsRef<Path>>(
&self,
path: P,
@@ -374,7 +376,7 @@ impl<'docker> Container<'docker> {
///
/// The tarball will be copied to the container and extracted at the given location (see `path`).
///
- /// Api Reference: <https://docs.docker.com/engine/api/v1.41/#operation/PutContainerArchive>
+ /// [Api Reference](https://docs.docker.com/engine/api/v1.41/#operation/PutContainerArchive)
pub async fn copy_to(
&self,
path: &Path,
@@ -398,7 +400,7 @@ impl<'docker> Container<'docker> {
/// Interface for docker containers
///
-/// Api Reference: <https://docs.docker.com/engine/api/v1.41/#tag/Containers>
+/// [Api Reference](https://docs.docker.com/engine/api/v1.41/#tag/Containers)
pub struct Containers<'docker> {
docker: &'docker Docker,
}
@@ -411,7 +413,7 @@ impl<'docker> Containers<'docker> {
/// Lists the container instances on the docker host
///
- /// Api Reference: <https://docs.docker.com/engine/api/v1.41/#operation/ContainerList>
+ /// [Api Reference](https://docs.docker.com/engine/api/v1.41/#operation/ContainerList)
pub async fn list(
&self,
opts: &ContainerListOptions,
@@ -490,6 +492,7 @@ pub enum ContainerFilter {
Status(String),
LabelName(String),
Label(String, String),
+ Name(String),
}
/// Builder interface for `ContainerListOptions`
@@ -503,14 +506,17 @@ impl ContainerListOptionsBuilder {
&mut self,
filters: Vec<ContainerFilter>,
) -> &mut Self {
- let mut param = HashMap::new();
+ let mut param: HashMap<&str, Vec<String>> = HashMap::new();
for f in filters {
- match f {
- ContainerFilter::ExitCode(c) => param.insert("exit", vec![c.to_string()]),
- ContainerFilter::Status(s) => param.insert("status", vec![s]),
- ContainerFilter::LabelName(n) => param.insert("label", vec![n]),
- ContainerFilter::Label(n, v) => param.insert("label", vec![format!("{}={}", n, v)]),
+ let (key, value) = match f {
+ ContainerFilter::ExitCode(c) => ("exited", c.to_string()),
+ ContainerFilter::Status(s) => ("status", s),
+ ContainerFilter::LabelName(n) => ("label", n),
+ ContainerFilter::Label(n, v) => ("label", format!("{}={}", n, v)),
+ ContainerFilter::Name(n) => ("name", n.to_string()),
};
+
+ param.entry(key).or_insert_with(Vec::new).push(value);
}
// structure is a a json encoded object mapping string keys to a list
// of string values
@@ -1287,13 +1293,25 @@ pub struct Port {
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Stats {
pub read: String,
- pub networks: HashMap<String, NetworkInfo>,
+ pub networks: HashMap<String, NetworkStats>,
pub memory_stats: MemoryStats,
pub blkio_stats: BlkioStats,
pub cpu_stats: CpuStats,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
+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(Clone, Debug, Serialize, Deserialize)]
pub struct MemoryStats {
pub max_usage: u64,
pub usage: u64,
@@ -1408,6 +1426,7 @@ pub struct Exit {
#[cfg(test)]
mod tests {
use super::*;
+ use crate::container::ContainerFilter::{ExitCode, Label, LabelName, Status};
#[test]
fn container_options_simple() {
@@ -1561,6 +1580,69 @@ mod tests {
);
}
+ #[test]
+ fn container_list_options_multiple_labels() {
+ let options = ContainerListOptions::builder()
+ .filter(vec![
+ Label("label1".to_string(), "value".to_string()),
+ LabelName("label2".to_string()),
+ ])
+ .build();
+
+ let form = form_urlencoded::Serializer::new(String::new())
+ .append_pair("filters", r#"{"label":["label1=value","label2"]}"#)
+ .finish();
+
+ assert_eq!(form, options.serialize().unwrap())
+ }
+
+ #[test]
+ fn container_list_options_exit_code() {
+ let options = ContainerListOptions::builder()
+ .filter(vec![ExitCode(0)])
+ .build();
+
+ let form = form_urlencoded::Serializer::new(String::new())
+ .append_pair("filters", r#"{"exited":["0"]}"#)
+ .finish();
+
+ assert_eq!(form, options.serialize().unwrap())
+ }
+
+ #[test]
+ fn container_list_options_status() {
+ let options = ContainerListOptions::builder()
+ .filter(vec![Status("running".to_string())])
+ .build();
+
+ let form = form_urlencoded::Serializer::new(String::new())
+ .append_pair("filters", r#"{"status":["running"]}"#)
+ .finish();
+
+ assert_eq!(form, options.serialize().unwrap())
+ }
+
+ #[test]
+ fn container_list_options_combined() {
+ let options = ContainerListOptions::builder()
+ .all()
+ .filter(vec![
+ Label("label1".to_string(), "value".to_string()),
+ LabelName("label2".to_string()),
+ ExitCode(0),
+ Status("running".to_string()),
+ ])
+ .build();
+
+ let serialized = options.serialize().unwrap();
+
+ assert!(serialized.contains("all=true"));
+ assert!(serialized.contains("filters="));
+ assert!(serialized.contains("%22label%22%3A%5B%22label1%3Dvalue%22%2C%22label2%22%5D"));
+ assert!(serialized.contains("%22status%22%3A%5B%22running%22%5D"));
+ assert!(serialized.contains("%22exited%22%3A%5B%220%22%5D"));
+ }
+
#[cfg(feature = "chrono")]
#[test]
fn logs_options() {