Skip to content

Commit e5104dd

Browse files
committed
migrate: separate ostree command to functions
Add `fn get_ostree_bootloader()` to get `sysroot.bootloader` in ostree repo config. Add `fn set_ostree_bootloader(bootloader: &str)` to set `sysroot.bootloader` value in ostree repo config.
1 parent d958938 commit e5104dd

File tree

2 files changed

+148
-65
lines changed

2 files changed

+148
-65
lines changed

src/bootupd.rs

Lines changed: 108 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use crate::coreos;
1010
))]
1111
use crate::efi;
1212
use crate::model::{ComponentStatus, ComponentUpdatable, ContentMetadata, SavedState, Status};
13-
use crate::util;
13+
use crate::{ostreeutil, util};
1414
use anyhow::{anyhow, Context, Result};
1515
use camino::{Utf8Path, Utf8PathBuf};
1616
use clap::crate_version;
@@ -561,23 +561,8 @@ pub(crate) fn client_run_validate() -> Result<()> {
561561
#[context("Migrating to a static GRUB config")]
562562
pub(crate) fn client_run_migrate_static_grub_config() -> Result<()> {
563563
// Did we already complete the migration?
564-
let mut cmd = std::process::Command::new("ostree");
565-
let result = cmd
566-
.args([
567-
"config",
568-
"--repo=/sysroot/ostree/repo",
569-
"get",
570-
"sysroot.bootloader",
571-
])
572-
.output()
573-
.context("Querying ostree sysroot.bootloader")?;
574-
if !result.status.success() {
575-
// ostree will exit with a non zero return code if the key does not exists
576-
println!("ostree repo 'sysroot.bootloader' config option is not set yet");
577-
} else {
578-
let res = String::from_utf8(result.stdout)
579-
.with_context(|| "decoding as UTF-8 output of ostree command")?;
580-
let bootloader = res.trim_end();
564+
// We need to migrate if bootloader is not none (or not set)
565+
if let Some(bootloader) = ostreeutil::get_ostree_bootloader()? {
581566
if bootloader == "none" {
582567
println!("Already using a static GRUB config");
583568
return Ok(());
@@ -586,6 +571,8 @@ pub(crate) fn client_run_migrate_static_grub_config() -> Result<()> {
586571
"ostree repo 'sysroot.bootloader' config option is currently set to: '{}'",
587572
bootloader
588573
);
574+
} else {
575+
println!("ostree repo 'sysroot.bootloader' config option is not set yet");
589576
}
590577

591578
// Remount /boot read write just for this unit (we are called in a slave mount namespace by systemd)
@@ -631,47 +618,19 @@ pub(crate) fn client_run_migrate_static_grub_config() -> Result<()> {
631618
// Read the current config, strip the ostree generated GRUB entries and
632619
// write the result to a temporary file
633620
println!("Stripping ostree generated entries from GRUB config...");
621+
let stripped_config = "grub.cfg.stripped";
634622
let current_config_file =
635623
File::open(current_config).context("Could not open current GRUB config")?;
636-
let stripped_config = String::from("grub.cfg.stripped");
637-
// mode = -rw-r--r-- (644)
638-
let mut writer = BufWriter::new(
639-
dirfd
640-
.write_file(
641-
&stripped_config,
642-
(S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) as mode_t,
643-
)
644-
.context("Failed to open temporary GRUB config")?,
645-
);
646-
let mut skip = false;
647-
for line in BufReader::new(current_config_file).lines() {
648-
let line = line.context("Failed to read line from GRUB config")?;
649-
if line == "### END /etc/grub.d/15_ostree ###" {
650-
skip = false;
651-
}
652-
if skip {
653-
continue;
654-
}
655-
if line == "### BEGIN /etc/grub.d/15_ostree ###" {
656-
skip = true;
657-
}
658-
writer
659-
.write_all(&line.as_bytes())
660-
.context("Failed to write stripped GRUB config")?;
661-
writer
662-
.write_all(b"\n")
663-
.context("Failed to write stripped GRUB config")?;
664-
}
665-
writer
666-
.flush()
667-
.context("Failed to write stripped GRUB config")?;
624+
let content = BufReader::new(current_config_file);
625+
626+
strip_grub_config_file(content, &dirfd, stripped_config)?;
668627

669628
// Sync changes to the filesystem (ignore failures)
670629
let _ = dirfd.syncfs();
671630

672631
// Atomically exchange the configs
673632
dirfd
674-
.local_exchange(&stripped_config, "grub.cfg")
633+
.local_exchange(stripped_config, "grub.cfg")
675634
.context("Failed to exchange symlink with current GRUB config")?;
676635

677636
// Sync changes to the filesystem (ignore failures)
@@ -680,28 +639,63 @@ pub(crate) fn client_run_migrate_static_grub_config() -> Result<()> {
680639
println!("GRUB config symlink successfully replaced with the current config");
681640

682641
// Remove the now unused symlink (optional cleanup, ignore any failures)
683-
_ = dirfd.remove_file(&stripped_config);
642+
_ = dirfd.remove_file(stripped_config);
684643
}
685644
};
686645

687646
println!("Setting 'sysroot.bootloader' to 'none' in ostree repo config...");
688-
let status = std::process::Command::new("ostree")
689-
.args([
690-
"config",
691-
"--repo=/sysroot/ostree/repo",
692-
"set",
693-
"sysroot.bootloader",
694-
"none",
695-
])
696-
.status()?;
697-
if !status.success() {
698-
anyhow::bail!("Failed to set 'sysroot.bootloader' to 'none' in ostree repo config");
699-
}
647+
ostreeutil::set_ostree_bootloader("none")?;
700648

701649
println!("Static GRUB config migration completed successfully");
702650
Ok(())
703651
}
704652

653+
/// Writes a stripped GRUB config to `stripped_config_name`, removing lines between
654+
/// `### BEGIN /etc/grub.d/15_ostree ###` and `### END /etc/grub.d/15_ostree ###`.
655+
fn strip_grub_config_file(
656+
current_config_content: impl BufRead,
657+
dirfd: &openat::Dir,
658+
stripped_config_name: &str,
659+
) -> Result<()> {
660+
// mode = -rw-r--r-- (644)
661+
let mut writer = BufWriter::new(
662+
dirfd
663+
.write_file(
664+
stripped_config_name,
665+
(S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) as mode_t,
666+
)
667+
.context("Failed to open temporary GRUB config")?,
668+
);
669+
670+
let mut skip = false;
671+
for line in current_config_content.lines() {
672+
let line = line.context("Failed to read line from GRUB config")?;
673+
if line == "### END /etc/grub.d/15_ostree ###" {
674+
skip = false;
675+
continue;
676+
}
677+
if skip {
678+
continue;
679+
}
680+
if line == "### BEGIN /etc/grub.d/15_ostree ###" {
681+
skip = true;
682+
continue;
683+
}
684+
writer
685+
.write_all(line.as_bytes())
686+
.context("Failed to write stripped GRUB config")?;
687+
writer
688+
.write_all(b"\n")
689+
.context("Failed to write stripped GRUB config")?;
690+
}
691+
692+
writer
693+
.flush()
694+
.context("Failed to write stripped GRUB config")?;
695+
696+
Ok(())
697+
}
698+
705699
#[cfg(test)]
706700
mod tests {
707701
use super::*;
@@ -714,4 +708,54 @@ mod tests {
714708
assert_eq!(r.is_err(), true);
715709
guard.teardown();
716710
}
711+
712+
#[test]
713+
fn test_strip_grub_config_file() -> Result<()> {
714+
let root: &tempfile::TempDir = &tempfile::tempdir()?;
715+
let root_path = root.path();
716+
let rootd = openat::Dir::open(root_path)?;
717+
let stripped_config = root_path.join("stripped");
718+
let content = r"
719+
### BEGIN /etc/grub.d/10_linux ###
720+
721+
### END /etc/grub.d/10_linux ###
722+
723+
### BEGIN /etc/grub.d/15_ostree ###
724+
menuentry 'Red Hat Enterprise Linux CoreOS 4 (ostree)' --class gnu-linux --class gnu --class os --unrestricted 'ostree-0-a92522f9-74dc-456a-ae0c-05ba22bca976' {
725+
load_video
726+
set gfxpayload=keep
727+
insmod gzio
728+
insmod part_gpt
729+
insmod ext2
730+
if [ x$feature_platform_search_hint = xy ]; then
731+
search --no-floppy --fs-uuid --set=root a92522f9-74dc-456a-ae0c-05ba22bca976
732+
else
733+
search --no-floppy --fs-uuid --set=root a92522f9-74dc-456a-ae0c-05ba22bca976
734+
fi
735+
linuxefi /ostree/rhcos-bf3b382/vmlinuz console=tty0 console=ttyS0,115200n8 rootflags=defaults,prjquota rw $ignition_firstboot root=UUID=cbac8cdc
736+
initrdefi /ostree/rhcos-bf3b382/initramfs.img
737+
}
738+
### END /etc/grub.d/15_ostree ###
739+
740+
### BEGIN /etc/grub.d/20_linux_xen ###
741+
### END /etc/grub.d/20_linux_xen ###";
742+
743+
strip_grub_config_file(
744+
BufReader::new(std::io::Cursor::new(content)),
745+
&rootd,
746+
stripped_config.to_str().unwrap(),
747+
)?;
748+
let stripped_content = fs::read_to_string(stripped_config)?;
749+
let expected = r"
750+
### BEGIN /etc/grub.d/10_linux ###
751+
752+
### END /etc/grub.d/10_linux ###
753+
754+
755+
### BEGIN /etc/grub.d/20_linux_xen ###
756+
### END /etc/grub.d/20_linux_xen ###
757+
";
758+
assert_eq!(expected, stripped_content);
759+
Ok(())
760+
}
717761
}

src/ostreeutil.rs

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
use std::path::Path;
88

9-
use anyhow::Result;
9+
use anyhow::{Context, Result};
1010
use log::debug;
1111

1212
/// https://github.com/coreos/rpm-ostree/pull/969/commits/dc0e8db5bd92e1f478a0763d1a02b48e57022b59
@@ -63,3 +63,42 @@ pub(crate) fn rpm_cmd<P: AsRef<Path>>(sysroot: P) -> Result<std::process::Comman
6363
}
6464
Ok(c)
6565
}
66+
67+
/// Get sysroot.bootloader in ostree repo config.
68+
pub(crate) fn get_ostree_bootloader() -> Result<Option<String>> {
69+
let mut cmd = std::process::Command::new("ostree");
70+
let result = cmd
71+
.args([
72+
"config",
73+
"--repo=/sysroot/ostree/repo",
74+
"get",
75+
"sysroot.bootloader",
76+
])
77+
.output()
78+
.context("Querying ostree sysroot.bootloader")?;
79+
if !result.status.success() {
80+
// ostree will exit with a none zero return code if the key does not exists
81+
return Ok(None);
82+
} else {
83+
let res = String::from_utf8(result.stdout)
84+
.with_context(|| "decoding as UTF-8 output of ostree command")?;
85+
let bootloader = res.trim_end().to_string();
86+
return Ok(Some(bootloader));
87+
}
88+
}
89+
90+
pub(crate) fn set_ostree_bootloader(bootloader: &str) -> Result<()> {
91+
let status = std::process::Command::new("ostree")
92+
.args([
93+
"config",
94+
"--repo=/sysroot/ostree/repo",
95+
"set",
96+
"sysroot.bootloader",
97+
bootloader,
98+
])
99+
.status()?;
100+
if !status.success() {
101+
anyhow::bail!("Failed to set 'sysroot.bootloader' to '{bootloader}' in ostree repo config");
102+
}
103+
Ok(())
104+
}

0 commit comments

Comments
 (0)