summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Beyer <mail@beyermatthias.de>2021-03-07 21:01:59 +0100
committerMatthias Beyer <mail@beyermatthias.de>2021-03-07 21:50:29 +0100
commit17d1b74c7674da470fe61676c873062404ab6e64 (patch)
treecf43c1f9944ef017e4d1e3a98db3380d2811afd6
parent94a828e2fd4cd7dfdbb52e1a7df3cd04ee9e4c35 (diff)
Reimplement container subcommands with less boilerplate
Signed-off-by: Matthias Beyer <mail@beyermatthias.de>
-rw-r--r--src/commands/endpoint_container.rs172
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