From 595845e1042e007391d1c32acf88c14688a67a12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lubom=C3=ADr=20Sedl=C3=A1=C5=99?= Date: Wed, 24 Feb 2016 15:26:48 +0100 Subject: [PATCH 1/2] [live-media] Rename test case MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Originally it was copied from image build phase, and was not renamed. Signed-off-by: Lubomír Sedlář --- tests/test_livemediaphase.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_livemediaphase.py b/tests/test_livemediaphase.py index e20f98dc..9910f959 100755 --- a/tests/test_livemediaphase.py +++ b/tests/test_livemediaphase.py @@ -154,7 +154,7 @@ class TestLiveMediaPhase(PungiTestCase): }))]) -class TestCreateImageBuildThread(PungiTestCase): +class TestLiveMediaThread(PungiTestCase): @mock.patch('pungi.phases.livemedia_phase.get_mtime') @mock.patch('pungi.phases.livemedia_phase.get_file_size') From 86bb816417e56914ad122f7777be10fe3264adfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lubom=C3=ADr=20Sedl=C3=A1=C5=99?= Date: Thu, 3 Mar 2016 12:57:46 +0100 Subject: [PATCH 2/2] [live-media] Add global settings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These can be overriden for a particular image, but in general case they can simplify the config. Signed-off-by: Lubomír Sedlář --- doc/configuration.rst | 23 ++++++++ pungi/phases/livemedia_phase.py | 58 +++++++++++++++++--- tests/test_livemediaphase.py | 93 +++++++++++++++++++++++++++++++++ 3 files changed, 167 insertions(+), 7 deletions(-) diff --git a/doc/configuration.rst b/doc/configuration.rst index 00e92473..55b37df1 100644 --- a/doc/configuration.rst +++ b/doc/configuration.rst @@ -758,6 +758,29 @@ Live Media Settings * ``title`` (*str*) * ``install_tree_from`` (*str*) -- variant to take install tree from +If many of your media use the same value for one of ``ksurl``, ``release``, +``target`` or ``version``, consider using these options to set the value in one +place and have all media inherit it. + +**live_media_ksurl** + (*str*) -- Provides a fallback for media that do not specify ``ksurl`` in + the ``live_media`` block. + +**live_media_release** + (*str*) -- Provides a fallback for media that do not specify ``release`` in + the ``live_media`` block. Please note that if you set this, there is no way + to unset it for a particular media. This is important if you want the + release generated by Koji. + +**live_media_target** + (*str*) -- Provides a fallback for media that do not specify ``target`` in + the ``live_media`` block. + +**live_media_version** + (*str*) -- Provides a fallback for media that do not specify ``version`` in + the ``live_media`` block. + + Image Build Settings ==================== diff --git a/pungi/phases/livemedia_phase.py b/pungi/phases/livemedia_phase.py index e6c90c59..1060317c 100644 --- a/pungi/phases/livemedia_phase.py +++ b/pungi/phases/livemedia_phase.py @@ -17,9 +17,33 @@ class LiveMediaPhase(PhaseBase): """class for wrapping up koji spin-livemedia""" name = 'live_media' + config_options = ( + { + "name": "live_media", + "expected_types": [dict], + "optional": True, + }, + { + "name": "live_media_ksurl", + "expected_types": [str], + "optional": True, + }, + { + "name": "live_media_target", + "expected_types": [str], + "optional": True, + }, + { + "name": "live_media_release", + "expected_types": [str], + "optional": True, + } + ) + def __init__(self, compose): super(LiveMediaPhase, self).__init__(compose) self.pool = ThreadPool(logger=self.compose._logger) + self._global_ksurl = None def skip(self): if super(LiveMediaPhase, self).skip(): @@ -61,10 +85,13 @@ class LiveMediaPhase(PhaseBase): return sorted(arches) def _get_release(self, image_conf): - """If release is set explicitly to None, replace it with date and respin.""" - if 'release' in image_conf and image_conf['release'] is None: - return '%s.%s' % (self.compose.compose_date, self.compose.compose_respin) - return image_conf.get('release', None) + """If release is set explicitly to None, replace it with date and respin. + Uses both image configuration and global config. + """ + for key, conf in [('release', image_conf), ('live_media_release', self.compose.conf)]: + if key in conf and conf[key] is None: + return '%s.%s' % (self.compose.compose_date, self.compose.compose_respin) + return image_conf.get('release', self.compose.conf.get('live_media_release')) def _get_install_tree(self, image_conf, variant): if 'install_tree_from' in image_conf: @@ -80,16 +107,33 @@ class LiveMediaPhase(PhaseBase): self.compose.paths.compose.os_tree('$basearch', variant, create_dir=False) ) + @property + def global_ksurl(self): + """Get globally configure kickstart URL. It will only be resolved once.""" + if not self._global_ksurl: + ksurl = self.compose.conf.get('live_media_ksurl') + self._global_ksurl = resolve_git_url(ksurl) + return self._global_ksurl + + def _get_ksurl(self, image_conf): + """Get ksurl from `image_conf`. If not present, fall back to global one.""" + if 'ksurl' in image_conf: + return resolve_git_url(image_conf['ksurl']) + return self.global_ksurl + + def _get_config(self, image_conf, opt): + return image_conf.get(opt, self.compose.conf.get('live_media_' + opt)) + def run(self): for variant in self.compose.get_variants(): arches = set([x for x in variant.arches if x != 'src']) for image_conf in get_variant_data(self.compose.conf, self.name, variant): config = { - 'target': image_conf['target'], + 'target': self._get_config(image_conf, 'target'), 'arches': self._get_arches(image_conf, arches), 'ksfile': image_conf['kickstart'], - 'ksurl': resolve_git_url(image_conf['ksurl']), + 'ksurl': self._get_ksurl(image_conf), 'ksversion': image_conf.get('ksversion'), 'scratch': image_conf.get('scratch', False), 'release': self._get_release(image_conf), @@ -98,7 +142,7 @@ class LiveMediaPhase(PhaseBase): 'title': image_conf.get('title'), 'repo': self._get_repos(image_conf, variant), 'install_tree': self._get_install_tree(image_conf, variant), - 'version': image_conf['version'], + 'version': self._get_config(image_conf, 'version'), } self.pool.add(LiveMediaThread(self.pool)) self.pool.queue_put((self.compose, variant, config)) diff --git a/tests/test_livemediaphase.py b/tests/test_livemediaphase.py index 9910f959..20ccdfcb 100755 --- a/tests/test_livemediaphase.py +++ b/tests/test_livemediaphase.py @@ -54,6 +54,99 @@ class TestLiveMediaPhase(PungiTestCase): 'version': 'Rawhide', }))]) + @mock.patch('pungi.phases.livemedia_phase.resolve_git_url') + @mock.patch('pungi.phases.livemedia_phase.ThreadPool') + def test_live_media_with_global_opts(self, ThreadPool, resolve_git_url): + compose = DummyCompose(self.topdir, { + 'live_media_ksurl': 'git://example.com/repo.git#HEAD', + 'live_media_target': 'f24', + 'live_media_release': 'RRR', + 'live_media_version': 'Rawhide', + 'live_media': { + '^Server$': [ + { + 'kickstart': 'file.ks', + 'name': 'Fedora Server Live', + }, + { + 'kickstart': 'different.ks', + 'name': 'Fedora Server Live', + }, + { + 'kickstart': 'yet-another.ks', + 'name': 'Fedora Server Live', + 'ksurl': 'git://different.com/repo.git', + 'target': 'f25', + 'release': 'XXX', + 'version': '25', + } + ] + }, + 'koji_profile': 'koji', + }) + + resolve_git_url.return_value = 'git://example.com/repo.git#BEEFCAFE' + + phase = LiveMediaPhase(compose) + + phase.run() + self.assertTrue(phase.pool.add.called) + self.assertItemsEqual(resolve_git_url.mock_calls, + [mock.call('git://example.com/repo.git#HEAD'), + mock.call('git://different.com/repo.git')]) + self.assertEqual(phase.pool.queue_put.call_args_list, + [mock.call((compose, + compose.variants['Server'], + { + 'arches': ['amd64', 'x86_64'], + 'ksfile': 'file.ks', + 'ksurl': 'git://example.com/repo.git#BEEFCAFE', + 'ksversion': None, + 'name': 'Fedora Server Live', + 'release': 'RRR', + 'repo': [self.topdir + '/compose/Server/$basearch/os'], + 'scratch': False, + 'skip_tag': None, + 'target': 'f24', + 'title': None, + 'install_tree': self.topdir + '/compose/Server/$basearch/os', + 'version': 'Rawhide', + })), + mock.call((compose, + compose.variants['Server'], + { + 'arches': ['amd64', 'x86_64'], + 'ksfile': 'different.ks', + 'ksurl': 'git://example.com/repo.git#BEEFCAFE', + 'ksversion': None, + 'name': 'Fedora Server Live', + 'release': 'RRR', + 'repo': [self.topdir + '/compose/Server/$basearch/os'], + 'scratch': False, + 'skip_tag': None, + 'target': 'f24', + 'title': None, + 'install_tree': self.topdir + '/compose/Server/$basearch/os', + 'version': 'Rawhide', + })), + mock.call((compose, + compose.variants['Server'], + { + 'arches': ['amd64', 'x86_64'], + 'ksfile': 'yet-another.ks', + 'ksurl': 'git://example.com/repo.git#BEEFCAFE', + 'ksversion': None, + 'name': 'Fedora Server Live', + 'release': 'XXX', + 'repo': [self.topdir + '/compose/Server/$basearch/os'], + 'scratch': False, + 'skip_tag': None, + 'target': 'f25', + 'title': None, + 'install_tree': self.topdir + '/compose/Server/$basearch/os', + 'version': '25', + }))]) + @mock.patch('pungi.phases.livemedia_phase.ThreadPool') def test_live_media_non_existing_install_tree(self, ThreadPool): compose = DummyCompose(self.topdir, {