mozreviewpulse: add support for analysis results with text only (
Bug 1287537). r?glob
The pulse consumer can now post reviews to Review Board using the
provided 'header' text. Once the message format has been finalized
support for diff comments will be added.
MozReview-Commit-ID: 8Tgu1DCe9Gh
--- a/pylib/mozreviewpulse/mozreviewpulse/__main__.py
+++ b/pylib/mozreviewpulse/mozreviewpulse/__main__.py
@@ -36,15 +36,17 @@ def main():
pulse_port=config.get('pulse', 'port'),
pulse_userid=config.get('pulse', 'userid'),
pulse_password=config.get('pulse', 'password'),
pulse_ssl=config.get('pulse', 'ssl'),
pulse_timeout=float(config.get('pulse', 'timeout')),
analysis_exchange=config.get('analysis', 'exchange'),
analysis_queue=config.get('analysis', 'queue'),
analysis_routing_key=config.get('analysis', 'routing_key'),
+ rb_url=config.get('reviewboard', 'url'),
+ bugzilla_user=config.get('bugzilla', 'user'),
+ bugzilla_apikey=config.get('bugzill', 'apikey')
)
consumer.consume(n=(None if args.loop else 1))
-
if __name__ == '__main__':
main()
new file mode 100644
--- /dev/null
+++ b/pylib/mozreviewpulse/mozreviewpulse/analysis.py
@@ -0,0 +1,32 @@
+from mozreviewpulse import BatchReview
+
+
+def process_analysis_results(consumer, body, message):
+ """Callback for analysis result messages."""
+ try:
+ payload = body['payload']
+ rrid = payload['review_request_id']
+ diff_revision = payload['diffset_revision']
+
+ rbc = consumer.get_rb_client()
+ rb_root = rbc.get_root()
+ # TODO: Add support for not acking when RB can't be
+ # reached. If that's the case we will automatically
+ # retry the message when we pull from the queue again.
+ # We'll need to add some sort of delay though so we
+ # don't rapidly re-attempt.
+
+ review = BatchReview(rb_root, rrid, diff_revision)
+ # TODO: Add support for diff comments after the message
+ # format is finalized.
+
+ header = payload['header']
+ review.publish(body_top=header)
+ except:
+ # This prevents the queue from growing indefinitely but
+ # prevents us from fixing whatever caused the exception
+ # and restarting the bot to handle the message.
+ message.ack()
+ raise
+
+ message.ack()
--- a/pylib/mozreviewpulse/mozreviewpulse/consumer.py
+++ b/pylib/mozreviewpulse/mozreviewpulse/consumer.py
@@ -1,54 +1,66 @@
+from functools import partial
import logging
import sys
from kombu import (
Connection,
Consumer,
Exchange,
Queue,
)
from kombu.common import eventloop
from kombu.utils import nested
+from mozreviewpulse.analysis import process_analysis_results
+from mozreviewpulse.rb import get_rb_client
+
class PulseConsumer(object):
"""Handle pulse consumption for MozReview."""
def __init__(self, pulse_host, pulse_port, pulse_userid, pulse_password,
pulse_ssl=False, pulse_timeout=1.0, logger=None,
analysis_exchange=None, analysis_queue=None,
- analysis_routing_key=None):
+ analysis_routing_key=None, rb_url=None, bugzilla_user=None,
+ bugzilla_apikey=None):
self.logger = logger or logging.getLogger('mozreviewpulse')
self.pulse_timeout = pulse_timeout
self.conn = Connection(hostname=pulse_host, port=pulse_port,
userid=pulse_userid, password=pulse_password,
ssl=pulse_ssl, auto_declare=False)
+
+ if not (rb_url and bugzilla_user and bugzilla_apikey):
+ self.get_rb_client = None
+ self.logger.warning('Insufficient configuration provided to '
+ 'communicate with Review Board')
+ else:
+ # Curry a method to make fetching an RBClient simple.
+ self.get_rb_client = partial(
+ get_rb_client, rb_url, user=bugzilla_user,
+ apikey=bugzilla_apikey)
+
self.queues = []
# Exchange and queue setup for analysis results.
- if not (analysis_exchange and analysis_queue and analysis_routing_key):
+ if not (analysis_exchange and analysis_queue and
+ analysis_routing_key and self.get_rb_client):
self.logger.warning('Insufficient configuration provided to '
'consume analysis messages')
else:
self.analysis_exchange = Exchange(
analysis_exchange, type='topic', durable=True, passive=True)
# self.analysis_exchange.passive = True
self.analysis_queue = Queue(
name=analysis_queue, exchange=self.analysis_exchange,
durable=True, routing_key=analysis_routing_key,
exclusive=False, auto_delete=False)
- self.queues.append((self.analysis_queue, self.on_analysis))
-
- def on_analysis(self, body, message):
- """Callback for analysis result messages."""
- # TODO: Post results of analysis as a review.
- print "Consuming Analysis"
- print body
+ self.queues.append((self.analysis_queue,
+ partial(process_analysis_results, self)))
def consume(self, n=None):
"""Consume pulse messages"""
if not self.queues:
self.logger.error('No queues configured for consumption.')
sys.exit(1)
with nested(*[Consumer(self.conn, queues=[q], callbacks=[callback])
new file mode 100644
--- /dev/null
+++ b/pylib/mozreviewpulse/mozreviewpulse/rb.py
@@ -0,0 +1,12 @@
+from rbtools.api.client import RBClient
+
+
+def get_rb_client(url, bugzilla_user=None, bugzilla_apikey=None):
+ """Return an authenticated RBClient instance."""
+ rbc = RBClient(url, save_cookies=False, allow_caching=False)
+ login_resource = rbc.get_path(
+ 'extensions/mozreview.extension.MozReviewExtension/'
+ 'bugzilla-api-key-logins/')
+ login_resource.create(username=bugzilla_user, api_key=bugzilla_apikey)
+
+ return rbc
--- a/pylib/mozreviewpulse/sample-config.ini
+++ b/pylib/mozreviewpulse/sample-config.ini
@@ -5,8 +5,15 @@ userid = public
password = public
ssl = True
timeout = 1.0
[analysis]
exchange = exchange/mozreview/
queue = queue/public/
routing_key = #
+
+[reviewboard]
+url = https://reviewboard.mozilla.org
+
+[bugzilla]
+username=mrpulse@example.com
+apikey=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX