Blob Blame Raw
#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
 (c) 2016 - Copyright Red Hat Inc

 Authors:
   Pierre-Yves Chibon <pingou@pingoured.fr>


This server listens to message sent via redis and send the corresponding
web-hook request.

Using this mechanism, we no longer block the main application if the
receiving end is offline or so.

"""

from __future__ import print_function
import json
import logging
import os

import jenkins
import trollius
import trollius_redis

import pagure
import pagure.lib


if 'PAGURE_CONFIG' not in os.environ \
        and os.path.exists('/etc/pagure/pagure.cfg'):
    print('Using configuration file `/etc/pagure/pagure.cfg`')
    os.environ['PAGURE_CONFIG'] = '/etc/pagure/pagure.cfg'


_log = logging.getLogger(__name__)
_config = pagure.config.config.reload_config()


@trollius.coroutine
def handle_messages():
    ''' Handles connecting to redis and acting upon messages received.
    In this case, it means triggering a build on jenkins based on the
    information provided.
    '''

    host = _config.get('REDIS_HOST', '0.0.0.0')
    port = _config.get('REDIS_PORT', 6379)
    dbname = _config.get('REDIS_DB', 0)
    connection = yield trollius.From(trollius_redis.Connection.create(
        host=host, port=port, db=dbname))

    # Create subscriber.
    subscriber = yield trollius.From(connection.start_subscribe())

    # Subscribe to channel.
    yield trollius.From(subscriber.subscribe(['pagure.ci']))

    # Inside a while loop, wait for incoming events.
    while True:
        reply = yield trollius.From(subscriber.next_published())
        _log.info(
            'Received: %s on channel: %s',
            repr(reply.value), reply.channel)
        data = json.loads(reply.value)

        pr_id = data['pr']['id']
        pr_uid = data['pr']['uid']
        branch = data['pr']['branch_from']
        _log.info('Looking for PR: %s', pr_uid)
        session = pagure.lib.create_session(_config['DB_URL'])
        request = pagure.lib.get_request_by_uid(session, pr_uid)

        _log.info('PR retrieved: %s', request)

        if not request:
            _log.warning(
                'No request could be found from the message %s', data)
            session.close()
            continue

        _log.info(
            "Trigger on %s PR #%s from %s: %s",
            request.project.fullname, pr_id,
            request.project_from.fullname, branch)

        url = request.project.ci_hook.ci_url.rstrip('/')

        if data['ci_type'] == 'jenkins':
            _log.info('Jenkins CI')
            repo = '%s/%s' % (
                _config['GIT_URL_GIT'].rstrip('/'),
                request.project_from.path)

            # Jenkins Base URL
            base_url, name = url.split('/job/', 1)
            jenkins_name = name.split('/', 1)[0]

            data = {
                'cause': pr_id,
                'REPO': repo,
                'BRANCH': branch
            }

            server = jenkins.Jenkins(base_url)
            _log.info('Triggering at: %s for: %s - data: %s' % (
                base_url, jenkins_name, data))
            try:
                server.build_job(
                    name=jenkins_name,
                    parameters=data,
                    token=request.project.ci_hook.pagure_ci_token
                )
                _log.info('Build triggered')
            except Exception as err:
                _log.info('An error occured: %s', err)

        else:
            _log.warning('Un-supported CI type')

        session.close()
        _log.info('Ready for another')


def main():
    ''' Start the main async loop. '''

    try:
        loop = trollius.get_event_loop()
        tasks = [
            trollius.async(handle_messages()),
        ]
        loop.run_until_complete(trollius.wait(tasks))
        loop.run_forever()
    except KeyboardInterrupt:
        pass
    except trollius.ConnectionResetError:
        pass

    _log.info("End Connection")
    loop.close()
    _log.info("End")


if __name__ == '__main__':
    formatter = logging.Formatter(
        "%(asctime)s %(levelname)s [%(module)s:%(lineno)d] %(message)s")

    logging.basicConfig(level=logging.DEBUG)

    # setup console logging
    _log.setLevel(logging.DEBUG)
    shellhandler = logging.StreamHandler()
    shellhandler.setLevel(logging.DEBUG)

    aslog = logging.getLogger("asyncio")
    aslog.setLevel(logging.DEBUG)
    aslog = logging.getLogger("trollius")
    aslog.setLevel(logging.DEBUG)

    shellhandler.setFormatter(formatter)
    _log.addHandler(shellhandler)
    main()