Skip to content

Commit acf767a

Browse files
committed
reinstall: Ensure podman is installed
Fixes #1104 Make podman dependency of system-reinstall-bootc optional * Change the spec file to recommend podman instead of requiring it (this will make it more palatable to have this package included in distros by default) * Now that it's only recommended, the system-reinstall-bootc binary must check whether podman is installed and prompt the user to install it if it's not. For now only dnf is supported. Signed-off-by: Omer Tuchfeld <[email protected]>
1 parent eddbb2d commit acf767a

File tree

4 files changed

+58
-4
lines changed

4 files changed

+58
-4
lines changed

contrib/packaging/bootc.spec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ Requires: composefs
5050
# For OS updates
5151
Requires: ostree
5252
Requires: skopeo
53-
Requires: podman
53+
Recommends: podman
5454
# For bootloader updates
5555
Recommends: bootupd
5656

system-reinstall-bootc/src/main.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ fn run() -> Result<()> {
1818
// Rootless podman is not supported by bootc
1919
ensure!(getuid().is_root(), "Must run as the root user");
2020

21+
podman::ensure_podman_installed()?;
22+
2123
let config = config::ReinstallConfig::load().context("loading config")?;
2224

2325
let mut reinstall_podman_command =

system-reinstall-bootc/src/podman.rs

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
use std::process::Command;
2-
31
use super::ROOT_KEY_MOUNT_POINT;
4-
use crate::users::UserKeys;
2+
use crate::{prompt, users::UserKeys};
3+
use anyhow::{ensure, Context, Result};
4+
use bootc_utils::CommandRunExt;
5+
use std::process::Command;
56

67
pub(crate) fn command(image: &str, root_key: &Option<UserKeys>) -> Command {
78
let mut podman_command_and_args = [
@@ -58,3 +59,42 @@ pub(crate) fn command(image: &str, root_key: &Option<UserKeys>) -> Command {
5859

5960
command
6061
}
62+
63+
pub(crate) fn ensure_podman_installed() -> Result<()> {
64+
if Command::new("podman").arg("--version").can_execute() {
65+
return Ok(());
66+
}
67+
68+
tracing::warn!("Podman was not found on this system. It's required to install a bootc image.");
69+
70+
ensure!(
71+
Command::new("dnf").arg("--version").can_execute(),
72+
"Please install podman and try again."
73+
);
74+
75+
let mut dnf_install_command = Command::new("dnf");
76+
dnf_install_command
77+
.arg("install")
78+
// TODO: Is this a bad idea? It answers yes to EVERYTHING. Maybe we should let the
79+
// user interact with dnf manually instead... I wish dnf gave us more granular
80+
// control over what we can automatically answer yes to.
81+
.arg("-y")
82+
.arg("podman");
83+
84+
if prompt::ask_yes_no(
85+
format!("Would you like to install podman using dnf? ({dnf_install_command:?})").as_str(),
86+
false,
87+
)? {
88+
dnf_install_command
89+
.run_with_cmd_context()
90+
.context("installing podman")?;
91+
92+
// Make sure the installation was actually successful
93+
ensure!(
94+
Command::new("podman").arg("--version").can_execute(),
95+
"podman still doesn't seem to be available, despite the installation. Please install it manually and try again."
96+
);
97+
}
98+
99+
Ok(())
100+
}

utils/src/command.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ pub trait CommandRunExt {
3333
/// Execute the child process, parsing its stdout as JSON. This uses `run` internally
3434
/// and will return an error if the child process exits abnormally.
3535
fn run_and_parse_json<T: serde::de::DeserializeOwned>(&mut self) -> Result<T>;
36+
37+
/// Check if the command can be executed by running it and ignoring its output. Returns `true`
38+
/// if the command exists, `false` otherwise. The exit code is ignored. Only run this with
39+
/// commands that have no undesired side effects, e.g. `dnf --version`.
40+
fn can_execute(&mut self) -> bool;
3641
}
3742

3843
/// Helpers intended for [`std::process::ExitStatus`].
@@ -146,6 +151,13 @@ impl CommandRunExt for Command {
146151
// representation that the user can copy paste into their shell
147152
.context("Failed to run command: {self:#?}")
148153
}
154+
155+
fn can_execute(&mut self) -> bool {
156+
self.stdout(std::process::Stdio::null())
157+
.stderr(std::process::Stdio::null())
158+
.status()
159+
.is_ok()
160+
}
149161
}
150162

151163
/// Helpers intended for [`tokio::process::Command`].

0 commit comments

Comments
 (0)