Commit 382674be authored by Aaron Gable's avatar Aaron Gable Committed by Commit Bot

Revert "Refactor ReadHttpResponse to be error-friendlier"

This reverts commit 6d7ab1bf.

Reason for revert: Stacktrace:
 File "/s/depot_tools/gerrit_util.py", line 816, in GetAccountDetails
   return ReadHttpJsonResponse(conn)
 File "/s/depot_tools/gerrit_util.py", line 376, in ReadHttpJsonResponse
   fh = ReadHttpResponse(conn, accept_statuses)
 File "/s/depot_tools/gerrit_util.py", line 365, in ReadHttpResponse
   if response.status not in accept_statuses:
TypeError: argument of type 'NoneType' is not iterable

Original change's description:
> Refactor ReadHttpResponse to be error-friendlier
> 
> Gerrit sometimes returns a full response json object at
> the same time as returning a non-200 status code. This
> refactor makes it easier for calling code to request
> access to that object and handle error cases on its own.
> 
> Bug: 710028
> Change-Id: Id1017d580d2fb843d5ca6287efcfed8775c52cd6
> Reviewed-on: https://chromium-review.googlesource.com/479450
> Commit-Queue: Aaron Gable <agable@chromium.org>
> Reviewed-by: Andrii Shyshkalov <tandrii@chromium.org>
> 

TBR=agable@chromium.org,tandrii@chromium.org,chromium-reviews@chromium.org
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true

Change-Id: Ia9d9ce835e207a32e7cc8ee35c0cf40c823c7b78
Reviewed-on: https://chromium-review.googlesource.com/481059Reviewed-by: 's avatarAaron Gable <agable@chromium.org>
Commit-Queue: Aaron Gable <agable@chromium.org>
parent 6c98dc69
...@@ -323,15 +323,18 @@ def CreateHttpConn(host, path, reqtype='GET', headers=None, body=None): ...@@ -323,15 +323,18 @@ def CreateHttpConn(host, path, reqtype='GET', headers=None, body=None):
return conn return conn
def ReadHttpResponse(conn, accept_statuses=frozenset([200, 404])): def ReadHttpResponse(conn, expect_status=200, ignore_404=True):
"""Reads an http response from a connection into a string buffer. """Reads an http response from a connection into a string buffer.
Args: Args:
conn: An Http object created by CreateHttpConn above. conn: An Http object created by CreateHttpConn above.
accept_statuses: Treat any of these statuses as success. Default: [200, 404] expect_status: Success is indicated by this status in the response.
Common additions include 204 and 400. ignore_404: For many requests, gerrit-on-borg will return 404 if the request
doesn't match the database contents. In most such cases, we
want the API to return None rather than raise an Exception.
Returns: A string buffer containing the connection's reply. Returns: A string buffer containing the connection's reply.
""" """
sleep_time = 0.5 sleep_time = 0.5
for idx in range(TRY_LIMIT): for idx in range(TRY_LIMIT):
response, contents = conn.request(**conn.req_params) response, contents = conn.request(**conn.req_params)
...@@ -342,7 +345,7 @@ def ReadHttpResponse(conn, accept_statuses=frozenset([200, 404])): ...@@ -342,7 +345,7 @@ def ReadHttpResponse(conn, accept_statuses=frozenset([200, 404])):
www_authenticate): www_authenticate):
auth_match = re.search('realm="([^"]+)"', www_authenticate, re.I) auth_match = re.search('realm="([^"]+)"', www_authenticate, re.I)
host = auth_match.group(1) if auth_match else conn.req_host host = auth_match.group(1) if auth_match else conn.req_host
reason = ('Authentication failed. Please make sure your .gitcookies file ' reason = ('Authentication failed. Please make sure your .netrc file '
'has credentials for %s' % host) 'has credentials for %s' % host)
raise GerritAuthenticationError(response.status, reason) raise GerritAuthenticationError(response.status, reason)
...@@ -362,7 +365,9 @@ def ReadHttpResponse(conn, accept_statuses=frozenset([200, 404])): ...@@ -362,7 +365,9 @@ def ReadHttpResponse(conn, accept_statuses=frozenset([200, 404])):
LOGGER.warn('... will retry %d more times.', TRY_LIMIT - idx - 1) LOGGER.warn('... will retry %d more times.', TRY_LIMIT - idx - 1)
time.sleep(sleep_time) time.sleep(sleep_time)
sleep_time = sleep_time * 2 sleep_time = sleep_time * 2
if response.status not in accept_statuses: if ignore_404 and response.status == 404:
return StringIO()
if response.status != expect_status:
if response.status in (401, 403): if response.status in (401, 403):
print('Your Gerrit credentials might be misconfigured. Try: \n' print('Your Gerrit credentials might be misconfigured. Try: \n'
' git cl creds-check') ' git cl creds-check')
...@@ -371,9 +376,10 @@ def ReadHttpResponse(conn, accept_statuses=frozenset([200, 404])): ...@@ -371,9 +376,10 @@ def ReadHttpResponse(conn, accept_statuses=frozenset([200, 404])):
return StringIO(contents) return StringIO(contents)
def ReadHttpJsonResponse(conn, accept_statuses=None): def ReadHttpJsonResponse(conn, expect_status=200, ignore_404=True):
"""Parses an https response as json.""" """Parses an https response as json."""
fh = ReadHttpResponse(conn, accept_statuses) fh = ReadHttpResponse(
conn, expect_status=expect_status, ignore_404=ignore_404)
# The first line of the response should always be: )]}' # The first line of the response should always be: )]}'
s = fh.readline() s = fh.readline()
if s and s.rstrip() != ")]}'": if s and s.rstrip() != ")]}'":
...@@ -411,7 +417,7 @@ def QueryChanges(host, param_dict, first_param=None, limit=None, o_params=None, ...@@ -411,7 +417,7 @@ def QueryChanges(host, param_dict, first_param=None, limit=None, o_params=None,
if o_params: if o_params:
path = '%s&%s' % (path, '&'.join(['o=%s' % p for p in o_params])) path = '%s&%s' % (path, '&'.join(['o=%s' % p for p in o_params]))
# Don't ignore 404; a query should always return a list, even if it's empty. # Don't ignore 404; a query should always return a list, even if it's empty.
return ReadHttpJsonResponse(CreateHttpConn(host, path), accept_statuses=[200]) return ReadHttpJsonResponse(CreateHttpConn(host, path), ignore_404=False)
def GenerateAllChanges(host, param_dict, first_param=None, limit=500, def GenerateAllChanges(host, param_dict, first_param=None, limit=500,
...@@ -494,8 +500,7 @@ def MultiQueryChanges(host, param_dict, change_list, limit=None, o_params=None, ...@@ -494,8 +500,7 @@ def MultiQueryChanges(host, param_dict, change_list, limit=None, o_params=None,
q.extend(['o=%s' % p for p in o_params]) q.extend(['o=%s' % p for p in o_params])
path = 'changes/?%s' % '&'.join(q) path = 'changes/?%s' % '&'.join(q)
try: try:
result = ReadHttpJsonResponse( result = ReadHttpJsonResponse(CreateHttpConn(host, path), ignore_404=False)
CreateHttpConn(host, path), accept_statuses=[200])
except GerritError as e: except GerritError as e:
msg = '%s:\n%s' % (e.message, path) msg = '%s:\n%s' % (e.message, path)
raise GerritError(e.http_status, msg) raise GerritError(e.http_status, msg)
...@@ -523,13 +528,13 @@ def GetChange(host, change): ...@@ -523,13 +528,13 @@ def GetChange(host, change):
return ReadHttpJsonResponse(CreateHttpConn(host, path)) return ReadHttpJsonResponse(CreateHttpConn(host, path))
def GetChangeDetail(host, change, o_params=None, accept_statuses=None): def GetChangeDetail(host, change, o_params=None, ignore_404=True):
"""Query a gerrit server for extended information about a single change.""" """Query a gerrit server for extended information about a single change."""
# TODO(tandrii): cahnge ignore_404 to False by default.
path = 'changes/%s/detail' % change path = 'changes/%s/detail' % change
if o_params: if o_params:
path += '?%s' % '&'.join(['o=%s' % p for p in o_params]) path += '?%s' % '&'.join(['o=%s' % p for p in o_params])
return ReadHttpJsonResponse( return ReadHttpJsonResponse(CreateHttpConn(host, path), ignore_404=ignore_404)
CreateHttpConn(host, path), accept_statuses=accept_statuses)
def GetChangeCommit(host, change, revision='current'): def GetChangeCommit(host, change, revision='current'):
...@@ -566,7 +571,7 @@ def AbandonChange(host, change, msg=''): ...@@ -566,7 +571,7 @@ def AbandonChange(host, change, msg=''):
path = 'changes/%s/abandon' % change path = 'changes/%s/abandon' % change
body = {'message': msg} if msg else {} body = {'message': msg} if msg else {}
conn = CreateHttpConn(host, path, reqtype='POST', body=body) conn = CreateHttpConn(host, path, reqtype='POST', body=body)
return ReadHttpJsonResponse(conn, accept_statuses=[200]) return ReadHttpJsonResponse(conn, ignore_404=False)
def RestoreChange(host, change, msg=''): def RestoreChange(host, change, msg=''):
...@@ -574,7 +579,7 @@ def RestoreChange(host, change, msg=''): ...@@ -574,7 +579,7 @@ def RestoreChange(host, change, msg=''):
path = 'changes/%s/restore' % change path = 'changes/%s/restore' % change
body = {'message': msg} if msg else {} body = {'message': msg} if msg else {}
conn = CreateHttpConn(host, path, reqtype='POST', body=body) conn = CreateHttpConn(host, path, reqtype='POST', body=body)
return ReadHttpJsonResponse(conn, accept_statuses=[200]) return ReadHttpJsonResponse(conn, ignore_404=False)
def SubmitChange(host, change, wait_for_merge=True): def SubmitChange(host, change, wait_for_merge=True):
...@@ -582,26 +587,31 @@ def SubmitChange(host, change, wait_for_merge=True): ...@@ -582,26 +587,31 @@ def SubmitChange(host, change, wait_for_merge=True):
path = 'changes/%s/submit' % change path = 'changes/%s/submit' % change
body = {'wait_for_merge': wait_for_merge} body = {'wait_for_merge': wait_for_merge}
conn = CreateHttpConn(host, path, reqtype='POST', body=body) conn = CreateHttpConn(host, path, reqtype='POST', body=body)
return ReadHttpJsonResponse(conn, accept_statuses=[200]) return ReadHttpJsonResponse(conn, ignore_404=False)
def HasPendingChangeEdit(host, change): def HasPendingChangeEdit(host, change):
conn = CreateHttpConn(host, 'changes/%s/edit' % change) conn = CreateHttpConn(host, 'changes/%s/edit' % change)
try: try:
ReadHttpResponse(conn, accept_statuses=[200]) ReadHttpResponse(conn, ignore_404=False)
except GerritError as e: except GerritError as e:
# 204 No Content means no pending change. # On success, gerrit returns status 204; anything else is an error.
if e.http_status == 204: if e.http_status != 204:
return False raise
raise return False
return True else:
return True
def DeletePendingChangeEdit(host, change): def DeletePendingChangeEdit(host, change):
conn = CreateHttpConn(host, 'changes/%s/edit' % change, reqtype='DELETE') conn = CreateHttpConn(host, 'changes/%s/edit' % change, reqtype='DELETE')
# On success, gerrit returns status 204; if the edit was already deleted it try:
# returns 404. Anything else is an error. ReadHttpResponse(conn, ignore_404=False)
ReadHttpResponse(conn, accept_statuses=[204, 404]) except GerritError as e:
# On success, gerrit returns status 204; if the edit was already deleted it
# returns 404. Anything else is an error.
if e.http_status not in (204, 404):
raise
def SetCommitMessage(host, change, description, notify='ALL'): def SetCommitMessage(host, change, description, notify='ALL'):
...@@ -613,24 +623,29 @@ def SetCommitMessage(host, change, description, notify='ALL'): ...@@ -613,24 +623,29 @@ def SetCommitMessage(host, change, description, notify='ALL'):
body = {'message': description} body = {'message': description}
conn = CreateHttpConn(host, path, reqtype='PUT', body=body) conn = CreateHttpConn(host, path, reqtype='PUT', body=body)
try: try:
ReadHttpResponse(conn, accept_statuses=[204]) ReadHttpResponse(conn, ignore_404=False)
except GerritError as e: except GerritError as e:
# On success, gerrit returns status 204; anything else is an error.
if e.http_status != 204:
raise
else:
raise GerritError( raise GerritError(
e.http_status, 'Unexpectedly received a 200 http status while editing message in '
'Received unexpected http status while editing message ' 'change %s' % change)
'in change %s' % change)
# And then publish it. # And then publish it.
path = 'changes/%s/edit:publish' % change path = 'changes/%s/edit:publish' % change
conn = CreateHttpConn(host, path, reqtype='POST', body={'notify': notify}) conn = CreateHttpConn(host, path, reqtype='POST', body={'notify': notify})
try: try:
ReadHttpResponse(conn, accept_statuses=[204]) ReadHttpResponse(conn, ignore_404=False)
except GerritError as e: except GerritError as e:
# On success, gerrit returns status 204; anything else is an error.
if e.http_status != 204:
raise
else:
raise GerritError( raise GerritError(
e.http_status, 'Unexpectedly received a 200 http status while publishing message '
'Received unexpected http status while publishing message ' 'change in %s' % change)
'in change %s' % change)
except (GerritError, KeyboardInterrupt) as e: except (GerritError, KeyboardInterrupt) as e:
# Something went wrong with one of the two calls, so we want to clean up # Something went wrong with one of the two calls, so we want to clean up
# after ourselves before returning. # after ourselves before returning.
...@@ -673,7 +688,7 @@ def AddReviewers(host, change, add=None, is_reviewer=True, notify=True): ...@@ -673,7 +688,7 @@ def AddReviewers(host, change, add=None, is_reviewer=True, notify=True):
} }
try: try:
conn = CreateHttpConn(host, path, reqtype='POST', body=body) conn = CreateHttpConn(host, path, reqtype='POST', body=body)
_ = ReadHttpJsonResponse(conn, accept_statuses=[200]) _ = ReadHttpJsonResponse(conn, ignore_404=False)
except GerritError as e: except GerritError as e:
if e.http_status == 422: # "Unprocessable Entity" if e.http_status == 422: # "Unprocessable Entity"
LOGGER.warn('Note: "%s" not added as a %s' % (r, state.lower())) LOGGER.warn('Note: "%s" not added as a %s' % (r, state.lower()))
...@@ -693,12 +708,15 @@ def RemoveReviewers(host, change, remove=None): ...@@ -693,12 +708,15 @@ def RemoveReviewers(host, change, remove=None):
path = 'changes/%s/reviewers/%s' % (change, r) path = 'changes/%s/reviewers/%s' % (change, r)
conn = CreateHttpConn(host, path, reqtype='DELETE') conn = CreateHttpConn(host, path, reqtype='DELETE')
try: try:
ReadHttpResponse(conn, accept_statuses=[204]) ReadHttpResponse(conn, ignore_404=False)
except GerritError as e: except GerritError as e:
# On success, gerrit returns status 204; anything else is an error.
if e.http_status != 204:
raise
else:
raise GerritError( raise GerritError(
e.http_status, 'Unexpectedly received a 200 http status while deleting reviewer "%s"'
'Received unexpected http status while deleting reviewer "%s" ' ' from change %s' % (r, change))
'from change %s' % (r, change))
def SetReview(host, change, msg=None, labels=None, notify=None): def SetReview(host, change, msg=None, labels=None, notify=None):
......
...@@ -2641,8 +2641,8 @@ class _GerritChangelistImpl(_ChangelistCodereviewBase): ...@@ -2641,8 +2641,8 @@ class _GerritChangelistImpl(_ChangelistCodereviewBase):
return data return data
try: try:
data = gerrit_util.GetChangeDetail( data = gerrit_util.GetChangeDetail(self._GetGerritHost(), str(issue),
self._GetGerritHost(), str(issue), options, accept_statuses=[200]) options, ignore_404=False)
except gerrit_util.GerritError as e: except gerrit_util.GerritError as e:
if e.http_status == 404: if e.http_status == 404:
raise GerritChangeNotExists(issue, self.GetCodereviewServer()) raise GerritChangeNotExists(issue, self.GetCodereviewServer())
......
...@@ -202,7 +202,7 @@ class GerritAccessor(object): ...@@ -202,7 +202,7 @@ class GerritAccessor(object):
return gerrit_util.GetChangeDetail( return gerrit_util.GetChangeDetail(
self.host, str(issue), self.host, str(issue),
['ALL_REVISIONS', 'DETAILED_LABELS', 'ALL_COMMITS'], ['ALL_REVISIONS', 'DETAILED_LABELS', 'ALL_COMMITS'],
accept_statuses=[200]) ignore_404=False)
except gerrit_util.GerritError as e: except gerrit_util.GerritError as e:
if e.http_status == 404: if e.http_status == 404:
raise Exception('Either Gerrit issue %s doesn\'t exist, or ' raise Exception('Either Gerrit issue %s doesn\'t exist, or '
......
...@@ -208,7 +208,7 @@ class GerritTestCase(unittest.TestCase): ...@@ -208,7 +208,7 @@ class GerritTestCase(unittest.TestCase):
path = 'projects/%s' % urllib.quote(name, '') path = 'projects/%s' % urllib.quote(name, '')
conn = gerrit_util.CreateHttpConn( conn = gerrit_util.CreateHttpConn(
cls.gerrit_instance.gerrit_host, path, reqtype='PUT', body=body) cls.gerrit_instance.gerrit_host, path, reqtype='PUT', body=body)
jmsg = gerrit_util.ReadHttpJsonResponse(conn, accept_statuses=[200, 201]) jmsg = gerrit_util.ReadHttpJsonResponse(conn, expect_status=201)
assert jmsg['name'] == name assert jmsg['name'] == name
@classmethod @classmethod
......
...@@ -2200,7 +2200,7 @@ class TestGitCl(TestCase): ...@@ -2200,7 +2200,7 @@ class TestGitCl(TestCase):
def test_patch_gerrit_not_exists(self): def test_patch_gerrit_not_exists(self):
def notExists(_issue, *_, **kwargs): def notExists(_issue, *_, **kwargs):
self.assertNotIn(404, kwargs['accept_statuses']) self.assertFalse(kwargs['ignore_404'])
raise git_cl.gerrit_util.GerritError(404, '') raise git_cl.gerrit_util.GerritError(404, '')
self.mock(git_cl.gerrit_util, 'GetChangeDetail', notExists) self.mock(git_cl.gerrit_util, 'GetChangeDetail', notExists)
......
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