summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Beyer <matthias.beyer@atos.net>2021-03-08 12:03:08 +0100
committerMatthias Beyer <matthias.beyer@atos.net>2021-03-08 12:44:27 +0100
commitfaafd9df072c9aaf0e276f4736f3f56011033dc7 (patch)
treeae523daaeefd02412966cca066a317a2ac44baf6
parent17d1b74c7674da470fe61676c873062404ab6e64 (diff)
Add "containers prune" subcommand
Signed-off-by: Matthias Beyer <matthias.beyer@atos.net>
-rw-r--r--src/cli.rs25
-rw-r--r--src/commands/endpoint.rs52
2 files changed, 77 insertions, 0 deletions
diff --git a/src/cli.rs b/src/cli.rs
index fef7957..7edd338 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -879,6 +879,31 @@ pub fn cli<'a>() -> App<'a> {
.subcommand(App::new("containers")
.version(crate_version!())
.about("Work with the containers of the endpoint(s)")
+ .subcommand(App::new("prune")
+ .version(crate_version!())
+ .about("Remove exited containers")
+ .arg(Arg::new("older_than")
+ .required(false)
+ .multiple(false)
+ .long("older-than")
+ .takes_value(true)
+ .value_name("DATE")
+ .about("List only containers that are older than DATE")
+ .validator(parse_date_from_string)
+ .conflicts_with("newer_than")
+ )
+
+ .arg(Arg::new("newer_than")
+ .required(false)
+ .multiple(false)
+ .long("newer-than")
+ .takes_value(true)
+ .value_name("DATE")
+ .about("List only containers that are newer than DATE")
+ .validator(parse_date_from_string)
+ .conflicts_with("older_than")
+ )
+ )
.subcommand(App::new("list")
.version(crate_version!())
.about("List the containers and stats about them")
diff --git a/src/commands/endpoint.rs b/src/commands/endpoint.rs
index 67e6929..7542d68 100644
--- a/src/commands/endpoint.rs
+++ b/src/commands/endpoint.rs
@@ -11,6 +11,7 @@
use std::str::FromStr;
use std::sync::Arc;
+use anyhow::Error;
use anyhow::Result;
use anyhow::anyhow;
use clap::ArgMatches;
@@ -160,6 +161,7 @@ async fn containers(endpoint_names: Vec<String>,
) -> Result<()> {
match matches.subcommand() {
Some(("list", matches)) => containers_list(endpoint_names, matches, config).await,
+ Some(("prune", matches)) => containers_prune(endpoint_names, matches, config).await,
Some((other, _)) => Err(anyhow!("Unknown subcommand: {}", other)),
None => Err(anyhow!("No subcommand")),
}
@@ -223,6 +225,56 @@ async fn containers_list(endpoint_names: Vec<String>,
crate::commands::util::display_data(hdr, data, csv)
}
+async fn containers_prune(endpoint_names: Vec<String>,
+ matches: &ArgMatches,
+ config: &Configuration,
+) -> Result<()> {
+ let older_than_filter = matches.value_of("older_than")
+ .map(humantime::parse_rfc3339_weak)
+ .transpose()?
+ .map(chrono::DateTime::<chrono::Local>::from);
+ let newer_than_filter = matches.value_of("newer_than")
+ .map(humantime::parse_rfc3339_weak)
+ .transpose()?
+ .map(chrono::DateTime::<chrono::Local>::from);
+
+ let stats = connect_to_endpoints(config, &endpoint_names)
+ .await?
+ .into_iter()
+ .map(move |ep| async move {
+ let stats = ep.container_stats()
+ .await?
+ .into_iter()
+ .filter(|stat| stat.state == "exited")
+ .filter(|stat| older_than_filter.as_ref().map(|time| time > &stat.created).unwrap_or(true))
+ .filter(|stat| newer_than_filter.as_ref().map(|time| time < &stat.created).unwrap_or(true))
+ .map(|stat| (ep.clone(), stat))
+ .collect::<Vec<(_, _)>>();
+ Ok(stats)
+ })
+ .collect::<futures::stream::FuturesUnordered<_>>()
+ .collect::<Result<Vec<_>>>()
+ .await?;
+
+ let prompt = format!("Really delete {} Containers?", stats.iter().flatten().count());
+ dialoguer::Confirm::new().with_prompt(prompt).interact()?;
+
+ stats.into_iter()
+ .map(Vec::into_iter)
+ .flatten()
+ .map(|(ep, stat)| async move {
+ ep.get_container_by_id(&stat.id)
+ .await?
+ .ok_or_else(|| anyhow!("Failed to find existing container {}", stat.id))?
+ .delete()
+ .await
+ .map_err(Error::from)
+ })
+ .collect::<futures::stream::FuturesUnordered<_>>()
+ .collect::<Result<()>>()
+ .await
+}
+
/// Helper function to connect to all endpoints from the configuration, that appear (by name) in
/// the `endpoint_names` list
pub(super) async fn connect_to_endpoints(config: &Configuration, endpoint_names: &[String]) -> Result<Vec<Arc<Endpoint>>> {