Use abide to look up jobs directly in zuul-web

This uses the abide in zuul-web to directly access the job
listing instead of asking the scheduler via RPC.

To allow encoding of MappingProxyTypes within cherrypy's response
handling, this implements a custom cherrypy JSON encoder based on
the ZuulJSONEncoder.

Relevant RPC listener methods are removed as they are not used
anywhere else.

Change-Id: I69d580876cdd290618e5c7b81c5fd0a35488cd12
This commit is contained in:
Felix Edel 2021-11-08 13:36:34 +01:00
parent bc3895e7e3
commit 509e8c292e
2 changed files with 77 additions and 70 deletions

View File

@ -143,8 +143,6 @@ class RPCListener(RPCListenerBase):
'autohold_list',
'get_running_jobs',
'tenant_list',
'job_get',
'job_list',
'project_get',
'project_list',
]
@ -258,57 +256,6 @@ class RPCListener(RPCListenerBase):
'queue': queue_size})
job.sendWorkComplete(json.dumps(output))
def handle_job_get(self, gear_job):
args = json.loads(gear_job.arguments)
tenant = self.sched.abide.tenants.get(args.get("tenant"))
if not tenant:
gear_job.sendWorkComplete(json.dumps(None))
return
jobs = tenant.layout.jobs.get(args.get("job"), [])
output = []
for job in jobs:
output.append(job.toDict(tenant))
gear_job.sendWorkComplete(json.dumps(output, cls=ZuulJSONEncoder))
def handle_job_list(self, job):
args = json.loads(job.arguments)
tenant = self.sched.abide.tenants.get(args.get("tenant"))
output = []
if not tenant:
job.sendWorkComplete(json.dumps(None))
for job_name in sorted(tenant.layout.jobs):
desc = None
tags = set()
variants = []
for variant in tenant.layout.jobs[job_name]:
if not desc and variant.description:
desc = variant.description.split('\n')[0]
if variant.tags:
tags.update(list(variant.tags))
job_variant = {}
if not variant.isBase():
if variant.parent:
job_variant['parent'] = str(variant.parent)
else:
job_variant['parent'] = tenant.default_base_job
branches = variant.getBranches()
if branches:
job_variant['branches'] = branches
if job_variant:
variants.append(job_variant)
job_output = {
"name": job_name,
}
if desc:
job_output["description"] = desc
if variants:
job_output["variants"] = variants
if tags:
job_output["tags"] = list(tags)
output.append(job_output)
job.sendWorkComplete(json.dumps(output))
def handle_project_get(self, gear_job):
args = json.loads(gear_job.arguments)
tenant = self.sched.abide.tenants.get(args["tenant"])

View File

@ -38,6 +38,7 @@ from zuul.connection import BaseConnection
import zuul.lib.repl
from zuul.lib import commandsocket, encryption, streamer_utils
from zuul.lib.ansible import AnsibleManager
from zuul.lib.jsonutil import ZuulJSONEncoder
from zuul.lib.keystorage import KeyStorage
from zuul.lib.re2util import filter_allowed_disallowed
from zuul.model import (
@ -130,6 +131,25 @@ cherrypy.tools.handle_options = cherrypy.Tool('on_start_resource',
handle_options)
# Custom JSONEncoder that combines the ZuulJSONEncoder with cherrypy's
# JSON functionality.
class ZuulWebJSONEncoder(ZuulJSONEncoder):
def iterencode(self, value):
# Adapted from cherrypy/_json.py
for chunk in super().iterencode(value):
yield chunk.encode("utf-8")
json_encoder = ZuulWebJSONEncoder()
def json_handler(*args, **kwargs):
# Adapted from cherrypy/lib/jsontools.py
value = cherrypy.serving.request._json_inner_handler(*args, **kwargs)
return json_encoder.iterencode(value)
class ChangeFilter(object):
def __init__(self, desired):
self.desired = desired
@ -1000,15 +1020,49 @@ class ZuulWebAPI(object):
@cherrypy.expose
@cherrypy.tools.save_params()
@cherrypy.tools.json_out(content_type='application/json; charset=utf-8')
def jobs(self, tenant):
job = self.rpc.submitJob('zuul:job_list', {'tenant': tenant})
ret = json.loads(job.data[0])
if ret is None:
raise cherrypy.HTTPError(404, 'Tenant %s does not exist.' % tenant)
@cherrypy.tools.json_out(
content_type='application/json; charset=utf-8', handler=json_handler,
)
def jobs(self, tenant_name):
tenant = self.zuulweb.abide.tenants.get(tenant_name)
if not tenant:
raise cherrypy.HTTPError(
404, f'Tenant {tenant_name} does not exist')
result = []
for job_name in sorted(tenant.layout.jobs):
desc = None
tags = set()
variants = []
for variant in tenant.layout.jobs[job_name]:
if not desc and variant.description:
desc = variant.description.split('\n')[0]
if variant.tags:
tags.update(list(variant.tags))
job_variant = {}
if not variant.isBase():
if variant.parent:
job_variant['parent'] = str(variant.parent)
else:
job_variant['parent'] = tenant.default_base_job
branches = variant.getBranches()
if branches:
job_variant['branches'] = branches
if job_variant:
variants.append(job_variant)
job_output = {"name": job_name}
if desc:
job_output["description"] = desc
if variants:
job_output["variants"] = variants
if tags:
job_output["tags"] = list(tags)
result.append(job_output)
resp = cherrypy.response
resp.headers['Access-Control-Allow-Origin'] = '*'
return ret
return result
@cherrypy.expose
@cherrypy.tools.save_params()
@ -1026,16 +1080,22 @@ class ZuulWebAPI(object):
@cherrypy.expose
@cherrypy.tools.save_params()
@cherrypy.tools.json_out(content_type='application/json; charset=utf-8')
def job(self, tenant, job_name):
job = self.rpc.submitJob(
'zuul:job_get', {'tenant': tenant, 'job': job_name})
ret = json.loads(job.data[0])
if not ret:
raise cherrypy.HTTPError(404, 'Job %s does not exist.' % job_name)
@cherrypy.tools.json_out(
content_type='application/json; charset=utf-8', handler=json_handler)
def job(self, tenant_name, job_name):
tenant = self.zuulweb.abide.tenants.get(tenant_name)
if not tenant:
raise cherrypy.HTTPError(
404, f'Tenant {tenant_name} does not exist')
job_variants = tenant.layout.jobs.get(job_name)
result = []
for job in job_variants:
result.append(job.toDict(tenant))
resp = cherrypy.response
resp.headers['Access-Control-Allow-Origin'] = '*'
return ret
return result
@cherrypy.expose
@cherrypy.tools.save_params()
@ -1633,9 +1693,9 @@ class ZuulWeb(object):
controller=api, action='status')
route_map.connect('api', '/api/tenant/{tenant}/status/change/{change}',
controller=api, action='status_change')
route_map.connect('api', '/api/tenant/{tenant}/jobs',
route_map.connect('api', '/api/tenant/{tenant_name}/jobs',
controller=api, action='jobs')
route_map.connect('api', '/api/tenant/{tenant}/job/{job_name}',
route_map.connect('api', '/api/tenant/{tenant_name}/job/{job_name}',
controller=api, action='job')
# if no auth configured, deactivate admin routes
if self.authenticators.authenticators: