From 78aa0138819c6f5e4ac68fbc504ec3f653c62c95 Mon Sep 17 00:00:00 2001 From: jbtrystram Date: Thu, 17 Jul 2025 17:19:08 +0200 Subject: [PATCH 1/7] osbuild: make disk usage log conditional and add tips The log disk usage message comming every 10 seconds is quite noisy, hide it when we are in a shell in osbuild. I aslo added a couple of helpful tips in comments given by @dustymabe to work with osbuild. --- src/cmd-osbuild | 7 ++++++- src/cmdlib.sh | 2 +- src/runvm-osbuild | 9 ++++++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/cmd-osbuild b/src/cmd-osbuild index 0f263bede4..4aff613fb0 100755 --- a/src/cmd-osbuild +++ b/src/cmd-osbuild @@ -408,7 +408,12 @@ main() { else cmd="runvm_with_cache" fi - $cmd -- /usr/lib/coreos-assembler/runvm-osbuild \ + + # To get a shell in the osbuild supermin VM uncomment this. + # osbuild can then be started with `bash tmp/build./cmd.sh` + # See comment about checkpoints in runvm-osbuild + # RUNVM_SHELL=1 \ + $cmd -- /usr/lib/coreos-assembler/runvm-osbuild \ --config "${runvm_osbuild_config_json}" \ --mpp "/usr/lib/coreos-assembler/osbuild-manifests/coreos.osbuild.${basearch}.mpp.yaml" \ --outdir "${outdir}" \ diff --git a/src/cmdlib.sh b/src/cmdlib.sh index 979e00f5c5..7b796d16b8 100755 --- a/src/cmdlib.sh +++ b/src/cmdlib.sh @@ -787,7 +787,7 @@ rc=0 if [ -z "${RUNVM_SHELL:-}" ]; then (cd ${workdir}; bash ${tmp_builddir}/cmd.sh |& tee /dev/virtio-ports/cosa-cmdout) || rc=\$? else - (cd ${workdir}; bash) + (cd ${workdir}; RUNVM_SHELL=${RUNVM_SHELL:-} bash) fi echo \$rc > ${rc_file} if [ -n "\${cachedev}" ]; then diff --git a/src/runvm-osbuild b/src/runvm-osbuild index 5193db3eb3..dcc0feabf8 100755 --- a/src/runvm-osbuild +++ b/src/runvm-osbuild @@ -117,11 +117,18 @@ set -x; osbuild-mpp \ "${mppyaml}" "${processed_json}" set +x -log_disk_usage +if [[ -z "${RUNVM_SHELL:-}" ]]; then + log_disk_usage +fi # Build the image set -x # shellcheck disable=SC2068 +# To stop osbuild at a given stage to inspect the state of +# things you can add `--break stage_id` to the following. +# eg : `--break org.osbuild.bootc.install-to-filesystem` +# The osbuild environnement is set up under `/run/osbuild` +# Use it in conjuction with `RUNVM_SHELL=1 in `cmd-osbuild` osbuild \ --out "$outdir" \ --store "$storedir" \ From 6ce8e88473b88e7f15a67b413fa8f3ff5ea2aa76 Mon Sep 17 00:00:00 2001 From: jbtrystram Date: Tue, 29 Jul 2025 11:38:17 +0200 Subject: [PATCH 2/7] supermin: add ostree prepare-root config file Bootc is looking for the prepare-root config file in the buildroot environnement because the main assumption is that it's run from the target container. However, in osbuild, it's run from te buildroot, because podman inside bwrap (inside supermin in our case) causes issues. It's fine for RHCOS and SCOS where we use the target container as the buildroot but we cannot do that for FCOS because we require python in the buildroot. For now, insert a prepare-root file in the supermin VM (use as the buildroot for osbuild) until either : - bootc learn to look into the container for it [1] - we ship python in our images and can use them as buildroot. Another approach would be to layer python and the osbuild dependencies on top of our image and use that as the buildroot, but that would create room for packages drift (what was in the repos at build time?). At least using COSA it's easier to keep track of versions. [1] https://github.com/bootc-dev/bootc/issues/1410 --- src/supermin-init-prelude.sh | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/supermin-init-prelude.sh b/src/supermin-init-prelude.sh index e17f7ff542..d5c506c63a 100644 --- a/src/supermin-init-prelude.sh +++ b/src/supermin-init-prelude.sh @@ -87,3 +87,13 @@ touch /etc/cosa-supermin # the missing link. Hehe. update-alternatives --install /etc/alternatives/iptables iptables /usr/sbin/iptables-legacy 1 update-alternatives --install /etc/alternatives/ip6tables ip6tables /usr/sbin/ip6tables-legacy 1 + +# To build the disk image using osbuild and bootc install to-filesystem we need to +# have a prepare-root config in the build environnement for bootc to read. +# This workaround can be removed when https://github.com/bootc-dev/bootc/issues/1410 +# is fixed or we have python in all streams which allows us to use the OCI image as the buildroot. +# Note that RHCOS and SCOS use the OCI as buildroot so they should not be affected by this. +cat > /usr/lib/ostree/prepare-root.conf < Date: Tue, 28 Oct 2025 16:18:35 +0100 Subject: [PATCH 3/7] cmdlib: make a helper for `manifest.metadata` knobs Prep work to add a knob for using bootc install in osbuild. Refactor the override logic in a helper function so we can easily add those knobs down the line. --- src/cmdlib.sh | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/src/cmdlib.sh b/src/cmdlib.sh index 7b796d16b8..6a4e8f442d 100755 --- a/src/cmdlib.sh +++ b/src/cmdlib.sh @@ -148,28 +148,49 @@ yaml2json() { python3 -c 'import sys, json, yaml; json.dump(yaml.safe_load(sys.stdin), sys.stdout, sort_keys=True)' < "$1" > "$2" } -should_build_with_buildah() { - local variant manifest - if [ -n "${COSA_BUILD_WITH_BUILDAH:-}" ]; then - if [ "${COSA_BUILD_WITH_BUILDAH:-}" = 1 ]; then +# Common helper to check for features that can be enabled via an env var or +# in the manifest metadata. +_should_enable_feature() { + local env_var_name=$1 + local metadata_key=$2 + local env_var_value + # Indirect expansion + env_var_value=${!env_var_name:-} + + if [ -n "${env_var_value}" ]; then + if [ "${env_var_value}" = 1 ]; then return 0 else return 1 fi fi + + # Make sure we are in the config directory (e.g. cmd-osbuild set a different working directory). + # When called very early (e.g. cmd-fetch), configdir isn't initialized yet so we assume we are in the top + # cosa initialized dir and use `src/config`. + # We redirect the output to /dev/null to avoid the noisy `dirs` output. + set +u + pushd "${configdir:-src/config}" > /dev/null + set -u # this slightly duplicates some logic in `prepare_build`, but meh... - if [[ -f "src/config.json" ]]; then - variant="$(jq --raw-output '."coreos-assembler.config-variant"' src/config.json)" - manifest="src/config/manifest-${variant}.yaml" + if [[ -f "../config.json" ]]; then + variant="$(jq --raw-output '."coreos-assembler.config-variant"' ../config.json)" + manifest="manifest-${variant}.yaml" else - manifest="src/config/manifest.yaml" + manifest="manifest.yaml" fi - if [ "$(yq .metadata.build_with_buildah "${manifest}")" = true ]; then + if [ "$(yq ".metadata.${metadata_key}" "${manifest}")" = true ]; then + popd > /dev/null return 0 fi + popd > /dev/null return 1 } +should_use_bootc_install() { + _should_enable_feature "COSA_OSBUILD_USE_BOOTC_INSTALL" "use_bootc_install" +} + prepare_build() { preflight preflight_kvm From 9d3e465c205c6a14f205e2ecac999f63d2e59c3d Mon Sep 17 00:00:00 2001 From: jbtrystram Date: Tue, 28 Oct 2025 16:21:37 +0100 Subject: [PATCH 4/7] osbuild: use bootc install to deploy the container Instead of deploying the container to the tree then copy all the contents to the disk image, use bootc to directly manage the installation to the target filesystems. Right now this requires to use the image as the buildroot so this requires python (for osbuild). This is tracked in [1]. As we have python in rawhide now I duplicated the manifest and added a switch in the osbuild wrapper script. We can keep the manifest duplicated until we are confident to roll this to all streams. [1] https://github.com/bootc-dev/bootc/issues/1410 Requires: https://github.com/bootc-dev/bootc/pull/1460 https://github.com/bootc-dev/bootc/pull/1451 https://github.com/osbuild/osbuild/pull/2149 https://github.com/osbuild/osbuild/pull/2152 All of which have landed in osbuild-159 and bootc 1.6 --- src/cmd-osbuild | 16 +- src/cmdlib.sh | 4 + .../coreos.osbuild.x86_64.bootc.mpp.yaml | 578 ++++++++++++++++++ 3 files changed, 594 insertions(+), 4 deletions(-) create mode 100644 src/osbuild-manifests/coreos.osbuild.x86_64.bootc.mpp.yaml diff --git a/src/cmd-osbuild b/src/cmd-osbuild index 4aff613fb0..b2f2d1bebd 100755 --- a/src/cmd-osbuild +++ b/src/cmd-osbuild @@ -409,14 +409,22 @@ main() { cmd="runvm_with_cache" fi + # Use the bootc install to-filesystem manifest if applicable + bootc_suffix="" + if should_use_bootc_install; then + bootc_suffix=".bootc" + fi + + manifest_path="/usr/lib/coreos-assembler/osbuild-manifests/coreos.osbuild.${basearch}${bootc_suffix}.mpp.yaml" + # To get a shell in the osbuild supermin VM uncomment this. # osbuild can then be started with `bash tmp/build./cmd.sh` # See comment about checkpoints in runvm-osbuild # RUNVM_SHELL=1 \ - $cmd -- /usr/lib/coreos-assembler/runvm-osbuild \ - --config "${runvm_osbuild_config_json}" \ - --mpp "/usr/lib/coreos-assembler/osbuild-manifests/coreos.osbuild.${basearch}.mpp.yaml" \ - --outdir "${outdir}" \ + $cmd -- /usr/lib/coreos-assembler/runvm-osbuild \ + --config "${runvm_osbuild_config_json}" \ + --mpp "${manifest_path}" \ + --outdir "${outdir}" \ --platforms "$(IFS=,; echo "${platforms[*]}")" for platform in "${platforms[@]}"; do diff --git a/src/cmdlib.sh b/src/cmdlib.sh index 6a4e8f442d..62caa8e0e2 100755 --- a/src/cmdlib.sh +++ b/src/cmdlib.sh @@ -191,6 +191,10 @@ should_use_bootc_install() { _should_enable_feature "COSA_OSBUILD_USE_BOOTC_INSTALL" "use_bootc_install" } +should_build_with_buildah() { + _should_enable_feature "COSA_BUILD_WITH_BUILDAH" "build_with_buildah" +} + prepare_build() { preflight preflight_kvm diff --git a/src/osbuild-manifests/coreos.osbuild.x86_64.bootc.mpp.yaml b/src/osbuild-manifests/coreos.osbuild.x86_64.bootc.mpp.yaml new file mode 100644 index 0000000000..b61cfd6a58 --- /dev/null +++ b/src/osbuild-manifests/coreos.osbuild.x86_64.bootc.mpp.yaml @@ -0,0 +1,578 @@ +version: '2' +mpp-vars: + artifact_name_prefix: $artifact_name_prefix + ociarchive: $ociarchive + osname: $osname + container_imgref: $container_imgref + container_repo: $container_repo + container_tag: $container_tag + extra_kargs: $extra_kargs + metal_image_size_mb: $metal_image_size_mb + cloud_image_size_mb: $cloud_image_size_mb + bios_boot_size_mb: 1 + ppc_prep_size_mb: 4 + reserved_part_size_mb: 1 + efi_system_size_mb: 127 + boot_size_mb: 384 + sector_size: 512 + four_k_sector_size: 4096 + # Filesystem UUID and label definitions. These UUIDs + # are looked for on boot and if found replaced with + # a new random UUID to make each install unique. + boot_fs_uuid: 96d15588-3596-4b3c-adca-a2ff7279ea63 + boot_fs_label: boot + root_fs_uuid: 910678ff-f77e-4a7d-8d53-86f2ac47a823 + root_fs_label: root + # For some stages (i.e. the qemu stages) we'll use the host as + # the buildroot (i.e. COSA in most cases but sometimes just + # the actual HOST filesystem like in coreos/custom-coreos-disk-images). + # This is useful/necessary because we definitely don't include + # qemu-img or tools like zip in the actual CoreOS OS. "" here + # means to use the host as buildroot. It is worth noting that + # the host buildroot is the default if nothing is specified. + # We're still defining it here in an attempt to be explicit. + host_as_buildroot: "" + # Set the buildroot string to use for most operations here. We create + # the buildroot from the target OSTree contents so we have version + # matches. Unfortunately for FCOS there is no python so we can't + # really use FCOS as the buildroot so we'll use the host as the + # buildroot there. + buildroot: + mpp-if: osname in ['rhcos', 'scos'] + then: "name:deployed-tree" + else: + mpp-format-string: '{host_as_buildroot}' +mpp-define-images: + - id: image + sector_size: + mpp-format-int: "{sector_size}" + size: + mpp-format-string: "{metal_image_size_mb * 1024 * 1024}" + table: + uuid: 00000000-0000-4000-a000-000000000001 + label: gpt + partitions: + - name: BIOS-BOOT + type: 21686148-6449-6E6F-744E-656564454649 + bootable: true + size: + mpp-format-int: "{bios_boot_size_mb * 1024 * 1024 / sector_size}" + - name: EFI-SYSTEM + type: C12A7328-F81F-11D2-BA4B-00A0C93EC93B + size: + mpp-format-int: "{efi_system_size_mb * 1024 * 1024 / sector_size}" + - name: boot + type: 0FC63DAF-8483-4772-8E79-3D69D8477DE4 + size: + mpp-format-int: "{boot_size_mb * 1024 * 1024 / sector_size}" + - name: root + type: 0FC63DAF-8483-4772-8E79-3D69D8477DE4 + - id: image4k + sector_size: + mpp-format-int: "{four_k_sector_size}" + size: + mpp-format-string: "{metal_image_size_mb * 1024 * 1024}" + table: + uuid: 00000000-0000-4000-a000-000000000001 + label: gpt + partitions: + - name: BIOS-BOOT + type: 21686148-6449-6E6F-744E-656564454649 + bootable: true + size: + mpp-format-int: "{bios_boot_size_mb * 1024 * 1024 / four_k_sector_size}" + - name: EFI-SYSTEM + type: C12A7328-F81F-11D2-BA4B-00A0C93EC93B + size: + mpp-format-int: "{efi_system_size_mb * 1024 * 1024 / four_k_sector_size}" + - name: boot + type: 0FC63DAF-8483-4772-8E79-3D69D8477DE4 + size: + mpp-format-int: "{boot_size_mb * 1024 * 1024 / four_k_sector_size}" + - name: root + type: 0FC63DAF-8483-4772-8E79-3D69D8477DE4 +pipelines: + # If installing from container then let's pull the container file into a pipeline + - name: oci-archive + stages: + - mpp-if: ociarchive != '' + then: + type: org.osbuild.copy + inputs: + inlinefile: + type: org.osbuild.files + origin: org.osbuild.source + mpp-embed: + id: coreos.ociarchive + url: + mpp-format-string: 'file://{ociarchive}' + options: + paths: + - from: + mpp-format-string: input://inlinefile/{embedded['coreos.ociarchive']} + to: tree:///coreos.ociarchive + else: + type: org.osbuild.noop + # Construct a tree here that is a representation of the filesystem + # that you would see on a running OSTree system. i.e. instead of just + # /ostree and /sysroot at the toplevel we see /usr/ /var/ /etc/ ... that + # you would see inside an OSTree deployment. Having the plain files accessible + # allows for this pipeline to be used as a buildroot for some stages + # or as inputs for others (i.e. file_context input to the org.osbuild.selinux + # stages). This pipeline isn't actually used for built artifacts but + # to help during build. + # + # NOTE: this is only used as a buildroot on RHCOS (FCOS doesn't ship python). + - name: deployed-tree + stages: + - mpp-if: ociarchive != '' + then: + type: org.osbuild.container-deploy + inputs: + images: + type: org.osbuild.containers + origin: org.osbuild.pipeline + references: + name:oci-archive: + name: coreos.ociarchive + else: + type: org.osbuild.container-deploy + inputs: + images: + type: org.osbuild.containers-storage + origin: org.osbuild.source + mpp-resolve-images: + images: + - source: $container_repo + tag: $container_tag + - name: tree + build: + mpp-format-string: '{buildroot}' + source-epoch: 1659397331 + stages: + # Set the context of the root of the tree so that we avoid unlabeled_t files. + # https://github.com/coreos/fedora-coreos-tracker/issues/1772 + - type: org.osbuild.selinux + options: + file_contexts: input://tree/etc/selinux/targeted/contexts/files/file_contexts + target: tree:/// + inputs: + tree: + type: org.osbuild.tree + origin: org.osbuild.pipeline + references: + - name:deployed-tree + - name: raw-image + build: + mpp-format-string: '{buildroot}' + stages: + - type: org.osbuild.truncate + options: + filename: disk.img + size: + mpp-format-string: '{image.size}' + - type: org.osbuild.sfdisk + devices: + device: + type: org.osbuild.loopback + options: + filename: disk.img + options: + mpp-format-json: '{image.layout}' + - type: org.osbuild.mkfs.fat + devices: + device: + type: org.osbuild.loopback + options: + filename: disk.img + start: + mpp-format-int: '{image.layout[''EFI-SYSTEM''].start}' + size: + mpp-format-int: '{image.layout[''EFI-SYSTEM''].size}' + lock: true + options: + label: EFI-SYSTEM + volid: 7B7795E7 + - type: org.osbuild.mkfs.ext4 + devices: + device: + type: org.osbuild.loopback + options: + filename: disk.img + start: + mpp-format-int: '{image.layout[''boot''].start}' + size: + mpp-format-int: '{image.layout[''boot''].size}' + lock: true + options: + uuid: + mpp-format-string: '{boot_fs_uuid}' + label: + mpp-format-string: '{boot_fs_label}' + # Set manually the metadata_csum_seed ext4 option otherwise changing the + # filesystem UUID while it's mounted doesn't work. Can remove this when + # metadata_csum_seed is default in RHEL, which can be checked by looking + # in /etc/mke2fs.conf. + metadata_csum_seed: true + - type: org.osbuild.mkfs.xfs + devices: + device: + type: org.osbuild.loopback + options: + filename: disk.img + start: + mpp-format-int: '{image.layout[''root''].start}' + size: + mpp-format-int: '{image.layout[''root''].size}' + lock: true + options: + uuid: + mpp-format-string: '{root_fs_uuid}' + label: + mpp-format-string: '{root_fs_label}' + # We've created the filesystems. Now let's create the mountpoints (directories) + # on the filesystems and label them with appropriate SELinux labels. This also + # covers things like filesystem autogenerated files like 'lost+found'. The labeling + # will happen once with just the root filesystem mounted and once with the boot + # filesystem mounted too (to make sure we get all potentially hidden mountpoints). + # https://github.com/coreos/fedora-coreos-tracker/issues/1771 + - type: org.osbuild.mkdir + options: + paths: + - path: mount://root/boot + mode: 493 + - path: mount://boot/efi + mode: 493 + devices: + disk: + type: org.osbuild.loopback + options: + filename: disk.img + partscan: true + mounts: + - name: root + type: org.osbuild.xfs + source: disk + partition: + mpp-format-int: '{image.layout[''root''].partnum}' + target: /root-mount-point + - name: boot + type: org.osbuild.ext4 + source: disk + partition: + mpp-format-int: '{image.layout[''boot''].partnum}' + target: /boot-mount-point + # Use bootc install to-filesystem to install the ostree content from the container image + # inside our disc image + - type: org.osbuild.bootc.install-to-filesystem + inputs: + images: + mpp-if: ociarchive != '' + then: + type: org.osbuild.containers + origin: org.osbuild.pipeline + references: + name:oci-archive: + name: coreos.ociarchive + else: + type: org.osbuild.containers-storage + origin: org.osbuild.source + mpp-resolve-images: + images: + - source: $container_repo + tag: $container_tag + options: + kernel-args: + - '$ignition_firstboot' + - mpp-format-string: '{extra_kargs}' + target-imgref: + mpp-format-string: '{container_imgref}' + stateroot: + mpp-format-string: '{osname}' + # Empty strings mean mount spec kargs are ommited entirely. + # See github.com/bootc-dev/bootc/issues/1441 + boot-mount-spec: "" + root-mount-spec: "" + devices: + disk: + type: org.osbuild.loopback + options: + filename: disk.img + partscan: true + mounts: + - name: root + type: org.osbuild.xfs + source: disk + partition: + mpp-format-int: '{image.layout[''root''].partnum}' + target: / + - name: boot + type: org.osbuild.ext4 + source: disk + partition: + mpp-format-int: '{image.layout[''boot''].partnum}' + target: /boot + - name: efi + type: org.osbuild.fat + source: disk + partition: + mpp-format-int: '{image.layout[''EFI-SYSTEM''].partnum}' + target: /boot/efi + # set up the `ignition.firstboot` stamp at the end because + # bootc want empty filesystems + - type: org.osbuild.ignition + options: + target: mount://boot/ + devices: + disk: + type: org.osbuild.loopback + options: + filename: disk.img + partscan: true + mounts: + - name: root + type: org.osbuild.xfs + source: disk + partition: + mpp-format-int: '{image.layout[''root''].partnum}' + target: / + - name: boot + type: org.osbuild.ext4 + source: disk + partition: + mpp-format-int: '{image.layout[''boot''].partnum}' + target: /boot + - name: raw-4k-image + build: + mpp-format-string: '{buildroot}' + stages: + - type: org.osbuild.truncate + options: + filename: disk.img + size: + mpp-format-string: '{image4k.size}' + - type: org.osbuild.sfdisk + devices: + device: + type: org.osbuild.loopback + options: + filename: disk.img + sector-size: + mpp-format-int: "{four_k_sector_size}" + options: + mpp-format-json: '{image4k.layout}' + - type: org.osbuild.mkfs.fat + devices: + device: + type: org.osbuild.loopback + options: + filename: disk.img + start: + mpp-format-int: '{image4k.layout[''EFI-SYSTEM''].start}' + size: + mpp-format-int: '{image4k.layout[''EFI-SYSTEM''].size}' + lock: true + sector-size: + mpp-format-int: "{four_k_sector_size}" + options: + label: EFI-SYSTEM + volid: 7B7795E7 + - type: org.osbuild.mkfs.ext4 + devices: + device: + type: org.osbuild.loopback + options: + filename: disk.img + start: + mpp-format-int: '{image4k.layout[''boot''].start}' + size: + mpp-format-int: '{image4k.layout[''boot''].size}' + lock: true + sector-size: + mpp-format-int: "{four_k_sector_size}" + options: + uuid: + mpp-format-string: '{boot_fs_uuid}' + label: + mpp-format-string: '{boot_fs_label}' + # Set manually the metadata_csum_seed ext4 option otherwise changing the + # filesystem UUID while it's mounted doesn't work. Can remove this when + # metadata_csum_seed is default in RHEL, which can be checked by looking + # in /etc/mke2fs.conf. + metadata_csum_seed: true + - type: org.osbuild.mkfs.xfs + devices: + device: + type: org.osbuild.loopback + options: + filename: disk.img + start: + mpp-format-int: '{image4k.layout[''root''].start}' + size: + mpp-format-int: '{image4k.layout[''root''].size}' + lock: true + sector-size: + mpp-format-int: "{four_k_sector_size}" + options: + uuid: + mpp-format-string: '{root_fs_uuid}' + label: + mpp-format-string: '{root_fs_label}' + # We've created the filesystems. Now let's create the mountpoints (directories) + # on the filesystems and label them with appropriate SELinux labels. This also + # covers things like filesystem autogenerated files like 'lost+found'. The labeling + # will happen once with just the root filesystem mounted and once with the boot + # filesystem mounted too (to make sure we get all potentially hidden mountpoints). + # https://github.com/coreos/fedora-coreos-tracker/issues/1771 + - type: org.osbuild.mkdir + options: + paths: + - path: mount://root/boot + mode: 493 + - path: mount://boot/efi + mode: 493 + devices: + disk: + type: org.osbuild.loopback + options: + filename: disk.img + partscan: true + sector-size: + mpp-format-int: "{four_k_sector_size}" + mounts: + - name: root + type: org.osbuild.xfs + source: disk + partition: + mpp-format-int: '{image4k.layout[''root''].partnum}' + target: /root-mount-point + - name: boot + type: org.osbuild.ext4 + source: disk + partition: + mpp-format-int: '{image4k.layout[''boot''].partnum}' + target: /boot-mount-point + # Use bootc install to-filesystem to install the ostree content from the container image + # inside our disc image + - type: org.osbuild.bootc.install-to-filesystem + inputs: + images: + mpp-if: ociarchive != '' + then: + type: org.osbuild.containers + origin: org.osbuild.pipeline + references: + name:oci-archive: + name: coreos.ociarchive + else: + type: org.osbuild.containers-storage + origin: org.osbuild.source + mpp-resolve-images: + images: + - source: $container_repo + tag: $container_tag + options: + kernel-args: + - '$ignition_firstboot' + - mpp-format-string: '{extra_kargs}' + target-imgref: + mpp-format-string: '{container_imgref}' + stateroot: + mpp-format-string: '{osname}' + # Empty strings mean mount spec kargs are ommited entirely. + # See github.com/bootc-dev/bootc/issues/1441 + boot-mount-spec: "" + root-mount-spec: "" + devices: + disk: + type: org.osbuild.loopback + options: + filename: disk.img + partscan: true + sector-size: + mpp-format-int: "{four_k_sector_size}" + mounts: + - name: root + type: org.osbuild.xfs + source: disk + partition: + mpp-format-int: '{image4k.layout[''root''].partnum}' + target: / + - name: boot + type: org.osbuild.ext4 + source: disk + partition: + mpp-format-int: '{image4k.layout[''boot''].partnum}' + target: /boot + - name: efi + type: org.osbuild.fat + source: disk + partition: + mpp-format-int: '{image4k.layout[''EFI-SYSTEM''].partnum}' + target: /boot/efi + # set up the `ignition.firstboot` stamp at the end because + # bootc want empty filesystems + - type: org.osbuild.ignition + options: + target: mount://boot/ + devices: + disk: + type: org.osbuild.loopback + options: + filename: disk.img + partscan: true + sector-size: + mpp-format-int: "{four_k_sector_size}" + mounts: + - name: root + type: org.osbuild.xfs + source: disk + partition: + mpp-format-int: '{image4k.layout[''root''].partnum}' + target: / + - name: boot + type: org.osbuild.ext4 + source: disk + partition: + mpp-format-int: '{image4k.layout[''boot''].partnum}' + target: /boot + - mpp-import-pipelines: + path: platform.aliyun.ipp.yaml + - mpp-import-pipelines: + path: platform.applehv.ipp.yaml + - mpp-import-pipelines: + path: platform.aws.ipp.yaml + - mpp-import-pipelines: + path: platform.azure.ipp.yaml + - mpp-import-pipelines: + path: platform.azurestack.ipp.yaml + - mpp-import-pipelines: + path: platform.digitalocean.ipp.yaml + - mpp-import-pipelines: + path: platform.exoscale.ipp.yaml + - mpp-import-pipelines: + path: platform.gcp.ipp.yaml + - mpp-import-pipelines: + path: platform.hetzner.ipp.yaml + - mpp-import-pipelines: + path: platform.hyperv.ipp.yaml + - mpp-import-pipelines: + path: platform.ibmcloud.ipp.yaml + - mpp-import-pipelines: + path: platform.kubevirt.ipp.yaml + - mpp-import-pipelines: + path: platform.openstack.ipp.yaml + - mpp-import-pipelines: + path: platform.oraclecloud.ipp.yaml + - mpp-import-pipelines: + path: platform.proxmoxve.ipp.yaml + - mpp-import-pipelines: + path: platform.metal.ipp.yaml + - mpp-import-pipelines: + path: platform.nutanix.ipp.yaml + - mpp-import-pipelines: + path: platform.qemu.ipp.yaml + - mpp-import-pipelines: + path: platform.vultr.ipp.yaml + - mpp-import-pipelines: + path: platform.live.ipp.yaml From 4cb21fb2c46c80ee3dd0d625812ec078808032ee Mon Sep 17 00:00:00 2001 From: jbtrystram Date: Tue, 21 Oct 2025 16:46:05 +0200 Subject: [PATCH 5/7] osbuild/ bootc-install: symlink aleph files Create symlinks to the aleph file created by bootc so our tests and tooling find the aleph at the expected path. Shipping the osbuild patches until [1] is merged. [1]: https://github.com/osbuild/osbuild/pull/2226 --- build.sh | 3 +- src/0001-stages-add-new-link-stage.patch | 132 ++++++++++++++++++ src/cmdlib.sh | 4 +- .../coreos.osbuild.x86_64.bootc.mpp.yaml | 46 ++++++ 4 files changed, 182 insertions(+), 3 deletions(-) create mode 100644 src/0001-stages-add-new-link-stage.patch diff --git a/build.sh b/build.sh index b46b9c886b..27abeca482 100755 --- a/build.sh +++ b/build.sh @@ -208,7 +208,8 @@ patch_osbuild() { /usr/lib/coreos-assembler/0002-osbuild-util-containers.py-rename-variable.patch \ /usr/lib/coreos-assembler/0003-osbuild-util-containers.py-drop-copy-when-using-cont.patch \ /usr/lib/coreos-assembler/0004-drop-remove_signatures-from-org.osbuild.container-de.patch \ - /usr/lib/coreos-assembler/0005-tools-osbuild-mpp-support-mpp-resolve-for-org.osbuil.patch | + /usr/lib/coreos-assembler/0005-tools-osbuild-mpp-support-mpp-resolve-for-org.osbuil.patch \ + /usr/lib/coreos-assembler/0001-stages-add-new-link-stage.patch | patch -d /usr/lib/osbuild -p1 # And then move the files back; supermin appliance creation will need it back diff --git a/src/0001-stages-add-new-link-stage.patch b/src/0001-stages-add-new-link-stage.patch new file mode 100644 index 0000000000..545155e0b5 --- /dev/null +++ b/src/0001-stages-add-new-link-stage.patch @@ -0,0 +1,132 @@ +From f53b6e1c5a8f0c26ee4f92641274a8cdba4b1d5a Mon Sep 17 00:00:00 2001 +From: jbtrystram +Date: Tue, 21 Oct 2025 13:13:57 +0200 +Subject: [PATCH 1/2] stages: add new link stage + +This introduces a new stage, `org.osbuild.ln`, for creating +links within the filesystem tree. + +This takes a list of paths, each with a `target` and a `link_name` +parameters, inspired by the copy stage. + +The target is a simple string because we want the link to be either +absolute or relative. +--- + stages/org.osbuild.ln | 31 ++++++++++++++++ + stages/org.osbuild.ln.meta.json | 66 +++++++++++++++++++++++++++++++++ + 2 files changed, 97 insertions(+) + create mode 100755 stages/org.osbuild.ln + create mode 100644 stages/org.osbuild.ln.meta.json + +diff --git a/stages/org.osbuild.ln b/stages/org.osbuild.ln +new file mode 100755 +index 00000000..8268685d +--- /dev/null ++++ b/stages/org.osbuild.ln +@@ -0,0 +1,31 @@ ++#!/usr/bin/python3 ++import os ++import sys ++ ++import osbuild.api ++from osbuild.util import parsing ++ ++ ++def main(args, options): ++ items = options["paths"] ++ ++ for path in items: ++ target = path["target"] ++ link_name = parsing.parse_location(path["link_name"], args) ++ symbolic = path.get("symbolic", False) ++ ++ print(f"Linking '{link_name}' -> '{target}'") ++ if symbolic: ++ print(f"Creating symbolic link: '{link_name}' -> '{target}'") ++ os.symlink(target, link_name) ++ else: ++ print(f"Creating hard link: '{link_name}' -> '{target}'") ++ os.link(target, link_name) ++ ++ return 0 ++ ++ ++if __name__ == '__main__': ++ _args = osbuild.api.arguments() ++ r = main(_args, _args["options"]) ++ sys.exit(r) +diff --git a/stages/org.osbuild.ln.meta.json b/stages/org.osbuild.ln.meta.json +new file mode 100644 +index 00000000..81f297be +--- /dev/null ++++ b/stages/org.osbuild.ln.meta.json +@@ -0,0 +1,66 @@ ++{ ++ "summary": "Create links", ++ "description": [ ++ "Creates links within the tree or mounts. The target and link_name are specified as URLs.", ++ "Only allows tree or mounts URLs." ++ ], ++ "schema_2": { ++ "options": { ++ "additionalProperties": false, ++ "required": [ ++ "paths" ++ ], ++ "properties": { ++ "paths": { ++ "description": "Array of links to create.", ++ "type": "array", ++ "minItems": 1, ++ "items": { ++ "type": "object", ++ "additionalProperties": false, ++ "required": [ ++ "target", ++ "link_name" ++ ], ++ "properties": { ++ "target": { ++ "type": "string", ++ "description": "The target for the link, relative or absolute" ++ }, ++ "link_name": { ++ "oneOf": [ ++ { ++ "type": "string", ++ "description": "The link path, if in a mount", ++ "pattern": "^mount://[^/]+/" ++ }, ++ { ++ "type": "string", ++ "description": "The link path, if in a tree", ++ "pattern": "^tree://" ++ } ++ ] ++ }, ++ "symbolic": { ++ "type": "boolean", ++ "description": "Use a symbolic link instead of hard-linking. Defaults to False.", ++ "default": false ++ } ++ } ++ } ++ } ++ } ++ }, ++ "devices": { ++ "type": "object", ++ "additionalProperties": true ++ }, ++ "mounts": { ++ "type": "array" ++ }, ++ "inputs": { ++ "type": "object", ++ "additionalProperties": true ++ } ++ } ++} +-- +2.51.0 + diff --git a/src/cmdlib.sh b/src/cmdlib.sh index 62caa8e0e2..d046d9fb44 100755 --- a/src/cmdlib.sh +++ b/src/cmdlib.sh @@ -784,8 +784,8 @@ runvm() { # include COSA in the image find /usr/lib/coreos-assembler/ -type f > "${vmpreparedir}/hostfiles" cat <> "${vmpreparedir}/hostfiles" -/usr/lib/osbuild/stages/org.osbuild.coreos.live-artifacts.mono -/usr/lib/osbuild/stages/org.osbuild.coreos.live-artifacts.mono.meta.json +/usr/lib/osbuild/stages/org.osbuild.ln +/usr/lib/osbuild/stages/org.osbuild.ln.meta.json EOF # and include all GPG keys diff --git a/src/osbuild-manifests/coreos.osbuild.x86_64.bootc.mpp.yaml b/src/osbuild-manifests/coreos.osbuild.x86_64.bootc.mpp.yaml index b61cfd6a58..a47b0c85db 100644 --- a/src/osbuild-manifests/coreos.osbuild.x86_64.bootc.mpp.yaml +++ b/src/osbuild-manifests/coreos.osbuild.x86_64.bootc.mpp.yaml @@ -342,6 +342,28 @@ pipelines: partition: mpp-format-int: '{image.layout[''boot''].partnum}' target: /boot + - type: org.osbuild.ln + options: + paths: + - target: ".bootc-aleph.json" + link_name: "mount://root/.coreos-aleph-version.json" + symbolic: true + - target: ".bootc-aleph.json" + link_name: "mount://root/.aleph-version.json" + symbolic: true + devices: + disk: + type: org.osbuild.loopback + options: + filename: disk.img + partscan: true + mounts: + - name: root + type: org.osbuild.xfs + source: disk + partition: + mpp-format-int: '{image.layout[''root''].partnum}' + target: / - name: raw-4k-image build: mpp-format-string: '{buildroot}' @@ -536,6 +558,30 @@ pipelines: partition: mpp-format-int: '{image4k.layout[''boot''].partnum}' target: /boot + - type: org.osbuild.ln + options: + paths: + - target: ".bootc-aleph.json" + link_name: "mount://root/.coreos-aleph-version.json" + symbolic: true + - target: ".bootc-aleph.json" + link_name: "mount://root/.aleph-version.json" + symbolic: true + devices: + disk: + type: org.osbuild.loopback + options: + filename: disk.img + partscan: true + sector-size: + mpp-format-int: "{four_k_sector_size}" + mounts: + - name: root + type: org.osbuild.xfs + source: disk + partition: + mpp-format-int: '{image4k.layout[''root''].partnum}' + target: / - mpp-import-pipelines: path: platform.aliyun.ipp.yaml - mpp-import-pipelines: From 1f1067403f8f88101407f459f7a39720ddeaa05a Mon Sep 17 00:00:00 2001 From: jbtrystram Date: Mon, 27 Oct 2025 20:11:52 +0100 Subject: [PATCH 6/7] vmdeps: add filesystem to supermin this rpm provides locales under `/usr/share/locale`. grub2 install these files inside the /boot partition using what exist on the host. Mooving to bootc install results in an empty folder because those files don't exist in the buildroot, so add them. In the previous manifest we were using a chroot before calling bootupd which made sure bootupd (and by extension grub2) would pull content from the target root. It would be nicer to have bootc install also chroot before calling bootupd, it would have the added benefit to use the bootupd (and other) binaries that are shipped on the image rather than what is in the buildroot. Again, this is a complication coming from not having python in the image so we can't use ourselves as the buildroot, which would alleviate a lot of pain. See https://github.com/coreos/coreos-assembler/pull/4224#issuecomment-3452307787 And https://github.com/bootc-dev/bootc/issues/1559 --- src/vmdeps.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vmdeps.txt b/src/vmdeps.txt index 1040dcae2c..f19b7cb67b 100644 --- a/src/vmdeps.txt +++ b/src/vmdeps.txt @@ -45,3 +45,6 @@ osbuild osbuild-ostree osbuild-selinux osbuild-tools python3-pyrsistent zip # For resetting the terminal inside supermin shell /usr/bin/reset /usr/bin/clear + +# To have the locales available for grub to install +filesystem From 01b2a139fb3c626b5ba2ac4e132d2ee8709696f2 Mon Sep 17 00:00:00 2001 From: jbtrystram Date: Wed, 29 Oct 2025 12:51:48 +0100 Subject: [PATCH 7/7] osbuild/bootc: properly label the mount points Properly label the filesystem mount points before calling bootc, otherwise `/sysroot` and `/boot` end up being `unlabeled_t` --- .../coreos.osbuild.x86_64.bootc.mpp.yaml | 116 ++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/src/osbuild-manifests/coreos.osbuild.x86_64.bootc.mpp.yaml b/src/osbuild-manifests/coreos.osbuild.x86_64.bootc.mpp.yaml index a47b0c85db..9a02052907 100644 --- a/src/osbuild-manifests/coreos.osbuild.x86_64.bootc.mpp.yaml +++ b/src/osbuild-manifests/coreos.osbuild.x86_64.bootc.mpp.yaml @@ -262,6 +262,64 @@ pipelines: partition: mpp-format-int: '{image.layout[''boot''].partnum}' target: /boot-mount-point + # Set the context of the root of disk so that we avoid unlabeled_t files. + # Here we make sure to not mount the boot partition because we want to label + # the directory mount point + # https://github.com/coreos/fedora-coreos-tracker/issues/1772 + - type: org.osbuild.selinux + options: + file_contexts: input://tree/etc/selinux/targeted/contexts/files/file_contexts + target: mount://root/ + inputs: + tree: + type: org.osbuild.tree + origin: org.osbuild.pipeline + references: + - name:deployed-tree + devices: + disk: + type: org.osbuild.loopback + options: + filename: disk.img + partscan: true + mounts: + - name: root + type: org.osbuild.xfs + source: disk + partition: + mpp-format-int: '{image.layout[''root''].partnum}' + target: / + # Then we mount le boot parition and label again so the /boot/efi + # mount point is labeled properly + - type: org.osbuild.selinux + options: + file_contexts: input://tree/etc/selinux/targeted/contexts/files/file_contexts + target: mount://root/boot + inputs: + tree: + type: org.osbuild.tree + origin: org.osbuild.pipeline + references: + - name:deployed-tree + devices: + disk: + type: org.osbuild.loopback + options: + filename: disk.img + partscan: true + mounts: + - name: root + type: org.osbuild.xfs + source: disk + partition: + mpp-format-int: '{image.layout[''root''].partnum}' + target: / + - name: boot + type: org.osbuild.ext4 + source: disk + partition: + mpp-format-int: '{image.layout[''boot''].partnum}' + target: /boot # Use bootc install to-filesystem to install the ostree content from the container image # inside our disc image - type: org.osbuild.bootc.install-to-filesystem @@ -474,6 +532,64 @@ pipelines: partition: mpp-format-int: '{image4k.layout[''boot''].partnum}' target: /boot-mount-point + # Set the context of the root of disk so that we avoid unlabeled_t files. + # Here we make sure to not mount the boot partition because we want to label + # the directory mount point + # https://github.com/coreos/fedora-coreos-tracker/issues/1772 + - type: org.osbuild.selinux + options: + file_contexts: input://tree/etc/selinux/targeted/contexts/files/file_contexts + target: mount://root/ + inputs: + tree: + type: org.osbuild.tree + origin: org.osbuild.pipeline + references: + - name:deployed-tree + devices: + disk: + type: org.osbuild.loopback + options: + filename: disk.img + partscan: true + mounts: + - name: root + type: org.osbuild.xfs + source: disk + partition: + mpp-format-int: '{image.layout[''root''].partnum}' + target: / + # Then we mount le boot parition and label again so the /boot/efi + # mount point is labeled properly + - type: org.osbuild.selinux + options: + file_contexts: input://tree/etc/selinux/targeted/contexts/files/file_contexts + target: mount://root/boot + inputs: + tree: + type: org.osbuild.tree + origin: org.osbuild.pipeline + references: + - name:deployed-tree + devices: + disk: + type: org.osbuild.loopback + options: + filename: disk.img + partscan: true + mounts: + - name: root + type: org.osbuild.xfs + source: disk + partition: + mpp-format-int: '{image.layout[''root''].partnum}' + target: / + - name: boot + type: org.osbuild.ext4 + source: disk + partition: + mpp-format-int: '{image.layout[''boot''].partnum}' + target: /boot # Use bootc install to-filesystem to install the ostree content from the container image # inside our disc image - type: org.osbuild.bootc.install-to-filesystem