feat(rechunk): Add the ability to rechunk an image

This commit is contained in:
Gerald Pinder 2024-11-29 00:16:10 -05:00
parent ffa1789422
commit b4fbac2a66
22 changed files with 1002 additions and 190 deletions

View file

@ -52,7 +52,7 @@ pub struct BuildCommand {
/// Requires `--registry`,
/// `--username`, and `--password` if not
/// building in CI.
#[arg(short, long)]
#[arg(short, long, group = "archive_push")]
#[builder(default)]
push: bool,
@ -84,7 +84,7 @@ pub struct BuildCommand {
/// Archives the built image into a tarfile
/// in the specified directory.
#[arg(short, long)]
#[arg(short, long, group = "archive_rechunk", group = "archive_push")]
#[builder(into)]
archive: Option<PathBuf>,
@ -110,6 +110,16 @@ pub struct BuildCommand {
#[builder(default)]
squash: bool,
/// Performs rechunking on the image to allow for smaller images
/// and smaller updates. This will increase the build-time
/// and take up more space during build-time.
///
/// NOTE: This must be run as root!
#[arg(long, group = "archive_rechunk")]
#[builder(default)]
#[cfg(feature = "rechunk")]
rechunk: bool,
#[clap(flatten)]
#[builder(default)]
credentials: CredentialsArgs,
@ -127,6 +137,11 @@ impl BlueBuildCommand for BuildCommand {
fn try_run(&mut self) -> Result<()> {
trace!("BuildCommand::try_run()");
#[cfg(feature = "rechunk")]
if !nix::unistd::Uid::effective().is_root() && self.rechunk {
bail!("You must be root to use the rechunk feature!");
}
Driver::init(self.drivers);
Credentials::init(self.credentials.clone());
@ -272,32 +287,82 @@ impl BuildCommand {
)?;
let image_name = self.image_name(&recipe)?;
let opts = if let Some(archive_dir) = self.archive.as_ref() {
BuildTagPushOpts::builder()
.containerfile(containerfile)
.platform(self.platform)
.archive_path(format!(
"{}/{}.{ARCHIVE_SUFFIX}",
archive_dir.to_string_lossy().trim_end_matches('/'),
recipe.name.to_lowercase().replace('/', "_"),
))
.squash(self.squash)
.build()
} else {
BuildTagPushOpts::builder()
.image(&image_name)
.containerfile(containerfile)
.platform(self.platform)
.tags(tags.collect_cow_vec())
.push(self.push)
.retry_push(self.retry_push)
.retry_count(self.retry_count)
.compression(self.compression_format)
.squash(self.squash)
.build()
let build_fn = || -> Result<Vec<String>> {
Driver::build_tag_push(&self.archive.as_ref().map_or_else(
|| {
BuildTagPushOpts::builder()
.image(&image_name)
.containerfile(containerfile)
.platform(self.platform)
.tags(tags.collect_cow_vec())
.push(self.push)
.retry_push(self.retry_push)
.retry_count(self.retry_count)
.compression(self.compression_format)
.squash(self.squash)
.build()
},
|archive_dir| {
BuildTagPushOpts::builder()
.containerfile(containerfile)
.platform(self.platform)
.archive_path(format!(
"{}/{}.{ARCHIVE_SUFFIX}",
archive_dir.to_string_lossy().trim_end_matches('/'),
recipe.name.to_lowercase().replace('/', "_"),
))
.squash(self.squash)
.build()
},
))
};
let images = Driver::build_tag_push(&opts)?;
#[cfg(feature = "rechunk")]
let images = if self.rechunk {
use blue_build_process_management::drivers::{
opts::{GetMetadataOpts, RechunkOpts},
InspectDriver, RechunkDriver,
};
Driver::rechunk(
&RechunkOpts::builder()
.image(&image_name)
.containerfile(containerfile)
.platform(self.platform)
.tags(tags.collect_cow_vec())
.push(self.push)
.version(format!(
"{version}.<date>",
version = Driver::get_os_version()
.oci_ref(&recipe.base_image_ref()?)
.platform(self.platform)
.call()?,
))
.retry_push(self.retry_push)
.retry_count(self.retry_count)
.compression(self.compression_format)
.base_digest(
Driver::get_metadata(
&GetMetadataOpts::builder()
.image(&*recipe.base_image)
.tag(&*recipe.image_version)
.platform(self.platform)
.build(),
)?
.digest,
)
.repo(Driver::get_repo_url()?)
.name(&*recipe.name)
.description(&*recipe.description)
.base_image(format!("{}:{}", &recipe.base_image, &recipe.image_version))
.build(),
)?
} else {
build_fn()?
};
#[cfg(not(feature = "rechunk"))]
let images = build_fn()?;
if self.push && !self.no_sign {
Driver::sign_and_verify(

View file

@ -12,7 +12,7 @@ use oci_distribution::Reference;
use tempfile::TempDir;
use blue_build_process_management::{
drivers::{opts::RunOpts, Driver, DriverArgs, RunDriver},
drivers::{opts::RunOpts, types::RunDriverType, Driver, DriverArgs, RunDriver},
run_volumes,
};
@ -122,6 +122,12 @@ impl BlueBuildCommand for GenerateIsoCommand {
fn try_run(&mut self) -> Result<()> {
Driver::init(self.drivers);
if !nix::unistd::Uid::effective().is_root()
&& matches!(Driver::get_run_driver(), RunDriverType::Podman)
{
bail!("You must be root to build an ISO!");
}
let image_out_dir = TempDir::new().into_diagnostic()?;
let output_dir = if let Some(output_dir) = self.output_dir.clone() {
@ -239,7 +245,7 @@ impl GenerateIsoCommand {
.volumes(vols)
.build();
let status = Driver::run(&opts).into_diagnostic()?;
let status = Driver::run(&opts)?;
if !status.success() {
bail!("Failed to create ISO");