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")