Blame pagure-ci/pagure_ci_server.py

Pierre-Yves Chibon dd38bc
#!/usr/bin/env python
Pierre-Yves Chibon dd38bc
# -*- coding: utf-8 -*-
Pierre-Yves Chibon dd38bc
Pierre-Yves Chibon dd38bc
"""
Pierre-Yves Chibon dd38bc
 (c) 2016 - Copyright Red Hat Inc
Pierre-Yves Chibon dd38bc
Pierre-Yves Chibon dd38bc
 Authors:
Pierre-Yves Chibon dd38bc
   Pierre-Yves Chibon <pingou@pingoured.fr></pingou@pingoured.fr>
Pierre-Yves Chibon dd38bc
Pierre-Yves Chibon dd38bc
Pierre-Yves Chibon dd38bc
This server listens to message sent via redis and send the corresponding
Pierre-Yves Chibon dd38bc
web-hook request.
Pierre-Yves Chibon dd38bc
Pierre-Yves Chibon dd38bc
Using this mechanism, we no longer block the main application if the
Pierre-Yves Chibon dd38bc
receiving end is offline or so.
Pierre-Yves Chibon dd38bc
Pierre-Yves Chibon dd38bc
"""
Pierre-Yves Chibon dd38bc
Vadim Rutkovsky 9b6a62
from __future__ import print_function
Pierre-Yves Chibon dd38bc
import json
Pierre-Yves Chibon dd38bc
import logging
Pierre-Yves Chibon dd38bc
import os
Pierre-Yves Chibon dd38bc
Pierre-Yves Chibon 30e2e6
import jenkins
Pierre-Yves Chibon dd38bc
import trollius
Pierre-Yves Chibon dd38bc
import trollius_redis
Pierre-Yves Chibon dd38bc
Pierre-Yves Chibon b130e5
import pagure
Pierre-Yves Chibon b130e5
import pagure.lib
Pierre-Yves Chibon dd38bc
Pierre-Yves Chibon dd38bc
Pierre-Yves Chibon dd38bc
if 'PAGURE_CONFIG' not in os.environ \
Pierre-Yves Chibon dd38bc
        and os.path.exists('/etc/pagure/pagure.cfg'):
Vadim Rutkovsky 9b6a62
    print('Using configuration file `/etc/pagure/pagure.cfg`')
Pierre-Yves Chibon dd38bc
    os.environ['PAGURE_CONFIG'] = '/etc/pagure/pagure.cfg'
Pierre-Yves Chibon dd38bc
Pierre-Yves Chibon dd38bc
Pierre-Yves Chibon b130e5
_log = logging.getLogger(__name__)
Pierre-Yves Chibon b130e5
_config = pagure.config.config.reload_config()
Pierre-Yves Chibon dd38bc
Pierre-Yves Chibon dd38bc
Pierre-Yves Chibon dd38bc
@trollius.coroutine
Pierre-Yves Chibon dd38bc
def handle_messages():
Pierre-Yves Chibon d31d7e
    ''' Handles connecting to redis and acting upon messages received.
Pierre-Yves Chibon d31d7e
    In this case, it means triggering a build on jenkins based on the
Pierre-Yves Chibon d31d7e
    information provided.
Pierre-Yves Chibon d31d7e
    '''
Pierre-Yves Chibon d31d7e
Pierre-Yves Chibon b130e5
    host = _config.get('REDIS_HOST', '0.0.0.0')
Pierre-Yves Chibon b130e5
    port = _config.get('REDIS_PORT', 6379)
Pierre-Yves Chibon b130e5
    dbname = _config.get('REDIS_DB', 0)
Pierre-Yves Chibon dd38bc
    connection = yield trollius.From(trollius_redis.Connection.create(
Pierre-Yves Chibon d31d7e
        host=host, port=port, db=dbname))
Pierre-Yves Chibon dd38bc
Pierre-Yves Chibon dd38bc
    # Create subscriber.
Pierre-Yves Chibon dd38bc
    subscriber = yield trollius.From(connection.start_subscribe())
Pierre-Yves Chibon dd38bc
Pierre-Yves Chibon dd38bc
    # Subscribe to channel.
Pierre-Yves Chibon dd38bc
    yield trollius.From(subscriber.subscribe(['pagure.ci']))
Pierre-Yves Chibon dd38bc
Pierre-Yves Chibon dd38bc
    # Inside a while loop, wait for incoming events.
Pierre-Yves Chibon dd38bc
    while True:
Pierre-Yves Chibon dd38bc
        reply = yield trollius.From(subscriber.next_published())
Pierre-Yves Chibon 4635b5
        _log.info(
Pierre-Yves Chibon dd38bc
            'Received: %s on channel: %s',
Pierre-Yves Chibon dd38bc
            repr(reply.value), reply.channel)
Pierre-Yves Chibon dd38bc
        data = json.loads(reply.value)
Pierre-Yves Chibon dd38bc
Pierre-Yves Chibon dd38bc
        pr_id = data['pr']['id']
Pierre-Yves Chibon 6c8123
        pr_uid = data['pr']['uid']
Pierre-Yves Chibon dd38bc
        branch = data['pr']['branch_from']
Pierre-Yves Chibon 4635b5
        _log.info('Looking for PR: %s', pr_uid)
Pierre-Yves Chibon b130e5
        session = pagure.lib.create_session(_config['DB_URL'])
Pierre-Yves Chibon d3ad5b
        request = pagure.lib.get_request_by_uid(session, pr_uid)
Pierre-Yves Chibon be8146
Pierre-Yves Chibon 4635b5
        _log.info('PR retrieved: %s', request)
Pierre-Yves Chibon dd38bc
Pierre-Yves Chibon 6c8123
        if not request:
Pierre-Yves Chibon 4635b5
            _log.warning(
Pierre-Yves Chibon 6c8123
                'No request could be found from the message %s', data)
Pierre-Yves Chibon be8146
            session.close()
Pierre-Yves Chibon dd38bc
            continue
Pierre-Yves Chibon dd38bc
Pierre-Yves Chibon 4635b5
        _log.info(
Pierre-Yves Chibon bf6903
            "Trigger on %s PR #%s from %s: %s",
Pierre-Yves Chibon 6c8123
            request.project.fullname, pr_id,
Pierre-Yves Chibon 6c8123
            request.project_from.fullname, branch)
Pierre-Yves Chibon dd38bc
Pierre-Yves Chibon 6c8123
        url = request.project.ci_hook.ci_url.rstrip('/')
Pierre-Yves Chibon dd38bc
Pierre-Yves Chibon dd38bc
        if data['ci_type'] == 'jenkins':
Pierre-Yves Chibon 2f0ff3
            _log.info('Jenkins CI')
Pierre-Yves Chibon 3e1101
            repo = '%s/%s' % (
Pierre-Yves Chibon b130e5
                _config['GIT_URL_GIT'].rstrip('/'),
Pierre-Yves Chibon 6c8123
                request.project_from.path)
Pierre-Yves Chibon 30e2e6
Pierre-Yves Chibon 30e2e6
            # Jenkins Base URL
Pierre-Yves Chibon 30e2e6
            base_url, name = url.split('/job/', 1)
Pierre-Yves Chibon 30e2e6
            jenkins_name = name.split('/', 1)[0]
Pierre-Yves Chibon 30e2e6
Pierre-Yves Chibon 30e2e6
            data = {
Pierre-Yves Chibon 30e2e6
                'cause': pr_id,
Pierre-Yves Chibon 30e2e6
                'REPO': repo,
Pierre-Yves Chibon 30e2e6
                'BRANCH': branch
Pierre-Yves Chibon 30e2e6
            }
Pierre-Yves Chibon 30e2e6
Pierre-Yves Chibon 30e2e6
            server = jenkins.Jenkins(base_url)
Pierre-Yves Chibon fc1fad
            _log.info('Triggering at: %s for: %s - data: %s' % (
Pierre-Yves Chibon fc1fad
                base_url, jenkins_name, data))
Pierre-Yves Chibon 2f0ff3
            try:
Pierre-Yves Chibon 2f0ff3
                server.build_job(
Pierre-Yves Chibon 2f0ff3
                    name=jenkins_name,
Pierre-Yves Chibon 2f0ff3
                    parameters=data,
Pierre-Yves Chibon 2f0ff3
                    token=request.project.ci_hook.pagure_ci_token
Pierre-Yves Chibon 2f0ff3
                )
Pierre-Yves Chibon 2f0ff3
                _log.info('Build triggered')
Pierre-Yves Chibon 2f0ff3
            except Exception as err:
Pierre-Yves Chibon 2f0ff3
                _log.info('An error occured: %s', err)
Pierre-Yves Chibon 2f0ff3
Pierre-Yves Chibon dd38bc
        else:
Pierre-Yves Chibon 4635b5
            _log.warning('Un-supported CI type')
Pierre-Yves Chibon dd38bc
Pierre-Yves Chibon be8146
        session.close()
Pierre-Yves Chibon 4635b5
        _log.info('Ready for another')
Pierre-Yves Chibon bb02dc
Pierre-Yves Chibon dd38bc
Pierre-Yves Chibon dd38bc
def main():
Pierre-Yves Chibon d31d7e
    ''' Start the main async loop. '''
Pierre-Yves Chibon d31d7e
Pierre-Yves Chibon dd38bc
    try:
Pierre-Yves Chibon dd38bc
        loop = trollius.get_event_loop()
Pierre-Yves Chibon dd38bc
        tasks = [
Pierre-Yves Chibon dd38bc
            trollius.async(handle_messages()),
Pierre-Yves Chibon dd38bc
        ]
Pierre-Yves Chibon dd38bc
        loop.run_until_complete(trollius.wait(tasks))
Pierre-Yves Chibon dd38bc
        loop.run_forever()
Pierre-Yves Chibon dd38bc
    except KeyboardInterrupt:
Pierre-Yves Chibon dd38bc
        pass
Pierre-Yves Chibon dd38bc
    except trollius.ConnectionResetError:
Pierre-Yves Chibon dd38bc
        pass
Pierre-Yves Chibon dd38bc
Pierre-Yves Chibon 4635b5
    _log.info("End Connection")
Pierre-Yves Chibon dd38bc
    loop.close()
Pierre-Yves Chibon 4635b5
    _log.info("End")
Pierre-Yves Chibon dd38bc
Pierre-Yves Chibon dd38bc
Pierre-Yves Chibon dd38bc
if __name__ == '__main__':
Pierre-Yves Chibon dd38bc
    formatter = logging.Formatter(
Pierre-Yves Chibon dd38bc
        "%(asctime)s %(levelname)s [%(module)s:%(lineno)d] %(message)s")
Pierre-Yves Chibon dd38bc
Pierre-Yves Chibon fe4b65
    logging.basicConfig(level=logging.DEBUG)
Pierre-Yves Chibon fe4b65
Pierre-Yves Chibon dd38bc
    # setup console logging
Pierre-Yves Chibon 4635b5
    _log.setLevel(logging.DEBUG)
Pierre-Yves Chibon d31d7e
    shellhandler = logging.StreamHandler()
Pierre-Yves Chibon d31d7e
    shellhandler.setLevel(logging.DEBUG)
Pierre-Yves Chibon dd38bc
Pierre-Yves Chibon dd38bc
    aslog = logging.getLogger("asyncio")
Pierre-Yves Chibon dd38bc
    aslog.setLevel(logging.DEBUG)
Pierre-Yves Chibon 6c8123
    aslog = logging.getLogger("trollius")
Pierre-Yves Chibon 6c8123
    aslog.setLevel(logging.DEBUG)
Pierre-Yves Chibon dd38bc
Pierre-Yves Chibon d31d7e
    shellhandler.setFormatter(formatter)
Pierre-Yves Chibon 4635b5
    _log.addHandler(shellhandler)
Pierre-Yves Chibon dd38bc
    main()