Browse Source

Merge "web: add /{tenant}/buildsets route"

Zuul 2 months ago
parent
commit
dcb801aa8e
3 changed files with 126 additions and 40 deletions
  1. 11
    0
      tests/unit/test_web.py
  2. 39
    0
      zuul/driver/sql/sqlconnection.py
  3. 76
    40
      zuul/web/__init__.py

+ 11
- 0
tests/unit/test_web.py View File

@@ -716,6 +716,17 @@ class TestBuildInfo(ZuulDBTestCase, BaseTestWeb):
716 716
         resp = self.get_url("api/tenant/non-tenant/builds")
717 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
 
720 731
 class TestArtifacts(ZuulDBTestCase, BaseTestWeb, AnsibleZuulTestCase):
721 732
     config_file = 'zuul-sql-driver.conf'

+ 39
- 0
zuul/driver/sql/sqlconnection.py View File

@@ -105,6 +105,40 @@ class DatabaseSession(object):
105 105
         self.session().flush()
106 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
 
109 143
 class SQLConnection(BaseConnection):
110 144
     driver_name = 'sql'
@@ -286,6 +320,11 @@ class SQLConnection(BaseConnection):
286 320
         with self.getSession() as db:
287 321
             return db.getBuilds(*args, **kw)
288 322
 
323
+    def getBuildsets(self, *args, **kw):
324
+        """Return a list of BuildSet objects"""
325
+        with self.getSession() as db:
326
+            return db.getBuildsets(*args, **kw)
327
+
289 328
 
290 329
 def getSchema():
291 330
     sql_connection = voluptuous.Any(str, voluptuous.Schema(dict))

+ 76
- 40
zuul/web/__init__.py View File

@@ -406,7 +406,7 @@ class ZuulWebAPI(object):
406 406
         resp.headers['Content-Type'] = 'text/plain'
407 407
         return job.data[0] + '\n'
408 408
 
409
-    def buildToDict(self, build):
409
+    def buildToDict(self, build, buildset=None):
410 410
         start_time = build.start_time
411 411
         if build.start_time:
412 412
             start_time = start_time.strftime(
@@ -421,7 +421,6 @@ class ZuulWebAPI(object):
421 421
         else:
422 422
             duration = None
423 423
 
424
-        buildset = build.buildset
425 424
         ret = {
426 425
             'uuid': build.uuid,
427 426
             'job_name': build.job_name,
@@ -432,37 +431,34 @@ class ZuulWebAPI(object):
432 431
             'voting': build.voting,
433 432
             'log_url': build.log_url,
434 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:
449
-            ret['artifacts'].append({
450
-                'name': artifact.name,
451
-                'url': artifact.url,
452
-            })
453
-        for provides in build.provides:
454
-            ret['provides'].append({
455
-                'name': artifact.name,
436
+        if buildset:
437
+            ret.update({
438
+                'project': buildset.project,
439
+                'branch': buildset.branch,
440
+                'pipeline': buildset.pipeline,
441
+                'change': buildset.change,
442
+                'patchset': buildset.patchset,
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 459
         return ret
458 460
 
459
-    @cherrypy.expose
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):
461
+    def _get_connection(self, tenant):
466 462
         # Ask the scheduler which sql connection to use for this tenant
467 463
         job = self.rpc.submitJob('zuul:tenant_sql_connection',
468 464
                                  {'tenant': tenant})
@@ -471,7 +467,16 @@ class ZuulWebAPI(object):
471 467
         if not connection_name:
472 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 481
         builds = connection.getBuilds(
477 482
             tenant=tenant, project=project, pipeline=pipeline, change=change,
@@ -481,30 +486,59 @@ class ZuulWebAPI(object):
481 486
 
482 487
         resp = cherrypy.response
483 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 491
     @cherrypy.expose
487 492
     @cherrypy.tools.save_params()
488 493
     @cherrypy.tools.json_out(content_type='application/json; charset=utf-8')
489 494
     def build(self, tenant, uuid):
490
-        # Ask the scheduler which sql connection to use for this 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]
495
+        connection = self._get_connection(tenant)
499 496
 
500 497
         data = connection.getBuilds(tenant=tenant, uuid=uuid, limit=1)
501 498
         if not data:
502 499
             raise cherrypy.HTTPError(404, "Build not found")
503
-        data = self.buildToDict(data[0])
500
+        data = self.buildToDict(data[0], data[0].buildset)
504 501
         resp = cherrypy.response
505 502
         resp.headers['Access-Control-Allow-Origin'] = '*'
506 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 542
     @cherrypy.expose
509 543
     @cherrypy.tools.save_params()
510 544
     @cherrypy.tools.websocket(handler_cls=LogStreamHandler)
@@ -661,6 +695,8 @@ class ZuulWeb(object):
661 695
                           controller=api, action='builds')
662 696
         route_map.connect('api', '/api/tenant/{tenant}/build/{uuid}',
663 697
                           controller=api, action='build')
698
+        route_map.connect('api', '/api/tenant/{tenant}/buildsets',
699
+                          controller=api, action='buildsets')
664 700
         route_map.connect('api', '/api/tenant/{tenant}/config-errors',
665 701
                           controller=api, action='config_errors')
666 702
 

Loading…
Cancel
Save