summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Beyer <matthias.beyer@atos.net>2021-06-07 15:36:26 +0200
committerMatthias Beyer <matthias.beyer@atos.net>2021-06-07 15:36:26 +0200
commit3f3ca1259919cb69f8915b11db9b21d9c03aadbc (patch)
treec5ca554d61caed8e29547670b02346bb2d2977c7
parentdcc8b42810d34b2b906f0307c26c4826d16ad997 (diff)
parentb665c7cd4aa4ea900dc194a6ba5ad9de527f978c (diff)
Merge branch 'db-release-arg-date-filter'
-rw-r--r--src/cli.rs67
-rw-r--r--src/commands/db.rs41
-rw-r--r--src/commands/endpoint.rs32
-rw-r--r--src/commands/util.rs33
4 files changed, 129 insertions, 44 deletions
diff --git a/src/cli.rs b/src/cli.rs
index f9820ff..71a2b67 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -362,6 +362,28 @@ pub fn cli<'a>() -> App<'a> {
.takes_value(false)
.about("Format output as CSV")
)
+
+ .arg(arg_older_than_date("List only releases older than DATE"))
+ .arg(arg_newer_than_date("List only releases newer than DATE"))
+
+ .arg(Arg::new("package_name_regex")
+ .required(false)
+ .multiple(false)
+ .long("pkg")
+ .short('p')
+ .takes_value(true)
+ .value_name("REGEX")
+ .about("Limit search with package name matching REGEX")
+ )
+ .arg(Arg::new("package_version_constraint")
+ .required(false)
+ .multiple(false)
+ .long("version")
+ .short('v')
+ .takes_value(true)
+ .value_name("VERSION_CONSTRAINT")
+ .about("Limit search for package in version VERSION")
+ )
)
)
@@ -1018,14 +1040,14 @@ pub fn cli<'a>() -> App<'a> {
.subcommand(App::new("prune")
.version(crate_version!())
.about("Remove exited containers")
- .arg(arg_older_than_date())
- .arg(arg_newer_than_date())
+ .arg(arg_older_than_date("Prune only containers older than DATE"))
+ .arg(arg_newer_than_date("Prune only containers newer than DATE"))
)
.subcommand(App::new("stop")
.version(crate_version!())
.about("Stop running containers")
- .arg(arg_older_than_date())
- .arg(arg_newer_than_date())
+ .arg(arg_older_than_date("Stop only containers older than DATE"))
+ .arg(arg_newer_than_date("Stop only containers newer than DATE"))
.arg(Arg::new("timeout")
.required(false)
.multiple(false)
@@ -1065,8 +1087,8 @@ pub fn cli<'a>() -> App<'a> {
.about("List only containers of IMAGE")
)
- .arg(arg_older_than_date())
- .arg(arg_newer_than_date())
+ .arg(arg_older_than_date("List only containers older than DATE"))
+ .arg(arg_newer_than_date("List only containers newer than DATE"))
)
.subcommand(App::new("top")
.version(crate_version!())
@@ -1246,18 +1268,18 @@ fn dir_exists_validator(s: &str) -> Result<(), String> {
}
}
-fn arg_older_than_date<'a>() -> Arg<'a> {
+fn arg_older_than_date(about: &str) -> 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")
+ .about(about)
.long_about(r#"
- List only containers that are older than DATE
-
DATE can be a freeform date, for example '2h'
+ It can also be a exact date: '2020-01-01 00:12:45'
+ If the hour-minute-second part is omitted, " 00:00:00" is appended automatically.
Supported suffixes:
@@ -1274,21 +1296,20 @@ fn arg_older_than_date<'a>() -> Arg<'a> {
"#)
.validator(parse_date_from_string)
- .conflicts_with("newer_than")
}
-fn arg_newer_than_date<'a>() -> Arg<'a> {
+fn arg_newer_than_date(about: &str) -> 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")
+ .about(about)
.long_about(r#"
- List only containers that are newer than DATE
-
DATE can be a freeform date, for example '2h'
+ It can also be a exact date: '2020-01-01 00:12:45'
+ If the hour-minute-second part is omitted, " 00:00:00" is appended automatically.
Supported suffixes:
@@ -1305,11 +1326,23 @@ fn arg_newer_than_date<'a>() -> Arg<'a> {
"#)
.validator(parse_date_from_string)
- .conflicts_with("older_than")
}
fn parse_date_from_string(s: &str) -> std::result::Result<(), String> {
- humantime::parse_duration(s).map_err(|e| e.to_string()).map(|_| ())
+ humantime::parse_duration(s)
+ .map_err(|e| e.to_string())
+ .map(|_| ())
+ .or_else(|_| {
+ humantime::parse_rfc3339_weak(s)
+ .map_err(|e| e.to_string())
+ .map(|_| ())
+ })
+ .or_else(|_| {
+ let s = format!("{} 00:00:00", s);
+ humantime::parse_rfc3339_weak(&s)
+ .map_err(|e| e.to_string())
+ .map(|_| ())
+ })
}
fn parse_usize(s: &str) -> std::result::Result<(), String> {
diff --git a/src/commands/db.rs b/src/commands/db.rs
index 5b9a85a..2e86954 100644
--- a/src/commands/db.rs
+++ b/src/commands/db.rs
@@ -14,6 +14,7 @@ use std::io::Write;
use std::path::PathBuf;
use std::process::Command;
use std::str::FromStr;
+use std::convert::TryFrom;
use anyhow::Context;
use anyhow::Error;
@@ -32,9 +33,11 @@ use log::info;
use log::trace;
use crate::config::Configuration;
-use crate::db::models;
use crate::db::DbConnectionConfig;
+use crate::db::models;
use crate::log::JobResult;
+use crate::package::PackageVersion;
+use crate::package::PackageVersionConstraint;
use crate::package::Script;
use crate::schema;
@@ -691,7 +694,7 @@ fn releases(conn_cfg: DbConnectionConfig<'_>, config: &Configuration, matches: &
let csv = matches.is_present("csv");
let conn = conn_cfg.establish_connection()?;
let header = crate::commands::util::mk_header(["Package", "Version", "Date", "Path"].to_vec());
- let data = schema::jobs::table
+ let mut query = schema::jobs::table
.inner_join(schema::packages::table)
.inner_join(schema::artifacts::table)
.inner_join(schema::releases::table
@@ -701,6 +704,28 @@ fn releases(conn_cfg: DbConnectionConfig<'_>, config: &Configuration, matches: &
.order_by(schema::packages::dsl::name.asc())
.then_order_by(schema::packages::dsl::version.asc())
.then_order_by(schema::releases::release_date.asc())
+ .into_boxed();
+
+ if let Some(date) = crate::commands::util::get_date_filter("older_than", matches)? {
+ query = query.filter(schema::releases::release_date.lt(date));
+ }
+
+ if let Some(date) = crate::commands::util::get_date_filter("newer_than", matches)? {
+ query = query.filter(schema::releases::release_date.gt(date));
+ }
+
+ let package_name_regex_filter = matches.value_of("package_name_regex")
+ .map(crate::commands::util::mk_package_name_regex)
+ .transpose()
+ .context("Constructing package name regex")?;
+
+ let package_version_filter = matches.value_of("package_version_constraint")
+ .map(PackageVersionConstraint::try_from)
+ .transpose()
+ .context("Parsing package version constraint")
+ .context("A valid package version constraint looks like this: '=1.0.0'")?;
+
+ let data = query
.select({
let art = schema::artifacts::all_columns;
let pac = schema::packages::all_columns;
@@ -710,8 +735,18 @@ fn releases(conn_cfg: DbConnectionConfig<'_>, config: &Configuration, matches: &
})
.load::<(models::Artifact, models::Package, models::Release, models::ReleaseStore)>(&conn)?
.into_iter()
+ .filter(|(_, package, _, _)| {
+ package_name_regex_filter.as_ref()
+ .map(|regex| regex.captures(&package.name).is_some())
+ .unwrap_or(true)
+ })
+ .filter(|(_, package, _, _)| {
+ package_version_filter.as_ref()
+ .map(|verf| verf.matches(&PackageVersion::from(package.version.clone())))
+ .unwrap_or(true)
+ })
.filter_map(|(art, pack, rel, rstore)| {
- let p = config.releases_directory().join(rstore.store_name).join(&art.path);
+ let p = config.releases_directory().join(rstore.store_name).join(&art.path);
if p.is_file() {
Some(vec![
diff --git a/src/commands/endpoint.rs b/src/commands/endpoint.rs
index 4de51d2..6fd7d69 100644
--- a/src/commands/endpoint.rs
+++ b/src/commands/endpoint.rs
@@ -182,8 +182,8 @@ async fn containers_list(endpoint_names: Vec<EndpointName>,
) -> Result<()> {
let list_stopped = matches.is_present("list_stopped");
let filter_image = matches.value_of("filter_image");
- let older_than_filter = get_date_filter("older_than", matches)?;
- let newer_than_filter = get_date_filter("newer_than", matches)?;
+ let older_than_filter = crate::commands::util::get_date_filter("older_than", matches)?;
+ let newer_than_filter = crate::commands::util::get_date_filter("newer_than", matches)?;
let csv = matches.is_present("csv");
let hdr = crate::commands::util::mk_header([
"Endpoint",
@@ -232,8 +232,8 @@ async fn containers_prune(endpoint_names: Vec<EndpointName>,
matches: &ArgMatches,
config: &Configuration,
) -> Result<()> {
- let older_than_filter = get_date_filter("older_than", matches)?;
- let newer_than_filter = get_date_filter("newer_than", matches)?;
+ let older_than_filter = crate::commands::util::get_date_filter("older_than", matches)?;
+ let newer_than_filter = crate::commands::util::get_date_filter("newer_than", matches)?;
let stats = connect_to_endpoints(config, &endpoint_names)
.await?
@@ -279,8 +279,8 @@ async fn containers_top(endpoint_names: Vec<EndpointName>,
config: &Configuration,
) -> Result<()> {
let limit = matches.value_of("limit").map(usize::from_str).transpose()?;
- let older_than_filter = get_date_filter("older_than", matches)?;
- let newer_than_filter = get_date_filter("newer_than", matches)?;
+ let older_than_filter = crate::commands::util::get_date_filter("older_than", matches)?;
+ let newer_than_filter = crate::commands::util::get_date_filter("newer_than", matches)?;
let csv = matches.is_present("csv");
let data = connect_to_endpoints(config, &endpoint_names)
@@ -374,8 +374,8 @@ async fn containers_stop(endpoint_names: Vec<EndpointName>,
matches: &ArgMatches,
config: &Configuration,
) -> Result<()> {
- let older_than_filter = get_date_filter("older_than", matches)?;
- let newer_than_filter = get_date_filter("newer_than", matches)?;
+ let older_than_filter = crate::commands::util::get_date_filter("older_than", matches)?;
+ let newer_than_filter = crate::commands::util::get_date_filter("newer_than", matches)?;
let stop_timeout = matches.value_of("timeout")
.map(u64::from_str)
@@ -422,22 +422,6 @@ async fn containers_stop(endpoint_names: Vec<EndpointName>,
}
-fn get_date_filter(name: &str, matches: &ArgMatches) -> Result<Option<chrono::DateTime::<chrono::Local>>> {
- matches.value_of(name)
- .map(humantime::parse_duration)
- .transpose()?
- .map(chrono::Duration::from_std)
- .transpose()?
- .map(|dur| {
- chrono::offset::Local::now()
- .checked_sub_signed(dur)
- .ok_or_else(|| anyhow!("Time calculation would overflow"))
- .with_context(|| anyhow!("Cannot subtract {} from 'now'", dur))
- .map_err(Error::from)
- })
- .transpose()
-}
-
/// 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: &[EndpointName]) -> Result<Vec<Arc<Endpoint>>> {
diff --git a/src/commands/util.rs b/src/commands/util.rs
index e892875..acfbdd2 100644
--- a/src/commands/util.rs
+++ b/src/commands/util.rs
@@ -225,3 +225,36 @@ pub fn display_data<D: Display>(
}
}
+pub fn get_date_filter(name: &str, matches: &ArgMatches) -> Result<Option<chrono::DateTime::<chrono::Local>>> {
+ matches.value_of(name)
+ .map(|s| {
+ trace!("Parsing duration: '{}'", s);
+ humantime::parse_duration(s)
+ .map_err(Error::from)
+ .or_else(|_| {
+ trace!("Parsing time: '{}'", s);
+ humantime::parse_rfc3339_weak(s)
+ .map_err(Error::from)
+ .and_then(|d| d.elapsed().map_err(Error::from))
+ })
+ .or_else(|_| {
+ let s = format!("{} 00:00:00", s);
+ trace!("Parsing time: '{}'", s);
+ humantime::parse_rfc3339_weak(&s)
+ .map_err(Error::from)
+ .and_then(|d| d.elapsed().map_err(Error::from))
+ })
+ })
+ .transpose()?
+ .map(chrono::Duration::from_std)
+ .transpose()?
+ .map(|dur| {
+ chrono::offset::Local::now()
+ .checked_sub_signed(dur)
+ .ok_or_else(|| anyhow!("Time calculation would overflow"))
+ .with_context(|| anyhow!("Cannot subtract {} from 'now'", dur))
+ .map_err(Error::from)
+ })
+ .transpose()
+}
+