diff --git a/build.sh b/build.sh index e73a1944a0..c112aa7a91 100755 --- a/build.sh +++ b/build.sh @@ -190,12 +190,15 @@ patch_osbuild() { ## Now all the software is under the /usr/lib/osbuild dir and we can patch #cat foo.patch | patch -d /usr/lib/osbuild -p1 patch -d /usr/lib/osbuild -p1 < /usr/lib/coreos-assembler/0001-live-artifacts-read-os-name-from-usr-lib-os-release.patch + patch -d /usr/lib/osbuild -p1 < /usr/lib/coreos-assembler/0001-stages-ignition-parametrize-the-path-to-boot.patch + patch -d /usr/lib/osbuild -p1 < /usr/lib/coreos-assembler/0005-stages-bootc.install-to-filesystem-parametrize-state.patch + patch -d /usr/lib/osbuild -p1 < /usr/lib/coreos-assembler/0006-stages-bootc.install-make-boot-and-root-mount-spec-c.patch ## And then move the files back; supermin appliance creation will need it back ## in the places delivered by the RPM. mv /usr/lib/osbuild/tools/osbuild-mpp /usr/bin/osbuild-mpp mv /usr/lib/osbuild/osbuild /usr/lib/python3.13/site-packages/osbuild - mkdir /usr/lib/osbuild/osbuild + mkdir -p /usr/lib/osbuild/osbuild } if [ $# -ne 0 ]; then diff --git a/src/0001-stages-ignition-parametrize-the-path-to-boot.patch b/src/0001-stages-ignition-parametrize-the-path-to-boot.patch new file mode 100644 index 0000000000..1fe393ee93 --- /dev/null +++ b/src/0001-stages-ignition-parametrize-the-path-to-boot.patch @@ -0,0 +1,68 @@ +From f4698da5bb76e369ccf5478d65a96862562aa2bb Mon Sep 17 00:00:00 2001 +From: jbtrystram +Date: Thu, 17 Jul 2025 15:59:27 +0200 +Subject: [PATCH 1/6] stages/ignition: parametrize the path to boot + +Allow passing a mount to specify where to write the igntion.firstboot +file. +This keeps the default `tree:///` value to not break existing stages. +--- + stages/org.osbuild.ignition | 11 +++++++---- + stages/org.osbuild.ignition.meta.json | 5 +++++ + 2 files changed, 12 insertions(+), 4 deletions(-) + +diff --git a/stages/org.osbuild.ignition b/stages/org.osbuild.ignition +index 23f91c48..3e5b1903 100755 +--- a/stages/org.osbuild.ignition ++++ b/stages/org.osbuild.ignition +@@ -2,9 +2,12 @@ + import sys + + import osbuild.api ++from osbuild.util import parsing + + +-def main(tree, options): ++def main(args, options): ++ target = options.get("target", "tree:///boot") ++ location = parsing.parse_location(target, args) + network = options.get("network", []) + + # grub, when detecting the '/boot/ignition.firstboot' file +@@ -13,7 +16,7 @@ def main(tree, options): + # itself will be sourced this the 'ignition_network_kcmdline' + # that is also in the "ignition_firstboot" variable can be + # overwritten with the contents of `network` +- with open(f"{tree}/boot/ignition.firstboot", "w", encoding="utf8") as f: ++ with open(f"{location}/ignition.firstboot", "w", encoding="utf8") as f: + if network: + netstr = " ".join(network) + f.write(f"set ignition_network_kcmdline='{netstr}'") +@@ -22,6 +25,6 @@ def main(tree, options): + + + if __name__ == '__main__': +- args = osbuild.api.arguments() +- r = main(args["tree"], args.get("options", {})) ++ _args = osbuild.api.arguments() ++ r = main(_args, _args["options"]) + sys.exit(r) +diff --git a/stages/org.osbuild.ignition.meta.json b/stages/org.osbuild.ignition.meta.json +index 612d59c7..dd295c24 100644 +--- a/stages/org.osbuild.ignition.meta.json ++++ b/stages/org.osbuild.ignition.meta.json +@@ -22,6 +22,11 @@ + "items": { + "type": "string" + } ++ }, ++ "target": { ++ "type": "string", ++ "description": "Location to write the 'ignition.firstboot' file.", ++ "default": "tree:///boot" + } + } + } +-- +2.50.1 + diff --git a/src/0005-stages-bootc.install-to-filesystem-parametrize-state.patch b/src/0005-stages-bootc.install-to-filesystem-parametrize-state.patch new file mode 100644 index 0000000000..c317abf256 --- /dev/null +++ b/src/0005-stages-bootc.install-to-filesystem-parametrize-state.patch @@ -0,0 +1,65 @@ +From bb33a9fbd3d60e807aa8038e5e5e965f827cbcc7 Mon Sep 17 00:00:00 2001 +From: jbtrystram +Date: Mon, 21 Jul 2025 20:33:29 +0200 +Subject: [PATCH 5/6] stages/bootc.install-to-filesystem: parametrize stateroot + value + +This adds an extra option to make the stateroot name customizable. + +Fixes https://github.com/osbuild/osbuild/issues/2151 +--- + stages/org.osbuild.bootc.install-to-filesystem | 3 +++ + ...org.osbuild.bootc.install-to-filesystem.meta.json | 12 ++++++------ + 2 files changed, 9 insertions(+), 6 deletions(-) + +diff --git a/stages/org.osbuild.bootc.install-to-filesystem b/stages/org.osbuild.bootc.install-to-filesystem +index 6edcbf8f..3d4c67cc 100755 +--- a/stages/org.osbuild.bootc.install-to-filesystem ++++ b/stages/org.osbuild.bootc.install-to-filesystem +@@ -43,6 +43,9 @@ def main(options, inputs, paths): + target_imgref = options.get("target-imgref") + if target_imgref: + pargs.extend(["--target-imgref", target_imgref]) ++ stateroot = options.get("stateroot") ++ if stateroot: ++ pargs.extend(["--stateroot", stateroot]) + # add target and go + pargs.append(dst) + subprocess.run(pargs, env=env, check=True) +diff --git a/stages/org.osbuild.bootc.install-to-filesystem.meta.json b/stages/org.osbuild.bootc.install-to-filesystem.meta.json +index 02268b27..a5a157c6 100644 +--- a/stages/org.osbuild.bootc.install-to-filesystem.meta.json ++++ b/stages/org.osbuild.bootc.install-to-filesystem.meta.json +@@ -7,16 +7,12 @@ + "mounted in the \"mounts\" path.", + "Buildhost commands used: bootc" + ], +- "capabilities": [ +- "CAP_MAC_ADMIN" +- ], ++ "capabilities": ["CAP_MAC_ADMIN"], + "schema_2": { + "inputs": { + "type": "object", + "additionalProperties": false, +- "required": [ +- "images" +- ], ++ "required": ["images"], + "properties": { + "images": { + "type": "object", +@@ -44,6 +40,10 @@ + "target-imgref": { + "description": "Specify the image to fetch for subsequent updates", + "type": "string" ++ }, ++ "stateroot": { ++ "type": "string", ++ "description": "The stateroot name to use. If not specified, defer to bootc's default" + } + } + }, +-- +2.50.1 + diff --git a/src/0006-stages-bootc.install-make-boot-and-root-mount-spec-c.patch b/src/0006-stages-bootc.install-make-boot-and-root-mount-spec-c.patch new file mode 100644 index 0000000000..c3cbeeb5a9 --- /dev/null +++ b/src/0006-stages-bootc.install-make-boot-and-root-mount-spec-c.patch @@ -0,0 +1,81 @@ +From 8e23f3fd16c96f5122d7572efb98776540683df4 Mon Sep 17 00:00:00 2001 +From: jbtrystram +Date: Mon, 21 Jul 2025 20:49:13 +0200 +Subject: [PATCH 6/6] stages/bootc.install: make boot and root mount spec + customizable + +Allow passing custom mount specs for boot and root. Optional fields. +--- + stages/org.osbuild.bootc.install-to-filesystem | 17 +++++++++++++++++ + ...sbuild.bootc.install-to-filesystem.meta.json | 16 ++++++++++++++-- + 2 files changed, 31 insertions(+), 2 deletions(-) + +diff --git a/stages/org.osbuild.bootc.install-to-filesystem b/stages/org.osbuild.bootc.install-to-filesystem +index 3d4c67cc..634263e2 100755 +--- a/stages/org.osbuild.bootc.install-to-filesystem ++++ b/stages/org.osbuild.bootc.install-to-filesystem +@@ -46,6 +46,23 @@ def main(options, inputs, paths): + stateroot = options.get("stateroot") + if stateroot: + pargs.extend(["--stateroot", stateroot]) ++ # Passing the flag with an empty value is intentional: ++ # it triggers a supported behaviour where mountspec ++ # kernel arguments are ommited. ++ # See https://github.com/bootc-dev/bootc/pull/1451 ++ if "root-mount-spec" in options: ++ root_spec = options["root-mount-spec"] ++ if root_spec == "": ++ pargs.extend(["--root-mount-spec="]) ++ else: ++ pargs.extend(["--root-mount-spec", root_spec]) ++ if "boot-mount-spec" in options: ++ boot_spec = options["boot-mount-spec"] ++ if boot_spec == "": ++ pargs.extend(["--boot-mount-spec="]) ++ else: ++ pargs.extend(["--boot-mount-spec", boot_spec]) ++ + # add target and go + pargs.append(dst) + subprocess.run(pargs, env=env, check=True) +diff --git a/stages/org.osbuild.bootc.install-to-filesystem.meta.json b/stages/org.osbuild.bootc.install-to-filesystem.meta.json +index a5a157c6..53369218 100644 +--- a/stages/org.osbuild.bootc.install-to-filesystem.meta.json ++++ b/stages/org.osbuild.bootc.install-to-filesystem.meta.json +@@ -7,12 +7,16 @@ + "mounted in the \"mounts\" path.", + "Buildhost commands used: bootc" + ], +- "capabilities": ["CAP_MAC_ADMIN"], ++ "capabilities": [ ++ "CAP_MAC_ADMIN" ++ ], + "schema_2": { + "inputs": { + "type": "object", + "additionalProperties": false, +- "required": ["images"], ++ "required": [ ++ "images" ++ ], + "properties": { + "images": { + "type": "object", +@@ -44,6 +48,14 @@ + "stateroot": { + "type": "string", + "description": "The stateroot name to use. If not specified, defer to bootc's default" ++ }, ++ "root-mount-spec": { ++ "type": "string", ++ "description": "Source device specification for the root filesystem. If not provided, the UUID of the target filesystem will be used." ++ }, ++ "boot-mount-spec": { ++ "type": "string", ++ "description": "Mount specification for the /boot filesystem. If `/boot` is detected as a mounted partition, then its UUID will be used." + } + } + }, +-- +2.50.1 + diff --git a/src/cmd-osbuild b/src/cmd-osbuild index 993be80b19..eefa0bd1bb 100755 --- a/src/cmd-osbuild +++ b/src/cmd-osbuild @@ -397,6 +397,10 @@ main() { fi outdir=$(mktemp -p "${tmp_builddir}" -d) + # To get a shell in the osbuild supervin VM uncomment this. + # osbuild can be started with `bash tmp/build./cmd.sh` + # See comment about checkpoints in runvm-osbuild + # RUNVM_SHELL=1 \ runvm_with_cache -- /usr/lib/coreos-assembler/runvm-osbuild \ --config "${runvm_osbuild_config_json}" \ --mpp "/usr/lib/coreos-assembler/osbuild-manifests/coreos.osbuild.${basearch}.mpp.yaml" \ diff --git a/src/osbuild-manifests/coreos.osbuild.x86_64.mpp.yaml b/src/osbuild-manifests/coreos.osbuild.x86_64.mpp.yaml index 753fee5a95..182304190c 100644 --- a/src/osbuild-manifests/coreos.osbuild.x86_64.mpp.yaml +++ b/src/osbuild-manifests/coreos.osbuild.x86_64.mpp.yaml @@ -162,86 +162,6 @@ pipelines: origin: org.osbuild.pipeline references: - name:deployed-tree - - type: org.osbuild.ostree.init-fs - - type: org.osbuild.ostree.os-init - options: - osname: - mpp-format-string: '{osname}' - - type: org.osbuild.ostree.config - options: - repo: /ostree/repo - config: - sysroot: - readonly: true - bootloader: none - # https://github.com/coreos/fedora-coreos-tracker/issues/1333 - bls-append-except-default: grub_users="" - # Opt-in to https://github.com/ostreedev/ostree/pull/2705 which will - # add /boot as the prefix on top of BLS config entries. This is OK - # because there is a symlink that is created in the root of the boot - # filesystem by OSTree (boot -> .) that makes it so that /boot paths - # will always work. - bootprefix: true - - type: org.osbuild.mkdir - options: - paths: - - path: /boot/efi - mode: 493 - - type: org.osbuild.ignition - # Deploy via ociarchive or container - - mpp-if: ociarchive != '' - then: - type: org.osbuild.ostree.deploy.container - options: - osname: - mpp-format-string: '{osname}' - target_imgref: - mpp-format-string: '{container_imgref}' - mounts: - - /boot - - /boot/efi - kernel_opts: - - rw - - '$ignition_firstboot' - - mpp-format-string: '{extra_kargs}' - inputs: - images: - type: org.osbuild.containers - origin: org.osbuild.pipeline - references: - name:oci-archive: - name: coreos.ociarchive - else: - type: org.osbuild.ostree.deploy.container - options: - osname: - mpp-format-string: '{osname}' - target_imgref: - mpp-format-string: '{container_imgref}' - mounts: - - /boot - - /boot/efi - kernel_opts: - - rw - - '$ignition_firstboot' - - mpp-format-string: '{extra_kargs}' - inputs: - images: - type: org.osbuild.containers - origin: org.osbuild.source - mpp-resolve-images: - images: - - source: $container_repo - tag: $container_tag - - type: org.osbuild.ostree.aleph - options: - coreos_compat: true - deployment: - default: true - - type: org.osbuild.ostree.selinux - options: - deployment: - default: true - name: raw-image build: mpp-format-string: '{buildroot}' @@ -342,69 +262,25 @@ pipelines: partition: mpp-format-int: '{image.layout[''boot''].partnum}' target: /boot-mount-point - - 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: / - - 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 + # 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: + type: org.osbuild.containers 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 - - type: org.osbuild.copy - inputs: - tree: - type: org.osbuild.tree - origin: org.osbuild.pipeline - references: - - name:tree + name:oci-archive: + name: coreos.ociarchive options: - paths: - - from: input://tree/ - to: mount://root/ + kernel-args: + - '$ignition_firstboot' + - mpp-format-string: '{extra_kargs}' + target-imgref: + mpp-format-string: '{container_imgref}' + stateroot: fedora-coreos + boot-mount-spec: "" + root-mount-spec: "" devices: disk: type: org.osbuild.loopback @@ -430,13 +306,11 @@ pipelines: partition: mpp-format-int: '{image.layout[''EFI-SYSTEM''].partnum}' target: /boot/efi - - type: org.osbuild.bootupd + # set up the `ignition.firstboot` stamp at the end because + # bootc want empty filesystems + - type: org.osbuild.ignition options: - bios: - device: disk - static-configs: true - deployment: - default: true + target: mount://boot/ devices: disk: type: org.osbuild.loopback @@ -456,36 +330,6 @@ pipelines: 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 - - type: org.osbuild.chattr - options: - items: - mount://root/: - immutable: 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: ostree.deployment - type: org.osbuild.ostree.deployment - options: - source: mount - deployment: - default: true - name: raw-4k-image build: mpp-format-string: '{buildroot}' @@ -596,41 +440,25 @@ pipelines: partition: mpp-format-int: '{image4k.layout[''boot''].partnum}' target: /boot-mount-point - - 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 + # 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: + type: org.osbuild.containers origin: org.osbuild.pipeline references: - - name:deployed-tree - 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: / - - type: org.osbuild.selinux + name:oci-archive: + name: coreos.ociarchive 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 + kernel-args: + - '$ignition_firstboot' + - mpp-format-string: '{extra_kargs}' + target-imgref: + mpp-format-string: '{container_imgref}' + stateroot: fedora-coreos + boot-mount-spec: "" + root-mount-spec: "" devices: disk: type: org.osbuild.loopback @@ -644,57 +472,25 @@ pipelines: 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 - - type: org.osbuild.copy - inputs: - tree: - type: org.osbuild.tree - origin: org.osbuild.pipeline - references: - - name:tree - options: - paths: - - from: input://tree/ - to: mount://root/ - 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}' + mpp-format-int: '{image.layout[''root''].partnum}' target: / - name: boot type: org.osbuild.ext4 source: disk partition: - mpp-format-int: '{image4k.layout[''boot''].partnum}' + mpp-format-int: '{image.layout[''boot''].partnum}' target: /boot - name: efi type: org.osbuild.fat source: disk partition: - mpp-format-int: '{image4k.layout[''EFI-SYSTEM''].partnum}' + mpp-format-int: '{image.layout[''EFI-SYSTEM''].partnum}' target: /boot/efi - - type: org.osbuild.bootupd + # set up the `ignition.firstboot` stamp at the end because + # bootc want empty filesystems + - type: org.osbuild.ignition options: - static-configs: true - deployment: - default: true + target: mount://boot/ devices: disk: type: org.osbuild.loopback @@ -708,46 +504,14 @@ pipelines: type: org.osbuild.xfs source: disk partition: - mpp-format-int: '{image4k.layout[''root''].partnum}' + mpp-format-int: '{image.layout[''root''].partnum}' target: / - name: boot type: org.osbuild.ext4 source: disk partition: - mpp-format-int: '{image4k.layout[''boot''].partnum}' + mpp-format-int: '{image.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 - - type: org.osbuild.chattr - options: - items: - mount://root/: - immutable: 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: / - - name: ostree.deployment - type: org.osbuild.ostree.deployment - options: - source: mount - deployment: - default: true - mpp-import-pipelines: path: platform.aliyun.ipp.yaml - mpp-import-pipelines: diff --git a/src/runvm-osbuild b/src/runvm-osbuild index 3b1523f89e..a248ac85dc 100755 --- a/src/runvm-osbuild +++ b/src/runvm-osbuild @@ -114,11 +114,16 @@ set -x; osbuild-mpp \ "${mppyaml}" "${processed_json}" set +x -log_disk_usage +#log_disk_usage # 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" \ diff --git a/src/supermin-init-prelude.sh b/src/supermin-init-prelude.sh index 0bb131d539..65b0de9e7e 100644 --- a/src/supermin-init-prelude.sh +++ b/src/supermin-init-prelude.sh @@ -73,3 +73,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 <