diff --git a/pungi/phases/livemedia_phase.py b/pungi/phases/livemedia_phase.py index aa0f400b..61b8d704 100644 --- a/pungi/phases/livemedia_phase.py +++ b/pungi/phases/livemedia_phase.py @@ -90,6 +90,8 @@ class LiveMediaPhase(PhaseLoggerMixin, ImageConfigMixin, ConfigGuardedPhase): 'version': self.get_version(image_conf), 'failable_arches': image_conf.get('failable', []), } + if config['failable_arches'] == ['*']: + config['failable_arches'] = config['arches'] self.pool.add(LiveMediaThread(self.pool)) self.pool.queue_put((self.compose, variant, config)) @@ -102,8 +104,8 @@ class LiveMediaThread(WorkerThread): subvariant = config.pop('subvariant') self.failable_arches = config.pop('failable_arches') self.num = num - # TODO handle failure per architecture; currently not possible in single task - with failable(compose, bool(self.failable_arches), variant, '*', 'live-media', subvariant, + can_fail = set(self.failable_arches) == set(config['arches']) + with failable(compose, can_fail, variant, '*', 'live-media', subvariant, logger=self.pool._logger): self.worker(compose, variant, subvariant, config) @@ -126,6 +128,7 @@ class LiveMediaThread(WorkerThread): """Replace `arches` (as list) with `arch` as a comma-separated string.""" copy = dict(config) copy['arch'] = ','.join(copy.pop('arches', [])) + copy['can_fail'] = self.failable_arches return koji_wrapper.get_live_media_cmd(copy) def worker(self, compose, variant, subvariant, config): @@ -149,9 +152,10 @@ class LiveMediaThread(WorkerThread): if path.endswith('.iso'): image_infos.append({'path': path, 'arch': arch}) - if len(image_infos) != len(config['arches']): + if len(image_infos) < len(config['arches']) - len(self.failable_arches): self.pool.log_error( - 'Error in koji task %s. Expected to find one image for each arch (%s). Got %s.' + 'Error in koji task %s. Expected to find at least one image ' + 'for each required arch (%s). Got %s.' % (output['task_id'], len(config['arches']), len(image_infos))) raise RuntimeError('Image count mismatch in task %s.' % output['task_id']) diff --git a/pungi/wrappers/kojiwrapper.py b/pungi/wrappers/kojiwrapper.py index 85a933a1..f75b3f54 100644 --- a/pungi/wrappers/kojiwrapper.py +++ b/pungi/wrappers/kojiwrapper.py @@ -189,6 +189,9 @@ class KojiWrapper(object): if 'release' in options: cmd.append('--release=%s' % options['release']) + if 'can_fail' in options: + cmd.append('--can-fail=%s' % options['can_fail']) + if wait: cmd.append('--wait') diff --git a/tests/test_livemediaphase.py b/tests/test_livemediaphase.py index 61c37ada..99dffd06 100644 --- a/tests/test_livemediaphase.py +++ b/tests/test_livemediaphase.py @@ -59,6 +59,51 @@ class TestLiveMediaPhase(PungiTestCase): 'failable_arches': [], }))]) + @mock.patch('pungi.phases.livemedia_phase.ThreadPool') + def test_expand_failable(self, ThreadPool): + compose = DummyCompose(self.topdir, { + 'live_media': { + '^Server$': [ + { + 'target': 'f24', + 'kickstart': 'file.ks', + 'ksurl': 'git://example.com/repo.git', + 'name': 'Fedora Server Live', + 'version': 'Rawhide', + 'failable': ['*'], + } + ] + }, + 'koji_profile': 'koji', + }) + + self.assertValidConfig(compose.conf) + + phase = LiveMediaPhase(compose) + + phase.run() + self.assertTrue(phase.pool.add.called) + 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', + 'ksversion': None, + 'name': 'Fedora Server Live', + 'release': None, + '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', + 'subvariant': 'Server', + 'failable_arches': ['amd64', 'x86_64'], + }))]) + @mock.patch('pungi.util.resolve_git_url') @mock.patch('pungi.phases.livemedia_phase.ThreadPool') def test_live_media_with_phase_global_opts(self, ThreadPool, resolve_git_url): @@ -368,7 +413,7 @@ class TestLiveMediaPhase(PungiTestCase): 'install_tree': self.topdir + '/compose/Server-optional/$basearch/os', 'version': '25', 'subvariant': 'Something', - 'failable_arches': ['*'], + 'failable_arches': ['x86_64'], }))]) @@ -446,7 +491,8 @@ class TestLiveMediaThread(PungiTestCase): 'skip_tag': None, 'target': 'f24', 'title': None, - 'version': 'Rawhide'})]) + 'version': 'Rawhide', + 'can_fail': []})]) self.assertEqual(get_image_paths.mock_calls, [mock.call(1234)]) self.assertTrue(os.path.isdir(self.topdir + '/compose/Server/x86_64/iso')) @@ -498,7 +544,7 @@ class TestLiveMediaThread(PungiTestCase): 'title': None, 'version': 'Rawhide', 'subvariant': 'KDE', - 'failable_arches': ['*'], + 'failable_arches': ['amd64', 'x86_64'], } pool = mock.Mock() @@ -520,6 +566,22 @@ class TestLiveMediaThread(PungiTestCase): mock.call('Live media task failed: 1234. See %s for more details.' % (os.path.join(self.topdir, 'logs/amd64-x86_64/livemedia-Server-KDE.amd64-x86_64.log'))) ]) + self.assertEqual(KojiWrapper.return_value.get_live_media_cmd.mock_calls, + [mock.call({ + 'arch': 'amd64,x86_64', + 'ksfile': 'file.ks', + 'ksurl': 'git://example.com/repo.git', + 'ksversion': None, + 'skip_tag': None, + 'target': 'f24', + 'title': None, + 'release': None, + 'version': 'Rawhide', + 'scratch': False, + 'can_fail': ['amd64', 'x86_64'], + 'name': 'Fedora Server Live', + 'repo': ['/repo/$basearch/Server'], + })]) @mock.patch('pungi.phases.livemedia_phase.get_mtime') @mock.patch('pungi.phases.livemedia_phase.get_file_size') @@ -545,7 +607,7 @@ class TestLiveMediaThread(PungiTestCase): 'title': None, 'version': 'Rawhide', 'subvariant': 'KDE', - 'failable_arches': ['*'], + 'failable_arches': ['amd64', 'x86_64'], } pool = mock.Mock() @@ -562,6 +624,77 @@ class TestLiveMediaThread(PungiTestCase): mock.call('[FAIL] Live media (variant Server, arch *, subvariant KDE) failed, but going on anyway.'), mock.call('BOOM') ]) + self.assertEqual(KojiWrapper.return_value.get_live_media_cmd.mock_calls, + [mock.call({ + 'arch': 'amd64,x86_64', + 'ksfile': 'file.ks', + 'ksurl': 'git://example.com/repo.git', + 'ksversion': None, + 'skip_tag': None, + 'target': 'f24', + 'title': None, + 'release': None, + 'version': 'Rawhide', + 'scratch': False, + 'can_fail': ['amd64', 'x86_64'], + 'name': 'Fedora Server Live', + 'repo': ['/repo/$basearch/Server'], + })]) + + @mock.patch('pungi.phases.livemedia_phase.get_mtime') + @mock.patch('pungi.phases.livemedia_phase.get_file_size') + @mock.patch('pungi.phases.livemedia_phase.KojiWrapper') + def test_handle_exception_only_one_arch_optional(self, KojiWrapper, get_file_size, get_mtime): + compose = DummyCompose(self.topdir, { + 'koji_profile': 'koji', + 'failable_deliverables': [ + ('^.+$', {'*': ['live-media']}) + ] + }) + config = { + 'arches': ['amd64', 'x86_64'], + 'ksfile': 'file.ks', + 'ksurl': 'git://example.com/repo.git', + 'ksversion': None, + 'name': 'Fedora Server Live', + 'release': None, + 'repo': ['/repo/$basearch/Server'], + 'scratch': False, + 'skip_tag': None, + 'target': 'f24', + 'title': None, + 'version': 'Rawhide', + 'subvariant': 'KDE', + 'failable_arches': ['amd64'], + } + pool = mock.Mock() + + run_blocking_cmd = KojiWrapper.return_value.run_blocking_cmd + run_blocking_cmd.side_effect = boom + get_file_size.return_value = 1024 + get_mtime.return_value.st_mtime = 13579 + + t = LiveMediaThread(pool) + with self.assertRaises(Exception): + with mock.patch('time.sleep'): + t.process((compose, compose.variants['Server'], config), 1) + + self.assertEqual(KojiWrapper.return_value.get_live_media_cmd.mock_calls, + [mock.call({ + 'arch': 'amd64,x86_64', + 'ksfile': 'file.ks', + 'ksurl': 'git://example.com/repo.git', + 'ksversion': None, + 'skip_tag': None, + 'target': 'f24', + 'title': None, + 'release': None, + 'version': 'Rawhide', + 'scratch': False, + 'can_fail': ['amd64'], + 'name': 'Fedora Server Live', + 'repo': ['/repo/$basearch/Server'], + })]) if __name__ == "__main__":