diff --git a/doc/configuration.rst b/doc/configuration.rst index cdaa6891..881a1931 100644 --- a/doc/configuration.rst +++ b/doc/configuration.rst @@ -44,7 +44,6 @@ Minimal Config Example # BUILDINSTALL bootable = True buildinstall_method = "lorax" - buildinstall_upgrade_image = True Release @@ -208,16 +207,44 @@ Anaconda installer is historically called `buildinstall `_. Options: +-------- -* bootable (*bool*) -- -* buildinstall_method (*str*) -- "lorax" (f16+, rhel7+) or "buildinstall" (older releases) -* buildinstall_upgrade_image (*bool*) -- build upgrade images, applicable on "lorax" buildinstall method +**bootable** + (*bool*) -- whether to run the buildinstall phase +**buildinstall_method** + (*str*) -- "lorax" (f16+, rhel7+) or "buildinstall" (older releases) +**buildinstall_upgrade_image** [deprecated] + (*bool*) -- use ``noupgrade`` with ``lorax_options`` instead +**lorax_options** + (*list*) -- special options passed on to *lorax*. -Example:: + Format: ``[(variant_uid_regex, {arch|*: {option: name}})]``. + + Recognized options are: + * ``bugurl`` -- *str* (default ``None``) + * ``nomacboot`` -- *bool* (default ``True``) + * ``noupgrade`` -- *bool* (default ``True``) + +Example +------- +:: bootable = True buildinstall_method = "lorax" + # Enables macboot on x86_64 for all variants and builds upgrade images + # everywhere. + lorax_options = [ + ("^.*$", { + "x86_64": { + "nomacboot": False + } + "*": { + "noupgrade": False + } + }) + ] + .. note:: diff --git a/pungi/phases/buildinstall.py b/pungi/phases/buildinstall.py index f97f4167..fb87e889 100644 --- a/pungi/phases/buildinstall.py +++ b/pungi/phases/buildinstall.py @@ -28,7 +28,7 @@ from kobo.shortcuts import run, relative_path from productmd.images import Image from pungi.arch import get_valid_arches -from pungi.util import get_buildroot_rpms, get_volid +from pungi.util import get_buildroot_rpms, get_volid, get_arch_variant_data from pungi.wrappers.lorax import LoraxWrapper from pungi.wrappers.kojiwrapper import KojiWrapper from pungi.wrappers.iso import IsoWrapper @@ -52,11 +52,20 @@ class BuildinstallPhase(PhaseBase): "requires": ( (lambda x: bool(x) is True, ["bootable"]), ), + "conflicts": ( + (lambda val: val == "buildinstall", ["lorax_options"]), + ), }, { "name": "buildinstall_upgrade_image", "expected_types": [bool], "optional": True, + "deprecated": True, + "comment": "use lorax_options instead", + }, + { + "name": "lorax_options", + "optional": True, }, { "name": "buildinstall_kickstart", @@ -78,12 +87,37 @@ class BuildinstallPhase(PhaseBase): return True return False + def _get_lorax_cmd(self, repo_baseurl, output_dir, variant, arch, buildarch, volid): + noupgrade = True + bugurl = None + nomacboot = True + for data in get_arch_variant_data(self.compose.conf, 'lorax_options', arch, variant): + if not data.get('noupgrade', True): + noupgrade = False + if data.get('bugurl'): + bugurl = data.get('bugurl') + if not data.get('nomacboot', True): + nomacboot = False + lorax = LoraxWrapper() + return lorax.get_lorax_cmd(self.compose.conf["release_name"], + self.compose.conf["release_version"], + self.compose.conf["release_version"], + repo_baseurl, + os.path.join(output_dir, variant.uid), + variant=variant.uid, + buildinstallpackages=variant.buildinstallpackages, + is_final=self.compose.supported, + buildarch=buildarch, + volid=volid, + nomacboot=nomacboot, + bugurl=bugurl, + noupgrade=noupgrade) + def run(self): lorax = LoraxWrapper() product = self.compose.conf["release_name"] version = self.compose.conf["release_version"] release = self.compose.conf["release_version"] - noupgrade = not self.compose.conf.get("buildinstall_upgrade_image", False) buildinstall_method = self.compose.conf["buildinstall_method"] commands = [] @@ -97,18 +131,8 @@ class BuildinstallPhase(PhaseBase): if buildinstall_method == "lorax": for variant in self.compose.get_variants(arch=arch, types=['variant']): commands.append( - lorax.get_lorax_cmd(product, - version, - release, - repo_baseurl, - os.path.join(output_dir, variant.uid), - variant=variant.uid, - buildinstallpackages=variant.buildinstallpackages, - is_final=self.compose.supported, - buildarch=buildarch, - volid=volid, - nomacboot=True, - noupgrade=noupgrade)) + self._get_lorax_cmd(repo_baseurl, output_dir, variant, arch, buildarch, volid) + ) elif buildinstall_method == "buildinstall": commands.append(lorax.get_buildinstall_cmd(product, version, diff --git a/tests/test_buildinstall.py b/tests/test_buildinstall.py index ad15189c..04978ed3 100755 --- a/tests/test_buildinstall.py +++ b/tests/test_buildinstall.py @@ -42,7 +42,7 @@ class _DummyCompose(object): return self.variants.get(arch, []) -class TestImageChecksumPhase(unittest.TestCase): +class TestBuildinstallPhase(unittest.TestCase): def test_config_skip_unless_bootable(self): compose = _DummyCompose({}) @@ -91,13 +91,16 @@ class TestImageChecksumPhase(unittest.TestCase): lorax.get_lorax_cmd.assert_has_calls( [mock.call('Test', '1', '1', 'file:///a/b/', '/buildinstall_dir/x86_64/Server', buildarch='x86_64', is_final=True, nomacboot=True, noupgrade=True, - volid='vol_id', variant='Server', buildinstallpackages=['bash', 'vim']), + volid='vol_id', variant='Server', buildinstallpackages=['bash', 'vim'], + bugurl=None), mock.call('Test', '1', '1', 'file:///a/b/', '/buildinstall_dir/amd64/Server', buildarch='amd64', is_final=True, nomacboot=True, noupgrade=True, - volid='vol_id', variant='Server', buildinstallpackages=['bash', 'vim']), + volid='vol_id', variant='Server', buildinstallpackages=['bash', 'vim'], + bugurl=None), mock.call('Test', '1', '1', 'file:///a/b/', '/buildinstall_dir/amd64/Client', buildarch='amd64', is_final=True, nomacboot=True, noupgrade=True, - volid='vol_id', variant='Client', buildinstallpackages=[])], + volid='vol_id', variant='Client', buildinstallpackages=[], + bugurl=None)], any_order=True) @mock.patch('pungi.phases.buildinstall.ThreadPool') @@ -132,6 +135,133 @@ class TestImageChecksumPhase(unittest.TestCase): buildarch='amd64', is_final=True, volid='vol_id')], any_order=True) + def test_global_upgrade_with_lorax(self): + compose = _DummyCompose({ + 'bootable': True, + 'buildinstall_method': 'lorax', + 'buildinstall_upgrade_image': True, + }) + + phase = BuildinstallPhase(compose) + + with self.assertRaises(ValueError) as ctx: + phase.validate() + + self.assertIn('Deprecated config option: buildinstall_upgrade_image', + ctx.exception.message) + + def test_lorax_options_with_buildinstall(self): + compose = _DummyCompose({ + 'bootable': True, + 'buildinstall_method': 'buildinstall', + 'lorax_options': [], + }) + + phase = BuildinstallPhase(compose) + + with self.assertRaises(ValueError) as ctx: + phase.validate() + + self.assertIn('buildinstall', ctx.exception.message) + self.assertIn('lorax_options', ctx.exception.message) + + @mock.patch('pungi.phases.buildinstall.ThreadPool') + @mock.patch('pungi.phases.buildinstall.LoraxWrapper') + @mock.patch('pungi.phases.buildinstall.get_volid') + def test_uses_lorax_options(self, get_volid, loraxCls, poolCls): + compose = _DummyCompose({ + 'bootable': True, + 'release_name': 'Test', + 'release_short': 't', + 'release_version': '1', + 'release_is_layered': False, + 'buildinstall_method': 'lorax', + 'lorax_options': [ + ('^Server$', { + 'x86_64': {'bugurl': 'http://example.com'}, + 'amd64': {'noupgrade': False} + }), + ('^Client$', { + '*': {'nomacboot': False} + }), + ] + }) + + get_volid.return_value = 'vol_id' + + phase = BuildinstallPhase(compose) + + phase.run() + + # Three items added for processing in total. + # Server.x86_64, Client.amd64, Server.x86_64 + pool = poolCls.return_value + self.assertEqual(3, len(pool.queue_put.mock_calls)) + + # Obtained correct lorax commands. + lorax = loraxCls.return_value + lorax.get_lorax_cmd.assert_has_calls( + [mock.call('Test', '1', '1', 'file:///a/b/', '/buildinstall_dir/x86_64/Server', + buildarch='x86_64', is_final=True, nomacboot=True, noupgrade=True, + volid='vol_id', variant='Server', buildinstallpackages=['bash', 'vim'], + bugurl='http://example.com'), + mock.call('Test', '1', '1', 'file:///a/b/', '/buildinstall_dir/amd64/Server', + buildarch='amd64', is_final=True, nomacboot=True, noupgrade=False, + volid='vol_id', variant='Server', buildinstallpackages=['bash', 'vim'], + bugurl=None), + mock.call('Test', '1', '1', 'file:///a/b/', '/buildinstall_dir/amd64/Client', + buildarch='amd64', is_final=True, nomacboot=False, noupgrade=True, + volid='vol_id', variant='Client', buildinstallpackages=[], + bugurl=None)], + any_order=True) + + @mock.patch('pungi.phases.buildinstall.ThreadPool') + @mock.patch('pungi.phases.buildinstall.LoraxWrapper') + @mock.patch('pungi.phases.buildinstall.get_volid') + def test_multiple_lorax_options(self, get_volid, loraxCls, poolCls): + compose = _DummyCompose({ + 'bootable': True, + 'release_name': 'Test', + 'release_short': 't', + 'release_version': '1', + 'release_is_layered': False, + 'buildinstall_method': 'lorax', + 'lorax_options': [ + ('^.*$', { + 'x86_64': {'nomacboot': False}, + '*': {'noupgrade': False} + }), + ] + }) + + get_volid.return_value = 'vol_id' + + phase = BuildinstallPhase(compose) + + phase.run() + + # Three items added for processing in total. + # Server.x86_64, Client.amd64, Server.x86_64 + pool = poolCls.return_value + self.assertEqual(3, len(pool.queue_put.mock_calls)) + + # Obtained correct lorax commands. + lorax = loraxCls.return_value + lorax.get_lorax_cmd.assert_has_calls( + [mock.call('Test', '1', '1', 'file:///a/b/', '/buildinstall_dir/x86_64/Server', + buildarch='x86_64', is_final=True, nomacboot=False, noupgrade=False, + volid='vol_id', variant='Server', buildinstallpackages=['bash', 'vim'], + bugurl=None), + mock.call('Test', '1', '1', 'file:///a/b/', '/buildinstall_dir/amd64/Server', + buildarch='amd64', is_final=True, nomacboot=True, noupgrade=False, + volid='vol_id', variant='Server', buildinstallpackages=['bash', 'vim'], + bugurl=None), + mock.call('Test', '1', '1', 'file:///a/b/', '/buildinstall_dir/amd64/Client', + buildarch='amd64', is_final=True, nomacboot=True, noupgrade=False, + volid='vol_id', variant='Client', buildinstallpackages=[], + bugurl=None)], + any_order=True) + class TestCopyFiles(unittest.TestCase):