summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Beyer <matthias.beyer@atos.net>2021-03-05 20:07:43 +0100
committerMatthias Beyer <mail@beyermatthias.de>2021-03-07 21:50:29 +0100
commitdc77417f60659a4919fb04bc9b14a65e3c6d3b03 (patch)
treec62b105c6506fdcea6b45e819e6bfbaa6f35efaf
parente09131771c6dbd535cd2f98a1830fb3d9a1bc427 (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.rs67
-rw-r--r--src/commands/endpoint.rs58
2 files changed, 125 insertions, 0 deletions
diff --git a/src/cli.rs b/src/cli.rs
index f110861..044acbe 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -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,