fix: Run image as fallback for version retrieval

This commit is contained in:
Gerald Pinder 2024-09-29 13:53:18 -04:00
parent 37a69df832
commit 20d1950530
6 changed files with 91 additions and 52 deletions

1
Cargo.lock generated
View file

@ -351,6 +351,7 @@ dependencies = [
"anyhow",
"blue-build-utils",
"bon",
"cached",
"chrono",
"clap",
"colored",

View file

@ -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

View file

@ -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<RwLock<Option<CiDriverType>>> = Lazy::new(|| RwL
/// UUID used to mark the current builds
static BUILD_ID: Lazy<Uuid> = Lazy::new(Uuid::new_v4);
/// The cached os versions
static OS_VERSION: Lazy<Mutex<HashMap<String, u64>>> = 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,78 @@ impl Driver {
}
}
#[cached(
result = true,
key = "String",
convert = "{ oci_ref.to_string() }",
sync_writes = true
)]
fn get_version(oci_ref: &Reference) -> Result<u64> {
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 os_version = Driver::get_metadata(&inspect_opts)
.and_then(|inspection| {
inspection.get_version().ok_or_else(|| {
miette!(
"Failed to parse version from metadata for {}",
oci_ref.to_string().bold()
)
})
})
.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<u64> + '_ {
|err: Report| -> Result<u64> {
warn!("Unable to get version via image inspection due to error:\n{err:?}");
warn!(concat!(
"Pulling and 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() {

View file

@ -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() {

View file

@ -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() {

View file

@ -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() {