summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTristan Cacqueray <tdecacqu@redhat.com>2019-01-11 06:44:13 +0000
committerTristan Cacqueray <tdecacqu@redhat.com>2019-02-07 08:39:16 +0000
commitfd2ff6f0f6c27cc119c067cd520545eeaf303504 (patch)
treeabe31aaf8256a3a6fc9fbec1fbe7335f9a47aab2
parent9019158139c433679d82e88de23c248206aab9e3 (diff)
web: add /{tenant}/buildsets route
This change adds a /buildsets route to return the buildsets. Change-Id: I0f951267e4531c05ec31d1af62dc56ea0f96257f
Notes
Notes (review): Code-Review+2: Tobias Henkel <tobias.henkel@bmw.de> Code-Review+2: James E. Blair <corvus@inaugust.com> Workflow+1: James E. Blair <corvus@inaugust.com> Verified+2: Zuul Submitted-by: Zuul Submitted-at: Tue, 12 Feb 2019 02:12:44 +0000 Reviewed-on: https://review.openstack.org/630035 Project: openstack-infra/zuul Branch: refs/heads/master
-rwxr-xr-xtests/unit/test_web.py11
-rw-r--r--zuul/driver/sql/sqlconnection.py39
-rwxr-xr-xzuul/web/__init__.py116
3 files changed, 126 insertions, 40 deletions
diff --git a/tests/unit/test_web.py b/tests/unit/test_web.py
index e267734..29817a9 100755
--- a/tests/unit/test_web.py
+++ b/tests/unit/test_web.py
@@ -716,6 +716,17 @@ class TestBuildInfo(ZuulDBTestCase, BaseTestWeb):
716 resp = self.get_url("api/tenant/non-tenant/builds") 716 resp = self.get_url("api/tenant/non-tenant/builds")
717 self.assertEqual(404, resp.status_code) 717 self.assertEqual(404, resp.status_code)
718 718
719 def test_web_list_buildsets(self):
720 # Generate some build records in the db.
721 self.add_base_changes()
722 self.executor_server.hold_jobs_in_build = False
723 self.executor_server.release()
724 self.waitUntilSettled()
725
726 buildsets = self.get_url("api/tenant/tenant-one/buildsets").json()
727 self.assertEqual(2, len(buildsets))
728 self.assertEqual(3, len(buildsets[0]["builds"]))
729
719 730
720class TestArtifacts(ZuulDBTestCase, BaseTestWeb, AnsibleZuulTestCase): 731class TestArtifacts(ZuulDBTestCase, BaseTestWeb, AnsibleZuulTestCase):
721 config_file = 'zuul-sql-driver.conf' 732 config_file = 'zuul-sql-driver.conf'
diff --git a/zuul/driver/sql/sqlconnection.py b/zuul/driver/sql/sqlconnection.py
index 5cf79e8..212ef25 100644
--- a/zuul/driver/sql/sqlconnection.py
+++ b/zuul/driver/sql/sqlconnection.py
@@ -105,6 +105,40 @@ class DatabaseSession(object):
105 self.session().flush() 105 self.session().flush()
106 return bs 106 return bs
107 107
108 def getBuildsets(self, tenant=None, project=None, pipeline=None,
109 change=None, branch=None, patchset=None, ref=None,
110 newrev=None, uuid=None, job_name=None, result=None,
111 limit=50, offset=0):
112
113 build_table = self.connection.zuul_build_table
114 buildset_table = self.connection.zuul_buildset_table
115
116 q = self.session().query(self.connection.buildSetModel).\
117 join(self.connection.buildModel).\
118 options(orm.contains_eager(self.connection.buildSetModel.builds)).\
119 with_hint(buildset_table, 'USE INDEX (PRIMARY)', 'mysql')
120
121 q = self.listFilter(q, buildset_table.c.tenant, tenant)
122 q = self.listFilter(q, buildset_table.c.project, project)
123 q = self.listFilter(q, buildset_table.c.pipeline, pipeline)
124 q = self.listFilter(q, buildset_table.c.change, change)
125 q = self.listFilter(q, buildset_table.c.branch, branch)
126 q = self.listFilter(q, buildset_table.c.patchset, patchset)
127 q = self.listFilter(q, buildset_table.c.ref, ref)
128 q = self.listFilter(q, buildset_table.c.newrev, newrev)
129 q = self.listFilter(q, buildset_table.c.uuid, uuid)
130 q = self.listFilter(q, buildset_table.c.result, result)
131 q = self.listFilter(q, build_table.c.job_name, job_name)
132
133 q = q.order_by(buildset_table.c.id.desc()).\
134 limit(limit).\
135 offset(offset)
136
137 try:
138 return q.all()
139 except sqlalchemy.orm.exc.NoResultFound:
140 return []
141
108 142
109class SQLConnection(BaseConnection): 143class SQLConnection(BaseConnection):
110 driver_name = 'sql' 144 driver_name = 'sql'
@@ -277,6 +311,11 @@ class SQLConnection(BaseConnection):
277 with self.getSession() as db: 311 with self.getSession() as db:
278 return db.getBuilds(*args, **kw) 312 return db.getBuilds(*args, **kw)
279 313
314 def getBuildsets(self, *args, **kw):
315 """Return a list of BuildSet objects"""
316 with self.getSession() as db:
317 return db.getBuildsets(*args, **kw)
318
280 319
281def getSchema(): 320def getSchema():
282 sql_connection = voluptuous.Any(str, voluptuous.Schema(dict)) 321 sql_connection = voluptuous.Any(str, voluptuous.Schema(dict))
diff --git a/zuul/web/__init__.py b/zuul/web/__init__.py
index 3acf480..6670826 100755
--- a/zuul/web/__init__.py
+++ b/zuul/web/__init__.py
@@ -406,7 +406,7 @@ class ZuulWebAPI(object):
406 resp.headers['Content-Type'] = 'text/plain' 406 resp.headers['Content-Type'] = 'text/plain'
407 return job.data[0] + '\n' 407 return job.data[0] + '\n'
408 408
409 def buildToDict(self, build): 409 def buildToDict(self, build, buildset=None):
410 start_time = build.start_time 410 start_time = build.start_time
411 if build.start_time: 411 if build.start_time:
412 start_time = start_time.strftime( 412 start_time = start_time.strftime(
@@ -421,7 +421,6 @@ class ZuulWebAPI(object):
421 else: 421 else:
422 duration = None 422 duration = None
423 423
424 buildset = build.buildset
425 ret = { 424 ret = {
426 'uuid': build.uuid, 425 'uuid': build.uuid,
427 'job_name': build.job_name, 426 'job_name': build.job_name,
@@ -432,37 +431,34 @@ class ZuulWebAPI(object):
432 'voting': build.voting, 431 'voting': build.voting,
433 'log_url': build.log_url, 432 'log_url': build.log_url,
434 'node_name': build.node_name, 433 'node_name': build.node_name,
435
436 'project': buildset.project,
437 'branch': buildset.branch,
438 'pipeline': buildset.pipeline,
439 'change': buildset.change,
440 'patchset': buildset.patchset,
441 'ref': buildset.ref,
442 'newrev': buildset.newrev,
443 'ref_url': buildset.ref_url,
444 'artifacts': [],
445 'provides': [],
446 } 434 }
447 435
448 for artifact in build.artifacts: 436 if buildset:
449 ret['artifacts'].append({ 437 ret.update({
450 'name': artifact.name, 438 'project': buildset.project,
451 'url': artifact.url, 439 'branch': buildset.branch,
452 }) 440 'pipeline': buildset.pipeline,
453 for provides in build.provides: 441 'change': buildset.change,
454 ret['provides'].append({ 442 'patchset': buildset.patchset,
455 'name': artifact.name, 443 'ref': buildset.ref,
444 'newrev': buildset.newrev,
445 'ref_url': buildset.ref_url,
446 'artifacts': [],
447 'provides': [],
456 }) 448 })
449
450 for artifact in build.artifacts:
451 ret['artifacts'].append({
452 'name': artifact.name,
453 'url': artifact.url,
454 })
455 for provides in build.provides:
456 ret['provides'].append({
457 'name': artifact.name,
458 })
457 return ret 459 return ret
458 460
459 @cherrypy.expose 461 def _get_connection(self, tenant):
460 @cherrypy.tools.save_params()
461 @cherrypy.tools.json_out(content_type='application/json; charset=utf-8')
462 def builds(self, tenant, project=None, pipeline=None, change=None,
463 branch=None, patchset=None, ref=None, newrev=None,
464 uuid=None, job_name=None, voting=None, node_name=None,
465 result=None, limit=50, skip=0):
466 # Ask the scheduler which sql connection to use for this tenant 462 # Ask the scheduler which sql connection to use for this tenant
467 job = self.rpc.submitJob('zuul:tenant_sql_connection', 463 job = self.rpc.submitJob('zuul:tenant_sql_connection',
468 {'tenant': tenant}) 464 {'tenant': tenant})
@@ -471,7 +467,16 @@ class ZuulWebAPI(object):
471 if not connection_name: 467 if not connection_name:
472 raise cherrypy.HTTPError(404, 'Tenant %s does not exist.' % tenant) 468 raise cherrypy.HTTPError(404, 'Tenant %s does not exist.' % tenant)
473 469
474 connection = self.zuulweb.connections.connections[connection_name] 470 return self.zuulweb.connections.connections[connection_name]
471
472 @cherrypy.expose
473 @cherrypy.tools.save_params()
474 @cherrypy.tools.json_out(content_type='application/json; charset=utf-8')
475 def builds(self, tenant, project=None, pipeline=None, change=None,
476 branch=None, patchset=None, ref=None, newrev=None,
477 uuid=None, job_name=None, voting=None, node_name=None,
478 result=None, limit=50, skip=0):
479 connection = self._get_connection(tenant)
475 480
476 builds = connection.getBuilds( 481 builds = connection.getBuilds(
477 tenant=tenant, project=project, pipeline=pipeline, change=change, 482 tenant=tenant, project=project, pipeline=pipeline, change=change,
@@ -481,30 +486,59 @@ class ZuulWebAPI(object):
481 486
482 resp = cherrypy.response 487 resp = cherrypy.response
483 resp.headers['Access-Control-Allow-Origin'] = '*' 488 resp.headers['Access-Control-Allow-Origin'] = '*'
484 return [self.buildToDict(b) for b in builds] 489 return [self.buildToDict(b, b.buildset) for b in builds]
485 490
486 @cherrypy.expose 491 @cherrypy.expose
487 @cherrypy.tools.save_params() 492 @cherrypy.tools.save_params()
488 @cherrypy.tools.json_out(content_type='application/json; charset=utf-8') 493 @cherrypy.tools.json_out(content_type='application/json; charset=utf-8')
489 def build(self, tenant, uuid): 494 def build(self, tenant, uuid):
490 # Ask the scheduler which sql connection to use for this tenant 495 connection = self._get_connection(tenant)
491 job = self.rpc.submitJob('zuul:tenant_sql_connection',
492 {'tenant': tenant})
493 connection_name = json.loads(job.data[0])
494
495 if not connection_name:
496 raise cherrypy.HTTPError(404, 'Tenant %s does not exist.' % tenant)
497
498 connection = self.zuulweb.connections.connections[connection_name]
499 496
500 data = connection.getBuilds(tenant=tenant, uuid=uuid, limit=1) 497 data = connection.getBuilds(tenant=tenant, uuid=uuid, limit=1)
501 if not data: 498 if not data:
502 raise cherrypy.HTTPError(404, "Build not found") 499 raise cherrypy.HTTPError(404, "Build not found")
503 data = self.buildToDict(data[0]) 500 data = self.buildToDict(data[0], data[0].buildset)
504 resp = cherrypy.response 501 resp = cherrypy.response
505 resp.headers['Access-Control-Allow-Origin'] = '*' 502 resp.headers['Access-Control-Allow-Origin'] = '*'
506 return data 503 return data
507 504
505 def buildsetToDict(self, buildset):
506 ret = {
507 'uuid': buildset.uuid,
508 'result': buildset.result,
509 'message': buildset.message,
510 'project': buildset.project,
511 'branch': buildset.branch,
512 'pipeline': buildset.pipeline,
513 'change': buildset.change,
514 'patchset': buildset.patchset,
515 'ref': buildset.ref,
516 'newrev': buildset.newrev,
517 'ref_url': buildset.ref_url,
518 'builds': []
519 }
520 for build in buildset.builds:
521 ret["builds"].append(self.buildToDict(build))
522 return ret
523
524 @cherrypy.expose
525 @cherrypy.tools.save_params()
526 @cherrypy.tools.json_out(content_type='application/json; charset=utf-8')
527 def buildsets(self, tenant, project=None, pipeline=None, change=None,
528 branch=None, patchset=None, ref=None, newrev=None,
529 uuid=None, job_name=None, result=None, limit=50, skip=0):
530 connection = self._get_connection(tenant)
531
532 buildsets = connection.getBuildsets(
533 tenant=tenant, project=project, pipeline=pipeline, change=change,
534 branch=branch, patchset=patchset, ref=ref, newrev=newrev,
535 uuid=uuid, job_name=job_name, result=result,
536 limit=limit, offset=skip)
537
538 resp = cherrypy.response
539 resp.headers['Access-Control-Allow-Origin'] = '*'
540 return [self.buildsetToDict(b) for b in buildsets]
541
508 @cherrypy.expose 542 @cherrypy.expose
509 @cherrypy.tools.save_params() 543 @cherrypy.tools.save_params()
510 @cherrypy.tools.websocket(handler_cls=LogStreamHandler) 544 @cherrypy.tools.websocket(handler_cls=LogStreamHandler)
@@ -661,6 +695,8 @@ class ZuulWeb(object):
661 controller=api, action='builds') 695 controller=api, action='builds')
662 route_map.connect('api', '/api/tenant/{tenant}/build/{uuid}', 696 route_map.connect('api', '/api/tenant/{tenant}/build/{uuid}',
663 controller=api, action='build') 697 controller=api, action='build')
698 route_map.connect('api', '/api/tenant/{tenant}/buildsets',
699 controller=api, action='buildsets')
664 route_map.connect('api', '/api/tenant/{tenant}/config-errors', 700 route_map.connect('api', '/api/tenant/{tenant}/config-errors',
665 controller=api, action='config_errors') 701 controller=api, action='config_errors')
666 702