This phase builds live media in Koji using the Live Media Creator. It runs in parallel with current live images, create ISO and image build phases. The documentation is updated to explain how to configure this. Signed-off-by: Lubomír Sedlář <lsedlar@redhat.com>
		
			
				
	
	
		
			329 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			329 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
| #!/usr/bin/env python2
 | |
| # -*- coding: utf-8 -*-
 | |
| 
 | |
| import unittest
 | |
| import mock
 | |
| 
 | |
| import sys
 | |
| import os
 | |
| 
 | |
| sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
 | |
| 
 | |
| from pungi.phases.livemedia_phase import LiveMediaPhase, LiveMediaThread
 | |
| from pungi.util import get_arch_variant_data
 | |
| 
 | |
| 
 | |
| class _DummyCompose(object):
 | |
|     def __init__(self, config):
 | |
|         self.compose_date = '20151203'
 | |
|         self.compose_type_suffix = '.t'
 | |
|         self.compose_respin = 0
 | |
|         self.ci_base = mock.Mock(
 | |
|             release_id='Test-1.0',
 | |
|             release=mock.Mock(
 | |
|                 short='test',
 | |
|                 version='1.0',
 | |
|             ),
 | |
|         )
 | |
|         self.conf = config
 | |
|         self.paths = mock.Mock(
 | |
|             compose=mock.Mock(
 | |
|                 topdir=mock.Mock(return_value='/a/b'),
 | |
|                 os_tree=mock.Mock(
 | |
|                     side_effect=lambda arch, variant, create_dir=False: os.path.join('/ostree', arch, variant.uid)
 | |
|                 ),
 | |
|                 repository=mock.Mock(
 | |
|                     side_effect=lambda arch, variant, create_dir=False: os.path.join('/repo', arch, variant.uid)
 | |
|                 ),
 | |
|                 image_dir=mock.Mock(
 | |
|                     side_effect=lambda variant, relative=False: os.path.join(
 | |
|                         '' if relative else '/', 'image_dir', variant.uid, '%(arch)s'
 | |
|                     )
 | |
|                 )
 | |
|             ),
 | |
|             work=mock.Mock(
 | |
|                 image_build_conf=mock.Mock(
 | |
|                     side_effect=lambda variant, image_name, image_type:
 | |
|                         '-'.join([variant.uid, image_name, image_type])
 | |
|                 )
 | |
|             ),
 | |
|             log=mock.Mock(
 | |
|                 log_file=mock.Mock(return_value='/a/b/log/log_file')
 | |
|             )
 | |
|         )
 | |
|         self._logger = mock.Mock()
 | |
|         self.variants = {
 | |
|             'Server': mock.Mock(uid='Server', arches=['x86_64', 'amd64']),
 | |
|             'Client': mock.Mock(uid='Client', arches=['amd64']),
 | |
|             'Everything': mock.Mock(uid='Everything', arches=['x86_64', 'amd64']),
 | |
|         }
 | |
|         self.im = mock.Mock()
 | |
|         self.log_error = mock.Mock()
 | |
| 
 | |
|     def get_variants(self, arch=None, types=None):
 | |
|         return [v for v in self.variants.values() if not arch or arch in v.arches]
 | |
| 
 | |
|     def can_fail(self, variant, arch, deliverable):
 | |
|         failable = get_arch_variant_data(self.conf, 'failable_deliverables', arch, variant)
 | |
|         return deliverable in failable
 | |
| 
 | |
| 
 | |
| class TestLiveMediaPhase(unittest.TestCase):
 | |
|     @mock.patch('pungi.phases.livemedia_phase.ThreadPool')
 | |
|     def test_live_media_minimal(self, ThreadPool):
 | |
|         compose = _DummyCompose({
 | |
|             'live_media': {
 | |
|                 '^Server$': [
 | |
|                     {
 | |
|                         'target': 'f24',
 | |
|                         'kickstart': 'file.ks',
 | |
|                         'ksurl': 'git://example.com/repo.git',
 | |
|                         'name': 'Fedora Server Live',
 | |
|                     }
 | |
|                 ]
 | |
|             },
 | |
|             'koji_profile': 'koji',
 | |
|         })
 | |
| 
 | |
|         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'],
 | |
|                                          'kickstart': 'file.ks',
 | |
|                                          'ksurl': 'git://example.com/repo.git',
 | |
|                                          'ksversion': None,
 | |
|                                          'name': 'Fedora Server Live',
 | |
|                                          'release': None,
 | |
|                                          'repo': ['/repo/$arch/Server'],
 | |
|                                          'scratch': False,
 | |
|                                          'skip_tag': None,
 | |
|                                          'target': 'f24',
 | |
|                                          'title': None,
 | |
|                                          'install_tree': '/ostree/$arch/Server',
 | |
|                                      }))])
 | |
| 
 | |
|     @mock.patch('pungi.phases.livemedia_phase.resolve_git_url')
 | |
|     @mock.patch('pungi.phases.livemedia_phase.ThreadPool')
 | |
|     def test_live_media_full(self, ThreadPool, resolve_git_url):
 | |
|         compose = _DummyCompose({
 | |
|             'live_media': {
 | |
|                 '^Server$': [
 | |
|                     {
 | |
|                         'target': 'f24',
 | |
|                         'kickstart': 'file.ks',
 | |
|                         'ksurl': 'git://example.com/repo.git#HEAD',
 | |
|                         'name': 'Fedora Server Live',
 | |
|                         'scratch': True,
 | |
|                         'skip_tag': True,
 | |
|                         'title': 'Custom Title',
 | |
|                         'repo_from': ['Everything'],
 | |
|                         'repo': ['http://example.com/extra_repo'],
 | |
|                         'arches': ['x86_64'],
 | |
|                         'ksversion': '24',
 | |
|                         'release': None
 | |
|                     }
 | |
|                 ]
 | |
|             }
 | |
|         })
 | |
| 
 | |
|         resolve_git_url.return_value = 'resolved'
 | |
| 
 | |
|         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': ['x86_64'],
 | |
|                                          'kickstart': 'file.ks',
 | |
|                                          'ksurl': 'resolved',
 | |
|                                          'ksversion': '24',
 | |
|                                          'name': 'Fedora Server Live',
 | |
|                                          'release': '20151203.0',
 | |
|                                          'repo': ['http://example.com/extra_repo',
 | |
|                                                   '/repo/$arch/Everything',
 | |
|                                                   '/repo/$arch/Server'],
 | |
|                                          'scratch': True,
 | |
|                                          'skip_tag': True,
 | |
|                                          'target': 'f24',
 | |
|                                          'title': 'Custom Title',
 | |
|                                          'install_tree': '/ostree/$arch/Server',
 | |
|                                      }))])
 | |
| 
 | |
| 
 | |
| class TestCreateImageBuildThread(unittest.TestCase):
 | |
| 
 | |
|     @mock.patch('pungi.phases.livemedia_phase.KojiWrapper')
 | |
|     @mock.patch('pungi.phases.livemedia_phase.Linker')
 | |
|     @mock.patch('pungi.phases.livemedia_phase.makedirs')
 | |
|     def test_process(self, makedirs, Linker, KojiWrapper):
 | |
|         compose = _DummyCompose({
 | |
|             'koji_profile': 'koji'
 | |
|         })
 | |
|         config = {
 | |
|             'arches': ['amd64', 'x86_64'],
 | |
|             'kickstart': 'file.ks',
 | |
|             'ksurl': 'git://example.com/repo.git',
 | |
|             'ksversion': None,
 | |
|             'name': 'Fedora Server Live',
 | |
|             'release': None,
 | |
|             'repo': ['/repo/$arch/Server'],
 | |
|             'scratch': False,
 | |
|             'skip_tag': None,
 | |
|             'target': 'f24',
 | |
|             'title': None,
 | |
|         }
 | |
|         pool = mock.Mock()
 | |
| 
 | |
|         get_live_media_cmd = KojiWrapper.return_value.get_live_media_cmd
 | |
|         get_live_media_cmd.return_value = 'koji-spin-livemedia'
 | |
| 
 | |
|         run_blocking_cmd = KojiWrapper.return_value.run_blocking_cmd
 | |
|         run_blocking_cmd.return_value = {
 | |
|             'task_id': 1234,
 | |
|             'retcode': 0,
 | |
|             'output': None,
 | |
|         }
 | |
| 
 | |
|         get_image_paths = KojiWrapper.return_value.get_image_paths
 | |
|         get_image_paths.return_value = {
 | |
|             'x86_64': [
 | |
|                 '/koji/task/1235/tdl-amd64.xml',
 | |
|                 '/koji/task/1235/Live-20160103.x86_64.iso',
 | |
|                 '/koji/task/1235/Live-20160103.x86_64.tar.xz'
 | |
|             ],
 | |
|             'amd64': [
 | |
|                 '/koji/task/1235/tdl-amd64.xml',
 | |
|                 '/koji/task/1235/Live-20160103.amd64.iso',
 | |
|                 '/koji/task/1235/Live-20160103.amd64.tar.xz'
 | |
|             ]
 | |
|         }
 | |
| 
 | |
|         t = LiveMediaThread(pool)
 | |
|         with mock.patch('os.stat') as stat:
 | |
|             with mock.patch('os.path.getsize') as getsize:
 | |
|                 with mock.patch('time.sleep'):
 | |
|                     getsize.return_value = 1024
 | |
|                     stat.return_value.st_mtime = 13579
 | |
|                     t.process((compose, compose.variants['Server'], config), 1)
 | |
| 
 | |
|         self.assertEqual(run_blocking_cmd.mock_calls,
 | |
|                          [mock.call('koji-spin-livemedia', log_file='/a/b/log/log_file')])
 | |
|         self.assertEqual(get_live_media_cmd.mock_calls,
 | |
|                          [mock.call(config)])
 | |
|         self.assertEqual(get_image_paths.mock_calls,
 | |
|                          [mock.call(1234)])
 | |
|         self.assertItemsEqual(makedirs.mock_calls,
 | |
|                               [mock.call('/image_dir/Server/x86_64'),
 | |
|                                mock.call('/image_dir/Server/amd64')])
 | |
|         link = Linker.return_value.link
 | |
|         self.assertItemsEqual(link.mock_calls,
 | |
|                               [mock.call('/koji/task/1235/Live-20160103.amd64.iso',
 | |
|                                          '/image_dir/Server/amd64/Live-20160103.amd64.iso',
 | |
|                                          link_type='hardlink-or-copy'),
 | |
|                                mock.call('/koji/task/1235/Live-20160103.x86_64.iso',
 | |
|                                          '/image_dir/Server/x86_64/Live-20160103.x86_64.iso',
 | |
|                                          link_type='hardlink-or-copy')])
 | |
| 
 | |
|         image_relative_paths = [
 | |
|             'image_dir/Server/amd64/Live-20160103.amd64.iso',
 | |
|             'image_dir/Server/x86_64/Live-20160103.x86_64.iso'
 | |
|         ]
 | |
| 
 | |
|         self.assertEqual(len(compose.im.add.call_args_list), 2)
 | |
|         for call in compose.im.add.call_args_list:
 | |
|             _, kwargs = call
 | |
|             image = kwargs['image']
 | |
|             self.assertEqual(kwargs['variant'], 'Server')
 | |
|             self.assertIn(kwargs['arch'], ('amd64', 'x86_64'))
 | |
|             self.assertEqual(kwargs['arch'], image.arch)
 | |
|             self.assertIn(image.path, image_relative_paths)
 | |
|             self.assertEqual('iso', image.format)
 | |
|             self.assertEqual('live', image.type)
 | |
| 
 | |
|     @mock.patch('pungi.phases.livemedia_phase.KojiWrapper')
 | |
|     def test_handle_koji_fail(self, KojiWrapper):
 | |
|         compose = _DummyCompose({
 | |
|             'koji_profile': 'koji',
 | |
|             'failable_deliverables': [
 | |
|                 ('^.+$', {'*': ['live-media']})
 | |
|             ]
 | |
|         })
 | |
|         config = {
 | |
|             'arches': ['amd64', 'x86_64'],
 | |
|             'kickstart': 'file.ks',
 | |
|             'ksurl': 'git://example.com/repo.git',
 | |
|             'ksversion': None,
 | |
|             'name': 'Fedora Server Live',
 | |
|             'release': None,
 | |
|             'repo': ['/repo/$arch/Server'],
 | |
|             'scratch': False,
 | |
|             'skip_tag': None,
 | |
|             'target': 'f24',
 | |
|             'title': None,
 | |
|         }
 | |
|         pool = mock.Mock()
 | |
| 
 | |
|         run_blocking_cmd = KojiWrapper.return_value.run_blocking_cmd
 | |
|         run_blocking_cmd.return_value = {
 | |
|             'task_id': 1234,
 | |
|             'retcode': 1,
 | |
|             'output': None,
 | |
|         }
 | |
| 
 | |
|         t = LiveMediaThread(pool)
 | |
|         with mock.patch('os.stat') as stat:
 | |
|             with mock.patch('os.path.getsize') as getsize:
 | |
|                 with mock.patch('time.sleep'):
 | |
|                     getsize.return_value = 1024
 | |
|                     stat.return_value.st_mtime = 13579
 | |
|                     t.process((compose, compose.variants['Server'], config), 1)
 | |
| 
 | |
|     @mock.patch('pungi.phases.livemedia_phase.KojiWrapper')
 | |
|     def test_handle_exception(self, KojiWrapper):
 | |
|         compose = _DummyCompose({
 | |
|             'koji_profile': 'koji',
 | |
|             'failable_deliverables': [
 | |
|                 ('^.+$', {'*': ['live-media']})
 | |
|             ]
 | |
|         })
 | |
|         config = {
 | |
|             'arches': ['amd64', 'x86_64'],
 | |
|             'kickstart': 'file.ks',
 | |
|             'ksurl': 'git://example.com/repo.git',
 | |
|             'ksversion': None,
 | |
|             'name': 'Fedora Server Live',
 | |
|             'release': None,
 | |
|             'repo': ['/repo/$arch/Server'],
 | |
|             'scratch': False,
 | |
|             'skip_tag': None,
 | |
|             'target': 'f24',
 | |
|             'title': None,
 | |
|         }
 | |
|         pool = mock.Mock()
 | |
| 
 | |
|         def boom(*args, **kwargs):
 | |
|             raise Exception('BOOM')
 | |
| 
 | |
|         run_blocking_cmd = KojiWrapper.return_value.run_blocking_cmd
 | |
|         run_blocking_cmd.side_effect = boom
 | |
| 
 | |
|         t = LiveMediaThread(pool)
 | |
|         with mock.patch('os.stat') as stat:
 | |
|             with mock.patch('os.path.getsize') as getsize:
 | |
|                 with mock.patch('time.sleep'):
 | |
|                     getsize.return_value = 1024
 | |
|                     stat.return_value.st_mtime = 13579
 | |
|                     t.process((compose, compose.variants['Server'], config), 1)
 | |
| 
 | |
| 
 | |
| if __name__ == "__main__":
 | |
|     unittest.main()
 |