From ccadfc690619e2c04a78323a35dcf888f7b4bf2f Mon Sep 17 00:00:00 2001 From: Techassi Date: Fri, 10 Oct 2025 09:43:18 +0200 Subject: [PATCH 1/3] feat(boil): Support Containerfiles per version --- Cargo.lock | 119 +++++++++++++++++++++++++++++++- Cargo.toml | 1 + rust/boil/Cargo.toml | 1 + rust/boil/src/build/bakefile.rs | 33 +++++++-- rust/boil/src/build/image.rs | 6 ++ 5 files changed, 153 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5ec421d3e..3ecf62a5d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,6 +26,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "ambient-authority" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9d4ee0d472d1cd2e28c97dfa124b3d8d992e10eb0a035f33f5d12e3a177ba3b" + [[package]] name = "anstream" version = "0.6.18" @@ -107,6 +113,7 @@ checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" name = "boil" version = "0.1.0" dependencies = [ + "cap-std", "clap", "clap_complete", "clap_complete_nushell", @@ -137,6 +144,36 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +[[package]] +name = "cap-primitives" +version = "3.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a1e394ed14f39f8bc26f59d4c0c010dbe7f0a1b9bafff451b1f98b67c8af62a" +dependencies = [ + "ambient-authority", + "fs-set-times", + "io-extras", + "io-lifetimes", + "ipnet", + "maybe-owned", + "rustix 1.0.8", + "rustix-linux-procfs", + "windows-sys 0.59.0", + "winx", +] + +[[package]] +name = "cap-std" +version = "3.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07c0355ca583dd58f176c3c12489d684163861ede3c9efa6fd8bba314c984189" +dependencies = [ + "cap-primitives", + "io-extras", + "io-lifetimes", + "rustix 1.0.8", +] + [[package]] name = "cc" version = "1.2.11" @@ -381,6 +418,17 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs-set-times" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94e7099f6313ecacbe1256e8ff9d617b75d1bcb16a6fddef94866d225a01a14a" +dependencies = [ + "io-lifetimes", + "rustix 1.0.8", + "windows-sys 0.59.0", +] + [[package]] name = "futures-core" version = "0.3.31" @@ -656,6 +704,22 @@ dependencies = [ "web-time", ] +[[package]] +name = "io-extras" +version = "0.18.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2285ddfe3054097ef4b2fe909ef8c3bcd1ea52a8f0d274416caebeef39f04a65" +dependencies = [ + "io-lifetimes", + "windows-sys 0.59.0", +] + +[[package]] +name = "io-lifetimes" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06432fb54d3be7964ecd3649233cddf80db2832f47fec34c01f65b3d9d774983" + [[package]] name = "io-uring" version = "0.7.8" @@ -667,6 +731,12 @@ dependencies = [ "libc", ] +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -756,6 +826,12 @@ version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + [[package]] name = "litemap" version = "0.7.4" @@ -777,6 +853,12 @@ dependencies = [ "regex-automata", ] +[[package]] +name = "maybe-owned" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4facc753ae494aeb6e3c22f839b158aebd4f9270f55cd3c79906c45476c47ab4" + [[package]] name = "memchr" version = "2.7.4" @@ -1063,10 +1145,33 @@ dependencies = [ "bitflags", "errno", "libc", - "linux-raw-sys", + "linux-raw-sys 0.4.15", "windows-sys 0.59.0", ] +[[package]] +name = "rustix" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys 0.9.4", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix-linux-procfs" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fc84bf7e9aa16c4f2c758f27412dc9841341e16aa682d9c7ac308fe3ee12056" +dependencies = [ + "once_cell", + "rustix 1.0.8", +] + [[package]] name = "rustversion" version = "1.0.21" @@ -1252,7 +1357,7 @@ dependencies = [ "fastrand", "getrandom", "once_cell", - "rustix", + "rustix 0.38.44", "windows-sys 0.59.0", ] @@ -1757,6 +1862,16 @@ dependencies = [ "memchr", ] +[[package]] +name = "winx" +version = "0.36.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f3fd376f71958b862e7afb20cfe5a22830e1963462f3a17f49d82a6c1d1f42d" +dependencies = [ + "bitflags", + "windows-sys 0.59.0", +] + [[package]] name = "wit-bindgen-rt" version = "0.33.0" diff --git a/Cargo.toml b/Cargo.toml index c39e68e35..bd77acaec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ authors = ["Stackable "] license = "Apache-2.0" [workspace.dependencies] +cap-std = "3.4.4" clap = { version = "4.5.41", features = ["derive"] } clap_complete = "4.5.55" clap_complete_nushell = "4.5.8" diff --git a/rust/boil/Cargo.toml b/rust/boil/Cargo.toml index 8340f293e..e122c2b97 100644 --- a/rust/boil/Cargo.toml +++ b/rust/boil/Cargo.toml @@ -8,6 +8,7 @@ repository.workspace = true publish = false [dependencies] +cap-std.workspace = true clap.workspace = true clap_complete.workspace = true clap_complete_nushell.workspace = true diff --git a/rust/boil/src/build/bakefile.rs b/rust/boil/src/build/bakefile.rs index 35a4f2f5f..05f9f0820 100644 --- a/rust/boil/src/build/bakefile.rs +++ b/rust/boil/src/build/bakefile.rs @@ -5,6 +5,7 @@ use std::{ path::PathBuf, }; +use cap_std::{ambient_authority, fs::Dir}; use glob::glob; use oci_spec::image::{ ANNOTATION_AUTHORS, ANNOTATION_CREATED, ANNOTATION_DOCUMENTATION, ANNOTATION_LICENSES, @@ -12,7 +13,7 @@ use oci_spec::image::{ }; use semver::Version; use serde::Serialize; -use snafu::{OptionExt, ResultExt, Snafu}; +use snafu::{OptionExt, ResultExt, Snafu, ensure}; use time::format_description::well_known::Rfc3339; use crate::{ @@ -55,6 +56,9 @@ pub enum Error { #[snafu(display("failed to parse build arguments"))] ParseBuildArguments { source: ParseBuildArgumentsError }, + + #[snafu(display("failed to locate containerfile relative to the image folder"))] + NoSuchContainerfileExists, } #[derive(Debug, Snafu)] @@ -329,9 +333,28 @@ impl Bakefile { args.strip_architecture, ); - let dockerfile = PathBuf::new() - .join(&image_name) - .join(&args.target_containerfile); + // By using a cap-std Dir, we can ensure that the paths provided must be relative to + // the appropriate image folder and wont escape it by providing absolute or relative + // paths with traversals (..). + let image_dir = Dir::open_ambient_dir(&image_name, ambient_authority()).unwrap(); + + let containerfile_path = if let Some(custom_path) = &image_options.containerfile { + ensure!( + image_dir.exists(custom_path), + NoSuchContainerfileExistsSnafu + ); + + PathBuf::new().join(&image_name).join(custom_path) + } else { + ensure!( + image_dir.exists(&args.target_containerfile), + NoSuchContainerfileExistsSnafu + ); + + PathBuf::new() + .join(&image_name) + .join(&args.target_containerfile) + }; let target_name = if is_entry { Self::format_entry_target_name(&image_name, &image_version) @@ -359,7 +382,7 @@ impl Bakefile { platforms: vec![args.target_platform.clone()], // NOTE (@Techassi): Should this instead be scoped to the folder of the image we build context: Some(PathBuf::from(".")), - dockerfile: Some(dockerfile), + dockerfile: Some(containerfile_path), inherits: vec![COMMON_TARGET_NAME.to_owned()], annotations, contexts, diff --git a/rust/boil/src/build/image.rs b/rust/boil/src/build/image.rs index fda888e65..8a68be7c6 100644 --- a/rust/boil/src/build/image.rs +++ b/rust/boil/src/build/image.rs @@ -182,6 +182,12 @@ pub struct ImageOptions { // suffixed with _VERSION. #[serde(default)] pub build_arguments: BuildArguments, + + /// A custom path to a Containerfile for a particular version of an image. + /// + /// This is usefull for cases where the same image is being built differently depending on it's + /// version and it is too difficult/messy to do it the same Containerfile. + pub containerfile: Option, } #[derive(Debug)] From 91f6ef9f0a947cef37e9e3d9935a562de30ae7de Mon Sep 17 00:00:00 2001 From: Techassi Date: Fri, 10 Oct 2025 10:40:41 +0200 Subject: [PATCH 2/3] feat(boil): Add image directory to error message --- rust/boil/src/build/bakefile.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rust/boil/src/build/bakefile.rs b/rust/boil/src/build/bakefile.rs index 05f9f0820..a4dee7cde 100644 --- a/rust/boil/src/build/bakefile.rs +++ b/rust/boil/src/build/bakefile.rs @@ -57,8 +57,8 @@ pub enum Error { #[snafu(display("failed to parse build arguments"))] ParseBuildArguments { source: ParseBuildArgumentsError }, - #[snafu(display("failed to locate containerfile relative to the image folder"))] - NoSuchContainerfileExists, + #[snafu(display("failed to locate containerfile relative to the {path:?} directory"))] + NoSuchContainerfileExists { path: String }, } #[derive(Debug, Snafu)] @@ -341,14 +341,14 @@ impl Bakefile { let containerfile_path = if let Some(custom_path) = &image_options.containerfile { ensure!( image_dir.exists(custom_path), - NoSuchContainerfileExistsSnafu + NoSuchContainerfileExistsSnafu { path: image_name } ); PathBuf::new().join(&image_name).join(custom_path) } else { ensure!( image_dir.exists(&args.target_containerfile), - NoSuchContainerfileExistsSnafu + NoSuchContainerfileExistsSnafu { path: image_name } ); PathBuf::new() From 1021efcfd1ba622801e01053e6ffb311d45d052e Mon Sep 17 00:00:00 2001 From: Techassi Date: Mon, 13 Oct 2025 10:34:22 +0200 Subject: [PATCH 3/3] chore: Use 'dockerfile' as config option by default --- rust/boil/src/build/bakefile.rs | 4 ++-- rust/boil/src/build/image.rs | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/rust/boil/src/build/bakefile.rs b/rust/boil/src/build/bakefile.rs index a4dee7cde..505746df1 100644 --- a/rust/boil/src/build/bakefile.rs +++ b/rust/boil/src/build/bakefile.rs @@ -338,7 +338,7 @@ impl Bakefile { // paths with traversals (..). let image_dir = Dir::open_ambient_dir(&image_name, ambient_authority()).unwrap(); - let containerfile_path = if let Some(custom_path) = &image_options.containerfile { + let dockerfile_path = if let Some(custom_path) = &image_options.dockerfile { ensure!( image_dir.exists(custom_path), NoSuchContainerfileExistsSnafu { path: image_name } @@ -382,7 +382,7 @@ impl Bakefile { platforms: vec![args.target_platform.clone()], // NOTE (@Techassi): Should this instead be scoped to the folder of the image we build context: Some(PathBuf::from(".")), - dockerfile: Some(containerfile_path), + dockerfile: Some(dockerfile_path), inherits: vec![COMMON_TARGET_NAME.to_owned()], annotations, contexts, diff --git a/rust/boil/src/build/image.rs b/rust/boil/src/build/image.rs index 8a68be7c6..f0009efc6 100644 --- a/rust/boil/src/build/image.rs +++ b/rust/boil/src/build/image.rs @@ -183,11 +183,12 @@ pub struct ImageOptions { #[serde(default)] pub build_arguments: BuildArguments, - /// A custom path to a Containerfile for a particular version of an image. + /// A custom path to a Dockerfile/Containerfile for a particular version of an image. /// /// This is usefull for cases where the same image is being built differently depending on it's - /// version and it is too difficult/messy to do it the same Containerfile. - pub containerfile: Option, + /// version and it is too difficult/messy to do it the same Dockerfile/Containerfile. + #[serde(alias = "containerfile")] + pub dockerfile: Option, } #[derive(Debug)]