From 7cae27a2035a63cb6c9dbb187b906bb9a7728499 Mon Sep 17 00:00:00 2001 From: Pragyan Poudyal Date: Wed, 28 Jan 2026 11:20:42 +0530 Subject: [PATCH 1/8] dockerfile: Clear cargo's local cache before fetching Signed-off-by: Pragyan Poudyal --- Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 27abdbd37..d97312697 100644 --- a/Dockerfile +++ b/Dockerfile @@ -34,7 +34,8 @@ WORKDIR /src # We aren't using the full recommendations there, just the simple bits. # First we download all of our Rust dependencies # Note: Local path dependencies (from [patch] sections) are auto-detected and bind-mounted by the Justfile -RUN --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp --mount=type=cache,target=/src/target --mount=type=cache,target=/var/roothome cargo fetch +RUN --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp --mount=type=cache,target=/src/target --mount=type=cache,target=/var/roothome \ + rm -rf /var/roothome/.cargo/registry; cargo fetch # We always do a "from scratch" build # https://docs.fedoraproject.org/en-US/bootc/building-from-scratch/ From f6b5e89794b1e29bd11a0d1e93e3b005ebeb55a6 Mon Sep 17 00:00:00 2001 From: Pragyan Poudyal Date: Wed, 28 Jan 2026 11:23:23 +0530 Subject: [PATCH 2/8] xtask: Introduce `composefs` variant This is the Non UKI version, i.e. version with Type1 bootloader entries Signed-off-by: Pragyan Poudyal --- crates/tests-integration/src/container.rs | 6 ++--- crates/xtask/src/tmt.rs | 15 ++++++++++++ crates/xtask/src/xtask.rs | 28 ++++++++++++++++++++++- 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/crates/tests-integration/src/container.rs b/crates/tests-integration/src/container.rs index 2063aac2e..032d9842c 100644 --- a/crates/tests-integration/src/container.rs +++ b/crates/tests-integration/src/container.rs @@ -52,8 +52,8 @@ pub(crate) fn test_bootc_container_inspect() -> Result<()> { .expect("kernel.unified should be a boolean"); if let Some(variant) = std::env::var("BOOTC_variant").ok() { match variant.as_str() { - "ostree" => { - assert!(!unified, "Expected unified=false for ostree variant"); + v @ "ostree" | v @ "composefs" => { + assert!(!unified, "Expected unified=false for variant {v}"); // For traditional kernels, version should look like a uname (contains digits) assert!( version.chars().any(|c| c.is_ascii_digit()), @@ -159,7 +159,7 @@ fn test_variant_base_crosscheck() -> Result<()> { // TODO add this to `bootc status` or so? let boot_efi = Utf8Path::new("/boot/EFI"); match variant.as_str() { - "ostree" => { + "composefs" | "ostree" => { assert!(!boot_efi.try_exists()?); } "composefs-sealeduki-sdboot" => { diff --git a/crates/xtask/src/tmt.rs b/crates/xtask/src/tmt.rs index dcc7009aa..e26751bbe 100644 --- a/crates/xtask/src/tmt.rs +++ b/crates/xtask/src/tmt.rs @@ -29,6 +29,8 @@ const ENV_BOOTC_UPGRADE_IMAGE: &str = "BOOTC_upgrade_image"; // Distro identifiers const DISTRO_CENTOS_9: &str = "centos-9"; +const COMPOSEFS_KERNEL_ARGS: [&str; 1] = ["--karg=enforcing=0"]; + // Import the argument types from xtask.rs use crate::{RunTmtArgs, TmtProvisionArgs}; @@ -430,6 +432,19 @@ pub(crate) fn run_tmt(sh: &Shell, args: &RunTmtArgs) -> Result<()> { opts.push("--filesystem=xfs".to_string()); } } + + if args.composefs_backend { + // TODO(Johan-Liebert1): Filesystem should be a parameter and we should test + // insecure with xfs + opts.push("--filesystem=ext4".into()); + opts.push("--composefs-backend".into()); + opts.extend(COMPOSEFS_KERNEL_ARGS.map(|x| x.into())); + } + + if let Some(b) = &args.bootloader { + opts.push(format!("--bootloader={b}")); + } + opts }; diff --git a/crates/xtask/src/xtask.rs b/crates/xtask/src/xtask.rs index 977ddec65..6886f7fc9 100644 --- a/crates/xtask/src/xtask.rs +++ b/crates/xtask/src/xtask.rs @@ -5,13 +5,14 @@ //! end up as a lot of nontrivial bash code. use std::borrow::Cow; +use std::fmt::Display; use std::fs::File; use std::io::{BufRead, BufReader, BufWriter, Write}; use std::process::Command; use anyhow::{Context, Result}; use camino::{Utf8Path, Utf8PathBuf}; -use clap::{Args, Parser, Subcommand}; +use clap::{Args, Parser, Subcommand, ValueEnum}; use fn_error_context::context; use xshell::{Shell, cmd}; @@ -76,6 +77,25 @@ pub(crate) struct LocalRustDepsArgs { pub(crate) format: String, } +/// Bootloader passed as --bootloader param for composefs builds +// TODO: Find a better way to share this Enum between this and crates/lib +#[derive(Debug, Clone, ValueEnum)] +pub enum Bootloader { + /// grub as bootloader + Grub, + /// systemd-boot as bootloader + Systemd, +} + +impl Display for Bootloader { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Bootloader::Grub => f.write_str("grub"), + Bootloader::Systemd => f.write_str("systemd"), + } + } +} + /// Arguments for run-tmt command #[derive(Debug, Args)] pub(crate) struct RunTmtArgs { @@ -101,6 +121,12 @@ pub(crate) struct RunTmtArgs { /// Preserve VMs after test completion (useful for debugging) #[arg(long)] pub(crate) preserve_vm: bool, + + #[arg(long)] + pub(crate) composefs_backend: bool, + + #[arg(long, requires = "composefs_backend")] + pub(crate) bootloader: Option, } /// Arguments for tmt-provision command From 78355e25878527f98eea1c2d81405c8c5d30a359 Mon Sep 17 00:00:00 2001 From: Pragyan Poudyal Date: Wed, 28 Jan 2026 11:36:12 +0530 Subject: [PATCH 3/8] tmt/tests: Add composefs/ostree distinguishing checks The tests assume an ostree system right now, but we want the same tests for composefs systems as well. Add a `is_composefs` function to differentitate between the two backends during testing Signed-off-by: Pragyan Poudyal --- tmt/tests/booted/readonly/001-test-status.nu | 2 +- .../010-test-bootc-container-store.nu | 2 +- .../readonly/011-test-ostree-ext-cli.nu | 2 +- .../booted/readonly/011-test-resolvconf.nu | 2 +- .../booted/readonly/012-test-unit-status.nu | 2 +- tmt/tests/booted/readonly/015-test-fsck.nu | 2 +- .../booted/readonly/017-test-bound-storage.nu | 2 +- .../booted/readonly/030-test-composefs.nu | 2 +- tmt/tests/booted/tap.nu | 5 ++ .../booted/test-image-pushpull-upgrade.nu | 37 ++++++++------ .../test-install-to-filesystem-var-mount.sh | 51 ++++++++++++++++--- tmt/tests/booted/test-soft-reboot.nu | 9 +++- .../booted/test-switch-mutate-in-place.nu | 4 +- 13 files changed, 85 insertions(+), 37 deletions(-) diff --git a/tmt/tests/booted/readonly/001-test-status.nu b/tmt/tests/booted/readonly/001-test-status.nu index 80c29028a..f239a5485 100644 --- a/tmt/tests/booted/readonly/001-test-status.nu +++ b/tmt/tests/booted/readonly/001-test-status.nu @@ -14,7 +14,7 @@ assert ($opts | any { |o| $o == "ro" }) "/sysroot should be mounted read-only" let st = bootc status --json | from json # Detect composefs by checking if composefs field is present -let is_composefs = ($st.status.booted.composefs? != null) +let is_composefs = (tap is_composefs) assert equal $st.apiVersion org.containers.bootc/v1 diff --git a/tmt/tests/booted/readonly/010-test-bootc-container-store.nu b/tmt/tests/booted/readonly/010-test-bootc-container-store.nu index ef23a039d..e344ff8e0 100644 --- a/tmt/tests/booted/readonly/010-test-bootc-container-store.nu +++ b/tmt/tests/booted/readonly/010-test-bootc-container-store.nu @@ -5,7 +5,7 @@ tap begin "verify bootc-owned container storage" # Detect composefs by checking if composefs field is present let st = bootc status --json | from json -let is_composefs = ($st.status.booted.composefs? != null) +let is_composefs = (tap is_composefs) if $is_composefs { print "# TODO composefs: skipping test - /usr/lib/bootc/storage doesn't exist with composefs" diff --git a/tmt/tests/booted/readonly/011-test-ostree-ext-cli.nu b/tmt/tests/booted/readonly/011-test-ostree-ext-cli.nu index edac11cba..817bc6003 100644 --- a/tmt/tests/booted/readonly/011-test-ostree-ext-cli.nu +++ b/tmt/tests/booted/readonly/011-test-ostree-ext-cli.nu @@ -8,7 +8,7 @@ tap begin "verify bootc wrapping ostree-ext" # Parse the status and get the booted image let st = bootc status --json | from json # Detect composefs by checking if composefs field is present -let is_composefs = ($st.status.booted.composefs? != null) +let is_composefs = (tap is_composefs) if $is_composefs { print "# TODO composefs: skipping test - ostree-container commands don't work with composefs" } else { diff --git a/tmt/tests/booted/readonly/011-test-resolvconf.nu b/tmt/tests/booted/readonly/011-test-resolvconf.nu index 8f040d665..fa61a46be 100644 --- a/tmt/tests/booted/readonly/011-test-resolvconf.nu +++ b/tmt/tests/booted/readonly/011-test-resolvconf.nu @@ -6,7 +6,7 @@ tap begin "verify there's not an empty /etc/resolv.conf in the image" let st = bootc status --json | from json # Detect composefs by checking if composefs field is present -let is_composefs = ($st.status.booted.composefs? != null) +let is_composefs = (tap is_composefs) if $is_composefs { print "# TODO composefs: skipping test - ostree commands don't work with composefs" } else { diff --git a/tmt/tests/booted/readonly/012-test-unit-status.nu b/tmt/tests/booted/readonly/012-test-unit-status.nu index ebc5363e8..4271a9653 100644 --- a/tmt/tests/booted/readonly/012-test-unit-status.nu +++ b/tmt/tests/booted/readonly/012-test-unit-status.nu @@ -6,7 +6,7 @@ tap begin "verify our systemd units" # Detect composefs by checking if composefs field is present let st = bootc status --json | from json -let is_composefs = ($st.status.booted.composefs? != null) +let is_composefs = (tap is_composefs) if $is_composefs { print "# TODO composefs: skipping test - bootc-status-updated.path watches /ostree/bootc which doesn't exist with composefs" diff --git a/tmt/tests/booted/readonly/015-test-fsck.nu b/tmt/tests/booted/readonly/015-test-fsck.nu index 555842681..8700a5f66 100644 --- a/tmt/tests/booted/readonly/015-test-fsck.nu +++ b/tmt/tests/booted/readonly/015-test-fsck.nu @@ -5,7 +5,7 @@ tap begin "Run fsck" # Detect composefs by checking if composefs field is present let st = bootc status --json | from json -let is_composefs = ($st.status.booted.composefs? != null) +let is_composefs = (tap is_composefs) if $is_composefs { print "# TODO composefs: skipping test - fsck requires ostree-booted host" diff --git a/tmt/tests/booted/readonly/017-test-bound-storage.nu b/tmt/tests/booted/readonly/017-test-bound-storage.nu index 9d5640356..56aaea7ea 100644 --- a/tmt/tests/booted/readonly/017-test-bound-storage.nu +++ b/tmt/tests/booted/readonly/017-test-bound-storage.nu @@ -10,7 +10,7 @@ if not (bootc_testlib have_hostexports) { bootc status let st = bootc status --json | from json -let is_composefs = ($st.status.booted.composefs? != null) +let is_composefs = (tap is_composefs) if $is_composefs { # TODO we don't have imageDigest yet in status exit 0 diff --git a/tmt/tests/booted/readonly/030-test-composefs.nu b/tmt/tests/booted/readonly/030-test-composefs.nu index b7f028e44..81e2acc4b 100644 --- a/tmt/tests/booted/readonly/030-test-composefs.nu +++ b/tmt/tests/booted/readonly/030-test-composefs.nu @@ -9,7 +9,7 @@ def parse_cmdline [] { # Detect composefs by checking if composefs field is present let st = bootc status --json | from json -let is_composefs = ($st.status.booted.composefs? != null) +let is_composefs = (tap is_composefs) let expecting_composefs = ($env.BOOTC_variant? | default "" | find "composefs") != null if $expecting_composefs { assert $is_composefs diff --git a/tmt/tests/booted/tap.nu b/tmt/tests/booted/tap.nu index 096638fa0..ae1576dad 100644 --- a/tmt/tests/booted/tap.nu +++ b/tmt/tests/booted/tap.nu @@ -13,3 +13,8 @@ export def ok [] { export def fail [] { print "not ok" } + +export def is_composefs [] { + let st = bootc status --json | from json + $st.status.booted.composefs? != null +} diff --git a/tmt/tests/booted/test-image-pushpull-upgrade.nu b/tmt/tests/booted/test-image-pushpull-upgrade.nu index 39f757b56..78a3354fa 100644 --- a/tmt/tests/booted/test-image-pushpull-upgrade.nu +++ b/tmt/tests/booted/test-image-pushpull-upgrade.nu @@ -22,7 +22,7 @@ const quoted_karg = '"thisarg=quoted with spaces"' bootc status let st = bootc status --json | from json let booted = $st.status.booted.image -let is_composefs = ($st.status.booted.composefs? != null) +let is_composefs = (tap is_composefs) # Parse the kernel commandline into a list. # This is not a proper parser, but good enough @@ -54,7 +54,11 @@ RUN echo test content > /usr/share/blah.txt let v = podman run --rm localhost/bootc-derived cat /usr/share/blah.txt | str trim assert equal $v "test content" - let orig_root_mtime = ls -Dl /ostree/bootc | get modified + mut orig_root_mtime = null; + + if not $is_composefs { + $orig_root_mtime = ls -Dl /ostree/bootc | get modified + } # Now, fetch it back into the bootc storage! # We also test the progress API here @@ -68,24 +72,25 @@ RUN echo test content > /usr/share/blah.txt systemd-run -u test-cat-progress -- /bin/bash -c $"exec cat ($progress_fifo) > ($progress_json)" # nushell doesn't do fd passing right now either, so run via bash bash -c $"bootc switch --progress-fd 3 --transport containers-storage localhost/bootc-derived 3>($progress_fifo)" - # Now, let's do some checking of the progress json - let progress = open --raw $progress_json | from json -o - sanity_check_switch_progress_json $progress - # Check that /run/reboot-required exists and is not empty - let rr_meta = (ls /run/reboot-required | first) - assert ($rr_meta.size > 0b) + if not $is_composefs { + # Now, let's do some checking of the progress json + let progress = open --raw $progress_json | from json -o + sanity_check_switch_progress_json $progress - # Verify that we logged to the journal - journalctl _MESSAGE_ID=3e2f1a0b9c8d7e6f5a4b3c2d1e0f9a8b7 + # Check that /run/reboot-required exists and is not empty + let rr_meta = (ls /run/reboot-required | first) + assert ($rr_meta.size > 0b) - # The mtime should change on modification - let new_root_mtime = ls -Dl /ostree/bootc | get modified - assert ($new_root_mtime > $orig_root_mtime) + # Verify that we logged to the journal + journalctl _MESSAGE_ID=3e2f1a0b9c8d7e6f5a4b3c2d1e0f9a8b7 - # Test for https://github.com/ostreedev/ostree/issues/3544 - # Add a quoted karg using rpm-ostree if available - if not $is_composefs { + # The mtime should change on modification + let new_root_mtime = ls -Dl /ostree/bootc | get modified + assert ($new_root_mtime > $orig_root_mtime) + + # Test for https://github.com/ostreedev/ostree/issues/3544 + # Add a quoted karg using rpm-ostree if available # Check rpm-ostree and rpm-ostreed service status before run rpm-ostree # And collect info for flaky error "error: System transaction in progress" rpm-ostree status diff --git a/tmt/tests/booted/test-install-to-filesystem-var-mount.sh b/tmt/tests/booted/test-install-to-filesystem-var-mount.sh index 5fe76d8a6..ed3529885 100644 --- a/tmt/tests/booted/test-install-to-filesystem-var-mount.sh +++ b/tmt/tests/booted/test-install-to-filesystem-var-mount.sh @@ -56,13 +56,13 @@ parted -s "$LOOP_DEV" mklabel gpt # BIOS boot partition (for GRUB on GPT) parted -s "$LOOP_DEV" mkpart primary 1MiB 2MiB parted -s "$LOOP_DEV" set 1 bios_grub on -# EFI partition (200 MiB) -parted -s "$LOOP_DEV" mkpart primary fat32 2MiB 202MiB +# EFI partition (1 GiB) +parted -s "$LOOP_DEV" mkpart primary fat32 2MiB 1026MiB parted -s "$LOOP_DEV" set 2 esp on # Boot partition (1 GiB) -parted -s "$LOOP_DEV" mkpart primary ext4 202MiB 1226MiB +parted -s "$LOOP_DEV" mkpart primary ext4 1026MiB 2052MiB # LVM partition (rest of disk) -parted -s "$LOOP_DEV" mkpart primary 1226MiB 100% +parted -s "$LOOP_DEV" mkpart primary 2052MiB 100% # Reload partition table partprobe "$LOOP_DEV" @@ -114,6 +114,18 @@ echo "Filesystem layout:" mount | grep /var/mnt/target || true df -h /var/mnt/target /var/mnt/target/boot /var/mnt/target/boot/efi /var/mnt/target/var +COMPOSEFS_BACKEND=() + +is_composefs=$(bootc status --json | jq '.status.booted.composefs') + +if [[ $is_composefs != "null" ]]; then + COMPOSEFS_BACKEND+=("--composefs-backend") + tune2fs -O verity /dev/BL/var02 + tune2fs -O verity /dev/BL/root02 +fi + +echo "${COMPOSEFS_BACKEND[@]}" + # Run bootc install to-filesystem from within the container image under test podman run \ --rm --privileged \ @@ -124,6 +136,7 @@ podman run \ "$TARGET_IMAGE" \ bootc install to-filesystem \ --disable-selinux \ + "${COMPOSEFS_BACKEND[@]}" \ --karg=root=UUID="$ROOT_UUID" \ --root-mount-spec=UUID="$ROOT_UUID" \ --boot-mount-spec=UUID="$BOOT_UUID" \ @@ -131,9 +144,31 @@ podman run \ # Verify the installation succeeded echo "Verifying installation..." -test -d /var/mnt/target/ostree -test -d /var/mnt/target/ostree/repo -# Verify bootloader was installed (grub2 or loader for different configurations) -test -d /var/mnt/target/boot/grub2 || test -d /var/mnt/target/boot/loader + +if [[ $is_composefs == "null" ]]; then + test -d /var/mnt/target/ostree + test -d /var/mnt/target/ostree/repo + + # Verify bootloader was installed (grub2 or loader for different configurations) + test -d /var/mnt/target/boot/grub2 || test -d /var/mnt/target/boot/loader +else + test -d /var/mnt/target/composefs + + # TODO(Johan-Liebert1): This is getting bootloader from the VM, which is not quite correct + # It works for now as the CI runs separately for each bootloader, but we need to get the + # bootloader from the installed systemd if we wish to run the tests locally without rebuilding the images + # This probably also happens in other tests, one instance is install-outside-container + bootloader=$(bootc status --json | jq '.status.booted.composefs.bootloader' | tr '[:upper:]' '[:lower:]') + bootloader=${bootloader//\"/} + + if [[ $bootloader == "grub" ]]; then + test -d /var/mnt/target/boot/grub2 || test -d /var/mnt/target/boot/loader + else + test -d /var/mnt/target/boot/efi/EFI + test -d /var/mnt/target/boot/efi/loader/entries + fi + +fi + echo "Installation to-filesystem with separate /var mount succeeded!" diff --git a/tmt/tests/booted/test-soft-reboot.nu b/tmt/tests/booted/test-soft-reboot.nu index dd3374e13..2b3a81316 100644 --- a/tmt/tests/booted/test-soft-reboot.nu +++ b/tmt/tests/booted/test-soft-reboot.nu @@ -41,8 +41,13 @@ RUN echo test content > /usr/share/testfile-for-soft-reboot.txt assert ("/run/nextroot" | path exists) - # See ../bug-soft-reboot.md - TMT cannot handle systemd soft-reboots - ostree admin prepare-soft-reboot --reset + if not (tap is_composefs) { + # See ../bug-soft-reboot.md - TMT cannot handle systemd soft-reboots + ostree admin prepare-soft-reboot --reset + } else { + bootc internals prep-soft-reboot --reset + } + # https://tmt.readthedocs.io/en/stable/stories/features.html#reboot-during-test tmt-reboot } diff --git a/tmt/tests/booted/test-switch-mutate-in-place.nu b/tmt/tests/booted/test-switch-mutate-in-place.nu index c112c8410..1c12907db 100644 --- a/tmt/tests/booted/test-switch-mutate-in-place.nu +++ b/tmt/tests/booted/test-switch-mutate-in-place.nu @@ -9,9 +9,7 @@ use bootc_testlib.nu # See https://github.com/bootc-dev/bootc/issues/1854 -let st = bootc status --json | from json -let is_composefs = ($st.status.booted.composefs? != null) -if not $is_composefs { +if not (tap is_composefs) { # This is aiming to reproduce an environment closer to the Anaconda case # where we're chrooted into a non-booted system. TODO: What we really want # is to add `bootc switch --sysroot` too. From 4c01f3d108a50ddf859e15a46112af363317fd8f Mon Sep 17 00:00:00 2001 From: Pragyan Poudyal Date: Wed, 28 Jan 2026 11:40:17 +0530 Subject: [PATCH 4/8] justfile/ci: Add composefs tests Add tests for composefs backend for grub and sdboot. Update the testing matrix in CI Signed-off-by: Pragyan Poudyal --- .github/workflows/ci.yml | 20 ++++++++++++++++---- Justfile | 26 +++++++++++++++++++++++++- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4936d028b..a0c30f9cf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -161,7 +161,7 @@ jobs: matrix: # No fedora-44 due to https://bugzilla.redhat.com/show_bug.cgi?id=2429501 test_os: [fedora-43, centos-9, centos-10] - variant: [ostree, composefs-sealeduki-sdboot] + variant: [ostree, composefs-sealeduki-sdboot, composefs-sdboot, composefs-grub] exclude: # centos-9 UKI is experimental/broken (https://github.com/bootc-dev/bootc/issues/1812) - test_os: centos-9 @@ -182,7 +182,18 @@ jobs: run: | BASE=$(just pullspec-for-os base ${{ matrix.test_os }}) echo "BOOTC_base=${BASE}" >> $GITHUB_ENV - echo "BOOTC_variant=${{ matrix.variant }}" >> $GITHUB_ENV + + case "${{ matrix.variant }}" in + composefs-grub|composefs-sdboot) + echo "BOOTC_variant=composefs" >> $GITHUB_ENV + ;; + + *) + echo "BOOTC_variant=${{ matrix.variant }}" >> $GITHUB_ENV + ;; + esac + + if [ "${{ matrix.variant }}" = "composefs-sealeduki-sdboot" ]; then BUILDROOTBASE=$(just pullspec-for-os buildroot-base ${{ matrix.test_os }}) @@ -211,11 +222,12 @@ jobs: - name: Run TMT integration tests run: | - if [ "${{ matrix.variant }}" = "composefs-sealeduki-sdboot" ]; then - just test-composefs + if [[ "${{ matrix.variant }}" = composefs* ]]; then + just "test-${{ matrix.variant }}" else just test-tmt integration fi + just clean-local-images - name: Archive TMT logs diff --git a/Justfile b/Justfile index dc865a38f..81cfe0acf 100644 --- a/Justfile +++ b/Justfile @@ -105,9 +105,32 @@ test-container: build build-units # Build and test sealed composefs images [group('core')] -test-composefs: +test-composefs-sealeduki-sdboot: just variant=composefs-sealeduki-sdboot test-tmt readonly local-upgrade-reboot +[group('core')] +test-composefs bootloader: + just variant=composefs test-tmt --composefs-backend --bootloader {{bootloader}} \ + readonly \ + bib-build \ + download-only \ + image-pushpull-upgrade \ + image-upgrade-reboot \ + install-outside-container \ + install-to-filesystem-var-mount \ + soft-reboot \ + usroverlay + +# Build and test composefs images booted using Type1 boot entries and systemd-boot as the bootloader +[group('core')] +test-composefs-sdboot: + just test-composefs systemd + +# Build and test composefs images booted using Type1 boot entries and grub as the bootloader +[group('core')] +test-composefs-grub: + just test-composefs grub + # Run cargo fmt and clippy checks in container [group('core')] validate: @@ -220,6 +243,7 @@ clean-local-images: podman image prune -f podman rmi {{fedora-coreos}} -f + # Build packages (RPM) into target/packages/ [group('maintenance')] package: From 7b2d288d3895c122347b00aec99d0e6dc93baa67 Mon Sep 17 00:00:00 2001 From: Pragyan Poudyal Date: Tue, 3 Feb 2026 11:17:28 +0530 Subject: [PATCH 5/8] ci/test: Pass bootloader as argument to Dockerfile We want to install systemd boot and remove bootupd for systemd boot images. Pass in a `bootloader` argument to the build Dockerfile Signed-off-by: Pragyan Poudyal --- .github/workflows/ci.yml | 21 ++++++++++++++++--- Dockerfile | 3 ++- Justfile | 6 ++++-- .../booted/test-image-pushpull-upgrade.nu | 2 +- 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a0c30f9cf..7f595dd0a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -182,14 +182,28 @@ jobs: run: | BASE=$(just pullspec-for-os base ${{ matrix.test_os }}) echo "BOOTC_base=${BASE}" >> $GITHUB_ENV + echo "RUST_BACKTRACE=full" >> $GITHUB_ENV + echo "RUST_LOG=trace" >> $GITHUB_ENV case "${{ matrix.variant }}" in - composefs-grub|composefs-sdboot) + composefs-grub) echo "BOOTC_variant=composefs" >> $GITHUB_ENV + echo "BOOTC_bootloader=grub" >> $GITHUB_ENV ;; - *) + composefs-sdboot) + echo "BOOTC_variant=composefs" >> $GITHUB_ENV + echo "BOOTC_bootloader=systemd" >> $GITHUB_ENV + ;; + + composefs-sealeduki-sdboot) + echo "BOOTC_variant=${{ matrix.variant }}" >> $GITHUB_ENV + echo "BOOTC_bootloader=systemd" >> $GITHUB_ENV + ;; + + ostree) echo "BOOTC_variant=${{ matrix.variant }}" >> $GITHUB_ENV + echo "BOOTC_bootloader=grub" >> $GITHUB_ENV ;; esac @@ -208,7 +222,8 @@ jobs: - name: Build container run: | - BOOTC_SKIP_PACKAGE=1 just build + BOOTC_SKIP_PACKAGE=1 just bootloader=$BOOTC_bootloader build + # Extra cross-check (duplicating the integration test) that we're using the right base used_vid=$(podman run --rm localhost/bootc bash -c '. /usr/lib/os-release && echo ${ID}-${VERSION_ID}') test ${{ matrix.test_os }} = "${used_vid}" diff --git a/Dockerfile b/Dockerfile index d97312697..5422d8e06 100644 --- a/Dockerfile +++ b/Dockerfile @@ -144,12 +144,13 @@ RUN --network=none --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp # Perform all filesystem transformations except generating the sealed UKI (if configured) FROM base as base-penultimate ARG variant +ARG bootloader # Switch to a signed systemd-boot, if configured RUN --network=none --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp \ --mount=type=bind,from=packaging,src=/,target=/run/packaging \ --mount=type=bind,from=sdboot-signed,src=/,target=/run/sdboot-signed < /usr/share/blah.txt let v = podman run --rm localhost/bootc-derived cat /usr/share/blah.txt | str trim assert equal $v "test content" - mut orig_root_mtime = null; + mut orig_root_mtime = 0; if not $is_composefs { $orig_root_mtime = ls -Dl /ostree/bootc | get modified From 7315e225c4b08b92eaeecc657379a29dff937740 Mon Sep 17 00:00:00 2001 From: Pragyan Poudyal Date: Fri, 6 Feb 2026 13:16:49 +0530 Subject: [PATCH 6/8] Make tests work for sdboot Signed-off-by: Pragyan Poudyal Remove root= cmdline for sdboot Signed-off-by: Pragyan Poudyal Use insecure UEFI for sdboot Signed-off-by: Pragyan Poudyal --- Justfile | 1 - crates/lib/src/bootc_composefs/boot.rs | 8 ++++++- crates/xtask/src/tmt.rs | 15 ++++++++---- crates/xtask/src/xtask.rs | 2 +- .../booted/readonly/030-test-composefs.nu | 24 ++++++++++++------- .../booted/test-install-outside-container.nu | 12 +++++++++- 6 files changed, 45 insertions(+), 17 deletions(-) diff --git a/Justfile b/Justfile index fa60222db..fae9f1048 100644 --- a/Justfile +++ b/Justfile @@ -114,7 +114,6 @@ test-composefs bootloader: just variant=composefs bootloader={{bootloader}} \ test-tmt --composefs-backend --bootloader {{bootloader}} \ readonly \ - bib-build \ download-only \ image-pushpull-upgrade \ image-upgrade-reboot \ diff --git a/crates/lib/src/bootc_composefs/boot.rs b/crates/lib/src/bootc_composefs/boot.rs index 801a019bd..c4787a917 100644 --- a/crates/lib/src/bootc_composefs/boot.rs +++ b/crates/lib/src/bootc_composefs/boot.rs @@ -68,7 +68,7 @@ use std::path::Path; use anyhow::{Context, Result, anyhow, bail}; use bootc_blockdev::find_parent_devices; -use bootc_kernel_cmdline::utf8::{Cmdline, Parameter}; +use bootc_kernel_cmdline::utf8::{Cmdline, Parameter, ParameterKey}; use bootc_mount::inspect_filesystem_of_dir; use bootc_mount::tempmount::TempMount; use camino::{Utf8Path, Utf8PathBuf}; @@ -566,6 +566,12 @@ pub(crate) fn setup_composefs_bls_boot( } }; + // Remove "root=" from kernel cmdline as systemd-auto-gpt-generator should use DPS + // UUID + if bootloader == Bootloader::Systemd { + cmdline_refs.remove(&ParameterKey::from("root")); + } + let is_upgrade = matches!(setup_type, BootSetupType::Upgrade(..)); let current_root = if is_upgrade { diff --git a/crates/xtask/src/tmt.rs b/crates/xtask/src/tmt.rs index e26751bbe..3cd9af19e 100644 --- a/crates/xtask/src/tmt.rs +++ b/crates/xtask/src/tmt.rs @@ -32,7 +32,7 @@ const DISTRO_CENTOS_9: &str = "centos-9"; const COMPOSEFS_KERNEL_ARGS: [&str; 1] = ["--karg=enforcing=0"]; // Import the argument types from xtask.rs -use crate::{RunTmtArgs, TmtProvisionArgs}; +use crate::{Bootloader, RunTmtArgs, TmtProvisionArgs}; /// Generate a random alphanumeric suffix for VM names fn generate_random_suffix() -> String { @@ -111,7 +111,11 @@ const DEFAULT_SB_KEYS_DIR: &str = "target/test-secureboot"; /// /// For sealed images, secure boot keys must be present or an error is returned. #[context("Building firmware arguments")] -fn build_firmware_args(sh: &Shell, image: &str) -> Result> { +fn build_firmware_args( + sh: &Shell, + image: &str, + bootloader: &Option, +) -> Result> { let is_sealed = is_sealed_image(sh, image)?; let sb_keys_dir = Utf8Path::new(DEFAULT_SB_KEYS_DIR); @@ -133,6 +137,8 @@ fn build_firmware_args(sh: &Shell, image: &str) -> Result> { sb_keys_dir ); } + } else if matches!(bootloader, Some(Bootloader::Systemd)) { + vec!["--firmware=uefi-insecure".into()] } else { Vec::new() }; @@ -310,7 +316,7 @@ pub(crate) fn run_tmt(sh: &Shell, args: &RunTmtArgs) -> Result<()> { println!("Detected distro: {}", distro); println!("Detected VARIANT_ID: {variant_id}"); - let firmware_args = build_firmware_args(sh, image)?; + let firmware_args = build_firmware_args(sh, image, &args.bootloader)?; // Create tmt-workdir and copy tmt bits to it // This works around https://github.com/teemtee/tmt/issues/4062 @@ -699,7 +705,8 @@ pub(crate) fn tmt_provision(sh: &Shell, args: &TmtProvisionArgs) -> Result<()> { println!(" Image: {}", image); println!(" VM name: {}\n", vm_name); - let firmware_args = build_firmware_args(sh, image)?; + // TODO: Send bootloader param here + let firmware_args = build_firmware_args(sh, image, &None)?; // Launch VM with bcvk // Use ds=iid-datasource-none to disable cloud-init for faster boot diff --git a/crates/xtask/src/xtask.rs b/crates/xtask/src/xtask.rs index 6886f7fc9..cb6afe29f 100644 --- a/crates/xtask/src/xtask.rs +++ b/crates/xtask/src/xtask.rs @@ -79,7 +79,7 @@ pub(crate) struct LocalRustDepsArgs { /// Bootloader passed as --bootloader param for composefs builds // TODO: Find a better way to share this Enum between this and crates/lib -#[derive(Debug, Clone, ValueEnum)] +#[derive(Debug, Clone, ValueEnum, PartialEq, Eq)] pub enum Bootloader { /// grub as bootloader Grub, diff --git a/tmt/tests/booted/readonly/030-test-composefs.nu b/tmt/tests/booted/readonly/030-test-composefs.nu index 81e2acc4b..8ac4a3574 100644 --- a/tmt/tests/booted/readonly/030-test-composefs.nu +++ b/tmt/tests/booted/readonly/030-test-composefs.nu @@ -13,15 +13,21 @@ let is_composefs = (tap is_composefs) let expecting_composefs = ($env.BOOTC_variant? | default "" | find "composefs") != null if $expecting_composefs { assert $is_composefs - # When using systemd-boot with DPS (Discoverable Partition Specification), - # /proc/cmdline should NOT contain a root= parameter because systemd-gpt-auto-generator - # discovers the root partition automatically - # Note that there is `bootctl --json=pretty` but it doesn't actually output JSON - let bootctl_output = (bootctl) - if ($bootctl_output | str contains 'Product: systemd-boot') { - let cmdline = parse_cmdline - let has_root_param = ($cmdline | any { |param| $param | str starts-with 'root=' }) - assert (not $has_root_param) "systemd-boot image should not have root= in kernel cmdline; systemd-gpt-auto-generator should discover the root partition via DPS" + + let bootloader = ($st.status.booted.composefs.bootloader | str downcase) + + if $bootloader == "systemd" { + # When using systemd-boot with DPS (Discoverable Partition Specification), + # /proc/cmdline should NOT contain a root= parameter because systemd-gpt-auto-generator + # discovers the root partition automatically + # Note that there is `bootctl --json=pretty` but it doesn't actually output JSON + let bootctl_output = (bootctl) + + if ($bootctl_output | str contains 'Product: systemd-boot') { + let cmdline = parse_cmdline + let has_root_param = ($cmdline | any { |param| $param | str starts-with 'root=' }) + assert (not $has_root_param) "systemd-boot image should not have root= in kernel cmdline; systemd-gpt-auto-generator should discover the root partition via DPS" + } } } diff --git a/tmt/tests/booted/test-install-outside-container.nu b/tmt/tests/booted/test-install-outside-container.nu index fb205f6af..a39429aea 100644 --- a/tmt/tests/booted/test-install-outside-container.nu +++ b/tmt/tests/booted/test-install-outside-container.nu @@ -28,6 +28,16 @@ umount /var/mnt # so we mask off /sysroot/ostree # And using systemd-run here breaks our install_t so we disable SELinux enforcement setenforce 0 + +let st = bootc status --json | from json +let bootloader = ($st.status.booted.composefs.bootloader | str downcase) + +let install_cmd = if (tap is_composefs) { + $"bootc install to-disk --disable-selinux --via-loopback --composefs-backend --bootloader=($bootloader) --filesystem ext4 --source-imgref ($target_image) ./disk.img" +} else { + $"bootc install to-disk --disable-selinux --via-loopback --filesystem xfs --source-imgref ($target_image) ./disk.img" +} + systemd-run -p MountFlags=slave -qdPG -- /bin/sh -c $" set -xeuo pipefail bootc usr-overlay @@ -36,7 +46,7 @@ if test -d /sysroot/ostree; then mount --bind /usr/share/empty /sysroot/ostree; rm -vrf /usr/lib/bootupd/updates # Another bootc install bug, we should not look at this in outside-of-container flows rm -vrf /usr/lib/bootc/bound-images.d -bootc install to-disk --disable-selinux --via-loopback --filesystem xfs --source-imgref ($target_image) ./disk.img +($install_cmd) " tap ok From b5f2873b63847e79a6710040127887d9517ee5ea Mon Sep 17 00:00:00 2001 From: Pragyan Poudyal Date: Tue, 10 Feb 2026 14:45:29 +0530 Subject: [PATCH 7/8] tmt/tests: Add `works_for_composefs` meta field Use the new `works_for_composefs` field to check whether a test is going to work for a composefs booted system or not. This allows us to simply do `cargo xtask test-tmt integration` and tests will be filtered by variant Signed-off-by: Pragyan Poudyal --- Justfile | 10 +- crates/xtask/src/tmt.rs | 92 ++++++++++++++++--- tmt/plans/integration.fmf | 8 ++ tmt/tests/booted/test-01-readonly.nu | 3 +- .../booted/test-download-only-upgrade.nu | 2 + .../booted/test-image-pushpull-upgrade.nu | 2 + tmt/tests/booted/test-image-upgrade-reboot.nu | 1 + .../booted/test-install-outside-container.nu | 2 + .../test-install-to-filesystem-var-mount.sh | 2 + tmt/tests/booted/test-soft-reboot.nu | 2 + tmt/tests/booted/test-usroverlay.nu | 2 + 11 files changed, 103 insertions(+), 23 deletions(-) diff --git a/Justfile b/Justfile index fae9f1048..176ff81e3 100644 --- a/Justfile +++ b/Justfile @@ -112,15 +112,7 @@ test-composefs-sealeduki-sdboot: [group('core')] test-composefs bootloader: just variant=composefs bootloader={{bootloader}} \ - test-tmt --composefs-backend --bootloader {{bootloader}} \ - readonly \ - download-only \ - image-pushpull-upgrade \ - image-upgrade-reboot \ - install-outside-container \ - install-to-filesystem-var-mount \ - soft-reboot \ - usroverlay + test-tmt --composefs-backend --bootloader {{bootloader}} integration # Build and test composefs images booted using Type1 boot entries and systemd-boot as the bootloader [group('core')] diff --git a/crates/xtask/src/tmt.rs b/crates/xtask/src/tmt.rs index 3cd9af19e..edadad412 100644 --- a/crates/xtask/src/tmt.rs +++ b/crates/xtask/src/tmt.rs @@ -22,6 +22,8 @@ const FIELD_TRY_BIND_STORAGE: &str = "try_bind_storage"; const FIELD_SUMMARY: &str = "summary"; const FIELD_ADJUST: &str = "adjust"; +const FIELD_WORKS_FOR_COMPOSEFS: &str = "works_for_composefs"; + // bcvk options const BCVK_OPT_BIND_STORAGE_RO: &str = "--bind-storage-ro"; const ENV_BOOTC_UPGRADE_IMAGE: &str = "BOOTC_upgrade_image"; @@ -247,9 +249,17 @@ fn verify_ssh_connectivity(sh: &Shell, port: u16, key_path: &Utf8Path) -> Result ) } +#[derive(Debug)] +struct PlanMetadata { + try_bind_storage: bool, + works_for_composefs: bool, +} + /// Parse integration.fmf to extract extra-try_bind_storage for all plans #[context("Parsing integration.fmf")] -fn parse_plan_metadata(plans_file: &Utf8Path) -> Result> { +fn parse_plan_metadata( + plans_file: &Utf8Path, +) -> Result> { let content = std::fs::read_to_string(plans_file)?; let yaml = serde_yaml::from_str::(&content) .context("Failed to parse integration.fmf YAML")?; @@ -258,7 +268,8 @@ fn parse_plan_metadata(plans_file: &Utf8Path) -> Result = + std::collections::HashMap::new(); for (key, value) in mapping { let Some(plan_name) = key.as_str() else { @@ -271,12 +282,34 @@ fn parse_plan_metadata(plans_file: &Utf8Path) -> Result Result<()> { .filter(|line| !line.is_empty() && line.starts_with("/")) .collect(); + let original_plans_count = plans.len(); + // Filter plans based on user arguments if !filter_args.is_empty() { - let original_count = plans.len(); plans.retain(|plan| filter_args.iter().any(|arg| plan.contains(arg.as_str()))); - if plans.len() < original_count { - println!( - "Filtered from {} to {} plan(s) based on arguments: {:?}", - original_count, - plans.len(), - filter_args - ); - } + } + + if args.composefs_backend { + plans.retain(|plan| { + plan_metadata + .iter() + .find(|(key, _)| plan.ends_with(key.as_str())) + .map(|(_, v)| v.works_for_composefs) + .unwrap_or(false) + }); + } + + if plans.len() < original_plans_count { + println!( + "Filtered from {} to {} plan(s) based on arguments: {:?}", + original_plans_count, + plans.len(), + filter_args + ); } if plans.is_empty() { @@ -412,7 +457,7 @@ pub(crate) fn run_tmt(sh: &Shell, args: &RunTmtArgs) -> Result<()> { let try_bind_storage = plan_metadata .iter() .find(|(key, _)| plan.ends_with(key.as_str())) - .map(|(_, &v)| v) + .map(|(_, v)| v.try_bind_storage) .unwrap_or(false); let mut opts = Vec::new(); @@ -863,6 +908,8 @@ struct TestDef { test_command: String, /// Whether this test wants to try bind storage (if distro supports it) try_bind_storage: bool, + /// Whether this test will work for composefs backend + works_for_composefs: bool, /// TMT fmf attributes to pass through (summary, duration, adjust, etc.) tmt: serde_yaml::Value, } @@ -953,11 +1000,23 @@ pub(crate) fn update_integration() -> Result<()> { .and_then(|v| v.as_bool()) .unwrap_or(false); + let works_for_composefs = metadata + .extra + .as_mapping() + .and_then(|m| { + m.get(&serde_yaml::Value::String( + FIELD_WORKS_FOR_COMPOSEFS.to_string(), + )) + }) + .and_then(|v| v.as_bool()) + .unwrap_or(false); + tests.push(TestDef { number: metadata.number, name: display_name, test_command, try_bind_storage, + works_for_composefs, tmt: metadata.tmt, }); } @@ -1076,6 +1135,13 @@ pub(crate) fn update_integration() -> Result<()> { ); } + if test.works_for_composefs { + plan_value.insert( + serde_yaml::Value::String(format!("extra-{}", FIELD_WORKS_FOR_COMPOSEFS)), + serde_yaml::Value::Bool(true), + ); + } + plans_mapping.insert( serde_yaml::Value::String(plan_key), serde_yaml::Value::Mapping(plan_value), diff --git a/tmt/plans/integration.fmf b/tmt/plans/integration.fmf index 3d00102a7..7ec0b7671 100644 --- a/tmt/plans/integration.fmf +++ b/tmt/plans/integration.fmf @@ -49,6 +49,7 @@ execute: test: - /tmt/tests/tests/test-01-readonly extra-try_bind_storage: true + extra-works_for_composefs: true /plan-20-image-pushpull-upgrade: summary: Execute local upgrade tests @@ -56,6 +57,7 @@ execute: how: fmf test: - /tmt/tests/tests/test-20-image-pushpull-upgrade + extra-works_for_composefs: true /plan-21-logically-bound-switch: summary: Execute logically bound images tests for switching images @@ -77,6 +79,7 @@ execute: how: fmf test: - /tmt/tests/tests/test-23-install-outside-container + extra-works_for_composefs: true /plan-23-usroverlay: summary: Execute tests for bootc usrover @@ -84,6 +87,7 @@ execute: how: fmf test: - /tmt/tests/tests/test-23-usroverlay + extra-works_for_composefs: true /plan-24-image-upgrade-reboot: summary: Execute local upgrade tests @@ -92,6 +96,7 @@ execute: test: - /tmt/tests/tests/test-24-image-upgrade-reboot extra-try_bind_storage: true + extra-works_for_composefs: true /plan-25-soft-reboot: summary: Execute soft reboot test @@ -99,6 +104,7 @@ execute: how: fmf test: - /tmt/tests/tests/test-25-soft-reboot + extra-works_for_composefs: true /plan-26-download-only-upgrade: summary: Execute download-only upgrade tests @@ -106,6 +112,7 @@ execute: how: fmf test: - /tmt/tests/tests/test-26-download-only-upgrade + extra-works_for_composefs: true /plan-27-custom-selinux-policy: summary: Execute custom selinux policy test @@ -159,6 +166,7 @@ execute: how: fmf test: - /tmt/tests/tests/test-32-install-to-filesystem-var-mount + extra-works_for_composefs: true /plan-33-bib-build: summary: Test building a qcow2 disk image with bootc-image-builder diff --git a/tmt/tests/booted/test-01-readonly.nu b/tmt/tests/booted/test-01-readonly.nu index 68d48f406..802481ff2 100644 --- a/tmt/tests/booted/test-01-readonly.nu +++ b/tmt/tests/booted/test-01-readonly.nu @@ -1,6 +1,7 @@ # number: 1 # extra: # try_bind_storage: true +# works_for_composefs: true # tmt: # summary: Execute booted readonly/nondestructive tests # duration: 30m @@ -18,4 +19,4 @@ for test_file in $tests { nu $test_file } -tap ok \ No newline at end of file +tap ok diff --git a/tmt/tests/booted/test-download-only-upgrade.nu b/tmt/tests/booted/test-download-only-upgrade.nu index 3f2bd7611..4280362d5 100644 --- a/tmt/tests/booted/test-download-only-upgrade.nu +++ b/tmt/tests/booted/test-download-only-upgrade.nu @@ -2,6 +2,8 @@ # tmt: # summary: Execute download-only upgrade tests # duration: 40m +# extra: +# works_for_composefs: true # # This test does: # bootc image copy-to-storage diff --git a/tmt/tests/booted/test-image-pushpull-upgrade.nu b/tmt/tests/booted/test-image-pushpull-upgrade.nu index 5c5d2ee2d..24ce6b7e5 100644 --- a/tmt/tests/booted/test-image-pushpull-upgrade.nu +++ b/tmt/tests/booted/test-image-pushpull-upgrade.nu @@ -2,6 +2,8 @@ # tmt: # summary: Execute local upgrade tests # duration: 30m +# extra: +# works_for_composefs: true # # This test does: # bootc image copy-to-storage diff --git a/tmt/tests/booted/test-image-upgrade-reboot.nu b/tmt/tests/booted/test-image-upgrade-reboot.nu index 676605658..2839a401b 100644 --- a/tmt/tests/booted/test-image-upgrade-reboot.nu +++ b/tmt/tests/booted/test-image-upgrade-reboot.nu @@ -1,6 +1,7 @@ # number: 24 # extra: # try_bind_storage: true +# works_for_composefs: true # tmt: # summary: Execute local upgrade tests # duration: 30m diff --git a/tmt/tests/booted/test-install-outside-container.nu b/tmt/tests/booted/test-install-outside-container.nu index a39429aea..36cdeaed5 100644 --- a/tmt/tests/booted/test-install-outside-container.nu +++ b/tmt/tests/booted/test-install-outside-container.nu @@ -2,6 +2,8 @@ # tmt: # summary: Execute tests for installing outside of a container # duration: 30m +# extra: +# works_for_composefs: true # use std assert use tap.nu diff --git a/tmt/tests/booted/test-install-to-filesystem-var-mount.sh b/tmt/tests/booted/test-install-to-filesystem-var-mount.sh index ed3529885..303f3c04a 100644 --- a/tmt/tests/booted/test-install-to-filesystem-var-mount.sh +++ b/tmt/tests/booted/test-install-to-filesystem-var-mount.sh @@ -1,4 +1,6 @@ # number: 32 +# extra: +# works_for_composefs: true # tmt: # summary: Test bootc install to-filesystem with separate /var mount # duration: 30m diff --git a/tmt/tests/booted/test-soft-reboot.nu b/tmt/tests/booted/test-soft-reboot.nu index 2b3a81316..e3f40f332 100644 --- a/tmt/tests/booted/test-soft-reboot.nu +++ b/tmt/tests/booted/test-soft-reboot.nu @@ -2,6 +2,8 @@ # tmt: # summary: Execute soft reboot test # duration: 30m +# extra: +# works_for_composefs: true # # Verify that soft reboot works (on by default) use std assert diff --git a/tmt/tests/booted/test-usroverlay.nu b/tmt/tests/booted/test-usroverlay.nu index ca68b239e..c22e7f1b0 100644 --- a/tmt/tests/booted/test-usroverlay.nu +++ b/tmt/tests/booted/test-usroverlay.nu @@ -2,6 +2,8 @@ # tmt: # summary: Execute tests for bootc usrover # duration: 30m +# extra: +# works_for_composefs: true # # Verify that bootc usroverlay works use std assert From f45724ebac1e8b64026e510d5139af24d5afd1bc Mon Sep 17 00:00:00 2001 From: Pragyan Poudyal Date: Tue, 10 Feb 2026 19:48:47 +0530 Subject: [PATCH 8/8] ci: Skip composefs tests on CentOS 9 On CentOS 9, composefs installation fails with EUCLEAN. Skipping the tests until we figure it out Avoid accessing status.booted.composefs.bootloader outside the composefs path in `test-install-outside-container` Increase DISK_IMG size to 15G in `test-install-to-filesystem-var-mount` as we need a larger ESP for composefs, and since we ate up space for that, ostree test failed with insufficient space Signed-off-by: Pragyan Poudyal --- .github/workflows/ci.yml | 6 ++++++ tmt/tests/booted/test-install-outside-container.nu | 5 ++--- tmt/tests/booted/test-install-to-filesystem-var-mount.sh | 5 +++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7f595dd0a..a87d3ea25 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -166,6 +166,12 @@ jobs: # centos-9 UKI is experimental/broken (https://github.com/bootc-dev/bootc/issues/1812) - test_os: centos-9 variant: composefs-sealeduki-sdboot + # centos-9 fails with EUCLEAN (https://github.com/bootc-dev/bootc/issues/1812) + # See: https://github.com/bootc-dev/bcvk/pull/204 + - test_os: centos-9 + variant: composefs-sdboot + - test_os: centos-9 + variant: composefs-grub runs-on: ubuntu-24.04 diff --git a/tmt/tests/booted/test-install-outside-container.nu b/tmt/tests/booted/test-install-outside-container.nu index 36cdeaed5..9d74c6b1e 100644 --- a/tmt/tests/booted/test-install-outside-container.nu +++ b/tmt/tests/booted/test-install-outside-container.nu @@ -31,10 +31,9 @@ umount /var/mnt # And using systemd-run here breaks our install_t so we disable SELinux enforcement setenforce 0 -let st = bootc status --json | from json -let bootloader = ($st.status.booted.composefs.bootloader | str downcase) - let install_cmd = if (tap is_composefs) { + let st = bootc status --json | from json + let bootloader = ($st.status.booted.composefs.bootloader | str downcase) $"bootc install to-disk --disable-selinux --via-loopback --composefs-backend --bootloader=($bootloader) --filesystem ext4 --source-imgref ($target_image) ./disk.img" } else { $"bootc install to-disk --disable-selinux --via-loopback --filesystem xfs --source-imgref ($target_image) ./disk.img" diff --git a/tmt/tests/booted/test-install-to-filesystem-var-mount.sh b/tmt/tests/booted/test-install-to-filesystem-var-mount.sh index 303f3c04a..2aac35e03 100644 --- a/tmt/tests/booted/test-install-to-filesystem-var-mount.sh +++ b/tmt/tests/booted/test-install-to-filesystem-var-mount.sh @@ -33,9 +33,10 @@ RUN rm -rf /usr/lib/bootc/bound-images.d/* EOF podman build -t "$TARGET_IMAGE" -f /tmp/Containerfile.drop-lbis -# Create a 12GB sparse disk image in /var/tmp (not /tmp which may be tmpfs) +# Create a 15GB sparse disk image in /var/tmp (not /tmp which may be tmpfs) +# Increased this as we want a larger ESP for composefs DISK_IMG=/var/tmp/disk-var-mount-test.img -truncate -s 12G "$DISK_IMG" +truncate -s 15G "$DISK_IMG" # Setup loop device LOOP_DEV=$(losetup -f --show "$DISK_IMG")