From 0c7825287d1f3b601c89f47ab3a83d2eafd49a36 Mon Sep 17 00:00:00 2001 From: Chenxiong Qi Date: May 03 2017 05:12:19 +0000 Subject: Refactor build image handler * Move Koji session operations into a separate module * New context manager koji_service used with with statement * Simplify build_image method Signed-off-by: Chenxiong Qi --- diff --git a/freshmaker/handlers/image_builder.py b/freshmaker/handlers/image_builder.py index 1ac2223..81993e1 100644 --- a/freshmaker/handlers/image_builder.py +++ b/freshmaker/handlers/image_builder.py @@ -24,6 +24,7 @@ from freshmaker import log, conf from freshmaker.handlers import BaseHandler from freshmaker.events import DockerfileChanged +from freshmaker.kojiservice import koji_service class DockerImageRebuildHandler(BaseHandler): @@ -33,52 +34,31 @@ class DockerImageRebuildHandler(BaseHandler): def handle(self, event): """Rebuild docker image""" - self.build_image(event) - - def build_image(self, event): import koji - config = koji.read_config(conf.koji_profile) - koji_server = config['server'] - - session = koji.ClientSession(koji_server, {'krb_rdns': config['krb_rdns']}) - - log.debug('Logging into {0} with Kerberos authentication.'.format(koji_server)) - proxyuser = conf.koji_build_owner if conf.koji_proxyuser else None - try: - session.krb_login(proxyuser=proxyuser) - except Exception as e: - log.error('Failed to login Koji via Kerberos using GSSAPI') - log.error('Error message from Koji: %s', e) - return + self.build_image(event) + except koji.krbV.Krb5Error as e: + log.exception('Failed to login Koji via Kerberos using GSSAPI. %s', e.args[1]) + except: + log.exception('Could not create task to build docker image %s', event.repo) - if not session.logged_in: - log.error('Could not login server %s', koji_server) - return + def build_image(self, event): + with koji_service(profile=conf.koji_profile, logger=log) as service: + log.debug('Logging into {0} with Kerberos authentication.'.format(service.server)) + proxyuser = conf.koji_build_owner if conf.koji_proxyuser else None - build_opts = { - 'scratch': conf.koji_container_scratch_build, - 'git_branch': event.branch, - } + service.krb_login(proxyuser=proxyuser) + + if not service.logged_in: + log.error('Could not login server %s', service.server) + return - try: - build_target = '{}-{}-candidate'.format( - 'rawhide' if event.branch == 'master' else event.branch, - event.namespace) build_source = '{}#{}'.format(event.repo_url, event.rev) log.info('Start to build docker image %s', event.repo) log.debug('Build from source: %s', build_source) - log.debug('Build in target: %s', build_target) - log.debug('Build options: %s', build_opts) - task_id = session.buildContainer(build_source, build_target, build_opts) - except Exception as e: - log.exception('Could not create task to build docker image %s', event.repo) - else: - log.info('Task %s is created to build docker image for repo %s', task_id, event.repo) - log.info('Task info: %s/taskinfo?taskID=%s', config['weburl'], task_id) - finally: - log.debug('Logout Koji session') - session.logout() + return service.build_container(build_source, event.branch, + namespace=event.namespace, + scratch=conf.koji_container_scratch_build) diff --git a/freshmaker/kojiservice.py b/freshmaker/kojiservice.py new file mode 100644 index 0000000..af9f0fd --- /dev/null +++ b/freshmaker/kojiservice.py @@ -0,0 +1,123 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017 Red Hat, Inc. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +# Written by Chenxiong Qi + +import contextlib + + +class KojiService(object): + """Wrapper of Koji API and profile configuration + + As an interface of Koji profile configuration, KojiService exposes part + of options that would be used frequently. However, other options are still + accessible from ``config`` property. + + As a wrapper of Koji API, new APIs could be added as well. + """ + + def __init__(self, profile=None, logger=None): + import koji + + self._config = koji.read_config(profile or 'koji') + self._logger = logger + + @property + def config(self): + return self._config + + @property + def weburl(self): + return self.config['weburl'] + + @property + def server(self): + return self.config['server'] + + @property + def session(self): + import koji + if not hasattr(self, '_session'): + self._session = koji.ClientSession(self.config['server'], + {'krb_rdns': self.config['krb_rdns']}) + return self._session + + def krb_login(self, proxyuser=None): + self.session.krb_login(proxyuser=proxyuser) + + @property + def logged_in(self): + return self.session.logged_in + + def logout(self): + self.session.logout() + + def build_container(self, source_url, branch, namespace=None, scratch=None): + """Build container by buildContainer""" + + build_target = '{}-{}-candidate'.format( + 'rawhide' if branch == 'master' else branch, + 'container' if namespace is None else namespace) + + build_opts = { + 'scratch': False if scratch is None else True, + 'git_branch': branch, + } + + if self._logger: + self._logger.debug('Build from target: %s', build_target) + self._logger.debug('Build options: %s', build_opts) + + task_id = self.session.buildContainer(source_url, build_target, build_opts) + + if self._logger: + self._logger.info('Task %s is created to build docker image for %s', + task_id, source_url) + self._logger.info('Task info: %s/taskinfo?taskID=%s', self.weburl, task_id) + + return task_id + + +@contextlib.contextmanager +def koji_service(profile=None, logger=None): + """A Koji service context manager that could be used with with + + Example:: + + with KojiService() as service: + ... + + # if you want it to log something + with KojiService(logger=logger) as service: + ... + + # if you want it to use alternative Koji profile rather than the default one koji + with KojiService(koji='stg', logger=logger) as service: + ... + """ + service = KojiService(profile=profile, logger=logger) + try: + yield service + finally: + if service.logged_in: + if logger: + logger.debug('Logout Koji session') + service.logout()