tumbi-assembler/pungi/phases/osbs.py
Lubomír Sedlář 463088d580 Refactor failables
This is a breaking change as big part of current failable_deliverables
options will be ignored.

There is no change for buildinstall and creatiso phase.

Failability for artifacts in other phases is now configured per
artifact. It already works correctly for ostree and ostree_installer
phases (even per-arch). For OSBS phase there is currently only a binary
switch as it does not handle multiple arches yet. When it gains that
support, the option should contain list of non-blocking architectures.

For live images, live media and image build phases each config block can
configure list of failable arches. If the list is not empty, it can
fail. Once we have a way to fail only some arches, the config will not
need to change.

Signed-off-by: Lubomír Sedlář <lsedlar@redhat.com>
2016-07-27 13:06:01 +02:00

146 lines
5.3 KiB
Python

# -*- coding: utf-8 -*-
import json
import os
from kobo.threads import ThreadPool, WorkerThread
from .base import ConfigGuardedPhase
from .. import util
from ..wrappers import kojiwrapper
from ..paths import translate_path
class OSBSPhase(ConfigGuardedPhase):
name = 'osbs'
config_options = [
{
"name": "osbs",
"expected_types": [dict],
"optional": True,
}
]
def __init__(self, compose):
super(OSBSPhase, self).__init__(compose)
self.pool = ThreadPool(logger=self.compose._logger)
self.pool.metadata = {}
def run(self):
for variant in self.compose.get_variants():
for conf in util.get_variant_data(self.compose.conf, self.name, variant):
self.pool.add(OSBSThread(self.pool))
self.pool.queue_put((self.compose, variant, conf))
self.pool.start()
def dump_metadata(self):
"""Create a file with image metadata if the phase actually ran."""
if self._skipped:
return
with open(self.compose.paths.compose.metadata('osbs.json'), 'w') as f:
json.dump(self.pool.metadata, f, indent=4, sort_keys=True,
separators=(',', ': '))
class OSBSThread(WorkerThread):
def process(self, item, num):
compose, variant, config = item
self.num = num
with util.failable(compose, bool(config.pop('failable', None)), variant, '*', 'osbs'):
self.worker(compose, variant, config)
def worker(self, compose, variant, config):
msg = 'OSBS phase for variant %s' % variant.uid
self.pool.log_info('[BEGIN] %s' % msg)
koji = kojiwrapper.KojiWrapper(compose.conf['koji_profile'])
koji.login()
# Start task
try:
source = util.resolve_git_url(config.pop('url'))
target = config.pop('target')
# Set release dynamically
if 'release' in config and config['release'] is None:
config['release'] = self._get_release(koji, target, config['name'])
except KeyError as exc:
raise RuntimeError('OSBS: missing config key %s for %s'
% (exc, variant.uid))
priority = config.pop('priority', None)
config['yum_repourls'] = [self._get_repo(compose, variant)]
task_id = koji.koji_proxy.buildContainer(source, target, config,
priority=priority)
# Wait for it to finish and capture the output into log file (even
# though there is not much there).
log_dir = os.path.join(compose.paths.log.topdir(), 'osbs')
util.makedirs(log_dir)
log_file = os.path.join(log_dir, '%s-%s-watch-task.log'
% (variant.uid, self.num))
if koji.watch_task(task_id, log_file) != 0:
raise RuntimeError('OSBS: task %s failed: see %s for details'
% (task_id, log_file))
# Only real builds get the metadata.
if not config.get('scratch', False):
self._add_metadata(koji.koji_proxy, variant, task_id)
self.pool.log_info('[DONE ] %s' % msg)
def _add_metadata(self, koji_proxy, variant, task_id):
# Create metadata
result = koji_proxy.getTaskResult(task_id)
build_id = result['koji_builds'][0]
buildinfo = koji_proxy.getBuild(build_id)
archives = koji_proxy.listArchives(build_id)
metadata = {
'name': buildinfo['name'],
'version': buildinfo['version'],
'release': buildinfo['release'],
'creation_time': buildinfo['creation_time'],
}
for archive in archives:
data = {
'filename': archive['filename'],
'size': archive['size'],
'checksum': archive['checksum'],
}
data.update(archive['extra'])
data.update(metadata)
arch = archive['extra']['image']['arch']
self.pool.metadata.setdefault(
variant.uid, {}).setdefault(arch, []).append(data)
def _get_repo(self, compose, variant):
"""
Write a .repo file pointing to current variant and return URL to the
file.
"""
os_tree = compose.paths.compose.os_tree('$basearch', variant,
create_dir=False)
repo_file = os.path.join(compose.paths.work.tmp_dir(None, variant),
'compose-rpms-%s.repo' % self.num)
with open(repo_file, 'w') as f:
f.write('[%s]\n' % compose.compose_id)
f.write('name=Compose %s (RPMs)\n' % compose.compose_id)
f.write('baseurl=%s\n' % translate_path(compose, os_tree))
f.write('enabled=1\n')
f.write('gpgcheck=0\n')
return translate_path(compose, repo_file)
def _get_release(self, koji, target, name):
"""
Get next release value based on last build. If no build has been done
yet (in given target), use 1 as initial value.
"""
latest_builds = koji.koji_proxy.getLatestBuilds(target, package=name)
try:
return koji.koji_proxy.getNextRelease(latest_builds[0])
except IndexError:
return 1