[image-build] Optionally do not break whole compose
The configuration can now specify image-build as a deliverable that can fail but not abort the whole compose. Signed-off-by: Lubomír Sedlář <lsedlar@redhat.com>
This commit is contained in:
parent
af11bebf1b
commit
5688f1cbae
|
@ -135,6 +135,12 @@ Options
|
||||||
* buildinstall
|
* buildinstall
|
||||||
* iso
|
* iso
|
||||||
* live
|
* live
|
||||||
|
* image-build
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Image building is not run per-architecture. If you want to mark it
|
||||||
|
as failable, specify it in a block with arch set as ``*``.
|
||||||
|
|
||||||
Please note that ``*`` as a wildcard matches all architectures but ``src``.
|
Please note that ``*`` as a wildcard matches all architectures but ``src``.
|
||||||
|
|
||||||
|
|
|
@ -133,6 +133,17 @@ class CreateImageBuildThread(WorkerThread):
|
||||||
|
|
||||||
def process(self, item, num):
|
def process(self, item, num):
|
||||||
compose, cmd = item
|
compose, cmd = item
|
||||||
|
try:
|
||||||
|
self.worker(num, compose, cmd)
|
||||||
|
except:
|
||||||
|
if not compose.can_fail(cmd['image_conf']['variant'], '*', 'image-build'):
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
msg = ('[FAIL] image-build for variant %s failed, but going on anyway.'
|
||||||
|
% cmd['image_conf']['variant'])
|
||||||
|
self.pool.log_info(msg)
|
||||||
|
|
||||||
|
def worker(self, num, compose, cmd):
|
||||||
arches = cmd['image_conf']['arches'].split(',')
|
arches = cmd['image_conf']['arches'].split(',')
|
||||||
|
|
||||||
mounts = [compose.paths.compose.topdir()]
|
mounts = [compose.paths.compose.topdir()]
|
||||||
|
|
|
@ -40,6 +40,9 @@ class ComposeTestCase(unittest.TestCase):
|
||||||
self.assertFalse(compose.can_fail(None, 'x86_64', 'live'))
|
self.assertFalse(compose.can_fail(None, 'x86_64', 'live'))
|
||||||
self.assertTrue(compose.can_fail(None, 'i386', 'live'))
|
self.assertTrue(compose.can_fail(None, 'i386', 'live'))
|
||||||
|
|
||||||
|
self.assertTrue(compose.can_fail(variant, '*', 'buildinstall'))
|
||||||
|
self.assertFalse(compose.can_fail(variant, '*', 'live'))
|
||||||
|
|
||||||
@mock.patch('pungi.compose.ComposeInfo')
|
@mock.patch('pungi.compose.ComposeInfo')
|
||||||
def test_get_image_name(self, ci):
|
def test_get_image_name(self, ci):
|
||||||
conf = {}
|
conf = {}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import sys
|
||||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
|
||||||
|
|
||||||
from pungi.phases.image_build import ImageBuildPhase, CreateImageBuildThread
|
from pungi.phases.image_build import ImageBuildPhase, CreateImageBuildThread
|
||||||
|
from pungi.util import get_arch_variant_data
|
||||||
|
|
||||||
|
|
||||||
class _DummyCompose(object):
|
class _DummyCompose(object):
|
||||||
|
@ -55,6 +56,7 @@ class _DummyCompose(object):
|
||||||
'Everything': mock.Mock(uid='Everything', arches=['x86_64', 'amd64']),
|
'Everything': mock.Mock(uid='Everything', arches=['x86_64', 'amd64']),
|
||||||
}
|
}
|
||||||
self.im = mock.Mock()
|
self.im = mock.Mock()
|
||||||
|
self.log_error = mock.Mock()
|
||||||
|
|
||||||
def get_arches(self):
|
def get_arches(self):
|
||||||
return ['x86_64', 'amd64']
|
return ['x86_64', 'amd64']
|
||||||
|
@ -62,6 +64,10 @@ class _DummyCompose(object):
|
||||||
def get_variants(self, arch=None, types=None):
|
def get_variants(self, arch=None, types=None):
|
||||||
return [v for v in self.variants.values() if not arch or arch in v.arches]
|
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 TestImageBuildPhase(unittest.TestCase):
|
class TestImageBuildPhase(unittest.TestCase):
|
||||||
|
|
||||||
|
@ -387,6 +393,102 @@ class TestCreateImageBuildThread(unittest.TestCase):
|
||||||
self.assertEqual(data['format'], image.format)
|
self.assertEqual(data['format'], image.format)
|
||||||
self.assertEqual(data['type'], image.type)
|
self.assertEqual(data['type'], image.type)
|
||||||
|
|
||||||
|
@mock.patch('pungi.phases.image_build.KojiWrapper')
|
||||||
|
@mock.patch('pungi.phases.image_build.Linker')
|
||||||
|
def test_process_handle_fail(self, Linker, KojiWrapper):
|
||||||
|
compose = _DummyCompose({
|
||||||
|
'koji_profile': 'koji',
|
||||||
|
'failable_deliverables': [
|
||||||
|
('^.*$', {
|
||||||
|
'*': ['image-build']
|
||||||
|
})
|
||||||
|
]
|
||||||
|
})
|
||||||
|
pool = mock.Mock()
|
||||||
|
cmd = {
|
||||||
|
"format": [('docker', 'tar.xz'), ('qcow2', 'qcow2')],
|
||||||
|
"image_conf": {
|
||||||
|
'install_tree': '/ostree/$arch/Client',
|
||||||
|
'kickstart': 'fedora-docker-base.ks',
|
||||||
|
'format': 'docker',
|
||||||
|
'repo': '/ostree/$arch/Client',
|
||||||
|
'variant': compose.variants['Client'],
|
||||||
|
'target': 'f24',
|
||||||
|
'disk_size': 3,
|
||||||
|
'name': 'Fedora-Docker-Base',
|
||||||
|
'arches': 'amd64,x86_64',
|
||||||
|
'version': 'Rawhide',
|
||||||
|
'ksurl': 'git://git.fedorahosted.org/git/spin-kickstarts.git',
|
||||||
|
'distro': 'Fedora-20',
|
||||||
|
},
|
||||||
|
"conf_file": 'amd64,x86_64-Client-Fedora-Docker-Base-docker',
|
||||||
|
"image_dir": '/image_dir/Client/%(arch)s',
|
||||||
|
"relative_image_dir": 'image_dir/Client/%(arch)s',
|
||||||
|
"link_type": 'hardlink-or-copy',
|
||||||
|
}
|
||||||
|
koji_wrapper = KojiWrapper.return_value
|
||||||
|
koji_wrapper.run_create_image_cmd.return_value = {
|
||||||
|
"retcode": 1,
|
||||||
|
"output": None,
|
||||||
|
"task_id": 1234,
|
||||||
|
}
|
||||||
|
|
||||||
|
t = CreateImageBuildThread(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, cmd), 1)
|
||||||
|
|
||||||
|
@mock.patch('pungi.phases.image_build.KojiWrapper')
|
||||||
|
@mock.patch('pungi.phases.image_build.Linker')
|
||||||
|
def test_process_handle_exception(self, Linker, KojiWrapper):
|
||||||
|
compose = _DummyCompose({
|
||||||
|
'koji_profile': 'koji',
|
||||||
|
'failable_deliverables': [
|
||||||
|
('^.*$', {
|
||||||
|
'*': ['image-build']
|
||||||
|
})
|
||||||
|
]
|
||||||
|
})
|
||||||
|
pool = mock.Mock()
|
||||||
|
cmd = {
|
||||||
|
"format": [('docker', 'tar.xz'), ('qcow2', 'qcow2')],
|
||||||
|
"image_conf": {
|
||||||
|
'install_tree': '/ostree/$arch/Client',
|
||||||
|
'kickstart': 'fedora-docker-base.ks',
|
||||||
|
'format': 'docker',
|
||||||
|
'repo': '/ostree/$arch/Client',
|
||||||
|
'variant': compose.variants['Client'],
|
||||||
|
'target': 'f24',
|
||||||
|
'disk_size': 3,
|
||||||
|
'name': 'Fedora-Docker-Base',
|
||||||
|
'arches': 'amd64,x86_64',
|
||||||
|
'version': 'Rawhide',
|
||||||
|
'ksurl': 'git://git.fedorahosted.org/git/spin-kickstarts.git',
|
||||||
|
'distro': 'Fedora-20',
|
||||||
|
},
|
||||||
|
"conf_file": 'amd64,x86_64-Client-Fedora-Docker-Base-docker',
|
||||||
|
"image_dir": '/image_dir/Client/%(arch)s',
|
||||||
|
"relative_image_dir": 'image_dir/Client/%(arch)s',
|
||||||
|
"link_type": 'hardlink-or-copy',
|
||||||
|
}
|
||||||
|
|
||||||
|
def boom(*args, **kwargs):
|
||||||
|
raise RuntimeError('BOOM')
|
||||||
|
|
||||||
|
koji_wrapper = KojiWrapper.return_value
|
||||||
|
koji_wrapper.run_create_image_cmd.side_effect = boom
|
||||||
|
|
||||||
|
t = CreateImageBuildThread(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, cmd), 1)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
Loading…
Reference in New Issue