Merge "Add API endpoint to get frozen jobs"
This commit is contained in:
commit
65596aaafd
|
@ -710,6 +710,53 @@ class TestWeb(BaseTestWeb):
|
|||
job = self.get_url("api/tenant/tenant-one/job/noop").json()
|
||||
self.assertEqual("noop", job[0]["name"])
|
||||
|
||||
def test_freeze_jobs(self):
|
||||
# Test can get a list of the jobs for a given project+pipeline+branch.
|
||||
resp = self.get_url(
|
||||
"api/tenant/tenant-one/pipeline/check"
|
||||
"/project/org/project1/branch/master/freeze-jobs")
|
||||
|
||||
freeze_jobs = [{
|
||||
'name': 'project-merge',
|
||||
'dependencies': [],
|
||||
}, {
|
||||
'name': 'project-test1',
|
||||
'dependencies': [{
|
||||
'name': 'project-merge',
|
||||
'soft': False,
|
||||
}],
|
||||
}, {
|
||||
'name': 'project-test2',
|
||||
'dependencies': [{
|
||||
'name': 'project-merge',
|
||||
'soft': False,
|
||||
}],
|
||||
}, {
|
||||
'name': 'project1-project2-integration',
|
||||
'dependencies': [{
|
||||
'name': 'project-merge',
|
||||
'soft': False,
|
||||
}],
|
||||
}]
|
||||
self.assertEqual(freeze_jobs, resp.json())
|
||||
|
||||
def test_freeze_jobs_set_includes_all_jobs(self):
|
||||
# When freezing a job set we want to include all jobs even if they
|
||||
# have certain matcher requirements (such as required files) since we
|
||||
# can't otherwise evaluate them.
|
||||
|
||||
resp = self.get_url(
|
||||
"api/tenant/tenant-one/pipeline/gate"
|
||||
"/project/org/project/branch/master/freeze-jobs")
|
||||
expected = {
|
||||
'name': 'project-testfile',
|
||||
'dependencies': [{
|
||||
'name': 'project-merge',
|
||||
'soft': False,
|
||||
}],
|
||||
}
|
||||
self.assertIn(expected, resp.json())
|
||||
|
||||
|
||||
class TestWebSecrets(BaseTestWeb):
|
||||
tenant_config_file = 'config/secrets/main.yaml'
|
||||
|
|
|
@ -2079,7 +2079,7 @@ class QueueItem(object):
|
|||
def warning(self, msg):
|
||||
self.current_build_set.warning_messages.append(msg)
|
||||
|
||||
def freezeJobGraph(self):
|
||||
def freezeJobGraph(self, skip_file_matcher=False):
|
||||
"""Find or create actual matching jobs for this item's change and
|
||||
store the resulting job tree."""
|
||||
|
||||
|
@ -2091,7 +2091,8 @@ class QueueItem(object):
|
|||
if ppc:
|
||||
for msg in ppc.debug_messages:
|
||||
self.debug(msg)
|
||||
job_graph = self.layout.createJobGraph(self, ppc)
|
||||
job_graph = self.layout.createJobGraph(
|
||||
self, ppc, skip_file_matcher)
|
||||
for job in job_graph.getJobs():
|
||||
# Ensure that each jobs's dependencies are fully
|
||||
# accessible. This will raise an exception if not.
|
||||
|
@ -3782,7 +3783,7 @@ class Layout(object):
|
|||
raise NoMatchingParentError()
|
||||
return jobs
|
||||
|
||||
def _createJobGraph(self, item, ppc, job_graph):
|
||||
def _createJobGraph(self, item, ppc, job_graph, skip_file_matcher):
|
||||
job_list = ppc.job_list
|
||||
change = item.change
|
||||
pipeline = item.pipeline
|
||||
|
@ -3845,7 +3846,8 @@ class Layout(object):
|
|||
item.debug("No matching pipeline variants for {jobname}".
|
||||
format(jobname=jobname), indent=2)
|
||||
continue
|
||||
if not frozen_job.changeMatchesFiles(change):
|
||||
if not skip_file_matcher and \
|
||||
not frozen_job.changeMatchesFiles(change):
|
||||
self.log.debug("Job %s did not match files in %s",
|
||||
repr(frozen_job), change)
|
||||
item.debug("Job {jobname} did not match files".
|
||||
|
@ -3879,12 +3881,12 @@ class Layout(object):
|
|||
|
||||
job_graph.addJob(frozen_job)
|
||||
|
||||
def createJobGraph(self, item, ppc):
|
||||
def createJobGraph(self, item, ppc, skip_file_matcher=False):
|
||||
# NOTE(pabelanger): It is possible for a foreign project not to have a
|
||||
# configured pipeline, if so return an empty JobGraph.
|
||||
ret = JobGraph()
|
||||
if ppc:
|
||||
self._createJobGraph(item, ppc, ret)
|
||||
self._createJobGraph(item, ppc, ret, skip_file_matcher)
|
||||
return ret
|
||||
|
||||
|
||||
|
|
|
@ -75,6 +75,7 @@ class RPCListener(object):
|
|||
self.worker.registerFunction("zuul:job_list")
|
||||
self.worker.registerFunction("zuul:project_get")
|
||||
self.worker.registerFunction("zuul:project_list")
|
||||
self.worker.registerFunction("zuul:project_freeze_jobs")
|
||||
self.worker.registerFunction("zuul:pipeline_list")
|
||||
self.worker.registerFunction("zuul:key_get")
|
||||
self.worker.registerFunction("zuul:config_errors_list")
|
||||
|
@ -448,6 +449,37 @@ class RPCListener(object):
|
|||
job.sendWorkComplete(json.dumps(
|
||||
sorted(output, key=lambda project: project["name"])))
|
||||
|
||||
def handle_project_freeze_jobs(self, gear_job):
|
||||
args = json.loads(gear_job.arguments)
|
||||
tenant = self.sched.abide.tenants.get(args.get("tenant"))
|
||||
project = None
|
||||
pipeline = None
|
||||
if tenant:
|
||||
(trusted, project) = tenant.getProject(args.get("project"))
|
||||
pipeline = tenant.layout.pipelines.get(args.get("pipeline"))
|
||||
if not project or not pipeline:
|
||||
gear_job.sendWorkComplete(json.dumps(None))
|
||||
return
|
||||
|
||||
change = model.Branch(project)
|
||||
change.branch = args.get("branch", "master")
|
||||
queue = model.ChangeQueue(pipeline)
|
||||
item = model.QueueItem(queue, change)
|
||||
item.layout = tenant.layout
|
||||
item.freezeJobGraph(skip_file_matcher=True)
|
||||
|
||||
output = []
|
||||
|
||||
for job in item.job_graph.getJobs():
|
||||
job.setBase(tenant.layout)
|
||||
output.append({
|
||||
'name': job.name,
|
||||
'dependencies':
|
||||
list(map(lambda x: x.toDict(), job.dependencies)),
|
||||
})
|
||||
|
||||
gear_job.sendWorkComplete(json.dumps(output))
|
||||
|
||||
def handle_pipeline_list(self, job):
|
||||
args = json.loads(job.arguments)
|
||||
tenant = self.sched.abide.tenants.get(args.get("tenant"))
|
||||
|
|
|
@ -572,6 +572,26 @@ class ZuulWebAPI(object):
|
|||
def console_stream(self, tenant):
|
||||
cherrypy.request.ws_handler.zuulweb = self.zuulweb
|
||||
|
||||
@cherrypy.expose
|
||||
@cherrypy.tools.save_params()
|
||||
@cherrypy.tools.json_out(content_type='application/json; charset=utf-8')
|
||||
def project_freeze_jobs(self, tenant, pipeline, project, branch):
|
||||
job = self.rpc.submitJob(
|
||||
'zuul:project_freeze_jobs',
|
||||
{
|
||||
'tenant': tenant,
|
||||
'project': project,
|
||||
'pipeline': pipeline,
|
||||
'branch': branch
|
||||
}
|
||||
)
|
||||
ret = json.loads(job.data[0])
|
||||
if not ret:
|
||||
raise cherrypy.HTTPError(404)
|
||||
resp = cherrypy.response
|
||||
resp.headers['Access-Control-Allow-Origin'] = '*'
|
||||
return ret
|
||||
|
||||
|
||||
class StaticHandler(object):
|
||||
def __init__(self, root):
|
||||
|
@ -707,6 +727,12 @@ class ZuulWeb(object):
|
|||
controller=api, action='projects')
|
||||
route_map.connect('api', '/api/tenant/{tenant}/project/{project:.*}',
|
||||
controller=api, action='project')
|
||||
route_map.connect(
|
||||
'api',
|
||||
'/api/tenant/{tenant}/pipeline/{pipeline}'
|
||||
'/project/{project:.*}/branch/{branch:.*}/freeze-jobs',
|
||||
controller=api, action='project_freeze_jobs'
|
||||
)
|
||||
route_map.connect('api', '/api/tenant/{tenant}/pipelines',
|
||||
controller=api, action='pipelines')
|
||||
route_map.connect('api', '/api/tenant/{tenant}/labels',
|
||||
|
|
Loading…
Reference in New Issue