mirror of
https://github.com/containers/bootc.git
synced 2026-02-05 06:45:13 +01:00
system-reinstall-bootc: Add context annotations to Result-returning functions
Add #[context()] attribute macro to all functions that return Result to improve error reporting. This includes adding the fn-error-context dependency and importing the context macro in all relevant modules. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: John Eckersberg <jeckersb@redhat.com>
This commit is contained in:
committed by
Colin Walters
parent
4d56384290
commit
82ff6770f2
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -2574,6 +2574,7 @@ dependencies = [
|
||||
"clap",
|
||||
"crossterm 0.29.0",
|
||||
"dialoguer",
|
||||
"fn-error-context",
|
||||
"indoc",
|
||||
"log",
|
||||
"openssh-keys",
|
||||
|
||||
@@ -22,6 +22,7 @@ bootc-utils = { package = "bootc-internal-utils", path = "../utils", version = "
|
||||
anstream = { workspace = true }
|
||||
anyhow = { workspace = true }
|
||||
clap = { workspace = true, features = ["derive"] }
|
||||
fn-error-context = { workspace = true }
|
||||
indoc = { workspace = true }
|
||||
log = { workspace = true }
|
||||
rustix = { workspace = true }
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use anyhow::Result;
|
||||
use bootc_mount::Filesystem;
|
||||
use fn_error_context::context;
|
||||
|
||||
#[context("check_root_siblings")]
|
||||
pub(crate) fn check_root_siblings() -> Result<Vec<String>> {
|
||||
let mounts = bootc_mount::run_findmnt(&[], None)?;
|
||||
let problem_filesystems: Vec<String> = mounts
|
||||
|
||||
@@ -2,6 +2,7 @@ use std::{fs::File, io::BufReader};
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use bootc_utils::PathQuotedDisplay;
|
||||
use fn_error_context::context;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
mod cli;
|
||||
@@ -17,6 +18,7 @@ pub(crate) struct ReinstallConfig {
|
||||
}
|
||||
|
||||
impl ReinstallConfig {
|
||||
#[context("load")]
|
||||
pub fn load() -> Result<Option<Self>> {
|
||||
let Some(config) = std::env::var_os(CONFIG_VAR) else {
|
||||
return Ok(None);
|
||||
|
||||
@@ -3,6 +3,7 @@ use std::process::Command;
|
||||
use anyhow::Result;
|
||||
use bootc_mount::run_findmnt;
|
||||
use bootc_utils::{CommandRunExt, ResultExt};
|
||||
use fn_error_context::context;
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
@@ -23,6 +24,7 @@ pub(crate) struct LogicalVolume {
|
||||
vg_name: String,
|
||||
}
|
||||
|
||||
#[context("parse_volumes")]
|
||||
pub(crate) fn parse_volumes(group: Option<&str>) -> Result<Vec<LogicalVolume>> {
|
||||
if which::which("podman").is_err() {
|
||||
tracing::debug!("lvs binary not found. Skipping logical volume check.");
|
||||
@@ -46,6 +48,7 @@ pub(crate) fn parse_volumes(group: Option<&str>) -> Result<Vec<LogicalVolume>> {
|
||||
.collect())
|
||||
}
|
||||
|
||||
#[context("check_root_siblings")]
|
||||
pub(crate) fn check_root_siblings() -> Result<Vec<String>> {
|
||||
let all_volumes = parse_volumes(None)?;
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
use anyhow::{ensure, Context, Result};
|
||||
use bootc_utils::CommandRunExt;
|
||||
use clap::Parser;
|
||||
use fn_error_context::context;
|
||||
use rustix::process::getuid;
|
||||
|
||||
mod btrfs;
|
||||
@@ -29,6 +30,7 @@ struct Opts {
|
||||
// Note if we ever add any other options here,
|
||||
}
|
||||
|
||||
#[context("run")]
|
||||
fn run() -> Result<()> {
|
||||
// We historically supported an environment variable providing a config to override the image, so
|
||||
// keep supporting that. I'm considering deprecating that though.
|
||||
|
||||
@@ -3,9 +3,11 @@ use crate::prompt;
|
||||
use super::ROOT_KEY_MOUNT_POINT;
|
||||
use anyhow::{ensure, Context, Result};
|
||||
use bootc_utils::CommandRunExt;
|
||||
use fn_error_context::context;
|
||||
use std::process::Command;
|
||||
use which::which;
|
||||
|
||||
#[context("bootc_has_clean")]
|
||||
fn bootc_has_clean(image: &str) -> Result<bool> {
|
||||
let output = Command::new("podman")
|
||||
.args([
|
||||
@@ -22,6 +24,7 @@ fn bootc_has_clean(image: &str) -> Result<bool> {
|
||||
Ok(stdout_str.contains("--cleanup"))
|
||||
}
|
||||
|
||||
#[context("reinstall_command")]
|
||||
pub(crate) fn reinstall_command(image: &str, ssh_key_file: &str) -> Result<Command> {
|
||||
let mut podman_command_and_args = [
|
||||
// We use podman to run the bootc container. This might change in the future to remove the
|
||||
@@ -108,6 +111,7 @@ fn image_exists_command(image: &str) -> Command {
|
||||
command
|
||||
}
|
||||
|
||||
#[context("pull_if_not_present")]
|
||||
pub(crate) fn pull_if_not_present(image: &str) -> Result<()> {
|
||||
let result = image_exists_command(image).status()?;
|
||||
|
||||
@@ -136,6 +140,7 @@ const fn podman_install_script_path() -> &'static str {
|
||||
}
|
||||
}
|
||||
|
||||
#[context("ensure_podman_installed")]
|
||||
pub(crate) fn ensure_podman_installed() -> Result<()> {
|
||||
if which("podman").is_ok() {
|
||||
return Ok(());
|
||||
|
||||
@@ -11,6 +11,7 @@ macro_rules! println_flush {
|
||||
|
||||
use crate::{btrfs, lvm, prompt, users::get_all_users_keys};
|
||||
use anyhow::{ensure, Context, Result};
|
||||
use fn_error_context::context;
|
||||
|
||||
use crossterm::event::{self, Event};
|
||||
use std::time::Duration;
|
||||
@@ -19,6 +20,7 @@ const NO_SSH_PROMPT: &str = "None of the users on this system found have authori
|
||||
if your image doesn't use cloud-init or other means to set up users, \
|
||||
you may not be able to log in after reinstalling. Do you want to continue?";
|
||||
|
||||
#[context("prompt_single_user")]
|
||||
fn prompt_single_user(user: &crate::users::UserKeys) -> Result<Vec<&crate::users::UserKeys>> {
|
||||
let prompt = indoc::formatdoc! {
|
||||
"Found only one user ({user}) with {num_keys} SSH authorized keys.
|
||||
@@ -32,6 +34,7 @@ fn prompt_single_user(user: &crate::users::UserKeys) -> Result<Vec<&crate::users
|
||||
Ok(if answer { vec![&user] } else { vec![] })
|
||||
}
|
||||
|
||||
#[context("prompt_user_selection")]
|
||||
fn prompt_user_selection(
|
||||
all_users: &[crate::users::UserKeys],
|
||||
) -> Result<Vec<&crate::users::UserKeys>> {
|
||||
@@ -55,6 +58,7 @@ fn prompt_user_selection(
|
||||
.collect())
|
||||
}
|
||||
|
||||
#[context("reboot")]
|
||||
pub(crate) fn reboot() -> Result<()> {
|
||||
let delay_seconds = 10;
|
||||
println_flush!(
|
||||
@@ -79,6 +83,7 @@ pub(crate) fn reboot() -> Result<()> {
|
||||
|
||||
/// Temporary safety mechanism to stop devs from running it on their dev machine. TODO: Discuss
|
||||
/// final prompting UX in https://github.com/bootc-dev/bootc/discussions/1060
|
||||
#[context("temporary_developer_protection_prompt")]
|
||||
pub(crate) fn temporary_developer_protection_prompt() -> Result<()> {
|
||||
// Print an empty line so that the warning stands out from the rest of the output
|
||||
println_flush!();
|
||||
@@ -94,6 +99,7 @@ pub(crate) fn temporary_developer_protection_prompt() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[context("ask_yes_no")]
|
||||
pub(crate) fn ask_yes_no(prompt: &str, default: bool) -> Result<bool> {
|
||||
dialoguer::Confirm::new()
|
||||
.with_prompt(prompt)
|
||||
@@ -114,6 +120,7 @@ pub(crate) fn press_enter() {
|
||||
}
|
||||
}
|
||||
|
||||
#[context("mount_warning")]
|
||||
pub(crate) fn mount_warning() -> Result<()> {
|
||||
let mut mounts = btrfs::check_root_siblings()?;
|
||||
mounts.extend(lvm::check_root_siblings()?);
|
||||
@@ -138,6 +145,7 @@ pub(crate) fn mount_warning() -> Result<()> {
|
||||
/// The keys are stored in a temporary file which is passed to
|
||||
/// the podman run invocation to be used by
|
||||
/// `bootc install to-existing-root --root-ssh-authorized-keys`
|
||||
#[context("get_ssh_keys")]
|
||||
pub(crate) fn get_ssh_keys(temp_key_file_path: &str) -> Result<()> {
|
||||
let users = get_all_users_keys()?;
|
||||
if users.is_empty() {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use anyhow::{Context, Result};
|
||||
use bootc_utils::CommandRunExt;
|
||||
use bootc_utils::PathQuotedDisplay;
|
||||
use fn_error_context::context;
|
||||
use openssh_keys::PublicKey;
|
||||
use rustix::fs::Uid;
|
||||
use rustix::process::geteuid;
|
||||
@@ -17,6 +18,7 @@ use std::os::unix::process::CommandExt;
|
||||
use std::process::Command;
|
||||
use uzers::os::unix::UserExt;
|
||||
|
||||
#[context("loginctl_users")]
|
||||
fn loginctl_users() -> Result<BTreeSet<String>> {
|
||||
let loginctl_raw_output = loginctl_run_compat()?;
|
||||
|
||||
@@ -24,6 +26,7 @@ fn loginctl_users() -> Result<BTreeSet<String>> {
|
||||
}
|
||||
|
||||
/// See [`test::test_parse_lsblk`] for example loginctl output
|
||||
#[context("loginctl_parse")]
|
||||
fn loginctl_parse(users: Value) -> Result<BTreeSet<String>> {
|
||||
users
|
||||
.as_array()
|
||||
@@ -47,6 +50,7 @@ fn loginctl_parse(users: Value) -> Result<BTreeSet<String>> {
|
||||
}
|
||||
|
||||
/// Run `loginctl` with some compatibility maneuvers to get JSON output
|
||||
#[context("loginctl_run_compat")]
|
||||
fn loginctl_run_compat() -> Result<Value> {
|
||||
let mut command = Command::new("loginctl");
|
||||
command.arg("list-sessions").arg("--output").arg("json");
|
||||
@@ -71,6 +75,7 @@ struct UidChange {
|
||||
}
|
||||
|
||||
impl UidChange {
|
||||
#[context("new")]
|
||||
fn new(change_to_uid: Uid) -> Result<Self> {
|
||||
let (uid, euid) = (getuid(), geteuid());
|
||||
set_thread_res_uid(uid, change_to_uid, euid).context("setting effective uid failed")?;
|
||||
@@ -115,6 +120,7 @@ struct SshdConfig<'a> {
|
||||
}
|
||||
|
||||
impl<'a> SshdConfig<'a> {
|
||||
#[context("parse")]
|
||||
pub fn parse(sshd_output: &'a str) -> Result<SshdConfig<'a>> {
|
||||
let config = sshd_output
|
||||
.lines()
|
||||
@@ -138,6 +144,7 @@ impl<'a> SshdConfig<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[context("get_keys_from_files")]
|
||||
fn get_keys_from_files(user: &uzers::User, keyfiles: &Vec<&str>) -> Result<Vec<PublicKey>> {
|
||||
let home_dir = user.home_dir();
|
||||
let mut user_authorized_keys: Vec<PublicKey> = Vec::new();
|
||||
@@ -170,6 +177,7 @@ fn get_keys_from_files(user: &uzers::User, keyfiles: &Vec<&str>) -> Result<Vec<P
|
||||
Ok(user_authorized_keys)
|
||||
}
|
||||
|
||||
#[context("get_keys_from_command")]
|
||||
fn get_keys_from_command(command: &str, command_user: &str) -> Result<Vec<PublicKey>> {
|
||||
let user_config = uzers::get_user_by_name(command_user).context(format!(
|
||||
"authorized_keys_command_user {command_user} not found"
|
||||
@@ -184,6 +192,7 @@ fn get_keys_from_command(command: &str, command_user: &str) -> Result<Vec<Public
|
||||
Ok(keys)
|
||||
}
|
||||
|
||||
#[context("get_all_users_keys")]
|
||||
pub(crate) fn get_all_users_keys() -> Result<Vec<UserKeys>> {
|
||||
let loginctl_user_names = loginctl_users().context("enumerate users")?;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user