129 lines
5 KiB
Python
Executable file
129 lines
5 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
|
|
# This file is part of Cockpit.
|
|
#
|
|
# Copyright (C) 2018 Red Hat, Inc.
|
|
#
|
|
# Cockpit is free software; you can redistribute it and/or modify it
|
|
# under the terms of the GNU Lesser General Public License as published by
|
|
# the Free Software Foundation; either version 2.1 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# Cockpit is distributed in the hope that it will be useful, but
|
|
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
# Lesser General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Lesser General Public License
|
|
# along with Cockpit; If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
MAX_PRIORITY = 9
|
|
|
|
import argparse
|
|
import json
|
|
import random
|
|
import subprocess
|
|
import sys
|
|
import time
|
|
import logging
|
|
import pipes
|
|
|
|
from task import redhat_network, distributed_queue
|
|
|
|
logging.basicConfig(level=logging.INFO)
|
|
|
|
# Returns a command to execute and the delivery tag needed to ack the message
|
|
def consume_webhook_queue(channel, amqp):
|
|
# interpret payload
|
|
# call tests-scan or issue-scan appropriately
|
|
method_frame, header_frame, body = channel.basic_get(queue='webhook')
|
|
if not method_frame:
|
|
return None, None
|
|
|
|
body = json.loads(body)
|
|
event = body['event']
|
|
request = body['request']
|
|
repo = None
|
|
cmd = None
|
|
if event == 'pull_request':
|
|
pull_request = request['pull_request']
|
|
action = request['action']
|
|
# scan for body changes (edited) and the bots label
|
|
if action == 'labeled' or (action == 'edited' and 'body' in request.get('changes', {})):
|
|
cmd = ['bots/issue-scan', '--issues-data', json.dumps(request), '--amqp', amqp]
|
|
if request['action'] in ['opened', 'synchronize']:
|
|
repo = pull_request['base']['repo']['full_name']
|
|
cmd = ['bots/tests-scan', '--pull-data', json.dumps(request), '--amqp', amqp]
|
|
elif event == 'status':
|
|
repo = request['repository']['full_name']
|
|
sha = request['sha']
|
|
context = pipes.quote(request['context'])
|
|
cmd = ['bots/tests-scan', '--sha', sha, '--amqp', amqp, '--context', context]
|
|
elif event == 'issues':
|
|
action = request['action']
|
|
# scan for opened, body changes (edited), and the bots label
|
|
if action in ['opened', 'labeled'] or (action == 'edited' and 'body' in request.get('changes', {})):
|
|
cmd = ['bots/issue-scan', '--issues-data', json.dumps(request), '--amqp', amqp]
|
|
else:
|
|
logging.error('Unkown event type in the webhook queue')
|
|
return None, None
|
|
|
|
# HACK: the cockpit repo itself is treated as a special case in tests-scan, in
|
|
# order to avoid complexity avoid supplying the repo option for now
|
|
if repo and repo != 'cockpit-project/cockpit':
|
|
cmd += ['--repo', repo]
|
|
return cmd, method_frame.delivery_tag
|
|
|
|
# Returns a command to execute and the delivery tag needed to ack the message
|
|
def consume_task_queue(channel, amqp, declare_public_result, declare_rhel_result):
|
|
queue='public'
|
|
if redhat_network():
|
|
# Try the rhel queue if the public queue is empty
|
|
if declare_public_result.method.message_count == 0:
|
|
queue = 'rhel'
|
|
# If both are non-empty, shuffle
|
|
elif declare_rhel_result.method.message_count > 0:
|
|
queue = ['public', 'rhel'][random.randrange(2)]
|
|
|
|
method_frame, header_frame, body = channel.basic_get(queue=queue)
|
|
if not method_frame:
|
|
return None, None
|
|
|
|
body = json.loads(body)
|
|
return body['command'], method_frame.delivery_tag
|
|
|
|
# Consume from the webhook queue and republish to the task queue via *-scan
|
|
# Consume from the task queue (endpoint)
|
|
def main():
|
|
parser = argparse.ArgumentParser(description='Bot: read a single test command from the queue and execute it')
|
|
parser.add_argument('--amqp', default='localhost:5671',
|
|
help='The host:port of the AMQP server to consume from (default: %(default)s)')
|
|
opts = parser.parse_args()
|
|
|
|
with distributed_queue.DistributedQueue(opts.amqp, ['webhook', 'rhel', 'public']) as q:
|
|
channel = q.channel
|
|
|
|
cmd, delivery_tag = consume_webhook_queue(channel, opts.amqp)
|
|
if not cmd and delivery_tag:
|
|
logging.info("Webhook message interpretation resulted in nop")
|
|
channel.basic_ack(delivery_tag)
|
|
return 0
|
|
|
|
if not cmd:
|
|
cmd, delivery_tag = consume_task_queue(channel, opts.amqp, q.declare_results['public'], q.declare_results['rhel'])
|
|
if not cmd:
|
|
logging.info("All queues are empty")
|
|
return 1
|
|
|
|
logging.info("Consuming message with command: {0}\n".format(' '.join(cmd) if type(cmd) is list else cmd))
|
|
p = subprocess.Popen(cmd, shell=type(cmd) is str)
|
|
while p.poll() is None:
|
|
q.connection.process_data_events()
|
|
time.sleep(3)
|
|
|
|
channel.basic_ack(delivery_tag)
|
|
|
|
return 0
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main())
|