use std::{
borrow::Borrow,
path::PathBuf,
process::{ExitStatus, Output},
};
use blue_build_utils::{constants::COSIGN_PUB_PATH, retry, semver::Version, string_vec};
use log::{debug, info, trace};
use miette::{Context, IntoDiagnostic, Result, bail};
use oci_distribution::Reference;
use semver::VersionReq;
use crate::drivers::{
Driver,
functions::get_private_key,
types::{CiDriverType, ImageRef},
};
use super::{
buildah_driver::BuildahDriver,
cosign_driver::CosignDriver,
docker_driver::DockerDriver,
github_driver::GithubDriver,
gitlab_driver::GitlabDriver,
local_driver::LocalDriver,
opts::{
BuildOpts, BuildTagPushOpts, CheckKeyPairOpts, CreateContainerOpts, GenerateImageNameOpts,
GenerateKeyPairOpts, GenerateTagsOpts, GetMetadataOpts, PushOpts, RechunkOpts,
RemoveContainerOpts, RemoveImageOpts, RunOpts, SignOpts, SignVerifyOpts, TagOpts,
VerifyOpts, VerifyType,
},
podman_driver::PodmanDriver,
sigstore_driver::SigstoreDriver,
skopeo_driver::SkopeoDriver,
types::{ContainerId, ImageMetadata, MountId},
};
trait PrivateDriver {}
macro_rules! impl_private_driver {
($($driver:ty),* $(,)?) => {
$(
impl PrivateDriver for $driver {}
)*
};
}
impl_private_driver!(
Driver,
DockerDriver,
PodmanDriver,
BuildahDriver,
GithubDriver,
GitlabDriver,
LocalDriver,
CosignDriver,
SkopeoDriver,
CiDriverType,
SigstoreDriver,
);
/// Trait for retrieving version of a driver.
#[allow(private_bounds)]
pub trait DriverVersion: PrivateDriver {
/// The version req string slice that follows
/// the semver standard .
const VERSION_REQ: &'static str;
/// Returns the version of the driver.
///
/// # Errors
/// Will error if it can't retrieve the version.
fn version() -> Result;
#[must_use]
fn is_supported_version() -> bool {
Self::version().is_ok_and(|version| {
VersionReq::parse(Self::VERSION_REQ).is_ok_and(|req| req.matches(&version))
})
}
}
/// Allows agnostic building, tagging
/// pushing, and login.
#[allow(private_bounds)]
pub trait BuildDriver: PrivateDriver {
/// Runs the build logic for the driver.
///
/// # Errors
/// Will error if the build fails.
fn build(opts: &BuildOpts) -> Result<()>;
/// Runs the tag logic for the driver.
///
/// # Errors
/// Will error if the tagging fails.
fn tag(opts: &TagOpts) -> Result<()>;
/// Runs the push logic for the driver
///
/// # Errors
/// Will error if the push fails.
fn push(opts: &PushOpts) -> Result<()>;
/// Runs the login logic for the driver.
///
/// # Errors
/// Will error if login fails.
fn login() -> Result<()>;
/// Runs prune commands for the driver.
///
/// # Errors
/// Will error if the driver fails to prune.
fn prune(opts: &super::opts::PruneOpts) -> Result<()>;
/// Runs the logic for building, tagging, and pushing an image.
///
/// # Errors
/// Will error if building, tagging, or pusing fails.
fn build_tag_push(opts: &BuildTagPushOpts) -> Result> {
trace!("BuildDriver::build_tag_push({opts:#?})");
let build_opts = BuildOpts::builder()
.image(&opts.image)
.containerfile(opts.containerfile.as_ref())
.maybe_platform(opts.platform)
.squash(opts.squash)
.maybe_cache_from(opts.cache_from)
.maybe_cache_to(opts.cache_to)
.secrets(opts.secrets.clone())
.build();
info!("Building image {}", opts.image);
Self::build(&build_opts)?;
let image_list: Vec = match &opts.image {
ImageRef::Remote(image) if !opts.tags.is_empty() => {
debug!("Tagging all images");
let mut image_list = Vec::with_capacity(opts.tags.len());
for tag in &opts.tags {
debug!("Tagging {} with {tag}", &image);
let tagged_image = Reference::with_tag(
image.registry().into(),
image.repository().into(),
tag.to_string(),
);
let tag_opts = TagOpts::builder()
.src_image(image.as_ref())
.dest_image(&tagged_image)
.build();
Self::tag(&tag_opts)?;
image_list.push(tagged_image.to_string());
if opts.push {
let retry_count = if opts.retry_push { opts.retry_count } else { 0 };
debug!("Pushing all images");
// Push images with retries (1s delay between retries)
blue_build_utils::retry(retry_count, 5, || {
debug!("Pushing image {tagged_image}");
let push_opts = PushOpts::builder()
.image(&tagged_image)
.compression_type(opts.compression)
.build();
Self::push(&push_opts)
})?;
}
}
image_list
}
_ => {
string_vec![&opts.image]
}
};
Ok(image_list)
}
}
/// Allows agnostic inspection of images.
#[allow(private_bounds)]
pub trait InspectDriver: PrivateDriver {
/// Gets the metadata on an image tag.
///
/// # Errors
/// Will error if it is unable to get the labels.
fn get_metadata(opts: &GetMetadataOpts) -> Result;
}
/// Allows agnostic running of containers.
#[allow(private_bounds)]
pub trait RunDriver: PrivateDriver {
/// Run a container to perform an action.
///
/// # Errors
/// Will error if there is an issue running the container.
fn run(opts: &RunOpts) -> Result;
/// Run a container to perform an action and capturing output.
///
/// # Errors
/// Will error if there is an issue running the container.
fn run_output(opts: &RunOpts) -> Result