diff options
author | Matthias Beyer <mail@beyermatthias.de> | 2021-03-07 21:01:59 +0100 |
---|---|---|
committer | Matthias Beyer <mail@beyermatthias.de> | 2021-03-07 21:50:29 +0100 |
commit | 17d1b74c7674da470fe61676c873062404ab6e64 (patch) | |
tree | cf43c1f9944ef017e4d1e3a98db3380d2811afd6 /src | |
parent | 94a828e2fd4cd7dfdbb52e1a7df3cd04ee9e4c35 (diff) |
Reimplement container subcommands with less boilerplate
Signed-off-by: Matthias Beyer <mail@beyermatthias.de>
Diffstat (limited to 'src')
-rw-r--r-- | src/commands/endpoint_container.rs | 172 |
1 files changed, 68 insertions, 104 deletions
diff --git a/src/commands/endpoint_container.rs b/src/commands/endpoint_container.rs index 2003ae0..b891e8b 100644 --- a/src/commands/endpoint_container.rs +++ b/src/commands/endpoint_container.rs @@ -15,9 +15,9 @@ use anyhow::Result; use anyhow::anyhow; use clap::ArgMatches; use tokio_stream::StreamExt; +use shiplift::Container; use crate::config::Configuration; -use crate::endpoint::Endpoint; pub async fn container(endpoint_names: Vec<String>, matches: &ArgMatches, @@ -52,139 +52,103 @@ pub async fn container(endpoint_names: Vec<String>, anyhow!("Found no container for id {}", container_id) })?; + let container = relevant_endpoint.get_container_by_id(container_id) + .await? + .ok_or_else(|| anyhow!("Cannot find container {} on {}", container_id, relevant_endpoint.name()))?; + + let confirm = |prompt: String| dialoguer::Confirm::new().with_prompt(prompt).interact(); + match matches.subcommand() { - Some(("top", matches)) => container_top(matches, relevant_endpoint, container_id).await, - Some(("kill", matches)) => container_kill(matches, relevant_endpoint, container_id).await, - 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(("top", matches)) => top(matches, container).await, + Some(("kill", matches)) => { + confirm({ + if let Some(sig) = matches.value_of("signal").as_ref() { + format!("Really kill {} with {}?", container_id, sig) + } else { + format!("Really kill {}?", container_id) + } + })?; + + kill(matches, container).await + }, + Some(("delete", _)) => { + confirm(format!("Really delete {}?", container_id))?; + delete(container).await + }, + Some(("start", _)) => { + confirm(format!("Really start {}?", container_id))?; + start(container).await + }, + Some(("stop", matches)) => { + confirm(format!("Really stop {}?", container_id))?; + stop(matches, container).await + }, + Some(("exec", matches)) => { + confirm({ + let commands = matches.values_of("commands").unwrap().collect::<Vec<&str>>(); + format!("Really run '{}' in {}?", commands.join(" "), container_id) + })?; + exec(matches, container).await + }, Some((other, _)) => Err(anyhow!("Unknown subcommand: {}", other)), None => Err(anyhow!("No subcommand")), } } -async fn container_top( - matches: &ArgMatches, - endpoint: &Endpoint, - container_id: &str, -) -> Result<()> { - let csv = matches.is_present("csv"); - let top = endpoint - .get_container_by_id(container_id) - .await? - .ok_or_else(|| anyhow!("Cannot find container {} on {}", container_id, endpoint.name()))? - .top(None) - .await?; - +async fn top<'a>(matches: &ArgMatches, container: Container<'a>) -> Result<()> { + let top = container.top(None).await?; let hdr = crate::commands::util::mk_header(top.titles.iter().map(|s| s.as_ref()).collect()); - crate::commands::util::display_data(hdr, top.processes, csv) + crate::commands::util::display_data(hdr, top.processes, matches.is_present("csv")) } -async fn container_kill( - matches: &ArgMatches, - endpoint: &Endpoint, - container_id: &str, -) -> Result<()> { +async fn kill<'a>(matches: &ArgMatches, container: Container<'a>) -> Result<()> { let signal = matches.value_of("signal"); - let prompt = if let Some(sig) = signal.as_ref() { - format!("Really kill {} with {}?", container_id, sig) - } else { - format!("Really kill {}?", container_id) - }; - - dialoguer::Confirm::new().with_prompt(prompt).interact()?; - - endpoint - .get_container_by_id(container_id) - .await? - .ok_or_else(|| anyhow!("Cannot find container {} on {}", container_id, endpoint.name()))? - .kill(signal) - .await - .map_err(Error::from) + container.kill(signal).await.map_err(Error::from) } -async fn container_delete( - endpoint: &Endpoint, - container_id: &str, -) -> Result<()> { - let prompt = format!("Really delete {}?", container_id); - dialoguer::Confirm::new().with_prompt(prompt).interact()?; - - endpoint - .get_container_by_id(container_id) - .await? - .ok_or_else(|| anyhow!("Cannot find container {} on {}", container_id, endpoint.name()))? - .delete() - .await - .map_err(Error::from) +async fn delete<'a>(container: Container<'a>) -> Result<()> { + container.delete().await.map_err(Error::from) } -async fn container_start( - endpoint: &Endpoint, - container_id: &str, -) -> Result<()> { - let prompt = format!("Really start {}?", container_id); - dialoguer::Confirm::new().with_prompt(prompt).interact()?; - - endpoint - .get_container_by_id(container_id) - .await? - .ok_or_else(|| anyhow!("Cannot find container {} on {}", container_id, endpoint.name()))? - .start() - .await - .map_err(Error::from) +async fn start<'a>(container: Container<'a>) -> Result<()> { + container.start().await.map_err(Error::from) } -async fn container_stop( - matches: &ArgMatches, - endpoint: &Endpoint, - container_id: &str, -) -> Result<()> { - let timeout = matches.value_of("timeout").map(u64::from_str).transpose()?.map(std::time::Duration::from_secs); - let prompt = format!("Really stop {}?", container_id); - dialoguer::Confirm::new().with_prompt(prompt).interact()?; - - endpoint - .get_container_by_id(container_id) - .await? - .ok_or_else(|| anyhow!("Cannot find container {} on {}", container_id, endpoint.name()))? - .stop(timeout) - .await - .map_err(Error::from) +async fn stop<'a>(matches: &ArgMatches, container: Container<'a>) -> Result<()> { + container.stop({ + matches + .value_of("timeout") + .map(u64::from_str) + .transpose()? + .map(std::time::Duration::from_secs) + }) + .await + .map_err(Error::from) } -async fn container_exec( - matches: &ArgMatches, - endpoint: &Endpoint, - container_id: &str, -) -> Result<()> { +async fn exec<'a>(matches: &ArgMatches, container: Container<'a>) -> 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) + .cmd({ + matches.values_of("commands").unwrap().collect::<Vec<&str>>() + }) .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) + container.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(|_| ()), + shiplift::tty::TtyChunk::StdOut(v) => { + std::io::stdout().write(&v).map_err(Error::from).map(|_| ()) + }, + shiplift::tty::TtyChunk::StdErr(v) => { + std::io::stderr().write(&v).map_err(Error::from).map(|_| ()) + }, } }) .await |