|
Pierre-Yves Chibon |
12f5e6 |
#!/usr/bin/env python
|
|
Pierre-Yves Chibon |
12f5e6 |
|
|
Pierre-Yves Chibon |
12f5e6 |
"""
|
|
Pierre-Yves Chibon |
12f5e6 |
(c) 2015 - Copyright Red Hat Inc
|
|
Pierre-Yves Chibon |
12f5e6 |
|
|
Pierre-Yves Chibon |
12f5e6 |
Authors:
|
|
Pierre-Yves Chibon |
12f5e6 |
Pierre-Yves Chibon <pingou@pingoured.fr></pingou@pingoured.fr>
|
|
Pierre-Yves Chibon |
12f5e6 |
|
|
Pierre-Yves Chibon |
12f5e6 |
|
|
Pierre-Yves Chibon |
12f5e6 |
This server listens to message sent via redis and send the corresponding
|
|
Pierre-Yves Chibon |
12f5e6 |
web-hook request.
|
|
Pierre-Yves Chibon |
12f5e6 |
|
|
Pierre-Yves Chibon |
12f5e6 |
Using this mechanism, we no longer block the main application if the
|
|
Pierre-Yves Chibon |
12f5e6 |
receiving end is offline or so.
|
|
Pierre-Yves Chibon |
12f5e6 |
|
|
Pierre-Yves Chibon |
12f5e6 |
"""
|
|
Pierre-Yves Chibon |
12f5e6 |
|
|
Pierre-Yves Chibon |
12f5e6 |
import datetime
|
|
Pierre-Yves Chibon |
12f5e6 |
import hashlib
|
|
Pierre-Yves Chibon |
12f5e6 |
import hmac
|
|
Pierre-Yves Chibon |
12f5e6 |
import json
|
|
Pierre-Yves Chibon |
12f5e6 |
import logging
|
|
Pierre-Yves Chibon |
12f5e6 |
import os
|
|
Pierre-Yves Chibon |
12f5e6 |
import requests
|
|
Pierre-Yves Chibon |
12f5e6 |
import time
|
|
Pierre-Yves Chibon |
12f5e6 |
import uuid
|
|
Pierre-Yves Chibon |
12f5e6 |
|
|
Pierre-Yves Chibon |
12f5e6 |
import six
|
|
Pierre-Yves Chibon |
12f5e6 |
import trollius
|
|
Pierre-Yves Chibon |
12f5e6 |
import trollius_redis
|
|
Pierre-Yves Chibon |
12f5e6 |
|
|
Pierre-Yves Chibon |
12f5e6 |
from kitchen.text.converters import to_bytes
|
|
Pierre-Yves Chibon |
12f5e6 |
|
|
Pierre-Yves Chibon |
12f5e6 |
|
|
Pierre-Yves Chibon |
12f5e6 |
log = logging.getLogger(__name__)
|
|
Pierre-Yves Chibon |
12f5e6 |
|
|
Pierre-Yves Chibon |
12f5e6 |
|
|
Pierre-Yves Chibon |
12f5e6 |
if 'PAGURE_CONFIG' not in os.environ \
|
|
Pierre-Yves Chibon |
12f5e6 |
and os.path.exists('/etc/pagure/pagure.cfg'):
|
|
Pierre-Yves Chibon |
12f5e6 |
print 'Using configuration file `/etc/pagure/pagure.cfg`'
|
|
Pierre-Yves Chibon |
12f5e6 |
os.environ['PAGURE_CONFIG'] = '/etc/pagure/pagure.cfg'
|
|
Pierre-Yves Chibon |
12f5e6 |
|
|
Pierre-Yves Chibon |
12f5e6 |
|
|
Pierre-Yves Chibon |
12f5e6 |
import pagure
|
|
Pierre-Yves Chibon |
12f5e6 |
import pagure.lib
|
|
Pierre-Yves Chibon |
12f5e6 |
from pagure.exceptions import PagureEvException
|
|
Pierre-Yves Chibon |
12f5e6 |
|
|
Pierre-Yves Chibon |
12f5e6 |
_i = 0
|
|
Pierre-Yves Chibon |
12f5e6 |
|
|
Pierre-Yves Chibon |
12f5e6 |
|
|
Pierre-Yves Chibon |
d38a61 |
def call_web_hooks(project, topic, msg, urls):
|
|
Pierre-Yves Chibon |
12f5e6 |
''' Sends the web-hook notification. '''
|
|
Pierre-Yves Chibon |
fc2b26 |
log.info(
|
|
Pierre-Yves Chibon |
fc2b26 |
"Processing project: %s - topic: %s", project.fullname, topic)
|
|
Pierre-Yves Chibon |
fc2b26 |
log.debug('msg: %s', msg)
|
|
Pierre-Yves Chibon |
12f5e6 |
|
|
Pierre-Yves Chibon |
12f5e6 |
# Send web-hooks notification
|
|
Pierre-Yves Chibon |
12f5e6 |
global _i
|
|
Pierre-Yves Chibon |
12f5e6 |
_i += 1
|
|
Pierre-Yves Chibon |
12f5e6 |
year = datetime.datetime.now().year
|
|
Pierre-Yves Chibon |
12f5e6 |
if isinstance(topic, six.text_type):
|
|
Pierre-Yves Chibon |
12f5e6 |
topic = to_bytes(topic, encoding='utf8', nonstring="passthru")
|
|
Pierre-Yves Chibon |
d28610 |
msg['pagure_instance'] = pagure.APP.config['APP_URL']
|
|
Pierre-Yves Chibon |
d28610 |
msg['project_fullname'] = project.fullname
|
|
Pierre-Yves Chibon |
12f5e6 |
msg = dict(
|
|
Pierre-Yves Chibon |
12f5e6 |
topic=topic.decode('utf-8'),
|
|
Pierre-Yves Chibon |
12f5e6 |
msg=msg,
|
|
Pierre-Yves Chibon |
12f5e6 |
timestamp=int(time.time()),
|
|
Pierre-Yves Chibon |
12f5e6 |
msg_id=str(year) + '-' + str(uuid.uuid4()),
|
|
Pierre-Yves Chibon |
12f5e6 |
i=_i,
|
|
Pierre-Yves Chibon |
12f5e6 |
)
|
|
Pierre-Yves Chibon |
12f5e6 |
|
|
Pierre-Yves Chibon |
12f5e6 |
content = json.dumps(msg)
|
|
Pierre-Yves Chibon |
12f5e6 |
hashhex = hmac.new(
|
|
Pierre-Yves Chibon |
12f5e6 |
str(project.hook_token), content, hashlib.sha1).hexdigest()
|
|
Patrick Uiterwijk |
e99b9a |
hashhex256 = hmac.new(
|
|
Patrick Uiterwijk |
e99b9a |
str(project.hook_token), content, hashlib.sha256).hexdigest()
|
|
Pierre-Yves Chibon |
12f5e6 |
headers = {
|
|
Pierre-Yves Chibon |
d28610 |
'X-Pagure': pagure.APP.config['APP_URL'],
|
|
Pierre-Yves Chibon |
d28610 |
'X-Pagure-project': project.fullname,
|
|
Patrick Uiterwijk |
e99b9a |
'X-Pagure-Signature': hashhex,
|
|
Pierre-Yves Chibon |
d28610 |
'X-Pagure-Signature-256': hashhex256,
|
|
Pierre-Yves Chibon |
d28610 |
'X-Pagure-Topic': topic,
|
|
Pierre-Yves Chibon |
39cc17 |
'Content-Type': 'application/json',
|
|
Pierre-Yves Chibon |
12f5e6 |
}
|
|
Pierre-Yves Chibon |
d38a61 |
for url in urls:
|
|
Pierre-Yves Chibon |
12f5e6 |
url = url.strip()
|
|
Pierre-Yves Chibon |
12f5e6 |
log.info('Calling url %s' % url)
|
|
Pierre-Yves Chibon |
12f5e6 |
try:
|
|
Pierre-Yves Chibon |
12f5e6 |
req = requests.post(
|
|
Pierre-Yves Chibon |
12f5e6 |
url,
|
|
Pierre-Yves Chibon |
12f5e6 |
headers=headers,
|
|
Pierre-Yves Chibon |
39cc17 |
data=content,
|
|
Pierre-Yves Chibon |
d38a61 |
timeout=60,
|
|
Pierre-Yves Chibon |
12f5e6 |
)
|
|
Pierre-Yves Chibon |
12f5e6 |
if not req:
|
|
Pierre-Yves Chibon |
9bd1d8 |
log.info(
|
|
Pierre-Yves Chibon |
12f5e6 |
'An error occured while querying: %s - '
|
|
Pierre-Yves Chibon |
12f5e6 |
'Error code: %s' % (url, req.status_code))
|
|
Pierre-Yves Chibon |
12f5e6 |
except (requests.exceptions.RequestException, Exception) as err:
|
|
Pierre-Yves Chibon |
9bd1d8 |
log.info(
|
|
Pierre-Yves Chibon |
12f5e6 |
'An error occured while querying: %s - Error: %s' % (
|
|
Pierre-Yves Chibon |
12f5e6 |
url, err))
|
|
Pierre-Yves Chibon |
12f5e6 |
|
|
Pierre-Yves Chibon |
12f5e6 |
|
|
Pierre-Yves Chibon |
12f5e6 |
@trollius.coroutine
|
|
Pierre-Yves Chibon |
af4c20 |
def handle_messages():
|
|
Patrick Uiterwijk |
b84a96 |
host = pagure.APP.config.get('REDIS_HOST', '0.0.0.0')
|
|
Patrick Uiterwijk |
b84a96 |
port = pagure.APP.config.get('REDIS_PORT', 6379)
|
|
Patrick Uiterwijk |
b84a96 |
dbname = pagure.APP.config.get('REDIS_DB', 0)
|
|
Patrick Uiterwijk |
b84a96 |
connection = yield trollius.From(trollius_redis.Connection.create(
|
|
Patrick Uiterwijk |
b84a96 |
host=host, port=port, db=dbname))
|
|
Patrick Uiterwijk |
b84a96 |
|
|
Patrick Uiterwijk |
b84a96 |
# Create subscriber.
|
|
Patrick Uiterwijk |
b84a96 |
subscriber = yield trollius.From(connection.start_subscribe())
|
|
Patrick Uiterwijk |
b84a96 |
|
|
Patrick Uiterwijk |
b84a96 |
# Subscribe to channel.
|
|
Patrick Uiterwijk |
b84a96 |
yield trollius.From(subscriber.subscribe(['pagure.hook']))
|
|
Patrick Uiterwijk |
b84a96 |
|
|
Patrick Uiterwijk |
b84a96 |
# Inside a while loop, wait for incoming events.
|
|
Patrick Uiterwijk |
b84a96 |
while True:
|
|
Patrick Uiterwijk |
b84a96 |
reply = yield trollius.From(subscriber.next_published())
|
|
Patrick Uiterwijk |
b84a96 |
log.info(
|
|
Patrick Uiterwijk |
b84a96 |
'Received: %s on channel: %s',
|
|
Patrick Uiterwijk |
b84a96 |
repr(reply.value), reply.channel)
|
|
Patrick Uiterwijk |
b84a96 |
data = json.loads(reply.value)
|
|
Patrick Uiterwijk |
b84a96 |
username = None
|
|
Patrick Uiterwijk |
b84a96 |
if data['project'].startswith('forks'):
|
|
Patrick Uiterwijk |
b84a96 |
username, projectname = data['project'].split('/', 2)[1:]
|
|
Patrick Uiterwijk |
b84a96 |
else:
|
|
Patrick Uiterwijk |
b84a96 |
projectname = data['project']
|
|
Patrick Uiterwijk |
b84a96 |
|
|
Patrick Uiterwijk |
b84a96 |
namespace = None
|
|
Patrick Uiterwijk |
b84a96 |
if '/' in projectname:
|
|
Patrick Uiterwijk |
b84a96 |
namespace, projectname = projectname.split('/', 1)
|
|
Patrick Uiterwijk |
b84a96 |
|
|
Patrick Uiterwijk |
b84a96 |
log.info(
|
|
Patrick Uiterwijk |
b84a96 |
'Searching %s/%s/%s' % (username, namespace, projectname))
|
|
Patrick Uiterwijk |
b84a96 |
session = pagure.lib.create_session(pagure.APP.config['DB_URL'])
|
|
Patrick Uiterwijk |
b84a96 |
project = pagure.lib._get_project(
|
|
Patrick Uiterwijk |
b84a96 |
session=session, name=projectname, user=username,
|
|
Patrick Uiterwijk |
b84a96 |
namespace=namespace)
|
|
Patrick Uiterwijk |
b84a96 |
if not project:
|
|
Patrick Uiterwijk |
b84a96 |
log.info('No project found with these criteria')
|
|
Pierre-Yves Chibon |
d1cbf9 |
session.close()
|
|
Patrick Uiterwijk |
b84a96 |
continue
|
|
Patrick Uiterwijk |
b84a96 |
urls = project.settings.get('Web-hooks')
|
|
Patrick Uiterwijk |
b84a96 |
session.close()
|
|
Patrick Uiterwijk |
b84a96 |
if not urls:
|
|
Patrick Uiterwijk |
b84a96 |
log.info('No URLs set: %s' % urls)
|
|
Patrick Uiterwijk |
b84a96 |
continue
|
|
Patrick Uiterwijk |
b84a96 |
urls = urls.split('\n')
|
|
Patrick Uiterwijk |
b84a96 |
log.info('Got the project, going to the webhooks')
|
|
Patrick Uiterwijk |
b84a96 |
call_web_hooks(project, data['topic'], data['msg'], urls)
|
|
Pierre-Yves Chibon |
12f5e6 |
|
|
Pierre-Yves Chibon |
12f5e6 |
|
|
Pierre-Yves Chibon |
12f5e6 |
def main():
|
|
Pierre-Yves Chibon |
12f5e6 |
server = None
|
|
Pierre-Yves Chibon |
12f5e6 |
try:
|
|
Pierre-Yves Chibon |
12f5e6 |
loop = trollius.get_event_loop()
|
|
Pierre-Yves Chibon |
12f5e6 |
tasks = [
|
|
Pierre-Yves Chibon |
af4c20 |
trollius.async(handle_messages()),
|
|
Pierre-Yves Chibon |
12f5e6 |
]
|
|
Pierre-Yves Chibon |
12f5e6 |
loop.run_until_complete(trollius.wait(tasks))
|
|
Pierre-Yves Chibon |
12f5e6 |
loop.run_forever()
|
|
Pierre-Yves Chibon |
12f5e6 |
except KeyboardInterrupt:
|
|
Pierre-Yves Chibon |
12f5e6 |
pass
|
|
Pierre-Yves Chibon |
12f5e6 |
except trollius.ConnectionResetError:
|
|
Pierre-Yves Chibon |
12f5e6 |
pass
|
|
Pierre-Yves Chibon |
12f5e6 |
|
|
Pierre-Yves Chibon |
12f5e6 |
log.info("End Connection")
|
|
Pierre-Yves Chibon |
12f5e6 |
loop.close()
|
|
Pierre-Yves Chibon |
12f5e6 |
log.info("End")
|
|
Pierre-Yves Chibon |
12f5e6 |
|
|
Pierre-Yves Chibon |
12f5e6 |
|
|
Pierre-Yves Chibon |
12f5e6 |
if __name__ == '__main__':
|
|
Pierre-Yves Chibon |
12f5e6 |
log = logging.getLogger("")
|
|
Pierre-Yves Chibon |
12f5e6 |
formatter = logging.Formatter(
|
|
Pierre-Yves Chibon |
12f5e6 |
"%(asctime)s %(levelname)s [%(module)s:%(lineno)d] %(message)s")
|
|
Pierre-Yves Chibon |
12f5e6 |
|
|
Pierre-Yves Chibon |
12f5e6 |
# setup console logging
|
|
Pierre-Yves Chibon |
12f5e6 |
log.setLevel(logging.DEBUG)
|
|
Pierre-Yves Chibon |
12f5e6 |
ch = logging.StreamHandler()
|
|
Pierre-Yves Chibon |
12f5e6 |
ch.setLevel(logging.DEBUG)
|
|
Pierre-Yves Chibon |
12f5e6 |
|
|
Pierre-Yves Chibon |
12f5e6 |
aslog = logging.getLogger("asyncio")
|
|
Pierre-Yves Chibon |
12f5e6 |
aslog.setLevel(logging.DEBUG)
|
|
Pierre-Yves Chibon |
12f5e6 |
|
|
Pierre-Yves Chibon |
12f5e6 |
ch.setFormatter(formatter)
|
|
Pierre-Yves Chibon |
12f5e6 |
log.addHandler(ch)
|
|
Pierre-Yves Chibon |
12f5e6 |
main()
|