blob: fe75dbdd7a6387635799183d8af58be8528cceeb [file] [log] [blame]
# 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_cq
from exonerator import gerrit_precq
from exonerator import innocent_cls
from exonerator import innocent_cls_precq
app = flask.Flask(__name__)
app.register_blueprint(debug_routes.app)
config = config.Config()
_CQ_BUILD_LIMIT = 1
_PRE_CQ_CL_LIMIT = 1
_APPENGINE_CRON_HEADER = 'X-Appengine-Cron'
PRE_CQ_TYPE = 'pre-cq'
CQ_TYPE = 'cq'
# 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/cq')
def ExonerateCQ():
"""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=_CQ_BUILD_LIMIT):
all_exonerated.extend(_ExonerateBatch(conn, batch, CQ_TYPE))
return flask.jsonify(exonerated=all_exonerated)
@app.route('/cron/exonerate/pre-cq')
def ExoneratePreCQ():
"""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_precq.NewInnocentCLs(conn, limit=_PRE_CQ_CL_LIMIT):
all_exonerated.extend(_ExonerateBatch(conn, batch, PRE_CQ_TYPE))
return flask.jsonify(exonerated=all_exonerated)
def _ExonerateBatch(conn, batch, exoneration_type):
"""Exonerates a batch of changes, then inserts CLActions.
Args:
conn: The CIDBConnection to use
batch: A List[exonerator.gerrit.ChangeWithBuild] of changes to exonerate.
exoneration_type: A string in ('cq', 'pre-cq')
"""
exonerate_f = {
'cq': gerrit_cq.MaybeExonerate,
'pre-cq': gerrit_precq.MaybeExonerate,
}[exoneration_type]
exonerated_flags = parallel.RunTasksInProcessPool(
exonerate_f,
[[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, exoneration_type)
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(_APPENGINE_CRON_HEADER) == 'true'
if __name__ == '__main__':
print('Run the app locally with '
'"gunicorn -c gunicorn.conf.py -b :8080 main:app"', file=sys.stderr)