diff options
author | Matthias Beyer <matthias.beyer@atos.net> | 2021-03-05 20:07:43 +0100 |
---|---|---|
committer | Matthias Beyer <mail@beyermatthias.de> | 2021-03-07 21:50:29 +0100 |
commit | dc77417f60659a4919fb04bc9b14a65e3c6d3b03 (patch) | |
tree | c62b105c6506fdcea6b45e819e6bfbaa6f35efaf | |
parent | e09131771c6dbd535cd2f98a1830fb3d9a1bc427 (diff) |
Add container CLI interface
This patch starts the implementation of a CLI interface to work with a single
container from one endpoint.
The "top" functionality is implemented by now, nothing more.
Signed-off-by: Matthias Beyer <matthias.beyer@atos.net>
Tested-by: Matthias Beyer <matthias.beyer@atos.net>
-rw-r--r-- | src/cli.rs | 67 | ||||
-rw-r--r-- | src/commands/endpoint.rs | 58 |
2 files changed, 125 insertions, 0 deletions
@@ -930,6 +930,73 @@ pub fn cli<'a>() -> App<'a> { ) ) ) + .subcommand(App::new("container") + .version(crate_version!()) + .about("Work with a specific container") + .arg(Arg::new("container_id") + .required(true) + .multiple(false) + .index(1) + .takes_value(true) + .value_name("CONTAINER_ID") + .about("Work with container CONTAINER_ID") + ) + .subcommand(App::new("top") + .version(crate_version!()) + .about("List the container processes") + .arg(Arg::new("csv") + .required(false) + .multiple(false) + .long("csv") + .takes_value(false) + .about("List top output as CSV") + ) + ) + .subcommand(App::new("kill") + .version(crate_version!()) + .about("Kill the container") + .arg(Arg::new("signal") + .required(false) + .multiple(false) + .index(1) + .takes_value(true) + .value_name("SIGNAL") + .about("Kill container with this signal") + ) + ) + .subcommand(App::new("delete") + .version(crate_version!()) + .about("Delete the container") + ) + .subcommand(App::new("start") + .version(crate_version!()) + .about("Start the container") + ) + .subcommand(App::new("stop") + .version(crate_version!()) + .about("Stop the container") + .arg(Arg::new("timeout") + .required(false) + .multiple(false) + .long("timeout") + .takes_value(true) + .value_name("DURATION") + .about("Timeout") + ) + ) + .subcommand(App::new("exec") + .version(crate_version!()) + .about("Execute commands in the container") + .arg(Arg::new("commands") + .required(false) + .multiple(true) + .index(1) + .takes_value(true) + .value_name("CMD") + .about("Commands to execute in the container") + ) + ) + ) ) } diff --git a/src/commands/endpoint.rs b/src/commands/endpoint.rs index abb09bb..0bede59 100644 --- a/src/commands/endpoint.rs +++ b/src/commands/endpoint.rs @@ -39,6 +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(("containers", matches)) => containers(endpoint_names, matches, config).await, Some((other, _)) => Err(anyhow!("Unknown subcommand: {}", other)), None => Err(anyhow!("No subcommand")), @@ -152,6 +153,63 @@ 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((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 containers(endpoint_names: Vec<String>, matches: &ArgMatches, config: &Configuration, |