blob: 1754ac0284e969b93e89df648d25e837dc13b48d [file] [log] [blame]
# Copyright 2017 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.
import logging
from google.appengine.api.modules import modules
from components import auth
from components import config as config_api
from components import decorators
from components import endpoints_webapp2
from components import prpc
import webapp2
from legacy import api as legacy_api
from legacy import api_common
from legacy import swarmbucket_api
import bq
import bulkproc
import config
import model
import notifications
import resultdb
import service
import swarming
import user
README_MD = (
'https://chromium.googlesource.com/infra/infra/+/HEAD/'
'appengine/cr-buildbucket/README.md'
)
class DummyStartHandler(auth.AuthenticatingHandler): # pragma: no cover
"""Dummy handler for /_ah/start.
Derived from AuthenticatingHandler to initialize auth db before the first
request.
"""
@auth.public
def get(self):
pass
class MainHandler(webapp2.RequestHandler): # pragma: no cover
"""Redirects to README.md."""
def get(self):
# `led` fetches the index page to see if Buildbucket is available. It
# follows HTTP-level redirects. We don't want it to test Gitiles available.
# Use Javascript-level redirect instead.
self.response.headers['Content-Type'] = 'text/html; charset=utf-8'
self.response.write(
"""
<p>Redirecting to the Buildbucket documentation...</p>
<script>window.location.replace(%r);</script>
""" % README_MD
)
class BuildRPCHandler(webapp2.RequestHandler): # pragma: no cover
"""Redirects to API explorer to see the build."""
def get(self, build_id):
api_path = '/_ah/api/buildbucket/v1/builds/%s' % build_id
return self.redirect(api_path)
class RedirectHandlerBase(auth.AuthenticatingHandler): # pragma: no cover
def _get_build(self, build_id):
try:
build_id = int(build_id)
except ValueError:
self.response.write('invalid build id')
self.abort(400)
build = model.Build.get_by_id(build_id)
can_view = build and (
user.has_perm(user.PERM_BUILDS_GET, build.bucket_id)
or user.has_perm(user.PERM_BUILDS_GET_LIMITED, build.bucket_id))
if not can_view:
if auth.get_current_identity().is_anonymous:
self.redirect(self.create_login_url(self.request.url), abort=True)
self.response.write('build %d not found' % build_id)
self.abort(404)
return build
class ViewBuildHandler(RedirectHandlerBase): # pragma: no cover
"""Redirects to Milo build page."""
@auth.public
def get(self, build_id):
build = self._get_build(build_id)
return self.redirect(str(api_common.get_build_url(build)))
class ViewLogHandler(RedirectHandlerBase): # pragma: no cover
"""Redirects to LogDog log content page."""
@staticmethod
def _find_log(build_proto, step_name, log_name):
for step in build_proto.steps:
if step.name == step_name:
for log in step.logs:
if log.name == log_name:
return log
break
return None
@auth.public
def get(self, build_id, step_name):
log_name = self.request.params.get('log') or 'stdout'
build = self._get_build(build_id)
bundle = model.BuildBundle.get(build, steps=True)
bundle.to_proto(build.proto, load_tags=False)
log = self._find_log(build.proto, step_name, log_name)
if not log or not log.view_url:
self.abort(
404, 'view url for log %r in step %r in build %r not found' %
(log_name, step_name, build_id)
)
return self.redirect(str(log.view_url))
class UnregisterBuilders(webapp2.RequestHandler): # pragma: no cover
"""Unregisters builders that didn't have builds for a long time."""
@decorators.require_cronjob
def get(self):
service.unregister_builders()
def get_frontend_routes(): # pragma: no cover
endpoints_services = [
legacy_api.BuildBucketApi,
config_api.ConfigApi,
swarmbucket_api.SwarmbucketApi,
]
routes = [
webapp2.Route(r'/_ah/start', DummyStartHandler),
webapp2.Route(r'/', MainHandler),
webapp2.Route(r'/b/<build_id:\d+>', BuildRPCHandler),
webapp2.Route(r'/build/<build_id:\d+>', ViewBuildHandler),
webapp2.Route(r'/builds/<build_id:\d+>', ViewBuildHandler),
webapp2.Route(r'/log/<build_id:\d+>/<step_name:.+>', ViewLogHandler),
]
routes.extend(endpoints_webapp2.api_routes(endpoints_services))
# /api routes should be removed once clients are hitting /_ah/api.
routes.extend(
endpoints_webapp2.api_routes(endpoints_services, base_path='/api')
)
prpc_server = prpc.Server()
prpc_server.add_interceptor(auth.prpc_interceptor)
routes += prpc_server.get_routes()
return routes
def get_backend_routes(): # pragma: no cover
prpc_server = prpc.Server()
prpc_server.add_interceptor(auth.prpc_interceptor)
return [ # pragma: no branch
webapp2.Route(r'/internal/cron/buildbucket/bq-export',
bq.CronExportBuilds),
webapp2.Route(r'/internal/cron/buildbucket/unregister-builders',
UnregisterBuilders),
webapp2.Route(r'/internal/task/buildbucket/notify/<build_id:\d+>',
notifications.TaskPublishNotification),
webapp2.Route(r'/internal/task/bq/export/<build_id:\d+>',
bq.TaskExport),
webapp2.Route(r'/internal/task/resultdb/finalize/<build_id:\d+>',
resultdb.FinalizeInvocation),
] + (bulkproc.get_routes() + prpc_server.get_routes())