diff --git a/doc/configuration.rst b/doc/configuration.rst index 88bac5e8..8a1769d2 100644 --- a/doc/configuration.rst +++ b/doc/configuration.rst @@ -569,7 +569,9 @@ Options (*list*) -- mapping that defines which variants and arches to skip during buildinstall; format: ``[(variant_uid_regex, {arch|*: True})]``. This is only supported for lorax. - +**buildinstall_allow_reuse** = False + (*bool*) -- When set to ``True``, *Pungi* will try to reuse buildinstall + results from old compose specified by ``--old-composes``. Example ------- diff --git a/pungi/checks.py b/pungi/checks.py index 4a30ad8a..a796d51b 100644 --- a/pungi/checks.py +++ b/pungi/checks.py @@ -748,6 +748,7 @@ def make_schema(): _variant_arch_mapping({"type": "boolean"}), ] }, + "buildinstall_allow_reuse": {"type": "boolean", "default": False}, "buildinstall_method": { "type": "string", "enum": ["lorax", "buildinstall"], diff --git a/pungi/phases/buildinstall.py b/pungi/phases/buildinstall.py index 308f998c..748cdadb 100644 --- a/pungi/phases/buildinstall.py +++ b/pungi/phases/buildinstall.py @@ -19,9 +19,12 @@ import os import time import shutil import re +from six.moves import cPickle as pickle +from copy import copy from kobo.threads import ThreadPool, WorkerThread from kobo.shortcuts import run, force_list +import kobo.rpmlib from productmd.images import Image from six.moves import shlex_quote @@ -32,6 +35,7 @@ from pungi.util import copy_all, translate_path, move_all from pungi.wrappers.lorax import LoraxWrapper from pungi.wrappers import iso from pungi.wrappers.scm import get_file_from_scm +from pungi.wrappers import kojiwrapper from pungi.phases.base import PhaseBase from pungi.runroot import Runroot @@ -268,7 +272,9 @@ class BuildinstallPhase(PhaseBase): for (variant, cmd) in commands: self.pool.add(BuildinstallThread(self.pool)) - self.pool.queue_put((self.compose, arch, variant, cmd)) + self.pool.queue_put( + (self.compose, arch, variant, cmd, self.pkgset_phase) + ) self.pool.start() @@ -495,12 +501,224 @@ def link_boot_iso(compose, arch, variant, can_fail): class BuildinstallThread(WorkerThread): def process(self, item, num): # The variant is None unless lorax is used as buildinstall method. - compose, arch, variant, cmd = item + compose, arch, variant, cmd, pkgset_phase = item can_fail = compose.can_fail(variant, arch, "buildinstall") with failable(compose, can_fail, variant, arch, "buildinstall"): - self.worker(compose, arch, variant, cmd, num) + self.worker(compose, arch, variant, cmd, pkgset_phase, num) - def worker(self, compose, arch, variant, cmd, num): + def _generate_buildinstall_metadata( + self, compose, arch, variant, cmd, buildroot_rpms, pkgset_phase + ): + """ + Generate buildinstall.metadata dict. + + :param Compose compose: Current compose. + :param str arch: Current architecture. + :param Variant variant: Compose variant. + :param list cmd: List of command line arguments passed to buildinstall task. + :param list buildroot_rpms: List of NVRAs of all RPMs installed in the + buildinstall task's buildroot. + :param PkgsetPhase pkgset_phase: Package set phase instance. + :return: The buildinstall.metadata dict. + """ + # Load the list of packages installed in the boot.iso. + # The list of installed packages is logged by Lorax in the "pkglists" + # directory. There is one file for each installed RPM and the name + # of the file is the name of the RPM. + # We need to resolve the name of each RPM back to its NVRA. + installed_rpms = [] + log_fname = "buildinstall-%s-logs/dummy" % variant.uid + log_dir = os.path.dirname(compose.paths.log.log_file(arch, log_fname, False)) + pkglists_dir = os.path.join(log_dir, "pkglists") + if os.path.exists(pkglists_dir): + for pkg_name in os.listdir(pkglists_dir): + for pkgset in pkgset_phase.package_sets: + global_pkgset = pkgset["global"] + # We actually do not care from which package_set the RPM + # came from or if there are multiple versions/release of + # the single RPM in more packages sets. We simply include + # all RPMs with this name in the metadata. + # Later when deciding if the buildinstall phase results + # can be reused, we check that all the RPMs with this name + # are still the same in old/new compose. + for rpm_path, rpm_obj in global_pkgset.file_cache.items(): + if rpm_obj.name == pkg_name: + installed_rpms.append(rpm_path) + + # Store the metadata in `buildinstall.metadata`. + metadata = { + "cmd": cmd, + "buildroot_rpms": sorted(buildroot_rpms), + "installed_rpms": sorted(installed_rpms), + } + return metadata + + def _write_buildinstall_metadata( + self, compose, arch, variant, cmd, buildroot_rpms, pkgset_phase + ): + """ + Write buildinstall.metadata file containing all the information about + buildinstall phase input and environment. + + This file is later used to decide whether old buildinstall results can + be reused instead of generating them again. + + :param Compose compose: Current compose. + :param str arch: Current architecture. + :param Variant variant: Compose variant. + :param list cmd: List of command line arguments passed to buildinstall task. + :param list buildroot_rpms: List of NVRAs of all RPMs installed in the + buildinstall task's buildroot. + :param PkgsetPhase pkgset_phase: Package set phase instance. + """ + # Generate the list of `*-RPMs` log file. + log_filename = ("buildinstall-%s" % variant.uid) if variant else "buildinstall" + log_file = compose.paths.log.log_file(arch, log_filename + "-RPMs") + with open(log_file, "w") as f: + f.write("\n".join(buildroot_rpms)) + + # Write buildinstall.metadata only if particular variant is defined. + # The `variant` is `None` only if old "buildinstall" method is used. + if not variant: + return + + metadata = self._generate_buildinstall_metadata( + compose, arch, variant, cmd, buildroot_rpms, pkgset_phase + ) + + log_fname = "buildinstall-%s-logs/dummy" % variant.uid + log_dir = os.path.dirname(compose.paths.log.log_file(arch, log_fname)) + metadata_path = os.path.join(log_dir, "buildinstall.metadata") + with open(metadata_path, "wb") as f: + pickle.dump(metadata, f, protocol=pickle.HIGHEST_PROTOCOL) + + def _load_old_buildinstall_metadata(self, compose, arch, variant): + """ + Helper method to load "buildinstall.metadata" from old compose. + + :param Compose compose: Current compose. + :param str arch: Current architecture. + :param Variant variant: Compose variant. + """ + if not variant: + return None + + log_fname = "buildinstall-%s-logs/dummy" % variant.uid + metadata = os.path.join( + os.path.dirname(compose.paths.log.log_file(arch, log_fname)), + "buildinstall.metadata", + ) + old_metadata = compose.paths.old_compose_path(metadata) + if not old_metadata: + return None + + compose.log_info("Loading old BUILDINSTALL phase metadata: %s", old_metadata) + with open(old_metadata, "rb") as f: + old_result = pickle.load(f) + return old_result + + def _reuse_old_buildinstall_result(self, compose, arch, variant, cmd, pkgset_phase): + """ + Try to reuse old buildinstall results. + + :param Compose compose: Current compose. + :param str arch: Current architecture. + :param Variant variant: Compose variant. + :param list cmd: List of command line arguments passed to buildinstall task. + :param list buildroot_rpms: List of NVRAs of all RPMs installed in the + buildinstall task's buildroot. + :param PkgsetPhase pkgset_phase: Package set phase instance. + :return: True if old buildinstall phase results have been reused. + """ + log_msg = "Cannot reuse old BUILDINSTALL phase results - %s" + + if not compose.conf["buildinstall_allow_reuse"]: + compose.log_info(log_msg % "reuse of old buildinstall results is disabled.") + return + + # Load the old buildinstall.metadata. + old_metadata = self._load_old_buildinstall_metadata(compose, arch, variant) + if old_metadata is None: + compose.log_info(log_msg % "no old BUILDINSTALL metadata.") + return + + # For now try to reuse only if pungi_buildinstall plugin is used. + # This is the easiest approach, because we later need to filter out + # some parts of `cmd` and for pungi_buildinstall, the `cmd` is a dict + # which makes this easy. + if not isinstance(old_metadata["cmd"], dict) or not isinstance(cmd, dict): + compose.log_info(log_msg % "pungi_buildinstall plugin is not used.") + return + + # Filter out "outputdir" and "sources" because they change everytime. + # The "sources" are not important, because we check the buildinstall + # input on RPM level. + cmd_copy = copy(cmd) + for key in ["outputdir", "sources"]: + del cmd_copy[key] + del old_metadata["cmd"][key] + + # Do not reuse if command line arguments are not the same. + if old_metadata["cmd"] != cmd_copy: + compose.log_info(log_msg % "lorax command line arguments differ.") + return + + # Check that the RPMs installed in the old boot.iso exists in the very + # same versions/releases in this compose. + for rpm_path in old_metadata["installed_rpms"]: + found = False + for pkgset in pkgset_phase.package_sets: + global_pkgset = pkgset["global"] + if rpm_path in global_pkgset.file_cache: + found = True + break + if not found: + compose.log_info( + log_msg % "RPM %s does not exist in new compose." % rpm_path + ) + return + + # Ask Koji for all the RPMs in the `runroot_tag` and check that + # those installed in the old buildinstall buildroot are still in the + # very same versions/releases. + koji_wrapper = kojiwrapper.KojiWrapper(compose.conf["koji_profile"]) + rpms = koji_wrapper.koji_proxy.listTaggedRPMS( + compose.conf.get("runroot_tag"), inherit=True, latest=True + )[0] + rpm_nvras = set() + for rpm in rpms: + rpm_nvras.add(kobo.rpmlib.make_nvra(rpm, add_rpm=False, force_epoch=False)) + for old_nvra in old_metadata["buildroot_rpms"]: + if old_nvra not in rpm_nvras: + compose.log_info( + log_msg % "RPM %s does not exist in new buildroot." % old_nvra + ) + return + + # We can reuse the old buildinstall results! + compose.log_info("Reusing old BUILDINSTALL phase output") + + # Copy old buildinstall output to this this compose. + final_output_dir = compose.paths.work.buildinstall_dir(arch, variant=variant) + old_final_output_dir = compose.paths.old_compose_path(final_output_dir) + copy_all(old_final_output_dir, final_output_dir) + + # Copy old buildinstall logs to this compose. + log_fname = "buildinstall-%s-logs/dummy" % variant.uid + final_log_dir = os.path.dirname(compose.paths.log.log_file(arch, log_fname)) + old_final_log_dir = compose.paths.old_compose_path(final_log_dir) + if not os.path.exists(final_log_dir): + makedirs(final_log_dir) + copy_all(old_final_log_dir, final_log_dir) + + # Write the buildinstall metadata so next compose can reuse this compose. + self._write_buildinstall_metadata( + compose, arch, variant, cmd, old_metadata["buildroot_rpms"], pkgset_phase + ) + + return True + + def worker(self, compose, arch, variant, cmd, pkgset_phase, num): buildinstall_method = compose.conf["buildinstall_method"] lorax_use_koji_plugin = compose.conf["lorax_use_koji_plugin"] log_filename = ("buildinstall-%s" % variant.uid) if variant else "buildinstall" @@ -536,6 +754,14 @@ class BuildinstallThread(WorkerThread): elif buildinstall_method == "buildinstall": packages += ["anaconda"] + if self._reuse_old_buildinstall_result( + compose, arch, variant, cmd, pkgset_phase + ): + self.copy_files(compose, variant, arch) + self.pool.finished_tasks.add((variant.uid if variant else None, arch)) + self.pool.log_info("[DONE ] %s" % msg) + return + # This should avoid a possible race condition with multiple processes # trying to get a kerberos ticket at the same time. # Kerberos authentication failed: @@ -592,15 +818,15 @@ class BuildinstallThread(WorkerThread): log_dir = os.path.join(output_dir, "logs") move_all(log_dir, final_log_dir, rm_src_dir=True) - log_file = compose.paths.log.log_file(arch, log_filename + "-RPMs") rpms = runroot.get_buildroot_rpms() - with open(log_file, "w") as f: - f.write("\n".join(rpms)) - - self.pool.finished_tasks.add((variant.uid if variant else None, arch)) + self._write_buildinstall_metadata( + compose, arch, variant, cmd, rpms, pkgset_phase + ) self.copy_files(compose, variant, arch) + self.pool.finished_tasks.add((variant.uid if variant else None, arch)) + self.pool.log_info("[DONE ] %s" % msg) def copy_files(self, compose, variant, arch): diff --git a/tests/helpers.py b/tests/helpers.py index 8ea06ca4..a3969953 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -129,6 +129,29 @@ class MockVariant(mock.Mock): return module_stream +class MockPackageSet(dict): + def __init__(self, *args): + for pkg in args: + self[pkg.path] = pkg + + +class MockPkg(object): + def __init__(self, path, is_system_release=False, **kwargs): + self.path = path + self.is_system_release = is_system_release + filename = os.path.basename(path) + self.nvr, self.arch, _ = filename.rsplit(".", 2) + self.name, self.version, self.release = self.nvr.rsplit("-", 2) + for k, v in kwargs.items(): + setattr(self, k, v) + + def __repr__(self): + return self.nvr + + def __lt__(self, another): + return self.nvr < another.nvr + + class IterableMock(mock.Mock): def __iter__(self): return iter([]) diff --git a/tests/test_buildinstall.py b/tests/test_buildinstall.py index 29b4664a..2ac404a2 100644 --- a/tests/test_buildinstall.py +++ b/tests/test_buildinstall.py @@ -3,6 +3,7 @@ import mock import six +from copy import copy import os @@ -13,7 +14,7 @@ from pungi.phases.buildinstall import ( BOOT_CONFIGS, tweak_configs, ) -from tests.helpers import DummyCompose, PungiTestCase, touch +from tests.helpers import DummyCompose, PungiTestCase, touch, MockPackageSet, MockPkg class BuildInstallCompose(DummyCompose): @@ -1100,7 +1101,7 @@ class BuildinstallThreadTestCase(PungiTestCase): def setUp(self): super(BuildinstallThreadTestCase, self).setUp() self.pool = mock.Mock(finished_tasks=set()) - self.cmd = mock.Mock() + self.cmd = ["echo", "1"] @mock.patch("pungi.phases.buildinstall.link_boot_iso") @mock.patch("pungi.phases.buildinstall.tweak_buildinstall") @@ -1134,7 +1135,11 @@ class BuildinstallThreadTestCase(PungiTestCase): t = BuildinstallThread(self.pool) with mock.patch("time.sleep"): - t.process((compose, "x86_64", compose.variants["Server"], self.cmd), 0) + pkgset_phase = self._make_pkgset_phase(["p1"]) + t.process( + (compose, "x86_64", compose.variants["Server"], self.cmd, pkgset_phase), + 0, + ) destdir = os.path.join(self.topdir, "work/x86_64/buildinstall/Server") self.assertEqual( @@ -1231,7 +1236,11 @@ class BuildinstallThreadTestCase(PungiTestCase): t = BuildinstallThread(self.pool) with mock.patch("time.sleep"): - t.process((compose, "x86_64", compose.variants["Server"], self.cmd), 0) + pkgset_phase = self._make_pkgset_phase(["p1"]) + t.process( + (compose, "x86_64", compose.variants["Server"], self.cmd, pkgset_phase), + 0, + ) destdir = os.path.join(self.topdir, "work/x86_64/buildinstall/Server") self.assertEqual( @@ -1328,7 +1337,8 @@ class BuildinstallThreadTestCase(PungiTestCase): t = BuildinstallThread(self.pool) with mock.patch("time.sleep"): - t.process((compose, "amd64", None, self.cmd), 0) + pkgset_phase = self._make_pkgset_phase(["p1"]) + t.process((compose, "amd64", None, self.cmd, pkgset_phase), 0) destdir = os.path.join(self.topdir, "work/amd64/buildinstall") self.assertEqual( @@ -1414,7 +1424,8 @@ class BuildinstallThreadTestCase(PungiTestCase): t = BuildinstallThread(self.pool) with mock.patch("time.sleep"): - t.process((compose, "x86_64", None, self.cmd), 0) + pkgset_phase = self._make_pkgset_phase(["p1"]) + t.process((compose, "x86_64", None, self.cmd, pkgset_phase), 0) compose._logger.error.assert_has_calls( [ @@ -1455,7 +1466,11 @@ class BuildinstallThreadTestCase(PungiTestCase): t = BuildinstallThread(self.pool) with mock.patch("time.sleep"): - t.process((compose, "x86_64", compose.variants["Server"], self.cmd), 0) + pkgset_phase = self._make_pkgset_phase(["p1"]) + t.process( + (compose, "x86_64", compose.variants["Server"], self.cmd, pkgset_phase), + 0, + ) compose._logger.error.assert_has_calls( [ @@ -1494,7 +1509,11 @@ class BuildinstallThreadTestCase(PungiTestCase): t = BuildinstallThread(self.pool) with mock.patch("time.sleep"): - t.process((compose, "x86_64", compose.variants["Server"], self.cmd), 0) + pkgset_phase = self._make_pkgset_phase(["p1"]) + t.process( + (compose, "x86_64", compose.variants["Server"], self.cmd, pkgset_phase), + 0, + ) self.assertEqual(0, len(run.mock_calls)) @@ -1535,7 +1554,11 @@ class BuildinstallThreadTestCase(PungiTestCase): t = BuildinstallThread(self.pool) with mock.patch("time.sleep"): - t.process((compose, "x86_64", compose.variants["Server"], self.cmd), 0) + pkgset_phase = self._make_pkgset_phase(["p1"]) + t.process( + (compose, "x86_64", compose.variants["Server"], self.cmd, pkgset_phase), + 0, + ) self.assertEqual( get_runroot_cmd.mock_calls, @@ -1613,6 +1636,203 @@ class BuildinstallThreadTestCase(PungiTestCase): [mock.call(compose, "x86_64", compose.variants["Server"], False)], ) + def _prepare_buildinstall_reuse_test(self): + compose = BuildInstallCompose( + self.topdir, + { + "buildinstall_allow_reuse": True, + "buildinstall_method": "lorax", + "runroot_tag": "rrt", + "koji_profile": "koji", + }, + ) + + pkgset = MockPackageSet( + MockPkg("/build/kernel-1.0.0-1.x86_64.rpm"), + MockPkg("/build/kernel-1.0.0-1.i686.rpm"), + MockPkg("/build/bash-1.0.0-1.x86_64.rpm"), + ) + pkgset.file_cache = pkgset + pkgsets = [{"global": pkgset, "x86_64": pkgset}] + pkgset_phase = mock.Mock(package_sets=pkgsets) + + cmd = { + "add-arch-template": [], + "buildarch": "x86_64", + "outputdir": self.topdir, + "product": "Fedora", + "release": "31", + "sources": ["/tmp/test/repo"], + "variant": "Server", + "version": "1", + } + return compose, pkgset_phase, cmd + + @mock.patch("os.listdir") + @mock.patch("os.path.exists") + def test_generate_buildinstall_metadata(self, exists, listdir): + compose, pkgset_phase, cmd = self._prepare_buildinstall_reuse_test() + buildroot_rpms = ["bash-1-1.x86_64", "httpd-1-1.x86_64"] + listdir.return_value = ["kernel"] + + t = BuildinstallThread(self.pool) + metadata = t._generate_buildinstall_metadata( + compose, + "x86_64", + compose.variants["Server"], + cmd, + buildroot_rpms, + pkgset_phase, + ) + self.assertEqual(metadata["cmd"], cmd) + self.assertEqual(metadata["buildroot_rpms"], buildroot_rpms) + self.assertEqual( + metadata["installed_rpms"], + ["/build/kernel-1.0.0-1.i686.rpm", "/build/kernel-1.0.0-1.x86_64.rpm"], + ) + + @mock.patch( + "pungi.phases.buildinstall.BuildinstallThread._write_buildinstall_metadata" + ) + @mock.patch( + "pungi.phases.buildinstall.BuildinstallThread._load_old_buildinstall_metadata" + ) + @mock.patch("pungi.wrappers.kojiwrapper.KojiWrapper") + @mock.patch("pungi.phases.buildinstall.copy_all") + def test_reuse_old_buildinstall_result( + self, + copy_all, + KojiWrapperMock, + load_old_buildinstall_metadata, + write_buildinstall_metadata, + ): + compose, pkgset_phase, cmd = self._prepare_buildinstall_reuse_test() + + listTaggedRPMS = KojiWrapperMock.return_value.koji_proxy.listTaggedRPMS + listTaggedRPMS.return_value = [ + [{"name": "bash", "version": "1", "release": 1, "arch": "x86_64"}], + [], + ] + + load_old_buildinstall_metadata.return_value = { + "cmd": cmd, + "installed_rpms": ["/build/kernel-1.0.0-1.x86_64.rpm"], + "buildroot_rpms": ["bash-1-1.x86_64"], + } + + t = BuildinstallThread(self.pool) + with mock.patch.object(compose.paths, "old_compose_path") as old_compose_path: + old_compose_path.side_effect = ["/tmp/old/1", "/tmp/old/2"] + ret = t._reuse_old_buildinstall_result( + compose, "x86_64", compose.variants["Server"], cmd, pkgset_phase + ) + + self.assertEqual(ret, True) + self.assertEqual( + copy_all.mock_calls, + [ + mock.call( + "/tmp/old/1", + os.path.join(self.topdir, "work/x86_64/buildinstall/Server"), + ), + mock.call( + "/tmp/old/2", + os.path.join(self.topdir, "logs/x86_64/buildinstall-Server-logs"), + ), + ], + ) + write_buildinstall_metadata.assert_called_once_with( + compose, + "x86_64", + compose.variants["Server"], + cmd, + ["bash-1-1.x86_64"], + pkgset_phase, + ) + + @mock.patch( + "pungi.phases.buildinstall.BuildinstallThread._load_old_buildinstall_metadata" + ) + def test_reuse_old_buildinstall_result_no_old_compose( + self, load_old_buildinstall_metadata, + ): + compose, pkgset_phase, cmd = self._prepare_buildinstall_reuse_test() + load_old_buildinstall_metadata.return_value = None + + t = BuildinstallThread(self.pool) + ret = t._reuse_old_buildinstall_result( + compose, "x86_64", compose.variants["Server"], cmd, pkgset_phase + ) + self.assertEqual(ret, None) + + @mock.patch( + "pungi.phases.buildinstall.BuildinstallThread._load_old_buildinstall_metadata" + ) + def test_reuse_old_buildinstall_result_different_cmd( + self, load_old_buildinstall_metadata, + ): + compose, pkgset_phase, cmd = self._prepare_buildinstall_reuse_test() + + old_cmd = copy(cmd) + old_cmd["version"] = "32" + + load_old_buildinstall_metadata.return_value = { + "cmd": old_cmd, + "installed_rpms": ["/build/kernel-1.0.0-1.x86_64.rpm"], + "buildroot_rpms": ["bash-1-1.x86_64"], + } + + t = BuildinstallThread(self.pool) + ret = t._reuse_old_buildinstall_result( + compose, "x86_64", compose.variants["Server"], cmd, pkgset_phase + ) + self.assertEqual(ret, None) + + @mock.patch( + "pungi.phases.buildinstall.BuildinstallThread._load_old_buildinstall_metadata" + ) + def test_reuse_old_buildinstall_result_different_installed_pkgs( + self, load_old_buildinstall_metadata, + ): + compose, pkgset_phase, cmd = self._prepare_buildinstall_reuse_test() + load_old_buildinstall_metadata.return_value = { + "cmd": cmd, + "installed_rpms": ["/build/kernel-1.0.0-0.x86_64.rpm"], + "buildroot_rpms": ["bash-1-1.x86_64"], + } + + t = BuildinstallThread(self.pool) + ret = t._reuse_old_buildinstall_result( + compose, "x86_64", compose.variants["Server"], cmd, pkgset_phase + ) + self.assertEqual(ret, None) + + @mock.patch( + "pungi.phases.buildinstall.BuildinstallThread._load_old_buildinstall_metadata" + ) + @mock.patch("pungi.wrappers.kojiwrapper.KojiWrapper") + def test_reuse_old_buildinstall_result_different_buildroot_rpms( + self, KojiWrapperMock, load_old_buildinstall_metadata, + ): + compose, pkgset_phase, cmd = self._prepare_buildinstall_reuse_test() + load_old_buildinstall_metadata.return_value = { + "cmd": cmd, + "installed_rpms": ["/build/kernel-1.0.0-1.x86_64.rpm"], + "buildroot_rpms": ["bash-1-1.x86_64"], + } + + listTaggedRPMS = KojiWrapperMock.return_value.koji_proxy.listTaggedRPMS + listTaggedRPMS.return_value = [ + [{"name": "bash", "version": "1", "release": 2, "arch": "x86_64"}], + [], + ] + + t = BuildinstallThread(self.pool) + ret = t._reuse_old_buildinstall_result( + compose, "x86_64", compose.variants["Server"], cmd, pkgset_phase + ) + self.assertEqual(ret, None) + class TestSymlinkIso(PungiTestCase): def setUp(self): diff --git a/tests/test_gather_phase.py b/tests/test_gather_phase.py index 4029fe84..b56a90c9 100644 --- a/tests/test_gather_phase.py +++ b/tests/test_gather_phase.py @@ -15,29 +15,7 @@ from pungi.phases import gather from pungi.phases.pkgset.common import MaterializedPackageSet from pungi.phases.gather import _mk_pkg_map from tests import helpers - - -class MockPackageSet(dict): - def __init__(self, *args): - for pkg in args: - self[pkg.path] = pkg - - -class MockPkg(object): - def __init__(self, path, is_system_release=False, **kwargs): - self.path = path - self.is_system_release = is_system_release - filename = os.path.basename(path) - self.nvr, self.arch, _ = filename.rsplit(".", 2) - self.name, self.version, self.release = self.nvr.rsplit("-", 2) - for k, v in kwargs.items(): - setattr(self, k, v) - - def __repr__(self): - return self.nvr - - def __lt__(self, another): - return self.nvr < another.nvr +from tests.helpers import MockPackageSet, MockPkg def _join(a, *rest):