diff --git a/doc/configuration.rst b/doc/configuration.rst index f564140e..a855e5c9 100644 --- a/doc/configuration.rst +++ b/doc/configuration.rst @@ -202,18 +202,18 @@ Options ------- There a couple common format specifiers available for both the options: - * compose_id - * release_short - * version - * date - * respin - * type - * type_suffix - * label - * label_major_version - * variant - * arch - * disc_type + * ``compose_id`` + * ``release_short`` + * ``version`` + * ``date`` + * ``respin`` + * ``type`` + * ``type_suffix`` + * ``label`` + * ``label_major_version`` + * ``variant`` + * ``arch`` + * ``disc_type`` **image_name_format** [optional] (*str*) -- Python's format string to serve as template for image names @@ -222,23 +222,33 @@ There a couple common format specifiers available for both the options: means ``createiso``, ``live_images`` and ``buildinstall``. Available extra keys are: - * disc_num - * suffix + * ``disc_num`` + * ``suffix`` **image_volid_formats** [optional] (*list*) -- A list of format strings for generating volume id. The extra available keys are: - * base_product_short - * base_product_version + * ``base_product_short`` + * ``base_product_version`` **image_volid_layered_product_formats** [optional] - (*list*) -- A listof format strings for generating volume id for layered + (*list*) -- A list of format strings for generating volume id for layered products. The keys available are the same as for ``image_volid_formats``. **volume_id_substitutions** [optional] (*dict*) -- A mapping of string replacements to shorten the volume id. +**disc_types** [optional] + (*dict*) -- A mapping for customizing ``disc_type`` used in image names. + + Available keys are: + * ``boot`` -- for ``boot.iso`` images created in *buildinstall* phase + * ``live`` -- for images created by *live_images* phase + * ``dvd`` -- for images created by *createiso* phase + + Default values are the same as the keys. + Example ------- :: @@ -259,6 +269,12 @@ Example 'TC': 'T', } + disc_types = { + 'boot': 'netinst', + 'live': 'Live', + 'dvd': 'DVD', + } + Signing ======= diff --git a/pungi/phases/buildinstall.py b/pungi/phases/buildinstall.py index a0665eb3..5cf40217 100644 --- a/pungi/phases/buildinstall.py +++ b/pungi/phases/buildinstall.py @@ -189,7 +189,7 @@ class BuildinstallPhase(PhaseBase): label = "" volid = get_volid(self.compose, arch, variant, escape_spaces=False, disc_type="dvd") tweak_buildinstall(buildinstall_dir, os_tree, arch, variant.uid, label, volid, kickstart_file) - symlink_boot_iso(self.compose, arch, variant) + link_boot_iso(self.compose, arch, variant) def get_kickstart_file(compose): @@ -312,10 +312,12 @@ def tweak_buildinstall(src, dst, arch, variant, label, volid, kickstart_file=Non shutil.rmtree(tmp_dir) -def symlink_boot_iso(compose, arch, variant): +def link_boot_iso(compose, arch, variant): if arch == "src": return + disc_type = compose.conf.get('disc_types', {}).get('boot', 'boot') + symlink_isos_to = compose.conf.get("symlink_isos_to", None) os_tree = compose.paths.compose.os_tree(arch, variant) # TODO: find in treeinfo? @@ -323,8 +325,8 @@ def symlink_boot_iso(compose, arch, variant): if not os.path.isfile(boot_iso_path): return - msg = "Symlinking boot.iso (arch: %s, variant: %s)" % (arch, variant) - filename = compose.get_image_name(arch, variant, disc_type="boot", + msg = "Linking boot.iso (arch: %s, variant: %s)" % (arch, variant) + filename = compose.get_image_name(arch, variant, disc_type=disc_type, disc_num=None, suffix=".iso") new_boot_iso_path = compose.paths.compose.iso_path(arch, variant, filename, symlink_to=symlink_isos_to) diff --git a/pungi/phases/createiso.py b/pungi/phases/createiso.py index bf582f9d..ec41221c 100644 --- a/pungi/phases/createiso.py +++ b/pungi/phases/createiso.py @@ -54,6 +54,7 @@ class CreateisoPhase(PhaseBase): def run(self): iso = IsoWrapper(logger=self.compose._logger) symlink_isos_to = self.compose.conf.get("symlink_isos_to", None) + disc_type = self.compose.conf.get('disc_types', {}).get('dvd', 'dvd') deliverables = [] commands = [] @@ -64,7 +65,7 @@ class CreateisoPhase(PhaseBase): self.compose.log_info("Skipping createiso for %s.%s due to config option" % (variant, arch)) continue - volid = get_volid(self.compose, arch, variant, disc_type='dvd') + volid = get_volid(self.compose, arch, variant, disc_type=disc_type) os_tree = self.compose.paths.compose.os_tree(arch, variant) iso_dir = self.compose.paths.compose.iso_dir(arch, variant, symlink_to=symlink_isos_to) @@ -90,9 +91,8 @@ class CreateisoPhase(PhaseBase): for disc_num, iso_data in enumerate(split_iso_data): disc_num += 1 - # XXX: hardcoded disc_type filename = self.compose.get_image_name(arch, variant, - disc_type="dvd", + disc_type=disc_type, disc_num=disc_num) iso_path = self.compose.paths.compose.iso_path(arch, variant, diff --git a/pungi/phases/live_images.py b/pungi/phases/live_images.py index 1ae4c381..99ed5c98 100644 --- a/pungi/phases/live_images.py +++ b/pungi/phases/live_images.py @@ -178,6 +178,8 @@ class LiveImagesPhase(PhaseBase): if self.compose.conf.get('live_images_no_rename', False): return None + disc_type = self.compose.conf.get('disc_types', {}).get('live', 'live') + format = "%(compose_id)s-%(variant)s-%(arch)s-%(disc_type)s%(disc_num)s%(suffix)s" # Custom name (prefix) if name: @@ -186,8 +188,8 @@ class LiveImagesPhase(PhaseBase): custom_iso_name += "-%s" % version format = custom_iso_name + "-%(variant)s-%(arch)s-%(disc_type)s%(disc_num)s%(suffix)s" - # XXX: hardcoded disc_type and disc_num - return self.compose.get_image_name(arch, variant, disc_type="live", + # XXX: hardcoded disc_num + return self.compose.get_image_name(arch, variant, disc_type=disc_type, disc_num=None, format=format) def stop(self, *args, **kwargs): diff --git a/tests/test_buildinstall.py b/tests/test_buildinstall.py index cd0c8b63..74e31790 100755 --- a/tests/test_buildinstall.py +++ b/tests/test_buildinstall.py @@ -10,7 +10,7 @@ import sys sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) -from pungi.phases.buildinstall import BuildinstallPhase, BuildinstallThread, symlink_boot_iso +from pungi.phases.buildinstall import BuildinstallPhase, BuildinstallThread, link_boot_iso from tests.helpers import DummyCompose, PungiTestCase, touch @@ -312,14 +312,14 @@ class TestBuildinstallPhase(PungiTestCase): class TestCopyFiles(PungiTestCase): - @mock.patch('pungi.phases.buildinstall.symlink_boot_iso') + @mock.patch('pungi.phases.buildinstall.link_boot_iso') @mock.patch('pungi.phases.buildinstall.tweak_buildinstall') @mock.patch('pungi.phases.buildinstall.get_volid') @mock.patch('os.listdir') @mock.patch('os.path.isdir') @mock.patch('pungi.phases.buildinstall.get_kickstart_file') def test_copy_files_buildinstall(self, get_kickstart_file, isdir, listdir, - get_volid, tweak_buildinstall, symlink_boot_iso): + get_volid, tweak_buildinstall, link_boot_iso): compose = BuildInstallCompose(self.topdir, { 'buildinstall_method': 'buildinstall' }) @@ -349,19 +349,19 @@ class TestCopyFiles(PungiTestCase): self.topdir + '/compose/Client/amd64/os', 'amd64', 'Client', '', 'Client.amd64', 'kickstart')]) self.assertItemsEqual( - symlink_boot_iso.mock_calls, + link_boot_iso.mock_calls, [mock.call(compose, 'x86_64', compose.variants['Server']), mock.call(compose, 'amd64', compose.variants['Client']), mock.call(compose, 'amd64', compose.variants['Server'])]) - @mock.patch('pungi.phases.buildinstall.symlink_boot_iso') + @mock.patch('pungi.phases.buildinstall.link_boot_iso') @mock.patch('pungi.phases.buildinstall.tweak_buildinstall') @mock.patch('pungi.phases.buildinstall.get_volid') @mock.patch('os.listdir') @mock.patch('os.path.isdir') @mock.patch('pungi.phases.buildinstall.get_kickstart_file') def test_copy_files_lorax(self, get_kickstart_file, isdir, listdir, - get_volid, tweak_buildinstall, symlink_boot_iso): + get_volid, tweak_buildinstall, link_boot_iso): compose = BuildInstallCompose(self.topdir, { 'buildinstall_method': 'lorax' }) @@ -391,7 +391,7 @@ class TestCopyFiles(PungiTestCase): self.topdir + '/compose/Client/amd64/os', 'amd64', 'Client', '', 'Client.amd64', 'kickstart')]) self.assertItemsEqual( - symlink_boot_iso.mock_calls, + link_boot_iso.mock_calls, [mock.call(compose, 'x86_64', compose.variants['Server']), mock.call(compose, 'amd64', compose.variants['Client']), mock.call(compose, 'amd64', compose.variants['Server'])]) @@ -576,7 +576,7 @@ class TestSymlinkIso(PungiTestCase): get_file_size.return_value = 1024 get_mtime.return_value = 13579 - symlink_boot_iso(self.compose, 'x86_64', self.compose.variants['Server']) + link_boot_iso(self.compose, 'x86_64', self.compose.variants['Server']) tgt = self.topdir + '/compose/Server/x86_64/iso/image-name' self.assertTrue(os.path.isfile(tgt)) @@ -611,6 +611,55 @@ class TestSymlinkIso(PungiTestCase): self.assertEqual(self.compose.im.add.mock_calls, [mock.call('Server', 'x86_64', image)]) + @mock.patch('pungi.phases.buildinstall.Image') + @mock.patch('pungi.phases.buildinstall.get_mtime') + @mock.patch('pungi.phases.buildinstall.get_file_size') + @mock.patch('pungi.phases.buildinstall.IsoWrapper') + @mock.patch('pungi.phases.buildinstall.run') + def test_hardlink_with_custom_type(self, run, IsoWrapperCls, get_file_size, get_mtime, ImageCls): + self.compose.conf = { + 'buildinstall_symlink': False, + 'disc_types': {'boot': 'netinst'}, + } + IsoWrapper = IsoWrapperCls.return_value + get_file_size.return_value = 1024 + get_mtime.return_value = 13579 + + link_boot_iso(self.compose, 'x86_64', self.compose.variants['Server']) + + tgt = self.topdir + '/compose/Server/x86_64/iso/image-name' + self.assertTrue(os.path.isfile(tgt)) + self.assertEqual(os.stat(tgt).st_ino, + os.stat(self.topdir + '/compose/Server/x86_64/os/images/boot.iso').st_ino) + + self.assertItemsEqual( + self.compose.get_image_name.mock_calls, + [mock.call('x86_64', self.compose.variants['Server'], + disc_type='netinst', disc_num=None, suffix='.iso')]) + self.assertItemsEqual(IsoWrapper.get_implanted_md5.mock_calls, + [mock.call(tgt)]) + self.assertItemsEqual(IsoWrapper.get_manifest_cmd.mock_calls, + [mock.call('image-name')]) + self.assertItemsEqual(IsoWrapper.get_volume_id.mock_calls, + [mock.call(tgt)]) + self.assertItemsEqual(run.mock_calls, + [mock.call(IsoWrapper.get_manifest_cmd.return_value, + workdir=self.topdir + '/compose/Server/x86_64/iso')]) + + image = ImageCls.return_value + self.assertEqual(image.path, 'Server/x86_64/iso/image-name') + self.assertEqual(image.mtime, 13579) + self.assertEqual(image.size, 1024) + self.assertEqual(image.arch, 'x86_64') + self.assertEqual(image.type, "boot") + self.assertEqual(image.format, "iso") + self.assertEqual(image.disc_number, 1) + self.assertEqual(image.disc_count, 1) + self.assertEqual(image.bootable, True) + self.assertEqual(image.implant_md5, IsoWrapper.get_implanted_md5.return_value) + self.assertEqual(self.compose.im.add.mock_calls, + [mock.call('Server', 'x86_64', image)]) + if __name__ == "__main__": unittest.main() diff --git a/tests/test_liveimagesphase.py b/tests/test_liveimagesphase.py index aea61770..fae97ba6 100755 --- a/tests/test_liveimagesphase.py +++ b/tests/test_liveimagesphase.py @@ -58,6 +58,10 @@ class TestLiveImagesPhase(PungiTestCase): 'ksurl': None}, compose.variants['Client'], 'amd64'))]) + self.assertItemsEqual( + compose.get_image_name.mock_calls, + [mock.call('amd64', compose.variants['Client'], disc_num=None, disc_type='live', + format='%(compose_id)s-%(variant)s-%(arch)s-%(disc_type)s%(disc_num)s%(suffix)s')]) @mock.patch('pungi.phases.live_images.ThreadPool') def test_live_image_build_single_repo_from(self, ThreadPool): @@ -260,6 +264,54 @@ class TestLiveImagesPhase(PungiTestCase): self.assertEqual(resolve_git_url.mock_calls, [mock.call('https://git.example.com/kickstarts.git?#HEAD')]) + @mock.patch('pungi.phases.live_images.ThreadPool') + def test_live_image_build_custom_type(self, ThreadPool): + compose = DummyCompose(self.topdir, { + 'disc_types': {'live': 'Live'}, + 'live_images': [ + ('^Client$', { + 'amd64': { + 'kickstart': 'test.ks', + 'additional_repos': ['http://example.com/repo/'], + 'repo_from': ['Everything'], + 'release': None, + } + }) + ], + }) + + phase = LiveImagesPhase(compose) + + phase.run() + + # assert at least one thread was started + self.assertTrue(phase.pool.add.called) + self.maxDiff = None + self.assertItemsEqual(phase.pool.queue_put.mock_calls, + [mock.call((compose, + {'ks_file': 'test.ks', + 'build_arch': 'amd64', + 'dest_dir': self.topdir + '/compose/Client/amd64/iso', + 'scratch': False, + 'repos': [self.topdir + '/compose/Client/amd64/os', + 'http://example.com/repo/', + self.topdir + '/compose/Everything/amd64/os'], + 'label': '', + 'name': None, + 'filename': 'image-name', + 'version': None, + 'specfile': None, + 'sign': False, + 'type': 'live', + 'release': '20151203.0', + 'ksurl': None}, + compose.variants['Client'], + 'amd64'))]) + self.assertItemsEqual( + compose.get_image_name.mock_calls, + [mock.call('amd64', compose.variants['Client'], disc_num=None, disc_type='Live', + format='%(compose_id)s-%(variant)s-%(arch)s-%(disc_type)s%(disc_num)s%(suffix)s')]) + class TestCreateLiveImageThread(PungiTestCase):