Commit 221ab25f authored by tandrii's avatar tandrii Committed by Commit bot

git cl try-results: refactor Rietveld + add tests.

R=emso@chromium.org,phajdan.jr@chromium.org
BUG=599931
TEST=new teste + local end-to-end

Review-Url: https://codereview.chromium.org/2395113002
parent 1838bade
...@@ -395,34 +395,42 @@ def trigger_try_jobs(auth_config, changelist, options, masters, category): ...@@ -395,34 +395,42 @@ def trigger_try_jobs(auth_config, changelist, options, masters, category):
print('\n'.join(print_text)) print('\n'.join(print_text))
def fetch_try_jobs(auth_config, changelist, options): def fetch_try_jobs(auth_config, changelist, buildbucket_host,
patchset=None):
"""Fetches try jobs from buildbucket. """Fetches try jobs from buildbucket.
Returns a map from build id to build info as a dictionary. Returns a map from build id to build info as a dictionary.
""" """
rietveld_url = settings.GetDefaultServerUrl() assert buildbucket_host
rietveld_host = urlparse.urlparse(rietveld_url).hostname assert changelist.GetIssue(), 'CL must be uploaded first'
authenticator = auth.get_authenticator_for_host(rietveld_host, auth_config) assert changelist.GetCodereviewServer(), 'CL must be uploaded first'
patchset = patchset or changelist.GetMostRecentPatchset()
assert patchset, 'CL must be uploaded first'
codereview_url = changelist.GetCodereviewServer()
codereview_host = urlparse.urlparse(codereview_url).hostname
authenticator = auth.get_authenticator_for_host(codereview_host, auth_config)
if authenticator.has_cached_credentials(): if authenticator.has_cached_credentials():
http = authenticator.authorize(httplib2.Http()) http = authenticator.authorize(httplib2.Http())
else: else:
print('Warning: Some results might be missing because %s' % print('Warning: Some results might be missing because %s' %
# Get the message on how to login. # Get the message on how to login.
(auth.LoginRequiredError(rietveld_host).message,)) (auth.LoginRequiredError(codereview_host).message,))
http = httplib2.Http() http = httplib2.Http()
http.force_exception_to_status_code = True http.force_exception_to_status_code = True
buildset = 'patch/rietveld/{hostname}/{issue}/{patch}'.format( buildset = 'patch/{codereview}/{hostname}/{issue}/{patch}'.format(
hostname=rietveld_host, codereview='gerrit' if changelist.IsGerrit() else 'rietveld',
hostname=codereview_host,
issue=changelist.GetIssue(), issue=changelist.GetIssue(),
patch=options.patchset) patch=patchset)
params = {'tag': 'buildset:%s' % buildset} params = {'tag': 'buildset:%s' % buildset}
builds = {} builds = {}
while True: while True:
url = 'https://{hostname}/_ah/api/buildbucket/v1/search?{params}'.format( url = 'https://{hostname}/_ah/api/buildbucket/v1/search?{params}'.format(
hostname=options.buildbucket_host, hostname=buildbucket_host,
params=urllib.urlencode(params)) params=urllib.urlencode(params))
content = _buildbucket_retry('fetching try jobs', http, url, 'GET') content = _buildbucket_retry('fetching try jobs', http, url, 'GET')
for build in content.get('builds', []): for build in content.get('builds', []):
...@@ -4838,23 +4846,25 @@ def CMDtry_results(parser, args): ...@@ -4838,23 +4846,25 @@ def CMDtry_results(parser, args):
if not cl.GetIssue(): if not cl.GetIssue():
parser.error('Need to upload first') parser.error('Need to upload first')
if not options.patchset: patchset = options.patchset
options.patchset = cl.GetMostRecentPatchset() if not patchset:
if options.patchset and options.patchset != cl.GetPatchset(): patchset = cl.GetMostRecentPatchset()
print( if not patchset:
'\nWARNING Mismatch between local config and server. Did a previous ' parser.error('Codereview doesn\'t know about issue %s. '
'upload fail?\ngit-cl try always uses latest patchset from rietveld. ' 'No access to issue or wrong issue number?\n'
'Continuing using\npatchset %s.\n' % options.patchset) 'Either upload first, or pass --patchset explicitely' %
cl.GetIssue())
if patchset != cl.GetPatchset():
print('WARNING: Mismatch between local config and server. Did a previous '
'upload fail?\n'
'By default, git cl try uses latest patchset from codereview.\n'
'Continuing using patchset %s.\n' % patchset)
try: try:
jobs = fetch_try_jobs(auth_config, cl, options) jobs = fetch_try_jobs(auth_config, cl, options.buildbucket_host, patchset)
except BuildbucketResponseException as ex: except BuildbucketResponseException as ex:
print('Buildbucket error: %s' % ex) print('Buildbucket error: %s' % ex)
return 1 return 1
except Exception as e:
stacktrace = (''.join(traceback.format_stack()) + traceback.format_exc())
print('ERROR: Exception when trying to fetch try jobs: %s\n%s' %
(e, stacktrace))
return 1
if options.json: if options.json:
write_try_results_json(options.json, jobs) write_try_results_json(options.json, jobs)
else: else:
......
...@@ -97,6 +97,8 @@ class AuthenticatorMock(object): ...@@ -97,6 +97,8 @@ class AuthenticatorMock(object):
pass pass
def has_cached_credentials(self): def has_cached_credentials(self):
return True return True
def authorize(self, http):
return http
def CookiesAuthenticatorMockFactory(hosts_with_creds=None, same_cookie=False): def CookiesAuthenticatorMockFactory(hosts_with_creds=None, same_cookie=False):
...@@ -1855,54 +1857,6 @@ class TestGitCl(TestCase): ...@@ -1855,54 +1857,6 @@ class TestGitCl(TestCase):
out.getvalue(), out.getvalue(),
'scheduled CQ Dry Run on https://codereview.chromium.org/123\n') 'scheduled CQ Dry Run on https://codereview.chromium.org/123\n')
def test_write_try_results_json(self):
builds = {
'9000': {
'id': '9000',
'status': 'STARTED',
'url': 'http://build.cr.org/p/x.y/builders/my-builder/builds/2',
'result_details_json': '{"properties": {}}',
'bucket': 'master.x.y',
'created_by': 'user:someone@chromium.org',
'created_ts': '147200002222000',
'parameters_json': '{"builder_name": "my-builder", "category": ""}',
},
'8000': {
'id': '8000',
'status': 'COMPLETED',
'result': 'FAILURE',
'failure_reason': 'BUILD_FAILURE',
'url': 'http://build.cr.org/p/x.y/builders/my-builder/builds/1',
'result_details_json': '{"properties": {}}',
'bucket': 'master.x.y',
'created_by': 'user:someone@chromium.org',
'created_ts': '147200001111000',
'parameters_json': '{"builder_name": "my-builder", "category": ""}',
},
}
expected_output = [
{
'buildbucket_id': '8000',
'bucket': 'master.x.y',
'builder_name': 'my-builder',
'status': 'COMPLETED',
'result': 'FAILURE',
'failure_reason': 'BUILD_FAILURE',
'url': 'http://build.cr.org/p/x.y/builders/my-builder/builds/1',
},
{
'buildbucket_id': '9000',
'bucket': 'master.x.y',
'builder_name': 'my-builder',
'status': 'STARTED',
'result': None,
'failure_reason': None,
'url': 'http://build.cr.org/p/x.y/builders/my-builder/builds/2',
}
]
self.calls = [(('write_json', 'output.json', expected_output), '')]
git_cl.write_try_results_json('output.json', builds)
def _common_GerritCommitMsgHookCheck(self): def _common_GerritCommitMsgHookCheck(self):
self.mock(git_cl.sys, 'stdout', StringIO.StringIO()) self.mock(git_cl.sys, 'stdout', StringIO.StringIO())
self.mock(git_cl.os.path, 'abspath', self.mock(git_cl.os.path, 'abspath',
...@@ -1967,6 +1921,89 @@ class TestGitCl(TestCase): ...@@ -1967,6 +1921,89 @@ class TestGitCl(TestCase):
self.assertEqual(0, cl.CMDLand(force=True, bypass_hooks=True, verbose=True)) self.assertEqual(0, cl.CMDLand(force=True, bypass_hooks=True, verbose=True))
self.assertRegexpMatches(out.getvalue(), 'Issue.*123 has been submitted') self.assertRegexpMatches(out.getvalue(), 'Issue.*123 has been submitted')
BUILDBUCKET_BUILDS_MAP = {
'9000': {
'id': '9000',
'status': 'STARTED',
'url': 'http://build.cr.org/p/x.y/builders/my-builder/builds/2',
'result_details_json': '{"properties": {}}',
'bucket': 'master.x.y',
'created_by': 'user:someone@chromium.org',
'created_ts': '147200002222000',
'parameters_json': '{"builder_name": "my-builder", "category": ""}',
},
'8000': {
'id': '8000',
'status': 'COMPLETED',
'result': 'FAILURE',
'failure_reason': 'BUILD_FAILURE',
'url': 'http://build.cr.org/p/x.y/builders/my-builder/builds/1',
'result_details_json': '{"properties": {}}',
'bucket': 'master.x.y',
'created_by': 'user:someone@chromium.org',
'created_ts': '147200001111000',
'parameters_json': '{"builder_name": "my-builder", "category": ""}',
},
}
def test_write_try_results_json(self):
expected_output = [
{
'buildbucket_id': '8000',
'bucket': 'master.x.y',
'builder_name': 'my-builder',
'status': 'COMPLETED',
'result': 'FAILURE',
'failure_reason': 'BUILD_FAILURE',
'url': 'http://build.cr.org/p/x.y/builders/my-builder/builds/1',
},
{
'buildbucket_id': '9000',
'bucket': 'master.x.y',
'builder_name': 'my-builder',
'status': 'STARTED',
'result': None,
'failure_reason': None,
'url': 'http://build.cr.org/p/x.y/builders/my-builder/builds/2',
}
]
self.calls = [(('write_json', 'output.json', expected_output), '')]
git_cl.write_try_results_json('output.json', self.BUILDBUCKET_BUILDS_MAP)
def _setup_fetch_try_jobs_rietveld(self, *request_results):
out = StringIO.StringIO()
self.mock(sys, 'stdout', out)
self.mock(git_cl.Changelist, 'GetMostRecentPatchset', lambda *args: 20001)
self.mock(git_cl.auth, 'get_authenticator_for_host', lambda host, _cfg:
self._mocked_call(['get_authenticator_for_host', host]))
self.mock(git_cl, '_buildbucket_retry', lambda *_, **__:
self._mocked_call(['_buildbucket_retry']))
self.calls += [
((['git', 'symbolic-ref', 'HEAD'],), 'feature'),
((['git', 'config', 'branch.feature.rietveldissue'],), '1'),
((['git', 'config', 'rietveld.autoupdate'],), CERR1),
((['git', 'config', 'rietveld.server'],), 'codereview.example.com'),
((['git', 'config', 'branch.feature.rietveldpatchset'],), '20001'),
((['git', 'config', 'branch.feature.rietveldserver'],),
'codereview.example.com'),
((['get_authenticator_for_host', 'codereview.example.com'],),
AuthenticatorMock()),
] + [((['_buildbucket_retry'],), r) for r in request_results]
def test_fetch_try_jobs_none_rietveld(self):
self._setup_fetch_try_jobs_rietveld({})
self.assertEqual(0, git_cl.main(['try-results']))
self.assertRegexpMatches(sys.stdout.getvalue(), 'No try jobs')
def test_fetch_try_jobs_some_rietveld(self):
self._setup_fetch_try_jobs_rietveld({
'builds': self.BUILDBUCKET_BUILDS_MAP.values(),
})
self.assertEqual(0, git_cl.main(['try-results']))
self.assertRegexpMatches(sys.stdout.getvalue(), 'Failures:')
self.assertRegexpMatches(sys.stdout.getvalue(), 'Started:')
self.assertRegexpMatches(sys.stdout.getvalue(), '2 try jobs')
if __name__ == '__main__': if __name__ == '__main__':
git_cl.logging.basicConfig( git_cl.logging.basicConfig(
......
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