Commit 6ebaf782 authored by sheyang@google.com's avatar sheyang@google.com

git cl try uses buildbucket via OAuth2

TEST=local run on Linux desktop and Macbook

NOPRESUBMIT=true(due to https://code.google.com/p/chromium/issues/detail?id=487172)

BUG=461614
R=maruel@chromium.org, nodir@chromium.org, vadimsh@chromium.org

Review URL: https://codereview.chromium.org/1063103002

git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/depot_tools@295236 0039d316-1c4b-4281-b951-d872f2087c98
parent eda2c9b5
...@@ -11,6 +11,7 @@ from distutils.version import LooseVersion ...@@ -11,6 +11,7 @@ from distutils.version import LooseVersion
from multiprocessing.pool import ThreadPool from multiprocessing.pool import ThreadPool
import base64 import base64
import glob import glob
import httplib
import json import json
import logging import logging
import optparse import optparse
...@@ -21,6 +22,8 @@ import stat ...@@ -21,6 +22,8 @@ import stat
import sys import sys
import tempfile import tempfile
import textwrap import textwrap
import time
import traceback
import urllib2 import urllib2
import urlparse import urlparse
import webbrowser import webbrowser
...@@ -31,8 +34,8 @@ try: ...@@ -31,8 +34,8 @@ try:
except ImportError: except ImportError:
pass pass
from third_party import colorama from third_party import colorama
from third_party import httplib2
from third_party import upload from third_party import upload
import auth import auth
import breakpad # pylint: disable=W0611 import breakpad # pylint: disable=W0611
...@@ -62,6 +65,9 @@ REFS_THAT_ALIAS_TO_OTHER_REFS = { ...@@ -62,6 +65,9 @@ REFS_THAT_ALIAS_TO_OTHER_REFS = {
'refs/remotes/origin/lkcr': 'refs/remotes/origin/master', 'refs/remotes/origin/lkcr': 'refs/remotes/origin/master',
} }
# Buildbucket-related constants
BUILDBUCKET_HOST = 'cr-buildbucket.appspot.com'
# Valid extensions for files we want to lint. # Valid extensions for files we want to lint.
DEFAULT_LINT_REGEX = r"(.*\.cpp|.*\.cc|.*\.h)" DEFAULT_LINT_REGEX = r"(.*\.cpp|.*\.cc|.*\.h)"
DEFAULT_LINT_IGNORE_REGEX = r"$^" DEFAULT_LINT_IGNORE_REGEX = r"$^"
...@@ -202,6 +208,115 @@ def add_git_similarity(parser): ...@@ -202,6 +208,115 @@ def add_git_similarity(parser):
parser.parse_args = Parse parser.parse_args = Parse
def _prefix_master(master):
"""Convert user-specified master name to full master name.
Buildbucket uses full master name(master.tryserver.chromium.linux) as bucket
name, while the developers always use shortened master name
(tryserver.chromium.linux) by stripping off the prefix 'master.'. This
function does the conversion for buildbucket migration.
"""
prefix = 'master.'
if master.startswith(prefix):
return master
return '%s%s' % (prefix, master)
def trigger_try_jobs(auth_config, changelist, options, masters, category):
rietveld_url = settings.GetDefaultServerUrl()
rietveld_host = urlparse.urlparse(rietveld_url).hostname
authenticator = auth.get_authenticator_for_host(rietveld_host, auth_config)
http = authenticator.authorize(httplib2.Http())
http.force_exception_to_status_code = True
issue_props = changelist.GetIssueProperties()
issue = changelist.GetIssue()
patchset = changelist.GetMostRecentPatchset()
buildbucket_put_url = (
'https://{hostname}/_ah/api/buildbucket/v1/builds/batch'.format(
hostname=BUILDBUCKET_HOST))
buildset = 'patch/rietveld/{hostname}/{issue}/{patch}'.format(
hostname=rietveld_host,
issue=issue,
patch=patchset)
batch_req_body = {'builds': []}
print_text = []
print_text.append('Tried jobs on:')
for master, builders_and_tests in sorted(masters.iteritems()):
print_text.append('Master: %s' % master)
bucket = _prefix_master(master)
for builder, tests in sorted(builders_and_tests.iteritems()):
print_text.append(' %s: %s' % (builder, tests))
parameters = {
'builder_name': builder,
'changes': [
{'author': {'email': issue_props['owner_email']}},
],
'properties': {
'category': category,
'issue': issue,
'master': master,
'patch_project': issue_props['project'],
'patch_storage': 'rietveld',
'patchset': patchset,
'reason': options.name,
'revision': options.revision,
'rietveld': rietveld_url,
'testfilter': tests,
},
}
if options.clobber:
parameters['properties']['clobber'] = True
batch_req_body['builds'].append(
{
'bucket': bucket,
'parameters_json': json.dumps(parameters),
'tags': ['builder:%s' % builder,
'buildset:%s' % buildset,
'master:%s' % master,
'user_agent:git_cl_try']
}
)
for try_count in xrange(3):
response, content = http.request(
buildbucket_put_url,
'PUT',
body=json.dumps(batch_req_body),
headers={'Content-Type': 'application/json'},
)
content_json = None
try:
content_json = json.loads(content)
except ValueError:
pass
# Buildbucket could return an error even if status==200.
if content_json and content_json.get('error'):
msg = 'Error in response. Code: %d. Reason: %s. Message: %s.' % (
content_json['error'].get('code', ''),
content_json['error'].get('reason', ''),
content_json['error'].get('message', ''))
raise BuildbucketResponseException(msg)
if response.status == 200:
if not content_json:
raise BuildbucketResponseException(
'Buildbucket returns invalid json content: %s.\n'
'Please file bugs at crbug.com, label "Infra-BuildBucket".' %
content)
break
if response.status < 500 or try_count >= 2:
raise httplib2.HttpLib2Error(content)
# status >= 500 means transient failures.
logging.debug('Transient errors when triggering tryjobs. Will retry.')
time.sleep(0.5 + 1.5*try_count)
print '\n'.join(print_text)
def MatchSvnGlob(url, base_url, glob_spec, allow_wildcards): def MatchSvnGlob(url, base_url, glob_spec, allow_wildcards):
"""Return the corresponding git ref if |base_url| together with |glob_spec| """Return the corresponding git ref if |base_url| together with |glob_spec|
matches the full |url|. matches the full |url|.
...@@ -269,6 +384,10 @@ def print_stats(similarity, find_copies, args): ...@@ -269,6 +384,10 @@ def print_stats(similarity, find_copies, args):
stdout=stdout, env=env) stdout=stdout, env=env)
class BuildbucketResponseException(Exception):
pass
class Settings(object): class Settings(object):
def __init__(self): def __init__(self):
self.default_server = None self.default_server = None
...@@ -2764,6 +2883,9 @@ def CMDtry(parser, args): ...@@ -2764,6 +2883,9 @@ def CMDtry(parser, args):
"server-side to define what default bot set to use") "server-side to define what default bot set to use")
group.add_option( group.add_option(
"-n", "--name", help="Try job name; default to current branch name") "-n", "--name", help="Try job name; default to current branch name")
group.add_option(
"--use-buildbucket", action="store_true", default=False,
help="Use buildbucket to trigger try jobs.")
parser.add_option_group(group) parser.add_option_group(group)
auth.add_auth_options(parser) auth.add_auth_options(parser)
options, args = parser.parse_args(args) options, args = parser.parse_args(args)
...@@ -2861,23 +2983,35 @@ def CMDtry(parser, args): ...@@ -2861,23 +2983,35 @@ def CMDtry(parser, args):
'\nWARNING Mismatch between local config and server. Did a previous ' '\nWARNING Mismatch between local config and server. Did a previous '
'upload fail?\ngit-cl try always uses latest patchset from rietveld. ' 'upload fail?\ngit-cl try always uses latest patchset from rietveld. '
'Continuing using\npatchset %s.\n' % patchset) 'Continuing using\npatchset %s.\n' % patchset)
try: if options.use_buildbucket:
cl.RpcServer().trigger_distributed_try_jobs( try:
cl.GetIssue(), patchset, options.name, options.clobber, trigger_try_jobs(auth_config, cl, options, masters, 'git_cl_try')
options.revision, masters) except BuildbucketResponseException as ex:
except urllib2.HTTPError, e: print 'ERROR: %s' % ex
if e.code == 404: return 1
print('404 from rietveld; ' except Exception as e:
'did you mean to use "git try" instead of "git cl try"?') stacktrace = (''.join(traceback.format_stack()) + traceback.format_exc())
print 'ERROR: Exception when trying to trigger tryjobs: %s\n%s' % (
e, stacktrace)
return 1 return 1
print('Tried jobs on:') else:
try:
for (master, builders) in masters.iteritems(): cl.RpcServer().trigger_distributed_try_jobs(
if master: cl.GetIssue(), patchset, options.name, options.clobber,
print 'Master: %s' % master options.revision, masters)
length = max(len(builder) for builder in builders) except urllib2.HTTPError as e:
for builder in sorted(builders): if e.code == 404:
print ' %*s: %s' % (length, builder, ','.join(builders[builder])) print('404 from rietveld; '
'did you mean to use "git try" instead of "git cl try"?')
return 1
print('Tried jobs on:')
for (master, builders) in sorted(masters.iteritems()):
if master:
print 'Master: %s' % master
length = max(len(builder) for builder in builders)
for builder in sorted(builders):
print ' %*s: %s' % (length, builder, ','.join(builders[builder]))
return 0 return 0
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment