Skip to content

Commit

Permalink
WIP: Add support for --replace-mode=alongside for ostree target
Browse files Browse the repository at this point in the history
Ironically our support for `--replace-mode=alongside` breaks
when we're targeting an already extant ostree host, because when
we first blow away the `/boot` directory, this means the ostree
stack loses its knowledge that we're in a booted deployment,
and will attempt to GC it...

ostreedev/ostree-rs-ext@8fa019b
is a key part of the fix for that.

However, a notable improvement we can do here is to grow this
whole thing into a real "factory reset" mode, and this will
be a compelling answer to
coreos/fedora-coreos-tracker#399

To implement this though we need to support configuring the
stateroot and not just hardcode `default`.

Signed-off-by: Colin Walters <[email protected]>
  • Loading branch information
cgwalters committed Nov 5, 2024
1 parent d4f7f97 commit 77fd56a
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 10 deletions.
32 changes: 23 additions & 9 deletions lib/src/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -588,11 +588,17 @@ async fn initialize_ostree_root(state: &State, root_setup: &RootSetup) -> Result

let stateroot = state.stateroot();

Task::new_and_run(
"Initializing ostree layout",
"ostree",
["admin", "init-fs", "--modern", rootfs.as_str()],
)?;
let has_ostree = rootfs_dir.try_exists("ostree/repo")?;
if !has_ostree {
Task::new_and_run(
"Initializing ostree layout",
"ostree",
["admin", "init-fs", "--modern", rootfs.as_str()],
)?;
} else {
println!("Reusing extant ostree layout");
let _ = crate::utils::open_dir_remount_rw(rootfs_dir, "sysroot".into())?;
}

// And also label /boot AKA xbootldr, if it exists
let bootdir = rootfs.join("boot");
Expand All @@ -617,9 +623,15 @@ async fn initialize_ostree_root(state: &State, root_setup: &RootSetup) -> Result
let sysroot = ostree::Sysroot::new(Some(&gio::File::for_path(rootfs)));
sysroot.load(cancellable)?;

sysroot
.init_osname(stateroot, cancellable)
.context("initializing stateroot")?;
let stateroot_exists = rootfs_dir.try_exists(format!("ostree/deploy/{stateroot}"))?;
if stateroot_exists {
anyhow::bail!("Cannot redeploy over extant stateroot {stateroot}");
} else {
Task::new("Initializing stateroot", "ostree")
.args(["admin", "os-init", stateroot, "--sysroot", "."])
.cwd(rootfs_dir)?
.run()?;
}

let sysroot_dir = Dir::reopen_dir(&crate::utils::sysroot_fd(&sysroot))?;

Expand Down Expand Up @@ -740,6 +752,7 @@ async fn install_container(
options.kargs = Some(kargs.as_slice());
options.target_imgref = Some(&state.target_imgref);
options.proxy_cfg = proxy_cfg;
options.no_clean = root_setup.has_ostree;
let imgstate = crate::utils::async_task_with_spinner(
"Deploying container image",
ostree_container::deploy::deploy(&sysroot, stateroot, &src_imageref, Some(options)),
Expand Down Expand Up @@ -1511,7 +1524,8 @@ fn remove_all_in_dir_no_xdev(d: &Dir) -> Result<()> {

#[context("Removing boot directory content")]
fn clean_boot_directories(rootfs: &Dir) -> Result<()> {
let bootdir = rootfs.open_dir(BOOT).context("Opening /boot")?;
let bootdir =
crate::utils::open_dir_remount_rw(rootfs, BOOT.into()).context("Opening /boot")?;
// This should not remove /boot/efi note.
remove_all_in_dir_no_xdev(&bootdir)?;
if ARCH_USES_EFI {
Expand Down
28 changes: 27 additions & 1 deletion lib/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ use std::process::Command;
use std::time::Duration;

use anyhow::{Context, Result};
use cap_std_ext::cap_std::fs::Dir;
use camino::Utf8Path;
use cap_std_ext::{cap_std::fs::Dir, prelude::CapStdExtCommandExt};
use fn_error_context::context;
use indicatif::HumanDuration;
use libsystemd::logging::journal_print;
use ostree::glib;
Expand Down Expand Up @@ -57,6 +59,30 @@ pub(crate) fn find_mount_option<'a>(
.next()
}

/// Given a target directory, if it's a read-only mount, then remount it writable
#[context("Opening {target} with writable mount")]
pub(crate) fn open_dir_remount_rw(root: &Dir, target: &Utf8Path) -> Result<Dir> {
if ostree_ext::mountutil::is_mountpoint(root, target)?.unwrap_or_default() {
tracing::debug!("Target {target} is a mountpoint, remounting rw");
let st = Command::new("mount")
.args(["-o", "remount,rw", target.as_str()])
.cwd_dir(root.try_clone()?)
.status()?;
if !st.success() {
anyhow::bail!("Failed to remount: {st:?}");
}
}
root.open_dir(target).map_err(anyhow::Error::new)
}

/// Run a command in the host mount namespace
#[allow(dead_code)]
pub(crate) fn run_in_host_mountns(cmd: &str) -> Command {
let mut c = Command::new("nsenter");
c.args(["-m", "-t", "1", "--", cmd]);
c
}

pub(crate) fn spawn_editor(tmpf: &tempfile::NamedTempFile) -> Result<()> {
let editor_variables = ["EDITOR"];
// These roughly match https://github.com/systemd/systemd/blob/769ca9ab557b19ee9fb5c5106995506cace4c68f/src/shared/edit-util.c#L275
Expand Down

0 comments on commit 77fd56a

Please sign in to comment.