Merge ~cjwatson/launchpad:build-private-bpb-immediately into launchpad:master

Proposed by Colin Watson
Status: Superseded
Proposed branch: ~cjwatson/launchpad:build-private-bpb-immediately
Merge into: launchpad:master
Diff against target: 276 lines (+60/-60)
4 files modified
lib/lp/buildmaster/tests/test_builder.py (+4/-4)
lib/lp/soyuz/model/binarypackagebuild.py (+3/-27)
lib/lp/soyuz/model/binarypackagebuildbehaviour.py (+10/-13)
lib/lp/soyuz/tests/test_binarypackagebuildbehaviour.py (+43/-16)
Reviewer Review Type Date Requested Status
Launchpad code reviewers Pending
Review via email: mp+373741@code.launchpad.net

This proposal has been superseded by a proposal from 2022-09-21.

Commit message

Dispatch private BPBs immediately

We now use macaroon authentication for source files.

Description of the change

I haven't yet tested this beyond what's in the test suite.

This is essentially the same as https://code.launchpad.net/~cjwatson/launchpad/build-private-bpb-immediately/+merge/345104, converted to git and rebased on master.

To post a comment you must log in.
Revision history for this message
Colin Watson (cjwatson) wrote :

I'm going to reject this and replace it with a new reduced version, since master has moved on quite a bit and part of this has essentially been merged.

Unmerged commits

fdfa673... by Colin Watson

Dispatch private BPBs immediately, using macaroon auth for source files.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/lib/lp/buildmaster/tests/test_builder.py b/lib/lp/buildmaster/tests/test_builder.py
index a97d3c9..3635643 100644
--- a/lib/lp/buildmaster/tests/test_builder.py
+++ b/lib/lp/buildmaster/tests/test_builder.py
@@ -403,13 +403,13 @@ class TestFindBuildCandidatePrivatePPA(TestFindBuildCandidatePPABase):
403 build = getUtility(IBinaryPackageBuildSet).getByQueueEntry(next_job)403 build = getUtility(IBinaryPackageBuildSet).getByQueueEntry(next_job)
404 self.assertEqual('joesppa', build.archive.name)404 self.assertEqual('joesppa', build.archive.name)
405405
406 # If the source for the build is still pending, it won't be406 # If the source for the build is still pending, it will still be
407 # dispatched because the builder has to fetch the source files407 # dispatched: the builder will use macaroon authentication to fetch
408 # from the (password protected) repo area, not the librarian.408 # the source files from the librarian.
409 pub = build.current_source_publication409 pub = build.current_source_publication
410 pub.status = PackagePublishingStatus.PENDING410 pub.status = PackagePublishingStatus.PENDING
411 candidate = removeSecurityProxy(self.builder4)._findBuildCandidate()411 candidate = removeSecurityProxy(self.builder4)._findBuildCandidate()
412 self.assertNotEqual(next_job.id, candidate.id)412 self.assertEqual(next_job.id, candidate.id)
413413
414414
415class TestFindBuildCandidateDistroArchive(TestFindBuildCandidateBase):415class TestFindBuildCandidateDistroArchive(TestFindBuildCandidateBase):
diff --git a/lib/lp/soyuz/model/binarypackagebuild.py b/lib/lp/soyuz/model/binarypackagebuild.py
index 748e102..1f87038 100644
--- a/lib/lp/soyuz/model/binarypackagebuild.py
+++ b/lib/lp/soyuz/model/binarypackagebuild.py
@@ -89,10 +89,7 @@ from lp.services.macaroons.interfaces import (
89 )89 )
90from lp.services.macaroons.model import MacaroonIssuerBase90from lp.services.macaroons.model import MacaroonIssuerBase
91from lp.soyuz.adapters.buildarch import determine_architectures_to_build91from lp.soyuz.adapters.buildarch import determine_architectures_to_build
92from lp.soyuz.enums import (92from lp.soyuz.enums import ArchivePurpose
93 ArchivePurpose,
94 PackagePublishingStatus,
95 )
96from lp.soyuz.interfaces.archive import (93from lp.soyuz.interfaces.archive import (
97 InvalidExternalDependencies,94 InvalidExternalDependencies,
98 validate_external_dependencies,95 validate_external_dependencies,
@@ -1212,33 +1209,12 @@ class BinaryPackageBuildSet(SpecificBuildFarmJobSourceMixin):
1212 @staticmethod1209 @staticmethod
1213 def addCandidateSelectionCriteria(processor, virtualized):1210 def addCandidateSelectionCriteria(processor, virtualized):
1214 """See `ISpecificBuildFarmJobSource`."""1211 """See `ISpecificBuildFarmJobSource`."""
1215 private_statuses = (
1216 PackagePublishingStatus.PUBLISHED,
1217 PackagePublishingStatus.SUPERSEDED,
1218 PackagePublishingStatus.DELETED,
1219 )
1220 return """1212 return """
1221 SELECT TRUE FROM Archive, BinaryPackageBuild, DistroArchSeries1213 SELECT TRUE FROM BinaryPackageBuild
1222 WHERE1214 WHERE
1223 BinaryPackageBuild.build_farm_job = BuildQueue.build_farm_job AND1215 BinaryPackageBuild.build_farm_job = BuildQueue.build_farm_job AND
1224 BinaryPackageBuild.distro_arch_series =
1225 DistroArchSeries.id AND
1226 BinaryPackageBuild.archive = Archive.id AND
1227 ((Archive.private IS TRUE AND
1228 EXISTS (
1229 SELECT SourcePackagePublishingHistory.id
1230 FROM SourcePackagePublishingHistory
1231 WHERE
1232 SourcePackagePublishingHistory.distroseries =
1233 DistroArchSeries.distroseries AND
1234 SourcePackagePublishingHistory.sourcepackagerelease =
1235 BinaryPackageBuild.source_package_release AND
1236 SourcePackagePublishingHistory.archive = Archive.id AND
1237 SourcePackagePublishingHistory.status IN %s))
1238 OR
1239 archive.private IS FALSE) AND
1240 BinaryPackageBuild.status = %s1216 BinaryPackageBuild.status = %s
1241 """ % sqlvalues(private_statuses, BuildStatus.NEEDSBUILD)1217 """ % sqlvalues(BuildStatus.NEEDSBUILD)
12421218
1243 @staticmethod1219 @staticmethod
1244 def postprocessCandidate(job, logger):1220 def postprocessCandidate(job, logger):
diff --git a/lib/lp/soyuz/model/binarypackagebuildbehaviour.py b/lib/lp/soyuz/model/binarypackagebuildbehaviour.py
index afb1df9..9d0591b 100644
--- a/lib/lp/soyuz/model/binarypackagebuildbehaviour.py
+++ b/lib/lp/soyuz/model/binarypackagebuildbehaviour.py
@@ -10,7 +10,9 @@ __all__ = [
10 ]10 ]
1111
12from twisted.internet import defer12from twisted.internet import defer
13from zope.component import getUtility
13from zope.interface import implementer14from zope.interface import implementer
15from zope.security.proxy import removeSecurityProxy
1416
15from lp.buildmaster.interfaces.builder import CannotBuild17from lp.buildmaster.interfaces.builder import CannotBuild
16from lp.buildmaster.interfaces.buildfarmjobbehaviour import (18from lp.buildmaster.interfaces.buildfarmjobbehaviour import (
@@ -20,13 +22,12 @@ from lp.buildmaster.model.buildfarmjobbehaviour import (
20 BuildFarmJobBehaviourBase,22 BuildFarmJobBehaviourBase,
21 )23 )
22from lp.registry.interfaces.pocket import PackagePublishingPocket24from lp.registry.interfaces.pocket import PackagePublishingPocket
23from lp.services.webapp import urlappend25from lp.services.macaroons.interfaces import IMacaroonIssuer
24from lp.soyuz.adapters.archivedependencies import (26from lp.soyuz.adapters.archivedependencies import (
25 get_primary_current_component,27 get_primary_current_component,
26 get_sources_list_for_building,28 get_sources_list_for_building,
27 )29 )
28from lp.soyuz.enums import ArchivePurpose30from lp.soyuz.enums import ArchivePurpose
29from lp.soyuz.model.publishing import makePoolPath
3031
3132
32@implementer(IBuildFarmJobBehaviour)33@implementer(IBuildFarmJobBehaviour)
@@ -63,14 +64,9 @@ class BinaryPackageBuildBehaviour(BuildFarmJobBehaviourBase):
63 # Build filemap structure with the files required in this build64 # Build filemap structure with the files required in this build
64 # and send them to the slave.65 # and send them to the slave.
65 if self.build.archive.private:66 if self.build.archive.private:
66 # Builds in private archive may have restricted files that67 issuer = getUtility(IMacaroonIssuer, 'binary-package-build')
67 # we can't obtain from the public librarian. Prepare a pool68 password = removeSecurityProxy(issuer).issueMacaroon(
68 # URL from which to fetch them.69 self.build).serialize()
69 pool_url = urlappend(
70 self.build.archive.archive_url,
71 makePoolPath(
72 self.build.source_package_release.sourcepackagename.name,
73 self.build.current_component.name))
74 filemap = {}70 filemap = {}
75 for source_file in self.build.source_package_release.files:71 for source_file in self.build.source_package_release.files:
76 lfa = source_file.libraryfile72 lfa = source_file.libraryfile
@@ -80,9 +76,10 @@ class BinaryPackageBuildBehaviour(BuildFarmJobBehaviourBase):
80 else:76 else:
81 filemap[lfa.filename] = {77 filemap[lfa.filename] = {
82 'sha1': lfa.content.sha1,78 'sha1': lfa.content.sha1,
83 'url': urlappend(pool_url, lfa.filename),79 'url': lfa.https_url,
84 'username': 'buildd',80 'username': '',
85 'password': self.build.archive.buildd_secret}81 'password': password,
82 }
86 return filemap83 return filemap
8784
88 def verifyBuildRequest(self, logger):85 def verifyBuildRequest(self, logger):
diff --git a/lib/lp/soyuz/tests/test_binarypackagebuildbehaviour.py b/lib/lp/soyuz/tests/test_binarypackagebuildbehaviour.py
index 61e4847..be27e00 100644
--- a/lib/lp/soyuz/tests/test_binarypackagebuildbehaviour.py
+++ b/lib/lp/soyuz/tests/test_binarypackagebuildbehaviour.py
@@ -12,8 +12,15 @@ import os
12import shutil12import shutil
13import tempfile13import tempfile
1414
15from pymacaroons import Macaroon
15from storm.store import Store16from storm.store import Store
16from testtools.matchers import MatchesListwise17from testtools.matchers import (
18 Contains,
19 Equals,
20 Matcher,
21 MatchesListwise,
22 Mismatch,
23 )
17from testtools.twistedsupport import AsynchronousDeferredRunTest24from testtools.twistedsupport import AsynchronousDeferredRunTest
18import transaction25import transaction
19from twisted.internet import defer26from twisted.internet import defer
@@ -21,7 +28,6 @@ from twisted.trial.unittest import TestCase as TrialTestCase
21from zope.component import getUtility28from zope.component import getUtility
22from zope.security.proxy import removeSecurityProxy29from zope.security.proxy import removeSecurityProxy
2330
24from lp.archivepublisher.diskpool import poolify
25from lp.archivepublisher.interfaces.archivesigningkey import (31from lp.archivepublisher.interfaces.archivesigningkey import (
26 IArchiveSigningKey,32 IArchiveSigningKey,
27 )33 )
@@ -58,6 +64,7 @@ from lp.registry.interfaces.sourcepackage import SourcePackageFileType
58from lp.services.config import config64from lp.services.config import config
59from lp.services.librarian.interfaces import ILibraryFileAliasSet65from lp.services.librarian.interfaces import ILibraryFileAliasSet
60from lp.services.log.logger import BufferLogger66from lp.services.log.logger import BufferLogger
67from lp.services.macaroons.interfaces import IMacaroonIssuer
61from lp.services.webapp import canonical_url68from lp.services.webapp import canonical_url
62from lp.soyuz.adapters.archivedependencies import (69from lp.soyuz.adapters.archivedependencies import (
63 get_sources_list_for_building,70 get_sources_list_for_building,
@@ -74,6 +81,27 @@ from lp.testing.keyserver import InProcessKeyServerFixture
74from lp.testing.layers import LaunchpadZopelessLayer81from lp.testing.layers import LaunchpadZopelessLayer
7582
7683
84class BinaryPackageBuildMacaroonVerifies(Matcher):
85 """Matches if a binary-package-build macaroon passes verification."""
86
87 def __init__(self, build, lfa):
88 self.build = build
89 self.lfa = lfa
90
91 def match(self, macaroon_raw):
92 macaroon = Macaroon.deserialize(macaroon_raw)
93 expected_caveat = (
94 "lp.principal.binary-package-build %s" %
95 removeSecurityProxy(self.build).id)
96 mismatch = Contains(expected_caveat).match(
97 [caveat.caveat_id for caveat in macaroon.caveats])
98 if mismatch is not None:
99 return mismatch
100 issuer = getUtility(IMacaroonIssuer, "binary-package-build")
101 if not issuer.verifyMacaroon(macaroon, self.lfa):
102 return Mismatch("Macaroon does not verify")
103
104
77class TestBinaryBuildPackageBehaviour(TestCaseWithFactory):105class TestBinaryBuildPackageBehaviour(TestCaseWithFactory):
78 """Tests for the BinaryPackageBuildBehaviour.106 """Tests for the BinaryPackageBuildBehaviour.
79107
@@ -98,7 +126,7 @@ class TestBinaryBuildPackageBehaviour(TestCaseWithFactory):
98 expected = yield self.makeExpectedInteraction(126 expected = yield self.makeExpectedInteraction(
99 builder, build, chroot, archive, archive_purpose, component,127 builder, build, chroot, archive, archive_purpose, component,
100 extra_uploads, filemap_names)128 extra_uploads, filemap_names)
101 self.assertEqual(expected, call_log)129 self.assertThat(call_log, MatchesListwise(expected))
102130
103 @defer.inlineCallbacks131 @defer.inlineCallbacks
104 def makeExpectedInteraction(self, builder, build, chroot, archive,132 def makeExpectedInteraction(self, builder, build, chroot, archive,
@@ -115,7 +143,7 @@ class TestBinaryBuildPackageBehaviour(TestCaseWithFactory):
115 builder. We specify this separately from the archive because143 builder. We specify this separately from the archive because
116 sometimes the behaviour object has to give a different purpose144 sometimes the behaviour object has to give a different purpose
117 in order to trick the slave into building correctly.145 in order to trick the slave into building correctly.
118 :return: A list of the calls we expect to be made.146 :return: A list of matchers for the calls we expect to be made.
119 """147 """
120 das = build.distro_arch_series148 das = build.distro_arch_series
121 ds_name = das.distroseries.name149 ds_name = das.distroseries.name
@@ -131,8 +159,9 @@ class TestBinaryBuildPackageBehaviour(TestCaseWithFactory):
131 extra_uploads = []159 extra_uploads = []
132160
133 upload_logs = [161 upload_logs = [
134 ('ensurepresent',) + upload162 MatchesListwise((Equals('ensurepresent'),) + upload)
135 for upload in [(chroot.http_url, '', '')] + extra_uploads]163 for upload in [(Equals(chroot.http_url), Equals(''), Equals(''))] +
164 extra_uploads]
136165
137 extra_args = {166 extra_args = {
138 'arch_indep': arch_indep,167 'arch_indep': arch_indep,
@@ -151,8 +180,9 @@ class TestBinaryBuildPackageBehaviour(TestCaseWithFactory):
151 'trusted_keys': trusted_keys,180 'trusted_keys': trusted_keys,
152 }181 }
153 build_log = [182 build_log = [
154 ('build', build.build_cookie, 'binarypackage',183 MatchesListwise([Equals(arg) for arg in (
155 chroot.content.sha1, filemap_names, extra_args)]184 'build', build.build_cookie, 'binarypackage',
185 chroot.content.sha1, filemap_names, extra_args)])]
156 result = upload_logs + build_log186 result = upload_logs + build_log
157 defer.returnValue(result)187 defer.returnValue(result)
158188
@@ -236,6 +266,8 @@ class TestBinaryBuildPackageBehaviour(TestCaseWithFactory):
236266
237 @defer.inlineCallbacks267 @defer.inlineCallbacks
238 def test_private_source_dispatch(self):268 def test_private_source_dispatch(self):
269 self.pushConfig(
270 "launchpad", internal_macaroon_secret_key="some-secret")
239 archive = self.factory.makeArchive(private=True)271 archive = self.factory.makeArchive(private=True)
240 slave = OkSlave()272 slave = OkSlave()
241 builder = self.factory.makeBuilder()273 builder = self.factory.makeBuilder()
@@ -246,13 +278,6 @@ class TestBinaryBuildPackageBehaviour(TestCaseWithFactory):
246 sprf = build.source_package_release.addFile(278 sprf = build.source_package_release.addFile(
247 self.factory.makeLibraryFileAlias(db_only=True),279 self.factory.makeLibraryFileAlias(db_only=True),
248 filetype=SourcePackageFileType.ORIG_TARBALL)280 filetype=SourcePackageFileType.ORIG_TARBALL)
249 sprf_url = (
250 'http://private-ppa.launchpad.test/%s/%s/ubuntu/pool/%s/%s'
251 % (archive.owner.name, archive.name,
252 poolify(
253 build.source_package_release.sourcepackagename.name,
254 'main'),
255 sprf.libraryfile.filename))
256 lf = self.factory.makeLibraryFileAlias()281 lf = self.factory.makeLibraryFileAlias()
257 transaction.commit()282 transaction.commit()
258 build.distro_arch_series.addOrUpdateChroot(lf)283 build.distro_arch_series.addOrUpdateChroot(lf)
@@ -264,7 +289,9 @@ class TestBinaryBuildPackageBehaviour(TestCaseWithFactory):
264 interactor.getBuildBehaviour(bq, builder, slave), BufferLogger())289 interactor.getBuildBehaviour(bq, builder, slave), BufferLogger())
265 yield self.assertExpectedInteraction(290 yield self.assertExpectedInteraction(
266 slave.call_log, builder, build, lf, archive, ArchivePurpose.PPA,291 slave.call_log, builder, build, lf, archive, ArchivePurpose.PPA,
267 extra_uploads=[(sprf_url, 'buildd', 'sekrit')],292 extra_uploads=[(
293 Equals(sprf.libraryfile.https_url), Equals(''),
294 BinaryPackageBuildMacaroonVerifies(build, sprf.libraryfile))],
268 filemap_names=[sprf.libraryfile.filename])295 filemap_names=[sprf.libraryfile.filename])
269296
270 @defer.inlineCallbacks297 @defer.inlineCallbacks

Subscribers

People subscribed via source and target branches

to status/vote changes: