From c1071c84badc9e41af1a130c8ca8d770cfba86e8 Mon Sep 17 00:00:00 2001 From: Gerald Pinder Date: Sun, 29 Sep 2024 13:53:18 -0400 Subject: [PATCH] fix: Run image as fallback for version retrieval --- Cargo.lock | 1 + process/Cargo.toml | 1 + process/drivers.rs | 122 +++++++++++++++++++------------ process/drivers/docker_driver.rs | 5 +- process/drivers/podman_driver.rs | 4 +- process/drivers/skopeo_driver.rs | 5 +- 6 files changed, 86 insertions(+), 52 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 97ef6cbc..61da1bb5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -351,6 +351,7 @@ dependencies = [ "anyhow", "blue-build-utils", "bon", + "cached", "chrono", "clap", "colored", diff --git a/process/Cargo.toml b/process/Cargo.toml index faf90972..3fcd3b2f 100644 --- a/process/Cargo.toml +++ b/process/Cargo.toml @@ -42,6 +42,7 @@ tempdir.workspace = true bon.workspace = true users.workspace = true uuid.workspace = true +cached = "0.53.1" [dev-dependencies] rstest.workspace = true diff --git a/process/drivers.rs b/process/drivers.rs index 42b60165..7b59ef13 100644 --- a/process/drivers.rs +++ b/process/drivers.rs @@ -6,17 +6,19 @@ use std::{ borrow::Borrow, - collections::{hash_map::Entry, HashMap}, fmt::Debug, process::{ExitStatus, Output}, sync::{Mutex, RwLock}, + time::Duration, }; -use blue_build_utils::constants::IMAGE_VERSION_LABEL; use bon::Builder; +use cached::proc_macro::cached; use clap::Args; -use log::{debug, info, trace}; -use miette::{miette, Result}; +use colored::Colorize; +use indicatif::{ProgressBar, ProgressStyle}; +use log::{info, trace, warn}; +use miette::{miette, IntoDiagnostic, Report, Result}; use oci_distribution::Reference; use once_cell::sync::Lazy; use opts::{GenerateImageNameOpts, GenerateTagsOpts}; @@ -24,6 +26,8 @@ use opts::{GenerateImageNameOpts, GenerateTagsOpts}; use sigstore_driver::SigstoreDriver; use uuid::Uuid; +use crate::logging::Logger; + use self::{ buildah_driver::BuildahDriver, cosign_driver::CosignDriver, @@ -75,9 +79,6 @@ static SELECTED_CI_DRIVER: Lazy>> = Lazy::new(|| RwL /// UUID used to mark the current builds static BUILD_ID: Lazy = Lazy::new(Uuid::new_v4); -/// The cached os versions -static OS_VERSION: Lazy>> = Lazy::new(|| Mutex::new(HashMap::new())); - /// Args for selecting the various drivers to use for runtime. /// /// If the args are left uninitialized, the program will determine @@ -202,45 +203,7 @@ impl Driver { } trace!("Driver::get_os_version({oci_ref:#?})"); - let mut os_version_lock = OS_VERSION.lock().expect("Should lock"); - - let entry = os_version_lock.get(&oci_ref.to_string()); - - let os_version = match entry { - None => { - info!("Retrieving OS version from {oci_ref}. This might take a bit"); - let inspect_opts = GetMetadataOpts::builder() - .image(format!( - "{}/{}", - oci_ref.resolve_registry(), - oci_ref.repository() - )) - .tag(oci_ref.tag().unwrap_or("latest")) - .build(); - let inspection = Self::get_metadata(&inspect_opts)?; - - let os_version = inspection.get_version().ok_or_else(|| { - miette!( - help = format!("Please check with the image author about using '{IMAGE_VERSION_LABEL}' to report the os version."), - "Unable to get the OS version from the labels" - ) - })?; - trace!("os_version: {os_version}"); - - os_version - } - Some(os_version) => { - debug!("Found cached {os_version} for {oci_ref}"); - *os_version - } - }; - - if let Entry::Vacant(entry) = os_version_lock.entry(oci_ref.to_string()) { - trace!("Caching version {os_version} for {oci_ref}"); - entry.insert(os_version); - } - drop(os_version_lock); - Ok(os_version) + get_version(oci_ref) } fn get_build_driver() -> BuildDriverType { @@ -264,6 +227,73 @@ impl Driver { } } +#[cached( + result = true, + key = "String", + convert = "{ oci_ref.to_string() }", + sync_writes = true +)] +fn get_version(oci_ref: &Reference) -> Result { + info!("Retrieving OS version from {oci_ref}. This might take a bit"); + let inspect_opts = GetMetadataOpts::builder() + .image(format!( + "{}/{}", + oci_ref.resolve_registry(), + oci_ref.repository() + )) + .tag(oci_ref.tag().unwrap_or("latest")) + .build(); + let inspection = Driver::get_metadata(&inspect_opts)?; + let os_version = inspection + .get_version() + .ok_or_else(|| miette!("Inspect failed")) + .or_else(get_version_run_image(oci_ref))?; + trace!("os_version: {os_version}"); + Ok(os_version) +} + +fn get_version_run_image(oci_ref: &Reference) -> impl FnOnce(Report) -> Result + '_ { + |err: Report| -> Result { + warn!("Unable to get version via image inspection due to error {err:?}"); + warn!(concat!( + "Running the image to retrieve the version. ", + "This will take a while..." + )); + + let progress = Logger::multi_progress().add( + ProgressBar::new_spinner() + .with_style(ProgressStyle::default_spinner()) + .with_message(format!( + "Pulling image {} to get version", + oci_ref.to_string().bold() + )), + ); + progress.enable_steady_tick(Duration::from_millis(100)); + + let output = Driver::run_output( + &RunOpts::builder() + .image(oci_ref.to_string()) + .args(bon::vec![ + "/bin/bash", + "-c", + "grep -Po '(?<=VERSION_ID=)\\d+' /usr/lib/os-release", + ]) + .pull(true) + .remove(true) + .build(), + ) + .into_diagnostic()?; + + progress.finish_and_clear(); + Logger::multi_progress().remove(&progress); + + String::from_utf8_lossy(&output.stdout) + .trim() + .parse() + .into_diagnostic() + } +} + macro_rules! impl_build_driver { ($func:ident($($args:expr),*)) => { match Self::get_build_driver() { diff --git a/process/drivers/docker_driver.rs b/process/drivers/docker_driver.rs index 047e7849..0f1055b7 100644 --- a/process/drivers/docker_driver.rs +++ b/process/drivers/docker_driver.rs @@ -13,6 +13,7 @@ use blue_build_utils::{ credentials::Credentials, string_vec, }; +use colored::Colorize; use indicatif::{ProgressBar, ProgressStyle}; use log::{debug, info, trace, warn}; use miette::{bail, IntoDiagnostic, Result}; @@ -317,7 +318,7 @@ impl InspectDriver for DockerDriver { let progress = Logger::multi_progress().add( ProgressBar::new_spinner() .with_style(ProgressStyle::default_spinner()) - .with_message(format!("Inspecting metadata for {url}")), + .with_message(format!("Inspecting metadata for {}", url.bold())), ); progress.enable_steady_tick(Duration::from_millis(100)); @@ -330,7 +331,7 @@ impl InspectDriver for DockerDriver { ) .into_diagnostic()?; - progress.finish(); + progress.finish_and_clear(); Logger::multi_progress().remove(&progress); if output.status.success() { diff --git a/process/drivers/podman_driver.rs b/process/drivers/podman_driver.rs index 37e6a68e..651bb5aa 100644 --- a/process/drivers/podman_driver.rs +++ b/process/drivers/podman_driver.rs @@ -194,7 +194,7 @@ impl InspectDriver for PodmanDriver { let progress = Logger::multi_progress().add( ProgressBar::new_spinner() .with_style(ProgressStyle::default_spinner()) - .with_message(format!("Inspecting metadata for {url}")), + .with_message(format!("Inspecting metadata for {}", url.bold())), ); progress.enable_steady_tick(Duration::from_millis(100)); @@ -207,7 +207,7 @@ impl InspectDriver for PodmanDriver { ) .into_diagnostic()?; - progress.finish(); + progress.finish_and_clear(); Logger::multi_progress().remove(&progress); if output.status.success() { diff --git a/process/drivers/skopeo_driver.rs b/process/drivers/skopeo_driver.rs index 5e7377ba..d0e1c1c7 100644 --- a/process/drivers/skopeo_driver.rs +++ b/process/drivers/skopeo_driver.rs @@ -1,6 +1,7 @@ use std::{process::Stdio, time::Duration}; use blue_build_utils::cmd; +use colored::Colorize; use indicatif::{ProgressBar, ProgressStyle}; use log::{debug, trace}; use miette::{bail, IntoDiagnostic, Result}; @@ -24,7 +25,7 @@ impl InspectDriver for SkopeoDriver { let progress = Logger::multi_progress().add( ProgressBar::new_spinner() .with_style(ProgressStyle::default_spinner()) - .with_message(format!("Inspecting metadata for {url}")), + .with_message(format!("Inspecting metadata for {}", url.bold())), ); progress.enable_steady_tick(Duration::from_millis(100)); @@ -34,7 +35,7 @@ impl InspectDriver for SkopeoDriver { .output() .into_diagnostic()?; - progress.finish(); + progress.finish_and_clear(); Logger::multi_progress().remove(&progress); if output.status.success() {