diff --git a/Justfile b/Justfile index a33e9640f..13d0eca12 100644 --- a/Justfile +++ b/Justfile @@ -138,7 +138,7 @@ test-composefs bootloader filesystem boot_type seal_state: --filesystem={{filesystem}} \ --seal-state={{seal_state}} \ --boot-type={{boot_type}} \ - $(if [ "{{boot_type}}" = "uki" ]; then echo "readonly"; else echo "integration"; fi) + $(if [ "{{boot_type}}" = "uki" ]; then echo "readonly composefs-upgrade"; else echo "integration"; fi) # Run cargo fmt and clippy checks in container [group('core')] @@ -314,7 +314,24 @@ _keygen: ./hack/generate-secureboot-keys _build-upgrade-image: - cat tmt/tests/Dockerfile.upgrade | podman build -t {{upgrade_img}} --from={{base_img}} - + #!/bin/bash + set -xeuo pipefail + # Secrets are always available (test-tmt depends on build which runs _keygen). + # Extra capabilities are only needed for UKI builds (composefs + fuse). + extra_args=() + if [ "{{boot_type}}" = "uki" ]; then + extra_args+=(--cap-add=all --security-opt=label=type:container_runtime_t --device /dev/fuse) + fi + podman build \ + --build-arg boot_type={{boot_type}} \ + --build-arg seal_state={{seal_state}} \ + --build-arg filesystem={{filesystem}} \ + --secret=id=secureboot_key,src=target/test-secureboot/db.key \ + --secret=id=secureboot_cert,src=target/test-secureboot/db.crt \ + "${extra_args[@]}" \ + -t {{upgrade_img}} \ + -f tmt/tests/Dockerfile.upgrade \ + . # Copy an image from user podman storage to root's podman storage # This allows building as regular user then running privileged tests diff --git a/tmt/plans/integration.fmf b/tmt/plans/integration.fmf index ae6374690..a695fd9c8 100644 --- a/tmt/plans/integration.fmf +++ b/tmt/plans/integration.fmf @@ -182,8 +182,15 @@ execute: test: - /tmt/tests/tests/test-34-user-agent +/plan-35-upgrade-preflight-disk-check: + summary: Verify pre-flight disk space check rejects images with inflated layer sizes + discover: + how: fmf + test: + - /tmt/tests/tests/test-35-upgrade-preflight-disk-check + /plan-36-rollback: - summary: Test bootc rollback functionality through image switch and rollback cycle + summary: Test bootc rollback functionality discover: how: fmf test: @@ -202,4 +209,12 @@ execute: how: fmf test: - /tmt/tests/tests/test-38-install-bootloader-none + +/plan-39-composefs-upgrade: + summary: Test composefs upgrade with pre-built (optionally sealed) image + discover: + how: fmf + test: + - /tmt/tests/tests/test-39-composefs-upgrade + extra-try_bind_storage: true # END GENERATED PLANS diff --git a/tmt/tests/Dockerfile.upgrade b/tmt/tests/Dockerfile.upgrade index a9e36ba50..9019dcdfa 100644 --- a/tmt/tests/Dockerfile.upgrade +++ b/tmt/tests/Dockerfile.upgrade @@ -1,3 +1,62 @@ -# Just creates a file as a new layer for a synthetic upgrade test -FROM localhost/bootc -RUN touch --reference=/usr/bin/bash /usr/share/testing-bootc-upgrade-apply +# Creates a synthetic upgrade image for testing. +# For non-UKI builds, this just adds a marker file on top of localhost/bootc. +# For UKI builds (boot_type=uki), the image is re-sealed with a new composefs +# digest and (optionally signed) UKI. +# +# Build secrets required (for sealed builds): +# secureboot_key, secureboot_cert +ARG boot_type=bls +ARG seal_state=unsealed +ARG filesystem=ext4 + +# Capture contrib/packaging scripts for use in later stages +FROM scratch AS packaging +COPY contrib/packaging / + +# Create the upgrade content (a simple marker file). +# For UKI builds, we also remove the existing UKI so that seal-uki can +# regenerate it with the correct composefs digest for this derived image. +FROM localhost/bootc AS upgrade-base +ARG boot_type +RUN touch --reference=/usr/bin/bash /usr/share/testing-bootc-upgrade-apply && \ + if test "${boot_type}" = "uki"; then rm -rf /boot/EFI/Linux/*.efi; fi + +# Tools for sealing (only meaningfully used for UKI builds) +FROM localhost/bootc AS tools +RUN --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp \ + --mount=type=bind,from=packaging,src=/,target=/run/packaging \ + /run/packaging/initialize-sealing-tools + +# Generate a sealed UKI for the upgrade image. +# bootc is already installed in localhost/bootc (our tools base); the +# container ukify command it provides is needed for seal-uki. +FROM tools AS sealed-upgrade-uki +ARG boot_type seal_state filesystem +RUN --network=none --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp \ + --mount=type=secret,id=secureboot_key \ + --mount=type=secret,id=secureboot_cert \ + --mount=type=bind,from=packaging,src=/,target=/run/packaging \ + --mount=type=bind,from=upgrade-base,src=/,target=/run/target < 0) "composefs verity digest should be present" + + # For UKI boot type, verify both the original and upgrade UKIs exist on the ESP + if ($composefs_info.bootType | str downcase) == "uki" { + let bootloader = ($composefs_info.bootloader | str downcase) + + # UKIs are stored in EFI/Linux/bootc/ on the ESP + let boot_dir = if $bootloader == "systemd" { + mkdir /var/tmp/efi + mount /dev/vda2 /var/tmp/efi + "/var/tmp/efi/EFI/Linux/bootc" + } else { + "/sysroot/boot/EFI/Linux/bootc" + } + + let original_verity = (open /var/original-verity | str trim) + let upgrade_verity = $composefs_info.verity + + print $"boot_dir: ($boot_dir)" + print $"original verity: ($original_verity)" + print $"upgrade verity: ($upgrade_verity)" + + # The two verities must differ since the upgrade image has different content + assert ($original_verity != $upgrade_verity) "upgrade should produce a different verity digest" + + # There should be two .efi UKI files on the ESP: one for the booted + # deployment (upgrade) and one for the rollback (original) + let efi_files = (glob $"($boot_dir)/*.efi") + print $"EFI files: ($efi_files)" + assert ((($efi_files | length) >= 2)) $"expected at least 2 UKIs on ESP, found ($efi_files | length)" + } + + tap ok +} + +def main [] { + match $env.TMT_REBOOT_COUNT? { + null | "0" => first_boot, + "1" => second_boot, + $o => { error make { msg: $"Invalid TMT_REBOOT_COUNT ($o)" } }, + } +} diff --git a/tmt/tests/tests.fmf b/tmt/tests/tests.fmf index 851d6b293..d9a3e7de6 100644 --- a/tmt/tests/tests.fmf +++ b/tmt/tests/tests.fmf @@ -104,11 +104,11 @@ /test-35-upgrade-preflight-disk-check: summary: Verify pre-flight disk space check rejects images with inflated layer sizes - duration: 20m + duration: 10m test: nu booted/test-upgrade-preflight-disk-check.nu /test-36-rollback: - summary: Test bootc rollback functionality through image switch and rollback cycle + summary: Test bootc rollback functionality duration: 30m test: nu booted/test-rollback.nu @@ -121,3 +121,8 @@ summary: Test bootc install with --bootloader=none duration: 30m test: nu booted/test-install-bootloader-none.nu + +/test-39-composefs-upgrade: + summary: Test composefs upgrade with pre-built (optionally sealed) image + duration: 30m + test: nu booted/test-composefs-upgrade.nu