pungi/pungi/phases/osbs.py
Qixiang Wan 2f5d6d7dcd unify repo and repo_from options
Config option 'repo' and 'repo_from' are used in several phases, merge
them with one option 'repo'. 'append' in schema is used for appending
the values from deprecated options to 'repo', so it won't break on any
existing config files that have the old options of 'repo_from' and
'source_repo_from' (which is an alias of 'repo_from').

And 'repo' schema is updated to support repo dict as the value or an
item in the values, a repo dict is just a dict contains repo options,
'baseurl' is required in the dict, like:

{"baseurl": "http://example.com/url/to/repo"}

or:

{"baseurl": "Serer"}

currently this is used in ostree phase to support extra repo options
like:

{"baseurl": "Server", "exclude": "systemd-container"}

Signed-off-by: Qixiang Wan <qwan@redhat.com>
2017-03-29 10:12:32 +08:00

152 lines
5.9 KiB
Python

# -*- coding: utf-8 -*-
import json
import os
from kobo.threads import ThreadPool, WorkerThread
from kobo import shortcuts
from .base import ConfigGuardedPhase, PhaseLoggerMixin
from .. import util
from ..wrappers import kojiwrapper
class OSBSPhase(PhaseLoggerMixin, ConfigGuardedPhase):
name = 'osbs'
def __init__(self, compose):
super(OSBSPhase, self).__init__(compose)
self.pool = ThreadPool(logger=self.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',
logger=self.pool._logger):
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
source = util.resolve_git_url(config.pop('url'))
target = config.pop('target')
priority = config.pop('priority', None)
gpgkey = config.pop('gpgkey', None)
repos = [self._get_repo(compose, v, gpgkey=gpgkey)
for v in [variant.uid] + shortcuts.force_list(config.pop('repo', []))]
config['yum_repourls'] = repos
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))
scratch = config.get('scratch', False)
self._add_metadata(koji.koji_proxy, variant, task_id, compose, scratch)
self.pool.log_info('[DONE ] %s' % msg)
def _add_metadata(self, koji_proxy, variant, task_id, compose, is_scratch):
# Create metadata
metadata = {
'compose_id': compose.compose_id,
'koji_task': task_id,
}
result = koji_proxy.getTaskResult(task_id)
if is_scratch:
metadata.update({
'repositories': result['repositories'],
})
# add a fake arch of 'scratch', so we can construct the metadata
# in same data structure as real builds.
self.pool.metadata.setdefault(
variant.uid, {}).setdefault('scratch', []).append(metadata)
else:
build_id = int(result['koji_builds'][0])
buildinfo = koji_proxy.getBuild(build_id)
archives = koji_proxy.listArchives(build_id)
metadata.update({
'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.log_debug('Created Docker base image %s-%s-%s.%s' % (
metadata['name'], metadata['version'], metadata['release'], arch))
self.pool.metadata.setdefault(
variant.uid, {}).setdefault(arch, []).append(data)
def _get_repo(self, compose, repo, gpgkey=None):
"""
Return repo file URL of repo, if repo contains "://", it's already
a URL of repo file. Or it's a variant UID, then write a .repo file
pointing to current variant and return the URL to .repo file.
"""
if "://" in repo:
return repo
try:
variant = compose.all_variants[repo]
except KeyError:
raise RuntimeError(
'There is no variant %s to get repo from to pass to OSBS.'
% (repo))
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)
gpgcheck = 1 if gpgkey else 0
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' % util.translate_path(compose, os_tree))
f.write('enabled=1\n')
f.write('gpgcheck=%s\n' % gpgcheck)
if gpgcheck:
f.write('gpgkey=%s\n' % gpgkey)
return util.translate_path(compose, repo_file)