From 0c7825287d1f3b601c89f47ab3a83d2eafd49a36 Mon Sep 17 00:00:00 2001
From: Chenxiong Qi <cqi@redhat.com>
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 <cqi@redhat.com>

---

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 <cqi@redhat.com>
+
+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()

