Merge #273 Deduplicate configuration a bit
This commit is contained in:
commit
c80b7c6894
@ -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
|
||||
====================
|
||||
|
||||
|
@ -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']
|
||||
|
@ -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'
|
||||
|
@ -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,
|
||||
},
|
||||
|
||||
)
|
||||
|
||||
|
@ -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')
|
||||
|
@ -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))
|
||||
|
@ -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, {
|
||||
|
@ -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, {
|
||||
|
@ -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
73
tests/test_phase_base.py
Normal 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()
|
Loading…
Reference in New Issue
Block a user