From cb901102e8fc5f741152745e9dba02e079847e8b Mon Sep 17 00:00:00 2001 From: Pavel Raiskup Date: Tue, 22 Oct 2024 12:59:24 +0200 Subject: [PATCH] export_buildroot_image: new plugin for OCI image exports The --enable-plugin=export_buildroot_image option automatically wraps the /root directory as OCI-image-as-tarball file, and stores it into Mock's resultdir. We exclude the /builddir/build directory from the generated image; it could contain RPMs, SRPMs, extracted sources, etc. Relates: #1482 --- docs/Plugin-Export-Buildroot-Image.md | 62 ++++++++++++++++++ docs/index.md | 1 + mock/mock.spec | 1 + mock/py/mockbuild/config.py | 4 +- .../plugins/export_buildroot_image.py | 63 +++++++++++++++++++ .../export-import-oci-buildroot-image.feature | 3 + 6 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 docs/Plugin-Export-Buildroot-Image.md create mode 100644 mock/py/mockbuild/plugins/export_buildroot_image.py create mode 100644 releng/release-notes-next/export-import-oci-buildroot-image.feature diff --git a/docs/Plugin-Export-Buildroot-Image.md b/docs/Plugin-Export-Buildroot-Image.md new file mode 100644 index 000000000..7be7a62d0 --- /dev/null +++ b/docs/Plugin-Export-Buildroot-Image.md @@ -0,0 +1,62 @@ +--- +layout: default +title: Plugin export_buildroot_image +--- + +This plugin allows you to (on demand) export the Mock chroot as an OCI image in +local archive format (tarball). This tarball can provide additional convenience +for local build reproducibility. See the example below for details. + +By default, this plugin is **disabled**. You can enable it using the +`--enable-plugin export_buildroot_image` option in `--rebuild` mode. + +This plugin has been added in Mock v6.0. + +## Example use-case + +First, let's start a standard Mock build, but enable the OCI archive generator: + + $ mock -r fedora-rawhide-x86_64 --enable-plugin export_buildroot_image \ + /tmp/quick-package/dummy-pkg-20241212_1114-1.src.rpm + ... mock installs all build-deps, and does other chroot tweaks ... + Start: producing buildroot as OCI image + ... mock performs the rpmbuild ... + INFO: Results and/or logs in: /var/lib/mock/fedora-rawhide-x86_64/result + Finish: run + +The archive has been saved in the result directory: + + $ ls /var/lib/mock/fedora-rawhide-x86_64/result/*.tar + /var/lib/mock/fedora-rawhide-x86_64/result/buildroot-oci.tar + +Then, you can try re-running the build without Mock, like this: + + $ chmod a+r /tmp/quick-package/dummy-pkg-20241212_1114-1.src.rpm + $ podman run --rm -ti \ + -v /tmp/quick-package/dummy-pkg-20241212_1114-1.src.rpm:/dummy-pkg.src.rpm:z \ + oci-archive:/var/lib/mock/fedora-rawhide-x86_64/result/buildroot-oci.tar \ + rpmbuild --rebuild /dummy-pkg.src.rpm + + Installing /dummy-pkg.src.rpm + setting SOURCE_DATE_EPOCH=1401926400 + Executing(%mkbuilddir): /bin/sh -e /var/tmp/rpm-tmp.XIm441 + ... + Executing(%prep): /bin/sh -e /var/tmp/rpm-tmp.pqJ9hu + ... + Executing(%build): /bin/sh -e /var/tmp/rpm-tmp.iaeMZG + ... + Executing(%install): /bin/sh -e /var/tmp/rpm-tmp.SHktaE + ... + Processing files: dummy-pkg-20241212_1114-1.fc42.x86_64 + ... + Executing(%clean): /bin/sh -e /var/tmp/rpm-tmp.E71FWH + ... + + exit 0 + +**Warning:** This method of reproducing a Mock build is not recommended for +production use. During a normal/full Mock rebuild, Mock ensures the buildroot +is fully up-to-date. Using just plain `rpmbuild` within Podman may result in +outdated files, different structure in the kernel-driven filesystems like +`/proc`, `/dev`, and `/sys`, different SELinux assumptions, permissions, etc. +Proceed with caution, and be prepared to encounter some differences (and perhaps +different build failures). diff --git a/docs/index.md b/docs/index.md index 7cda178c2..040916c04 100644 --- a/docs/index.md +++ b/docs/index.md @@ -196,6 +196,7 @@ See a [separate document](Mock-Core-Configs). * [ccache](Plugin-CCache) - compiler cache plugin * [chroot_scan](Plugin-ChrootScan) - allows you to retrieve build artifacts from buildroot (e.g. additional logs, coredumps) * [compress_logs](Plugin-CompressLogs) - compress logs +* [export_buildroot_image](Plugin-Export-Buildroot-Image) - export prepared build chroot as an OCI image * [hw_info](Plugin-HwInfo) - prints HW information of builder * [lvm_root](Plugin-LvmRoot) - caching buildroots using LVM * [mount](Plugin-Mount) - allows you to mount directories into chroot diff --git a/mock/mock.spec b/mock/mock.spec index 42bdca277..362da4e2b 100644 --- a/mock/mock.spec +++ b/mock/mock.spec @@ -94,6 +94,7 @@ Recommends: dnf-utils Recommends: btrfs-progs Suggests: qemu-user-static Suggests: procenv +Recommends: buildah Recommends: podman Recommends: fuse-overlayfs diff --git a/mock/py/mockbuild/config.py b/mock/py/mockbuild/config.py index 22da41426..8aa7cde31 100644 --- a/mock/py/mockbuild/config.py +++ b/mock/py/mockbuild/config.py @@ -31,7 +31,7 @@ 'ccache', 'selinux', 'package_state', 'chroot_scan', 'lvm_root', 'compress_logs', 'sign', 'pm_request', 'hw_info', 'procenv', 'showrc', 'rpkg_preprocessor', - 'rpmautospec', 'buildroot_lock'] + 'rpmautospec', 'buildroot_lock', 'export_buildroot_image'] def nspawn_supported(): """Detect some situations where the systemd-nspawn chroot code won't work""" @@ -234,6 +234,8 @@ def setup_default_config_opts(): 'process-distgit', ] }, + 'export_buildroot_image_enable': False, + 'export_buildroot_image_opts': {}, } config_opts['environment'] = { diff --git a/mock/py/mockbuild/plugins/export_buildroot_image.py b/mock/py/mockbuild/plugins/export_buildroot_image.py new file mode 100644 index 000000000..6ebccc145 --- /dev/null +++ b/mock/py/mockbuild/plugins/export_buildroot_image.py @@ -0,0 +1,63 @@ +""" +Generate OCI from prepared build chroot. +Use given OCI image as build chroot (TODO). +""" + +import os +import mockbuild.util +from mockbuild.trace_decorator import getLog + +requires_api_version = "1.1" + + +def init(plugins, conf, buildroot): + """ The obligatory plugin entry point """ + OCIAsBuildroot(plugins, conf, buildroot) + + +class OCIAsBuildroot: + """ + OCIAsBuildroot plugin (class). + """ + def __init__(self, plugins, conf, buildroot): + self.buildroot = buildroot + self.state = buildroot.state + self.conf = conf + plugins.add_hook("postdeps", self.produce_buildroot_image) + + def do(self, cmd): + """ Execute command on host (as root) """ + getLog().info("Executing %s", ' '.join(cmd)) + mockbuild.util.do(cmd, returnOutput=True, returnStderr=True) + + def _produce_image_as_root(self): + name = f"mock-container-{self.buildroot.config['root']}" + tarball = os.path.join(self.buildroot.resultdir, "buildroot-oci.tar") + chroot = self.buildroot.make_chroot_path() + + # Add the whole chroot directory into the container + self.do(["buildah", "from", "--name", name, "scratch"]) + self.do(["buildah", "add", name, chroot, "/"]) + + # Keep just /builddir directory, make it correctly owned + self.do(["buildah", "run", name, "rm", "-r", + self.buildroot.config["chroothome"] + "/build"]) + self.do(["buildah", "run", name, "chown", "-R", "mockbuild:mock", + self.buildroot.config["chroothome"]]) + + # When starting container, switch to mockbuild user directly + self.do(["buildah", "config", "--user", "mockbuild:mock", name]) + + # Export the image as OCI archive, and remove the WIP container + self.do(["buildah", "commit", "--format", "oci", name, + "oci-archive:" + tarball]) + self.do(["buildah", "rm", name]) + + def produce_buildroot_image(self): + """ Generate OCI image from buildroot using Buildah """ + try: + self.state.start("producing buildroot as OCI image") + with self.buildroot.uid_manager.elevated_privileges(): + self._produce_image_as_root() + finally: + self.state.finish("producing buildroot as OCI image") diff --git a/releng/release-notes-next/export-import-oci-buildroot-image.feature b/releng/release-notes-next/export-import-oci-buildroot-image.feature new file mode 100644 index 000000000..112ccc930 --- /dev/null +++ b/releng/release-notes-next/export-import-oci-buildroot-image.feature @@ -0,0 +1,3 @@ +A new plugin, `export_buildroot_image`, has been added. This plugin can export +the Mock chroot as an OCI archive once all the build dependencies have been +installed (when the chroot is ready-made for runnign `/bin/rpmbuild -bb`).