summaryrefslogtreecommitdiffstats
path: root/src/commands/find_artifact.rs
diff options
context:
space:
mode:
authorMatthias Beyer <matthias.beyer@atos.net>2021-01-25 12:58:38 +0100
committerMatthias Beyer <mail@beyermatthias.de>2021-02-11 18:40:48 +0100
commit94d062da9a2040b2a424dd530a647d3827eb4989 (patch)
treebf04e03db48a1db327074f4797d26c5bef0e4136 /src/commands/find_artifact.rs
parent2c4adce9fe8cef616ded756bb10b50e5ec782ecc (diff)
Implement find-artifact subcommand
This patch implements the find-artifact subcommand, which is the basis for artifact finding. Right now we have the problem that finding artifacts after the fact is non-trivial. What we can do, though, is search for them in the database using the same inputs as the ones that created the artifact. That means that if we want to find an artifact or multiple artifacts for a package P in version V, we have to search the database for jobs that built that package in that version, with the same script and the same variables (environment, used container image, etc). So if a package was built with the same script, the same environment variables and on an image that is, for example, not in the denylist of the package, chances are good that the artifacts produced by the job for the package are the ones we search for. In the `crate::command::find_artifact()` function, results are sorted before they are printed, so that we preferrably print results with a release date. Env filtering is also implemented, so a user has to provide the appropriate additional environment variables, as they were submitted for the `buid` command when the artifact was built. Signed-off-by: Matthias Beyer <matthias.beyer@atos.net> Tested-by: Matthias Beyer <matthias.beyer@atos.net>
Diffstat (limited to 'src/commands/find_artifact.rs')
-rw-r--r--src/commands/find_artifact.rs136
1 files changed, 136 insertions, 0 deletions
diff --git a/src/commands/find_artifact.rs b/src/commands/find_artifact.rs
new file mode 100644
index 0000000..4f053f8
--- /dev/null
+++ b/src/commands/find_artifact.rs
@@ -0,0 +1,136 @@
+//
+// Copyright (c) 2020-2021 science+computing ag and other contributors
+//
+// This program and the accompanying materials are made
+// available under the terms of the Eclipse Public License 2.0
+// which is available at https://www.eclipse.org/legal/epl-2.0/
+//
+// SPDX-License-Identifier: EPL-2.0
+//
+
+use std::path::PathBuf;
+use std::io::Write;
+use std::sync::Arc;
+
+use anyhow::Context;
+use anyhow::Error;
+use anyhow::Result;
+use clap::ArgMatches;
+use diesel::PgConnection;
+use itertools::Itertools;
+use log::debug;
+use log::trace;
+
+use crate::config::Configuration;
+use crate::filestore::ReleaseStore;
+use crate::filestore::StagingStore;
+use crate::filestore::path::StoreRoot;
+use crate::package::PackageVersionConstraint;
+use crate::repository::Repository;
+use crate::util::progress::ProgressBars;
+
+/// Implementation of the "find_artifact" subcommand
+pub async fn find_artifact(matches: &ArgMatches, config: &Configuration, progressbars: ProgressBars, repo: Repository, database_connection: PgConnection, max_packages: u64) -> Result<()> {
+ let package_name_regex = crate::commands::util::mk_package_name_regex({
+ matches.value_of("package_name_regex").unwrap() // safe by clap
+ })?;
+
+ let package_version_constraint = matches
+ .value_of("package_version_constraint")
+ .map(String::from)
+ .map(PackageVersionConstraint::new)
+ .transpose()
+ .context("Parsing package version constraint")
+ .context("A valid package version constraint looks like this: '=1.0.0'")?;
+
+ let env_filter = matches.values_of("env_filter")
+ .map(|vals| vals.map(crate::util::env::parse_to_env).collect::<Result<Vec<_>>>())
+ .transpose()?
+ .unwrap_or_default();
+
+ log::debug!("Finding artifacts for '{:?}' '{:?}'", package_name_regex, package_version_constraint);
+
+ let release_store = {
+ let bar_release_loading = progressbars.bar();
+ bar_release_loading.set_length(max_packages);
+
+ let p = config.releases_directory();
+ debug!("Loading release directory: {}", p.display());
+ let r = ReleaseStore::load(StoreRoot::new(p.clone())?, bar_release_loading.clone());
+ if r.is_ok() {
+ bar_release_loading.finish_with_message("Loaded releases successfully");
+ } else {
+ bar_release_loading.finish_with_message("Failed to load releases");
+ }
+ r?
+ };
+ let staging_store = if let Some(p) = matches.value_of("staging_dir").map(PathBuf::from) {
+ let bar_staging_loading = progressbars.bar();
+ bar_staging_loading.set_length(max_packages);
+
+ if !p.is_dir() {
+ let _ = tokio::fs::create_dir_all(&p).await?;
+ }
+
+ debug!("Loading staging directory: {}", p.display());
+ let r = StagingStore::load(StoreRoot::new(p.clone())?, bar_staging_loading.clone());
+ if r.is_ok() {
+ bar_staging_loading.finish_with_message("Loaded staging successfully");
+ } else {
+ bar_staging_loading.finish_with_message("Failed to load staging");
+ }
+ Some(r?)
+ } else {
+ None
+ };
+
+ let database = Arc::new(database_connection);
+ repo.packages()
+ .filter(|p| package_name_regex.captures(p.name()).is_some())
+ .filter(|p| {
+ package_version_constraint
+ .as_ref()
+ .map(|v| v.matches(p.version()))
+ .unwrap_or(true)
+ })
+ .inspect(|pkg| trace!("Found package: {:?}", pkg))
+ .map(|pkg| {
+ let script_filter = !matches.is_present("no_script_filter");
+ let pathes = crate::db::find_artifacts(database.clone(), config, &pkg, &release_store, staging_store.as_ref(), &env_filter, script_filter)?;
+
+ pathes.iter()
+ .map(|tpl| (tpl.0.joined(), tpl.1))
+ .sorted_by(|tpla, tplb| {
+ use std::cmp::Ordering;
+
+ // Sort the iterator elements, so that if there is a release date, we always
+ // prefer the entry with the release date AS LONG AS the path is equal.
+ match (tpla, tplb) {
+ ((a, Some(ta)), (b, Some(tb))) => match a.cmp(b) {
+ Ordering::Equal => ta.cmp(tb),
+ other => other,
+ },
+
+ ((a, Some(_)), (b, None)) => match a.cmp(b) {
+ Ordering::Equal => Ordering::Greater,
+ other => other,
+ },
+ ((a, None), (b, Some(_))) => match a.cmp(b) {
+ Ordering::Equal => Ordering::Less,
+ other => other,
+ },
+ ((a, None), (b, None)) => a.cmp(b),
+ }
+ })
+ .unique_by(|tpl| tpl.0.clone()) // TODO: Dont clone()
+ .try_for_each(|(path, releasetime)| {
+ if let Some(time) = releasetime {
+ writeln!(std::io::stdout(), "[{}] {}", time, path.display())
+ } else {
+ writeln!(std::io::stdout(), "[unknown] {}", path.display())
+ }.map_err(Error::from)
+ })
+ })
+ .inspect(|r| trace!("Query resulted in: {:?}", r))
+ .collect::<Result<()>>()
+}