| OLD | NEW |
| 1 # coding: utf-8 | 1 # coding: utf-8 |
| 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 """Defines class Rietveld to easily access a rietveld instance. | 5 """Defines class Rietveld to easily access a rietveld instance. |
| 6 | 6 |
| 7 Security implications: | 7 Security implications: |
| 8 | 8 |
| 9 The following hypothesis are made: | 9 The following hypothesis are made: |
| 10 - Rietveld enforces: | 10 - Rietveld enforces: |
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 90 | 90 |
| 91 Converts any CRLF into LF and strip extraneous whitespace. | 91 Converts any CRLF into LF and strip extraneous whitespace. |
| 92 """ | 92 """ |
| 93 return '\n'.join(self.get('/%d/description' % issue).strip().splitlines()) | 93 return '\n'.join(self.get('/%d/description' % issue).strip().splitlines()) |
| 94 | 94 |
| 95 def get_issue_properties(self, issue, messages): | 95 def get_issue_properties(self, issue, messages): |
| 96 """Returns all the issue's metadata as a dictionary.""" | 96 """Returns all the issue's metadata as a dictionary.""" |
| 97 url = '/api/%d' % issue | 97 url = '/api/%d' % issue |
| 98 if messages: | 98 if messages: |
| 99 url += '?messages=true' | 99 url += '?messages=true' |
| 100 data = json.loads(self.get(url)) | 100 data = json.loads(self.get(url, retry_on_404=True)) |
| 101 data['description'] = '\n'.join(data['description'].strip().splitlines()) | 101 data['description'] = '\n'.join(data['description'].strip().splitlines()) |
| 102 return data | 102 return data |
| 103 | 103 |
| 104 def get_patchset_properties(self, issue, patchset): | 104 def get_patchset_properties(self, issue, patchset): |
| 105 """Returns the patchset properties.""" | 105 """Returns the patchset properties.""" |
| 106 url = '/api/%d/%d' % (issue, patchset) | 106 url = '/api/%d/%d' % (issue, patchset) |
| 107 return json.loads(self.get(url)) | 107 return json.loads(self.get(url)) |
| 108 | 108 |
| 109 def get_file_content(self, issue, patchset, item): | 109 def get_file_content(self, issue, patchset, item): |
| 110 """Returns the content of a new file. | 110 """Returns the content of a new file. |
| (...skipping 271 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 382 return data['jobs'], data['cursor'] | 382 return data['jobs'], data['cursor'] |
| 383 | 383 |
| 384 def get(self, request_path, **kwargs): | 384 def get(self, request_path, **kwargs): |
| 385 kwargs.setdefault('payload', None) | 385 kwargs.setdefault('payload', None) |
| 386 return self._send(request_path, **kwargs) | 386 return self._send(request_path, **kwargs) |
| 387 | 387 |
| 388 def post(self, request_path, data, **kwargs): | 388 def post(self, request_path, data, **kwargs): |
| 389 ctype, body = upload.EncodeMultipartFormData(data, []) | 389 ctype, body = upload.EncodeMultipartFormData(data, []) |
| 390 return self._send(request_path, payload=body, content_type=ctype, **kwargs) | 390 return self._send(request_path, payload=body, content_type=ctype, **kwargs) |
| 391 | 391 |
| 392 def _send(self, request_path, **kwargs): | 392 def _send(self, request_path, retry_on_404=False, **kwargs): |
| 393 """Sends a POST/GET to Rietveld. Returns the response body.""" | 393 """Sends a POST/GET to Rietveld. Returns the response body.""" |
| 394 # rpc_server.Send() assumes timeout=None by default; make sure it's set | 394 # rpc_server.Send() assumes timeout=None by default; make sure it's set |
| 395 # to something reasonable. | 395 # to something reasonable. |
| 396 kwargs.setdefault('timeout', 15) | 396 kwargs.setdefault('timeout', 15) |
| 397 logging.debug('POSTing to %s, args %s.', request_path, kwargs) | 397 logging.debug('POSTing to %s, args %s.', request_path, kwargs) |
| 398 try: | 398 try: |
| 399 # Sadly, upload.py calls ErrorExit() which does a sys.exit(1) on HTTP | 399 # Sadly, upload.py calls ErrorExit() which does a sys.exit(1) on HTTP |
| 400 # 500 in AbstractRpcServer.Send(). | 400 # 500 in AbstractRpcServer.Send(). |
| 401 old_error_exit = upload.ErrorExit | 401 old_error_exit = upload.ErrorExit |
| 402 def trap_http_500(msg): | 402 def trap_http_500(msg): |
| (...skipping 10 matching lines...) Expand all Loading... |
| 413 for retry in xrange(maxtries): | 413 for retry in xrange(maxtries): |
| 414 try: | 414 try: |
| 415 logging.debug('%s' % request_path) | 415 logging.debug('%s' % request_path) |
| 416 result = self.rpc_server.Send(request_path, **kwargs) | 416 result = self.rpc_server.Send(request_path, **kwargs) |
| 417 # Sometimes GAE returns a HTTP 200 but with HTTP 500 as the content. | 417 # Sometimes GAE returns a HTTP 200 but with HTTP 500 as the content. |
| 418 # How nice. | 418 # How nice. |
| 419 return result | 419 return result |
| 420 except urllib2.HTTPError, e: | 420 except urllib2.HTTPError, e: |
| 421 if retry >= (maxtries - 1): | 421 if retry >= (maxtries - 1): |
| 422 raise | 422 raise |
| 423 if e.code not in (500, 502, 503): | 423 flake_codes = [500, 502, 503] |
| 424 if retry_on_404: |
| 425 flake_codes.append(404) |
| 426 if e.code not in flake_codes: |
| 424 raise | 427 raise |
| 425 except urllib2.URLError, e: | 428 except urllib2.URLError, e: |
| 426 if retry >= (maxtries - 1): | 429 if retry >= (maxtries - 1): |
| 427 raise | 430 raise |
| 428 if (not 'Name or service not known' in e.reason and | 431 if (not 'Name or service not known' in e.reason and |
| 429 not 'EOF occurred in violation of protocol' in e.reason): | 432 not 'EOF occurred in violation of protocol' in e.reason): |
| 430 # Usually internal GAE flakiness. | 433 # Usually internal GAE flakiness. |
| 431 raise | 434 raise |
| 432 except ssl.SSLError, e: | 435 except ssl.SSLError, e: |
| 433 if retry >= (maxtries - 1): | 436 if retry >= (maxtries - 1): |
| (...skipping 283 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 717 def trigger_try_jobs( # pylint:disable=R0201 | 720 def trigger_try_jobs( # pylint:disable=R0201 |
| 718 self, issue, patchset, reason, clobber, revision, builders_and_tests, | 721 self, issue, patchset, reason, clobber, revision, builders_and_tests, |
| 719 master=None): | 722 master=None): |
| 720 logging.info('ReadOnlyRietveld: triggering try jobs %r for issue %d' % | 723 logging.info('ReadOnlyRietveld: triggering try jobs %r for issue %d' % |
| 721 (builders_and_tests, issue)) | 724 (builders_and_tests, issue)) |
| 722 | 725 |
| 723 def trigger_distributed_try_jobs( # pylint:disable=R0201 | 726 def trigger_distributed_try_jobs( # pylint:disable=R0201 |
| 724 self, issue, patchset, reason, clobber, revision, masters): | 727 self, issue, patchset, reason, clobber, revision, masters): |
| 725 logging.info('ReadOnlyRietveld: triggering try jobs %r for issue %d' % | 728 logging.info('ReadOnlyRietveld: triggering try jobs %r for issue %d' % |
| 726 (masters, issue)) | 729 (masters, issue)) |
| OLD | NEW |