Skip to content

Commit 3ca9715

Browse files
uki-addons: GC Global UKI Addons
Similar to how we GC UKIs and scoped UKI Addons, GC Global UKI addons if we have no EROFS images remaining that hold a reference to them, which means that the deployments that depended on the Global addons have been removed Signed-off-by: Pragyan Poudyal <pragyanpoudyal41999@gmail.com>
1 parent ee9b7d7 commit 3ca9715

3 files changed

Lines changed: 69 additions & 7 deletions

File tree

‎crates/lib/src/bootc_composefs/gc.rs‎

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ use ostree_ext::composefs_oci::linked_erofs_images;
1717
use rustix::fs::AtFlags;
1818
use rustix::fs::{statat, unlinkat};
1919

20+
use crate::bootc_composefs::boot::GLOBAL_UKI_ADDONS_DIR;
21+
use crate::bootc_composefs::boot::get_global_uki_addon_name;
22+
use crate::bootc_composefs::uki_addon::UkiAddonType;
23+
use crate::bootc_composefs::uki_addon::list_installed_uki_addons;
24+
use crate::bootc_composefs::uki_addon::list_referenced_uki_addons;
2025
use crate::{
2126
bootc_composefs::{
2227
boot::{BOOTC_UKI_DIR, BootType, get_type1_dir_name, get_uki_addon_dir_name, get_uki_name},
@@ -142,9 +147,6 @@ fn delete_kernel_initrd(storage: &Storage, dir_to_delete: &str, dry_run: bool) -
142147
fn delete_uki(storage: &Storage, uki_id: &str, dry_run: bool) -> Result<()> {
143148
let esp_mnt = storage.require_esp()?;
144149

145-
// NOTE: We don't delete global addons here (see `GLOBAL_UKI_ADDONS_DIR`)
146-
// Which is fine as global addons don't belong to any single deployment, but it also
147-
// means they're never cleaned up at all: see the TODO on `GLOBAL_UKI_ADDONS_DIR`.
148150
let uki_dir = esp_mnt.fd.open_dir(BOOTC_UKI_DIR)?;
149151

150152
for entry in uki_dir.entries_utf8()? {
@@ -291,7 +293,7 @@ pub(crate) async fn composefs_gc(
291293
)
292294
}
293295

294-
for (ty, verity) in unreferenced_boot_binaries {
296+
for (ty, verity) in &unreferenced_boot_binaries {
295297
match ty {
296298
BootType::Bls => {
297299
delete_kernel_initrd(storage, &get_type1_dir_name(verity), gc_opts.dry_run)?
@@ -300,6 +302,55 @@ pub(crate) async fn composefs_gc(
300302
}
301303
}
302304

305+
// Remove unreferenced global UKI Addons
306+
let currently_referenced_addons = list_referenced_uki_addons(booted_cfs, &bootloader_entries)?;
307+
let currently_installed_addons = list_installed_uki_addons(storage)?;
308+
let mut unreferenced_global_addons = vec![];
309+
310+
tracing::debug!("currently_referenced_addons: {currently_referenced_addons:#?}");
311+
tracing::debug!("currently_installed_addons: {currently_installed_addons:#?}");
312+
313+
for installed_addon in &currently_installed_addons {
314+
// We handle scoped UKI Addons along with the UKI itself
315+
if installed_addon.addon_type != UkiAddonType::Global {
316+
continue;
317+
}
318+
319+
let is_referenced = currently_referenced_addons.iter().any(|(_, refs)| {
320+
refs.iter().any(|r| {
321+
r.addon_type == installed_addon.addon_type && r.name == installed_addon.name
322+
})
323+
});
324+
325+
if !is_referenced {
326+
unreferenced_global_addons.push(installed_addon.name.clone());
327+
}
328+
}
329+
330+
tracing::debug!("Unreferenced Global Addons: {unreferenced_global_addons:?}");
331+
332+
if !unreferenced_global_addons.is_empty() {
333+
let global_uki_dir = storage
334+
.require_esp()?
335+
.fd
336+
.open_dir(GLOBAL_UKI_ADDONS_DIR)
337+
.context("Opening global UKI Addons dir")?;
338+
339+
for addon in &unreferenced_global_addons {
340+
let global_addon_name = get_global_uki_addon_name(&addon);
341+
342+
tracing::debug!("Deleting Global UKI Addon: {}", addon);
343+
344+
if gc_opts.dry_run {
345+
continue;
346+
}
347+
348+
global_uki_dir
349+
.remove_file(&global_addon_name)
350+
.with_context(|| format!("Removing global addon {global_addon_name}"))?;
351+
}
352+
}
353+
303354
if !gc_opts.prune_repo {
304355
return Ok(GcResult::default());
305356
}

‎crates/lib/src/bootc_composefs/uki_addon.rs‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,10 +163,15 @@ pub fn list_installed_uki_addons(storage: &Storage) -> Result<Vec<UkiAddonsList>
163163
.context("Gathering global addons")?;
164164
};
165165

166-
for ent in esp
166+
let Some(bootc_uki_dir) = esp
167167
.fd
168-
.open_dir(BOOTC_UKI_DIR)
168+
.open_dir_optional(BOOTC_UKI_DIR)
169169
.context("Opening UKI dir")?
170+
else {
171+
return Ok(addons);
172+
};
173+
174+
for ent in bootc_uki_dir
170175
.entries_utf8()
171176
.context("Reading UKI dir entries")?
172177
{

‎tmt/tests/booted/test-composefs-gc-uki.nu‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,15 @@ def first_boot [] {
3333
let addon_cmds = "
3434
mkdir -p /out/${kver}.efi.extra.d
3535
ukify build --cmdline 'gc_test=1' --output /out/${kver}.efi.extra.d/gc-test.addon.efi
36+
mkdir -p /out/loader/addons
37+
ukify build --cmdline 'gc_test=global' --output /out/loader/addons/gc-test-global.addon.efi
3638
"
3739

3840
$containerfile = (tap make_uki_containerfile $containerfile --addon-cmds $addon_cmds)
3941

4042
echo $containerfile | podman build -t localhost/bootc-first . -f -
4143

42-
bootc switch --transport containers-storage --uki-addon gc-test localhost/bootc-first
44+
bootc switch --transport containers-storage --uki-addon gc-test --global-uki-addon gc-test-global localhost/bootc-first
4345

4446
# Make sure we have the .boot EROFS
4547
let st = bootc status --json | from json
@@ -141,6 +143,10 @@ def fourth_boot [] {
141143
let boot1_addon_dir = $"/var/tmp/efi/EFI/Linux/bootc/($uki_prefix)(cat /var/boot1-verity).efi.extra.d"
142144
assert (not ($boot1_addon_dir | path exists))
143145

146+
# The global addon should also be gone
147+
let global_addon = $"/var/tmp/efi/EFI/loader/addons/bootc_composefs-gc-test-global.addon.efi"
148+
assert (not ($global_addon | path exists))
149+
144150
mut containerfile = "
145151
FROM localhost/bootc as base
146152
RUN echo 'another file' > /usr/share/another-one

0 commit comments

Comments
 (0)