Merge "Report retried builds in a build set via mqtt."

This commit is contained in:
Zuul 2020-06-23 18:21:38 +00:00 committed by Gerrit Code Review
commit 03d9ea294a
5 changed files with 52 additions and 11 deletions

View File

@ -5737,7 +5737,18 @@ For CI problems and help debugging, contact ci@example.org"""
self.executor_server.release('.*-test*') self.executor_server.release('.*-test*')
self.waitUntilSettled() self.waitUntilSettled()
tenant = self.scheds.first.sched.abide.tenants.get('tenant-one')
items = tenant.layout.pipelines['check'].getAllItems()
build_set = items[0].current_build_set
for x in range(3): for x in range(3):
# We should have x+1 retried builds for project-test1
retry_builds = build_set.getRetryBuildsForJob('project-test1')
self.assertEqual(len(retry_builds), x + 1)
for build in retry_builds:
self.assertEqual(build.retry, True)
self.assertEqual(build.result, 'RETRY')
self.assertEqual(len(self.builds), 1, self.assertEqual(len(self.builds), 1,
'len of builds at x=%d is wrong' % x) 'len of builds at x=%d is wrong' % x)
self.builds[0].requeue = True self.builds[0].requeue = True

View File

@ -48,7 +48,8 @@ class MQTTReporter(BaseReporter):
'buildset': { 'buildset': {
'uuid': item.current_build_set.uuid, 'uuid': item.current_build_set.uuid,
'result': item.current_build_set.result, 'result': item.current_build_set.result,
'builds': [] 'builds': [],
'retry_builds': [],
}, },
'zuul_event_id': item.event.zuul_event_id, 'zuul_event_id': item.event.zuul_event_id,
} }
@ -70,6 +71,24 @@ class MQTTReporter(BaseReporter):
'result': result, 'result': result,
'dependencies': [j.name for j in job.dependencies], 'dependencies': [j.name for j in job.dependencies],
}) })
# Report build data of retried builds if available
retry_builds = item.current_build_set.getRetryBuildsForJob(
job.name)
for build in retry_builds:
(result, url) = item.formatJobResult(job, build)
retry_build_information = {
'job_name': job.name,
'voting': job.voting,
'uuid': build.uuid,
'start_time': build.start_time,
'end_time': build.end_time,
'execute_time': build.execute_time,
'log_url': url,
'result': result,
}
message['buildset']['retry_builds'].append(
retry_build_information)
message['buildset']['builds'].append(job_informations) message['buildset']['builds'].append(job_informations)
topic = None topic = None
try: try:

View File

@ -457,11 +457,9 @@ class ExecutorClient(object):
warnings = data.get('warnings', []) warnings = data.get('warnings', [])
log.info("Build complete, result %s, warnings %s", log.info("Build complete, result %s, warnings %s",
result, warnings) result, warnings)
# If the build should be retried, don't supply the result
# so that elsewhere we don't have to deal with keeping
# track of which results are non-final.
if build.retry: if build.retry:
result = None result = 'RETRY'
# If the build was canceled, we did actively cancel the job so # If the build was canceled, we did actively cancel the job so
# don't overwrite the result and don't retry. # don't overwrite the result and don't retry.

View File

@ -1035,7 +1035,7 @@ class PipelineManager(metaclass=ABCMeta):
self._resumeBuilds(build.build_set) self._resumeBuilds(build.build_set)
if (item.project_pipeline_config.fail_fast and if (item.project_pipeline_config.fail_fast and
build.failed and build.job.voting): build.failed and build.job.voting and not build.retry):
# If fail-fast is set and the build is not successful # If fail-fast is set and the build is not successful
# cancel all remaining jobs. # cancel all remaining jobs.
log.debug("Build %s failed and fail-fast enabled, canceling " log.debug("Build %s failed and fail-fast enabled, canceling "

View File

@ -1993,6 +1993,7 @@ class BuildSet(object):
def __init__(self, item): def __init__(self, item):
self.item = item self.item = item
self.builds = {} self.builds = {}
self.retry_builds = {}
self.result = None self.result = None
self.uuid = None self.uuid = None
self.commit = None self.commit = None
@ -2053,6 +2054,9 @@ class BuildSet(object):
self.tries[build.job.name] = 1 self.tries[build.job.name] = 1
build.build_set = self build.build_set = self
def addRetryBuild(self, build):
self.retry_builds.setdefault(build.job.name, []).append(build)
def removeBuild(self, build): def removeBuild(self, build):
if build.job.name not in self.builds: if build.job.name not in self.builds:
return return
@ -2067,6 +2071,9 @@ class BuildSet(object):
keys.sort() keys.sort()
return [self.builds.get(x) for x in keys] return [self.builds.get(x) for x in keys]
def getRetryBuildsForJob(self, job_name):
return self.retry_builds.get(job_name, [])
def getJobNodeSet(self, job_name: str) -> NodeSet: def getJobNodeSet(self, job_name: str) -> NodeSet:
# Return None if not provisioned; empty NodeSet if no nodes # Return None if not provisioned; empty NodeSet if no nodes
# required # required
@ -2185,6 +2192,9 @@ class QueueItem(object):
def addBuild(self, build): def addBuild(self, build):
self.current_build_set.addBuild(build) self.current_build_set.addBuild(build)
def addRetryBuild(self, build):
self.current_build_set.addRetryBuild(build)
def removeBuild(self, build): def removeBuild(self, build):
self.current_build_set.removeBuild(build) self.current_build_set.removeBuild(build)
@ -2641,6 +2651,7 @@ class QueueItem(object):
def setResult(self, build): def setResult(self, build):
if build.retry: if build.retry:
self.addRetryBuild(build)
self.removeBuild(build) self.removeBuild(build)
return return
@ -2749,16 +2760,17 @@ class QueueItem(object):
return url return url
def formatJobResult(self, job): def formatJobResult(self, job, build=None):
if (self.pipeline.tenant.report_build_page and if (self.pipeline.tenant.report_build_page and
self.pipeline.tenant.web_root): self.pipeline.tenant.web_root):
build = self.current_build_set.getBuild(job.name) if build is None:
build = self.current_build_set.getBuild(job.name)
pattern = urllib.parse.urljoin(self.pipeline.tenant.web_root, pattern = urllib.parse.urljoin(self.pipeline.tenant.web_root,
'build/{build.uuid}') 'build/{build.uuid}')
url = self.formatUrlPattern(pattern, job, build) url = self.formatUrlPattern(pattern, job, build)
return (build.result, url) return (build.result, url)
else: else:
return self.formatProvisionalJobResult(job) return self.formatProvisionalJobResult(job, build)
def formatStatusUrl(self): def formatStatusUrl(self):
# If we don't have a web root set, we can't format any url # If we don't have a web root set, we can't format any url
@ -2784,8 +2796,9 @@ class QueueItem(object):
) )
return self.formatUrlPattern(pattern) return self.formatUrlPattern(pattern)
def formatProvisionalJobResult(self, job): def formatProvisionalJobResult(self, job, build=None):
build = self.current_build_set.getBuild(job.name) if build is None:
build = self.current_build_set.getBuild(job.name)
result = build.result result = build.result
pattern = None pattern = None
if result == 'SUCCESS': if result == 'SUCCESS':