From f1a65b66271da26d37c8dfa65391741cc6042be5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20Sch=C3=A4fer?= Date: Sat, 14 Mar 2026 11:18:41 +0100 Subject: [PATCH] Add support for oci format for disk images The oci:... format is a special case that allows you to create an OCI-compliant container from a specified reference container. The created container image has the actual disk image added as a new layer. The specification of the oci:image_format:container_transport format contains two parts separated by a colon. The first part specifies one of the above disk formats or just "raw" if the disk should be stored as raw disk inside of the container. The second part specifies one of the supported skopeo container transport formats, for details see the skopeo man page. This format is particularly useful for building an image which bundles the actual disk image and its runtime requirements into one artifact. --- .../test-image-disk-formats/appliance.kiwi | 76 +++++++++ .../test-image-disk-formats/config.sh | 7 + doc/source/image_description/elements.rst | 31 +++- kiwi/defaults.py | 15 +- kiwi/schema/kiwi.rnc | 33 +++- kiwi/schema/kiwi.rng | 29 +++- kiwi/storage/subformat/__init__.py | 10 ++ kiwi/storage/subformat/oci.py | 153 ++++++++++++++++++ test/unit/storage/subformat/init_test.py | 16 ++ test/unit/storage/subformat/oci_test.py | 135 ++++++++++++++++ 10 files changed, 484 insertions(+), 21 deletions(-) create mode 100644 build-tests/x86/tumbleweed/test-image-disk-formats/appliance.kiwi create mode 100644 build-tests/x86/tumbleweed/test-image-disk-formats/config.sh create mode 100644 kiwi/storage/subformat/oci.py create mode 100644 test/unit/storage/subformat/oci_test.py diff --git a/build-tests/x86/tumbleweed/test-image-disk-formats/appliance.kiwi b/build-tests/x86/tumbleweed/test-image-disk-formats/appliance.kiwi new file mode 100644 index 00000000000..8bf4a05724f --- /dev/null +++ b/build-tests/x86/tumbleweed/test-image-disk-formats/appliance.kiwi @@ -0,0 +1,76 @@ + + + + + Marcus Schäfer + marcus.schaefer@gmail.com + Build different disk formats + + + 1.42.1 + zypper + en_US + us + Europe/Berlin + true + false + breeze + openSUSE + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build-tests/x86/tumbleweed/test-image-disk-formats/config.sh b/build-tests/x86/tumbleweed/test-image-disk-formats/config.sh new file mode 100644 index 00000000000..61803a44fa9 --- /dev/null +++ b/build-tests/x86/tumbleweed/test-image-disk-formats/config.sh @@ -0,0 +1,7 @@ +#!/bin/bash +set -ex + +#====================================== +# Activate services +#-------------------------------------- +systemctl enable sshd diff --git a/doc/source/image_description/elements.rst b/doc/source/image_description/elements.rst index 06315f19c63..e863400ba4f 100644 --- a/doc/source/image_description/elements.rst +++ b/doc/source/image_description/elements.rst @@ -931,10 +931,33 @@ flags="overlay|dmsquash": system based on the capabilities of the upstream dracut module. -format="gce|ova|qcow2|vagrant|vmdk|vdi|vhd|vhdx|vhd-fixed": - For disk image type oem, this specifies the format of - the virtual disk so that it can run on the desired target - virtualization platform. +format="format_name": + For disk image type `oem`, this specifies the format of the virtual disk + so that it can run on the desired target virtualization platform. The + supported formats_name's are: + + * `gce`: Google Compute Engine image format. + * `ova`: Open Virtualization Format Archive. + * `qcow2`: QEMU Copy-On-Write version 2. + * `vagrant`: Vagrant box format (tar.gz). + * `vmdk`: VMware Virtual Machine Disk. + * `vdi`: VirtualBox Disk Image. + * `vhd`: Microsoft Virtual Hard Disk. + * `vhdx`: Microsoft Virtual Hard Disk version 2. + * `vhd-fixed`: Microsoft Virtual Hard Disk with fixed size. + * `oci:image_format:container_transport`: OCI image archive. + + The `oci:...` format is a special case that allows you to create an + OCI-compliant container from a specified reference container. The + created container image has the actual disk image added as a new layer. + The specification of the `oci:image_format:container_transport` format + contains two parts separated by a colon. The first part specifies + one of the above disk formats or just `raw` if the disk should be + stored as raw disk inside of the container. The second part specifies + one of the supported `skopeo` container transport formats, for + details see the `skopeo` man page. This format is particularly useful + for building an image which bundles the actual disk image and its + runtime requirements into one artifact. formatoptions="string": Specifies additional format options passed on to `qemu-img`. diff --git a/kiwi/defaults.py b/kiwi/defaults.py index 821304b2c78..d7c0b429125 100644 --- a/kiwi/defaults.py +++ b/kiwi/defaults.py @@ -1528,8 +1528,19 @@ def get_disk_format_types(): :rtype: list """ return [ - 'gce', 'qcow2', 'vmdk', 'ova', 'meta', 'vmx', 'vhd', 'vhdx', - 'vhdfixed', 'vdi', 'vagrant.libvirt.box', 'vagrant.virtualbox.box' + 'gce', + 'qcow2', + 'vmdk', + 'ova', + 'meta', + 'vmx', + 'vhd', + 'vhdx', + 'vhdfixed', + 'vdi', + 'vagrant.libvirt.box', + 'vagrant.virtualbox.box', + 'oci' ] @staticmethod diff --git a/kiwi/schema/kiwi.rnc b/kiwi/schema/kiwi.rnc index 6119779e57e..c51096e4088 100644 --- a/kiwi/schema/kiwi.rnc +++ b/kiwi/schema/kiwi.rnc @@ -43,6 +43,7 @@ grub_console_input = xsd:token {pattern = "(none|console|serial|at_keyboard|usb_ fs_attributes = xsd:token {pattern = "(no-copy-on-write|synchronous-updates)(,(no-copy-on-write|synchronous-updates))*"} package-version-type = xsd:token {pattern = "(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){3}"} simple-uri-type = xsd:token {pattern = "(file:|https:|http:|ftp:).*"} +oci_format_type = xsd:token {pattern = "oci:(gce:|ova:|qcow2:|vmdk:|vdi:|vhd:|vhdx:|vhd-fixed:|raw:).*"} #========================================== # start with image description @@ -1986,7 +1987,7 @@ div { ## Specifies the format of the virtual disk. attribute format { "gce" | "ova" | "qcow2" | "vagrant" | "vmdk" | - "vdi" | "vhd" | "vhdx" | "vhd-fixed" + "vdi" | "vhd" | "vhdx" | "vhd-fixed" | oci_format_type } >> sch:pattern [ id = "format" is-a = "image_type" sch:param [ name = "attr" value = "format" ] @@ -3090,14 +3091,30 @@ div { ] ] ] + sch:pattern [ + abstract = "true" + id = "format_type" + sch:rule [ + context = "containerconfig[@$attr]" + sch:assert [ + test = "not(../@format) or starts-with(../@format, '$format')" + "containerconfig($attr) is only available for the following "~ + "image @format values: $format" + ] + ] + ] k.containerconfig.name.attribute = ## Specifies a name for the container. This is usually the ## the repository name of the container as read if the container ## image is imported via the docker load command attribute name { text } - >> sch:pattern [ id = "name" is-a = "container_type" + >> sch:pattern [ id = "image_name" is-a = "container_type" + sch:param [ name = "attr" value = "name" ] + sch:param [ name = "types" value = "oem docker oci appx" ] + ] + >> sch:pattern [ id = "format_name" is-a = "format_type" sch:param [ name = "attr" value = "name" ] - sch:param [ name = "types" value = "docker oci appx" ] + sch:param [ name = "format" value = "oci" ] ] k.containerconfig.tag.attribute = ## Specifies a tag for the container. This is usually the @@ -3106,7 +3123,7 @@ div { attribute tag { text } >> sch:pattern [ id = "tag" is-a = "container_type" sch:param [ name = "attr" value = "tag" ] - sch:param [ name = "types" value = "docker oci" ] + sch:param [ name = "types" value = "oem docker oci" ] ] k.containerconfig.additionalnames.attribute = ## Specifies additional names for the container using a comma @@ -3117,28 +3134,28 @@ div { attribute additionalnames { text } >> sch:pattern [ id = "additionalnames" is-a = "container_type" sch:param [ name = "attr" value = "additionalnames" ] - sch:param [ name = "types" value = "docker oci" ] + sch:param [ name = "types" value = "oem docker oci" ] ] k.containerconfig.maintainer.attribute = ## Specifies a maintainer for the container. attribute maintainer { text } >> sch:pattern [ id = "maintainer" is-a = "container_type" sch:param [ name = "attr" value = "maintainer" ] - sch:param [ name = "types" value = "docker oci" ] + sch:param [ name = "types" value = "oem docker oci" ] ] k.containerconfig.user.attribute = ## Specifies a user for the container. attribute user { text } >> sch:pattern [ id = "user" is-a = "container_type" sch:param [ name = "attr" value = "user" ] - sch:param [ name = "types" value = "docker oci" ] + sch:param [ name = "types" value = "oem docker oci" ] ] k.containerconfig.workingdir.attribute = ## Specifies the default working directory of the container attribute workingdir { text } >> sch:pattern [ id = "workingdir" is-a = "container_type" sch:param [ name = "attr" value = "workingdir" ] - sch:param [ name = "types" value = "docker oci" ] + sch:param [ name = "types" value = "oem docker oci" ] ] k.containerconfig.attlist = k.containerconfig.name.attribute & diff --git a/kiwi/schema/kiwi.rng b/kiwi/schema/kiwi.rng index ebbf86544e3..d53b37e9fd4 100644 --- a/kiwi/schema/kiwi.rng +++ b/kiwi/schema/kiwi.rng @@ -116,6 +116,11 @@ (file:|https:|http:|ftp:).* + + + oci:(gce:|ova:|qcow2:|vmdk:|vdi:|vhd:|vhdx:|vhd-fixed:|raw:).* + +