Merge lp:~cjwatson/launchpad/snap-revision-id into lp:launchpad

Proposed by Colin Watson
Status: Merged
Merged at revision: 18366
Proposed branch: lp:~cjwatson/launchpad/snap-revision-id
Merge into: lp:launchpad
Diff against target: 254 lines (+96/-18)
9 files modified
lib/lp/buildmaster/interactor.py (+7/-10)
lib/lp/buildmaster/interfaces/buildqueue.py (+4/-1)
lib/lp/buildmaster/model/buildqueue.py (+14/-1)
lib/lp/buildmaster/tests/test_manager.py (+37/-6)
lib/lp/snappy/browser/tests/test_snapbuild.py (+10/-0)
lib/lp/snappy/interfaces/snapbuild.py (+6/-0)
lib/lp/snappy/model/snapbuild.py (+6/-0)
lib/lp/snappy/templates/snapbuild-index.pt (+3/-0)
lib/lp/snappy/tests/test_snapbuild.py (+9/-0)
To merge this branch: bzr merge lp:~cjwatson/launchpad/snap-revision-id
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+321690@code.launchpad.net

Commit message

Populate SnapBuild.revision_id from information returned by the builder.

To post a comment you must log in.
Revision history for this message
Adam Collard (adam-collard) :
Revision history for this message
Colin Watson (cjwatson) :
Revision history for this message
William Grant (wgrant) :
review: Approve (code)
Revision history for this message
Colin Watson (cjwatson) :

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/buildmaster/interactor.py'
--- lib/lp/buildmaster/interactor.py 2016-06-06 12:55:36 +0000
+++ lib/lp/buildmaster/interactor.py 2017-04-27 15:55:40 +0000
@@ -1,4 +1,4 @@
1# Copyright 2009-2016 Canonical Ltd. This software is licensed under the1# Copyright 2009-2017 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4__metaclass__ = type4__metaclass__ = type
@@ -536,15 +536,12 @@
536 # matches the DB, and this method isn't called unless the DB536 # matches the DB, and this method isn't called unless the DB
537 # says there's a job.537 # says there's a job.
538 builder_status = slave_status['builder_status']538 builder_status = slave_status['builder_status']
539 if builder_status == 'BuilderStatus.BUILDING':539 if builder_status in (
540 # Build still building, collect the logtail.540 'BuilderStatus.BUILDING', 'BuilderStatus.ABORTING'):
541 vitals.build_queue.logtail = str(541 vitals.build_queue.collectStatus(slave_status)
542 slave_status.get('logtail')).decode('UTF-8', errors='replace')542 vitals.build_queue.specific_build.updateStatus(
543 transaction.commit()543 vitals.build_queue.specific_build.status,
544 elif builder_status == 'BuilderStatus.ABORTING':544 slave_status=slave_status)
545 # Build is being aborted.
546 vitals.build_queue.logtail = (
547 "Waiting for slave process to be terminated")
548 transaction.commit()545 transaction.commit()
549 elif builder_status == 'BuilderStatus.WAITING':546 elif builder_status == 'BuilderStatus.WAITING':
550 # Build has finished. Delegate handling to the build itself.547 # Build has finished. Delegate handling to the build itself.
551548
=== modified file 'lib/lp/buildmaster/interfaces/buildqueue.py'
--- lib/lp/buildmaster/interfaces/buildqueue.py 2015-04-20 09:48:57 +0000
+++ lib/lp/buildmaster/interfaces/buildqueue.py 2017-04-27 15:55:40 +0000
@@ -1,4 +1,4 @@
1# Copyright 2009-2013 Canonical Ltd. This software is licensed under the1# Copyright 2009-2017 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Build interfaces."""4"""Build interfaces."""
@@ -86,6 +86,9 @@
86 def markAsBuilding(builder):86 def markAsBuilding(builder):
87 """Set this queue item to a 'building' state."""87 """Set this queue item to a 'building' state."""
8888
89 def collectStatus(slave_status):
90 """Collect status information from the builder."""
91
89 def suspend():92 def suspend():
90 """Suspend this waiting job, removing it from the active queue."""93 """Suspend this waiting job, removing it from the active queue."""
9194
9295
=== modified file 'lib/lp/buildmaster/model/buildqueue.py'
--- lib/lp/buildmaster/model/buildqueue.py 2016-05-14 00:25:07 +0000
+++ lib/lp/buildmaster/model/buildqueue.py 2017-04-27 15:55:40 +0000
@@ -1,4 +1,4 @@
1# Copyright 2009-2013 Canonical Ltd. This software is licensed under the1# Copyright 2009-2017 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4__metaclass__ = type4__metaclass__ = type
@@ -180,6 +180,19 @@
180 if builder is not None:180 if builder is not None:
181 del get_property_cache(builder).currentjob181 del get_property_cache(builder).currentjob
182182
183 def collectStatus(self, slave_status):
184 """See `IBuildQueue`."""
185 builder_status = slave_status["builder_status"]
186 if builder_status == "BuilderStatus.ABORTING":
187 self.logtail = "Waiting for slave process to be terminated"
188 elif slave_status.get("logtail") is not None:
189 # slave_status["logtail"] is normally an xmlrpclib.Binary
190 # instance, and the contents might include invalid UTF-8 due to
191 # being a fixed number of bytes from the tail of the log. Turn
192 # it into Unicode as best we can.
193 self.logtail = str(
194 slave_status.get("logtail")).decode("UTF-8", errors="replace")
195
183 def suspend(self):196 def suspend(self):
184 """See `IBuildQueue`."""197 """See `IBuildQueue`."""
185 if self.status != BuildQueueStatus.WAITING:198 if self.status != BuildQueueStatus.WAITING:
186199
=== modified file 'lib/lp/buildmaster/tests/test_manager.py'
--- lib/lp/buildmaster/tests/test_manager.py 2016-06-06 12:50:55 +0000
+++ lib/lp/buildmaster/tests/test_manager.py 2017-04-27 15:55:40 +0000
@@ -2,7 +2,7 @@
2# NOTE: The first line above must stay first; do not move the copyright2# NOTE: The first line above must stay first; do not move the copyright
3# notice to the top. See http://www.python.org/dev/peps/pep-0263/.3# notice to the top. See http://www.python.org/dev/peps/pep-0263/.
4#4#
5# Copyright 2009-2014 Canonical Ltd. This software is licensed under the5# Copyright 2009-2017 Canonical Ltd. This software is licensed under the
6# GNU Affero General Public License version 3 (see the file LICENSE).6# GNU Affero General Public License version 3 (see the file LICENSE).
77
8"""Tests for the renovated slave scanner aka BuilddManager."""8"""Tests for the renovated slave scanner aka BuilddManager."""
@@ -113,11 +113,11 @@
113 """113 """
114 super(TestSlaveScannerScan, self).setUp()114 super(TestSlaveScannerScan, self).setUp()
115 # Creating the required chroots needed for dispatching.115 # Creating the required chroots needed for dispatching.
116 test_publisher = make_publisher()116 self.test_publisher = make_publisher()
117 ubuntu = getUtility(IDistributionSet).getByName('ubuntu')117 ubuntu = getUtility(IDistributionSet).getByName('ubuntu')
118 hoary = ubuntu.getSeries('hoary')118 hoary = ubuntu.getSeries('hoary')
119 test_publisher.setUpDefaultDistroSeries(hoary)119 self.test_publisher.setUpDefaultDistroSeries(hoary)
120 test_publisher.addFakeChroots(db_only=True)120 self.test_publisher.addFakeChroots(db_only=True)
121121
122 def _resetBuilder(self, builder):122 def _resetBuilder(self, builder):
123 """Reset the given builder and its job."""123 """Reset the given builder and its job."""
@@ -139,8 +139,7 @@
139 self.assertEqual(job.builder, builder)139 self.assertEqual(job.builder, builder)
140 self.assertTrue(job.date_started is not None)140 self.assertTrue(job.date_started is not None)
141 self.assertEqual(job.status, BuildQueueStatus.RUNNING)141 self.assertEqual(job.status, BuildQueueStatus.RUNNING)
142 build = getUtility(IBinaryPackageBuildSet).getByQueueEntry(job)142 self.assertEqual(job.specific_build.status, BuildStatus.BUILDING)
143 self.assertEqual(build.status, BuildStatus.BUILDING)
144 self.assertEqual(job.logtail, logtail)143 self.assertEqual(job.logtail, logtail)
145144
146 def _getScanner(self, builder_name=None, clock=None, builder_factory=None):145 def _getScanner(self, builder_name=None, clock=None, builder_factory=None):
@@ -397,6 +396,38 @@
397 self.assertEqual(2, fake_scan.call_count)396 self.assertEqual(2, fake_scan.call_count)
398397
399 @defer.inlineCallbacks398 @defer.inlineCallbacks
399 def test_scan_of_snap_build(self):
400 # Snap builds return additional status information, which the scan
401 # collects.
402 class SnapBuildingSlave(BuildingSlave):
403 revision_id = None
404
405 @defer.inlineCallbacks
406 def status(self):
407 status = yield super(SnapBuildingSlave, self).status()
408 status["revision_id"] = self.revision_id
409 defer.returnValue(status)
410
411 build = self.factory.makeSnapBuild(
412 distroarchseries=self.test_publisher.distroseries.architectures[0])
413 job = build.queueBuild()
414 builder = self.factory.makeBuilder(
415 processors=[job.processor], vm_host="fake_vm_host")
416 job.markAsBuilding(builder)
417 slave = SnapBuildingSlave(build_id="SNAPBUILD-%d" % build.id)
418 self.patch(BuilderSlave, "makeBuilderSlave", FakeMethod(slave))
419 transaction.commit()
420 scanner = self._getScanner(builder_name=builder.name)
421 yield scanner.scan()
422 self.assertBuildingJob(job, builder, logtail="This is a build log: 0")
423 self.assertIsNone(build.revision_id)
424 slave.revision_id = "dummy"
425 scanner = self._getScanner(builder_name=builder.name)
426 yield scanner.scan()
427 self.assertBuildingJob(job, builder, logtail="This is a build log: 1")
428 self.assertEqual("dummy", build.revision_id)
429
430 @defer.inlineCallbacks
400 def _assertFailureCounting(self, builder_count, job_count,431 def _assertFailureCounting(self, builder_count, job_count,
401 expected_builder_count, expected_job_count):432 expected_builder_count, expected_job_count):
402 # If scan() fails with an exception, failure_counts should be433 # If scan() fails with an exception, failure_counts should be
403434
=== modified file 'lib/lp/snappy/browser/tests/test_snapbuild.py'
--- lib/lp/snappy/browser/tests/test_snapbuild.py 2017-01-27 12:44:41 +0000
+++ lib/lp/snappy/browser/tests/test_snapbuild.py 2017-04-27 15:55:40 +0000
@@ -77,6 +77,16 @@
77 build_view = create_initialized_view(build, "+index")77 build_view = create_initialized_view(build, "+index")
78 self.assertEqual([], build_view.files)78 self.assertEqual([], build_view.files)
7979
80 def test_revision_id(self):
81 build = self.factory.makeSnapBuild()
82 build.updateStatus(
83 BuildStatus.FULLYBUILT, slave_status={"revision_id": "dummy"})
84 build_view = create_initialized_view(build, "+index")
85 self.assertThat(build_view(), soupmatchers.HTMLContains(
86 soupmatchers.Tag(
87 "revision ID", "li", attrs={"id": "revision-id"},
88 text=re.compile(r"^\s*Revision: dummy\s*$"))))
89
80 def test_store_upload_status_in_progress(self):90 def test_store_upload_status_in_progress(self):
81 build = self.factory.makeSnapBuild(status=BuildStatus.FULLYBUILT)91 build = self.factory.makeSnapBuild(status=BuildStatus.FULLYBUILT)
82 getUtility(ISnapStoreUploadJobSource).create(build)92 getUtility(ISnapStoreUploadJobSource).create(build)
8393
=== modified file 'lib/lp/snappy/interfaces/snapbuild.py'
--- lib/lp/snappy/interfaces/snapbuild.py 2017-04-03 14:43:22 +0000
+++ lib/lp/snappy/interfaces/snapbuild.py 2017-04-27 15:55:40 +0000
@@ -175,6 +175,12 @@
175 title=_("The date when the build completed or is estimated to "175 title=_("The date when the build completed or is estimated to "
176 "complete."), readonly=True)176 "complete."), readonly=True)
177177
178 revision_id = exported(TextLine(
179 title=_("Revision ID"), required=False, readonly=True,
180 description=_(
181 "The revision ID of the branch used for this build, if "
182 "available.")))
183
178 store_upload_jobs = CollectionField(184 store_upload_jobs = CollectionField(
179 title=_("Store upload jobs for this build."),185 title=_("Store upload jobs for this build."),
180 # Really ISnapStoreUploadJob.186 # Really ISnapStoreUploadJob.
181187
=== modified file 'lib/lp/snappy/model/snapbuild.py'
--- lib/lp/snappy/model/snapbuild.py 2017-04-03 14:43:22 +0000
+++ lib/lp/snappy/model/snapbuild.py 2017-04-27 15:55:40 +0000
@@ -158,6 +158,8 @@
158158
159 status = DBEnum(name='status', enum=BuildStatus, allow_none=False)159 status = DBEnum(name='status', enum=BuildStatus, allow_none=False)
160160
161 revision_id = Unicode(name='revision_id')
162
161 log_id = Int(name='log')163 log_id = Int(name='log')
162 log = Reference(log_id, 'LibraryFileAlias.id')164 log = Reference(log_id, 'LibraryFileAlias.id')
163165
@@ -337,6 +339,10 @@
337 status, builder=builder, slave_status=slave_status,339 status, builder=builder, slave_status=slave_status,
338 date_started=date_started, date_finished=date_finished,340 date_started=date_started, date_finished=date_finished,
339 force_invalid_transition=force_invalid_transition)341 force_invalid_transition=force_invalid_transition)
342 if slave_status is not None:
343 revision_id = slave_status.get("revision_id")
344 if revision_id is not None:
345 self.revision_id = unicode(revision_id)
340 notify(SnapBuildStatusChangedEvent(self))346 notify(SnapBuildStatusChangedEvent(self))
341347
342 def notify(self, extra_info=None):348 def notify(self, extra_info=None):
343349
=== modified file 'lib/lp/snappy/templates/snapbuild-index.pt'
--- lib/lp/snappy/templates/snapbuild-index.pt 2017-02-27 18:46:38 +0000
+++ lib/lp/snappy/templates/snapbuild-index.pt 2017-04-27 15:55:40 +0000
@@ -112,6 +112,9 @@
112 </p>112 </p>
113113
114 <ul>114 <ul>
115 <li id="revision-id" tal:condition="context/revision_id">
116 Revision: <span tal:replace="context/revision_id" />
117 </li>
115 <li tal:condition="context/dependencies">118 <li tal:condition="context/dependencies">
116 Missing build dependencies: <em tal:content="context/dependencies"/>119 Missing build dependencies: <em tal:content="context/dependencies"/>
117 </li>120 </li>
118121
=== modified file 'lib/lp/snappy/tests/test_snapbuild.py'
--- lib/lp/snappy/tests/test_snapbuild.py 2017-03-20 00:03:52 +0000
+++ lib/lp/snappy/tests/test_snapbuild.py 2017-04-27 15:55:40 +0000
@@ -218,6 +218,15 @@
218 self.factory.makeSnapFile(snapbuild=self.build)218 self.factory.makeSnapFile(snapbuild=self.build)
219 self.assertTrue(self.build.verifySuccessfulUpload())219 self.assertTrue(self.build.verifySuccessfulUpload())
220220
221 def test_updateStatus_stores_revision_id(self):
222 # If the builder reports a revision_id, updateStatus saves it.
223 self.assertIsNone(self.build.revision_id)
224 self.build.updateStatus(BuildStatus.BUILDING, slave_status={})
225 self.assertIsNone(self.build.revision_id)
226 self.build.updateStatus(
227 BuildStatus.BUILDING, slave_status={"revision_id": "dummy"})
228 self.assertEqual("dummy", self.build.revision_id)
229
221 def test_updateStatus_triggers_webhooks(self):230 def test_updateStatus_triggers_webhooks(self):
222 # Updating the status of a SnapBuild triggers webhooks on the231 # Updating the status of a SnapBuild triggers webhooks on the
223 # corresponding Snap.232 # corresponding Snap.