Merge "Handle missing diff_refs attribute"
This commit is contained in:
commit
2f0a02124e
@ -2118,6 +2118,21 @@ class FakeGitlabConnection(gitlabconnection.GitlabConnection):
|
||||
yield
|
||||
self._test_web_server.options['community_edition'] = False
|
||||
|
||||
@contextmanager
|
||||
def enable_delayed_complete_mr(self, complete_at):
|
||||
self._test_web_server.options['delayed_complete_mr'] = complete_at
|
||||
yield
|
||||
self._test_web_server.options['delayed_complete_mr'] = 0
|
||||
|
||||
@contextmanager
|
||||
def enable_uncomplete_mr(self):
|
||||
self._test_web_server.options['uncomplete_mr'] = True
|
||||
orig = self.gl_client.get_mr_wait_factor
|
||||
self.gl_client.get_mr_wait_factor = 0.1
|
||||
yield
|
||||
self.gl_client.get_mr_wait_factor = orig
|
||||
self._test_web_server.options['uncomplete_mr'] = False
|
||||
|
||||
|
||||
class GitlabChangeReference(git.Reference):
|
||||
_common_path_default = "refs/merge-requests"
|
||||
|
@ -21,6 +21,7 @@ import re
|
||||
import socketserver
|
||||
import threading
|
||||
import urllib.parse
|
||||
import time
|
||||
|
||||
from git.util import IterableList
|
||||
|
||||
@ -32,12 +33,17 @@ class GitlabWebServer(object):
|
||||
self.merge_requests = merge_requests
|
||||
self.fake_repos = defaultdict(lambda: IterableList('name'))
|
||||
# A dictionary so we can mutate it
|
||||
self.options = dict(community_edition=False)
|
||||
self.options = dict(
|
||||
community_edition=False,
|
||||
delayed_complete_mr=0,
|
||||
uncomplete_mr=False)
|
||||
self.stats = {"get_mr": 0}
|
||||
|
||||
def start(self):
|
||||
merge_requests = self.merge_requests
|
||||
fake_repos = self.fake_repos
|
||||
options = self.options
|
||||
stats = self.stats
|
||||
|
||||
class Server(http.server.SimpleHTTPRequestHandler):
|
||||
log = logging.getLogger("zuul.test.GitlabWebServer")
|
||||
@ -146,6 +152,7 @@ class GitlabWebServer(object):
|
||||
self.wfile.write(data)
|
||||
|
||||
def get_mr(self, project, mr):
|
||||
stats["get_mr"] += 1
|
||||
mr = self._get_mr(project, mr)
|
||||
data = {
|
||||
'target_branch': mr.branch,
|
||||
@ -162,13 +169,20 @@ class GitlabWebServer(object):
|
||||
'labels': mr.labels,
|
||||
'merged_at': mr.merged_at.strftime('%Y-%m-%dT%H:%M:%S.%fZ')
|
||||
if mr.merged_at else mr.merged_at,
|
||||
'diff_refs': {
|
||||
'merge_status': mr.merge_status,
|
||||
}
|
||||
if options['delayed_complete_mr'] and \
|
||||
time.monotonic() < options['delayed_complete_mr']:
|
||||
diff_refs = None
|
||||
elif options['uncomplete_mr']:
|
||||
diff_refs = None
|
||||
else:
|
||||
diff_refs = {
|
||||
'base_sha': mr.base_sha,
|
||||
'head_sha': mr.sha,
|
||||
'start_sha': 'c380d3acebd181f13629a25d2e2acca46ffe1e00'
|
||||
},
|
||||
'merge_status': mr.merge_status,
|
||||
}
|
||||
}
|
||||
data['diff_refs'] = diff_refs
|
||||
self.send_data(data)
|
||||
|
||||
def get_mr_approvals(self, project, mr):
|
||||
|
@ -125,6 +125,27 @@ class TestGitlabDriver(ZuulTestCase):
|
||||
MatchesRegex(r'.*project-test2.*SUCCESS.*', re.DOTALL))
|
||||
self.assertTrue(A.approved)
|
||||
|
||||
@simple_layout('layouts/basic-gitlab.yaml', driver='gitlab')
|
||||
def test_merge_request_opened_imcomplete(self):
|
||||
|
||||
now = time.monotonic()
|
||||
complete_at = now + 3
|
||||
with self.fake_gitlab.enable_delayed_complete_mr(complete_at):
|
||||
description = "This is the\nMR description."
|
||||
A = self.fake_gitlab.openFakeMergeRequest(
|
||||
'org/project', 'master', 'A', description=description)
|
||||
self.fake_gitlab.emitEvent(
|
||||
A.getMergeRequestOpenedEvent(), project='org/project')
|
||||
self.waitUntilSettled()
|
||||
|
||||
self.assertEqual('SUCCESS',
|
||||
self.getJobFromHistory('project-test1').result)
|
||||
|
||||
self.assertEqual('SUCCESS',
|
||||
self.getJobFromHistory('project-test2').result)
|
||||
|
||||
self.assertTrue(self.fake_gitlab._test_web_server.stats["get_mr"] > 2)
|
||||
|
||||
@simple_layout('layouts/basic-gitlab.yaml', driver='gitlab')
|
||||
def test_merge_request_updated(self):
|
||||
|
||||
@ -407,7 +428,7 @@ class TestGitlabDriver(ZuulTestCase):
|
||||
|
||||
@simple_layout('layouts/basic-gitlab.yaml', driver='gitlab')
|
||||
def test_pull_request_with_dyn_reconf(self):
|
||||
|
||||
path = os.path.join(self.upstream_root, 'org/project')
|
||||
zuul_yaml = [
|
||||
{'job': {
|
||||
'name': 'project-test3',
|
||||
@ -424,11 +445,13 @@ class TestGitlabDriver(ZuulTestCase):
|
||||
playbook = "- hosts: all\n tasks: []"
|
||||
|
||||
A = self.fake_gitlab.openFakeMergeRequest(
|
||||
'org/project', 'master', 'A')
|
||||
'org/project', 'master', 'A',
|
||||
base_sha=git.Repo(path).head.object.hexsha)
|
||||
A.addCommit(
|
||||
{'.zuul.yaml': yaml.dump(zuul_yaml),
|
||||
'job.yaml': playbook}
|
||||
)
|
||||
A.addCommit({"dummy.file": ""})
|
||||
self.fake_gitlab.emitEvent(A.getMergeRequestOpenedEvent())
|
||||
self.waitUntilSettled()
|
||||
|
||||
@ -439,6 +462,40 @@ class TestGitlabDriver(ZuulTestCase):
|
||||
self.assertEqual('SUCCESS',
|
||||
self.getJobFromHistory('project-test3').result)
|
||||
|
||||
@simple_layout('layouts/basic-gitlab.yaml', driver='gitlab')
|
||||
def test_pull_request_with_dyn_reconf_alt(self):
|
||||
with self.fake_gitlab.enable_uncomplete_mr():
|
||||
zuul_yaml = [
|
||||
{'job': {
|
||||
'name': 'project-test3',
|
||||
'run': 'job.yaml'
|
||||
}},
|
||||
{'project': {
|
||||
'check': {
|
||||
'jobs': [
|
||||
'project-test3'
|
||||
]
|
||||
}
|
||||
}}
|
||||
]
|
||||
playbook = "- hosts: all\n tasks: []"
|
||||
A = self.fake_gitlab.openFakeMergeRequest(
|
||||
'org/project', 'master', 'A')
|
||||
A.addCommit(
|
||||
{'.zuul.yaml': yaml.dump(zuul_yaml),
|
||||
'job.yaml': playbook}
|
||||
)
|
||||
A.addCommit({"dummy.file": ""})
|
||||
self.fake_gitlab.emitEvent(A.getMergeRequestOpenedEvent())
|
||||
self.waitUntilSettled()
|
||||
|
||||
self.assertEqual('SUCCESS',
|
||||
self.getJobFromHistory('project-test1').result)
|
||||
self.assertEqual('SUCCESS',
|
||||
self.getJobFromHistory('project-test2').result)
|
||||
self.assertEqual('SUCCESS',
|
||||
self.getJobFromHistory('project-test3').result)
|
||||
|
||||
@simple_layout('layouts/basic-gitlab.yaml', driver='gitlab')
|
||||
def test_ref_updated_and_tenant_reconfigure(self):
|
||||
|
||||
|
@ -281,6 +281,7 @@ class GitlabAPIClient():
|
||||
self.api_token = api_token
|
||||
self.keepalive = keepalive
|
||||
self.disable_pool = disable_pool
|
||||
self.get_mr_wait_factor = 2
|
||||
self.headers = {'Authorization': 'Bearer %s' % (
|
||||
self.api_token)}
|
||||
|
||||
@ -342,11 +343,36 @@ class GitlabAPIClient():
|
||||
|
||||
# https://docs.gitlab.com/ee/api/merge_requests.html#get-single-mr
|
||||
def get_mr(self, project_name, number, zuul_event_id=None):
|
||||
path = "/projects/%s/merge_requests/%s" % (
|
||||
quote_plus(project_name), number)
|
||||
resp = self.get(self.baseurl + path, zuul_event_id=zuul_event_id)
|
||||
self._manage_error(*resp, zuul_event_id=zuul_event_id)
|
||||
return resp[0]
|
||||
log = get_annotated_logger(self.log, zuul_event_id)
|
||||
attempts = 0
|
||||
|
||||
def _get_mr():
|
||||
path = "/projects/%s/merge_requests/%s" % (
|
||||
quote_plus(project_name), number)
|
||||
resp = self.get(self.baseurl + path, zuul_event_id=zuul_event_id)
|
||||
self._manage_error(*resp, zuul_event_id=zuul_event_id)
|
||||
return resp[0]
|
||||
|
||||
# The Gitlab API might not return a complete MR description as
|
||||
# some attributes are updated asynchronously. This loop ensures
|
||||
# we query the API until all async attributes are available or until
|
||||
# a defined delay is reached.
|
||||
while True:
|
||||
attempts += 1
|
||||
mr = _get_mr()
|
||||
# The diff_refs attribute is updated asynchronously
|
||||
if all(map(lambda k: mr.get(k, None), ['diff_refs'])):
|
||||
return mr
|
||||
if attempts > 4:
|
||||
log.warning(
|
||||
"Fetched MR %s#%s with imcomplete data" % (
|
||||
project_name, number))
|
||||
return mr
|
||||
wait_delay = attempts * self.get_mr_wait_factor
|
||||
log.info(
|
||||
"Will retry to fetch %s#%s due to imcomplete data "
|
||||
"(in %s seconds) ..." % (project_name, number, wait_delay))
|
||||
time.sleep(wait_delay)
|
||||
|
||||
# https://docs.gitlab.com/ee/api/branches.html#list-repository-branches
|
||||
def get_project_branches(self, project_name, exclude_unprotected,
|
||||
@ -667,8 +693,12 @@ class GitlabConnection(ZKChangeCacheMixin, ZKBranchCacheMixin, BaseConnection):
|
||||
change.ref = "refs/merge-requests/%s/head" % change.number
|
||||
change.branch = change.mr['target_branch']
|
||||
change.is_current_patchset = (change.mr['sha'] == change.patchset)
|
||||
change.base_sha = change.mr['diff_refs'].get('base_sha')
|
||||
change.commit_id = change.mr['diff_refs'].get('head_sha')
|
||||
change.commit_id = event.patch_number
|
||||
diff_refs = change.mr.get("diff_refs", {})
|
||||
if diff_refs:
|
||||
change.base_sha = diff_refs.get('base_sha')
|
||||
else:
|
||||
change.base_sha = None
|
||||
change.owner = change.mr['author'].get('username')
|
||||
# Files changes are not part of the Merge Request data
|
||||
# See api/merge_requests.html#get-single-mr-changes
|
||||
|
Loading…
x
Reference in New Issue
Block a user