fix: Use built-in image inspection for podman and docker
This commit is contained in:
parent
566380a82a
commit
4e0ab374c9
8 changed files with 195 additions and 80 deletions
|
|
@ -35,7 +35,6 @@ use self::{
|
|||
docker_driver::DockerDriver,
|
||||
github_driver::GithubDriver,
|
||||
gitlab_driver::GitlabDriver,
|
||||
image_metadata::ImageMetadata,
|
||||
local_driver::LocalDriver,
|
||||
opts::{
|
||||
BuildOpts, BuildTagPushOpts, CheckKeyPairOpts, GenerateKeyPairOpts, GetMetadataOpts,
|
||||
|
|
@ -44,8 +43,8 @@ use self::{
|
|||
podman_driver::PodmanDriver,
|
||||
skopeo_driver::SkopeoDriver,
|
||||
types::{
|
||||
BuildDriverType, CiDriverType, DetermineDriver, InspectDriverType, RunDriverType,
|
||||
SigningDriverType,
|
||||
BuildDriverType, CiDriverType, DetermineDriver, ImageMetadata, InspectDriverType,
|
||||
RunDriverType, SigningDriverType,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -57,7 +56,6 @@ mod docker_driver;
|
|||
mod functions;
|
||||
mod github_driver;
|
||||
mod gitlab_driver;
|
||||
pub mod image_metadata;
|
||||
mod local_driver;
|
||||
pub mod opts;
|
||||
mod podman_driver;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use std::{
|
||||
collections::HashMap,
|
||||
env,
|
||||
io::Write,
|
||||
path::Path,
|
||||
|
|
@ -9,15 +10,15 @@ use std::{
|
|||
|
||||
use blue_build_utils::{
|
||||
cmd,
|
||||
constants::{BB_BUILDKIT_CACHE_GHA, CONTAINER_FILE, DOCKER_HOST, SKOPEO_IMAGE},
|
||||
constants::{BB_BUILDKIT_CACHE_GHA, CONTAINER_FILE, DOCKER_HOST},
|
||||
credentials::Credentials,
|
||||
string_vec,
|
||||
traits::IntoCollector,
|
||||
};
|
||||
use colored::Colorize;
|
||||
use indicatif::{ProgressBar, ProgressStyle};
|
||||
use log::{debug, info, trace, warn};
|
||||
use miette::{bail, IntoDiagnostic, Result};
|
||||
use miette::{bail, miette, IntoDiagnostic, Report, Result};
|
||||
use oci_distribution::Reference;
|
||||
use once_cell::sync::Lazy;
|
||||
use semver::Version;
|
||||
use serde::Deserialize;
|
||||
|
|
@ -25,9 +26,9 @@ use tempdir::TempDir;
|
|||
|
||||
use crate::{
|
||||
drivers::{
|
||||
image_metadata::ImageMetadata,
|
||||
opts::{RunOptsEnv, RunOptsVolume},
|
||||
types::Platform,
|
||||
types::{InspectDriverType, Platform},
|
||||
Driver,
|
||||
},
|
||||
logging::{CommandLogging, Logger},
|
||||
signal_handler::{add_cid, remove_cid, ContainerId, ContainerRuntime},
|
||||
|
|
@ -35,9 +36,53 @@ use crate::{
|
|||
|
||||
use super::{
|
||||
opts::{BuildOpts, BuildTagPushOpts, GetMetadataOpts, PushOpts, RunOpts, TagOpts},
|
||||
types::ImageMetadata,
|
||||
BuildDriver, DriverVersion, InspectDriver, RunDriver,
|
||||
};
|
||||
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
struct DockerImageMetadata {
|
||||
config: DockerImageMetadataConfig,
|
||||
repo_digests: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
struct DockerImageMetadataConfig {
|
||||
labels: HashMap<String, serde_json::Value>,
|
||||
}
|
||||
|
||||
impl TryFrom<Vec<DockerImageMetadata>> for ImageMetadata {
|
||||
type Error = Report;
|
||||
|
||||
fn try_from(mut value: Vec<DockerImageMetadata>) -> Result<Self> {
|
||||
if value.is_empty() {
|
||||
bail!("Need at least one metadata entry:\n{value:?}");
|
||||
}
|
||||
|
||||
let mut value = value.swap_remove(0);
|
||||
if value.repo_digests.is_empty() {
|
||||
bail!("Metadata requires at least 1 digest:\n{value:#?}");
|
||||
}
|
||||
|
||||
let digest: Reference = value
|
||||
.repo_digests
|
||||
.swap_remove(0)
|
||||
.parse()
|
||||
.into_diagnostic()?;
|
||||
let digest = digest
|
||||
.digest()
|
||||
.ok_or_else(|| miette!("Unable to read digest from {digest}"))?
|
||||
.to_string();
|
||||
|
||||
Ok(Self {
|
||||
labels: value.config.labels,
|
||||
digest,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct DockerVerisonJsonClient {
|
||||
#[serde(alias = "Version")]
|
||||
|
|
@ -280,7 +325,14 @@ impl BuildDriver for DockerDriver {
|
|||
format!(
|
||||
"type=image,name={first_image},push=true,compression={},oci-mediatypes=true",
|
||||
opts.compression
|
||||
)
|
||||
),
|
||||
// Load the image to the local image registry
|
||||
// if the inspect driver is docker so that
|
||||
// we don't have to pull the image again to inspect.
|
||||
if matches!(
|
||||
Driver::get_inspect_driver(),
|
||||
InspectDriverType::Docker,
|
||||
) => "--load",
|
||||
);
|
||||
} else {
|
||||
cmd!(command, "--load");
|
||||
|
|
@ -318,34 +370,44 @@ impl BuildDriver for DockerDriver {
|
|||
|
||||
impl InspectDriver for DockerDriver {
|
||||
fn get_metadata(opts: &GetMetadataOpts) -> Result<ImageMetadata> {
|
||||
trace!("DockerDriver::get_labels({opts:#?})");
|
||||
trace!("DockerDriver::get_metadata({opts:#?})");
|
||||
|
||||
let url = opts.tag.as_ref().map_or_else(
|
||||
|| format!("docker://{}", opts.image),
|
||||
|tag| format!("docker://{}:{tag}", opts.image),
|
||||
|| format!("{}", opts.image),
|
||||
|tag| format!("{}:{tag}", opts.image),
|
||||
);
|
||||
|
||||
let progress = Logger::multi_progress().add(
|
||||
ProgressBar::new_spinner()
|
||||
.with_style(ProgressStyle::default_spinner())
|
||||
.with_message(format!("Inspecting metadata for {}", url.bold())),
|
||||
.with_message(format!(
|
||||
"Inspecting metadata for {}, pulling image...",
|
||||
url.bold()
|
||||
)),
|
||||
);
|
||||
progress.enable_steady_tick(Duration::from_millis(100));
|
||||
|
||||
let mut args = Vec::new();
|
||||
if !matches!(opts.platform, Platform::Native) {
|
||||
args.extend(["--override-arch", opts.platform.arch()]);
|
||||
}
|
||||
args.extend(["inspect", &url]);
|
||||
let mut command = cmd!(
|
||||
"docker",
|
||||
"pull",
|
||||
if !matches!(opts.platform, Platform::Native) => [
|
||||
"--platform",
|
||||
opts.platform.to_string(),
|
||||
],
|
||||
&url,
|
||||
);
|
||||
trace!("{command:?}");
|
||||
|
||||
let output = Self::run_output(
|
||||
&RunOpts::builder()
|
||||
.image(SKOPEO_IMAGE)
|
||||
.args(args.collect_into_vec())
|
||||
.remove(true)
|
||||
.build(),
|
||||
)
|
||||
.into_diagnostic()?;
|
||||
let output = command.output().into_diagnostic()?;
|
||||
|
||||
if !output.status.success() {
|
||||
bail!("Failed to pull {} for inspection!", url.bold());
|
||||
}
|
||||
|
||||
let mut command = cmd!("docker", "image", "inspect", "--format=json", &url);
|
||||
trace!("{command:?}");
|
||||
|
||||
let output = command.output().into_diagnostic()?;
|
||||
|
||||
progress.finish_and_clear();
|
||||
Logger::multi_progress().remove(&progress);
|
||||
|
|
@ -356,7 +418,11 @@ impl InspectDriver for DockerDriver {
|
|||
bail!("Failed to inspect image {url}")
|
||||
}
|
||||
|
||||
serde_json::from_slice(&output.stdout).into_diagnostic()
|
||||
serde_json::from_slice::<Vec<DockerImageMetadata>>(&output.stdout)
|
||||
.into_diagnostic()
|
||||
.inspect(|metadata| trace!("{metadata:#?}"))
|
||||
.and_then(ImageMetadata::try_from)
|
||||
.inspect(|metadata| trace!("{metadata:#?}"))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,26 +0,0 @@
|
|||
use blue_build_utils::constants::IMAGE_VERSION_LABEL;
|
||||
use serde::Deserialize;
|
||||
use serde_json::Value;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
pub struct ImageMetadata {
|
||||
#[serde(alias = "Labels")]
|
||||
pub labels: HashMap<String, Value>,
|
||||
|
||||
#[serde(alias = "Digest")]
|
||||
pub digest: String,
|
||||
}
|
||||
|
||||
impl ImageMetadata {
|
||||
#[must_use]
|
||||
pub fn get_version(&self) -> Option<u64> {
|
||||
Some(
|
||||
self.labels
|
||||
.get(IMAGE_VERSION_LABEL)?
|
||||
.as_str()
|
||||
.and_then(|v| lenient_semver::parse(v).ok())?
|
||||
.major,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,25 +1,25 @@
|
|||
use std::{
|
||||
collections::HashMap,
|
||||
io::Write,
|
||||
path::Path,
|
||||
process::{Command, ExitStatus, Stdio},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use blue_build_utils::{
|
||||
cmd, constants::SKOPEO_IMAGE, credentials::Credentials, traits::IntoCollector,
|
||||
};
|
||||
use blue_build_utils::{cmd, credentials::Credentials};
|
||||
use colored::Colorize;
|
||||
use indicatif::{ProgressBar, ProgressStyle};
|
||||
use log::{debug, error, info, trace, warn};
|
||||
use miette::{bail, miette, IntoDiagnostic, Result};
|
||||
use miette::{bail, miette, IntoDiagnostic, Report, Result};
|
||||
use oci_distribution::Reference;
|
||||
use semver::Version;
|
||||
use serde::Deserialize;
|
||||
use tempdir::TempDir;
|
||||
|
||||
use crate::{
|
||||
drivers::{
|
||||
image_metadata::ImageMetadata,
|
||||
opts::{RunOptsEnv, RunOptsVolume},
|
||||
types::ImageMetadata,
|
||||
types::Platform,
|
||||
},
|
||||
logging::{CommandLogging, Logger},
|
||||
|
|
@ -31,6 +31,46 @@ use super::{
|
|||
BuildDriver, DriverVersion, InspectDriver, RunDriver,
|
||||
};
|
||||
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
struct PodmanImageMetadata {
|
||||
labels: HashMap<String, serde_json::Value>,
|
||||
repo_digests: Vec<String>,
|
||||
}
|
||||
|
||||
impl TryFrom<Vec<PodmanImageMetadata>> for ImageMetadata {
|
||||
type Error = Report;
|
||||
|
||||
fn try_from(mut value: Vec<PodmanImageMetadata>) -> std::result::Result<Self, Self::Error> {
|
||||
if value.is_empty() {
|
||||
bail!("Podman inspection must have at least one metadata entry:\n{value:?}");
|
||||
}
|
||||
if value.is_empty() {
|
||||
bail!("Need at least one metadata entry:\n{value:?}");
|
||||
}
|
||||
|
||||
let mut value = value.swap_remove(0);
|
||||
if value.repo_digests.is_empty() {
|
||||
bail!("Podman Metadata requires at least 1 digest:\n{value:#?}");
|
||||
}
|
||||
|
||||
let digest: Reference = value
|
||||
.repo_digests
|
||||
.swap_remove(0)
|
||||
.parse()
|
||||
.into_diagnostic()?;
|
||||
let digest = digest
|
||||
.digest()
|
||||
.ok_or_else(|| miette!("Unable to read digest from {digest}"))?
|
||||
.to_string();
|
||||
|
||||
Ok(Self {
|
||||
labels: value.labels,
|
||||
digest,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct PodmanVersionJsonClient {
|
||||
#[serde(alias = "Version")]
|
||||
|
|
@ -194,31 +234,41 @@ impl InspectDriver for PodmanDriver {
|
|||
trace!("PodmanDriver::get_metadata({opts:#?})");
|
||||
|
||||
let url = opts.tag.as_deref().map_or_else(
|
||||
|| format!("docker://{}", opts.image),
|
||||
|tag| format!("docker://{}:{tag}", opts.image),
|
||||
|| format!("{}", opts.image),
|
||||
|tag| format!("{}:{tag}", opts.image),
|
||||
);
|
||||
|
||||
let progress = Logger::multi_progress().add(
|
||||
ProgressBar::new_spinner()
|
||||
.with_style(ProgressStyle::default_spinner())
|
||||
.with_message(format!("Inspecting metadata for {}", url.bold())),
|
||||
.with_message(format!(
|
||||
"Inspecting metadata for {}, pulling image...",
|
||||
url.bold()
|
||||
)),
|
||||
);
|
||||
progress.enable_steady_tick(Duration::from_millis(100));
|
||||
|
||||
let mut args = Vec::new();
|
||||
if !matches!(opts.platform, Platform::Native) {
|
||||
args.extend(["--override-arch", opts.platform.arch()]);
|
||||
}
|
||||
args.extend(["inspect", &url]);
|
||||
let mut command = cmd!(
|
||||
"podman",
|
||||
"pull",
|
||||
if !matches!(opts.platform, Platform::Native) => [
|
||||
"--platform",
|
||||
opts.platform.to_string(),
|
||||
],
|
||||
&url,
|
||||
);
|
||||
trace!("{command:?}");
|
||||
|
||||
let output = Self::run_output(
|
||||
&RunOpts::builder()
|
||||
.image(SKOPEO_IMAGE)
|
||||
.args(args.collect_into_vec())
|
||||
.remove(true)
|
||||
.build(),
|
||||
)
|
||||
.into_diagnostic()?;
|
||||
let output = command.output().into_diagnostic()?;
|
||||
|
||||
if !output.status.success() {
|
||||
bail!("Failed to pull {} for inspection!", url.bold());
|
||||
}
|
||||
|
||||
let mut command = cmd!("podman", "image", "inspect", "--format=json", &url);
|
||||
trace!("{command:?}");
|
||||
|
||||
let output = command.output().into_diagnostic()?;
|
||||
|
||||
progress.finish_and_clear();
|
||||
Logger::multi_progress().remove(&progress);
|
||||
|
|
@ -228,7 +278,11 @@ impl InspectDriver for PodmanDriver {
|
|||
} else {
|
||||
bail!("Failed to inspect image {url}");
|
||||
}
|
||||
serde_json::from_slice(&output.stdout).into_diagnostic()
|
||||
serde_json::from_slice::<Vec<PodmanImageMetadata>>(&output.stdout)
|
||||
.into_diagnostic()
|
||||
.inspect(|metadata| trace!("{metadata:#?}"))
|
||||
.and_then(TryFrom::try_from)
|
||||
.inspect(|metadata| trace!("{metadata:#?}"))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use miette::{bail, IntoDiagnostic, Result};
|
|||
|
||||
use crate::{drivers::types::Platform, logging::Logger};
|
||||
|
||||
use super::{image_metadata::ImageMetadata, opts::GetMetadataOpts, InspectDriver};
|
||||
use super::{opts::GetMetadataOpts, types::ImageMetadata, InspectDriver};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SkopeoDriver;
|
||||
|
|
|
|||
|
|
@ -13,12 +13,12 @@ use semver::{Version, VersionReq};
|
|||
use crate::drivers::{functions::get_private_key, types::CiDriverType, Driver};
|
||||
|
||||
use super::{
|
||||
image_metadata::ImageMetadata,
|
||||
opts::{
|
||||
BuildOpts, BuildTagPushOpts, CheckKeyPairOpts, GenerateImageNameOpts, GenerateKeyPairOpts,
|
||||
GenerateTagsOpts, GetMetadataOpts, PushOpts, RunOpts, SignOpts, SignVerifyOpts, TagOpts,
|
||||
VerifyOpts, VerifyType,
|
||||
},
|
||||
types::ImageMetadata,
|
||||
};
|
||||
|
||||
/// Trait for retrieving version of a driver.
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
use std::env;
|
||||
use std::{collections::HashMap, env};
|
||||
|
||||
use blue_build_utils::constants::{GITHUB_ACTIONS, GITLAB_CI};
|
||||
use blue_build_utils::constants::{GITHUB_ACTIONS, GITLAB_CI, IMAGE_VERSION_LABEL};
|
||||
use clap::ValueEnum;
|
||||
use log::trace;
|
||||
use serde::Deserialize;
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::drivers::{
|
||||
buildah_driver::BuildahDriver, docker_driver::DockerDriver, podman_driver::PodmanDriver,
|
||||
|
|
@ -205,3 +207,23 @@ impl std::fmt::Display for Platform {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
pub struct ImageMetadata {
|
||||
pub labels: HashMap<String, Value>,
|
||||
pub digest: String,
|
||||
}
|
||||
|
||||
impl ImageMetadata {
|
||||
#[must_use]
|
||||
pub fn get_version(&self) -> Option<u64> {
|
||||
Some(
|
||||
self.labels
|
||||
.get(IMAGE_VERSION_LABEL)?
|
||||
.as_str()
|
||||
.and_then(|v| lenient_semver::parse(v).ok())?
|
||||
.major,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -179,6 +179,7 @@ impl BlueBuildCommand for BuildCommand {
|
|||
} else {
|
||||
PathBuf::from(CONTAINER_FILE)
|
||||
})
|
||||
.platform(self.platform)
|
||||
.recipe(recipe)
|
||||
.drivers(self.drivers)
|
||||
.build()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue