summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Beyer <mail@beyermatthias.de>2021-03-07 20:31:26 +0100
committerMatthias Beyer <mail@beyermatthias.de>2021-03-07 21:50:29 +0100
commit94a828e2fd4cd7dfdbb52e1a7df3cd04ee9e4c35 (patch)
treea9ca183000f4b05a75ee31cdd848be8e6832b7d6
parentf0a223a5c78fb5e7b534f0595899af42b296e0d5 (diff)
Move container subcommand to own file
Signed-off-by: Matthias Beyer <mail@beyermatthias.de>
-rw-r--r--src/commands/endpoint.rs176
-rw-r--r--src/commands/endpoint_container.rs192
-rw-r--r--src/commands/mod.rs1
3 files changed, 195 insertions, 174 deletions
diff --git a/src/commands/endpoint.rs b/src/commands/endpoint.rs
index 7dd7c27..67e6929 100644
--- a/src/commands/endpoint.rs
+++ b/src/commands/endpoint.rs
@@ -11,7 +11,6 @@
use std::str::FromStr;
use std::sync::Arc;
-use anyhow::Error;
use anyhow::Result;
use anyhow::anyhow;
use clap::ArgMatches;
@@ -40,7 +39,7 @@ pub async fn endpoint(matches: &ArgMatches, config: &Configuration, progress_gen
match matches.subcommand() {
Some(("ping", matches)) => ping(endpoint_names, matches, config, progress_generator).await,
Some(("stats", matches)) => stats(endpoint_names, matches, config, progress_generator).await,
- Some(("container", matches)) => container(endpoint_names, matches, config).await,
+ Some(("container", matches)) => crate::commands::endpoint_container::container(endpoint_names, matches, config).await,
Some(("containers", matches)) => containers(endpoint_names, matches, config).await,
Some((other, _)) => Err(anyhow!("Unknown subcommand: {}", other)),
None => Err(anyhow!("No subcommand")),
@@ -154,177 +153,6 @@ async fn stats(endpoint_names: Vec<String>,
crate::commands::util::display_data(hdr, data, csv)
}
-async fn container(endpoint_names: Vec<String>,
- matches: &ArgMatches,
- config: &Configuration,
-) -> Result<()> {
- let container_id = matches.value_of("container_id").unwrap();
- let endpoints = connect_to_endpoints(config, &endpoint_names).await?;
- let relevant_endpoints = endpoints.into_iter()
- .map(|ep| async {
- ep.has_container_with_id(container_id)
- .await
- .map(|b| (ep, b))
- })
- .collect::<futures::stream::FuturesUnordered<_>>()
- .collect::<Result<Vec<(_, bool)>>>()
- .await?
- .into_iter()
- .filter_map(|tpl| {
- if tpl.1 {
- Some(tpl.0)
- } else {
- None
- }
- })
- .collect::<Vec<_>>();
-
- if relevant_endpoints.len() > 1 {
- return Err(anyhow!("Found more than one container for id {}", container_id))
- }
-
- let relevant_endpoint = relevant_endpoints.get(0).ok_or_else(|| {
- anyhow!("Found no container for id {}", container_id)
- })?;
-
- 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((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?;
-
- 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)
-}
-
-async fn container_kill(
- matches: &ArgMatches,
- endpoint: &Endpoint,
- container_id: &str,
-) -> 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)
-}
-
-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 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 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 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,
@@ -397,7 +225,7 @@ async fn containers_list(endpoint_names: Vec<String>,
/// Helper function to connect to all endpoints from the configuration, that appear (by name) in
/// the `endpoint_names` list
-async fn connect_to_endpoints(config: &Configuration, endpoint_names: &[String]) -> Result<Vec<Arc<Endpoint>>> {
+pub(super) async fn connect_to_endpoints(config: &Configuration, endpoint_names: &[String]) -> Result<Vec<Arc<Endpoint>>> {
let endpoint_configurations = config
.docker()
.endpoints()
diff --git a/src/commands/endpoint_container.rs b/src/commands/endpoint_container.rs
new file mode 100644
index 0000000..2003ae0
--- /dev/null
+++ b/src/commands/endpoint_container.rs
@@ -0,0 +1,192 @@
+//
+// Copyright (c) 2020-2021 science+computing ag and other contributors
+//
+// This program and the accompanying materials are made
+// available under the terms of the Eclipse Public License 2.0
+// which is available at https://www.eclipse.org/legal/epl-2.0/
+//
+// SPDX-License-Identifier: EPL-2.0
+//
+
+use std::str::FromStr;
+
+use anyhow::Error;
+use anyhow::Result;
+use anyhow::anyhow;
+use clap::ArgMatches;
+use tokio_stream::StreamExt;
+
+use crate::config::Configuration;
+use crate::endpoint::Endpoint;
+
+pub async fn container(endpoint_names: Vec<String>,
+ matches: &ArgMatches,
+ config: &Configuration,
+) -> Result<()> {
+ let container_id = matches.value_of("container_id").unwrap();
+ let endpoints = crate::commands::endpoint::connect_to_endpoints(config, &endpoint_names).await?;
+ let relevant_endpoints = endpoints.into_iter()
+ .map(|ep| async {
+ ep.has_container_with_id(container_id)
+ .await
+ .map(|b| (ep, b))
+ })
+ .collect::<futures::stream::FuturesUnordered<_>>()
+ .collect::<Result<Vec<(_, bool)>>>()
+ .await?
+ .into_iter()
+ .filter_map(|tpl| {
+ if tpl.1 {
+ Some(tpl.0)
+ } else {
+ None
+ }
+ })
+ .collect::<Vec<_>>();
+
+ if relevant_endpoints.len() > 1 {
+ return Err(anyhow!("Found more than one container for id {}", container_id))
+ }
+
+ let relevant_endpoint = relevant_endpoints.get(0).ok_or_else(|| {
+ anyhow!("Found no container for id {}", container_id)
+ })?;
+
+ 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((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?;
+
+ 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)
+}
+
+async fn container_kill(
+ matches: &ArgMatches,
+ endpoint: &Endpoint,
+ container_id: &str,
+) -> 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)
+}
+
+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 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 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 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
+}
+
diff --git a/src/commands/mod.rs b/src/commands/mod.rs
index bdabd7e..0256507 100644
--- a/src/commands/mod.rs
+++ b/src/commands/mod.rs
@@ -16,6 +16,7 @@ pub use db::db;
mod endpoint;
pub use endpoint::endpoint;
+pub(super) mod endpoint_container;
mod env_of;
pub use env_of::env_of;