Retry queries for commits

When a pull_request event arrives we also get the commit statuses
which are attached to the commit object. Sometimes the commit is not
yet queryable and we get a 404 [1]. In this case we need to retry the
query. In the future we probably want to use that as a feedback
mechanism of a dynamic event delay.

[1] Trace
2018-06-13 11:49:11,881 ERROR zuul.GithubEventConnector: Exception moving GitHub event:
Traceback (most recent call last):
  File "/usr/lib/python3.6/site-packages/zuul/driver/github/githubconnection.py", line 391, in run
    self._handleEvent()
  File "/usr/lib/python3.6/site-packages/zuul/driver/github/githubconnection.py", line 226, in _handleEvent
    refresh=True)
  File "/usr/lib/python3.6/site-packages/zuul/driver/github/githubconnection.py", line 751, in _getChange
    self._updateChange(change)
  File "/usr/lib/python3.6/site-packages/zuul/driver/github/githubconnection.py", line 837, in _updateChange
    change.patchset)
  File "/usr/lib/python3.6/site-packages/zuul/driver/github/githubconnection.py", line 1226, in _get_statuses
    for status in self.getCommitStatuses(project.name, sha):
  File "/usr/lib/python3.6/site-packages/zuul/driver/github/githubconnection.py", line 1171, in getCommitStatuses
    commit = repository.commit(sha)
  File "/usr/lib/python3.6/site-packages/github3/repos/repo.py", line 342, in commit
    json = self._json(self._get(url), 200)
  File "/usr/lib/python3.6/site-packages/github3/models.py", line 156, in _json
    raise exceptions.error_for(response)
github3.exceptions.NotFoundError: 404 Not Found

Change-Id: I5cc6d95ed18148657d9e5a8d132a8e43f385771d
This commit is contained in:
Tobias Henkel 2018-06-13 16:41:56 +02:00 committed by Tobias Henkel
parent bc136cb40e
commit f312bda118
No known key found for this signature in database
GPG Key ID: 03750DEC158E5FA2
3 changed files with 68 additions and 1 deletions

View File

@ -14,6 +14,7 @@
# License for the specific language governing permissions and limitations
# under the License.
import github3.exceptions
import re
FAKE_BASE_URL = 'https://example.com/api/v3/'
@ -82,6 +83,9 @@ class FakeRepository(object):
self.data = data
self.name = name
# fail the next commit requests with 404
self.fail_not_found = 0
def branches(self, protected=False):
if protected:
# simulate there is no protected branch
@ -121,6 +125,25 @@ class FakeRepository(object):
commit.set_status(state, url, description, context, user)
def commit(self, sha):
if self.fail_not_found > 0:
self.fail_not_found -= 1
class Response:
status_code = 0
message = ''
def json(self):
return {
'message': self.message
}
resp = Response()
resp.status_code = 404
resp.message = 'Not Found'
raise github3.exceptions.NotFoundError(resp)
commit = self._commits.get(sha, None)
if commit is None:
commit = FakeCommit(sha)

View File

@ -1000,6 +1000,37 @@ class TestGithubDriver(ZuulTestCase):
self.assertEquals('https://github.com/org/project/pull/1',
job2_params['zuul']['items'][0]['change_url'])
@simple_layout('layouts/basic-github.yaml', driver='github')
def test_pull_commit_race(self):
"""Test graceful handling of delayed availability of commits"""
github = self.fake_github.getGithubClient('org/project')
repo = github.repo_from_project('org/project')
repo.fail_not_found = 1
A = self.fake_github.openFakePullRequest('org/project', 'master', 'A')
self.fake_github.emitEvent(A.getPullRequestOpenedEvent())
self.waitUntilSettled()
self.assertEqual('SUCCESS',
self.getJobFromHistory('project-test1').result)
self.assertEqual('SUCCESS',
self.getJobFromHistory('project-test2').result)
job = self.getJobFromHistory('project-test2')
zuulvars = job.parameters['zuul']
self.assertEqual(str(A.number), zuulvars['change'])
self.assertEqual(str(A.head_sha), zuulvars['patchset'])
self.assertEqual('master', zuulvars['branch'])
self.assertEqual(1, len(A.comments))
self.assertThat(
A.comments[0],
MatchesRegex(r'.*\[project-test1 \]\(.*\).*', re.DOTALL))
self.assertThat(
A.comments[0],
MatchesRegex(r'.*\[project-test2 \]\(.*\).*', re.DOTALL))
self.assertEqual(2, len(self.history))
class TestGithubUnprotectedBranches(ZuulTestCase):
config_file = 'zuul-github-driver.conf'

View File

@ -1322,11 +1322,24 @@ class GithubConnection(BaseConnection):
if not result:
raise Exception('Pull request was not merged')
def _getCommit(self, repository, sha, retries=5):
try:
return repository.commit(sha)
except github3.exceptions.NotFoundError:
self.log.warning("Commit %s of project %s returned None",
sha, repository.name)
if retries <= 0:
raise
time.sleep(1)
return self._getCommit(repository, sha, retries - 1)
def getCommitStatuses(self, project, sha):
github = self.getGithubClient(project)
owner, proj = project.split('/')
repository = github.repository(owner, proj)
commit = repository.commit(sha)
commit = self._getCommit(repository, sha, 5)
# make a list out of the statuses so that we complete our
# API transaction
statuses = [status.as_dict() for status in commit.statuses()]