Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -217,9 +217,11 @@ jobs:
test_os: ${{ fromJson(needs.compute-ci-level.outputs.integration_os_matrix) }}
variant: [ostree, composefs]
filesystem: ["ext4", "xfs"]
bootloader: ["grub", "systemd"]
# TODO: Remove "grub" once "grub-cc" is stable
bootloader: ["grub", "grub-cc", "systemd"]
boot_type: ["bls", "uki"]
seal_state: ["sealed", "unsealed"]

exclude:
# https://github.com/bootc-dev/bootc/issues/1812
- test_os: centos-9
Expand All @@ -245,6 +247,22 @@ jobs:
- variant: ostree
bootloader: systemd

# For now only have grub-cc tests in F44
- test_os: fedora-45
bootloader: grub-cc
- test_os: fedora-43
bootloader: grub-cc
- test_os: centos-9
bootloader: grub-cc
- test_os: centos-10
bootloader: grub-cc
# Not in ostree
- variant: ostree
bootloader: grub-cc
# Not yet "sealed"
- bootloader: grub-cc
seal_state: sealed

runs-on: ubuntu-24.04

steps:
Expand Down
15 changes: 15 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ COPY --from=target-base /target-rootfs/ /
ARG SKIP_CONFIGS
ARG boot_type
ARG seal_state
ARG bootloader
# All network-fetching operations: package installs from distro repos, Copr, Koji.
# Separated so `just build-fetch --target=fetch` can be retried independently on
# transient network failures without re-running the configuration phase.
Expand Down Expand Up @@ -89,6 +90,20 @@ RUN --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp \
if [[ ${#pkgs_to_install[@]} -gt 0 ]]; then
dnf install -y "${pkgs_to_install[@]}"
fi

if [[ "$bootloader" == "grub-cc" ]]; then
# We have this until we get grub-cc support in bootupd
arch=$(uname -m)
curl -L -o /var/grub-cc.rpm "https://kojipkgs.fedoraproject.org/packages/grub2/2.12/59.eln156/x86_64/grub2-efi-x64-cc-2.12-59.eln156.${arch}.rpm"
mkdir /var/grub-cc
rpm2archive /var/grub-cc.rpm | tar -xvz -C /var/grub-cc
file=$(find /var/grub-cc -name '*.efi')
mkdir /usr/lib/grub-cc
cp $file /usr/lib/grub-cc
rm -rvf /var/grub-cc
rm -rvf /var/grub-cc.rpm
fi
Comment thread
Johan-Liebert1 marked this conversation as resolved.

EOF

# Note we don't do any customization here yet
Expand Down
10 changes: 4 additions & 6 deletions crates/lib/src/bootc_composefs/backwards_compat/bcompat_boot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use crate::{
TYPE1_ENT_PATH_STAGED, UKI_NAME_PREFIX, USER_CFG_STAGED,
},
parsers::bls_config::{BLSConfig, BLSConfigType},
spec::Bootloader,
spec::BootloaderKind,
store::Storage,
};
use anyhow::{Context, Result};
Expand Down Expand Up @@ -323,8 +323,8 @@ pub(crate) async fn prepend_custom_prefix(
handle_bls_conf(storage, cfs_cmdline, boot_dir, false)?;
}

BootType::Uki => match bootloader {
Bootloader::Grub => {
BootType::Uki => match bootloader.kind()? {
BootloaderKind::GRUBClassic => {
let esp = storage.require_esp()?;

let mut buf = String::new();
Expand Down Expand Up @@ -384,11 +384,9 @@ pub(crate) async fn prepend_custom_prefix(
rename_exchange_user_cfg(&grub_dir)?;
}

Bootloader::Systemd => {
BootloaderKind::BLSCompatible => {
handle_bls_conf(storage, cfs_cmdline, boot_dir, true)?;
}

Bootloader::None => unreachable!("Checked at install time"),
},
};

Expand Down
56 changes: 45 additions & 11 deletions crates/lib/src/bootc_composefs/boot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ use crate::bootc_composefs::status::ComposefsCmdline;
use crate::bootc_kargs::compute_new_kargs;
use crate::composefs_consts::{TYPE1_BOOT_DIR_PREFIX, TYPE1_ENT_PATH, TYPE1_ENT_PATH_STAGED};
use crate::parsers::bls_config::{BLSConfig, BLSConfigType};
use crate::spec::BootloaderKind;
use crate::task::Task;
use crate::{bootc_composefs::repo::open_composefs_repo, store::Storage};
use crate::{bootc_composefs::status::get_sorted_grub_uki_boot_entries, install::PostFetchState};
Expand Down Expand Up @@ -582,8 +583,8 @@ pub(crate) fn setup_composefs_bls_boot(

compute_new_kargs(mounted_erofs, current_root, &mut cmdline_refs)?;

let (entry_paths, _tmpdir_guard) = match bootloader {
Bootloader::Grub => {
let (entry_paths, _tmpdir_guard) = match bootloader.kind()? {
BootloaderKind::GRUBClassic => {
let root = Dir::open_ambient_dir(&root_path, ambient_authority())
.context("Opening root path")?;

Expand All @@ -607,7 +608,7 @@ pub(crate) fn setup_composefs_bls_boot(
)
}

Bootloader::Systemd => {
BootloaderKind::BLSCompatible => {
let efi_mount = mount_esp(&esp_device).context("Mounting ESP")?;

let mounted_efi = Utf8PathBuf::from(efi_mount.dir.path().as_str()?);
Expand All @@ -622,8 +623,6 @@ pub(crate) fn setup_composefs_bls_boot(
Some(efi_mount),
)
}

Bootloader::None => unreachable!("Checked at install time"),
};

let (bls_config, boot_digest, os_id) = match &entry {
Expand Down Expand Up @@ -1164,14 +1163,14 @@ pub(crate) fn setup_composefs_uki_boot(

let boot_digest = uki_info.boot_digest.clone();

match bootloader {
Bootloader::Grub => {
match bootloader.kind()? {
BootloaderKind::GRUBClassic => {
write_grub_uki_menuentry(root_path, &setup_type, uki_info.boot_label, id, &esp_device)?
}

Bootloader::Systemd => write_systemd_uki_config(&esp_mount.fd, &setup_type, uki_info, id)?,

Bootloader::None => unreachable!("Checked at install time"),
BootloaderKind::BLSCompatible => {
write_systemd_uki_config(&esp_mount.fd, &setup_type, uki_info, id)?
}
};

Ok(boot_digest)
Expand Down Expand Up @@ -1369,13 +1368,48 @@ pub(crate) async fn setup_composefs_boot(
&root_setup.device_info.require_single_root()?,
boot_uuid,
)?;
} else if postfetch.detected_bootloader == Bootloader::Grub {
} else if matches!(
postfetch.detected_bootloader,
Bootloader::Grub | Bootloader::GrubCC
) {
crate::bootloader::install_via_bootupd(
&root_setup.device_info,
&root_setup.physical_root_path,
&state.config_opts,
None,
)?;

// FIXME: Remove this hack once we have support in bootupd
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but it'd be clearer if this was in the conditional above right? Wouldn't bootupd bail in this scenario as is now?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, bootupd will fail if it's only grub-cc, but another hack is that we don't remove grub from the Dockerfile currently. We keep it alongside the grub-cc binary. So bootupd installs grub, shim etc and afterwards we replace the grubx64.efi with grub-cc.efi. It's not clean

if matches!(postfetch.detected_bootloader, Bootloader::GrubCC) {
root_setup
.physical_root
.remove_dir_all("boot/grub2")
.context("removing grub2")?;
Comment thread
Johan-Liebert1 marked this conversation as resolved.

let (os_id, ..) = parse_os_release(mounted_root.dir())?
.ok_or_else(|| anyhow::anyhow!("Failed to parse os-release"))?;

let dir = format!("EFI/{os_id}");

// Files are in EFI/<os-name>/
let efis_dir = mounted_root
.open_esp_dir()
.context("opening esp")?
.open_dir(&dir)
.with_context(|| format!("Opening {dir}"))?;

efis_dir
.remove_file_optional("bootuuid.cfg")
.context("Removing bootuuid.cfg")?;
efis_dir
.remove_file_optional("grub.cfg")
.context("Removing grub.cfg")?;

mounted_root
.dir()
.copy("usr/lib/grub-cc/grubx64-cc.efi", &efis_dir, "grubx64.efi")
.context("Copying grub-cc binary")?;
}
} else {
crate::bootloader::install_systemd_boot(
&mounted_root,
Expand Down
10 changes: 4 additions & 6 deletions crates/lib/src/bootc_composefs/delete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use crate::{
TYPE1_ENT_PATH, TYPE1_ENT_PATH_STAGED, USER_CFG_STAGED,
},
parsers::bls_config::{BLSConfigType, parse_bls_config},
spec::{BootEntry, Bootloader, DeploymentEntry},
spec::{BootEntry, BootloaderKind, DeploymentEntry},
status::Slot,
store::{BootedComposefs, Storage},
};
Expand Down Expand Up @@ -145,20 +145,18 @@ fn delete_depl_boot_entries(
) -> Result<()> {
let boot_dir = storage.require_boot_dir()?;

match deployment.deployment.bootloader {
Bootloader::Grub => match deployment.deployment.boot_type {
match deployment.deployment.bootloader.kind()? {
BootloaderKind::GRUBClassic => match deployment.deployment.boot_type {
BootType::Bls => delete_type1_conf_file(deployment, boot_dir, deleting_staged),
BootType::Uki => {
remove_grub_menucfg_entry(&deployment.deployment.verity, boot_dir, deleting_staged)
}
},

Bootloader::Systemd => {
BootloaderKind::BLSCompatible => {
// For Systemd UKI as well, we use .conf files
delete_type1_conf_file(deployment, boot_dir, deleting_staged)
}

Bootloader::None => unreachable!("Checked at install time"),
}
}

Expand Down
10 changes: 4 additions & 6 deletions crates/lib/src/bootc_composefs/finalize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::bootc_composefs::gc::{GCOpts, composefs_gc};
use crate::bootc_composefs::rollback::{rename_exchange_bls_entries, rename_exchange_user_cfg};
use crate::bootc_composefs::status::get_composefs_status;
use crate::composefs_consts::STATE_DIR_ABS;
use crate::spec::Bootloader;
use crate::spec::BootloaderKind;
use crate::store::{BootedComposefs, Storage};
use anyhow::{Context, Result};
use bootc_initramfs_setup::mount_composefs_image;
Expand Down Expand Up @@ -131,21 +131,19 @@ pub(crate) async fn composefs_backend_finalize(

let boot_dir = storage.require_boot_dir()?;

match booted_composefs.bootloader {
Bootloader::Grub => match staged_composefs.boot_type {
match booted_composefs.bootloader.kind()? {
BootloaderKind::GRUBClassic => match staged_composefs.boot_type {
BootType::Bls => {
let entries_dir = boot_dir.open_dir("loader")?;
rename_exchange_bls_entries(&entries_dir)?;
}
BootType::Uki => finalize_staged_grub_uki(boot_dir)?,
},

Bootloader::Systemd => {
BootloaderKind::BLSCompatible => {
let entries_dir = boot_dir.open_dir("loader")?;
rename_exchange_bls_entries(&entries_dir)?;
}

Bootloader::None => unreachable!("Checked at install time"),
};

// Now that we have successfully updated bootloader entires, we can GC the unreferenced ones
Expand Down
10 changes: 4 additions & 6 deletions crates/lib/src/bootc_composefs/rollback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::bootc_composefs::boot::{
};
use crate::bootc_composefs::status::{get_composefs_status, get_sorted_type1_boot_entries};
use crate::composefs_consts::TYPE1_ENT_PATH_STAGED;
use crate::spec::Bootloader;
use crate::spec::{Bootloader, BootloaderKind};
use crate::store::{BootedComposefs, Storage};
use crate::{
bootc_composefs::{boot::get_efi_uuid_source, status::get_sorted_grub_uki_boot_entries},
Expand Down Expand Up @@ -224,8 +224,8 @@ pub(crate) async fn composefs_rollback(

let boot_dir = storage.require_boot_dir()?;

match &rollback_entry.bootloader {
Bootloader::Grub => match rollback_entry.boot_type {
match &rollback_entry.bootloader.kind()? {
BootloaderKind::GRUBClassic => match rollback_entry.boot_type {
BootType::Bls => {
rollback_composefs_entries(boot_dir, rollback_entry.bootloader.clone())?;
}
Expand All @@ -234,12 +234,10 @@ pub(crate) async fn composefs_rollback(
}
},

Bootloader::Systemd => {
BootloaderKind::BLSCompatible => {
// We use BLS entries for systemd UKI as well
rollback_composefs_entries(boot_dir, rollback_entry.bootloader.clone())?;
}

Bootloader::None => unreachable!("Checked at install time"),
}

if reverting {
Expand Down
31 changes: 17 additions & 14 deletions crates/lib/src/bootc_composefs/status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use crate::{
bls_config::{BLSConfig, BLSConfigType, parse_bls_config},
grub_menuconfig::{MenuEntry, parse_grub_menuentry_file},
},
spec::{BootEntry, BootOrder, Host, HostSpec, ImageStatus},
spec::{BootEntry, BootOrder, BootloaderKind, Host, HostSpec, ImageStatus},
store::Storage,
utils::{EfiError, read_uefi_var},
};
Expand Down Expand Up @@ -339,8 +339,8 @@ pub(crate) fn list_bootloader_entries(storage: &Storage) -> Result<Vec<Bootloade
let bootloader = get_bootloader()?;
let boot_dir = storage.require_boot_dir()?;

let entries = match bootloader {
Bootloader::Grub => {
let entries = match bootloader.kind()? {
BootloaderKind::GRUBClassic => {
// Grub entries are always in boot
let grub_dir = boot_dir.open_dir("grub2").context("Opening grub dir")?;

Expand Down Expand Up @@ -368,9 +368,7 @@ pub(crate) fn list_bootloader_entries(storage: &Storage) -> Result<Vec<Bootloade
}
}

Bootloader::Systemd => list_type1_entries(boot_dir)?,

Bootloader::None => unreachable!("Checked at install time"),
BootloaderKind::BLSCompatible => list_type1_entries(boot_dir)?,
};

Ok(entries)
Expand Down Expand Up @@ -414,10 +412,14 @@ pub(crate) fn get_bootloader() -> Result<Bootloader> {
let bootloader = match read_uefi_var(EFI_LOADER_INFO) {
Ok(loader) => {
if loader.to_lowercase().contains("systemd-boot") {
Bootloader::Systemd
} else {
Bootloader::Grub
return Ok(Bootloader::Systemd);
}

if loader.to_lowercase().contains("grub cc") {
return Ok(Bootloader::GrubCC);
}

return Ok(Bootloader::Grub);
}

Err(efi_error) => match efi_error {
Expand Down Expand Up @@ -869,8 +871,11 @@ async fn composefs_deployment_status_from(
let booted_cfs = host.require_composefs_booted()?;

let mut grub_menu_string = String::new();
let (is_rollback_queued, sorted_bls_config, grub_menu_entries) = match booted_cfs.bootloader {
Bootloader::Grub => match boot_type {
let (is_rollback_queued, sorted_bls_config, grub_menu_entries) = match booted_cfs
.bootloader
.kind()?
{
BootloaderKind::GRUBClassic => match boot_type {
BootType::Bls => {
let bls_configs = get_sorted_type1_boot_entries(boot_dir, false)?;
let bls_config = bls_configs
Expand Down Expand Up @@ -911,7 +916,7 @@ async fn composefs_deployment_status_from(
},

// We will have BLS stuff and the UKI stuff in the same DIR
Bootloader::Systemd => {
BootloaderKind::BLSCompatible => {
let bls_configs = get_sorted_type1_boot_entries(boot_dir, true)?;
let bls_config = bls_configs
.first()
Expand All @@ -934,8 +939,6 @@ async fn composefs_deployment_status_from(

(is_rollback_queued, Some(bls_configs), None)
}

Bootloader::None => unreachable!("Checked at install time"),
};

// Determine rollback deployment by matching extra deployment boot entries against entires read from /boot
Expand Down
Loading
Loading