Propogate use_sudo pattern to all traits that podman can be used in

This commit is contained in:
Gerald Pinder 2025-03-20 23:29:39 -04:00
parent 430d18de8a
commit 883090ee85
14 changed files with 346 additions and 116 deletions

2
Cargo.lock generated
View file

@ -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",

View file

@ -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 \

View file

@ -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

View file

@ -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<u64> {
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<u64> {
)?;
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<types::ContainerId> {
impl_run_driver!(create_container(image))
fn create_container(opts: &CreateContainerOpts) -> Result<types::ContainerId> {
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<Vec<Reference>> {
impl_run_driver!(list_images())
fn list_images(privileged: bool) -> Result<Vec<Reference>> {
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<types::MountId> {
PodmanDriver::mount_container(container_id)
fn mount_container(opts: &ContainerOpts) -> Result<types::MountId> {
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<Vec<String>> {
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");
}
}

View file

@ -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<super::types::ContainerId> {
trace!("DockerDriver::create_container({image})");
fn create_container(opts: &CreateContainerOpts) -> Result<super::types::ContainerId> {
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<Vec<Reference>> {
fn list_images(_privileged: bool) -> Result<Vec<Reference>> {
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
struct Image {

View file

@ -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<CompressionType>,
#[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,
}

View file

@ -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,
}

View file

@ -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,
}

View file

@ -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<Vec<PodmanImageMetadata>> 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!(
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!(
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<ImageMetadata> {
#[cfg(feature = "rechunk")]
impl ContainerMountDriver for PodmanDriver {
fn mount_container(container_id: &super::types::ContainerId) -> Result<MountId> {
fn mount_container(opts: &ContainerOpts) -> Result<MountId> {
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<ContainerId> {
fn create_container(opts: &CreateContainerOpts) -> Result<ContainerId> {
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<Vec<Reference>> {
fn list_images(privileged: bool) -> Result<Vec<Reference>> {
#[derive(Deserialize)]
#[serde(rename_all = "PascalCase")]
struct Image {
names: Option<Vec<String>>,
}
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"

View file

@ -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<ContainerId>;
fn create_container(opts: &CreateContainerOpts) -> Result<ContainerId>;
/// 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<Vec<Reference>>;
fn list_images(privileged: bool) -> Result<Vec<Reference>>;
}
#[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<MountId>;
fn mount_container(opts: &ContainerOpts) -> Result<MountId>;
/// 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);

View file

@ -244,6 +244,7 @@ impl ImageMetadata {
}
}
#[derive(Debug, Clone)]
pub struct ContainerId(pub(super) String);
impl std::fmt::Display for ContainerId {

View file

@ -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());

View file

@ -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

View file

@ -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<String> {
.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()
}