mirror of
https://github.com/odoo/runbot.git
synced 2025-03-27 13:25:47 +07:00
[FIX] runbot: try to update github status multiple times
When updating github statuses, it happens that we face a "Bad gateway" from github. In that case, the error is logged in the runbot logs and that's it. As a consequence, when the runbot_merge is waiting status for the staging branch and this kind of error occurs, the runbot_merge timeouts and the users vainly search the reason. With this commit, the runbot tries to update the status at least twice. If it fails, an INFO message is logged on the build itself.
This commit is contained in:
parent
2fb0f2c79a
commit
c13128a229
@ -858,10 +858,13 @@ class runbot_build(models.Model):
|
|||||||
"""Notify each repo with a status"""
|
"""Notify each repo with a status"""
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
if self.config_id.update_github_state:
|
if self.config_id.update_github_state:
|
||||||
commits = {(b.repo_id, b.name) for b in self.search([('name', '=', self.name)])}
|
repos = {b.repo_id for b in self.search([('name', '=', self.name)])}
|
||||||
for repo, commit_hash in commits:
|
for repo in repos:
|
||||||
_logger.debug("github updating %s status %s to %s in repo %s", status['context'], commit_hash, status['state'], repo.name)
|
_logger.debug("github updating %s status %s to %s in repo %s", status['context'], self.name, status['state'], repo.name)
|
||||||
repo._github('/repos/:owner/:repo/statuses/%s' % commit_hash, status, ignore_errors=True)
|
try:
|
||||||
|
repo._github('/repos/:owner/:repo/statuses/%s' % self.name, status, ignore_errors=True)
|
||||||
|
except Exception:
|
||||||
|
self._log('_github_status_notify_all', 'Status notification failed for "%s" in repo "%s"' % (self.name, repo.name))
|
||||||
|
|
||||||
def _github_status(self):
|
def _github_status(self):
|
||||||
"""Notify github of failed/successful builds"""
|
"""Notify github of failed/successful builds"""
|
||||||
|
@ -123,31 +123,39 @@ class runbot_repo(models.Model):
|
|||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _github(self, url, payload=None, ignore_errors=False):
|
def _github(self, url, payload=None, ignore_errors=False, nb_tries=2):
|
||||||
"""Return a http request to be sent to github"""
|
"""Return a http request to be sent to github"""
|
||||||
for repo in self:
|
for repo in self:
|
||||||
if not repo.token:
|
if not repo.token:
|
||||||
return
|
return
|
||||||
try:
|
match_object = re.search('([^/]+)/([^/]+)/([^/.]+(.git)?)', repo.base)
|
||||||
match_object = re.search('([^/]+)/([^/]+)/([^/.]+(.git)?)', repo.base)
|
if match_object:
|
||||||
if match_object:
|
url = url.replace(':owner', match_object.group(2))
|
||||||
url = url.replace(':owner', match_object.group(2))
|
url = url.replace(':repo', match_object.group(3))
|
||||||
url = url.replace(':repo', match_object.group(3))
|
url = 'https://api.%s%s' % (match_object.group(1), url)
|
||||||
url = 'https://api.%s%s' % (match_object.group(1), url)
|
session = requests.Session()
|
||||||
session = requests.Session()
|
session.auth = (repo.token, 'x-oauth-basic')
|
||||||
session.auth = (repo.token, 'x-oauth-basic')
|
session.headers.update({'Accept': 'application/vnd.github.she-hulk-preview+json'})
|
||||||
session.headers.update({'Accept': 'application/vnd.github.she-hulk-preview+json'})
|
try_count = 0
|
||||||
if payload:
|
while try_count < nb_tries:
|
||||||
response = session.post(url, data=json.dumps(payload))
|
try:
|
||||||
else:
|
if payload:
|
||||||
response = session.get(url)
|
response = session.post(url, data=json.dumps(payload))
|
||||||
response.raise_for_status()
|
else:
|
||||||
return response.json()
|
response = session.get(url)
|
||||||
except Exception:
|
response.raise_for_status()
|
||||||
if ignore_errors:
|
if try_count > 0:
|
||||||
_logger.exception('Ignored github error %s %r', url, payload)
|
_logger.info('Success after %s tries' % (try_count + 1))
|
||||||
else:
|
return response.json()
|
||||||
raise
|
except Exception as e:
|
||||||
|
try_count += 1
|
||||||
|
if try_count < nb_tries:
|
||||||
|
time.sleep(2)
|
||||||
|
else:
|
||||||
|
if ignore_errors:
|
||||||
|
_logger.exception('Ignored github error %s %r (try %s/%s)' % (url, payload, try_count + 1, nb_tries))
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
def _get_fetch_head_time(self):
|
def _get_fetch_head_time(self):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
import datetime
|
||||||
from unittest import skip
|
from unittest import skip
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch, Mock
|
||||||
from odoo.tests import common
|
from odoo.tests import common
|
||||||
import logging
|
import logging
|
||||||
import odoo
|
import odoo
|
||||||
@ -152,6 +153,30 @@ class Test_Repo(common.TransactionCase):
|
|||||||
|
|
||||||
_logger.info('Create pending builds took: %ssec', (time.time() - inserted_time))
|
_logger.info('Create pending builds took: %ssec', (time.time() - inserted_time))
|
||||||
|
|
||||||
|
def test_github(self):
|
||||||
|
""" Test different github responses or failures"""
|
||||||
|
repo = self.Repo.create({'name': 'bla@example.com:foo/foo'})
|
||||||
|
self.assertEqual(repo._github('/repos/:owner/:repo/statuses/abcdef', dict(), ignore_errors=True), None, 'A repo without token should return None')
|
||||||
|
repo.token = 'abc'
|
||||||
|
with patch('odoo.addons.runbot.models.repo.requests.Session') as mock_session:
|
||||||
|
with self.assertRaises(Exception, msg='should raise an exception with ignore_errors=False'):
|
||||||
|
mock_session.return_value.post.side_effect = Exception('301: Bad gateway')
|
||||||
|
repo._github('/repos/:owner/:repo/statuses/abcdef', {'foo': 'bar'}, ignore_errors=False)
|
||||||
|
|
||||||
|
mock_session.return_value.post.reset_mock()
|
||||||
|
with self.assertLogs(logger='odoo.addons.runbot.models.repo') as assert_log:
|
||||||
|
repo._github('/repos/:owner/:repo/statuses/abcdef', {'foo': 'bar'}, ignore_errors=True)
|
||||||
|
self.assertIn('Ignored github error', assert_log.output[0])
|
||||||
|
|
||||||
|
self.assertEqual(2, mock_session.return_value.post.call_count, "_github method should try two times by default")
|
||||||
|
|
||||||
|
mock_session.return_value.post.reset_mock()
|
||||||
|
mock_session.return_value.post.side_effect = [Exception('301: Bad gateway'), Mock()]
|
||||||
|
with self.assertLogs(logger='odoo.addons.runbot.models.repo') as assert_log:
|
||||||
|
repo._github('/repos/:owner/:repo/statuses/abcdef', {'foo': 'bar'}, ignore_errors=True)
|
||||||
|
self.assertIn('Success after 2 tries', assert_log.output[0])
|
||||||
|
|
||||||
|
self.assertEqual(2, mock_session.return_value.post.call_count, "_github method should try two times by default")
|
||||||
|
|
||||||
class Test_Repo_Scheduler(common.TransactionCase):
|
class Test_Repo_Scheduler(common.TransactionCase):
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user