Repository: osbuild/osbuild
Pull request #1568 (2024-02-03 14:13:52)

README: Add OSBuild's principles
I have added the OSBuild principles based on our discussion and agreement. I have reformulated some sentences for clarity.


Patch:
diff --git a/README.md b/README.md
index eae4ea9f..42862848 100644
--- a/README.md
+++ b/README.md
@@ -18,6 +18,15 @@ of the pipeline description, and more.
  * **Mailing List**: image-builder@redhat.com
  * **Changelog**: https://github.com/osbuild/osbuild/releases
 
+### Principles
+
+1. [OSBuild stages](./stages) are never broken, only deprecated. The same manifest should always produce the same output.
+2. [OSBuild stages](./stages) should be explicit whenever possible instead of e.g. relying on the state of the tree.
+3. Pipelines are independent, so the tree is expected to be empty at the beginning of each.
+4. Manifests are expected to be machine-generated, so OSBuild has no convenience functions to support manually created manifests.
+5. The build environment is confined against accidental misuse, but this should not be considered a security boundary.
+6. OSBuild may only use Python language features supported by the oldest target distribution.
+
 ### Contributing
 
 Please refer to the [developer guide](https://www.osbuild.org/guides/developer-guide/index.html) to learn about our workflow, code style and more.
diff --git a/osbuild/buildroot.py b/osbuild/buildroot.py
index ddc60a0b..85c3e192 100644
--- a/osbuild/buildroot.py
+++ b/osbuild/buildroot.py
@@ -237,6 +237,12 @@ class BuildRoot(contextlib.AbstractContextManager):
         mounts += ["--ro-bind-try",
                    os.path.join(self._rootdir, "etc/containers"),
                    "/etc/containers"]
+        mounts += ["--ro-bind-try",
+                   os.path.join(self._rootdir, "ostree"),
+                   "/ostree"]
+        mounts += ["--ro-bind-try",
+                   os.path.join(self._rootdir, "etc/selinux/targeted/contexts"),
+                   "/etc/selinux/targeted/contexts"]
 
         # We execute our own modules by bind-mounting them from the host into
         # the build-root. We have minimal requirements on the build-root, so
diff --git a/stages/org.osbuild.bootc.install-to-filesystem b/stages/org.osbuild.bootc.install-to-filesystem
new file mode 100755
index 00000000..6b8c376a
--- /dev/null
+++ b/stages/org.osbuild.bootc.install-to-filesystem
@@ -0,0 +1,62 @@
+#!/usr/bin/python3
+"""Run bootc install to-filesystem
+
+Note that this needs the disk.img in the inputs as one continous
+devices so that bootupd can install grub to the mbr.
+
+Buildhost commands used: bootc
+"""
+
+import subprocess
+import sys
+
+import osbuild.api
+from osbuild.util import containers
+
+CAPABILITIES = ["CAP_MAC_ADMIN"]
+
+SCHEMA_2 = r"""
+"inputs": {
+  "type": "object",
+  "additionalProperties": false,
+  "required": ["images"],
+  "properties": {
+    "images": {
+      "type": "object",
+      "additionalProperties": true
+    }
+  }
+},
+"options": {
+  "additionalProperties": false
+},
+"devices": {
+  "type": "object",
+  "additionalProperties": true
+},
+"mounts": {
+  "type": "array"
+}
+"""
+
+
+def main(inputs, mounts):
+    images = containers.parse_containers_input(inputs)
+    assert len(images) == 1
+    image = list(images.values())[0]
+
+    with containers.container_source(image) as (_, source):
+        dst = mounts["root"]["path"]
+        subprocess.run(
+            ["bootc", "install", "to-filesystem",
+             "--source-imgref", source,
+             "--skip-fetch-check", "--generic-image",
+             dst],
+            check=True
+        )
+
+
+if __name__ == "__main__":
+    args = osbuild.api.arguments()
+    r = main(args["inputs"], args["mounts"])
+    sys.exit(r)
diff --git a/tools/osbuild-mpp b/tools/osbuild-mpp
index 7bf93a18..5eab453e 100755
--- a/tools/osbuild-mpp
+++ b/tools/osbuild-mpp
@@ -1611,7 +1611,10 @@ class ManifestFileV2(ManifestFile):
 
     def _process_container(self, stage):
         if stage.get("type", "") not in \
-                ["org.osbuild.skopeo", "org.osbuild.ostree.deploy.container", "org.osbuild.container-deploy"]:
+                ["org.osbuild.skopeo",
+                 "org.osbuild.ostree.deploy.container",
+                 "org.osbuild.container-deploy",
+                 "org.osbuild.bootc.install-to-filesystem"]:
             return
 
         inputs = element_enter(stage, "inputs", {})


Test patch:
diff --git a/stages/test/test_bootc_install_to_fs.py b/stages/test/test_bootc_install_to_fs.py
new file mode 100644
index 00000000..a87d7139
--- /dev/null
+++ b/stages/test/test_bootc_install_to_fs.py
@@ -0,0 +1,46 @@
+#!/usr/bin/python3
+
+import os.path
+from contextlib import contextmanager
+from unittest.mock import call, patch
+
+from osbuild.testutil.imports import import_module_from_path
+
+
+@patch("subprocess.run")
+def test_bootc_install_to_fs(mock_run, tmp_path):
+    stage_path = os.path.join(os.path.dirname(__file__), "../org.osbuild.bootc.install-to-filesystem")
+    stage = import_module_from_path("bootc_install_to_fs_stage", stage_path)
+
+    inputs = {
+        "images": {
+            "path": "/input/images/path",
+            "data": {
+                "archives": {
+                    "/input/images/path": {
+                        "format": "oci-archive",
+                        "name": "some-img-name",
+                    },
+                },
+            },
+        },
+    }
+    mounts = {
+        "root": {
+            "path": "/path/to/root",
+        },
+    }
+
+    @contextmanager
+    def faked_tmp_dir():
+        yield tmp_path
+    with patch("tempfile.TemporaryDirectory", side_effect=faked_tmp_dir):
+        stage.main(inputs, mounts)
+
+    assert len(mock_run.call_args_list) == 1
+    assert mock_run.call_args_list == [
+        call(["bootc", "install", "to-filesystem",
+              "--source-imgref", f"oci-archive:{tmp_path}/image",
+              "--skip-fetch-check", "--generic-image",
+              "/path/to/root"], check=True)
+    ]
diff --git a/test/data/manifests/manifest.json b/test/data/manifests/manifest.json
new file mode 100644
index 00000000..36ef87b3
--- /dev/null
+++ b/test/data/manifests/manifest.json
@@ -0,0 +1,204 @@
+{
+  "version": "2",
+  "pipelines": [
+    {
+      "name": "build",
+      "stages": [
+        {
+          "type": "org.osbuild.container-deploy",
+          "inputs": {
+            "images": {
+              "type": "org.osbuild.containers",
+              "origin": "org.osbuild.source",
+              "references": {
+                "sha256:129c330a79048413e9609b2ddc808522905e4277f0ceaa79cd84be2ec773f495": {
+                  "name": "registry.gitlab.com/redhat/services/products/image-builder/ci/images/centos-bootc-dev:stream9"
+                }
+              }
+            }
+          }
+        },
+        {
+          "type": "org.osbuild.selinux",
+          "options": {
+            "file_contexts": "etc/selinux/targeted/contexts/files/file_contexts",
+            "labels": {
+              "/usr/bin/bootc": "system_u:object_r:install_exec_t:s0",
+              "/usr/bin/ostree": "system_u:object_r:install_exec_t:s0"
+            }
+          }
+        }
+      ]
+    },
+    {
+      "name": "image",
+      "build": "name:build",
+      "stages": [
+        {
+          "type": "org.osbuild.truncate",
+          "options": {
+            "filename": "disk.img",
+            "size": "5368709120"
+          }
+        },
+        {
+          "type": "org.osbuild.sfdisk",
+          "options": {
+            "label": "gpt",
+            "uuid": "D209C89E-EA5E-4FBD-B161-B461CCE297E0",
+            "partitions": [
+              {
+                "bootable": true,
+                "size": 2048,
+                "start": 2048,
+                "type": "21686148-6449-6E6F-744E-656564454649",
+                "uuid": "FAC7F1FB-3E8D-4137-A512-961DE09A5549"
+              },
+              {
+                "size": 409600,
+                "start": 4096,
+                "type": "C12A7328-F81F-11D2-BA4B-00A0C93EC93B",
+                "uuid": "68B2905B-DF3E-4FB3-80FA-49D1E773AA33"
+              },
+              {
+                "size": 1024000,
+                "start": 413696,
+                "type": "0FC63DAF-8483-4772-8E79-3D69D8477DE4",
+                "uuid": "CB07C243-BC44-4717-853E-28852021225B"
+              },
+              {
+                "size": 9048031,
+                "start": 1437696,
+                "type": "0FC63DAF-8483-4772-8E79-3D69D8477DE4",
+                "uuid": "6264D520-3FB9-423F-8AB8-7A0A8E3D3562"
+              }
+            ]
+          },
+          "devices": {
+            "device": {
+              "type": "org.osbuild.loopback",
+              "options": {
+                "filename": "disk.img",
+                "lock": true
+              }
+            }
+          }
+        },
+        {
+          "type": "org.osbuild.mkfs.fat",
+          "options": {
+            "volid": "7B7795E7"
+          },
+          "devices": {
+            "device": {
+              "type": "org.osbuild.loopback",
+              "options": {
+                "filename": "disk.img",
+                "start": 4096,
+                "size": 409600,
+                "lock": true
+              }
+            }
+          }
+        },
+        {
+          "type": "org.osbuild.mkfs.ext4",
+          "options": {
+            "uuid": "0194fdc2-fa2f-4cc0-81d3-ff12045b73c8",
+            "label": "boot"
+          },
+          "devices": {
+            "device": {
+              "type": "org.osbuild.loopback",
+              "options": {
+                "filename": "disk.img",
+                "start": 413696,
+                "size": 1024000,
+                "lock": true
+              }
+            }
+          }
+        },
+        {
+          "type": "org.osbuild.mkfs.ext4",
+          "options": {
+            "uuid": "6e4ff95f-f662-45ee-a82a-bdf44a2d0b75",
+            "label": "root"
+          },
+          "devices": {
+            "device": {
+              "type": "org.osbuild.loopback",
+              "options": {
+                "filename": "disk.img",
+                "start": 1437696,
+                "size": 9048031,
+                "lock": true
+              }
+            }
+          }
+        },
+        {
+          "type": "org.osbuild.bootc.install-to-filesystem",
+          "inputs": {
+            "images": {
+              "type": "org.osbuild.containers",
+              "origin": "org.osbuild.source",
+              "references": {
+                "sha256:129c330a79048413e9609b2ddc808522905e4277f0ceaa79cd84be2ec773f495": {
+                  "name": "registry.gitlab.com/redhat/services/products/image-builder/ci/images/centos-bootc-dev:stream9"
+                }
+              }
+            }
+          },
+          "devices": {
+            "disk": {
+              "type": "org.osbuild.loopback",
+              "options": {
+                "filename": "disk.img",
+                "partscan": true
+              }
+            }
+          },
+          "mounts": [
+            {
+              "name": "root",
+              "type": "org.osbuild.ext4",
+              "source": "disk",
+              "partition": 4,
+              "target": "/"
+            },
+            {
+              "name": "boot",
+              "type": "org.osbuild.ext4",
+              "source": "disk",
+              "partition": 3,
+              "target": "/boot"
+            },
+            {
+              "name": "boot.efi",
+              "type": "org.osbuild.fat",
+              "partition": 2,
+              "source": "disk",
+              "target": "/boot/efi"
+            }
+          ]
+        }
+      ]
+    }
+  ],
+  "sources": {
+    "org.osbuild.curl": {
+      "items": {}
+    },
+    "org.osbuild.skopeo": {
+      "items": {
+        "sha256:129c330a79048413e9609b2ddc808522905e4277f0ceaa79cd84be2ec773f495": {
+          "image": {
+            "name": "registry.gitlab.com/redhat/services/products/image-builder/ci/images/centos-bootc-dev",
+            "digest": "sha256:11fbc536fad3ae9c95602a76f38d3f61c345da7610ef496f0198b045f1463fe4"
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/test/data/manifests/manifest.mpp.yaml b/test/data/manifests/manifest.mpp.yaml
new file mode 100644
index 00000000..75019b2b
--- /dev/null
+++ b/test/data/manifests/manifest.mpp.yaml
@@ -0,0 +1,120 @@
+version: '2'
+pipelines:
+  - name: build
+    stages:
+      - type: org.osbuild.container-deploy
+        inputs:
+          images:
+            type: org.osbuild.containers
+            origin: org.osbuild.source
+            mpp-resolve-images:
+              images:
+                - source: registry.gitlab.com/redhat/services/products/image-builder/ci/images/centos-bootc-dev
+                  tag: "stream9"
+      - type: org.osbuild.selinux
+        options:
+          file_contexts: etc/selinux/targeted/contexts/files/file_contexts
+          labels:
+            /usr/bin/bootc: system_u:object_r:install_exec_t:s0
+            /usr/bin/ostree: system_u:object_r:install_exec_t:s0
+  - name: image
+    build: name:build
+    stages:
+      - type: org.osbuild.truncate
+        options:
+          filename: disk.img
+          size: '5368709120'
+      - type: org.osbuild.sfdisk
+        options:
+          label: gpt
+          uuid: D209C89E-EA5E-4FBD-B161-B461CCE297E0
+          partitions:
+            - bootable: true
+              size: 2048
+              start: 2048
+              type: 21686148-6449-6E6F-744E-656564454649
+              uuid: FAC7F1FB-3E8D-4137-A512-961DE09A5549
+            - size: 409600
+              start: 4096
+              type: C12A7328-F81F-11D2-BA4B-00A0C93EC93B
+              uuid: 68B2905B-DF3E-4FB3-80FA-49D1E773AA33
+            - size: 1024000
+              start: 413696
+              type: 0FC63DAF-8483-4772-8E79-3D69D8477DE4
+              uuid: CB07C243-BC44-4717-853E-28852021225B
+            - size: 9048031
+              start: 1437696
+              type: 0FC63DAF-8483-4772-8E79-3D69D8477DE4
+              uuid: 6264D520-3FB9-423F-8AB8-7A0A8E3D3562
+        devices:
+          device:
+            type: org.osbuild.loopback
+            options:
+              filename: disk.img
+              lock: true
+      - type: org.osbuild.mkfs.fat
+        options:
+          volid: 7B7795E7
+        devices:
+          device:
+            type: org.osbuild.loopback
+            options:
+              filename: disk.img
+              start: 4096
+              size: 409600
+              lock: true
+      - type: org.osbuild.mkfs.ext4
+        options:
+          uuid: 0194fdc2-fa2f-4cc0-81d3-ff12045b73c8
+          label: boot
+        devices:
+          device:
+            type: org.osbuild.loopback
+            options:
+              filename: disk.img
+              start: 413696
+              size: 1024000
+              lock: true
+      - type: org.osbuild.mkfs.ext4
+        options:
+          uuid: 6e4ff95f-f662-45ee-a82a-bdf44a2d0b75
+          label: root
+        devices:
+          device:
+            type: org.osbuild.loopback
+            options:
+              filename: disk.img
+              start: 1437696
+              size: 9048031
+              lock: true
+      - type: org.osbuild.bootc.install-to-filesystem
+        inputs:
+          images:
+            type: org.osbuild.containers
+            origin: org.osbuild.source
+            mpp-resolve-images:
+              images:
+                - source: registry.gitlab.com/redhat/services/products/image-builder/ci/images/centos-bootc-dev
+                  tag: "stream9"
+        devices:
+          disk:
+            type: org.osbuild.loopback
+            options:
+              filename: disk.img
+              partscan: true
+        mounts:
+          - name: root
+            type: org.osbuild.ext4
+            source: disk
+            partition: 4
+            target: /
+          - name: boot
+            type: org.osbuild.ext4
+            source: disk
+            partition: 3
+            target: /boot
+          - name: boot.efi
+            type: org.osbuild.fat
+            partition: 2
+            source: disk
+            target: /boot/efi