Merge #273 Deduplicate configuration a bit

This commit is contained in:
Dennis Gilmore 2016-04-29 12:26:51 +00:00
commit c80b7c6894
10 changed files with 544 additions and 95 deletions

View File

@ -735,6 +735,47 @@ Example
]
Common options for Live Images, Live Media and Image Build
==========================================================
All images can have ``ksurl``, ``version``, ``release`` and ``target``
specified. Since this can create a lot of duplication, there are global options
that can be used instead.
For each of the phases, if the option is not specified for a particular
deliverable, an option named ``<PHASE_NAME>_<OPTION>`` is checked. If that is
not specified either, the last fallback is ``global_<OPTION>``. If even that is
unset, the value is considered to not be specified.
The kickstart URL is configured by these options.
* ``global_ksurl`` -- global fallback setting
* ``live_media_ksurl``
* ``image_build_ksurl``
* ``live_images_ksurl``
Target is specified by these settings. For live images refer to ``live_target``.
* ``global_target`` -- global fallback setting
* ``live_media_target``
* ``image_build_target``
Version is specified by these options.
* ``global_version`` -- global fallback setting
* ``live_media_version``
* ``image_build_version``
* ``live_images_version``
Release is specified by these options. If set explicitly to ``None``, a value
will be generated based on date, compose type and respin.
* ``global_release`` -- global fallback setting
* ``live_media_release``
* ``image_build_release``
* ``live_images_release``
Live Images Settings
====================

View File

@ -15,6 +15,7 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from pungi.checks import validate_options
from pungi import util
class PhaseBase(object):
@ -84,3 +85,67 @@ class ConfigGuardedPhase(PhaseBase):
self.compose.log_info("Config section '%s' was not found. Skipping." % self.name)
return True
return False
class ImageConfigMixin(object):
"""
A mixin for phase that needs to access image related settings: ksurl,
version, target and release.
First, it checks config object given as argument, then it checks
phase-level configuration and finally falls back to global configuration.
"""
def __init__(self, *args, **kwargs):
super(ImageConfigMixin, self).__init__(*args, **kwargs)
self._phase_ksurl = None
def get_config(self, cfg, opt):
return cfg.get(
opt, self.compose.conf.get(
'{}_{}'.format(self.name, opt), self.compose.conf.get(
'global_{}'.format(opt))))
def get_release(self, cfg):
"""
If release is set explicitly to None, replace it with date and respin.
Uses configuration passed as argument, phase specific settings and
global settings.
"""
for key, conf in [('release', cfg),
('{}_release'.format(self.name), self.compose.conf),
('global_release', self.compose.conf)]:
if key in conf:
return conf[key] or self.compose.image_release
return None
def get_ksurl(self, cfg):
"""
Get ksurl from `cfg`. If not present, fall back to phase defined one or
global one.
"""
if 'ksurl' in cfg:
return util.resolve_git_url(cfg['ksurl'])
if '{}_ksurl'.format(self.name) in self.compose.conf:
return self.phase_ksurl
if 'global_ksurl' in self.compose.conf:
return self.global_ksurl
return None
@property
def phase_ksurl(self):
"""Get phase level ksurl, making sure to resolve it only once."""
# The phase-level setting is cached as instance attribute of the phase.
if not self._phase_ksurl:
ksurl = self.compose.conf.get('{}_ksurl'.format(self.name))
self._phase_ksurl = util.resolve_git_url(ksurl)
return self._phase_ksurl
@property
def global_ksurl(self):
"""Get global ksurl setting, making sure to resolve it only once."""
# The global setting is cached in the configuration object.
if '_global_ksurl' not in self.compose.conf:
ksurl = self.compose.conf.get('global_ksurl')
self.compose.conf['_global_ksurl'] = util.resolve_git_url(ksurl)
return self.compose.conf['_global_ksurl']

View File

@ -5,8 +5,8 @@ import os
import time
from kobo import shortcuts
from pungi.util import get_variant_data, resolve_git_url, makedirs, get_mtime, get_file_size, failable
from pungi.phases.base import PhaseBase
from pungi.util import get_variant_data, makedirs, get_mtime, get_file_size, failable
from pungi.phases import base
from pungi.linker import Linker
from pungi.paths import translate_path
from pungi.wrappers.kojiwrapper import KojiWrapper
@ -14,21 +14,41 @@ from kobo.threads import ThreadPool, WorkerThread
from productmd.images import Image
class ImageBuildPhase(PhaseBase):
class ImageBuildPhase(base.ImageConfigMixin, base.ConfigGuardedPhase):
"""class for wrapping up koji image-build"""
name = "image_build"
def __init__(self, compose):
PhaseBase.__init__(self, compose)
self.pool = ThreadPool(logger=self.compose._logger)
config_options = [
{
"name": "image_build",
"expected_types": [dict],
"optional": True,
},
{
"name": "image_build_ksurl",
"expected_types": [str],
"optional": True,
},
{
"name": "image_build_target",
"expected_types": [str],
"optional": True,
},
{
"name": "image_build_release",
"expected_types": [str, type(None)],
"optional": True,
},
{
"name": "image_build_version",
"expected_types": [str],
"optional": True,
},
]
def skip(self):
if PhaseBase.skip(self):
return True
if not self.compose.conf.get(self.name):
self.compose.log_info("Config section '%s' was not found. Skipping" % self.name)
return True
return False
def __init__(self, compose):
super(ImageBuildPhase, self).__init__(compose)
self.pool = ThreadPool(logger=self.compose._logger)
def _get_install_tree(self, image_conf, variant):
"""
@ -100,14 +120,20 @@ class ImageBuildPhase(PhaseBase):
continue
# Replace possible ambiguous ref name with explicit hash.
if 'ksurl' in image_conf['image-build']:
image_conf["image-build"]['ksurl'] = resolve_git_url(image_conf["image-build"]['ksurl'])
ksurl = self.get_ksurl(image_conf['image-build'])
if ksurl:
image_conf["image-build"]['ksurl'] = ksurl
image_conf["image-build"]["variant"] = variant
image_conf["image-build"]["install_tree"] = self._get_install_tree(image_conf['image-build'], variant)
self._set_release(image_conf['image-build'])
release = self.get_release(image_conf['image-build'])
if release:
image_conf['image-build']['release'] = release
image_conf['image-build']['version'] = self.get_config(image_conf['image-build'], 'version')
image_conf['image-build']['target'] = self.get_config(image_conf['image-build'], 'target')
# transform format into right 'format' for image-build
# e.g. 'docker,qcow2'

View File

@ -150,6 +150,27 @@ class InitPhase(PhaseBase):
"optional": True,
},
# Configuration shared by all image building phases.
{
"name": "global_ksurl",
"expected_types": [str],
"optional": True,
},
{
"name": "global_target",
"expected_types": [str],
"optional": True,
},
{
"name": "global_release",
"expected_types": [str, type(None)],
"optional": True,
},
{
"name": "global_version",
"expected_types": [str],
"optional": True,
},
)

View File

@ -27,8 +27,8 @@ from productmd.images import Image
from pungi.wrappers.kojiwrapper import KojiWrapper
from pungi.wrappers.iso import IsoWrapper
from pungi.phases.base import PhaseBase
from pungi.util import get_arch_variant_data, resolve_git_url, makedirs, get_mtime, get_file_size, failable
from pungi.phases import base
from pungi.util import get_arch_variant_data, makedirs, get_mtime, get_file_size, failable
from pungi.paths import translate_path
@ -38,8 +38,8 @@ if sys.version_info[0] == 3:
return (a > b) - (a < b)
class LiveImagesPhase(PhaseBase):
name = "liveimages"
class LiveImagesPhase(base.ImageConfigMixin, base.ConfigGuardedPhase):
name = "live_images"
config_options = (
{
@ -71,20 +71,28 @@ class LiveImagesPhase(PhaseBase):
"name": "live_images_no_rename",
"expected_types": [bool],
"optional": True,
}
},
{
"name": "live_images_ksurl",
"expected_types": [str],
"optional": True,
},
{
"name": "live_images_release",
"expected_types": [str, type(None)],
"optional": True,
},
{
"name": "live_images_version",
"expected_types": [str],
"optional": True,
},
)
def __init__(self, compose):
PhaseBase.__init__(self, compose)
super(LiveImagesPhase, self).__init__(compose)
self.pool = ThreadPool(logger=self.compose._logger)
def skip(self):
if PhaseBase.skip(self):
return True
if not self.compose.conf.get("live_images"):
return True
return False
def _get_extra_repos(self, arch, variant, extras):
repo = []
for extra in extras:
@ -109,19 +117,13 @@ class LiveImagesPhase(PhaseBase):
repos.extend(self._get_extra_repos(arch, variant, force_list(data.get('repo_from', []))))
return repos
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 self.compose.image_release
return image_conf.get('release', None)
def run(self):
symlink_isos_to = self.compose.conf.get("symlink_isos_to", None)
commands = []
for variant in self.compose.variants.values():
for arch in variant.arches + ["src"]:
for data in get_arch_variant_data(self.compose.conf, "live_images", arch, variant):
for data in get_arch_variant_data(self.compose.conf, self.name, arch, variant):
subvariant = data.get('subvariant', variant.uid)
type = data.get('type', 'live')
@ -138,12 +140,12 @@ class LiveImagesPhase(PhaseBase):
cmd = {
"name": data.get('name'),
"version": data.get("version", None),
"release": self._get_release(data),
"version": self.get_config(data, 'version'),
"release": self.get_release(data),
"dest_dir": dest_dir,
"build_arch": arch,
"ks_file": data['kickstart'],
"ksurl": None,
"ksurl": self.get_ksurl(data),
# Used for images wrapped in RPM
"specfile": data.get("specfile", None),
# Scratch (only taken in consideration if specfile
@ -157,9 +159,6 @@ class LiveImagesPhase(PhaseBase):
"subvariant": subvariant,
}
if 'ksurl' in data:
cmd['ksurl'] = resolve_git_url(data['ksurl'])
cmd["repos"] = self._get_repos(arch, variant, data)
# Signing of the rpm wrapped image
@ -194,11 +193,6 @@ class LiveImagesPhase(PhaseBase):
return self.compose.get_image_name(arch, variant, disc_type=disc_type,
disc_num=None, format=format)
def stop(self, *args, **kwargs):
PhaseBase.stop(self, *args, **kwargs)
if self.skip():
return
class CreateLiveImageThread(WorkerThread):
EXTS = ('.iso', '.raw.xz')

View File

@ -4,8 +4,8 @@ import os
import time
from kobo import shortcuts
from pungi.util import get_variant_data, resolve_git_url, makedirs, get_mtime, get_file_size, failable
from pungi.phases.base import PhaseBase
from pungi.util import get_variant_data, makedirs, get_mtime, get_file_size, failable
from pungi.phases.base import ConfigGuardedPhase, ImageConfigMixin
from pungi.linker import Linker
from pungi.paths import translate_path
from pungi.wrappers.kojiwrapper import KojiWrapper
@ -13,7 +13,7 @@ from kobo.threads import ThreadPool, WorkerThread
from productmd.images import Image
class LiveMediaPhase(PhaseBase):
class LiveMediaPhase(ImageConfigMixin, ConfigGuardedPhase):
"""class for wrapping up koji spin-livemedia"""
name = 'live_media'
@ -37,21 +37,17 @@ class LiveMediaPhase(PhaseBase):
"name": "live_media_release",
"expected_types": [str, type(None)],
"optional": True,
}
},
{
"name": "live_media_version",
"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():
return True
if not self.compose.conf.get(self.name):
self.compose.log_info("Config section '%s' was not found. Skipping" % self.name)
return True
return False
def _get_repos(self, image_conf, variant):
"""
@ -84,15 +80,6 @@ class LiveMediaPhase(PhaseBase):
arches = set(image_conf.get('arches', [])) & arches
return sorted(arches)
def _get_release(self, image_conf):
"""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 self.compose.image_release
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:
variant_uid = image_conf['install_tree_from']
@ -107,23 +94,6 @@ 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'])
@ -132,20 +102,20 @@ class LiveMediaPhase(PhaseBase):
name = image_conf.get(
'name', "%s-%s-Live" % (self.compose.ci_base.release.short, subvariant))
config = {
'target': self._get_config(image_conf, 'target'),
'target': self.get_config(image_conf, 'target'),
'arches': self._get_arches(image_conf, arches),
'ksfile': image_conf['kickstart'],
'ksurl': self._get_ksurl(image_conf),
'ksurl': self.get_ksurl(image_conf),
'ksversion': image_conf.get('ksversion'),
'scratch': image_conf.get('scratch', False),
'release': self._get_release(image_conf),
'release': self.get_release(image_conf),
'skip_tag': image_conf.get('skip_tag'),
'name': name,
'subvariant': subvariant,
'title': image_conf.get('title'),
'repo': self._get_repos(image_conf, variant),
'install_tree': self._get_install_tree(image_conf, variant),
'version': self._get_config(image_conf, 'version'),
'version': self.get_config(image_conf, 'version'),
}
self.pool.add(LiveMediaThread(self.pool))
self.pool.queue_put((self.compose, variant, config))

View File

@ -98,6 +98,65 @@ class TestImageBuildPhase(PungiTestCase):
[mock.call((compose, client_args)),
mock.call((compose, server_args))])
@mock.patch('pungi.phases.image_build.ThreadPool')
def test_image_build_phase_global_options(self, ThreadPool):
compose = DummyCompose(self.topdir, {
'image_build_ksurl': 'git://git.fedorahosted.org/git/spin-kickstarts.git',
'image_build_release': None,
'image_build_target': 'f24',
'image_build_version': 'Rawhide',
'image_build': {
'^Server$': [
{
'image-build': {
'format': [('docker', 'tar.xz')],
'name': 'Fedora-Docker-Base',
'kickstart': "fedora-docker-base.ks",
'distro': 'Fedora-20',
'disk_size': 3
}
}
]
},
'koji_profile': 'koji',
})
phase = ImageBuildPhase(compose)
phase.run()
self.maxDiff = None
# assert at least one thread was started
self.assertTrue(phase.pool.add.called)
server_args = {
"format": [('docker', 'tar.xz')],
"image_conf": {
'image-build': {
'install_tree': self.topdir + '/compose/Server/$arch/os',
'kickstart': 'fedora-docker-base.ks',
'format': 'docker',
'repo': self.topdir + '/compose/Server/$arch/os',
'variant': compose.variants['Server'],
'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',
'release': '20151203.t.0',
}
},
"conf_file": self.topdir + '/work/image-build/Server/docker_Fedora-Docker-Base.cfg',
"image_dir": self.topdir + '/compose/Server/%(arch)s/images',
"relative_image_dir": 'Server/%(arch)s/images',
"link_type": 'hardlink-or-copy',
"scratch": False,
}
self.maxDiff = None
self.assertItemsEqual(phase.pool.queue_put.mock_calls,
[mock.call((compose, server_args))])
@mock.patch('pungi.phases.image_build.ThreadPool')
def test_image_build_filter_all_variants(self, ThreadPool):
compose = DummyCompose(self.topdir, {
@ -321,7 +380,7 @@ class TestImageBuildPhase(PungiTestCase):
args, kwargs = phase.pool.queue_put.call_args
self.assertTrue(args[0][1].get('scratch'))
@mock.patch('pungi.phases.image_build.resolve_git_url')
@mock.patch('pungi.util.resolve_git_url')
@mock.patch('pungi.phases.image_build.ThreadPool')
def test_image_build_resolve_ksurl(self, ThreadPool, resolve_git_url):
compose = DummyCompose(self.topdir, {

View File

@ -221,7 +221,7 @@ class TestLiveImagesPhase(PungiTestCase):
'amd64'))])
@mock.patch('pungi.phases.live_images.ThreadPool')
@mock.patch('pungi.phases.live_images.resolve_git_url')
@mock.patch('pungi.util.resolve_git_url')
def test_spin_appliance(self, resolve_git_url, ThreadPool):
compose = DummyCompose(self.topdir, {
'live_images': [
@ -270,6 +270,110 @@ 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')
@mock.patch('pungi.util.resolve_git_url')
def test_spin_appliance_phase_global_settings(self, resolve_git_url, ThreadPool):
compose = DummyCompose(self.topdir, {
'live_images_ksurl': 'https://git.example.com/kickstarts.git?#HEAD',
'live_images_release': None,
'live_images_version': 'Rawhide',
'live_images': [
('^Client$', {
'amd64': {
'kickstart': 'test.ks',
'additional_repos': ['http://example.com/repo/'],
'repo_from': ['Everything'],
'type': 'appliance',
}
})
],
})
resolve_git_url.return_value = 'https://git.example.com/kickstarts.git?#CAFEBABE'
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/images',
'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': 'Rawhide',
'specfile': None,
'sign': False,
'type': 'appliance',
'release': '20151203.t.0',
'subvariant': 'Client',
'ksurl': 'https://git.example.com/kickstarts.git?#CAFEBABE'},
compose.variants['Client'],
'amd64'))])
self.assertEqual(resolve_git_url.mock_calls,
[mock.call('https://git.example.com/kickstarts.git?#HEAD')])
@mock.patch('pungi.phases.live_images.ThreadPool')
@mock.patch('pungi.util.resolve_git_url')
def test_spin_appliance_global_settings(self, resolve_git_url, ThreadPool):
compose = DummyCompose(self.topdir, {
'global_ksurl': 'https://git.example.com/kickstarts.git?#HEAD',
'global_release': None,
'global_version': 'Rawhide',
'live_images': [
('^Client$', {
'amd64': {
'kickstart': 'test.ks',
'additional_repos': ['http://example.com/repo/'],
'repo_from': ['Everything'],
'type': 'appliance',
}
})
],
})
resolve_git_url.return_value = 'https://git.example.com/kickstarts.git?#CAFEBABE'
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/images',
'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': 'Rawhide',
'specfile': None,
'sign': False,
'type': 'appliance',
'release': '20151203.t.0',
'subvariant': 'Client',
'ksurl': 'https://git.example.com/kickstarts.git?#CAFEBABE'},
compose.variants['Client'],
'amd64'))])
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, {

View File

@ -75,9 +75,9 @@ class TestLiveMediaPhase(PungiTestCase):
'subvariant': 'Server',
}))])
@mock.patch('pungi.phases.livemedia_phase.resolve_git_url')
@mock.patch('pungi.util.resolve_git_url')
@mock.patch('pungi.phases.livemedia_phase.ThreadPool')
def test_live_media_with_global_opts(self, ThreadPool, resolve_git_url):
def test_live_media_with_phase_global_opts(self, ThreadPool, resolve_git_url):
compose = DummyCompose(self.topdir, {
'live_media_ksurl': 'git://example.com/repo.git#HEAD',
'live_media_target': 'f24',
@ -171,6 +171,102 @@ class TestLiveMediaPhase(PungiTestCase):
'subvariant': 'Server',
}))])
@mock.patch('pungi.util.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, {
'global_ksurl': 'git://example.com/repo.git#HEAD',
'global_target': 'f24',
'global_release': 'RRR',
'global_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',
'subvariant': 'Server',
})),
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',
'subvariant': 'Server',
})),
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',
'subvariant': 'Server',
}))])
@mock.patch('pungi.phases.livemedia_phase.ThreadPool')
def test_live_media_non_existing_install_tree(self, ThreadPool):
compose = DummyCompose(self.topdir, {
@ -217,7 +313,7 @@ class TestLiveMediaPhase(PungiTestCase):
with self.assertRaisesRegexp(RuntimeError, r'no.+Missing.+when building.+Server'):
phase.run()
@mock.patch('pungi.phases.livemedia_phase.resolve_git_url')
@mock.patch('pungi.util.resolve_git_url')
@mock.patch('pungi.phases.livemedia_phase.ThreadPool')
def test_live_media_full(self, ThreadPool, resolve_git_url):
compose = DummyCompose(self.topdir, {

73
tests/test_phase_base.py Normal file
View File

@ -0,0 +1,73 @@
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
import mock
import unittest
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
from pungi.phases import base
from tests.helpers import DummyCompose, PungiTestCase
class Phase1(base.ImageConfigMixin, base.PhaseBase):
name = 'phase1'
class Phase2(base.ImageConfigMixin, base.PhaseBase):
name = 'phase2'
class Phase3(base.ImageConfigMixin, base.PhaseBase):
name = 'phase3'
class DummyResolver(object):
def __init__(self):
self.num = 0
def __call__(self, url):
self.num += 1
return url.replace('HEAD', 'RES' + str(self.num))
class ImageConfigMixinTestCase(PungiTestCase):
@mock.patch('pungi.util.resolve_git_url', new_callable=DummyResolver)
def test_git_url_resolved_once(self, resolve_git_url):
compose = DummyCompose(self.topdir, {
'global_ksurl': 'git://example.com/repo.git?#HEAD',
'phase1_ksurl': 'git://example.com/another.git?#HEAD',
})
p1 = Phase1(compose)
p2 = Phase2(compose)
p3 = Phase3(compose)
self.assertEqual(p1.get_ksurl({}),
'git://example.com/another.git?#RES1')
# Phase-level setting retrieved second time.
self.assertEqual(p1.get_ksurl({}),
'git://example.com/another.git?#RES1')
self.assertEqual(p2.get_ksurl({}),
'git://example.com/repo.git?#RES2')
# Global setting retrieved again from same phase.
self.assertEqual(p2.get_ksurl({}),
'git://example.com/repo.git?#RES2')
# Global setting retrieved from another phase.
self.assertEqual(p3.get_ksurl({}),
'git://example.com/repo.git?#RES2')
# Local setting ignores global ones.
self.assertEqual(p3.get_ksurl({'ksurl': 'git://example.com/more.git?#HEAD'}),
'git://example.com/more.git?#RES3')
self.assertEqual(resolve_git_url.num, 3, 'Resolver was not called three times')
if __name__ == "__main__":
unittest.main()