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

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!(
"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<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 {