| # Copyright 2017 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Routes for CL exonerator.""" |
| from __future__ import absolute_import |
| from __future__ import division |
| from __future__ import print_function |
| |
| import httplib |
| import sys |
| |
| from chromite.lib import cidb |
| from chromite.lib import parallel |
| from chromite.lib import cros_logging as logging |
| import flask |
| |
| import config |
| import debug_routes |
| from exonerator import cidblib |
| from exonerator import gerrit |
| from exonerator import innocent_cls |
| |
| app = flask.Flask(__name__) |
| app.register_blueprint(debug_routes.app) |
| config = config.Config() |
| |
| _BUILD_LIMIT = 1 |
| _APPENGINE_CRON_HEADER = 'X-Appengine-Cron' |
| |
| # This must be done at the top level because gunicorn and nginx does not run us |
| # as __main__ |
| logging.basicConfig(level=logging.DEBUG) |
| |
| |
| @app.route('/') |
| def Hello(): |
| """Placeholder route""" |
| return 'hello world' |
| |
| |
| # Ideally this would be a POST, but Appengine cron only makes GET requests. |
| @app.route('/cron/exonerate') |
| def Exonerate(): |
| """Exonerates innocent CLs which were kicked out.""" |
| if not _RequestIsFromAppEngineCron(): |
| flask.abort(httplib.UNAUTHORIZED) |
| |
| conn = cidb.CIDBConnection( |
| config.cidb_cred_dir, for_service=config.is_service) |
| all_exonerated = [] |
| for batch in innocent_cls.NewInnocentCLs(conn, limit=_BUILD_LIMIT): |
| all_exonerated.extend(_ExonerateBatch(conn, batch)) |
| |
| return flask.jsonify(exonerated=all_exonerated) |
| |
| |
| @app.route('/exonerate-all') |
| @debug_routes.auth.login_required |
| def ExonerateAll(): |
| """Manually kicks off an exoneration batch. Identical to /cron/exonerate.""" |
| conn = cidb.CIDBConnection( |
| config.cidb_cred_dir, for_service=config.is_service) |
| all_exonerated = [] |
| for batch in innocent_cls.NewInnocentCLs(conn, limit=_BUILD_LIMIT): |
| all_exonerated.extend(_ExonerateBatch(conn, batch)) |
| |
| return flask.jsonify(exonerated=all_exonerated) |
| |
| |
| def _ExonerateBatch(conn, batch): |
| """Exonerates a batch of changes, then inserts CLActions. |
| |
| Args: |
| conn: The CIDBConnection to use |
| batch: A List[exonerator.gerrit.ChangeWithBuild] of changes to exonerate. |
| """ |
| exonerated_flags = parallel.RunTasksInProcessPool( |
| gerrit.MaybeExonerate, |
| [[change, build_id] for change, build_id in batch]) |
| |
| exonerated_changes = [] |
| for change, exonerated in zip(batch, exonerated_flags): |
| if exonerated: |
| exonerated_changes.append(change) |
| |
| if exonerated_changes: |
| cidblib.InsertExoneratedCLActions(conn, exonerated_changes) |
| |
| return exonerated_changes |
| |
| |
| def _RequestIsFromAppEngineCron(): |
| """Returns whether the request came from Appengine's cron service. |
| |
| See https://cloud.google.com/appengine/docs/flexible/python/ |
| scheduling-jobs-with-cron-yaml#validating_cron_requests |
| for more details. |
| """ |
| return flask.request.headers.get('X-Appengine-Cron') == 'true' |
| |
| |
| if __name__ == '__main__': |
| print('Run the app locally with ' |
| '"gunicorn -c gunicorn.conf.py -b :8080 main:app"', file=sys.stderr) |