From 480c0fa9637856478f6c88740dc03aa7f011036f Mon Sep 17 00:00:00 2001 From: Gregory Giguashvili Date: Mon, 27 Apr 2026 10:02:35 +0300 Subject: [PATCH 1/3] 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 7944cfea88..d432db2bf3 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=""): @@ -452,6 +454,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) @@ -568,6 +640,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") @@ -602,7 +679,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 d4ea3fb205ff79ef191d486e81b3f645b26fba9c Mon Sep 17 00:00:00 2001 From: Gregory Giguashvili Date: Mon, 27 Apr 2026 10:03:17 +0300 Subject: [PATCH 2/3] 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/3] 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 <