chore: Add version checks for upstream tools (#121)

This commit is contained in:
Gerald Pinder 2024-03-17 14:14:07 -04:00 committed by GitHub
parent 0e3d6eba9e
commit 5fc4096f0f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 238 additions and 84 deletions

8
Cargo.lock generated
View file

@ -300,6 +300,7 @@ dependencies = [
"podman-api",
"requestty",
"rusty-hook",
"semver",
"serde",
"serde_json",
"serde_yaml 0.9.32",
@ -2936,9 +2937,12 @@ dependencies = [
[[package]]
name = "semver"
version = "1.0.21"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0"
checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca"
dependencies = [
"serde",
]
[[package]]
name = "serde"

View file

@ -57,9 +57,11 @@ clap_complete_nushell = "4"
colorized = "1"
env_logger = "0.11"
fuzzy-matcher = "0.3"
once_cell = "1.19.0"
open = "5"
os_info = "3.7" # update os module config and tests when upgrading os_info
requestty = { version = "0.5", features = ["macros", "termion"] }
semver = { version = "1.0.22", features = ["serde"] }
shadow-rs = { version = "0.26" }
urlencoding = "2.1.3"
users = "0.11.0"
@ -82,7 +84,6 @@ serde_json.workspace = true
serde_yaml.workspace = true
typed-builder.workspace = true
uuid.workspace = true
once_cell = "1.19.0"
[features]
default = []

View file

@ -8,6 +8,14 @@
BlueBuild's command line program that builds Containerfiles and custom images based on your recipe.yml.
## Requirements
The `bluebuild` tool takes advantage of newer build features. Specifically bind, cache, and tmpfs mounts on the `RUN` instructions. We support using the following tools and their versions:
- Docker - v23 and above
- Podman - v4 and above
- Buildah - v1.24 and above
## Installation
### Distrobox

View file

@ -1,3 +1,16 @@
#!/bin/sh
echo 'Running buildah'
print_version_json() {
local version="1.24.0"
printf '{"version": "%s"}\n' "$version"
}
main() {
if [[ "$1" == "version" && "$2" == "--json" ]]; then
print_version_json
else
echo 'Running buildah'
fi
}
main "$@"

View file

@ -1,3 +1,16 @@
#!/bin/sh
echo 'Running podman'
print_version_json() {
local version="4.0.0"
printf '{"Client":{"Version": "%s"}}\n' "$version"
}
main() {
if [[ "$1" == "version" && "$2" == "-f" && "$3" == "json" ]]; then
print_version_json
else
echo 'Running podman'
fi
}
main "$@"

View file

@ -20,6 +20,7 @@ use blue_build_utils::constants::{
};
use log::{debug, error, info, trace};
use once_cell::sync::Lazy;
use semver::{Version, VersionReq};
use typed_builder::TypedBuilder;
use uuid::Uuid;
@ -92,6 +93,26 @@ static BUILD_ID: Lazy<Uuid> = Lazy::new(Uuid::new_v4);
/// The cached os versions
static OS_VERSION: Lazy<Mutex<HashMap<String, String>>> = Lazy::new(|| Mutex::new(HashMap::new()));
/// Trait for retrieving version of a driver.
pub trait DriverVersion {
/// The version req string slice that follows
/// the semver standard <https://semver.org/>.
const VERSION_REQ: &'static str;
/// Returns the version of the driver.
///
/// # Errors
/// Will error if it can't retrieve the version.
fn version() -> Result<Version>;
#[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.
pub trait BuildDriver: Sync + Send {
@ -221,86 +242,92 @@ impl Driver<'_> {
fn determine_inspect_driver() -> Result<Arc<dyn InspectDriver>> {
trace!("Strategy::determine_inspect_strategy()");
Ok(
match (
blue_build_utils::check_command_exists("skopeo"),
blue_build_utils::check_command_exists("docker"),
blue_build_utils::check_command_exists("podman"),
) {
(Ok(_skopeo), _, _) => Arc::new(SkopeoDriver),
(_, Ok(_docker), _) => Arc::new(DockerDriver),
(_, _, Ok(_podman)) => Arc::new(PodmanDriver),
_ => bail!("Could not determine inspection strategy. You need either skopeo, docker, or podman"),
}
)
let driver: Arc<dyn InspectDriver> = match (
blue_build_utils::check_command_exists("skopeo"),
blue_build_utils::check_command_exists("docker"),
blue_build_utils::check_command_exists("podman"),
) {
(Ok(_skopeo), _, _) => Arc::new(SkopeoDriver),
(_, Ok(_docker), _) => Arc::new(DockerDriver),
(_, _, Ok(_podman)) => Arc::new(PodmanDriver),
_ => bail!("Could not determine inspection strategy. You need either skopeo, docker, or podman"),
};
Ok(driver)
}
fn determine_build_driver() -> Result<Arc<dyn BuildDriver>> {
trace!("Strategy::determine_build_strategy()");
Ok(
match (
env::var(XDG_RUNTIME_DIR),
PathBuf::from(RUN_PODMAN_SOCK),
PathBuf::from(VAR_RUN_PODMAN_PODMAN_SOCK),
PathBuf::from(VAR_RUN_PODMAN_SOCK),
blue_build_utils::check_command_exists("docker"),
blue_build_utils::check_command_exists("podman"),
blue_build_utils::check_command_exists("buildah"),
) {
#[cfg(feature = "builtin-podman")]
(Ok(xdg_runtime), _, _, _, _, _, _)
if PathBuf::from(format!("{xdg_runtime}/podman/podman.sock")).exists() =>
{
Arc::new(
PodmanApiDriver::builder()
.client(
Podman::unix(PathBuf::from(format!(
"{xdg_runtime}/podman/podman.sock"
)))
.into(),
)
.rt(Runtime::new()?)
.build(),
)
}
#[cfg(feature = "builtin-podman")]
(_, run_podman_podman_sock, _, _, _, _, _) if run_podman_podman_sock.exists() => {
Arc::new(
PodmanApiDriver::builder()
.client(Podman::unix(run_podman_podman_sock).into())
.rt(Runtime::new()?)
.build(),
)
}
#[cfg(feature = "builtin-podman")]
(_, _, var_run_podman_podman_sock, _, _, _, _)
if var_run_podman_podman_sock.exists() =>
{
Arc::new(
PodmanApiDriver::builder()
.client(Podman::unix(var_run_podman_podman_sock).into())
.rt(Runtime::new()?)
.build(),
)
}
#[cfg(feature = "builtin-podman")]
(_, _, _, var_run_podman_sock, _, _, _) if var_run_podman_sock.exists() => {
Arc::new(
PodmanApiDriver::builder()
.client(Podman::unix(var_run_podman_sock).into())
.rt(Runtime::new()?)
.build(),
)
}
// (_, _, _, _, Ok(_docker), _, _) if !oci_required => {
(_, _, _, _, Ok(_docker), _, _) => Arc::new(DockerDriver),
(_, _, _, _, _, Ok(_podman), _) => Arc::new(PodmanDriver),
(_, _, _, _, _, _, Ok(_buildah)) => Arc::new(BuildahDriver),
_ => bail!(
"Could not determine strategy, need either docker, podman, or buildah to continue"
),
},
)
let driver: Arc<dyn BuildDriver> = match (
env::var(XDG_RUNTIME_DIR),
PathBuf::from(RUN_PODMAN_SOCK),
PathBuf::from(VAR_RUN_PODMAN_PODMAN_SOCK),
PathBuf::from(VAR_RUN_PODMAN_SOCK),
blue_build_utils::check_command_exists("docker"),
blue_build_utils::check_command_exists("podman"),
blue_build_utils::check_command_exists("buildah"),
) {
#[cfg(feature = "builtin-podman")]
(Ok(xdg_runtime), _, _, _, _, _, _)
if PathBuf::from(format!("{xdg_runtime}/podman/podman.sock")).exists() =>
{
Arc::new(
PodmanApiDriver::builder()
.client(
Podman::unix(PathBuf::from(format!(
"{xdg_runtime}/podman/podman.sock"
)))
.into(),
)
.rt(Runtime::new()?)
.build(),
)
}
#[cfg(feature = "builtin-podman")]
(_, run_podman_podman_sock, _, _, _, _, _) if run_podman_podman_sock.exists() => {
Arc::new(
PodmanApiDriver::builder()
.client(Podman::unix(run_podman_podman_sock).into())
.rt(Runtime::new()?)
.build(),
)
}
#[cfg(feature = "builtin-podman")]
(_, _, var_run_podman_podman_sock, _, _, _, _)
if var_run_podman_podman_sock.exists() =>
{
Arc::new(
PodmanApiDriver::builder()
.client(Podman::unix(var_run_podman_podman_sock).into())
.rt(Runtime::new()?)
.build(),
)
}
#[cfg(feature = "builtin-podman")]
(_, _, _, var_run_podman_sock, _, _, _) if var_run_podman_sock.exists() => Arc::new(
PodmanApiDriver::builder()
.client(Podman::unix(var_run_podman_sock).into())
.rt(Runtime::new()?)
.build(),
),
(_, _, _, _, Ok(_docker), _, _) if DockerDriver::is_supported_version() => {
Arc::new(DockerDriver)
}
(_, _, _, _, _, Ok(_podman), _) if PodmanDriver::is_supported_version() => {
Arc::new(PodmanDriver)
}
(_, _, _, _, _, _, Ok(_buildah)) if BuildahDriver::is_supported_version() => {
Arc::new(BuildahDriver)
}
_ => bail!(
"Could not determine strategy, need either docker version {}, podman version {}, or buildah version {} to continue",
DockerDriver::VERSION_REQ,
PodmanDriver::VERSION_REQ,
BuildahDriver::VERSION_REQ,
),
};
Ok(driver)
}
}

View file

@ -2,14 +2,38 @@ use std::process::Command;
use anyhow::{bail, Result};
use log::{info, trace};
use semver::Version;
use serde::Deserialize;
use crate::credentials;
use super::BuildDriver;
use super::{BuildDriver, DriverVersion};
#[derive(Debug, Deserialize)]
struct BuildahVersionJson {
pub version: Version,
}
#[derive(Debug)]
pub struct BuildahDriver;
impl DriverVersion for BuildahDriver {
// RUN mounts for bind, cache, and tmpfs first supported in 1.24.0
// https://buildah.io/releases/#changes-for-v1240
const VERSION_REQ: &'static str = ">=1.24";
fn version() -> Result<Version> {
let output = Command::new("buildah")
.arg("version")
.arg("--json")
.output()?;
let version_json: BuildahVersionJson = serde_json::from_slice(&output.stdout)?;
Ok(version_json.version)
}
}
impl BuildDriver for BuildahDriver {
fn build(&self, image: &str) -> Result<()> {
trace!("buildah build -t {image}");

View file

@ -6,14 +6,46 @@ use std::{
use anyhow::{bail, Result};
use blue_build_utils::constants::{BB_BUILDKIT_CACHE_GHA, SKOPEO_IMAGE};
use log::{info, trace};
use semver::Version;
use serde::Deserialize;
use crate::image_inspection::ImageInspection;
use super::{credentials, BuildDriver, InspectDriver};
use super::{credentials, BuildDriver, DriverVersion, InspectDriver};
#[derive(Debug, Deserialize)]
struct DockerVerisonJsonClient {
#[serde(alias = "Version")]
pub version: Version,
}
#[derive(Debug, Deserialize)]
struct DockerVersionJson {
#[serde(alias = "Client")]
pub client: DockerVerisonJsonClient,
}
#[derive(Debug)]
pub struct DockerDriver;
impl DriverVersion for DockerDriver {
// First docker verison to use buildkit
// https://docs.docker.com/build/buildkit/
const VERSION_REQ: &'static str = ">=23";
fn version() -> Result<Version> {
let output = Command::new("docker")
.arg("version")
.arg("-f")
.arg("json")
.output()?;
let version_json: DockerVersionJson = serde_json::from_slice(&output.stdout)?;
Ok(version_json.client.version)
}
}
impl BuildDriver for DockerDriver {
fn build(&self, image: &str) -> Result<()> {
trace!("docker");

View file

@ -3,14 +3,46 @@ use std::process::{Command, Stdio};
use anyhow::{bail, Result};
use blue_build_utils::constants::SKOPEO_IMAGE;
use log::{debug, info, trace};
use semver::Version;
use serde::Deserialize;
use crate::image_inspection::ImageInspection;
use super::{credentials, BuildDriver, InspectDriver};
use super::{credentials, BuildDriver, DriverVersion, InspectDriver};
#[derive(Debug, Deserialize)]
struct PodmanVersionJsonClient {
#[serde(alias = "Version")]
pub version: Version,
}
#[derive(Debug, Deserialize)]
struct PodmanVersionJson {
#[serde(alias = "Client")]
pub client: PodmanVersionJsonClient,
}
#[derive(Debug)]
pub struct PodmanDriver;
impl DriverVersion for PodmanDriver {
// First podman version to use buildah v1.24
// https://github.com/containers/podman/blob/main/RELEASE_NOTES.md#400
const VERSION_REQ: &'static str = ">=4";
fn version() -> Result<Version> {
let output = Command::new("podman")
.arg("version")
.arg("-f")
.arg("json")
.output()?;
let version_json: PodmanVersionJson = serde_json::from_slice(&output.stdout)?;
Ok(version_json.client.version)
}
}
impl BuildDriver for PodmanDriver {
fn build(&self, image: &str) -> Result<()> {
trace!("podman build . -t {image}");