Merge "web: add /{tenant}/buildsets route"
This commit is contained in:
commit
dcb801aa8e
|
@ -716,6 +716,17 @@ class TestBuildInfo(ZuulDBTestCase, BaseTestWeb):
|
||||||
resp = self.get_url("api/tenant/non-tenant/builds")
|
resp = self.get_url("api/tenant/non-tenant/builds")
|
||||||
self.assertEqual(404, resp.status_code)
|
self.assertEqual(404, resp.status_code)
|
||||||
|
|
||||||
|
def test_web_list_buildsets(self):
|
||||||
|
# Generate some build records in the db.
|
||||||
|
self.add_base_changes()
|
||||||
|
self.executor_server.hold_jobs_in_build = False
|
||||||
|
self.executor_server.release()
|
||||||
|
self.waitUntilSettled()
|
||||||
|
|
||||||
|
buildsets = self.get_url("api/tenant/tenant-one/buildsets").json()
|
||||||
|
self.assertEqual(2, len(buildsets))
|
||||||
|
self.assertEqual(3, len(buildsets[0]["builds"]))
|
||||||
|
|
||||||
|
|
||||||
class TestArtifacts(ZuulDBTestCase, BaseTestWeb, AnsibleZuulTestCase):
|
class TestArtifacts(ZuulDBTestCase, BaseTestWeb, AnsibleZuulTestCase):
|
||||||
config_file = 'zuul-sql-driver.conf'
|
config_file = 'zuul-sql-driver.conf'
|
||||||
|
|
|
@ -105,6 +105,40 @@ class DatabaseSession(object):
|
||||||
self.session().flush()
|
self.session().flush()
|
||||||
return bs
|
return bs
|
||||||
|
|
||||||
|
def getBuildsets(self, tenant=None, project=None, pipeline=None,
|
||||||
|
change=None, branch=None, patchset=None, ref=None,
|
||||||
|
newrev=None, uuid=None, job_name=None, result=None,
|
||||||
|
limit=50, offset=0):
|
||||||
|
|
||||||
|
build_table = self.connection.zuul_build_table
|
||||||
|
buildset_table = self.connection.zuul_buildset_table
|
||||||
|
|
||||||
|
q = self.session().query(self.connection.buildSetModel).\
|
||||||
|
join(self.connection.buildModel).\
|
||||||
|
options(orm.contains_eager(self.connection.buildSetModel.builds)).\
|
||||||
|
with_hint(buildset_table, 'USE INDEX (PRIMARY)', 'mysql')
|
||||||
|
|
||||||
|
q = self.listFilter(q, buildset_table.c.tenant, tenant)
|
||||||
|
q = self.listFilter(q, buildset_table.c.project, project)
|
||||||
|
q = self.listFilter(q, buildset_table.c.pipeline, pipeline)
|
||||||
|
q = self.listFilter(q, buildset_table.c.change, change)
|
||||||
|
q = self.listFilter(q, buildset_table.c.branch, branch)
|
||||||
|
q = self.listFilter(q, buildset_table.c.patchset, patchset)
|
||||||
|
q = self.listFilter(q, buildset_table.c.ref, ref)
|
||||||
|
q = self.listFilter(q, buildset_table.c.newrev, newrev)
|
||||||
|
q = self.listFilter(q, buildset_table.c.uuid, uuid)
|
||||||
|
q = self.listFilter(q, buildset_table.c.result, result)
|
||||||
|
q = self.listFilter(q, build_table.c.job_name, job_name)
|
||||||
|
|
||||||
|
q = q.order_by(buildset_table.c.id.desc()).\
|
||||||
|
limit(limit).\
|
||||||
|
offset(offset)
|
||||||
|
|
||||||
|
try:
|
||||||
|
return q.all()
|
||||||
|
except sqlalchemy.orm.exc.NoResultFound:
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
class SQLConnection(BaseConnection):
|
class SQLConnection(BaseConnection):
|
||||||
driver_name = 'sql'
|
driver_name = 'sql'
|
||||||
|
@ -286,6 +320,11 @@ class SQLConnection(BaseConnection):
|
||||||
with self.getSession() as db:
|
with self.getSession() as db:
|
||||||
return db.getBuilds(*args, **kw)
|
return db.getBuilds(*args, **kw)
|
||||||
|
|
||||||
|
def getBuildsets(self, *args, **kw):
|
||||||
|
"""Return a list of BuildSet objects"""
|
||||||
|
with self.getSession() as db:
|
||||||
|
return db.getBuildsets(*args, **kw)
|
||||||
|
|
||||||
|
|
||||||
def getSchema():
|
def getSchema():
|
||||||
sql_connection = voluptuous.Any(str, voluptuous.Schema(dict))
|
sql_connection = voluptuous.Any(str, voluptuous.Schema(dict))
|
||||||
|
|
|
@ -406,7 +406,7 @@ class ZuulWebAPI(object):
|
||||||
resp.headers['Content-Type'] = 'text/plain'
|
resp.headers['Content-Type'] = 'text/plain'
|
||||||
return job.data[0] + '\n'
|
return job.data[0] + '\n'
|
||||||
|
|
||||||
def buildToDict(self, build):
|
def buildToDict(self, build, buildset=None):
|
||||||
start_time = build.start_time
|
start_time = build.start_time
|
||||||
if build.start_time:
|
if build.start_time:
|
||||||
start_time = start_time.strftime(
|
start_time = start_time.strftime(
|
||||||
|
@ -421,7 +421,6 @@ class ZuulWebAPI(object):
|
||||||
else:
|
else:
|
||||||
duration = None
|
duration = None
|
||||||
|
|
||||||
buildset = build.buildset
|
|
||||||
ret = {
|
ret = {
|
||||||
'uuid': build.uuid,
|
'uuid': build.uuid,
|
||||||
'job_name': build.job_name,
|
'job_name': build.job_name,
|
||||||
|
@ -432,37 +431,34 @@ class ZuulWebAPI(object):
|
||||||
'voting': build.voting,
|
'voting': build.voting,
|
||||||
'log_url': build.log_url,
|
'log_url': build.log_url,
|
||||||
'node_name': build.node_name,
|
'node_name': build.node_name,
|
||||||
|
|
||||||
'project': buildset.project,
|
|
||||||
'branch': buildset.branch,
|
|
||||||
'pipeline': buildset.pipeline,
|
|
||||||
'change': buildset.change,
|
|
||||||
'patchset': buildset.patchset,
|
|
||||||
'ref': buildset.ref,
|
|
||||||
'newrev': buildset.newrev,
|
|
||||||
'ref_url': buildset.ref_url,
|
|
||||||
'artifacts': [],
|
|
||||||
'provides': [],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for artifact in build.artifacts:
|
if buildset:
|
||||||
ret['artifacts'].append({
|
ret.update({
|
||||||
'name': artifact.name,
|
'project': buildset.project,
|
||||||
'url': artifact.url,
|
'branch': buildset.branch,
|
||||||
})
|
'pipeline': buildset.pipeline,
|
||||||
for provides in build.provides:
|
'change': buildset.change,
|
||||||
ret['provides'].append({
|
'patchset': buildset.patchset,
|
||||||
'name': artifact.name,
|
'ref': buildset.ref,
|
||||||
|
'newrev': buildset.newrev,
|
||||||
|
'ref_url': buildset.ref_url,
|
||||||
|
'artifacts': [],
|
||||||
|
'provides': [],
|
||||||
})
|
})
|
||||||
|
|
||||||
|
for artifact in build.artifacts:
|
||||||
|
ret['artifacts'].append({
|
||||||
|
'name': artifact.name,
|
||||||
|
'url': artifact.url,
|
||||||
|
})
|
||||||
|
for provides in build.provides:
|
||||||
|
ret['provides'].append({
|
||||||
|
'name': artifact.name,
|
||||||
|
})
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
@cherrypy.expose
|
def _get_connection(self, tenant):
|
||||||
@cherrypy.tools.save_params()
|
|
||||||
@cherrypy.tools.json_out(content_type='application/json; charset=utf-8')
|
|
||||||
def builds(self, tenant, project=None, pipeline=None, change=None,
|
|
||||||
branch=None, patchset=None, ref=None, newrev=None,
|
|
||||||
uuid=None, job_name=None, voting=None, node_name=None,
|
|
||||||
result=None, limit=50, skip=0):
|
|
||||||
# Ask the scheduler which sql connection to use for this tenant
|
# Ask the scheduler which sql connection to use for this tenant
|
||||||
job = self.rpc.submitJob('zuul:tenant_sql_connection',
|
job = self.rpc.submitJob('zuul:tenant_sql_connection',
|
||||||
{'tenant': tenant})
|
{'tenant': tenant})
|
||||||
|
@ -471,7 +467,16 @@ class ZuulWebAPI(object):
|
||||||
if not connection_name:
|
if not connection_name:
|
||||||
raise cherrypy.HTTPError(404, 'Tenant %s does not exist.' % tenant)
|
raise cherrypy.HTTPError(404, 'Tenant %s does not exist.' % tenant)
|
||||||
|
|
||||||
connection = self.zuulweb.connections.connections[connection_name]
|
return self.zuulweb.connections.connections[connection_name]
|
||||||
|
|
||||||
|
@cherrypy.expose
|
||||||
|
@cherrypy.tools.save_params()
|
||||||
|
@cherrypy.tools.json_out(content_type='application/json; charset=utf-8')
|
||||||
|
def builds(self, tenant, project=None, pipeline=None, change=None,
|
||||||
|
branch=None, patchset=None, ref=None, newrev=None,
|
||||||
|
uuid=None, job_name=None, voting=None, node_name=None,
|
||||||
|
result=None, limit=50, skip=0):
|
||||||
|
connection = self._get_connection(tenant)
|
||||||
|
|
||||||
builds = connection.getBuilds(
|
builds = connection.getBuilds(
|
||||||
tenant=tenant, project=project, pipeline=pipeline, change=change,
|
tenant=tenant, project=project, pipeline=pipeline, change=change,
|
||||||
|
@ -481,30 +486,59 @@ class ZuulWebAPI(object):
|
||||||
|
|
||||||
resp = cherrypy.response
|
resp = cherrypy.response
|
||||||
resp.headers['Access-Control-Allow-Origin'] = '*'
|
resp.headers['Access-Control-Allow-Origin'] = '*'
|
||||||
return [self.buildToDict(b) for b in builds]
|
return [self.buildToDict(b, b.buildset) for b in builds]
|
||||||
|
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
@cherrypy.tools.save_params()
|
@cherrypy.tools.save_params()
|
||||||
@cherrypy.tools.json_out(content_type='application/json; charset=utf-8')
|
@cherrypy.tools.json_out(content_type='application/json; charset=utf-8')
|
||||||
def build(self, tenant, uuid):
|
def build(self, tenant, uuid):
|
||||||
# Ask the scheduler which sql connection to use for this tenant
|
connection = self._get_connection(tenant)
|
||||||
job = self.rpc.submitJob('zuul:tenant_sql_connection',
|
|
||||||
{'tenant': tenant})
|
|
||||||
connection_name = json.loads(job.data[0])
|
|
||||||
|
|
||||||
if not connection_name:
|
|
||||||
raise cherrypy.HTTPError(404, 'Tenant %s does not exist.' % tenant)
|
|
||||||
|
|
||||||
connection = self.zuulweb.connections.connections[connection_name]
|
|
||||||
|
|
||||||
data = connection.getBuilds(tenant=tenant, uuid=uuid, limit=1)
|
data = connection.getBuilds(tenant=tenant, uuid=uuid, limit=1)
|
||||||
if not data:
|
if not data:
|
||||||
raise cherrypy.HTTPError(404, "Build not found")
|
raise cherrypy.HTTPError(404, "Build not found")
|
||||||
data = self.buildToDict(data[0])
|
data = self.buildToDict(data[0], data[0].buildset)
|
||||||
resp = cherrypy.response
|
resp = cherrypy.response
|
||||||
resp.headers['Access-Control-Allow-Origin'] = '*'
|
resp.headers['Access-Control-Allow-Origin'] = '*'
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
def buildsetToDict(self, buildset):
|
||||||
|
ret = {
|
||||||
|
'uuid': buildset.uuid,
|
||||||
|
'result': buildset.result,
|
||||||
|
'message': buildset.message,
|
||||||
|
'project': buildset.project,
|
||||||
|
'branch': buildset.branch,
|
||||||
|
'pipeline': buildset.pipeline,
|
||||||
|
'change': buildset.change,
|
||||||
|
'patchset': buildset.patchset,
|
||||||
|
'ref': buildset.ref,
|
||||||
|
'newrev': buildset.newrev,
|
||||||
|
'ref_url': buildset.ref_url,
|
||||||
|
'builds': []
|
||||||
|
}
|
||||||
|
for build in buildset.builds:
|
||||||
|
ret["builds"].append(self.buildToDict(build))
|
||||||
|
return ret
|
||||||
|
|
||||||
|
@cherrypy.expose
|
||||||
|
@cherrypy.tools.save_params()
|
||||||
|
@cherrypy.tools.json_out(content_type='application/json; charset=utf-8')
|
||||||
|
def buildsets(self, tenant, project=None, pipeline=None, change=None,
|
||||||
|
branch=None, patchset=None, ref=None, newrev=None,
|
||||||
|
uuid=None, job_name=None, result=None, limit=50, skip=0):
|
||||||
|
connection = self._get_connection(tenant)
|
||||||
|
|
||||||
|
buildsets = connection.getBuildsets(
|
||||||
|
tenant=tenant, project=project, pipeline=pipeline, change=change,
|
||||||
|
branch=branch, patchset=patchset, ref=ref, newrev=newrev,
|
||||||
|
uuid=uuid, job_name=job_name, result=result,
|
||||||
|
limit=limit, offset=skip)
|
||||||
|
|
||||||
|
resp = cherrypy.response
|
||||||
|
resp.headers['Access-Control-Allow-Origin'] = '*'
|
||||||
|
return [self.buildsetToDict(b) for b in buildsets]
|
||||||
|
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
@cherrypy.tools.save_params()
|
@cherrypy.tools.save_params()
|
||||||
@cherrypy.tools.websocket(handler_cls=LogStreamHandler)
|
@cherrypy.tools.websocket(handler_cls=LogStreamHandler)
|
||||||
|
@ -661,6 +695,8 @@ class ZuulWeb(object):
|
||||||
controller=api, action='builds')
|
controller=api, action='builds')
|
||||||
route_map.connect('api', '/api/tenant/{tenant}/build/{uuid}',
|
route_map.connect('api', '/api/tenant/{tenant}/build/{uuid}',
|
||||||
controller=api, action='build')
|
controller=api, action='build')
|
||||||
|
route_map.connect('api', '/api/tenant/{tenant}/buildsets',
|
||||||
|
controller=api, action='buildsets')
|
||||||
route_map.connect('api', '/api/tenant/{tenant}/config-errors',
|
route_map.connect('api', '/api/tenant/{tenant}/config-errors',
|
||||||
controller=api, action='config_errors')
|
controller=api, action='config_errors')
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue