diff options
author | Matthias Beyer <mail@beyermatthias.de> | 2021-03-07 20:25:35 +0100 |
---|---|---|
committer | Matthias Beyer <mail@beyermatthias.de> | 2021-03-07 21:50:29 +0100 |
commit | f0a223a5c78fb5e7b534f0595899af42b296e0d5 (patch) | |
tree | aed10434bf251dcd8d3d27d1da7290a4bced55e2 | |
parent | e010cb9e9ec4a23ef46ac302e0b1a5c198b5859f (diff) |
Implement container exec subcommand
Signed-off-by: Matthias Beyer <mail@beyermatthias.de>
-rw-r--r-- | src/cli.rs | 2 | ||||
-rw-r--r-- | src/commands/endpoint.rs | 37 |
2 files changed, 38 insertions, 1 deletions
@@ -988,7 +988,7 @@ pub fn cli<'a>() -> App<'a> { .version(crate_version!()) .about("Execute commands in the container") .arg(Arg::new("commands") - .required(false) + .required(true) .multiple(true) .index(1) .takes_value(true) diff --git a/src/commands/endpoint.rs b/src/commands/endpoint.rs index 240770e..7dd7c27 100644 --- a/src/commands/endpoint.rs +++ b/src/commands/endpoint.rs @@ -193,6 +193,7 @@ async fn container(endpoint_names: Vec<String>, Some(("delete", _)) => container_delete(relevant_endpoint, container_id).await, Some(("start", _)) => container_start(relevant_endpoint, container_id).await, Some(("stop", matches)) => container_stop(matches, relevant_endpoint, container_id).await, + Some(("exec", matches)) => container_exec(matches, relevant_endpoint, container_id).await, Some((other, _)) => Err(anyhow!("Unknown subcommand: {}", other)), None => Err(anyhow!("No subcommand")), } @@ -288,6 +289,42 @@ async fn container_stop( .map_err(Error::from) } +async fn container_exec( + matches: &ArgMatches, + endpoint: &Endpoint, + container_id: &str, +) -> Result<()> { + use std::io::Write; + use futures::TryStreamExt; + + let commands = matches.values_of("commands").unwrap().collect::<Vec<&str>>(); + let prompt = format!("Really run '{}' in {}?", commands.join(" "), container_id); + dialoguer::Confirm::new().with_prompt(prompt).interact()?; + + let execopts = shiplift::builder::ExecContainerOptions::builder() + .cmd(commands) + .attach_stdout(true) + .attach_stderr(true) + .build(); + + endpoint + .get_container_by_id(container_id) + .await? + .ok_or_else(|| anyhow!("Cannot find container {} on {}", container_id, endpoint.name()))? + .exec(&execopts) + .map_err(Error::from) + .try_for_each(|chunk| async { + let mut stdout = std::io::stdout(); + let mut stderr = std::io::stderr(); + match chunk { + shiplift::tty::TtyChunk::StdIn(_) => Err(anyhow!("Cannot handle STDIN TTY chunk")), + shiplift::tty::TtyChunk::StdOut(v) => stdout.write(&v).map_err(Error::from).map(|_| ()), + shiplift::tty::TtyChunk::StdErr(v) => stderr.write(&v).map_err(Error::from).map(|_| ()), + } + }) + .await +} + async fn containers(endpoint_names: Vec<String>, matches: &ArgMatches, |