From 42e349a29b4e5fc0db8db8ead5d2231c45e8d28d Mon Sep 17 00:00:00 2001 From: Gregory Giguashvili Date: Mon, 27 Apr 2026 10:02:35 +0300 Subject: [PATCH 1/6] Introduce image-installer type support in build_bootc_images.py script --- test/bin/pyutils/build_bootc_images.py | 103 +++++++++++++++++++++---- 1 file changed, 90 insertions(+), 13 deletions(-) diff --git a/test/bin/pyutils/build_bootc_images.py b/test/bin/pyutils/build_bootc_images.py index 210881eec0..ec105bada7 100644 --- a/test/bin/pyutils/build_bootc_images.py +++ b/test/bin/pyutils/build_bootc_images.py @@ -34,6 +34,7 @@ # Switch to quay.io/centos-bootc/bootc-image-builder:latest if any new upstream # features are required BIB_IMAGE = "registry.redhat.io/rhel9/bootc-image-builder:latest" +IBC_IMAGE = "ghcr.io/osbuild/image-builder-cli:latest" GOMPLATE = common.get_env_var('GOMPLATE') MIRROR_REGISTRY = common.get_env_var('MIRROR_REGISTRY_URL') FORCE_REBUILD = False @@ -46,18 +47,19 @@ def cleanup_atexit(dry_run): common.print_msg(f"Terminating {pid} PID") common.terminate_process(pid) - # Terminate running bootc image builder containers - podman_args = [ - "sudo", "podman", "ps", - "--filter", f"ancestor={BIB_IMAGE}", - "--format", "{{.ID}}" - ] - cids = common.run_command_in_shell(podman_args, dry_run) - if cids: - # Make sure the ids are normalized in a single line - cids = re.sub(r'\s+', ' ', cids) - common.print_msg(f"Terminating '{cids}' container(s)") - common.run_command_in_shell(["sudo", "podman", "stop", cids], dry_run) + # Terminate running image builder containers + for builder_image in [BIB_IMAGE, IBC_IMAGE]: + podman_args = [ + "sudo", "podman", "ps", + "--filter", f"ancestor={builder_image}", + "--format", "{{.ID}}" + ] + cids = common.run_command_in_shell(podman_args, dry_run) + if cids: + # Make sure the ids are normalized in a single line + cids = re.sub(r'\s+', ' ', cids) + common.print_msg(f"Terminating '{cids}' container(s)") + common.run_command_in_shell(["sudo", "podman", "stop", cids], dry_run) def find_latest_rpm(repo_path, version=""): @@ -430,6 +432,76 @@ def should_skip(file): os.rename(f"{bf_outdir}/bootiso/install.iso", bf_targetiso) +def process_image_installer(groupdir, installerfile, dry_run): + ii_path, ii_outname, ii_outdir, ii_logfile = get_process_file_names( + groupdir, installerfile, BOOTC_ISO_DIR) + ii_targetiso = os.path.join(VM_DISK_BASEDIR, f"{ii_outname}.iso") + + def should_skip(file): + if FORCE_REBUILD: + common.print_msg(f"Forcing rebuild of '{file}'") + return False + if not os.path.exists(file): + return False + common.print_msg(f"The '{file}' already exists, skipping") + return True + + if should_skip(ii_targetiso): + common.record_junit(ii_path, "process-image-installer", "SKIPPED") + return + + os.makedirs(ii_outdir, exist_ok=True) + os.makedirs(VM_DISK_BASEDIR, exist_ok=True) + ii_outfile = os.path.join(BOOTC_IMAGE_DIR, installerfile) + run_template_cmd(ii_path, ii_outfile, dry_run) + if not dry_run: + if not common.file_has_valid_lines(ii_outfile): + common.print_msg(f"Skipping an empty {installerfile} file") + return + + common.print_msg(f"Processing {installerfile} with logs in {ii_logfile}") + start_process_image_installer = time.time() + try: + with open(ii_logfile, 'w') as logfile: + ii_distro = common.read_file_valid_lines(ii_outfile).strip() + + build_args = [ + "sudo", "podman", "run", + "--rm", "-i", "--privileged", + "--network", "host", + "--pull=newer", + "--security-opt", "label=type:unconfined_t", + "-v", f"{ii_outdir}:/output", + ] + if os.path.isdir("/etc/pki/entitlement"): + build_args += [ + "-v", "/etc/pki/entitlement:/etc/pki/entitlement:ro", + "-v", "/etc/rhsm:/etc/rhsm:ro", + ] + build_args += [ + IBC_IMAGE, + "build", "--distro", ii_distro, + "image-installer" + ] + start = time.time() + common.retry_on_exception(3, common.run_command_in_shell, build_args, dry_run, logfile, logfile) + common.record_junit(ii_path, "build-installer-image", "OK", start) + except Exception: + common.record_junit(ii_path, "process-image-installer", "FAILED", start_process_image_installer, log_filepath=ii_logfile) + raise + finally: + common.run_command(["sed", f"s/^/{ii_outname}: /", ii_logfile], dry_run) + + if not dry_run: + common.run_command( + ["sudo", "chown", "-R", f"{getpass.getuser()}.", ii_outdir], + dry_run) + iso_candidates = glob.glob(f"{ii_outdir}/**/*.iso", recursive=True) + if not iso_candidates: + raise Exception(f"No ISO found in {ii_outdir}") + os.rename(iso_candidates[0], ii_targetiso) + + def process_container_encapsulate(groupdir, containerfile, dry_run): ce_path, ce_outname, _, ce_logfile = get_process_file_names( groupdir, containerfile, BOOTC_IMAGE_DIR) @@ -546,6 +618,11 @@ def process_group(groupdir, build_type, pattern="*", dry_run=False): common.print_msg(f"Skipping '{file}' due to '{build_type}' filter") continue futures.append(executor.submit(process_image_bootc, groupdir, file, dry_run)) + elif file.endswith(".image-installer"): + if build_type and build_type != "image-installer": + common.print_msg(f"Skipping '{file}' due to '{build_type}' filter") + continue + futures.append(executor.submit(process_image_installer, groupdir, file, dry_run)) elif file.endswith(".container-encapsulate"): if build_type and build_type != "container-encapsulate": common.print_msg(f"Skipping '{file}' due to '{build_type}' filter") @@ -580,7 +657,7 @@ def main(): parser.add_argument("-E", "--no-extract-images", action="store_true", help="Skip container image extraction.") parser.add_argument("-X", "--skip-all-builds", action="store_true", help="Skip all image builds.") parser.add_argument("-b", "--build-type", - choices=["image-bootc", "containerfile", "container-encapsulate"], + choices=["image-bootc", "image-installer", "containerfile", "container-encapsulate"], help="Only build images of the specified type.") dirgroup = parser.add_mutually_exclusive_group(required=False) dirgroup.add_argument("-l", "--layer-dir", action="append", default=[], help="Path to the layer directory to process. Can be specified multiple times.") From fb93b24e0c16c9eb7a810d3bc4c067e179242182 Mon Sep 17 00:00:00 2001 From: Gregory Giguashvili Date: Mon, 27 Apr 2026 10:03:17 +0300 Subject: [PATCH 2/6] Add RHEL 10 RPM install presubmit tests --- .../group2/rhel102-installer.image-installer | 4 + .../el10/presubmits/el102-src@rpm-install.sh | 119 ++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 test/image-blueprints-bootc/el10/layer1-base/group2/rhel102-installer.image-installer create mode 100644 test/scenarios-bootc/el10/presubmits/el102-src@rpm-install.sh diff --git a/test/image-blueprints-bootc/el10/layer1-base/group2/rhel102-installer.image-installer b/test/image-blueprints-bootc/el10/layer1-base/group2/rhel102-installer.image-installer new file mode 100644 index 0000000000..2210caeccf --- /dev/null +++ b/test/image-blueprints-bootc/el10/layer1-base/group2/rhel102-installer.image-installer @@ -0,0 +1,4 @@ + +# TODO: Replace this by a RHEL 10.2 image when its RPM repositories are released. +# rhel-10.2 +rhel-10.1 diff --git a/test/scenarios-bootc/el10/presubmits/el102-src@rpm-install.sh b/test/scenarios-bootc/el10/presubmits/el102-src@rpm-install.sh new file mode 100644 index 0000000000..85f62bed7a --- /dev/null +++ b/test/scenarios-bootc/el10/presubmits/el102-src@rpm-install.sh @@ -0,0 +1,119 @@ +#!/bin/bash + +# Sourced from scenario.sh and uses functions defined there. + +# The RPM-based image used to create the VM for this test does not +# include MicroShift or greenboot, so tell the framework not to wait +# for greenboot to finish when creating the VM. +export SKIP_GREENBOOT=true + +# NOTE: Unlike most suites, these tests rely on being run IN ORDER to +# ensure the host is in a good state at the start of each test. We +# could have separated them and run them as separate scenarios, but +# did not want to spend the resources on a new VM. +export TEST_RANDOMIZATION=none + +configure_microshift_mirror() { + local -r repo=$1 + + # `repo` might be empty if we install microshift from rhocp + if [[ -z "${repo}" ]] ; then + return + fi + + # `repo` might be an enabled repo from a released version instead + # of a mirror. + if [[ ! "${repo}" =~ ^http ]]; then + return + fi + + local -r tmp_file=$(mktemp) + tee "${tmp_file}" >/dev/null </dev/null < Date: Mon, 27 Apr 2026 11:56:04 +0300 Subject: [PATCH 3/6] Add RHEL 9.8 RPM install presubmit tests --- .../group2/rhel98-installer.image-installer | 4 + .../el9/presubmits/el98-src@rpm-install.sh | 119 ++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 test/image-blueprints-bootc/el9/layer1-base/group2/rhel98-installer.image-installer create mode 100644 test/scenarios-bootc/el9/presubmits/el98-src@rpm-install.sh diff --git a/test/image-blueprints-bootc/el9/layer1-base/group2/rhel98-installer.image-installer b/test/image-blueprints-bootc/el9/layer1-base/group2/rhel98-installer.image-installer new file mode 100644 index 0000000000..f19d87fe99 --- /dev/null +++ b/test/image-blueprints-bootc/el9/layer1-base/group2/rhel98-installer.image-installer @@ -0,0 +1,4 @@ + +# TODO: Replace this by a RHEL 9.8 image when its RPM repositories are released. +# rhel-9.8 +rhel-9.7 diff --git a/test/scenarios-bootc/el9/presubmits/el98-src@rpm-install.sh b/test/scenarios-bootc/el9/presubmits/el98-src@rpm-install.sh new file mode 100644 index 0000000000..7b5afba9b9 --- /dev/null +++ b/test/scenarios-bootc/el9/presubmits/el98-src@rpm-install.sh @@ -0,0 +1,119 @@ +#!/bin/bash + +# Sourced from scenario.sh and uses functions defined there. + +# The RPM-based image used to create the VM for this test does not +# include MicroShift or greenboot, so tell the framework not to wait +# for greenboot to finish when creating the VM. +export SKIP_GREENBOOT=true + +# NOTE: Unlike most suites, these tests rely on being run IN ORDER to +# ensure the host is in a good state at the start of each test. We +# could have separated them and run them as separate scenarios, but +# did not want to spend the resources on a new VM. +export TEST_RANDOMIZATION=none + +configure_microshift_mirror() { + local -r repo=$1 + + # `repo` might be empty if we install microshift from rhocp + if [[ -z "${repo}" ]] ; then + return + fi + + # `repo` might be an enabled repo from a released version instead + # of a mirror. + if [[ ! "${repo}" =~ ^http ]]; then + return + fi + + local -r tmp_file=$(mktemp) + tee "${tmp_file}" >/dev/null </dev/null < Date: Fri, 10 Apr 2026 09:05:20 +0000 Subject: [PATCH 4/6] add layer5-upgrade symlinks --- test/bin/ci_phase_iso_build.sh | 3 +-- .../el10/layer5-upgrade/group1/rhel96-bootc.image-bootc | 1 + .../el10/layer5-upgrade/group1/rhel96-test-agent.containerfile | 1 + .../el10/layer5-upgrade/group1/rhel98-bootc.image-bootc | 1 + .../el10/layer5-upgrade/group1/rhel98-test-agent.containerfile | 1 + .../el10/layer5-upgrade/group2/rhel96-bootc-prel.containerfile | 1 + .../layer5-upgrade/group2/rhel96-bootc-yminus2.containerfile | 1 + .../layer5-upgrade/group2/rhel98-bootc-source.containerfile | 1 + 8 files changed, 8 insertions(+), 2 deletions(-) create mode 120000 test/image-blueprints-bootc/el10/layer5-upgrade/group1/rhel96-bootc.image-bootc create mode 120000 test/image-blueprints-bootc/el10/layer5-upgrade/group1/rhel96-test-agent.containerfile create mode 120000 test/image-blueprints-bootc/el10/layer5-upgrade/group1/rhel98-bootc.image-bootc create mode 120000 test/image-blueprints-bootc/el10/layer5-upgrade/group1/rhel98-test-agent.containerfile create mode 120000 test/image-blueprints-bootc/el10/layer5-upgrade/group2/rhel96-bootc-prel.containerfile create mode 120000 test/image-blueprints-bootc/el10/layer5-upgrade/group2/rhel96-bootc-yminus2.containerfile create mode 120000 test/image-blueprints-bootc/el10/layer5-upgrade/group2/rhel98-bootc-source.containerfile diff --git a/test/bin/ci_phase_iso_build.sh b/test/bin/ci_phase_iso_build.sh index c47f91232f..0edd72a2d7 100755 --- a/test/bin/ci_phase_iso_build.sh +++ b/test/bin/ci_phase_iso_build.sh @@ -137,8 +137,7 @@ run_bootc_image_build() { if [[ "${os}" == "el10" ]]; then # Build el9 images for upgrade tests - $(dry_run) bash -x ./bin/build_bootc_images.sh -l ./image-blueprints-bootc/el9/layer1-base - $(dry_run) bash -x ./bin/build_bootc_images.sh -l ./image-blueprints-bootc/el9/layer2-presubmit + $(dry_run) bash -x ./bin/build_bootc_images.sh -l ./image-blueprints-bootc/el10/layer5-upgrade fi if [[ "${CI_JOB_NAME}" =~ .*periodic.* ]]; then diff --git a/test/image-blueprints-bootc/el10/layer5-upgrade/group1/rhel96-bootc.image-bootc b/test/image-blueprints-bootc/el10/layer5-upgrade/group1/rhel96-bootc.image-bootc new file mode 120000 index 0000000000..6d4dd553a7 --- /dev/null +++ b/test/image-blueprints-bootc/el10/layer5-upgrade/group1/rhel96-bootc.image-bootc @@ -0,0 +1 @@ +../../../el9/layer1-base/group2/rhel96-bootc.image-bootc \ No newline at end of file diff --git a/test/image-blueprints-bootc/el10/layer5-upgrade/group1/rhel96-test-agent.containerfile b/test/image-blueprints-bootc/el10/layer5-upgrade/group1/rhel96-test-agent.containerfile new file mode 120000 index 0000000000..6f426e19b3 --- /dev/null +++ b/test/image-blueprints-bootc/el10/layer5-upgrade/group1/rhel96-test-agent.containerfile @@ -0,0 +1 @@ +../../../el9/layer1-base/group1/rhel96-test-agent.containerfile \ No newline at end of file diff --git a/test/image-blueprints-bootc/el10/layer5-upgrade/group1/rhel98-bootc.image-bootc b/test/image-blueprints-bootc/el10/layer5-upgrade/group1/rhel98-bootc.image-bootc new file mode 120000 index 0000000000..cfefc32ee4 --- /dev/null +++ b/test/image-blueprints-bootc/el10/layer5-upgrade/group1/rhel98-bootc.image-bootc @@ -0,0 +1 @@ +../../../el9/layer1-base/group2/rhel98-bootc.image-bootc \ No newline at end of file diff --git a/test/image-blueprints-bootc/el10/layer5-upgrade/group1/rhel98-test-agent.containerfile b/test/image-blueprints-bootc/el10/layer5-upgrade/group1/rhel98-test-agent.containerfile new file mode 120000 index 0000000000..15bfcb0495 --- /dev/null +++ b/test/image-blueprints-bootc/el10/layer5-upgrade/group1/rhel98-test-agent.containerfile @@ -0,0 +1 @@ +../../../el9/layer1-base/group1/rhel98-test-agent.containerfile \ No newline at end of file diff --git a/test/image-blueprints-bootc/el10/layer5-upgrade/group2/rhel96-bootc-prel.containerfile b/test/image-blueprints-bootc/el10/layer5-upgrade/group2/rhel96-bootc-prel.containerfile new file mode 120000 index 0000000000..3f9147cb31 --- /dev/null +++ b/test/image-blueprints-bootc/el10/layer5-upgrade/group2/rhel96-bootc-prel.containerfile @@ -0,0 +1 @@ +../../../el9/layer1-base/group2/rhel96-bootc-prel.containerfile \ No newline at end of file diff --git a/test/image-blueprints-bootc/el10/layer5-upgrade/group2/rhel96-bootc-yminus2.containerfile b/test/image-blueprints-bootc/el10/layer5-upgrade/group2/rhel96-bootc-yminus2.containerfile new file mode 120000 index 0000000000..a6685fa9fd --- /dev/null +++ b/test/image-blueprints-bootc/el10/layer5-upgrade/group2/rhel96-bootc-yminus2.containerfile @@ -0,0 +1 @@ +../../../el9/layer1-base/group2/rhel96-bootc-yminus2.containerfile \ No newline at end of file diff --git a/test/image-blueprints-bootc/el10/layer5-upgrade/group2/rhel98-bootc-source.containerfile b/test/image-blueprints-bootc/el10/layer5-upgrade/group2/rhel98-bootc-source.containerfile new file mode 120000 index 0000000000..2d7bf40b69 --- /dev/null +++ b/test/image-blueprints-bootc/el10/layer5-upgrade/group2/rhel98-bootc-source.containerfile @@ -0,0 +1 @@ +../../../el9/layer2-presubmit/group1/rhel98-bootc-source.containerfile \ No newline at end of file From f3c7b029069754e695a097c8bf235b62bac7137b Mon Sep 17 00:00:00 2001 From: Gregory Giguashvili Date: Wed, 29 Apr 2026 13:09:19 +0300 Subject: [PATCH 5/6] Fix references to undefined variables in rpm scenarios --- .../el10/presubmits/el102-src@rpm-install.sh | 8 ++++---- .../el9/presubmits/el98-src@rpm-install.sh | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/test/scenarios-bootc/el10/presubmits/el102-src@rpm-install.sh b/test/scenarios-bootc/el10/presubmits/el102-src@rpm-install.sh index 85f62bed7a..376d2a2b29 100644 --- a/test/scenarios-bootc/el10/presubmits/el102-src@rpm-install.sh +++ b/test/scenarios-bootc/el10/presubmits/el102-src@rpm-install.sh @@ -101,10 +101,10 @@ scenario_run_tests() { # Note that rhocp or beta dependencies repository may not yet exist # for the current version. Then, just use whatever we can get for # the previous minor version. - configure_rhocp_repo "${RHOCP_MINOR_Y}" "${MAJOR_VERSION}" "${MINOR_VERSION}" - configure_rhocp_repo "${RHOCP_MINOR_Y_BETA}" "${MAJOR_VERSION}" "${MINOR_VERSION}" - configure_rhocp_repo "${RHOCP_MINOR_Y1}" "${PREVIOUS_MAJOR_VERSION}" "${PREVIOUS_MINOR_VERSION}" - configure_rhocp_repo "${RHOCP_MINOR_Y1_BETA}" "${PREVIOUS_MAJOR_VERSION}" "${PREVIOUS_MINOR_VERSION}" + configure_rhocp_repo "${RHOCP_MINOR_Y}" 4 "${MINOR_VERSION}" + configure_rhocp_repo "${RHOCP_MINOR_Y_BETA}" 4 "${MINOR_VERSION}" + configure_rhocp_repo "${RHOCP_MINOR_Y1}" 4 "${PREVIOUS_MINOR_VERSION}" + configure_rhocp_repo "${RHOCP_MINOR_Y1_BETA}" 4 "${PREVIOUS_MINOR_VERSION}" configure_microshift_mirror "${PREVIOUS_RELEASE_REPO}" run_command_on_vm host1 "sudo subscription-manager repos --enable fast-datapath-for-rhel-10-\$(uname -m)-rpms" diff --git a/test/scenarios-bootc/el9/presubmits/el98-src@rpm-install.sh b/test/scenarios-bootc/el9/presubmits/el98-src@rpm-install.sh index 7b5afba9b9..9ff944eae3 100644 --- a/test/scenarios-bootc/el9/presubmits/el98-src@rpm-install.sh +++ b/test/scenarios-bootc/el9/presubmits/el98-src@rpm-install.sh @@ -101,10 +101,10 @@ scenario_run_tests() { # Note that rhocp or beta dependencies repository may not yet exist # for the current version. Then, just use whatever we can get for # the previous minor version. - configure_rhocp_repo "${RHOCP_MINOR_Y}" "${MAJOR_VERSION}" "${MINOR_VERSION}" - configure_rhocp_repo "${RHOCP_MINOR_Y_BETA}" "${MAJOR_VERSION}" "${MINOR_VERSION}" - configure_rhocp_repo "${RHOCP_MINOR_Y1}" "${PREVIOUS_MAJOR_VERSION}" "${PREVIOUS_MINOR_VERSION}" - configure_rhocp_repo "${RHOCP_MINOR_Y1_BETA}" "${PREVIOUS_MAJOR_VERSION}" "${PREVIOUS_MINOR_VERSION}" + configure_rhocp_repo "${RHOCP_MINOR_Y}" 4 "${MINOR_VERSION}" + configure_rhocp_repo "${RHOCP_MINOR_Y_BETA}" 4 "${MINOR_VERSION}" + configure_rhocp_repo "${RHOCP_MINOR_Y1}" 4 "${PREVIOUS_MINOR_VERSION}" + configure_rhocp_repo "${RHOCP_MINOR_Y1_BETA}" 4 "${PREVIOUS_MINOR_VERSION}" configure_microshift_mirror "${PREVIOUS_RELEASE_REPO}" run_command_on_vm host1 "sudo subscription-manager repos --enable fast-datapath-for-rhel-9-\$(uname -m)-rpms" From ee10f861f3e274dd5793687e2e439d4f8e02baca Mon Sep 17 00:00:00 2001 From: Gregory Giguashvili Date: Wed, 29 Apr 2026 18:16:47 +0300 Subject: [PATCH 6/6] Fix rhel10 rhocp repo configuration --- .../el10/presubmits/el102-src@rpm-install.sh | 49 ++++++++++++++++--- 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/test/scenarios-bootc/el10/presubmits/el102-src@rpm-install.sh b/test/scenarios-bootc/el10/presubmits/el102-src@rpm-install.sh index 376d2a2b29..0951921273 100644 --- a/test/scenarios-bootc/el10/presubmits/el102-src@rpm-install.sh +++ b/test/scenarios-bootc/el10/presubmits/el102-src@rpm-install.sh @@ -41,10 +41,40 @@ EOF rm -f "${tmp_file}" } +# On RHEL 10, rhocp and fast-datapath repos are not available via +# subscription-manager. Create repo files pointing to the RHEL 9 CDN +# using entitlement certificates as a workaround. +configure_cdn_repo() { + local -r repo_id=$1 + local -r repo_name=$2 + local -r baseurl=$3 + + local -r cert=$(run_command_on_vm host1 "ls /etc/pki/entitlement/[0-9]*.pem | grep -v '\-key.pem' | head -n1") + local -r key=$(run_command_on_vm host1 "ls /etc/pki/entitlement/[0-9]*-key.pem | head -n1") + local -r tmp_file=$(mktemp) + + tee "${tmp_file}" >/dev/null <