Merge lp:~cjwatson/launchpad/bpb-external-dependencies into lp:launchpad

Proposed by Colin Watson
Status: Merged
Merged at revision: 17881
Proposed branch: lp:~cjwatson/launchpad/bpb-external-dependencies
Merge into: lp:launchpad
Diff against target: 259 lines (+126/-9)
7 files modified
lib/lp/security.py (+19/-1)
lib/lp/soyuz/configure.zcml (+6/-2)
lib/lp/soyuz/interfaces/archive.py (+2/-2)
lib/lp/soyuz/interfaces/binarypackagebuild.py (+21/-2)
lib/lp/soyuz/model/binarypackagebuild.py (+17/-1)
lib/lp/soyuz/stories/webservice/xx-builds.txt (+1/-0)
lib/lp/soyuz/tests/test_binarypackagebuild.py (+60/-1)
To merge this branch: bzr merge lp:~cjwatson/launchpad/bpb-external-dependencies
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+281744@code.launchpad.net

Commit message

Allow PPA admins to set external_dependencies on individual binary package builds.

Description of the change

Allow PPA admins to set external_dependencies on individual binary package builds, allowing them to bootstrap cyclic dependencies without needing a bootstrap archive sources.list.d file in the chroot.

Using launchpad.Moderate is a little odd, but no other existing permission on that object was suitable, and this doesn't seem completely wrong. The alternatives that I can think of would be opening this facility up to buildd admins, or introducing a new role-based permission for PPA admins.

To post a comment you must log in.
Revision history for this message
William Grant (wgrant) :
review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/security.py'
--- lib/lp/security.py 2015-12-02 02:15:45 +0000
+++ lib/lp/security.py 2016-01-06 16:01:07 +0000
@@ -1,4 +1,4 @@
1# Copyright 2009-2015 Canonical Ltd. This software is licensed under the1# Copyright 2009-2016 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"""Security policies for using content objects."""4"""Security policies for using content objects."""
@@ -1957,6 +1957,24 @@
1957 return auth_spr.checkUnauthenticated()1957 return auth_spr.checkUnauthenticated()
19581958
19591959
1960class ModerateBinaryPackageBuild(ViewBinaryPackageBuild):
1961 permission = 'launchpad.Moderate'
1962
1963 def checkAuthenticated(self, user):
1964 # Only people who can see the build and administer its archive can
1965 # edit restricted attributes of builds. (Currently this allows
1966 # setting BinaryPackageBuild.external_dependencies; people who can
1967 # administer the archive can already achieve the same effect by
1968 # setting Archive.external_dependencies.)
1969 return (
1970 super(ModerateBinaryPackageBuild, self).checkAuthenticated(
1971 user) and
1972 AdminArchive(self.obj.archive).checkAuthenticated(user))
1973
1974 def checkUnauthenticated(self, user):
1975 return False
1976
1977
1960class ViewTranslationTemplatesBuild(DelegatedAuthorization):1978class ViewTranslationTemplatesBuild(DelegatedAuthorization):
1961 """Permission to view an `ITranslationTemplatesBuild`.1979 """Permission to view an `ITranslationTemplatesBuild`.
19621980
19631981
=== modified file 'lib/lp/soyuz/configure.zcml'
--- lib/lp/soyuz/configure.zcml 2015-11-26 15:46:38 +0000
+++ lib/lp/soyuz/configure.zcml 2016-01-06 16:01:07 +0000
@@ -1,4 +1,4 @@
1<!-- Copyright 2009-2015 Canonical Ltd. This software is licensed under the1<!-- Copyright 2009-2016 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).
3-->3-->
44
@@ -467,11 +467,15 @@
467 class="lp.soyuz.model.binarypackagebuild.BinaryPackageBuild">467 class="lp.soyuz.model.binarypackagebuild.BinaryPackageBuild">
468 <require468 <require
469 permission="launchpad.View"469 permission="launchpad.View"
470 interface="lp.soyuz.interfaces.binarypackagebuild.IBinaryPackageBuildView"/>470 interface="lp.soyuz.interfaces.binarypackagebuild.IBinaryPackageBuildView
471 lp.soyuz.interfaces.binarypackagebuild.IBinaryPackageBuildRestricted"/>
471 <require472 <require
472 permission="launchpad.Edit"473 permission="launchpad.Edit"
473 interface="lp.soyuz.interfaces.binarypackagebuild.IBinaryPackageBuildEdit"/>474 interface="lp.soyuz.interfaces.binarypackagebuild.IBinaryPackageBuildEdit"/>
474 <require475 <require
476 permission="launchpad.Moderate"
477 set_schema="lp.soyuz.interfaces.binarypackagebuild.IBinaryPackageBuildRestricted"/>
478 <require
475 permission="launchpad.Admin"479 permission="launchpad.Admin"
476 interface="lp.soyuz.interfaces.binarypackagebuild.IBinaryPackageBuildAdmin"/>480 interface="lp.soyuz.interfaces.binarypackagebuild.IBinaryPackageBuildAdmin"/>
477 </class>481 </class>
478482
=== modified file 'lib/lp/soyuz/interfaces/archive.py'
--- lib/lp/soyuz/interfaces/archive.py 2015-09-25 14:18:28 +0000
+++ lib/lp/soyuz/interfaces/archive.py 2016-01-06 16:01:07 +0000
@@ -1,4 +1,4 @@
1# Copyright 2009-2015 Canonical Ltd. This software is licensed under the1# Copyright 2009-2016 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"""Archive interfaces."""4"""Archive interfaces."""
@@ -641,7 +641,7 @@
641 Text(title=_("External dependencies"), required=False,641 Text(title=_("External dependencies"), required=False,
642 readonly=False, description=_(642 readonly=False, description=_(
643 "Newline-separated list of repositories to be used to retrieve "643 "Newline-separated list of repositories to be used to retrieve "
644 "any external build dependencies when building packages in the "644 "any external build-dependencies when building packages in the "
645 "archive, in the format:\n"645 "archive, in the format:\n"
646 "deb http[s]://[user:pass@]<host>[/path] %(series)s[-pocket] "646 "deb http[s]://[user:pass@]<host>[/path] %(series)s[-pocket] "
647 "[components]\n"647 "[components]\n"
648648
=== modified file 'lib/lp/soyuz/interfaces/binarypackagebuild.py'
--- lib/lp/soyuz/interfaces/binarypackagebuild.py 2015-05-14 08:50:41 +0000
+++ lib/lp/soyuz/interfaces/binarypackagebuild.py 2016-01-06 16:01:07 +0000
@@ -1,4 +1,4 @@
1# Copyright 2009-2015 Canonical Ltd. This software is licensed under the1# Copyright 2009-2016 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"""BinaryPackageBuild interfaces."""4"""BinaryPackageBuild interfaces."""
@@ -266,6 +266,25 @@
266 """266 """
267267
268268
269class IBinaryPackageBuildRestricted(Interface):
270 """Restricted `IBinaryPackageBuild` attributes.
271
272 These attributes need launchpad.View to see, and launchpad.Moderate to
273 change.
274 """
275 external_dependencies = exported(
276 Text(
277 title=_("External dependencies"), required=False, readonly=False,
278 description=_(
279 "Newline-separated list of repositories to be used to "
280 "retrieve any external build-dependencies when performing "
281 "this build, in the format:\n"
282 "deb http[s]://[user:pass@]<host>[/path] series[-pocket] "
283 "[components]\n"
284 "This is intended for bootstrapping build-dependency loops.")),
285 as_of="devel")
286
287
269class IBinaryPackageBuildAdmin(Interface):288class IBinaryPackageBuildAdmin(Interface):
270 """A Build interface for items requiring launchpad.Admin."""289 """A Build interface for items requiring launchpad.Admin."""
271290
@@ -277,7 +296,7 @@
277296
278class IBinaryPackageBuild(297class IBinaryPackageBuild(
279 IBinaryPackageBuildView, IBinaryPackageBuildEdit,298 IBinaryPackageBuildView, IBinaryPackageBuildEdit,
280 IBinaryPackageBuildAdmin):299 IBinaryPackageBuildRestricted, IBinaryPackageBuildAdmin):
281 """A Build interface"""300 """A Build interface"""
282 export_as_webservice_entry(singular_name='build', plural_name='builds')301 export_as_webservice_entry(singular_name='build', plural_name='builds')
283302
284303
=== modified file 'lib/lp/soyuz/model/binarypackagebuild.py'
--- lib/lp/soyuz/model/binarypackagebuild.py 2015-12-08 23:40:07 +0000
+++ lib/lp/soyuz/model/binarypackagebuild.py 2016-01-06 16:01:07 +0000
@@ -1,4 +1,4 @@
1# Copyright 2009-2015 Canonical Ltd. This software is licensed under the1# Copyright 2009-2016 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
@@ -86,6 +86,10 @@
86 PackagePublishingStatus,86 PackagePublishingStatus,
87 )87 )
88from lp.soyuz.adapters.buildarch import determine_architectures_to_build88from lp.soyuz.adapters.buildarch import determine_architectures_to_build
89from lp.soyuz.interfaces.archive import (
90 InvalidExternalDependencies,
91 validate_external_dependencies,
92 )
89from lp.soyuz.interfaces.binarypackagebuild import (93from lp.soyuz.interfaces.binarypackagebuild import (
90 BuildSetStatus,94 BuildSetStatus,
91 CannotBeRescored,95 CannotBeRescored,
@@ -149,6 +153,14 @@
149 or not processor.supports_nonvirtualized)153 or not processor.supports_nonvirtualized)
150154
151155
156def storm_validate_external_dependencies(build, attr, value):
157 assert attr == 'external_dependencies'
158 errors = validate_external_dependencies(value)
159 if len(errors) > 0:
160 raise InvalidExternalDependencies(errors)
161 return value
162
163
152@implementer(IBinaryPackageBuild)164@implementer(IBinaryPackageBuild)
153class BinaryPackageBuild(PackageBuildMixin, SQLBase):165class BinaryPackageBuild(PackageBuildMixin, SQLBase):
154 _table = 'BinaryPackageBuild'166 _table = 'BinaryPackageBuild'
@@ -213,6 +225,10 @@
213 source_package_name = Reference(225 source_package_name = Reference(
214 source_package_name_id, 'SourcePackageName.id')226 source_package_name_id, 'SourcePackageName.id')
215227
228 external_dependencies = Unicode(
229 name='external_dependencies',
230 validator=storm_validate_external_dependencies)
231
216 def getLatestSourcePublication(self):232 def getLatestSourcePublication(self):
217 from lp.soyuz.model.publishing import SourcePackagePublishingHistory233 from lp.soyuz.model.publishing import SourcePackagePublishingHistory
218 store = Store.of(self)234 store = Store.of(self)
219235
=== modified file 'lib/lp/soyuz/stories/webservice/xx-builds.txt'
--- lib/lp/soyuz/stories/webservice/xx-builds.txt 2015-05-12 01:43:31 +0000
+++ lib/lp/soyuz/stories/webservice/xx-builds.txt 2016-01-06 16:01:07 +0000
@@ -122,6 +122,7 @@
122 dependencies: None122 dependencies: None
123 distribution_link: u'http://.../ubuntu'123 distribution_link: u'http://.../ubuntu'
124 duration: u'0:01:20'124 duration: u'0:01:20'
125 external_dependencies: None
125 pocket: u'Release'126 pocket: u'Release'
126 resource_type_link: u'http://.../#build'127 resource_type_link: u'http://.../#build'
127 score: None128 score: None
128129
=== modified file 'lib/lp/soyuz/tests/test_binarypackagebuild.py'
--- lib/lp/soyuz/tests/test_binarypackagebuild.py 2015-12-08 23:40:07 +0000
+++ lib/lp/soyuz/tests/test_binarypackagebuild.py 2016-01-06 16:01:07 +0000
@@ -1,4 +1,4 @@
1# Copyright 2009-2015 Canonical Ltd. This software is licensed under the1# Copyright 2009-2016 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"""Test Build features."""4"""Test Build features."""
@@ -562,6 +562,65 @@
562 entry = self.webservice.get(build_url, api_version='devel').jsonBody()562 entry = self.webservice.get(build_url, api_version='devel').jsonBody()
563 self.assertEqual(name, entry['source_package_name'])563 self.assertEqual(name, entry['source_package_name'])
564564
565 def test_external_dependencies_random_user(self):
566 # Normal users can look but not touch.
567 person = self.factory.makePerson()
568 build_url = api_url(self.build)
569 logout()
570 webservice = webservice_for_person(
571 person, permission=OAuthPermission.WRITE_PUBLIC)
572 entry = webservice.get(build_url, api_version="devel").jsonBody()
573 self.assertIsNone(entry["external_dependencies"])
574 response = webservice.patch(
575 entry["self_link"], "application/json",
576 dumps({"external_dependencies": "random"}))
577 self.assertEqual(401, response.status)
578
579 def test_external_dependencies_owner(self):
580 # Normal archive owners can look but not touch.
581 build_url = api_url(self.build)
582 logout()
583 entry = self.webservice.get(build_url, api_version="devel").jsonBody()
584 self.assertIsNone(entry["external_dependencies"])
585 response = self.webservice.patch(
586 entry["self_link"], "application/json",
587 dumps({"external_dependencies": "random"}))
588 self.assertEqual(401, response.status)
589
590 def test_external_dependencies_ppa_owner_invalid(self):
591 # PPA admins can look and touch.
592 ppa_admin_team = getUtility(ILaunchpadCelebrities).ppa_admin
593 ppa_admin = self.factory.makePerson(member_of=[ppa_admin_team])
594 build_url = api_url(self.build)
595 logout()
596 webservice = webservice_for_person(
597 ppa_admin, permission=OAuthPermission.WRITE_PUBLIC)
598 entry = webservice.get(build_url, api_version="devel").jsonBody()
599 self.assertIsNone(entry["external_dependencies"])
600 response = webservice.patch(
601 entry["self_link"], "application/json",
602 dumps({"external_dependencies": "random"}))
603 self.assertEqual(400, response.status)
604 self.assertIn("Invalid external dependencies", response.body)
605
606 def test_external_dependencies_ppa_owner_valid(self):
607 # PPA admins can look and touch.
608 ppa_admin_team = getUtility(ILaunchpadCelebrities).ppa_admin
609 ppa_admin = self.factory.makePerson(member_of=[ppa_admin_team])
610 build_url = api_url(self.build)
611 logout()
612 webservice = webservice_for_person(
613 ppa_admin, permission=OAuthPermission.WRITE_PUBLIC)
614 entry = webservice.get(build_url, api_version="devel").jsonBody()
615 self.assertIsNone(entry["external_dependencies"])
616 dependencies = "deb http://example.org suite components"
617 response = webservice.patch(
618 entry["self_link"], "application/json",
619 dumps({"external_dependencies": dependencies}))
620 self.assertEqual(209, response.status)
621 self.assertEqual(
622 dependencies, response.jsonBody()["external_dependencies"])
623
565624
566class TestPostprocessCandidate(TestCaseWithFactory):625class TestPostprocessCandidate(TestCaseWithFactory):
567626