From 779793386c07ae5145810ecc59472ef61fa90999 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Budai?= Date: Wed, 10 Aug 2022 10:28:56 +0200 Subject: [PATCH] osbuild: add support for building ostree artifacts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to start building Fedora IoT images with osbuild, we need to be able to pass ostree options from pungi to the koji's osbuildImage task. This commit adds support for it via new configuration options: ostree_url, ostree_url and ostree_parent. A test was added to cover these new options and they are were also added into the documentation. JIRA: COMPOSER-1702 Merges: https://pagure.io/pungi/pull-request/1624 Signed-off-by: Ondřej Budai --- doc/configuration.rst | 5 ++ pungi/checks.py | 3 + pungi/phases/osbuild.py | 11 +++ tests/test_osbuild_phase.py | 134 +++++++++++++++++++++++++++++++++++- 4 files changed, 152 insertions(+), 1 deletion(-) diff --git a/doc/configuration.rst b/doc/configuration.rst index 8602a520..d91d37d8 100644 --- a/doc/configuration.rst +++ b/doc/configuration.rst @@ -1610,6 +1610,11 @@ OSBuild Composer for building images * ``arches`` -- list of architectures for which to build the image. By default, the variant arches are used. This option can only restrict it, not add a new one. + * ``ostree_url`` -- URL of the repository that's used to fetch the parent + commit from. + * ``ostree_ref`` -- name of the ostree branch + * ``ostree_parent`` -- commit hash or a a branch-like reference to the + parent commit. .. note:: There is initial support for having this task as failable without aborting diff --git a/pungi/checks.py b/pungi/checks.py index 0f339556..80d4e506 100644 --- a/pungi/checks.py +++ b/pungi/checks.py @@ -1177,6 +1177,9 @@ def make_schema(): "repo": {"$ref": "#/definitions/list_of_strings"}, "failable": {"$ref": "#/definitions/list_of_strings"}, "subvariant": {"type": "string"}, + "ostree_url": {"type": "string"}, + "ostree_ref": {"type": "string"}, + "ostree_parent": {"type": "string"}, }, "required": ["name", "distro", "image_types"], "additionalProperties": False, diff --git a/pungi/phases/osbuild.py b/pungi/phases/osbuild.py index 89719050..9215acfa 100644 --- a/pungi/phases/osbuild.py +++ b/pungi/phases/osbuild.py @@ -113,8 +113,19 @@ class RunOSBuildThread(WorkerThread): koji = kojiwrapper.KojiWrapper(compose) koji.login() + ostree = {} + if config.get("ostree_url"): + ostree["url"] = config["ostree_url"] + if config.get("ostree_ref"): + ostree["ref"] = config["ostree_ref"] + if config.get("ostree_parent"): + ostree["parent"] = config["ostree_parent"] + # Start task opts = {"repo": repo} + if ostree: + opts["ostree"] = ostree + if release: opts["release"] = release task_id = koji.koji_proxy.osbuildImage( diff --git a/tests/test_osbuild_phase.py b/tests/test_osbuild_phase.py index 3dbf6fe5..b3f5078a 100644 --- a/tests/test_osbuild_phase.py +++ b/tests/test_osbuild_phase.py @@ -178,7 +178,6 @@ class RunOSBuildThreadTest(helpers.PungiTestCase): # Verify two Koji instances were created. self.assertEqual(len(KojiWrapper.call_args), 2) - print(koji.mock_calls) # Verify correct calls to Koji self.assertEqual( koji.mock_calls, @@ -248,6 +247,139 @@ class RunOSBuildThreadTest(helpers.PungiTestCase): ], ) + @mock.patch("pungi.util.get_file_size", new=lambda fp: 65536) + @mock.patch("pungi.util.get_mtime", new=lambda fp: 1024) + @mock.patch("pungi.phases.osbuild.Linker") + @mock.patch("pungi.phases.osbuild.kojiwrapper.KojiWrapper") + def test_process_ostree(self, KojiWrapper, Linker): + cfg = { + "name": "test-image", + "distro": "rhel-8", + "image_types": ["edge-raw-disk"], + "ostree_url": "http://edge.example.com/repo", + "ostree_ref": "test/iot", + "ostree_parent": "test/iot-parent", + } + build_id = 5678 + koji = KojiWrapper.return_value + koji.watch_task.side_effect = self.make_fake_watch(0) + koji.koji_proxy.osbuildImage.return_value = 1234 + koji.koji_proxy.getTaskResult.return_value = { + "composer": {"server": "https://composer.osbuild.org", "id": ""}, + "koji": {"build": build_id}, + } + koji.koji_proxy.getBuild.return_value = { + "build_id": build_id, + "name": "test-image", + "version": "1", + "release": "1", + } + koji.koji_proxy.listArchives.return_value = [ + { + "extra": {"image": {"arch": "aarch64"}}, + "filename": "image.aarch64.raw.xz", + "type_name": "raw-xz", + }, + { + "extra": {"image": {"arch": "x86_64"}}, + "filename": "image.x86_64.raw.xz", + "type_name": "raw-xz", + }, + ] + koji.koji_module.pathinfo = orig_koji.pathinfo + + self.t.process( + ( + self.compose, + self.compose.variants["Everything"], + cfg, + ["aarch64", "x86_64"], + "1", # version + "15", # release + "image-target", + [self.topdir + "/compose/Everything/$arch/os"], + ["x86_64"], + ), + 1, + ) + + # Verify two Koji instances were created. + self.assertEqual(len(KojiWrapper.call_args), 2) + # Verify correct calls to Koji + self.assertEqual( + koji.mock_calls, + [ + mock.call.login(), + mock.call.koji_proxy.osbuildImage( + "test-image", + "1", + "rhel-8", + ["edge-raw-disk"], + "image-target", + ["aarch64", "x86_64"], + opts={ + "release": "15", + "repo": [self.topdir + "/compose/Everything/$arch/os"], + "ostree": { + "url": "http://edge.example.com/repo", + "ref": "test/iot", + "parent": "test/iot-parent", + }, + }, + ), + mock.call.save_task_id(1234), + mock.call.watch_task(1234, mock.ANY), + mock.call.koji_proxy.getTaskResult(1234), + mock.call.koji_proxy.getBuild(build_id), + mock.call.koji_proxy.listArchives(buildID=build_id), + ], + ) + + # Assert there are 2 images added to manifest and the arguments are sane + self.assertEqual( + self.compose.im.add.call_args_list, + [ + mock.call(arch="aarch64", variant="Everything", image=mock.ANY), + mock.call(arch="x86_64", variant="Everything", image=mock.ANY), + ], + ) + for call in self.compose.im.add.call_args_list: + _, kwargs = call + image = kwargs["image"] + self.assertEqual(kwargs["variant"], "Everything") + self.assertIn(kwargs["arch"], ("aarch64", "x86_64")) + self.assertEqual(kwargs["arch"], image.arch) + self.assertEqual( + "Everything/%(arch)s/images/image.%(arch)s.raw.xz" + % {"arch": image.arch}, + image.path, + ) + self.assertEqual("raw.xz", image.format) + self.assertEqual("raw-xz", image.type) + self.assertEqual("Everything", image.subvariant) + + self.assertTrue( + os.path.isdir(self.topdir + "/compose/Everything/aarch64/images") + ) + self.assertTrue( + os.path.isdir(self.topdir + "/compose/Everything/x86_64/images") + ) + + self.assertEqual( + Linker.return_value.mock_calls, + [ + mock.call.link( + "/mnt/koji/packages/test-image/1/1/images/image.%(arch)s.raw.xz" + % {"arch": arch}, + self.topdir + + "/compose/Everything/%(arch)s/images/image.%(arch)s.raw.xz" + % {"arch": arch}, + link_type="hardlink-or-copy", + ) + for arch in ["aarch64", "x86_64"] + ], + ) + @mock.patch("pungi.util.get_file_size", new=lambda fp: 65536) @mock.patch("pungi.util.get_mtime", new=lambda fp: 1024) @mock.patch("pungi.phases.osbuild.Linker")