Browse Source

Merge "web: add /{tenant}/buildset/{uuid} route"

tags/3.6.0
Zuul 2 months ago
parent
commit
875bb95363
3 changed files with 112 additions and 15 deletions
  1. 46
    0
      tests/unit/test_web.py
  2. 31
    0
      zuul/driver/sql/sqlconnection.py
  3. 35
    15
      zuul/web/__init__.py

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

@@ -815,6 +815,23 @@ class TestBuildInfo(ZuulDBTestCase, BaseTestWeb):
815 815
 
816 816
         buildsets = self.get_url("api/tenant/tenant-one/buildsets").json()
817 817
         self.assertEqual(2, len(buildsets))
818
+        project_bs = [x for x in buildsets if x["project"] == "org/project"][0]
819
+
820
+        buildset = self.get_url(
821
+            "api/tenant/tenant-one/buildset/%s" % project_bs['uuid']).json()
822
+        self.assertEqual(3, len(buildset["builds"]))
823
+
824
+        project_test1_build = [x for x in buildset["builds"]
825
+                               if x["job_name"] == "project-test1"][0]
826
+        self.assertEqual('SUCCESS', project_test1_build['result'])
827
+
828
+        project_test2_build = [x for x in buildset["builds"]
829
+                               if x["job_name"] == "project-test2"][0]
830
+        self.assertEqual('SUCCESS', project_test2_build['result'])
831
+
832
+        project_merge_build = [x for x in buildset["builds"]
833
+                               if x["job_name"] == "project-merge"][0]
834
+        self.assertEqual('SUCCESS', project_merge_build['result'])
818 835
 
819 836
 
820 837
 class TestArtifacts(ZuulDBTestCase, BaseTestWeb, AnsibleZuulTestCase):
@@ -844,3 +861,32 @@ class TestArtifacts(ZuulDBTestCase, BaseTestWeb, AnsibleZuulTestCase):
844 861
             {'url': 'http://example.com/tarball',
845 862
              'name': 'tarball'},
846 863
         ])
864
+
865
+    def test_buildset_artifacts(self):
866
+        self.add_base_changes()
867
+        self.executor_server.hold_jobs_in_build = False
868
+        self.executor_server.release()
869
+        self.waitUntilSettled()
870
+
871
+        buildsets = self.get_url("api/tenant/tenant-one/buildsets").json()
872
+        project_bs = [x for x in buildsets if x["project"] == "org/project"][0]
873
+        buildset = self.get_url(
874
+            "api/tenant/tenant-one/buildset/%s" % project_bs['uuid']).json()
875
+        print("X" * 120)
876
+        print(buildset)
877
+        print("Y" * 120)
878
+        self.assertEqual(3, len(buildset["builds"]))
879
+
880
+        test1_build = [x for x in buildset["builds"]
881
+                       if x["job_name"] == "project-test1"][0]
882
+        arts = test1_build['artifacts']
883
+        arts.sort(key=lambda x: x['name'])
884
+        self.assertEqual([
885
+            {'url': 'http://example.com/docs',
886
+             'name': 'docs'},
887
+            {'url': 'http://logs.example.com/build/relative/docs',
888
+             'name': 'relative',
889
+             'metadata': {'foo': 'bar'}},
890
+            {'url': 'http://example.com/tarball',
891
+             'name': 'tarball'},
892
+        ], test1_build['artifacts'])

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

@@ -31,6 +31,9 @@ PROVIDES_TABLE = 'zuul_provides'
31 31
 
32 32
 
33 33
 class DatabaseSession(object):
34
+
35
+    log = logging.getLogger("zuul.DatabaseSession")
36
+
34 37
     def __init__(self, connection):
35 38
         self.connection = connection
36 39
         self.session = connection.session
@@ -134,6 +137,29 @@ class DatabaseSession(object):
134 137
         except sqlalchemy.orm.exc.NoResultFound:
135 138
             return []
136 139
 
140
+    def getBuildset(self, tenant, uuid):
141
+        """Get one buildset with its builds"""
142
+
143
+        buildset_table = self.connection.zuul_buildset_table
144
+
145
+        q = self.session().query(self.connection.buildSetModel).\
146
+            options(orm.joinedload(self.connection.buildSetModel.builds).
147
+                    subqueryload(self.connection.buildModel.artifacts)).\
148
+            options(orm.joinedload(self.connection.buildSetModel.builds).
149
+                    subqueryload(self.connection.buildModel.provides)).\
150
+            with_hint(buildset_table, 'USE INDEX (PRIMARY)', 'mysql')
151
+
152
+        q = self.listFilter(q, buildset_table.c.tenant, tenant)
153
+        q = self.listFilter(q, buildset_table.c.uuid, uuid)
154
+
155
+        try:
156
+            return q.one()
157
+        except sqlalchemy.orm.exc.NoResultFound:
158
+            return None
159
+        except sqlalchemy.orm.exc.MultipleResultsFound:
160
+            self.log.error("Multiple buildset found with uuid %s", uuid)
161
+            return None
162
+
137 163
 
138 164
 class SQLConnection(BaseConnection):
139 165
     driver_name = 'sql'
@@ -319,3 +345,8 @@ class SQLConnection(BaseConnection):
319 345
         """Return a list of BuildSet objects"""
320 346
         with self.getSession() as db:
321 347
             return db.getBuildsets(*args, **kw)
348
+
349
+    def getBuildset(self, *args, **kw):
350
+        """Return a BuildSet objects"""
351
+        with self.getSession() as db:
352
+            return db.getBuildset(*args, **kw)

+ 35
- 15
zuul/web/__init__.py View File

@@ -431,6 +431,8 @@ class ZuulWebAPI(object):
431 431
             'voting': build.voting,
432 432
             'log_url': build.log_url,
433 433
             'node_name': build.node_name,
434
+            'artifacts': [],
435
+            'provides': [],
434 436
         }
435 437
 
436 438
         if buildset:
@@ -443,22 +445,20 @@ class ZuulWebAPI(object):
443 445
                 'ref': buildset.ref,
444 446
                 'newrev': buildset.newrev,
445 447
                 'ref_url': buildset.ref_url,
446
-                'artifacts': [],
447
-                'provides': [],
448 448
             })
449 449
 
450
-            for artifact in build.artifacts:
451
-                art = {
452
-                    'name': artifact.name,
453
-                    'url': artifact.url,
454
-                }
455
-                if artifact.meta:
456
-                    art['metadata'] = json.loads(artifact.meta)
457
-                ret['artifacts'].append(art)
458
-            for provides in build.provides:
459
-                ret['provides'].append({
460
-                    'name': provides.name,
461
-                })
450
+        for artifact in build.artifacts:
451
+            art = {
452
+                'name': artifact.name,
453
+                'url': artifact.url,
454
+            }
455
+            if artifact.meta:
456
+                art['metadata'] = json.loads(artifact.meta)
457
+            ret['artifacts'].append(art)
458
+        for provides in build.provides:
459
+            ret['provides'].append({
460
+                'name': provides.name,
461
+            })
462 462
         return ret
463 463
 
464 464
     def _get_connection(self, tenant):
@@ -505,7 +505,7 @@ class ZuulWebAPI(object):
505 505
         resp.headers['Access-Control-Allow-Origin'] = '*'
506 506
         return data
507 507
 
508
-    def buildsetToDict(self, buildset):
508
+    def buildsetToDict(self, buildset, builds=[]):
509 509
         ret = {
510 510
             'uuid': buildset.uuid,
511 511
             'result': buildset.result,
@@ -519,6 +519,10 @@ class ZuulWebAPI(object):
519 519
             'newrev': buildset.newrev,
520 520
             'ref_url': buildset.ref_url,
521 521
         }
522
+        if builds:
523
+            ret['builds'] = []
524
+        for build in builds:
525
+            ret['builds'].append(self.buildToDict(build))
522 526
         return ret
523 527
 
524 528
     @cherrypy.expose
@@ -539,6 +543,20 @@ class ZuulWebAPI(object):
539 543
         resp.headers['Access-Control-Allow-Origin'] = '*'
540 544
         return [self.buildsetToDict(b) for b in buildsets]
541 545
 
546
+    @cherrypy.expose
547
+    @cherrypy.tools.save_params()
548
+    @cherrypy.tools.json_out(content_type='application/json; charset=utf-8')
549
+    def buildset(self, tenant, uuid):
550
+        connection = self._get_connection(tenant)
551
+
552
+        data = connection.getBuildset(tenant, uuid)
553
+        if not data:
554
+            raise cherrypy.HTTPError(404, "Buildset not found")
555
+        data = self.buildsetToDict(data, data.builds)
556
+        resp = cherrypy.response
557
+        resp.headers['Access-Control-Allow-Origin'] = '*'
558
+        return data
559
+
542 560
     @cherrypy.expose
543 561
     @cherrypy.tools.save_params()
544 562
     @cherrypy.tools.websocket(handler_cls=LogStreamHandler)
@@ -697,6 +715,8 @@ class ZuulWeb(object):
697 715
                           controller=api, action='build')
698 716
         route_map.connect('api', '/api/tenant/{tenant}/buildsets',
699 717
                           controller=api, action='buildsets')
718
+        route_map.connect('api', '/api/tenant/{tenant}/buildset/{uuid}',
719
+                          controller=api, action='buildset')
700 720
         route_map.connect('api', '/api/tenant/{tenant}/config-errors',
701 721
                           controller=api, action='config_errors')
702 722
 

Loading…
Cancel
Save