Skip to content

Commit 73d8d28

Browse files
committed
utils: add BOOTC_PODMAN_BIN and BOOTC_SKOPEO_BIN env overrides
Add two environment variables that allow callers to substitute an alternative binary for podman and skopeo without creating hard links or symlinks on the filesystem. BOOTC_PODMAN_BIN and BOOTC_SKOPEO_BIN default to "podman" and "skopeo" respectively, preserving existing behaviour when unset. Helper functions podman_bin() and skopeo_bin() are added to bootc-internal-utils and used at every call site across crates/lib and crates/ostree-ext. This unblocks downstream projects that ship a single alternative binary (e.g. dtool) in place of both tools by setting the env vars rather than hard-linking that binary into /usr/bin. Assisted-by: OpenCode (claude-sonnet-4-6) Signed-off-by: Eric Curtin <eric.curtin@docker.com>
1 parent eedfdf1 commit 73d8d28

9 files changed

Lines changed: 28 additions & 11 deletions

File tree

crates/lib/src/cli.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1853,7 +1853,7 @@ async fn run_from_opt(opt: Opt) -> Result<()> {
18531853
);
18541854
}
18551855
// And ensure we're finding the image in the host storage
1856-
let mut cmd = Command::new("skopeo");
1856+
let mut cmd = Command::new(bootc_utils::skopeo_bin());
18571857
set_additional_image_store(&mut cmd, "/run/host-container-storage");
18581858
proxycfg.skopeo_cmd = Some(cmd);
18591859
iid

crates/lib/src/deploy.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use std::process::Command;
99

1010
use anyhow::{Context, Result, anyhow};
1111
use bootc_kernel_cmdline::utf8::CmdlineOwned;
12+
use bootc_utils::skopeo_bin;
1213
use cap_std::fs::{Dir, MetadataExt};
1314
use cap_std_ext::cap_std;
1415
use cap_std_ext::dirext::CapStdExtDirExt;
@@ -558,7 +559,7 @@ pub(crate) async fn prepare_for_pull_unified(
558559

559560
// Configure the importer to use bootc storage as an additional image store
560561
let mut config = new_proxy_config();
561-
let mut cmd = Command::new("skopeo");
562+
let mut cmd = Command::new(skopeo_bin());
562563
// Use the physical path to bootc storage from the Storage struct
563564
let storage_path = format!(
564565
"{}/{}",

crates/lib/src/image.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ pub(crate) const IMAGE_DEFAULT: &str = "localhost/bootc";
3030
/// get structured responses.
3131
async fn image_exists_in_host_storage(image: &str) -> Result<bool> {
3232
use tokio::process::Command as AsyncCommand;
33-
let mut cmd = AsyncCommand::new("podman");
33+
let mut cmd = AsyncCommand::new(bootc_utils::podman_bin());
3434
cmd.args(["image", "exists", image]);
3535
Ok(cmd.status().await?.success())
3636
}

crates/lib/src/podman.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ pub(crate) struct ImageListEntry {
2424
/// Given an image ID, return its manifest digest
2525
pub(crate) fn imageid_to_digest(imgid: &str) -> Result<String> {
2626
use bootc_utils::CommandRunExt;
27-
let o: Vec<Inspect> = crate::install::run_in_host_mountns("podman")?
27+
let o: Vec<Inspect> = crate::install::run_in_host_mountns(bootc_utils::podman_bin())?
2828
.args(["inspect", imgid])
2929
.run_and_parse_json()?;
3030
let i = o

crates/lib/src/podman_client.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ impl PodmanClient {
100100
std::fs::create_dir_all("/run/bootc/").ok();
101101
let _ = std::fs::remove_file(&socket_path);
102102

103-
let mut cmd = std::process::Command::new("podman");
103+
let mut cmd = std::process::Command::new(bootc_utils::podman_bin());
104104
let mut fds = CmdFds::new();
105105
crate::podstorage::bind_storage_roots(&mut cmd, &mut fds, storage_root, run_root)?;
106106
crate::podstorage::setup_auth(&mut cmd, &mut fds, sysroot)?;
@@ -252,7 +252,7 @@ impl PodmanClient {
252252
tracing::debug!(
253253
"Image uses non-docker transport, falling back to podman pull subprocess: {image}"
254254
);
255-
let mut cmd = Command::new("podman");
255+
let mut cmd = Command::new(bootc_utils::podman_bin());
256256
let mut fds = CmdFds::new();
257257
crate::podstorage::bind_storage_roots(
258258
&mut cmd,

crates/lib/src/podstorage.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ pub(crate) fn setup_auth(cmd: &mut Command, fds: &mut CmdFds, sysroot: &Dir) ->
167167
// - storage overridden to point to to storage_root
168168
// - Authentication (auth.json) using the bootc/ostree owned auth
169169
fn new_podman_cmd_in(sysroot: &Dir, storage_root: &Dir, run_root: &Dir) -> Result<Command> {
170-
let mut cmd = Command::new("podman");
170+
let mut cmd = Command::new(bootc_utils::podman_bin());
171171
let mut fds = CmdFds::new();
172172
bind_storage_roots(&mut cmd, &mut fds, storage_root, run_root)?;
173173
let run_root = format!("/proc/self/fd/{STORAGE_RUN_FD}");
@@ -201,7 +201,7 @@ pub fn set_additional_image_store<'c>(
201201
///
202202
/// Call this function any time we're going to write to containers-storage.
203203
pub(crate) fn ensure_floating_c_storage_initialized() {
204-
if let Err(e) = Command::new("podman")
204+
if let Err(e) = Command::new(bootc_utils::podman_bin())
205205
.args(["system", "info"])
206206
.stdout(Stdio::null())
207207
.run_capture_stderr()
@@ -447,7 +447,7 @@ impl CStorage {
447447
/// to this storage.
448448
#[context("Pulling from host storage: {image}")]
449449
pub(crate) async fn pull_from_host_storage(&self, image: &str) -> Result<()> {
450-
let mut cmd = Command::new("podman");
450+
let mut cmd = Command::new(bootc_utils::podman_bin());
451451
cmd.stdin(Stdio::null());
452452
cmd.stdout(Stdio::null());
453453
// An ephemeral place for the transient state;

crates/ostree-ext/src/container/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -588,7 +588,7 @@ pub fn merge_default_container_proxy_opts_with_isolation(
588588
if let Some(authfile) = config.authfile.take() {
589589
config.auth_data = Some(std::fs::File::open(authfile)?);
590590
}
591-
let cmd = crate::isolation::unprivileged_subprocess("skopeo", user);
591+
let cmd = crate::isolation::unprivileged_subprocess(bootc_utils::skopeo_bin(), user);
592592
config.skopeo_cmd = Some(cmd);
593593
}
594594
Ok(())

crates/ostree-ext/src/container/skopeo.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ pub(crate) fn container_policy_is_default_insecure() -> Result<bool> {
5151

5252
/// Create a Command builder for skopeo.
5353
pub(crate) fn new_cmd() -> std::process::Command {
54-
let mut cmd = std::process::Command::new("skopeo");
54+
let mut cmd = std::process::Command::new(bootc_utils::skopeo_bin());
5555
cmd.stdin(Stdio::null());
5656
cmd
5757
}

crates/utils/src/lib.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,22 @@ pub use tracing_util::*;
2222
/// The name of our binary
2323
pub const NAME: &str = "bootc";
2424

25+
/// Return the podman binary path, honouring the `BOOTC_PODMAN_BIN` environment
26+
/// variable so callers can substitute an alternative tool (e.g. `dtool`)
27+
/// without hard-linking it as `/usr/bin/podman`.
28+
pub fn podman_bin() -> &'static str {
29+
static BIN: std::sync::OnceLock<String> = std::sync::OnceLock::new();
30+
BIN.get_or_init(|| std::env::var("BOOTC_PODMAN_BIN").unwrap_or_else(|_| "podman".to_string()))
31+
}
32+
33+
/// Return the skopeo binary path, honouring the `BOOTC_SKOPEO_BIN` environment
34+
/// variable so callers can substitute an alternative tool (e.g. `dtool`)
35+
/// without hard-linking it as `/usr/bin/skopeo`.
36+
pub fn skopeo_bin() -> &'static str {
37+
static BIN: std::sync::OnceLock<String> = std::sync::OnceLock::new();
38+
BIN.get_or_init(|| std::env::var("BOOTC_SKOPEO_BIN").unwrap_or_else(|_| "skopeo".to_string()))
39+
}
40+
2541
/// Intended for use in `main`, calls an inner function and
2642
/// handles errors by printing them.
2743
pub fn run_main<F>(f: F)

0 commit comments

Comments
 (0)