starter-kit/bots/run-queue
2019-08-22 16:05:13 +00:00

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())