diff --git a/Cargo.lock b/Cargo.lock index 6be1bd7..8c36278 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -511,6 +511,7 @@ dependencies = [ "base64 0.22.1", "blake2", "bon", + "cached", "chrono", "clap", "comlexr", @@ -521,6 +522,7 @@ dependencies = [ "lenient_semver", "log", "miette", + "nix", "process_control", "rstest", "semver", diff --git a/justfile b/justfile index 3a5c90e..9def7ce 100644 --- a/justfile +++ b/justfile @@ -149,7 +149,7 @@ test-docker-build: install-debug-all-features test-rechunk-build: install-debug-all-features cd integration-tests/test-repo \ - && sudo -E {{ cargo_bin }}/bluebuild build \ + && bluebuild build \ {{ should_push }} \ -vv \ --rechunk \ @@ -157,7 +157,7 @@ test-rechunk-build: install-debug-all-features test-fresh-rechunk-build: install-debug-all-features cd integration-tests/test-repo \ - && sudo -E {{ cargo_bin }}/bluebuild build \ + && bluebuild build \ {{ should_push }} \ -vv \ --rechunk \ diff --git a/process/Cargo.toml b/process/Cargo.toml index e4caa1a..1f137c9 100644 --- a/process/Cargo.toml +++ b/process/Cargo.toml @@ -32,7 +32,7 @@ indicatif.workspace = true indexmap.workspace = true log.workspace = true miette.workspace = true -nix = { workspace = true, features = ["signal", "user"] } +nix = { workspace = true, features = ["signal"] } oci-distribution.workspace = true reqwest.workspace = true semver.workspace = true diff --git a/process/drivers.rs b/process/drivers.rs index 8ad4bd2..1469e25 100644 --- a/process/drivers.rs +++ b/process/drivers.rs @@ -24,8 +24,9 @@ use miette::{miette, IntoDiagnostic, Result}; use oci_distribution::Reference; use once_cell::sync::Lazy; use opts::{ - BuildOpts, BuildTagPushOpts, CheckKeyPairOpts, GenerateImageNameOpts, GenerateKeyPairOpts, - GenerateTagsOpts, GetMetadataOpts, PushOpts, RunOpts, SignOpts, TagOpts, VerifyOpts, + BuildOpts, BuildTagPushOpts, CheckKeyPairOpts, ContainerOpts, CreateContainerOpts, + GenerateImageNameOpts, GenerateKeyPairOpts, GenerateTagsOpts, GetMetadataOpts, PushOpts, + RemoveContainerOpts, RemoveImageOpts, RunOpts, SignOpts, TagOpts, VerifyOpts, VolumeOpts, }; use types::{ BuildDriverType, CiDriverType, DetermineDriver, ImageMetadata, InspectDriverType, Platform, @@ -272,7 +273,7 @@ fn get_version_run_image(oci_ref: &Reference) -> Result { progress.enable_steady_tick(Duration::from_millis(100)); let should_remove = if matches!(Driver::get_run_driver(), RunDriverType::Docker) { - !Driver::list_images()?.contains(oci_ref) + !Driver::list_images(false)?.contains(oci_ref) } else { false }; @@ -291,7 +292,7 @@ fn get_version_run_image(oci_ref: &Reference) -> Result { )?; if should_remove { - Driver::remove_image(oci_ref)?; + Driver::remove_image(&RemoveImageOpts::builder().image(oci_ref).build())?; } progress.finish_and_clear(); @@ -407,20 +408,20 @@ impl RunDriver for Driver { impl_run_driver!(run_output(opts)) } - fn create_container(image: &Reference) -> Result { - impl_run_driver!(create_container(image)) + fn create_container(opts: &CreateContainerOpts) -> Result { + impl_run_driver!(create_container(opts)) } - fn remove_container(container_id: &types::ContainerId) -> Result<()> { - impl_run_driver!(remove_container(container_id)) + fn remove_container(opts: &RemoveContainerOpts) -> Result<()> { + impl_run_driver!(remove_container(opts)) } - fn remove_image(image: &Reference) -> Result<()> { - impl_run_driver!(remove_image(image)) + fn remove_image(opts: &RemoveImageOpts) -> Result<()> { + impl_run_driver!(remove_image(opts)) } - fn list_images() -> Result> { - impl_run_driver!(list_images()) + fn list_images(privileged: bool) -> Result> { + impl_run_driver!(list_images(privileged)) } } @@ -473,16 +474,16 @@ impl CiDriver for Driver { #[cfg(feature = "rechunk")] impl ContainerMountDriver for Driver { - fn mount_container(container_id: &types::ContainerId) -> Result { - PodmanDriver::mount_container(container_id) + fn mount_container(opts: &ContainerOpts) -> Result { + PodmanDriver::mount_container(opts) } - fn unmount_container(container_id: &types::ContainerId) -> Result<()> { - PodmanDriver::unmount_container(container_id) + fn unmount_container(opts: &ContainerOpts) -> Result<()> { + PodmanDriver::unmount_container(opts) } - fn remove_volume(volume_id: &str) -> Result<()> { - PodmanDriver::remove_volume(volume_id) + fn remove_volume(opts: &VolumeOpts) -> Result<()> { + PodmanDriver::remove_volume(opts) } } @@ -501,32 +502,4 @@ impl RechunkDriver for Driver { fn rechunk(opts: &opts::RechunkOpts) -> Result> { PodmanDriver::rechunk(opts) } - - fn prune_image( - _mount: &types::MountId, - _container: &types::ContainerId, - _raw_image: &Reference, - _opts: &opts::RechunkOpts<'_>, - ) -> Result<(), miette::Error> { - unimplemented!("Use the `rechunk` function instead"); - } - - fn create_ostree_commit( - _mount: &types::MountId, - _ostree_cache_id: &str, - _container: &types::ContainerId, - _raw_image: &Reference, - _opts: &opts::RechunkOpts<'_>, - ) -> Result<()> { - unimplemented!("Use the `rechunk` function instead"); - } - - fn rechunk_image( - _ostree_cache_id: &str, - _temp_dir_str: &str, - _current_dir: &str, - _opts: &opts::RechunkOpts<'_>, - ) -> Result<()> { - unimplemented!("Use the `rechunk` function instead"); - } } diff --git a/process/drivers/docker_driver.rs b/process/drivers/docker_driver.rs index 31e9f04..6aa11f3 100644 --- a/process/drivers/docker_driver.rs +++ b/process/drivers/docker_driver.rs @@ -36,6 +36,8 @@ use crate::{ signal_handler::{add_cid, remove_cid, ContainerRuntime, ContainerSignalId}, }; +use super::opts::{CreateContainerOpts, RemoveContainerOpts, RemoveImageOpts}; + #[derive(Debug, Deserialize)] struct DockerVerisonJsonClient { #[serde(alias = "Version")] @@ -507,11 +509,11 @@ impl RunDriver for DockerDriver { Ok(output) } - fn create_container(image: &oci_distribution::Reference) -> Result { - trace!("DockerDriver::create_container({image})"); + fn create_container(opts: &CreateContainerOpts) -> Result { + trace!("DockerDriver::create_container({opts:?})"); let output = { - let c = cmd!("docker", "create", image.to_string(), "bash",); + let c = cmd!("docker", "create", opts.image.to_string(), "bash",); trace!("{c:?}"); c } @@ -519,7 +521,7 @@ impl RunDriver for DockerDriver { .into_diagnostic()?; if !output.status.success() { - bail!("Failed to create container from image {image}"); + bail!("Failed to create container from image {}", opts.image); } Ok(ContainerId( @@ -527,11 +529,11 @@ impl RunDriver for DockerDriver { )) } - fn remove_container(container_id: &super::types::ContainerId) -> Result<()> { - trace!("DockerDriver::remove_container({container_id})"); + fn remove_container(opts: &RemoveContainerOpts) -> Result<()> { + trace!("DockerDriver::remove_container({opts:?})"); let output = { - let c = cmd!("docker", "rm", container_id); + let c = cmd!("docker", "rm", opts.container_id); trace!("{c:?}"); c } @@ -539,17 +541,17 @@ impl RunDriver for DockerDriver { .into_diagnostic()?; if !output.status.success() { - bail!("Failed to remove container {container_id}"); + bail!("Failed to remove container {}", opts.container_id); } Ok(()) } - fn remove_image(image: &oci_distribution::Reference) -> Result<()> { - trace!("DockerDriver::remove_image({image})"); + fn remove_image(opts: &RemoveImageOpts) -> Result<()> { + trace!("DockerDriver::remove_image({opts:?})"); let output = { - let c = cmd!("docker", "rmi", image.to_string()); + let c = cmd!("docker", "rmi", opts.image.to_string()); trace!("{c:?}"); c } @@ -557,13 +559,13 @@ impl RunDriver for DockerDriver { .into_diagnostic()?; if !output.status.success() { - bail!("Failed to remove the image {image}"); + bail!("Failed to remove the image {}", opts.image); } Ok(()) } - fn list_images() -> Result> { + fn list_images(_privileged: bool) -> Result> { #[derive(Deserialize, Debug)] #[serde(rename_all = "PascalCase")] struct Image { diff --git a/process/drivers/opts/build.rs b/process/drivers/opts/build.rs index 04d097c..f5c946b 100644 --- a/process/drivers/opts/build.rs +++ b/process/drivers/opts/build.rs @@ -24,18 +24,27 @@ pub struct BuildOpts<'scope> { #[builder(default)] pub host_network: bool, + + #[builder(default)] + pub privileged: bool, } #[derive(Debug, Clone, Builder)] pub struct TagOpts<'scope> { pub src_image: &'scope Reference, pub dest_image: &'scope Reference, + + #[builder(default)] + pub privileged: bool, } #[derive(Debug, Clone, Builder)] pub struct PushOpts<'scope> { pub image: &'scope Reference, pub compression_type: Option, + + #[builder(default)] + pub privileged: bool, } #[derive(Debug, Clone, Builder)] @@ -95,4 +104,8 @@ pub struct BuildTagPushOpts<'scope> { /// The platform to build the image on. #[builder(default)] pub platform: Platform, + + /// Runs the build with elevated privileges + #[builder(default)] + pub privileged: bool, } diff --git a/process/drivers/opts/rechunk.rs b/process/drivers/opts/rechunk.rs index 82f9b84..3269560 100644 --- a/process/drivers/opts/rechunk.rs +++ b/process/drivers/opts/rechunk.rs @@ -2,7 +2,7 @@ use std::{borrow::Cow, path::Path}; use bon::Builder; -use crate::drivers::types::Platform; +use crate::drivers::types::{ContainerId, Platform}; use super::CompressionType; @@ -49,3 +49,20 @@ pub struct RechunkOpts<'scope> { #[builder(default)] pub clear_plan: bool, } + +#[derive(Debug, Clone, Builder)] +pub struct ContainerOpts<'scope> { + pub container_id: &'scope ContainerId, + + #[builder(default)] + pub privileged: bool, +} + +#[derive(Debug, Clone, Builder)] +pub struct VolumeOpts<'scope> { + #[builder(into)] + pub volume_id: Cow<'scope, str>, + + #[builder(default)] + pub privileged: bool, +} diff --git a/process/drivers/opts/run.rs b/process/drivers/opts/run.rs index cd86d04..6ce37dc 100644 --- a/process/drivers/opts/run.rs +++ b/process/drivers/opts/run.rs @@ -1,6 +1,9 @@ use std::borrow::Cow; use bon::Builder; +use oci_distribution::Reference; + +use crate::drivers::types::ContainerId; #[derive(Debug, Clone, Builder)] pub struct RunOpts<'scope> { @@ -74,3 +77,27 @@ macro_rules! run_envs { } }; } + +#[derive(Debug, Clone, Builder)] +pub struct CreateContainerOpts<'scope> { + pub image: &'scope Reference, + + #[builder(default)] + pub privileged: bool, +} + +#[derive(Debug, Clone, Builder)] +pub struct RemoveContainerOpts<'scope> { + pub container_id: &'scope ContainerId, + + #[builder(default)] + pub privileged: bool, +} + +#[derive(Debug, Clone, Builder)] +pub struct RemoveImageOpts<'scope> { + pub image: &'scope Reference, + + #[builder(default)] + pub privileged: bool, +} diff --git a/process/drivers/podman_driver.rs b/process/drivers/podman_driver.rs index d34c7d2..854a748 100644 --- a/process/drivers/podman_driver.rs +++ b/process/drivers/podman_driver.rs @@ -5,7 +5,7 @@ use std::{ time::Duration, }; -use blue_build_utils::{credentials::Credentials, semver::Version}; +use blue_build_utils::{credentials::Credentials, running_as_root, semver::Version}; use cached::proc_macro::cached; use colored::Colorize; use comlexr::{cmd, pipe}; @@ -26,7 +26,10 @@ use crate::{ signal_handler::{add_cid, remove_cid, ContainerRuntime, ContainerSignalId}, }; -use super::types::ContainerId; +use super::{ + opts::{ContainerOpts, CreateContainerOpts, RemoveContainerOpts, RemoveImageOpts, VolumeOpts}, + types::ContainerId, +}; #[cfg(feature = "rechunk")] use super::{types::MountId, ContainerMountDriver, RechunkDriver}; @@ -44,9 +47,6 @@ impl TryFrom> for ImageMetadata { if value.is_empty() { bail!("Podman inspection must have at least one metadata entry:\n{value:?}"); } - if value.is_empty() { - bail!("Need at least one metadata entry:\n{value:?}"); - } let mut value = value.swap_remove(0); if value.repo_digests.is_empty() { @@ -134,8 +134,17 @@ impl BuildDriver for PodmanDriver { fn build(opts: &BuildOpts) -> Result<()> { trace!("PodmanDriver::build({opts:#?})"); + let use_sudo = opts.privileged && !running_as_root(); let command = cmd!( - "podman", + if use_sudo { + "sudo" + } else { + "podman" + }, + if use_sudo => [ + "-A", + "podman", + ], "build", if !matches!(opts.platform, Platform::Native) => [ "--platform", @@ -169,7 +178,21 @@ impl BuildDriver for PodmanDriver { let dest_image_str = opts.dest_image.to_string(); - let mut command = cmd!("podman", "tag", opts.src_image.to_string(), &dest_image_str); + let use_sudo = opts.privileged && !running_as_root(); + let mut command = cmd!( + if use_sudo { + "sudo" + } else { + "podman" + }, + if use_sudo => [ + "-A", + "podman", + ], + "tag", + opts.src_image.to_string(), + &dest_image_str + ); trace!("{command:?}"); let status = command.status().into_diagnostic()?; @@ -187,8 +210,17 @@ impl BuildDriver for PodmanDriver { let image_str = opts.image.to_string(); + let use_sudo = opts.privileged && !running_as_root(); let command = cmd!( - "podman", + if use_sudo { + "sudo" + } else { + "podman" + }, + if use_sudo => [ + "-A", + "podman", + ], "push", format!( "--compression-format={}", @@ -345,9 +377,22 @@ fn get_metadata_cache(opts: &GetMetadataOpts) -> Result { #[cfg(feature = "rechunk")] impl ContainerMountDriver for PodmanDriver { - fn mount_container(container_id: &super::types::ContainerId) -> Result { + fn mount_container(opts: &ContainerOpts) -> Result { + let use_sudo = opts.privileged && !running_as_root(); let output = { - let c = cmd!("podman", "mount", container_id); + let c = cmd!( + if use_sudo { + "sudo" + } else { + "podman" + }, + if use_sudo => [ + "-A", + "podman", + ], + "mount", + opts.container_id, + ); trace!("{c:?}"); c } @@ -355,7 +400,7 @@ impl ContainerMountDriver for PodmanDriver { .into_diagnostic()?; if !output.status.success() { - bail!("Failed to mount container {container_id}"); + bail!("Failed to mount container {}", opts.container_id); } Ok(MountId( @@ -363,9 +408,22 @@ impl ContainerMountDriver for PodmanDriver { )) } - fn unmount_container(container_id: &super::types::ContainerId) -> Result<()> { + fn unmount_container(opts: &ContainerOpts) -> Result<()> { + let use_sudo = opts.privileged && !running_as_root(); let output = { - let c = cmd!("podman", "unmount", container_id); + let c = cmd!( + if use_sudo { + "sudo" + } else { + "podman" + }, + if use_sudo => [ + "-A", + "podman", + ], + "unmount", + opts.container_id + ); trace!("{c:?}"); c } @@ -373,15 +431,29 @@ impl ContainerMountDriver for PodmanDriver { .into_diagnostic()?; if !output.status.success() { - bail!("Failed to unmount container {container_id}"); + bail!("Failed to unmount container {}", opts.container_id); } Ok(()) } - fn remove_volume(volume_id: &str) -> Result<()> { + fn remove_volume(opts: &VolumeOpts) -> Result<()> { + let use_sudo = opts.privileged && !running_as_root(); let output = { - let c = cmd!("podman", "volume", "rm", volume_id); + let c = cmd!( + if use_sudo { + "sudo" + } else { + "podman" + }, + if use_sudo => [ + "-A", + "podman", + ], + "volume", + "rm", + &*opts.volume_id + ); trace!("{c:?}"); c } @@ -389,7 +461,7 @@ impl ContainerMountDriver for PodmanDriver { .into_diagnostic()?; if !output.status.success() { - bail!("Failed to remove volume {volume_id}"); + bail!("Failed to remove volume {}", &opts.volume_id); } Ok(()) @@ -436,9 +508,25 @@ impl RunDriver for PodmanDriver { Ok(output) } - fn create_container(image: &Reference) -> Result { + fn create_container(opts: &CreateContainerOpts) -> Result { + trace!("PodmanDriver::create_container({opts:?})"); + + let use_sudo = opts.privileged && !running_as_root(); let output = { - let c = cmd!("podman", "create", image.to_string(), "bash"); + let c = cmd!( + if use_sudo { + "sudo" + } else { + "podman" + }, + if use_sudo => [ + "-A", + "podman", + ], + "create", + opts.image.to_string(), + "bash" + ); trace!("{c:?}"); c } @@ -446,7 +534,7 @@ impl RunDriver for PodmanDriver { .into_diagnostic()?; if !output.status.success() { - bail!("Failed to create a container from image {image}"); + bail!("Failed to create a container from image {}", opts.image); } Ok(ContainerId( @@ -454,9 +542,24 @@ impl RunDriver for PodmanDriver { )) } - fn remove_container(container_id: &super::types::ContainerId) -> Result<()> { + fn remove_container(opts: &RemoveContainerOpts) -> Result<()> { + trace!("PodmanDriver::remove_container({opts:?})"); + + let use_sudo = opts.privileged && !running_as_root(); let output = { - let c = cmd!("podman", "rm", container_id); + let c = cmd!( + if use_sudo { + "sudo" + } else { + "podman" + }, + if use_sudo => [ + "-A", + "podman", + ], + "rm", + opts.container_id, + ); trace!("{c:?}"); c } @@ -464,15 +567,30 @@ impl RunDriver for PodmanDriver { .into_diagnostic()?; if !output.status.success() { - bail!("Failed to remove container {container_id}"); + bail!("Failed to remove container {}", opts.container_id); } Ok(()) } - fn remove_image(image: &Reference) -> Result<()> { + fn remove_image(opts: &RemoveImageOpts) -> Result<()> { + trace!("PodmanDriver::remove_image({opts:?})"); + + let use_sudo = opts.privileged && !running_as_root(); let output = { - let c = cmd!("podman", "rmi", image.to_string()); + let c = cmd!( + if use_sudo { + "sudo" + } else { + "podman" + }, + if use_sudo => [ + "-A", + "podman", + ], + "rmi", + opts.image.to_string() + ); trace!("{c:?}"); c } @@ -480,21 +598,37 @@ impl RunDriver for PodmanDriver { .into_diagnostic()?; if !output.status.success() { - bail!("Failed to remove the image {image}"); + bail!("Failed to remove the image {}", opts.image); } Ok(()) } - fn list_images() -> Result> { + fn list_images(privileged: bool) -> Result> { #[derive(Deserialize)] #[serde(rename_all = "PascalCase")] struct Image { names: Option>, } + trace!("PodmanDriver::list_images({privileged})"); + + let use_sudo = privileged && !running_as_root(); let output = { - let c = cmd!("podman", "images", "--format", "json"); + let c = cmd!( + if use_sudo { + "sudo" + } else { + "podman" + }, + if use_sudo => [ + "-A", + "podman", + ], + "images", + "--format", + "json" + ); trace!("{c:?}"); c } @@ -520,7 +654,7 @@ impl RunDriver for PodmanDriver { } fn podman_run(opts: &RunOpts, cid_file: &Path) -> Command { - let use_sudo = opts.privileged && !nix::unistd::Uid::effective().is_root(); + let use_sudo = opts.privileged && !running_as_root(); let command = cmd!( if use_sudo { "sudo" diff --git a/process/drivers/traits.rs b/process/drivers/traits.rs index 6d7d7fb..dd7432c 100644 --- a/process/drivers/traits.rs +++ b/process/drivers/traits.rs @@ -22,9 +22,10 @@ use super::{ gitlab_driver::GitlabDriver, local_driver::LocalDriver, opts::{ - BuildOpts, BuildTagPushOpts, CheckKeyPairOpts, GenerateImageNameOpts, GenerateKeyPairOpts, - GenerateTagsOpts, GetMetadataOpts, PushOpts, RunOpts, SignOpts, SignVerifyOpts, TagOpts, - VerifyOpts, VerifyType, + BuildOpts, BuildTagPushOpts, CheckKeyPairOpts, ContainerOpts, CreateContainerOpts, + GenerateImageNameOpts, GenerateKeyPairOpts, GenerateTagsOpts, GetMetadataOpts, PushOpts, + RemoveContainerOpts, RemoveImageOpts, RunOpts, SignOpts, SignVerifyOpts, TagOpts, + VerifyOpts, VerifyType, VolumeOpts, }, podman_driver::PodmanDriver, skopeo_driver::SkopeoDriver, @@ -218,25 +219,25 @@ pub trait RunDriver: PrivateDriver { /// /// # Errors /// Will error if the container create command fails. - fn create_container(image: &Reference) -> Result; + fn create_container(opts: &CreateContainerOpts) -> Result; /// Removes a container /// /// # Errors /// Will error if the container remove command fails. - fn remove_container(container_id: &ContainerId) -> Result<()>; + fn remove_container(opts: &RemoveContainerOpts) -> Result<()>; /// Removes an image /// /// # Errors /// Will error if the image remove command fails. - fn remove_image(image: &Reference) -> Result<()>; + fn remove_image(opts: &RemoveImageOpts) -> Result<()>; /// List all images in the local image registry. /// /// # Errors /// Will error if the image list command fails. - fn list_images() -> Result>; + fn list_images(privileged: bool) -> Result>; } #[allow(private_bounds)] @@ -246,19 +247,19 @@ pub(super) trait ContainerMountDriver: PrivateDriver { /// /// # Errors /// Will error if the container mount command fails. - fn mount_container(container_id: &ContainerId) -> Result; + fn mount_container(opts: &ContainerOpts) -> Result; /// Unmount the container /// /// # Errors /// Will error if the container unmount command fails. - fn unmount_container(container_id: &ContainerId) -> Result<()>; + fn unmount_container(opts: &ContainerOpts) -> Result<()>; /// Remove a volume /// /// # Errors /// Will error if the volume remove command fails. - fn remove_volume(volume_id: &str) -> Result<()>; + fn remove_volume(opts: &VolumeOpts) -> Result<()>; } #[cfg(feature = "rechunk")] @@ -295,13 +296,24 @@ pub trait RechunkDriver: RunDriver + BuildDriver + ContainerMountDriver { .image(raw_image.to_string()) .containerfile(&*opts.containerfile) .platform(opts.platform) + .privileged(true) .squash(true) .host_network(true) .build(), )?; - let container = &Self::create_container(raw_image)?; - let mount = &Self::mount_container(container)?; + let container = &Self::create_container( + &CreateContainerOpts::builder() + .image(raw_image) + .privileged(true) + .build(), + )?; + let mount = &Self::mount_container( + &ContainerOpts::builder() + .container_id(container) + .privileged(true) + .build(), + )?; Self::prune_image(mount, container, raw_image, opts)?; Self::create_ostree_commit(mount, ostree_cache_id, container, raw_image, opts)?; @@ -366,9 +378,24 @@ pub trait RechunkDriver: RunDriver + BuildDriver + ContainerMountDriver { )?; if !status.success() { - Self::unmount_container(container)?; - Self::remove_container(container)?; - Self::remove_image(raw_image)?; + Self::unmount_container( + &ContainerOpts::builder() + .container_id(container) + .privileged(true) + .build(), + )?; + Self::remove_container( + &RemoveContainerOpts::builder() + .container_id(container) + .privileged(true) + .build(), + )?; + Self::remove_image( + &RemoveImageOpts::builder() + .image(raw_image) + .privileged(true) + .build(), + )?; bail!("Failed to run prune step for {}", &opts.image); } @@ -404,9 +431,24 @@ pub trait RechunkDriver: RunDriver + BuildDriver + ContainerMountDriver { .args(bon::vec!["/sources/rechunk/2_create.sh"]) .build(), )?; - Self::unmount_container(container)?; - Self::remove_container(container)?; - Self::remove_image(raw_image)?; + Self::unmount_container( + &ContainerOpts::builder() + .container_id(container) + .privileged(true) + .build(), + )?; + Self::remove_container( + &RemoveContainerOpts::builder() + .container_id(container) + .privileged(true) + .build(), + )?; + Self::remove_image( + &RemoveImageOpts::builder() + .image(raw_image) + .privileged(true) + .build(), + )?; if !status.success() { bail!("Failed to run Ostree create step for {}", &opts.image); @@ -460,7 +502,12 @@ pub trait RechunkDriver: RunDriver + BuildDriver + ContainerMountDriver { .build(), )?; - Self::remove_volume(ostree_cache_id)?; + Self::remove_volume( + &VolumeOpts::builder() + .volume_id(ostree_cache_id) + .privileged(true) + .build(), + )?; if !status.success() { bail!("Failed to run rechunking for {}", &opts.image); diff --git a/process/drivers/types.rs b/process/drivers/types.rs index 85f572d..3a193ad 100644 --- a/process/drivers/types.rs +++ b/process/drivers/types.rs @@ -244,6 +244,7 @@ impl ImageMetadata { } } +#[derive(Debug, Clone)] pub struct ContainerId(pub(super) String); impl std::fmt::Display for ContainerId { diff --git a/src/commands/build.rs b/src/commands/build.rs index 49c7a35..6efe40b 100644 --- a/src/commands/build.rs +++ b/src/commands/build.rs @@ -150,11 +150,6 @@ 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()); diff --git a/utils/Cargo.toml b/utils/Cargo.toml index acde38d..7ae6f88 100644 --- a/utils/Cargo.toml +++ b/utils/Cargo.toml @@ -20,11 +20,13 @@ lenient_semver = "0.4" process_control = { version = "4", features = ["crossbeam-channel"] } which = "7" +cached.workspace = true chrono.workspace = true clap = { workspace = true, features = ["derive", "env"] } comlexr.workspace = true log.workspace = true miette.workspace = true +nix = { workspace = true, features = ["user"] } semver = { workspace = true, features = ["serde"] } serde.workspace = true serde_json.workspace = true diff --git a/utils/src/lib.rs b/utils/src/lib.rs index 94b24f3..e8d0966 100644 --- a/utils/src/lib.rs +++ b/utils/src/lib.rs @@ -9,6 +9,7 @@ pub mod test_utils; pub mod traits; use std::{ + ops::Not, os::unix::ffi::OsStrExt, path::{Path, PathBuf}, thread, @@ -20,6 +21,7 @@ use blake2::{ digest::{Update, VariableOutput}, Blake2bVar, }; +use cached::proc_macro::once; use chrono::Local; use comlexr::cmd; use format_serde_error::SerdeError; @@ -129,3 +131,18 @@ pub fn get_env_var(key: &str) -> Result { .into_diagnostic() .with_context(|| format!("Failed to get {key}'")) } + +/// Checks if an environment variable is set and isn't empty. +#[must_use] +pub fn has_env_var(key: &str) -> bool { + get_env_var(key).is_ok_and(|v| v.is_empty().not()) +} + +/// Checks if the process is running as root. +/// +/// This call is cached to reduce syscalls. +#[once] +#[must_use] +pub fn running_as_root() -> bool { + nix::unistd::Uid::effective().is_root() +}