diff --git a/.packit.yaml b/.packit.yaml index 4fe8e5bc2b..ebb3521e15 100644 --- a/.packit.yaml +++ b/.packit.yaml @@ -123,10 +123,20 @@ _: tf_extra_params: environments: - tmt: - context: + context: &provision-context how: provision <<: *tmt-cloud-resources + # Image mode + - &image-mode + tf_extra_params: + environments: + - tmt: + context: + <<: *provision-context + image_mode: "yes" + <<: *tmt-cloud-resources + jobs: # Build released bits to stable - <<: *copr-under-teemtee @@ -176,6 +186,11 @@ jobs: identifier: provision-virtual tmt_plan: /plans/provision/virtual + - <<: *provision + <<: *image-mode + identifier: provision-image-mode-virtual + tmt_plan: /plans/provision/virtual + # Test internal plugins - <<: *test-base <<: *internal diff --git a/plans/provision/virtual.fmf b/plans/provision/virtual.fmf index ec835719ee..4601a16eee 100644 --- a/plans/provision/virtual.fmf +++ b/plans/provision/virtual.fmf @@ -60,9 +60,18 @@ adjust+: because: Disable IPv6 in CI to avoid IPv6 connections that are disabled in CI when: trigger == commit + - discover+: + filter: 'tag:provision-virtual & tag:image-mode' + environment+: + IMAGE_MODE: "yes" + when: image_mode == yes + /provision: discover+: test: "^/tests/provision" + adjust+: + - enabled: false + when: image_mode == yes /prepare: discover+: @@ -77,11 +86,20 @@ adjust+: discover+: test: "^/tests/execute" exclude: "^/tests/execute/upgrade" + adjust+: + - enabled: false + when: image_mode == yes /upgrade: discover+: test: "^/tests/execute/upgrade" + adjust+: + - enabled: false + when: image_mode == yes /the-rest: discover+: test: "^/tests/(?!provision|prepare|execute)" + adjust+: + - enabled: false + when: image_mode == yes diff --git a/tests/images.sh b/tests/images.sh index 6fb561fa17..ff91544115 100644 --- a/tests/images.sh +++ b/tests/images.sh @@ -51,6 +51,20 @@ fedora-coreos}" # combinations, just make sure the basic functionality works. TEST_VIRTUAL_IMAGES_SECONDARY="${TEST_VIRTUAL_IMAGES_SECONDARY:-fedora-42}" +# Base URL for image mode qcow2s +IMAGE_MODE_QCOW2_BASE_URL="https://artifacts.dev.testing-farm.io/images" + +# Image mode QCOW2 images, with mapping to compatible container image used for artifacts downloading +declare -A IMAGE_MODE_QCOW2_CONTAINER_MAP +IMAGE_MODE_QCOW2_CONTAINER_MAP=( + ["$IMAGE_MODE_QCOW2_BASE_URL/CentOS-Stream-9-image-mode-x86_64.qcow2"]="centos:stream9" + ["$IMAGE_MODE_QCOW2_BASE_URL/CentOS-Stream-10-image-mode-x86_64.qcow2"]="centos:stream10" + ["$IMAGE_MODE_QCOW2_BASE_URL/Fedora-44-image-mode-x86_64.qcow2"]="fedora:44" +) + +# Set of image mode virtual images to test on. +TEST_IMAGE_MODE_IMAGES="${TEST_IMAGE_MODE_IMAGES:-$(printf "%s\n" "${!IMAGE_MODE_QCOW2_CONTAINER_MAP[@]}")}" + # A couple of "is image this?" helpers, to simplify conditions. function is_fedora_rawhide () { [[ "$1" =~ ^.*fedora/rawhide[:/].* ]] && return 0 @@ -90,6 +104,7 @@ function is_centos_stream_9 () { function is_centos_stream_10 () { [[ "$1" =~ ^.*centos/stream10[:/].* ]] && return 0 [[ "$1" = "centos-stream-10" ]] && return 0 + [[ "$1" =~ CentOS-Stream-10 ]] && return 0 return 1 } @@ -130,11 +145,11 @@ function is_fedora_coreos () { } function is_fedora () { - [[ "$1" =~ ^.*fedora.* ]] && return 0 || return 1 + [[ "${1,,}" =~ ^.*fedora.* ]] && return 0 || return 1 } function is_centos () { - [[ "$1" =~ ^.*centos.* ]] && return 0 || return 1 + [[ "${1,,}" =~ ^.*centos.* ]] && return 0 || return 1 } function is_rhel () { @@ -153,6 +168,10 @@ function is_ubi_8 () { [[ "$1" =~ ^.*ubi/8.* ]] && return 0 || return 1 } +function is_image_mode () { + [[ "$1" =~ image-mode ]] +} + function test_phase_prefix () { if [ "$PROVISION_HOW" = "local" ]; then echo "[$PROVISION_HOW]" diff --git a/tests/prepare/bootc/data/.fmf/version b/tests/prepare/bootc/data/.fmf/version deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/prepare/bootc/data/.fmf/version +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/prepare/bootc/data/plans.fmf b/tests/prepare/bootc/data/plans.fmf deleted file mode 100644 index 309f3bc00e..0000000000 --- a/tests/prepare/bootc/data/plans.fmf +++ /dev/null @@ -1,26 +0,0 @@ -discover: - how: fmf - -provision: - how: virtual - -execute: - how: tmt - -/centos-stream-10: - provision+: - image: https://artifacts.dev.testing-farm.io/images/CentOS-Stream-10-image-mode-x86_64.qcow2 - - /prepare-shell: - summary: "Test prepare/shell on bootc guest - install tree package" - prepare: - how: shell - script: - - dnf -y install tree - - /prepare-install: - summary: "Test prepare/install on bootc guest - install tree package from local rpm" - prepare: - how: install - package: - - $TREE_RPM diff --git a/tests/prepare/bootc/data/test.fmf b/tests/prepare/bootc/data/test.fmf deleted file mode 100644 index 638837c843..0000000000 --- a/tests/prepare/bootc/data/test.fmf +++ /dev/null @@ -1,9 +0,0 @@ -summary: Check that tree package was installed via prepare/shell -description: | - Verify that the tree command is available after prepare/shell - executed 'dnf -y install tree' via the bootc Containerfile build. -test: | - # Verify tree is installed and working - tree --version - # Also verify the booted image shows we're running a modified image - bootc status --booted --format humanreadable diff --git a/tests/prepare/bootc/main.fmf b/tests/prepare/bootc/main.fmf deleted file mode 100644 index 92dd724ee9..0000000000 --- a/tests/prepare/bootc/main.fmf +++ /dev/null @@ -1,16 +0,0 @@ -summary: Test prepare/shell step on bootc guests -description: | - Verify that prepare/shell commands are collected and executed - via Containerfile build on bootc guests. Commands with - immediately=False are collected and applied via image rebuild - and reboot. -link: - - verifies: https://github.com/teemtee/tmt/issues/4495 -tag+: - - provision-only - - provision-virtual -require: - - centpkg - - koji - - tmt+provision-virtual -duration: 2h diff --git a/tests/prepare/bootc/test.sh b/tests/prepare/bootc/test.sh deleted file mode 100755 index 8ee3dc6c3e..0000000000 --- a/tests/prepare/bootc/test.sh +++ /dev/null @@ -1,60 +0,0 @@ -#!/bin/bash -. /usr/share/beakerlib/beakerlib.sh || exit 1 -. ../artifact/lib/common.sh || exit 1 - -rlJournalStart - rlPhaseStartSetup - rlRun "pushd data" - rlRun "run=\$(mktemp -d --tmpdir=/var/tmp/tmt)" 0 "Create run directory" - rlPhaseEnd - - # Test prepare/shell on bootc guest - should use Containerfile collection - rlPhaseStartTest "Prepare/shell on bootc guest - install tree package" - rlRun -s "tmt -dddvvv run --scratch -i $run plan --name /plans/centos-stream-10/prepare-shell" - - # Verify the prepare/shell command was collected .. - rlAssertGrep "Collected command for Containerfile" $rlRun_LOG - - # Verify the container image was built - rlAssertGrep "building container image from collected commands" $rlRun_LOG - - # Verify bootc switch was called - rlAssertGrep "switching to new image" $rlRun_LOG - - # Verify reboot happened - rlAssertGrep "rebooting to apply new image" $rlRun_LOG - - # Verify tree --version ran successfully in the test - rlAssertGrep "tree v" $rlRun_LOG - rlPhaseEnd - - # Test prepare/install on bootc guest - should install rpm via Containerfile - rlPhaseStartTest "Prepare/install on bootc guest - install tree package from rpm" - # Download tree RPM - get_koji_nvr "tree-pkg" "c10s-candidate" "stream" - rlRun "koji --profile stream download-build --arch=x86_64 $KOJI_NVR" 0 "Download tree RPM" - rlRun "TREE_RPM=$(ls tree-*.rpm)" - - # Run tmt - rlRun -s "tmt -dddvvv run -e TREE_RPM=$TREE_RPM --scratch -i $run plan --name /plans/centos-stream-10/prepare-install" - - # Verify the container image was built - rlAssertGrep "Trying to pull quay.io/testing-farm/centos-bootc:stream10" $rlRun_LOG - rlAssertGrep "package: building container image with dependencies" $rlRun_LOG - - # Verify bootc switch was called - rlAssertGrep "switching to new image" $rlRun_LOG - - # Verify reboot happened - rlAssertGrep "rebooting to apply new image" $rlRun_LOG - - # Verify tree --version ran successfully in the test - rlAssertGrep "tree v" $rlRun_LOG - rlPhaseEnd - - rlPhaseStartCleanup - rlRun "rm -rf $run" 0 "Remove run directory" - rlRun "rm -f tree-*.rpm" 0 "Remove tree rpm" - rlRun "popd" - rlPhaseEnd -rlJournalEnd diff --git a/tests/prepare/feature/crb/data/plans.fmf b/tests/prepare/feature/crb/data/plans.fmf index 25214b4455..9237971bd4 100644 --- a/tests/prepare/feature/crb/data/plans.fmf +++ b/tests/prepare/feature/crb/data/plans.fmf @@ -1,5 +1,5 @@ provision: - how: container + how: $PROVISION_HOW execute: how: tmt diff --git a/tests/prepare/feature/crb/main.fmf b/tests/prepare/feature/crb/main.fmf index 6fff5ecfac..369a078e45 100644 --- a/tests/prepare/feature/crb/main.fmf +++ b/tests/prepare/feature/crb/main.fmf @@ -3,3 +3,5 @@ description: Make sure enable/disable CRB works. tag+: - provision-container - provision-only + - provision-virtual + - image-mode diff --git a/tests/prepare/feature/crb/test.sh b/tests/prepare/feature/crb/test.sh index 5e9461159f..e2e5daee19 100755 --- a/tests/prepare/feature/crb/test.sh +++ b/tests/prepare/feature/crb/test.sh @@ -2,51 +2,67 @@ . /usr/share/beakerlib/beakerlib.sh || exit 1 . ../../../images.sh || exit 1 +CONTAINER_IMAGES="$TEST_IMAGE_PREFIX/centos/stream9/upstream:latest +$TEST_IMAGE_PREFIX/ubi/8/upstream:latest +ubi9" + rlJournalStart "CRB Feature Test" rlPhaseStartSetup rlRun "PROVISION_HOW=${PROVISION_HOW:-container}" + rlRun "IMAGE_MODE=${IMAGE_MODE:-no}" - build_container_image "centos/stream9/upstream\:latest" - build_container_image "ubi/8/upstream\:latest" - build_container_image "fedora/latest\:latest" + if [ "$PROVISION_HOW" = "container" ]; then + build_container_image "centos/stream9/upstream\:latest" + build_container_image "ubi/8/upstream\:latest" + build_container_image "fedora/latest\:latest" + rlRun "IMAGES='$CONTAINER_IMAGES'" + elif [ "$PROVISION_HOW" = "virtual" ]; then + if [ "$IMAGE_MODE" = "yes" ]; then + rlRun "IMAGES='$TEST_IMAGE_MODE_IMAGES'" + else + rlRun "IMAGES='$TEST_VIRTUAL_IMAGES'" + fi + else + rlDie "Test supported only on containers or VMs" + fi rlRun "pushd data" rlPhaseEnd - images="$TEST_IMAGE_PREFIX/centos/stream9/upstream:latest $TEST_IMAGE_PREFIX/ubi/8/upstream:latest ubi9" - # CRB - for image in $images; do - # Run the '/crb/enabled' plan, overriding the provision image. - # The plan itself contains the check (dnf repolist enabled | grep ...). - # Expecting tmt run to succeed (exit code 0) as the check should pass. - rlPhaseStartTest "Test /crb/enabled on $image" - rlRun "tmt run --all provision --how container --image $image plan --name /crb/enabled" 0 "Run /crb/enabled plan for $image" - rlPhaseEnd - - # Run the '/crb/disabled' plan, overriding the provision image. - # The plan itself contains the checks (! dnf repolist enabled ... && dnf repolist disabled ...). - # Expecting tmt run to succeed (exit code 0) as the checks should pass. - rlPhaseStartTest "Test /crb/disabled on $image" - rlRun "tmt run --all provision --how container --image $image plan --name /crb/disabled" 0 "Run /crb/disabled plan for $image" - rlPhaseEnd - - # Run the '/crb_package' only on c9s - if is_centos_stream_9 "$image"; then - rlPhaseStartTest "Test /crb/crb_package on $image" - # This plan enables CRB and tries to install a package from it. - rlRun "tmt run --all provision --how container --image $image prepare execute plan --name /crb/crb_package" 0 "Run /crb/crb_package plan for $image" + while IFS= read -r image; do + if is_fedora "$image"; then + # Test Fedora for the warning message + rlPhaseStartTest "Test warning on $image" + # Run a CRB plan (just provision, prepare and finish) on fedora and verify the warning is shown + # We expect the tmt run to succeed (exit code 0) because it's a warning, not an error. + rlRun -s "tmt run provision --how $PROVISION_HOW --image $image prepare finish plan --name /crb/enabled" 0 "Run plan on fedora and capture output" + rlAssertGrep "CRB prepare feature is supported on RHEL/CentOS-Stream 8, 9 or 10." $rlRun_LOG + rlPhaseEnd + else + # Run the '/crb/enabled' plan, overriding the provision image. + # The plan itself contains the check (dnf repolist enabled | grep ...). + # Expecting tmt run to succeed (exit code 0) as the check should pass. + rlPhaseStartTest "Test /crb/enabled on $image" + rlRun "tmt run --all provision --how $PROVISION_HOW --image $image plan --name /crb/enabled" 0 "Run /crb/enabled plan for $image" rlPhaseEnd + + # Run the '/crb/disabled' plan, overriding the provision image. + # The plan itself contains the checks (! dnf repolist enabled ... && dnf repolist disabled ...). + # Expecting tmt run to succeed (exit code 0) as the checks should pass. + rlPhaseStartTest "Test /crb/disabled on $image" + rlRun "tmt run --all provision --how $PROVISION_HOW --image $image plan --name /crb/disabled" 0 "Run /crb/disabled plan for $image" + rlPhaseEnd + + # Run the '/crb_package' only on c9s + if is_centos_stream_9 "$image"; then + rlPhaseStartTest "Test /crb/crb_package on $image" + # This plan enables CRB and tries to install a package from it. + rlRun "tmt run --all provision --how $PROVISION_HOW --image $image prepare execute plan --name /crb/crb_package" 0 "Run /crb/crb_package plan for $image" + rlPhaseEnd + fi fi - done - - # Test Fedora for the warning message - rlPhaseStartTest "Test warning on fedora:latest" - # Run a CRB plan (just provision, prepare and finish) on fedora and verify the warning is shown - # We expect the tmt run to succeed (exit code 0) because it's a warning, not an error. - rlRun -s "tmt run provision --how container --image $TEST_IMAGE_PREFIX/fedora/latest:latest prepare finish plan --name /crb/enabled" 0 "Run plan on fedora and capture output" - rlAssertGrep "CRB prepare feature is supported on RHEL/CentOS-Stream 8, 9 or 10." $rlRun_LOG - rlPhaseEnd + done <<< "$IMAGES" rlPhaseStartCleanup rlRun "popd" diff --git a/tests/prepare/feature/epel/main.fmf b/tests/prepare/feature/epel/main.fmf index 4b563482d4..3e5bd09414 100644 --- a/tests/prepare/feature/epel/main.fmf +++ b/tests/prepare/feature/epel/main.fmf @@ -3,3 +3,6 @@ description: Make sure enable/disable EPEL works. tag+: - provision-container + - provision-only + - provision-virtual + - image-mode diff --git a/tests/prepare/feature/epel/test.sh b/tests/prepare/feature/epel/test.sh index c353bbb492..4fa95abac1 100755 --- a/tests/prepare/feature/epel/test.sh +++ b/tests/prepare/feature/epel/test.sh @@ -2,44 +2,57 @@ . /usr/share/beakerlib/beakerlib.sh || exit 1 . ../../../images.sh || exit 1 +CONTAINER_IMAGES="$TEST_IMAGE_PREFIX/centos/stream9/upstream:latest +$TEST_IMAGE_PREFIX/ubi/8/upstream:latest +ubi9" + rlJournalStart rlPhaseStartSetup rlRun "PROVISION_HOW=${PROVISION_HOW:-container}" + rlRun "IMAGE_MODE=${IMAGE_MODE:-no}" - build_container_image "centos/stream9/upstream\:latest" - build_container_image "ubi/8/upstream\:latest" + if [ "$PROVISION_HOW" = "container" ]; then + build_container_image "centos/stream9/upstream\:latest" + build_container_image "ubi/8/upstream\:latest" + rlRun "IMAGES='$CONTAINER_IMAGES'" + elif [ "$PROVISION_HOW" = "virtual" ]; then + if [ "$IMAGE_MODE" = "yes" ]; then + rlRun "IMAGES='$TEST_IMAGE_MODE_IMAGES'" + else + rlRun "IMAGES='$TEST_VIRTUAL_IMAGES'" + fi + else + rlDie "Test supported only on containers or VMs" + fi rlRun "pushd data" rlPhaseEnd - images="$TEST_IMAGE_PREFIX/centos/stream9/upstream:latest $TEST_IMAGE_PREFIX/ubi/8/upstream:latest ubi9" - # EPEL - for image in $images; do - if rlIsFedora ">=42" && is_centos_7 "$image"; then - rlLogInfo "Skipping because Ansible shipped with Fedora does not support Python 3.6" - + while IFS= read -r image; do + if is_fedora "$image"; then + rlLogInfo "Skipping Fedora for testing EPEL" continue fi rlPhaseStartTest "Enable EPEL on $image" - rlRun -s "tmt -vvv run -a plan --name '/epel/enabled/default' provision --how container --image $image" + rlRun -s "tmt -vvv run -a plan --name '/epel/enabled/default' provision --how $PROVISION_HOW --image $image" rlPhaseEnd rlPhaseStartTest "Enable EPEL on $image (epel pre-installed)" - rlRun -s "tmt -vvv run -a plan --name '/epel/enabled/with-epel-preinstalled' provision --how container --image $image" + rlRun -s "tmt -vvv run -a plan --name '/epel/enabled/with-epel-preinstalled' provision --how $PROVISION_HOW --image $image" rlPhaseEnd rlPhaseStartTest "Disable EPEL on $image" - rlRun -s "tmt -vvv run -a plan --name '/epel/disabled' provision --how container --image $image" + rlRun -s "tmt -vvv run -a plan --name '/epel/disabled' provision --how $PROVISION_HOW --image $image" rlPhaseEnd if is_centos_stream_9 "$image"; then rlPhaseStartTest "Check CRB on $image" - rlRun -s "tmt -vvv run -a plan --name '/flac' provision --how container --image $image" + rlRun -s "tmt -vvv run -a plan --name '/flac' provision --how $PROVISION_HOW --image $image" rlPhaseEnd fi - done + done <<< "$IMAGES" # Environment profiles # TODO: chicken and egg: we need profile to test whether tmt can apply it, and we need tmt @@ -47,10 +60,9 @@ rlJournalStart # Once we get the tmt, we can continue with profiles and eventually enable the test below. # # rlPhaseStartTest "Enable EPEL on $image" - # rlRun -s "tmt -vvv run -a plan --name '/profile' provision --how container --image fedora" + # rlRun -s "tmt -vvv run -a plan --name '/profile' provision --how $PROVISION_HOW --image fedora" # rlPhaseEnd - rlPhaseStartCleanup rlRun "popd" rlPhaseEnd diff --git a/tests/prepare/install/data/reboot-persistence.fmf b/tests/prepare/install/data/reboot-persistence.fmf new file mode 100644 index 0000000000..9feeda338e --- /dev/null +++ b/tests/prepare/install/data/reboot-persistence.fmf @@ -0,0 +1,20 @@ +summary: Verify installed packages persist after reboot +description: | + Install packages and verify they survive a reboot. + This is critical for image mode where changes are applied + via Containerfile rebuild and bootc switch. + +prepare: + - how: install + package: + - tree + - diffutils + +execute: + how: tmt + script: | + tree --version + diff --version + if [ "$TMT_REBOOT_COUNT" == "0" ]; then + tmt-reboot + fi diff --git a/tests/prepare/install/main.fmf b/tests/prepare/install/main.fmf index 2161f49b82..2f579d5d37 100644 --- a/tests/prepare/install/main.fmf +++ b/tests/prepare/install/main.fmf @@ -10,6 +10,7 @@ tag+: - provision-only - provision-container - provision-virtual + - image-mode # TODO: what test cases are safe enough to be executed against the localhost? # - provision-local diff --git a/tests/prepare/install/test.sh b/tests/prepare/install/test.sh index 26da198678..5118619267 100755 --- a/tests/prepare/install/test.sh +++ b/tests/prepare/install/test.sh @@ -3,11 +3,17 @@ . ../../images.sh || exit 1 function fetch_downloaded_packages () { - in_subdirectory="$2" + local image="$1" + local in_subdirectory="$2" if [ ! -e $package_cache/tree.rpm ]; then + # Transform image mode qcow2 URL to a distro compatible container image for artifact download. + # We want artifacts to come from the same distribution, but using standard fedora container + # images is enough, they are smaller and rpm compatible with image mode based container images. + [ "$IMAGE_MODE" = "yes" ] && image=${IMAGE_MODE_QCOW2_CONTAINER_MAP["$image"]} + # For some reason, this command will get stuck in rlRun... - container_id="$(podman run -d $1 sleep 3600)" + container_id="$(podman run -d $image sleep 3600)" rlRun "podman exec $container_id bash -c \"set -x; \ dnf install -y 'dnf-command(download)' \ @@ -33,8 +39,13 @@ function fetch_downloaded_packages () { rlJournalStart rlPhaseStartSetup rlRun "PROVISION_HOW=${PROVISION_HOW:-container}" + rlRun "IMAGE_MODE=${IMAGE_MODE:-no}" + + if [ "$IMAGE_MODE" = "yes" ]; then + rlRun "IMAGES='$TEST_IMAGE_MODE_IMAGES'" + rlRun "SECONDARY_IMAGES=''" - if [ "$PROVISION_HOW" = "container" ]; then + elif [ "$PROVISION_HOW" = "container" ]; then rlRun "IMAGES='$TEST_CONTAINER_IMAGES'" rlRun "SECONDARY_IMAGES='$TEST_CONTAINER_IMAGES_SECONDARY'" @@ -123,6 +134,10 @@ rlJournalStart rlRun "distro=rhel-8" rlRun "package_manager=dnf" + elif is_fedora "$image"; then + rlRun "distro=fedora" + rlRun "package_manager=dnf5" + elif is_alpine "$image"; then rlRun "distro=alpine" rlRun "package_manager=apk" @@ -131,6 +146,10 @@ rlJournalStart rlFail "Cannot infer distro for image $image" fi + if is_image_mode "$image"; then + rlRun "package_manager=bootc" + fi + tmt_run="tmt -vvv -c distro=$distro run --id $run --scratch" tmt_steps="cleanup discover provision --how $PROVISION_HOW --image $image prepare" tmt="$tmt_run $tmt_steps" @@ -144,6 +163,12 @@ rlJournalStart rlAssertGrep "package manager: $package_manager$" $rlRun_LOG + if is_image_mode "$image"; then + rlAssertGrep "package: building container image with dependencies" $rlRun_LOG + rlAssertGrep "switching to new image" $rlRun_LOG + rlAssertGrep "rebooting to apply new image" $rlRun_LOG + fi + if is_ubuntu "$image" || is_debian "$image"; then # Runs 1 extra phase, to populate local caches. rlAssertGrep "summary: 3 preparations applied" $rlRun_LOG @@ -152,6 +177,23 @@ rlJournalStart fi rlPhaseEnd + # On image mode, verify packages persist after reboot + if is_image_mode "$image"; then + rlPhaseStartTest "$phase_prefix Reboot persistence" + rlRun -s "$tmt_run --all provision --how $PROVISION_HOW --image $image plan --name /reboot-persistence" + + rlAssertGrep "package: building container image with dependencies" $rlRun_LOG + rlAssertGrep "switching to new image" $rlRun_LOG + rlAssertGrep "rebooting to apply new image" $rlRun_LOG + + # should see the tree and diffutils version printed twice (before and after the test reboot) + rlAssertEquals "Verify tree available after reboot" $(grep "stdout: tree v" $rlRun_LOG | wc -l) 2 + rlAssertEquals "Verify diffutils available after reboot" $(grep "stdout: diff (GNU diffutils)" $rlRun_LOG | wc -l) 2 + + rlAssertGrep "total: 1 test passed" $rlRun_LOG + rlPhaseEnd + fi + # Here the basic functionality check ends for the secondary distros. if [[ "$SECONDARY_IMAGES" =~ "$image" ]]; then continue @@ -174,7 +216,11 @@ rlJournalStart fi rlPhaseEnd - if [ "$PROVISION_HOW" = "container" ] && rlIsFedora 43 && is_fedora_43 "$image"; then + # Limit these test cases to: + # * container provisioner - to save resources, they do not provide additional value with the virtual provisioner + # * image mode - the code handling is different in this case, so we need to make sure these cases work well + # * fedora - the tests use rpms + if ([ "$PROVISION_HOW" = "container" ] || is_image_mode "$image") && is_fedora "$image"; then rlPhaseStartTest "$phase_prefix Install downloaded packages from current directory (plan)" fetch_downloaded_packages "$image" @@ -244,6 +290,13 @@ rlJournalStart if is_centos_7 "$image"; then rlAssertGrep "stdout: no package provides tree-but-spelled-wrong" $rlRun_LOG + elif is_image_mode "$image"; then + if is_fedora "$image"; then + rlAssertGrep "stderr: No match for argument: tree-but-spelled-wrong" $rlRun_LOG + else + rlAssertGrep "stderr: Error: Unable to find a match: tree-but-spelled-wrong" $rlRun_LOG + fi + elif is_ostree "$image"; then if [ "$PROVISION_HOW" = "virtual" ]; then rlAssertGrep "stderr: No match for argument: tree-but-spelled-wrong" $rlRun_LOG @@ -273,8 +326,8 @@ rlJournalStart rlAssertGrep "stderr: Error: Unable to find a match: tree-but-spelled-wrong" $rlRun_LOG fi - rlAssertGrep "Other failed packages:" $rlRun_LOG - rlAssertGrep "tree-but-spelled-wrong" $rlRun_LOG + rlAssertGrep "Other failed packages:" $rlRun_LOG + rlAssertGrep "tree-but-spelled-wrong" $rlRun_LOG rlPhaseEnd rlPhaseStartTest "$phase_prefix Install existing and invalid packages (test)" @@ -285,6 +338,13 @@ rlJournalStart if is_centos_7 "$image"; then rlAssertGrep "stdout: no package provides tree-but-spelled-wrong" $rlRun_LOG + elif is_image_mode "$image"; then + if is_fedora "$image"; then + rlAssertGrep "stderr: No match for argument: tree-but-spelled-wrong" $rlRun_LOG + else + rlAssertGrep "stderr: Error: Unable to find a match: tree-but-spelled-wrong" $rlRun_LOG + fi + elif is_ostree "$image"; then if [ "$PROVISION_HOW" = "virtual" ]; then rlAssertGrep "stderr: No match for argument: tree-but-spelled-wrong" $rlRun_LOG @@ -314,7 +374,7 @@ rlJournalStart rlAssertGrep "stderr: Error: Unable to find a match: tree-but-spelled-wrong" $rlRun_LOG fi - rlAssertGrep "Required packages failed to install, aborting:" $rlRun_LOG + rlAssertGrep "Required packages failed to install, aborting:" $rlRun_LOG rlAssertGrep "tree-but-spelled-wrong: required by: /test-with-invalid-package" $rlRun_LOG rlPhaseEnd @@ -326,6 +386,13 @@ rlJournalStart if is_centos_7 "$image"; then rlAssertGrep "stdout: no package provides tree-but-spelled-wrong" $rlRun_LOG + elif is_image_mode "$image"; then + if is_fedora "$image"; then + rlAssertGrep "stderr: No match for argument: tree-but-spelled-wrong" $rlRun_LOG + else + rlAssertGrep "stderr: Error: Unable to find a match: tree-but-spelled-wrong" $rlRun_LOG + fi + elif is_ostree "$image"; then if [ "$PROVISION_HOW" = "virtual" ]; then rlAssertGrep "stderr: No match for argument: tree-but-spelled-wrong" $rlRun_LOG @@ -354,8 +421,8 @@ rlJournalStart else rlAssertGrep "stderr: Error: Unable to find a match: tree-but-spelled-wrong" $rlRun_LOG fi - rlAssertGrep "Other failed packages:" $rlRun_LOG - rlAssertGrep "tree-but-spelled-wrong" $rlRun_LOG + rlAssertGrep "Other failed packages:" $rlRun_LOG + rlAssertGrep "tree-but-spelled-wrong" $rlRun_LOG rlPhaseEnd rlPhaseStartTest "$phase_prefix Empty prepare install with exclude" @@ -364,7 +431,8 @@ rlJournalStart # TODO: at least copr is RH-specific, but package name escaping and debuginfo should be # possible to extend to other distros. - if (is_fedora "$image" && ! is_fedora_coreos "$image") || is_centos "$image" || is_ubi "$image"; then + # TODO: image mode copr support depends on #4748 + if ! is_image_mode "$image" && ((is_fedora "$image" && ! is_fedora_coreos "$image") || is_centos "$image" || is_ubi "$image"); then if ! is_centos_7 "$image" && ! is_ubi_8 "$image"; then rlPhaseStartTest "$phase_prefix Just enable copr" rlRun "$tmt execute plan --name copr" diff --git a/tests/prepare/recommend/main.fmf b/tests/prepare/recommend/main.fmf index 69e1a9658e..926077ce1b 100644 --- a/tests/prepare/recommend/main.fmf +++ b/tests/prepare/recommend/main.fmf @@ -8,3 +8,4 @@ tag+: - provision-container - provision-local - provision-virtual + - image-mode diff --git a/tests/prepare/recommend/test.sh b/tests/prepare/recommend/test.sh index a0d8151705..81e85c0d76 100755 --- a/tests/prepare/recommend/test.sh +++ b/tests/prepare/recommend/test.sh @@ -5,25 +5,32 @@ rlJournalStart rlPhaseStartSetup rlRun "PROVISION_HOW=${PROVISION_HOW:-container}" - rlRun "pushd data" + rlRun "IMAGE_MODE=${IMAGE_MODE:-no}" + + if [ "$PROVISION_HOW" = "container" ]; then + build_container_image "ubi/8/upstream\:latest" + build_container_image "centos/7/upstream\:latest" + build_container_image "fedora/latest/upstream\:latest" + fi - build_container_image "ubi/8/upstream\:latest" - build_container_image "centos/7/upstream\:latest" - build_container_image "fedora/latest/upstream\:latest" + rlRun "pushd data" rlPhaseEnd tmt="tmt run -vv --all --remove provision --how $PROVISION_HOW" basic="plan --name 'mixed|weird'" debuginfo="plan --name debuginfo" - # Verify against the default provision image - rlPhaseStartTest "Test the default image ($PROVISION_HOW)" - rlRun -s "$tmt $basic" - rlAssertGrep "Recommended packages failed to install, continuing regardless:" $rlRun_LOG - rlAssertGrep "forest: recommended by: /test/mixed" $rlRun_LOG - rlAssertGrep "weird-package: recommended by: /test/weird" $rlRun_LOG - rlAssertNotGrep "dconf: recommended by: /test/mixed" $rlRun_LOG - rlPhaseEnd + # Verify against the default provision image (skip for image mode, no default image) + # TODO: re-enable once default images supported with image mode, see #4806" + if [[ "$IMAGE_MODE" != "yes" ]]; then + rlPhaseStartTest "Test the default image ($PROVISION_HOW)" + rlRun -s "$tmt $basic" + rlAssertGrep "Recommended packages failed to install, continuing regardless:" $rlRun_LOG + rlAssertGrep "forest: recommended by: /test/mixed" $rlRun_LOG + rlAssertGrep "weird-package: recommended by: /test/weird" $rlRun_LOG + rlAssertNotGrep "dconf: recommended by: /test/mixed" $rlRun_LOG + rlPhaseEnd + fi # Check CentOS images for container provision if [[ "$PROVISION_HOW" == "container" ]]; then @@ -50,8 +57,21 @@ rlJournalStart done fi - # Add one extra CoreOS run for virtual provision - if [[ "$PROVISION_HOW" == "virtual" ]]; then + # Run against image mode images + if [[ "$IMAGE_MODE" == "yes" ]]; then + while IFS= read -r image; do + rlPhaseStartTest "Test $image (image-mode)" + rlRun -s "$tmt --image $image $basic" + rlAssertGrep "Recommended packages failed to install, continuing regardless:" $rlRun_LOG + rlAssertGrep "forest: recommended by: /test/mixed" $rlRun_LOG + rlAssertGrep "weird-package: recommended by: /test/weird" $rlRun_LOG + rlAssertNotGrep "dconf: recommended by: /test/mixed" $rlRun_LOG + rlPhaseEnd + done <<< "$TEST_IMAGE_MODE_IMAGES" + fi + + # Add one extra CoreOS run for virtual provision (not image mode) + if [[ "$PROVISION_HOW" == "virtual" ]] && [[ "$IMAGE_MODE" != "yes" ]]; then rlPhaseStartTest "Test fedora-coreos ($PROVISION_HOW)" rlRun -s "$tmt --image fedora-coreos $basic" rlAssertGrep "Recommended packages failed to install, continuing regardless:" $rlRun_LOG diff --git a/tests/prepare/shell/data/multiple.fmf b/tests/prepare/shell/data/multiple.fmf index 6dbc49401f..33c3bad657 100644 --- a/tests/prepare/shell/data/multiple.fmf +++ b/tests/prepare/shell/data/multiple.fmf @@ -2,4 +2,4 @@ provision: how: container execute: how: tmt - script: test -e /tmp/first && test -e /tmp/second + script: test -e $FIRST && test -e $SECOND diff --git a/tests/prepare/shell/main.fmf b/tests/prepare/shell/main.fmf index 0ebcc88f17..1d7a171fde 100644 --- a/tests/prepare/shell/main.fmf +++ b/tests/prepare/shell/main.fmf @@ -5,6 +5,7 @@ tag+: - provision-container - provision-local - provision-virtual + - image-mode # TODO Disable for now under the mock provision plan because of # https://bugzilla.redhat.com/show_bug.cgi?id=2415701 diff --git a/tests/prepare/shell/test.sh b/tests/prepare/shell/test.sh index a10e5ea6b8..d077fd9c63 100755 --- a/tests/prepare/shell/test.sh +++ b/tests/prepare/shell/test.sh @@ -1,31 +1,59 @@ #!/bin/bash . /usr/share/beakerlib/beakerlib.sh || exit 1 +. ../../images.sh || exit 1 rlJournalStart rlPhaseStartSetup rlRun "PROVISION_HOW=${PROVISION_HOW:-local}" + rlRun "IMAGE_MODE=${IMAGE_MODE:-no}" + if [ "$IMAGE_MODE" = "yes" ]; then + rlRun "IMAGES='$TEST_IMAGE_MODE_IMAGES'" + fi rlRun "pushd data" rlPhaseEnd - rlPhaseStartTest "Custom Script" - rlRun "tmt run -arv provision --how=$PROVISION_HOW plan -n custom" 0 "Prepare using a custom script" - rlPhaseEnd + while IFS= read -r image; do + if [ -n "$image" ]; then + image_opt="--image=$image" + fi - rlPhaseStartTest "Commandline Script" - rlRun "tmt run -arv provision --how=$PROVISION_HOW plan -n custom \ - prepare -h shell -s './prepare.sh'" 0 "Prepare using a custom script from cmdline" - rlPhaseEnd + assert_image_mode() { + is_image_mode "$image" || return + rlAssertGrep "building container image from collected commands" $rlRun_LOG + rlAssertGrep "switching to new image" $rlRun_LOG + rlAssertGrep "rebooting to apply new image" $rlRun_LOG + } - rlPhaseStartTest "Multiple Commandline Scripts" - rlRun "tmt run -arv provision --how=$PROVISION_HOW plans -n multiple \ - prepare -h shell -s 'touch /tmp/first' -s 'touch /tmp/second'" - rlPhaseEnd + rlPhaseStartTest "Custom Script" + rlRun -s "tmt run -arv provision --how=$PROVISION_HOW $image_opt plan -n custom" 0 "Prepare using a custom script" + assert_image_mode + rlPhaseEnd - rlPhaseStartTest "Remote Script" - rlRun -s "tmt -vvv run provision --how=$PROVISION_HOW prepare finish cleanup plan -n url" 0 "Prepare using a remote script" - rlAssertGrep "Hello world" "$rlRun_LOG" #check for the prepare script - rlAssertGrep "third" "$rlRun_LOG" # check for the finish script - rlPhaseEnd + rlPhaseStartTest "Commandline Script" + rlRun "tmt run -arv provision --how=$PROVISION_HOW $image_opt plan -n custom \ + prepare -h shell -s './prepare.sh'" 0 "Prepare using a custom script from cmdline" + assert_image_mode + rlPhaseEnd + + rlPhaseStartTest "Multiple Commandline Scripts" + # NOTE: These paths need to persist from the image and survive a reboot + # See https://developers.redhat.com/articles/2025/08/25/what-image-mode-3-way-merge + rlRun "FIRST=/usr/share/first SECOND=/usr/share/second" + rlRun -s "tmt run -arv -e FIRST=$FIRST -e SECOND=$SECOND provision --how=$PROVISION_HOW $image_opt plans -n multiple \ + prepare -h shell -s 'touch $FIRST' -s 'touch $SECOND'" + assert_image_mode + rlPhaseEnd + + # TODO: #4785 Preparing from a remote script is broken in Image Mode + if [ "$IMAGE_MODE" != "yes" ]; then + rlPhaseStartTest "Remote Script" + rlRun -s "tmt -vvv run provision --how=$PROVISION_HOW $image_opt prepare finish cleanup plan -n url" 0 "Prepare using a remote script" + rlAssertGrep "Hello world" "$rlRun_LOG" #check for the prepare script + rlAssertGrep "third" "$rlRun_LOG" # check for the finish script + assert_image_mode + rlPhaseEnd + fi + done <<< "$IMAGES" rlPhaseStartCleanup rlRun "popd" diff --git a/tmt/guest/__init__.py b/tmt/guest/__init__.py index fa636c4772..187bd6a8e7 100644 --- a/tmt/guest/__init__.py +++ b/tmt/guest/__init__.py @@ -611,6 +611,8 @@ class GuestFacts(SerializableContainer): sudo_prefix: Optional[str] = None is_ostree: Optional[bool] = None is_image_mode: Optional[bool] = None + distro_id: Optional[str] = None + distro_major_version: Optional[int] = None is_toolbox: Optional[bool] = None toolbox_container_name: Optional[str] = None is_container: Optional[bool] = None @@ -783,6 +785,15 @@ def _query_distro(self, guest: 'Guest') -> Optional[str]: ], ) + def _query_distro_id(self, guest: 'Guest') -> Optional[str]: + return self.os_release_content.get('ID') + + def _query_distro_major_version(self, guest: 'Guest') -> Optional[int]: + version_id = self.os_release_content.get('VERSION_ID') + if version_id: + return int(version_id.split('.')[0]) + return None + def _query_kernel_release(self, guest: 'Guest') -> Optional[str]: return self._query(guest, [(Command('uname', '-r'), r'(.+)')]) @@ -1054,6 +1065,8 @@ def sync(self, guest: 'Guest', *facts: str) -> None: self.sudo_prefix = self._query_sudo_prefix(guest) self.is_ostree = self._query_is_ostree(guest) self.is_image_mode = self._query_is_image_mode(guest) + self.distro_id = self._query_distro_id(guest) + self.distro_major_version = self._query_distro_major_version(guest) self.is_toolbox = self._query_is_toolbox(guest) self.toolbox_container_name = self._query_toolbox_container_name(guest) self.is_container = self._query_is_container(guest) @@ -1087,6 +1100,8 @@ def _flag(field: str, label: str) -> tuple[str, str, str]: yield _flag('is_container', 'is container') yield _flag('is_ostree', 'is ostree') yield _flag('is_image_mode', 'is image mode') + yield _value('distro_id', 'distro id') + yield _value('distro_major_version', 'distro major version') yield _flag('is_toolbox', 'is toolbox') yield _flag('has_selinux', 'selinux') yield _flag('has_systemd', 'systemd') diff --git a/tmt/steps/prepare/feature/epel-disable.yaml b/tmt/steps/prepare/feature/epel-disable.yaml index 4299158824..efde3d6315 100644 --- a/tmt/steps/prepare/feature/epel-disable.yaml +++ b/tmt/steps/prepare/feature/epel-disable.yaml @@ -2,7 +2,8 @@ # Pre-setup to install Python 3.9 on RHEL/CentOS 8 systems # This is needed because Ansible 2.17+ requires Python 3.7+ but RHEL/CentOS 8 -# ships with Python 3.6, causing "future feature annotations is not defined" errors +# ships with Python 3.6, causing "future feature annotations is not defined" errors. +# NOTE: RHEL/CentOS 8 is not supported in image mode, so dnf commands are fine here. - name: Workaround Ansible 2.17+ not supporting RHEL/CentOS 8 systems hosts: all gather_facts: false @@ -22,7 +23,7 @@ 'Red Hat Enterprise Linux release 8.' in release_out.stdout or 'CentOS Linux release 8' in release_out.stdout or 'CentOS Stream release 8' in release_out.stdout - changed_when: true + changed_when: false - name: Disable EPEL repositories hosts: all @@ -32,11 +33,6 @@ - ansible_distribution in ["RedHat", "CentOS"] - ansible_distribution_major_version | int == 7 block: - - name: Install package 'yum-utils' - ansible.builtin.dnf: - name: yum-utils - state: present - - name: Detect package 'epel-release' is installed ansible.builtin.command: rpm -q epel-release # noqa: command-instead-of-module register: result @@ -54,11 +50,6 @@ - ansible_distribution in ["RedHat", "CentOS"] - ansible_distribution_major_version | int >= 8 block: - - name: Install 'dnf config-manager' - ansible.builtin.command: dnf -y install 'dnf-command(config-manager)' - register: output - changed_when: output.rc != 0 - - name: Detect package 'epel-release' is installed ansible.builtin.command: rpm -q epel-release # noqa: command-instead-of-module register: result @@ -72,7 +63,7 @@ changed_when: output.rc != 0 - name: Detect package 'epel-next-release' is installed - ansible.builtin.command: rpm -q epel-release # noqa: command-instead-of-module + ansible.builtin.command: rpm -q epel-next-release # noqa: command-instead-of-module register: result ignore_errors: true changed_when: false diff --git a/tmt/steps/prepare/feature/epel-enable.yaml b/tmt/steps/prepare/feature/epel-enable.yaml index 12ec66f00c..6f5a8d62e7 100644 --- a/tmt/steps/prepare/feature/epel-enable.yaml +++ b/tmt/steps/prepare/feature/epel-enable.yaml @@ -2,7 +2,8 @@ # Pre-setup to install Python 3.9 on RHEL/CentOS 8 systems # This is needed because Ansible 2.17+ requires Python 3.7+ but RHEL/CentOS 8 -# ships with Python 3.6, causing "future feature annotations is not defined" errors +# ships with Python 3.6, causing "future feature annotations is not defined" errors. +# NOTE: RHEL/CentOS 8 is not supported in image mode, so dnf commands are fine here. - name: Workaround Ansible 2.17+ not supporting RHEL/CentOS 8 systems hosts: all gather_facts: false @@ -32,17 +33,6 @@ - ansible_distribution == "RedHat" - ansible_distribution_major_version | int == 7 block: - - name: Install package 'epel-release' - ansible.builtin.dnf: - name: "https://dl.fedoraproject.org/pub/epel/epel-release-latest-{{ ansible_distribution_major_version }}.noarch.rpm" # yamllint disable rule:line-length - disable_gpg_check: true - state: present - - - name: Install package 'yum-utils' - ansible.builtin.dnf: - name: yum-utils - state: present - - name: Enable EPEL repos ansible.builtin.command: yum-config-manager --enable epel epel-debuginfo epel-source register: output @@ -53,47 +43,15 @@ - ansible_distribution == "RedHat" - ansible_distribution_major_version | int >= 8 block: - - name: Install package 'epel-release' - ansible.builtin.dnf: - name: "https://dl.fedoraproject.org/pub/epel/epel-release-latest-{{ ansible_distribution_major_version }}.noarch.rpm" # yamllint disable rule:line-length - disable_gpg_check: true - state: present - when: ansible_distribution_major_version | int >= 9 - - - name: Install package 'epel-release' - ansible.builtin.shell: | - set -e - if ! rpm -q epel-release; then - rpm -ivh https://dl.fedoraproject.org/pub/epel/epel-release-latest-{{ ansible_distribution_major_version }}.noarch.rpm - echo "CHANGED" - fi - # The dnf python module is not available with python3.9 installed on the host, do not use ansible.builtin.dnf module - when: ansible_distribution_major_version | int == 8 - register: output - changed_when: "'CHANGED' in output.stdout" - - - - name: Install package 'epel-next-release' - ansible.builtin.dnf: - name: "https://dl.fedoraproject.org/pub/epel/epel-next-release-latest-{{ ansible_distribution_major_version }}.noarch.rpm" # yamllint disable rule:line-length - disable_gpg_check: true - state: present - # EPEL Next is available for CentOS Stream 9 - # Enable for Stream 10 once epel-next is available - when: ansible_distribution_major_version | int == 9 - - - name: Install 'dnf config-manager' - ansible.builtin.command: dnf -y install 'dnf-command(config-manager)' - register: output - changed_when: output.rc != 0 - - name: Enable EPEL repos ansible.builtin.command: dnf config-manager --enable epel epel-debuginfo epel-source register: output changed_when: output.rc != 0 - name: Enable EPEL-Next repos - ansible.builtin.command: dnf config-manager --enable epel-next epel-next-debuginfo epel-next-source + ansible.builtin.command: >- + dnf config-manager --enable epel-next + epel-next-debuginfo epel-next-source register: output changed_when: output.rc != 0 # EPEL Next is available for CentOS Stream 9 @@ -103,7 +61,8 @@ - name: Enable the crb repository ansible.builtin.command: crb enable environment: - # Skips subscription-manager usage, which is generally not configured in test environment + # Skips subscription-manager usage, which is generally + # not configured in test environment FORCE_DNF: "1" register: output changed_when: output.rc != 0 @@ -113,18 +72,10 @@ - ansible_distribution == "CentOS" - ansible_distribution_major_version | int == 7 block: - - name: Install package 'epel-release' - ansible.builtin.dnf: - name: epel-release - state: present - - - name: Install package 'yum-utils' - ansible.builtin.dnf: - name: yum-utils - state: present - - name: Enable EPEL repos - ansible.builtin.command: yum-config-manager --enable epel epel-debuginfo epel-source + ansible.builtin.command: >- + yum-config-manager --enable epel + epel-debuginfo epel-source register: output changed_when: output.rc != 0 @@ -133,31 +84,17 @@ - ansible_distribution == "CentOS" - ansible_distribution_major_version | int >= 8 block: - - name: Install package 'epel-release' - ansible.builtin.dnf: - name: epel-release - state: present - - - name: Install package 'epel-next-release' - ansible.builtin.dnf: - name: epel-next-release - state: present - # EPEL Next is available for CentOS Stream 9 - # Enable for Stream 10 once epel-next is available - when: ansible_distribution_major_version | int == 9 - - - name: Install 'dnf config-manager' - ansible.builtin.command: dnf -y install 'dnf-command(config-manager)' - register: output - changed_when: output.rc != 0 - - name: Enable EPEL repos - ansible.builtin.command: dnf config-manager --enable epel epel-debuginfo epel-source + ansible.builtin.command: >- + dnf config-manager --enable epel + epel-debuginfo epel-source register: output changed_when: output.rc != 0 - name: Enable EPEL-Next repos - ansible.builtin.command: dnf config-manager --enable epel-next epel-next-debuginfo epel-next-source + ansible.builtin.command: >- + dnf config-manager --enable epel-next + epel-next-debuginfo epel-next-source register: output changed_when: output.rc != 0 # EPEL Next is available for CentOS Stream 9 @@ -167,7 +104,8 @@ - name: Enable the crb repository ansible.builtin.command: crb enable environment: - # Skips subscription-manager usage, which is generally not configured in test environment + # Skips subscription-manager usage, which is generally + # not configured in test environment FORCE_DNF: "1" register: output changed_when: output.rc != 0 diff --git a/tmt/steps/prepare/feature/epel.py b/tmt/steps/prepare/feature/epel.py index 800b1b3507..eb539add44 100644 --- a/tmt/steps/prepare/feature/epel.py +++ b/tmt/steps/prepare/feature/epel.py @@ -4,6 +4,7 @@ import tmt.log from tmt.container import container, field from tmt.guest import Guest +from tmt.package_managers import Options, Package, PackageUrl from tmt.steps.prepare.feature import PrepareFeatureData, ToggleableFeature, provides_feature SUPPORTED_DISTRO_PATTERNS = tuple( @@ -64,8 +65,48 @@ def enable(cls, guest: Guest, logger: tmt.log.Logger) -> None: ): logger.warning('EPEL prepare feature is supported on RHEL/CentOS-Stream 8+.') return + + distro = guest.facts.distro_id + version = guest.facts.distro_major_version + + # Install packages via package_manager instead of Ansible playbook + # to support image mode (bootc) guests with immutable /usr filesystem. + # The playbook handles only repo enable/disable operations (/etc mutations). + # https://docs.fedoraproject.org/en-US/epel/getting-started/ + if distro == 'rhel': + # RHEL uses URL-based epel-release from Fedora Project + guest.package_manager.install( + PackageUrl( + f"https://dl.fedoraproject.org/pub/epel/" + f"epel-release-latest-{version}.noarch.rpm" + ), + options=Options(allow_untrusted=True), + ) + if version == 7: + guest.package_manager.install(Package("yum-utils")) + else: + guest.package_manager.install(Package("dnf-command(config-manager)")) + if version == 9: + guest.package_manager.install(Package("epel-next-release")) + elif distro == 'centos': + # CentOS has epel-release in its default repos + guest.package_manager.install(Package("epel-release")) + if version == 7: + guest.package_manager.install(Package("yum-utils")) + else: + guest.package_manager.install(Package("dnf-command(config-manager)")) + if version == 9: + guest.package_manager.install(Package("epel-next-release")) + cls._run_playbook('enable', "epel-enable.yaml", guest, logger) @classmethod def disable(cls, guest: Guest, logger: tmt.log.Logger) -> None: + version = guest.facts.distro_major_version + + if version and version == 7: + guest.package_manager.install(Package("yum-utils")) + elif version and version >= 8: + guest.package_manager.install(Package("dnf-command(config-manager)")) + cls._run_playbook('disable', "epel-disable.yaml", guest, logger) diff --git a/tmt/steps/prepare/feature/fips-enable.yaml b/tmt/steps/prepare/feature/fips-enable.yaml index 687c96fe92..025436aadc 100644 --- a/tmt/steps/prepare/feature/fips-enable.yaml +++ b/tmt/steps/prepare/feature/fips-enable.yaml @@ -25,30 +25,6 @@ # https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/10-beta/html/security_hardening/switching-rhel-to-fips-mode --- -# Pre-setup to install Python 3.9 on RHEL/CentOS 8 systems -# This is needed because Ansible 2.17+ requires Python 3.7+ but RHEL/CentOS 8 -# ships with Python 3.6, causing "future feature annotations is not defined" errors -- name: Workaround Ansible 2.17+ not supporting RHEL/CentOS 8 systems - hosts: all - gather_facts: false - tasks: - - name: Check OS release version - ansible.builtin.raw: cat /etc/redhat-release - register: release_out - changed_when: false - - - name: Install Python 3.9 on RHEL/CentOS 8 systems - ansible.builtin.raw: | - dnf -y module enable python39 - dnf -y install python39 - args: - executable: /bin/bash - when: | - 'Red Hat Enterprise Linux release 8.' in release_out.stdout - or 'CentOS Linux release 8' in release_out.stdout - or 'CentOS Stream release 8' in release_out.stdout - changed_when: false - - name: Enable FIPS mode on RHEL 7 or RHEL/CentosStream 8, 9 or 10 hosts: all @@ -63,34 +39,6 @@ register: output changed_when: '"package prelink is not installed" not in output.stdout' - - name: Install dracut-fips and grubby on RHEL7 - ansible.builtin.command: yum install -y dracut-fips grubby # noqa: command-instead-of-module - register: output - changed_when: "output.rc == 0 and 'Nothing to do' not in output.stdout" - when: ansible_distribution_major_version | int == 7 - - - name: Install crypto-policies-scripts, dracut-fips and grubby on RHEL8 - # noqa: command-instead-of-module - ansible.builtin.command: dnf install -y crypto-policies-scripts dracut-fips grubby - register: output - changed_when: "output.rc == 0 and 'Nothing to do' not in output.stdout" - when: ansible_distribution_major_version | int == 8 - - - name: Install crypto-policies-scripts, dracut-fips and grubby on RHEL9 - ansible.builtin.dnf: - name: - - crypto-policies-scripts - - dracut-fips - - grubby - state: present - when: ansible_distribution_major_version | int == 9 - - - name: Install grubby - ansible.builtin.dnf: - name: grubby - state: present - when: ansible_distribution_major_version | int == 10 - - name: Modify bootloader block: diff --git a/tmt/steps/prepare/feature/fips.py b/tmt/steps/prepare/feature/fips.py index c55a5a4771..e2246918bb 100644 --- a/tmt/steps/prepare/feature/fips.py +++ b/tmt/steps/prepare/feature/fips.py @@ -5,6 +5,7 @@ import tmt.utils from tmt.container import container, field from tmt.guest import Guest +from tmt.package_managers import Package from tmt.steps.prepare.feature import PrepareFeatureData, ToggleableFeature, provides_feature SUPPORTED_DISTRO_PATTERNS = tuple( @@ -61,7 +62,7 @@ def disable(cls, guest: Guest, logger: tmt.log.Logger) -> None: @classmethod def enable(cls, guest: Guest, logger: tmt.log.Logger) -> None: - if guest.facts.is_ostree or guest.facts.is_container: + if guest.facts.is_container or (guest.facts.is_ostree and not guest.facts.is_image_mode): raise tmt.utils.GeneralError( 'FIPS prepare feature is not supported on ostree or container systems.' ) @@ -72,4 +73,26 @@ def enable(cls, guest: Guest, logger: tmt.log.Logger) -> None: raise tmt.utils.GeneralError( 'FIPS prepare feature is supported on RHEL 7 and RHEL/CentOS-Stream 8, 9 or 10.' ) + + # Install packages via package_manager instead of Ansible playbook + # to support image mode (bootc) guests with immutable /usr filesystem. + # The playbook handles only /etc and /var mutations (bootloader config, + # crypto policies, prelink, reboot and verification). + # + # RHEL 7: dracut-fips + grubby, then grubby sets fips=1 boot param + # RHEL/CentOS 8-9: crypto-policies-scripts + dracut-fips + grubby, + # then fips-mode-setup --enable + # RHEL/CentOS 10+: grubby only, then grubby sets fips=1 boot param + # (fips-mode-setup is no longer available on RHEL/CentOS 10) + version = guest.facts.distro_major_version + + if version == 7: + guest.package_manager.install(Package("dracut-fips"), Package("grubby")) + elif version in (8, 9): + guest.package_manager.install( + Package("crypto-policies-scripts"), Package("dracut-fips"), Package("grubby") + ) + elif version is not None and version >= 10: + guest.package_manager.install(Package("grubby")) + cls._run_playbook('enable', 'fips-enable.yaml', guest, logger)