Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2373)

Unified Diff: dashboard/dashboard/post_bisect_results.py

Issue 1566013002: Add support for bisect bots to post results to dashboard. (Closed) Base URL: https://github.com/catapult-project/catapult.git@master
Patch Set: address comments Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: dashboard/dashboard/post_bisect_results.py
diff --git a/dashboard/dashboard/post_bisect_results.py b/dashboard/dashboard/post_bisect_results.py
new file mode 100644
index 0000000000000000000000000000000000000000..48207d6632a719b704353ac6f3c1ee7a938e6c81
--- /dev/null
+++ b/dashboard/dashboard/post_bisect_results.py
@@ -0,0 +1,137 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""URL endpoint to allow bisect bots to post results to the dashboard."""
+
+import json
+import logging
+
+from google.appengine.api import app_identity
+from google.appengine.ext import ndb
+
+from dashboard import bisect_report
+from dashboard import datastore_hooks
+from dashboard import post_data_handler
+from dashboard import quick_logger
+from dashboard import rietveld_service
+from dashboard import update_bug_with_results
+from dashboard.models import try_job
+
+_EXPECTED_RESULT_PROPERTIES = {
+ 'try_job_id': int,
+ 'status': ['completed', 'failed', 'pending', 'aborted'],
+}
+
+
+class BadRequestError(Exception):
+ """An error indicating that a 400 response status should be returned."""
+ pass
+
+
+class PostBisectResultsHandler(post_data_handler.PostDataHandler):
+
+ def post(self):
+ """Validates data parameter and saves to TryJob entity.
+
+ Bisect results come from a "data" parameter, which is a JSON encoding of a
+ dictionary.
+
+ The required fields are "master", "bot", "test".
+
+ Request parameters:
+ data: JSON encoding of a dictionary.
+
+ Outputs:
+ Empty 200 response with if successful,
+ 200 response with warning message if optional data is invalid,
+ 403 response with error message if sender IP is not white-listed,
+ 400 response with error message if required data is invalid.
+ 500 with error message otherwise.
+ """
+ datastore_hooks.SetPrivilegedRequest()
+ if not self._CheckIpAgainstWhitelist():
+ return
+
+ data = self.request.get('data')
+ if not data:
+ self.ReportError('Missing "data" parameter.', status=400)
+ return
+
+ try:
+ data = json.loads(self.request.get('data'))
+ except ValueError:
+ self.ReportError('Invalid JSON string.', status=400)
+ return
+
+ logging.info('Received data: %s', data)
+
+ try:
+ _ValidateResultsData(data)
+ job = _UpdateTryJob(data)
+ update_bug_with_results.UpdateQuickLog(job)
+ except BadRequestError as error:
+ self.ReportError(error.message, status=400)
+
+
+def _ValidateResultsData(results_data):
+ _Validate(_EXPECTED_RESULT_PROPERTIES, results_data)
+ # TODO(chrisphan): Validate other values.
+
+
+def _Validate(expected, actual):
+ """Generic validator for expected keys, values, and types.
+
+ See post_bisect_results_test.py for examples.
+
+ Args:
+ expected: Either a list of expected values or a dictionary of expected
+ keys and type. A dictionary can contain a list of expected values.
+ actual: A value.
+ """
+ if not expected:
+ return
+ expected_type = type(expected)
+ actual_type = type(actual)
+ if expected_type is list:
+ if actual not in expected:
+ raise BadRequestError('Invalid value. Expected one of the following '
+ '%s. Actual %s.' % (','.join(expected), actual))
+ elif expected_type is dict:
+ if actual_type is not dict:
+ raise BadRequestError('Invalid type. Expected %s, actual %s.'
+ % (expected_type, actual_type))
+ missing = set(expected.keys()) - set(actual.keys())
+ if missing:
+ raise BadRequestError('Missing the following properties: %s'
+ % ','.join(missing))
+ for key in expected:
+ _Validate(expected[key], actual[key])
+ elif type(expected) is type and actual_type is not expected:
+ raise BadRequestError('Invalid type. Expected %s, actual %s.' %
+ (expected, actual_type))
+
+
+def _UpdateTryJob(results_data):
+ try_job_id = results_data.get('try_job_id')
+ job = ndb.Key(try_job.TryJob, try_job_id).get()
+ if not job.results_data:
+ job.results_data = {}
+ job.results_data.update(results_data)
+ job.results_data['issue_url'] = (job.results_data.get('issue_url') or
+ _IssueURL(job))
+ job.put()
+ return job
+
+
+def _IssueURL(job):
+ """Returns a URL for information about a bisect try job."""
+ if job.use_buildbucket:
+ hostname = app_identity.get_default_version_hostname()
+ job_id = job.buildbucket_job_id
+ return 'https://%s/buildbucket_job_status/%s' % (hostname, job_id)
+ else:
+ config = rietveld_service.GetDefaultRietveldConfig()
+ host = (config.internal_server_url if job.internal_only else
+ config.server_url)
+ return '%s/%d' % (host, job.rietveld_issue_id)

Powered by Google App Engine
This is Rietveld 408576698