summaryrefslogtreecommitdiffstats
path: root/src/commands/endpoint.rs
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 /src/commands/endpoint.rs
parent17d1b74c7674da470fe61676c873062404ab6e64 (diff)
Add "containers prune" subcommand
Signed-off-by: Matthias Beyer <matthias.beyer@atos.net>
Diffstat (limited to 'src/commands/endpoint.rs')
-rw-r--r--src/commands/endpoint.rs52
1 files changed, 52 insertions, 0 deletions
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>>> {