Skip to content

Commit

Permalink
feat: Add driver selection args (#153)
Browse files Browse the repository at this point in the history
There are 2 new args available that allow the user to specify which
program to use for building and inspecting images. If the user doesn't
provide an argument, the tool will determine which program to use like
it has been.

Help text:

```
Build an image from a recipe

Usage: bluebuild build [OPTIONS] [RECIPE]

Arguments:
  [RECIPE]
          The recipe file to build an image

Options:
  -p, --push
          Push the image with all the tags.

          Requires `--registry`, `--username`, and `--password` if not building in CI.

  -c, --compression-format <COMPRESSION_FORMAT>
          The compression format the images will be pushed in

          [default: gzip]
          [possible values: gzip, zstd]

  -n, --no-retry-push
          Block `bluebuild` from retrying to push the image

      --retry-count <RETRY_COUNT>
          The number of times to retry pushing the image

          [default: 1]

  -f, --force
          Allow `bluebuild` to overwrite an existing Containerfile without confirmation.

          This is not needed if the Containerfile is in .gitignore or has already been built by `bluebuild`.

  -a, --archive <ARCHIVE>
          Archives the built image into a tarfile in the specified directory

      --registry <REGISTRY>
          The registry's domain name

  -v, --verbose...
          Increase logging verbosity

  -q, --quiet...
          Decrease logging verbosity

      --registry-namespace <REGISTRY_NAMESPACE>
          The url path to your base project images

          [aliases: registry-path]

  -U, --username <USERNAME>
          The username to login to the container registry

  -P, --password <PASSWORD>
          The password to login to the container registry

  -B, --build-driver <BUILD_DRIVER>
          Select which driver to use to build your image

          [possible values: buildah, podman, docker]

  -I, --inspect-driver <INSPECT_DRIVER>
          Select which driver to use to inspect images

          [possible values: skopeo, podman, docker]

  -h, --help
          Print help (see a summary with '-h')
```
  • Loading branch information
gmpinder authored Apr 8, 2024
1 parent 0f76b16 commit e9c96e2
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 47 deletions.
23 changes: 21 additions & 2 deletions src/commands.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
use log::error;

use clap::{command, crate_authors, Parser, Subcommand};
use clap::{command, crate_authors, Args, Parser, Subcommand};
use clap_verbosity_flag::{InfoLevel, Verbosity};
use typed_builder::TypedBuilder;

use crate::shadow;
use crate::{
drivers::types::{BuildDriverType, InspectDriverType},
shadow,
};

pub mod bug_report;
pub mod build;
Expand Down Expand Up @@ -92,3 +96,18 @@ pub enum CommandArgs {
/// Generate shell completions for your shell to stdout
Completions(completions::CompletionsCommand),
}

#[derive(Default, Clone, Copy, Debug, TypedBuilder, Args)]
pub struct DriverArgs {
/// Select which driver to use to build
/// your image.
#[builder(default)]
#[arg(short = 'B', long)]
build_driver: Option<BuildDriverType>,

/// Select which driver to use to inspect
/// images.
#[builder(default)]
#[arg(short = 'I', long)]
inspect_driver: Option<InspectDriverType>,
}
8 changes: 7 additions & 1 deletion src/commands/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use crate::{
},
};

use super::BlueBuildCommand;
use super::{BlueBuildCommand, DriverArgs};

#[derive(Debug, Clone, Args, TypedBuilder)]
pub struct BuildCommand {
Expand Down Expand Up @@ -99,6 +99,10 @@ pub struct BuildCommand {
#[arg(short = 'P', long)]
#[builder(default, setter(into, strip_option))]
password: Option<String>,

#[clap(flatten)]
#[builder(default)]
drivers: DriverArgs,
}

impl BlueBuildCommand for BuildCommand {
Expand All @@ -110,6 +114,8 @@ impl BlueBuildCommand for BuildCommand {
.username(self.username.as_ref())
.password(self.password.as_ref())
.registry(self.registry.as_ref())
.build_driver(self.drivers.build_driver)
.inspect_driver(self.drivers.inspect_driver)
.build()
.init()?;

Expand Down
12 changes: 11 additions & 1 deletion src/commands/template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use typed_builder::TypedBuilder;

use crate::drivers::Driver;

use super::BlueBuildCommand;
use super::{BlueBuildCommand, DriverArgs};

#[derive(Debug, Clone, Args, TypedBuilder)]
pub struct TemplateCommand {
Expand Down Expand Up @@ -41,6 +41,10 @@ pub struct TemplateCommand {
#[arg(long)]
#[builder(default, setter(into, strip_option))]
registry_namespace: Option<String>,

#[clap(flatten)]
#[builder(default)]
drivers: DriverArgs,
}

impl BlueBuildCommand for TemplateCommand {
Expand All @@ -53,6 +57,12 @@ impl BlueBuildCommand for TemplateCommand {
.display()
);

Driver::builder()
.build_driver(self.drivers.build_driver)
.inspect_driver(self.drivers.inspect_driver)
.build()
.init()?;

self.template_file()
}
}
Expand Down
127 changes: 84 additions & 43 deletions src/drivers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,13 @@
use std::{
collections::{hash_map::Entry, HashMap},
process,
sync::{Arc, Mutex},
};

use anyhow::{anyhow, bail, Result};
use blue_build_recipe::Recipe;
use blue_build_utils::constants::IMAGE_VERSION_LABEL;
use log::{debug, error, info, trace};
use log::{debug, info, trace};
use once_cell::sync::Lazy;
use semver::{Version, VersionReq};
use typed_builder::TypedBuilder;
Expand All @@ -27,53 +26,70 @@ use self::{
opts::{BuildTagPushOpts, CompressionType},
podman_driver::PodmanDriver,
skopeo_driver::SkopeoDriver,
types::{BuildDriverType, InspectDriverType},
};

mod buildah_driver;
mod docker_driver;
pub mod opts;
mod podman_driver;
mod skopeo_driver;
pub mod types;

/// Stores the build strategy.
static INIT: Lazy<Mutex<()>> = Lazy::new(|| Mutex::new(()));
static SELECTED_BUILD_DRIVER: Lazy<Mutex<Option<BuildDriverType>>> = Lazy::new(|| Mutex::new(None));
static SELECTED_INSPECT_DRIVER: Lazy<Mutex<Option<InspectDriverType>>> =
Lazy::new(|| Mutex::new(None));

/// Stores the build driver.
///
/// This will, on load, find the best way to build in the
/// current environment. Once that strategy is determined,
/// it will be available for any part of the program to call
/// on to perform builds.
///
/// # Exits
/// # Panics
///
/// This will cause the program to exit if a build strategy could
/// This will cause a panic if a build strategy could
/// not be determined.
static BUILD_STRATEGY: Lazy<Arc<dyn BuildDriver>> =
Lazy::new(|| match Driver::determine_build_driver() {
Err(e) => {
error!("{e}");
process::exit(1);
}
Ok(strat) => strat,
});
static BUILD_DRIVER: Lazy<Arc<dyn BuildDriver>> = Lazy::new(|| {
let driver = SELECTED_BUILD_DRIVER.lock().unwrap();
driver.map_or_else(
|| panic!("Driver needs to be initialized"),
|driver| -> Arc<dyn BuildDriver> {
match driver {
BuildDriverType::Buildah => Arc::new(BuildahDriver),
BuildDriverType::Podman => Arc::new(PodmanDriver),
BuildDriverType::Docker => Arc::new(DockerDriver),
}
},
)
});

/// Stores the inspection strategy.
/// Stores the inspection driver.
///
/// This will, on load, find the best way to inspect images in the
/// current environment. Once that strategy is determined,
/// it will be available for any part of the program to call
/// on to perform inspections.
///
/// # Exits
/// # Panics
///
/// This will cause the program to exit if a build strategy could
/// This will cause a panic if a build strategy could
/// not be determined.
static INSPECT_STRATEGY: Lazy<Arc<dyn InspectDriver>> =
Lazy::new(|| match Driver::determine_inspect_driver() {
Err(e) => {
error!("{e}");
process::exit(1);
}
Ok(strat) => strat,
});
static INSPECT_DRIVER: Lazy<Arc<dyn InspectDriver>> = Lazy::new(|| {
let driver = SELECTED_INSPECT_DRIVER.lock().unwrap();
driver.map_or_else(
|| panic!("Driver needs to be initialized"),
|driver| -> Arc<dyn InspectDriver> {
match driver {
InspectDriverType::Skopeo => Arc::new(SkopeoDriver),
InspectDriverType::Podman => Arc::new(PodmanDriver),
InspectDriverType::Docker => Arc::new(DockerDriver),
}
},
)
});

/// UUID used to mark the current builds
static BUILD_ID: Lazy<Uuid> = Lazy::new(Uuid::new_v4);
Expand Down Expand Up @@ -205,6 +221,12 @@ pub struct Driver<'a> {

#[builder(default)]
registry: Option<&'a String>,

#[builder(default)]
build_driver: Option<BuildDriverType>,

#[builder(default)]
inspect_driver: Option<InspectDriverType>,
}

impl Driver<'_> {
Expand All @@ -218,7 +240,30 @@ impl Driver<'_> {
/// Will error if it is unable to set the user credentials.
pub fn init(self) -> Result<()> {
trace!("Driver::init()");
let init = INIT.lock().map_err(|e| anyhow!("{e}"))?;
credentials::set_user_creds(self.username, self.password, self.registry)?;

let mut build_driver = SELECTED_BUILD_DRIVER.lock().map_err(|e| anyhow!("{e}"))?;
let mut inspect_driver = SELECTED_INSPECT_DRIVER.lock().map_err(|e| anyhow!("{e}"))?;

*build_driver = Some(match self.build_driver {
None => Self::determine_build_driver()?,
Some(driver) => driver,
});
trace!("Build driver set to {:?}", *build_driver);
drop(build_driver);
let _ = Self::get_build_driver();

*inspect_driver = Some(match self.inspect_driver {
None => Self::determine_inspect_driver()?,
Some(driver) => driver,
});
trace!("Inspect driver set to {:?}", *inspect_driver);
drop(inspect_driver);
let _ = Self::get_inspection_driver();

drop(init);

Ok(())
}

Expand All @@ -232,13 +277,13 @@ impl Driver<'_> {
/// Gets the current run's build strategy
pub fn get_build_driver() -> Arc<dyn BuildDriver> {
trace!("Driver::get_build_driver()");
BUILD_STRATEGY.clone()
BUILD_DRIVER.clone()
}

/// Gets the current run's inspectioin strategy
pub fn get_inspection_driver() -> Arc<dyn InspectDriver> {
trace!("Driver::get_inspection_driver()");
INSPECT_STRATEGY.clone()
INSPECT_DRIVER.clone()
}

/// Retrieve the `os_version` for an image.
Expand All @@ -263,7 +308,7 @@ impl Driver<'_> {
None => {
info!("Retrieving OS version from {image}. This might take a bit");
let inspection =
INSPECT_STRATEGY.get_metadata(&recipe.base_image, &recipe.image_version)?;
INSPECT_DRIVER.get_metadata(&recipe.base_image, &recipe.image_version)?;

let os_version = inspection.get_version().ok_or_else(|| {
anyhow!(
Expand All @@ -288,48 +333,44 @@ impl Driver<'_> {
Ok(os_version)
}

fn determine_inspect_driver() -> Result<Arc<dyn InspectDriver>> {
fn determine_inspect_driver() -> Result<InspectDriverType> {
trace!("Driver::determine_inspect_driver()");

let driver: Arc<dyn InspectDriver> = match (
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),
(Ok(_skopeo), _, _) => InspectDriverType::Skopeo,
(_, Ok(_docker), _) => InspectDriverType::Docker,
(_, _, Ok(_podman)) => InspectDriverType::Podman,
_ => bail!("Could not determine inspection strategy. You need either skopeo, docker, or podman"),
};

Ok(driver)
})
}

fn determine_build_driver() -> Result<Arc<dyn BuildDriver>> {
fn determine_build_driver() -> Result<BuildDriverType> {
trace!("Driver::determine_build_driver()");

let driver: Arc<dyn BuildDriver> = match (
Ok(match (
blue_build_utils::check_command_exists("docker"),
blue_build_utils::check_command_exists("podman"),
blue_build_utils::check_command_exists("buildah"),
) {
(Ok(_docker), _, _) if DockerDriver::is_supported_version() => {
Arc::new(DockerDriver)
BuildDriverType::Docker
}
(_, Ok(_podman), _) if PodmanDriver::is_supported_version() => {
Arc::new(PodmanDriver)
BuildDriverType::Podman
}
(_, _, Ok(_buildah)) if BuildahDriver::is_supported_version() => {
Arc::new(BuildahDriver)
BuildDriverType::Buildah
}
_ => 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)
})
}
}
15 changes: 15 additions & 0 deletions src/drivers/types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use clap::ValueEnum;

#[derive(Debug, Clone, Copy, ValueEnum)]
pub enum InspectDriverType {
Skopeo,
Podman,
Docker,
}

#[derive(Debug, Clone, Copy, ValueEnum)]
pub enum BuildDriverType {
Buildah,
Podman,
Docker,
}

0 comments on commit e9c96e2

Please sign in to comment.