summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorWojciech Kępka <46892771+wojciechkepka@users.noreply.github.com>2021-02-06 07:57:49 +0100
committerGitHub <noreply@github.com>2021-02-06 01:57:49 -0500
commitef3dfad8f691f64e41fb1d369399471cde6ad8c0 (patch)
treee8b5bba9d148b3522171eaf72b33624334774416 /src
parent9b85dc8a9d370139e8eb3cafadf5a0f8f6dc6597 (diff)
Add Exec struct for easier manipulation of exec instances (#251)
* Add ExecDetails and ProcessConfig * Add exec_with_id, exec_inspect * Add example how to inspect an exec instance * Make clippy happy * exit_code is an Option on ExecDetails * Add Exec struct * Update example * Fix typo * Add Exec::resize and ExecResizeOptions * Add resize example
Diffstat (limited to 'src')
-rw-r--r--src/builder.rs69
-rw-r--r--src/lib.rs178
-rw-r--r--src/rep.rs27
3 files changed, 229 insertions, 45 deletions
diff --git a/src/builder.rs b/src/builder.rs
index 110abcc..6971be9 100644
--- a/src/builder.rs
+++ b/src/builder.rs
@@ -1701,6 +1701,75 @@ impl VolumeCreateOptionsBuilder {
}
}
}
+///
+/// Interface for creating volumes
+#[derive(Serialize, Debug)]
+pub struct ExecResizeOptions {
+ params: HashMap<&'static str, Value>,
+}
+
+impl ExecResizeOptions {
+ /// serialize options as a string. returns None if no options are defined
+ pub fn serialize(&self) -> Result<String> {
+ serde_json::to_string(&self.params).map_err(Error::from)
+ }
+
+ pub fn parse_from<'a, K, V>(
+ &self,
+ params: &'a HashMap<K, V>,
+ body: &mut BTreeMap<String, Value>,
+ ) where
+ &'a HashMap<K, V>: IntoIterator,
+ K: ToString + Eq + Hash,
+ V: Serialize,
+ {
+ for (k, v) in params.iter() {
+ let key = k.to_string();
+ let value = serde_json::to_value(v).unwrap();
+
+ body.insert(key, value);
+ }
+ }
+
+ /// return a new instance of a builder for options
+ pub fn builder() -> ExecResizeOptionsBuilder {
+ ExecResizeOptionsBuilder::new()
+ }
+}
+
+#[derive(Default)]
+pub struct ExecResizeOptionsBuilder {
+ params: HashMap<&'static str, Value>,
+}
+
+impl ExecResizeOptionsBuilder {
+ pub(crate) fn new() -> Self {
+ let params = HashMap::new();
+ ExecResizeOptionsBuilder { params }
+ }
+
+ pub fn height(
+ &mut self,
+ height: u64,
+ ) -> &mut Self {
+ self.params.insert("Name", json!(height));
+ self
+ }
+
+ pub fn width(
+ &mut self,
+ width: u64,
+ ) -> &mut Self {
+ self.params.insert("Name", json!(width));
+ self
+ }
+
+ pub fn build(&self) -> ExecResizeOptions {
+ ExecResizeOptions {
+ params: self.params.clone(),
+ }
+ }
+}
#[cfg(test)]
mod tests {
diff --git a/src/lib.rs b/src/lib.rs
index da60d27..79813e3 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -28,16 +28,16 @@ mod tarball;
pub use crate::{
builder::{
BuildOptions, ContainerConnectionOptions, ContainerFilter, ContainerListOptions,
- ContainerOptions, EventsOptions, ExecContainerOptions, ImageFilter, ImageListOptions,
- LogsOptions, NetworkCreateOptions, NetworkListOptions, PullOptions, RegistryAuth,
- RmContainerOptions, TagOptions, VolumeCreateOptions,
+ ContainerOptions, EventsOptions, ExecContainerOptions, ExecResizeOptions, ImageFilter,
+ ImageListOptions, LogsOptions, NetworkCreateOptions, NetworkListOptions, PullOptions,
+ RegistryAuth, RmContainerOptions, TagOptions, VolumeCreateOptions,
},
errors::Error,
};
use crate::{
rep::{
- Change, Container as ContainerRep, ContainerCreateInfo, ContainerDetails, Event, Exit,
- History, Image as ImageRep, ImageDetails, Info, NetworkCreateInfo,
+ Change, Container as ContainerRep, ContainerCreateInfo, ContainerDetails, Event,
+ ExecDetails, Exit, History, Image as ImageRep, ImageDetails, Info, NetworkCreateInfo,
NetworkDetails as NetworkInfo, SearchResult, Stats, Status, Top, Version,
Volume as VolumeRep, VolumeCreateInfo, Volumes as VolumesRep,
},
@@ -514,52 +514,15 @@ impl<'a> Container<'a> {
Ok(())
}
- async fn exec_create(
- &self,
- opts: &ExecContainerOptions,
- ) -> Result<String> {
- #[derive(serde::Deserialize)]
- #[serde(rename_all = "PascalCase")]
- struct Response {
- id: String,
- }
-
- let body: Body = opts.serialize()?.into();
-
- let Response { id } = self
- .docker
- .post_json(
- &format!("/containers/{}/exec", self.id)[..],
- Some((body, mime::APPLICATION_JSON)),
- )
- .await?;
-
- Ok(id)
- }
-
- fn exec_start(
- &self,
- id: String,
- ) -> impl Stream<Item = Result<tty::TtyChunk>> + 'a {
- let bytes: &[u8] = b"{}";
-
- let stream = Box::pin(self.docker.stream_post(
- format!("/exec/{}/start", id),
- Some((bytes.into(), mime::APPLICATION_JSON)),
- None::<iter::Empty<_>>,
- ));
-
- tty::decode(stream)
- }
-
+ /// Execute a command in this container
pub fn exec(
&'a self,
opts: &'a ExecContainerOptions,
) -> impl Stream<Item = Result<tty::TtyChunk>> + Unpin + 'a {
Box::pin(
async move {
- let id = self.exec_create(opts).await?;
- Ok(self.exec_start(id))
+ let id = Exec::create_id(&self.docker, &self.id, opts).await?;
+ Ok(Exec::_start(&self.docker, &id))
}
.try_flatten_stream(),
)
@@ -693,6 +656,131 @@ impl<'a> Containers<'a> {
.await
}
}
+/// Interface for docker exec instance
+pub struct Exec<'a> {
+ docker: &'a Docker,
+ id: String,
+}
+
+impl<'a> Exec<'a> {
+ fn new<S>(
+ docker: &'a Docker,
+ id: S,
+ ) -> Exec<'a>
+ where
+ S: Into<String>,
+ {
+ Exec {
+ docker,
+ id: id.into(),
+ }
+ }
+
+ /// Creates an exec instance in docker and returns its id
+ pub(crate) async fn create_id(
+ docker: &'a Docker,
+ container_id: &str,
+ opts: &ExecContainerOptions,
+ ) -> Result<String> {
+ #[derive(serde::Deserialize)]
+ #[serde(rename_all = "PascalCase")]
+ struct Response {
+ id: String,
+ }
+
+ let body: Body = opts.serialize()?.into();
+
+ docker
+ .post_json(
+ &format!("/containers/{}/exec", container_id)[..],
+ Some((body, mime::APPLICATION_JSON)),
+ )
+ .await
+ .map(|resp: Response| resp.id)
+ }
+
+ /// Starts an exec instance with id exec_id
+ pub(crate) fn _start(
+ docker: &'a Docker,
+ exec_id: &str,
+ ) -> impl Stream<Item = Result<tty::TtyChunk>> + 'a {
+ let bytes: &[u8] = b"{}";
+
+ let stream = Box::pin(docker.stream_post(
+ format!("/exec/{}/start", &exec_id),
+ Some((bytes.into(), mime::APPLICATION_JSON)),
+ None::<iter::Empty<_>>,
+ ));
+
+ tty::decode(stream)
+ }
+
+ /// Creates a new exec instance that will be executed in a container with id == container_id
+ pub async fn create(
+ docker: &'a Docker,
+ container_id: &str,
+ opts: &ExecContainerOptions,
+ ) -> Result<Exec<'a>> {
+ Ok(Exec::new(
+ docker,
+ Exec::create_id(docker, container_id, opts).await?,
+ ))
+ }
+
+ /// Get a reference to a set of operations available to an already created exec instance.
+ ///
+ /// It's in callers responsibility to ensure that exec instance with specified id actually
+ /// exists. Use [Exec::create](Exec::create) to ensure that the exec instance is created
+ /// beforehand.
+ pub async fn get<S>(
+ docker: &'a Docker,
+ id: S,
+ ) -> Exec<'a>
+ where
+ S: Into<String>,
+ {
+ Exec::new(docker, id)
+ }
+
+ /// Starts this exec instance returning a multiplexed tty stream
+ pub fn start(&'a self) -> impl Stream<Item = Result<tty::TtyChunk>> + 'a {
+ Box::pin(
+ async move {
+ let bytes: &[u8] = b"{}";
+
+ let stream = Box::pin(self.docker.stream_post(
+ format!("/exec/{}/start", &self.id),
+ Some((bytes.into(), mime::APPLICATION_JSON)),
+ None::<iter::Empty<_>>,
+ ));
+
+ Ok(tty::decode(stream))
+ }
+ .try_flatten_stream(),
+ )
+ }
+
+ /// Inspect this exec instance to aquire detailed information
+ pub async fn inspect(&self) -> Result<ExecDetails> {
+ self.docker
+ .get_json(&format!("/exec/{}/json", &self.id)[..])
+ .await
+ }
+
+ pub async fn resize(
+ &self,
+ opts: &ExecResizeOptions,
+ ) -> Result<()> {
+ let body: Body = opts.serialize()?.into();
+
+ self.docker
+ .post_json(
+ &format!("/exec/{}/resize", &self.id)[..],
+ Some((body, mime::APPLICATION_JSON)),
+ )
+ .await
+ }
+}
/// Interface for docker network
pub struct Networks<'a> {
diff --git a/src/rep.rs b/src/rep.rs
index ededa6e..b5619f6 100644
--- a/src/rep.rs
+++ b/src/rep.rs
@@ -495,6 +495,33 @@ pub struct Event {
}
#[derive(Clone, Debug, Serialize, Deserialize)]
+#[serde(rename_all = "PascalCase")]
+pub struct ExecDetails {
+ pub can_remove: bool,
+ #[serde(rename = "ContainerID")]
+ pub container_id: String,
+ pub detach_keys: String,
+ pub exit_code: Option<u64>,
+ #[serde(rename = "ID")]
+ pub id: String,
+ pub open_stderr: bool,
+ pub open_stdin: bool,
+ pub open_stdout: bool,
+ pub process_config: ProcessConfig,
+ pub running: bool,
+ pub pid: u64,
+}
+
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct ProcessConfig {
+ pub arguments: Vec<String>,
+ pub entrypoint: String,
+ pub privileged: bool,
+ pub tty: bool,
+ pub user: Option<String>,
+}
+
+#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Actor {
#[serde(rename = "ID")]
pub id: String,