Merge ~cjwatson/launchpad:snap-base-arch into launchpad:master

Proposed by Colin Watson
Status: Merged
Approved by: Colin Watson
Approved revision: c1421e9cae13dce4ba5d57d06859022c3007e52e
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~cjwatson/launchpad:snap-base-arch
Merge into: launchpad:master
Diff against target: 558 lines (+256/-31)
7 files modified
database/schema/security.cfg (+5/-0)
lib/lp/snappy/interfaces/snapbase.py (+20/-1)
lib/lp/snappy/model/snap.py (+11/-8)
lib/lp/snappy/model/snapbase.py (+43/-1)
lib/lp/snappy/tests/test_snap.py (+59/-19)
lib/lp/snappy/tests/test_snapbase.py (+116/-0)
lib/lp/testing/factory.py (+2/-2)
Reviewer Review Type Date Requested Status
Cristian Gonzalez (community) Approve
Review via email: mp+402533@code.launchpad.net

Commit message

Add and use SnapBaseArch

Description of the change

A snap base may not support all the architectures supported by its underlying distroseries: in particular, core20 does not support i386. Add a `SnapBaseArch` table along the same lines as the existing `SnapArch` (but with a simpler `setProcessors`, since only registry experts and admins can edit `SnapBase` anyway), and intersect the architectures supported by the snap base with other existing constraints when dispatching snap builds.

DB MP: https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/402532

To post a comment you must log in.
Revision history for this message
Cristian Gonzalez (cristiangsp) wrote :

Looks good and well tested!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/database/schema/security.cfg b/database/schema/security.cfg
index c3aba65..b13d4a8 100644
--- a/database/schema/security.cfg
+++ b/database/schema/security.cfg
@@ -301,6 +301,7 @@ public.sharingjob = SELECT, INSERT, UPDATE, DELETE
301public.snap = SELECT, INSERT, UPDATE, DELETE301public.snap = SELECT, INSERT, UPDATE, DELETE
302public.snaparch = SELECT, INSERT, DELETE302public.snaparch = SELECT, INSERT, DELETE
303public.snapbase = SELECT, INSERT, UPDATE, DELETE303public.snapbase = SELECT, INSERT, UPDATE, DELETE
304public.snapbasearch = SELECT, INSERT, DELETE
304public.snapbuild = SELECT, INSERT, UPDATE, DELETE305public.snapbuild = SELECT, INSERT, UPDATE, DELETE
305public.snapbuildjob = SELECT, INSERT, UPDATE, DELETE306public.snapbuildjob = SELECT, INSERT, UPDATE, DELETE
306public.snapfile = SELECT, INSERT, UPDATE, DELETE307public.snapfile = SELECT, INSERT, UPDATE, DELETE
@@ -842,6 +843,7 @@ public.product = SELECT
842public.snap = SELECT, UPDATE843public.snap = SELECT, UPDATE
843public.snaparch = SELECT844public.snaparch = SELECT
844public.snapbase = SELECT845public.snapbase = SELECT
846public.snapbasearch = SELECT
845public.snapbuild = SELECT, INSERT847public.snapbuild = SELECT, INSERT
846public.snapbuildjob = SELECT848public.snapbuildjob = SELECT
847public.sourcepackagename = SELECT849public.sourcepackagename = SELECT
@@ -1032,6 +1034,7 @@ public.seriessourcepackagebranch = SELECT
1032public.snap = SELECT1034public.snap = SELECT
1033public.snaparch = SELECT1035public.snaparch = SELECT
1034public.snapbase = SELECT1036public.snapbase = SELECT
1037public.snapbasearch = SELECT
1035public.snapbuild = SELECT, UPDATE1038public.snapbuild = SELECT, UPDATE
1036public.snapbuildjob = SELECT, INSERT1039public.snapbuildjob = SELECT, INSERT
1037public.snapfile = SELECT1040public.snapfile = SELECT
@@ -1490,6 +1493,7 @@ public.signedcodeofconduct = SELECT
1490public.snap = SELECT, UPDATE1493public.snap = SELECT, UPDATE
1491public.snaparch = SELECT1494public.snaparch = SELECT
1492public.snapbase = SELECT1495public.snapbase = SELECT
1496public.snapbasearch = SELECT
1493public.snapbuild = SELECT, UPDATE1497public.snapbuild = SELECT, UPDATE
1494public.snapbuildjob = SELECT, INSERT, UPDATE1498public.snapbuildjob = SELECT, INSERT, UPDATE
1495public.snapfile = SELECT, INSERT, UPDATE1499public.snapfile = SELECT, INSERT, UPDATE
@@ -2719,6 +2723,7 @@ public.product = SELECT
2719public.snap = SELECT, UPDATE2723public.snap = SELECT, UPDATE
2720public.snaparch = SELECT2724public.snaparch = SELECT
2721public.snapbase = SELECT2725public.snapbase = SELECT
2726public.snapbasearch = SELECT
2722public.snapbuild = SELECT, INSERT, UPDATE2727public.snapbuild = SELECT, INSERT, UPDATE
2723public.snapbuildjob = SELECT, UPDATE2728public.snapbuildjob = SELECT, UPDATE
2724public.snapfile = SELECT2729public.snapfile = SELECT
diff --git a/lib/lp/snappy/interfaces/snapbase.py b/lib/lp/snappy/interfaces/snapbase.py
index a5fdf90..21e4729 100644
--- a/lib/lp/snappy/interfaces/snapbase.py
+++ b/lib/lp/snappy/interfaces/snapbase.py
@@ -44,12 +44,14 @@ from zope.schema import (
44 Datetime,44 Datetime,
45 Dict,45 Dict,
46 Int,46 Int,
47 List,
47 TextLine,48 TextLine,
48 )49 )
4950
50from lp import _51from lp import _
51from lp.app.errors import NameLookupFailed52from lp.app.errors import NameLookupFailed
52from lp.app.validators.name import name_validator53from lp.app.validators.name import name_validator
54from lp.buildmaster.interfaces.processor import IProcessor
53from lp.registry.interfaces.distroseries import IDistroSeries55from lp.registry.interfaces.distroseries import IDistroSeries
54from lp.services.fields import (56from lp.services.fields import (
55 ContentNameField,57 ContentNameField,
@@ -125,6 +127,12 @@ class ISnapBaseView(Interface):
125 could not be found.127 could not be found.
126 """128 """
127129
130 processors = exported(CollectionField(
131 title=_("Processors"),
132 description=_("The architectures that the snap base supports."),
133 value_type=Reference(schema=IProcessor),
134 readonly=True))
135
128136
129class ISnapBaseEditableAttributes(Interface):137class ISnapBaseEditableAttributes(Interface):
130 """`ISnapBase` attributes that can be edited.138 """`ISnapBase` attributes that can be edited.
@@ -197,6 +205,14 @@ class ISnapBaseEdit(Interface):
197 :param dependency: an `IArchive`.205 :param dependency: an `IArchive`.
198 """206 """
199207
208 @operation_parameters(
209 processors=List(
210 value_type=Reference(schema=IProcessor), required=True))
211 @export_write_operation()
212 @operation_for_version("devel")
213 def setProcessors(processors):
214 """Set the architectures that the snap base supports."""
215
200 @export_destructor_operation()216 @export_destructor_operation()
201 @operation_for_version("devel")217 @operation_for_version("devel")
202 def destroySelf():218 def destroySelf():
@@ -218,11 +234,14 @@ class ISnapBaseSetEdit(Interface):
218 """`ISnapBaseSet` methods that require launchpad.Edit permission."""234 """`ISnapBaseSet` methods that require launchpad.Edit permission."""
219235
220 @call_with(registrant=REQUEST_USER)236 @call_with(registrant=REQUEST_USER)
237 @operation_parameters(
238 processors=List(
239 value_type=Reference(schema=IProcessor), required=False))
221 @export_factory_operation(240 @export_factory_operation(
222 ISnapBase, ["name", "display_name", "distro_series", "build_channels"])241 ISnapBase, ["name", "display_name", "distro_series", "build_channels"])
223 @operation_for_version("devel")242 @operation_for_version("devel")
224 def new(registrant, name, display_name, distro_series, build_channels,243 def new(registrant, name, display_name, distro_series, build_channels,
225 date_created=None):244 processors=None, date_created=None):
226 """Create an `ISnapBase`."""245 """Create an `ISnapBase`."""
227246
228 @operation_parameters(247 @operation_parameters(
diff --git a/lib/lp/snappy/model/snap.py b/lib/lp/snappy/model/snap.py
index 1a108b0..4dc26e5 100644
--- a/lib/lp/snappy/model/snap.py
+++ b/lib/lp/snappy/model/snap.py
@@ -612,7 +612,7 @@ class Snap(Storm, WebhookTargetMixin):
612612
613 processors = property(_getProcessors, setProcessors)613 processors = property(_getProcessors, setProcessors)
614614
615 def _isBuildableArchitectureAllowed(self, das):615 def _isBuildableArchitectureAllowed(self, das, snap_base=None):
616 """Check whether we may build for a buildable `DistroArchSeries`.616 """Check whether we may build for a buildable `DistroArchSeries`.
617617
618 The caller is assumed to have already checked that a suitable chroot618 The caller is assumed to have already checked that a suitable chroot
@@ -624,20 +624,21 @@ class Snap(Storm, WebhookTargetMixin):
624 and das.processor in self.processors624 and das.processor in self.processors
625 and (625 and (
626 das.processor.supports_virtualized626 das.processor.supports_virtualized
627 or not self.require_virtualized))627 or not self.require_virtualized)
628 and (snap_base is None or das.processor in snap_base.processors))
628629
629 def _isArchitectureAllowed(self, das, pocket):630 def _isArchitectureAllowed(self, das, pocket, snap_base=None):
630 return (631 return (
631 das.getChroot(pocket=pocket) is not None632 das.getChroot(pocket=pocket) is not None
632 and self._isBuildableArchitectureAllowed(das))633 and self._isBuildableArchitectureAllowed(das, snap_base=snap_base))
633634
634 def getAllowedArchitectures(self, distro_series=None):635 def getAllowedArchitectures(self, distro_series=None, snap_base=None):
635 """See `ISnap`."""636 """See `ISnap`."""
636 if distro_series is None:637 if distro_series is None:
637 distro_series = self.distro_series638 distro_series = self.distro_series
638 return [639 return [
639 das for das in distro_series.buildable_architectures640 das for das in distro_series.buildable_architectures
640 if self._isBuildableArchitectureAllowed(das)]641 if self._isBuildableArchitectureAllowed(das, snap_base=snap_base)]
641642
642 @property643 @property
643 def store_distro_series(self):644 def store_distro_series(self):
@@ -779,7 +780,8 @@ class Snap(Storm, WebhookTargetMixin):
779 snap_base=None, channels=None, build_request=None):780 snap_base=None, channels=None, build_request=None):
780 """See `ISnap`."""781 """See `ISnap`."""
781 self._checkRequestBuild(requester, archive)782 self._checkRequestBuild(requester, archive)
782 if not self._isArchitectureAllowed(distro_arch_series, pocket):783 if not self._isArchitectureAllowed(
784 distro_arch_series, pocket, snap_base=snap_base):
783 raise SnapBuildDisallowedArchitecture(distro_arch_series, pocket)785 raise SnapBuildDisallowedArchitecture(distro_arch_series, pocket)
784786
785 if not channels:787 if not channels:
@@ -898,7 +900,8 @@ class Snap(Storm, WebhookTargetMixin):
898 # minimise confusion.900 # minimise confusion.
899 supported_arches = OrderedDict(901 supported_arches = OrderedDict(
900 (das.architecturetag, das) for das in sorted(902 (das.architecturetag, das) for das in sorted(
901 self.getAllowedArchitectures(distro_series),903 self.getAllowedArchitectures(
904 distro_series, snap_base=snap_base),
902 key=attrgetter("processor.id"))905 key=attrgetter("processor.id"))
903 if (architectures is None or906 if (architectures is None or
904 das.architecturetag in architectures))907 das.architecturetag in architectures))
diff --git a/lib/lp/snappy/model/snapbase.py b/lib/lp/snappy/model/snapbase.py
index 8916c61..c2f2a76 100644
--- a/lib/lp/snappy/model/snapbase.py
+++ b/lib/lp/snappy/model/snapbase.py
@@ -27,6 +27,7 @@ from zope.interface import implementer
27from zope.security.proxy import removeSecurityProxy27from zope.security.proxy import removeSecurityProxy
2828
29from lp.app.errors import NotFoundError29from lp.app.errors import NotFoundError
30from lp.buildmaster.model.processor import Processor
30from lp.registry.interfaces.pocket import PackagePublishingPocket31from lp.registry.interfaces.pocket import PackagePublishingPocket
31from lp.registry.model.person import Person32from lp.registry.model.person import Person
32from lp.services.database.constants import DEFAULT33from lp.services.database.constants import DEFAULT
@@ -85,6 +86,30 @@ class SnapBase(Storm):
85 self.date_created = date_created86 self.date_created = date_created
86 self.is_default = False87 self.is_default = False
8788
89 def _getProcessors(self):
90 return list(Store.of(self).find(
91 Processor,
92 Processor.id == SnapBaseArch.processor_id,
93 SnapBaseArch.snap_base == self))
94
95 def setProcessors(self, processors):
96 """See `ISnapBase`."""
97 enablements = dict(Store.of(self).find(
98 (Processor, SnapBaseArch),
99 Processor.id == SnapBaseArch.processor_id,
100 SnapBaseArch.snap_base == self))
101 for proc in enablements:
102 if proc not in processors:
103 Store.of(self).remove(enablements[proc])
104 for proc in processors:
105 if proc not in self.processors:
106 snap_base_arch = SnapBaseArch()
107 snap_base_arch.snap_base = self
108 snap_base_arch.processor = proc
109 Store.of(self).add(snap_base_arch)
110
111 processors = property(_getProcessors, setProcessors)
112
88 @property113 @property
89 def dependencies(self):114 def dependencies(self):
90 """See `ISnapBase`."""115 """See `ISnapBase`."""
@@ -145,18 +170,35 @@ class SnapBase(Storm):
145 Store.of(self).remove(self)170 Store.of(self).remove(self)
146171
147172
173class SnapBaseArch(Storm):
174 """Link table to back `SnapArch.processors`."""
175
176 __storm_table__ = "SnapBaseArch"
177 __storm_primary__ = ("snap_base_id", "processor_id")
178
179 snap_base_id = Int(name="snap_base", allow_none=False)
180 snap_base = Reference(snap_base_id, "SnapBase.id")
181
182 processor_id = Int(name="processor", allow_none=False)
183 processor = Reference(processor_id, "Processor.id")
184
185
148@implementer(ISnapBaseSet)186@implementer(ISnapBaseSet)
149class SnapBaseSet:187class SnapBaseSet:
150 """See `ISnapBaseSet`."""188 """See `ISnapBaseSet`."""
151189
152 def new(self, registrant, name, display_name, distro_series,190 def new(self, registrant, name, display_name, distro_series,
153 build_channels, date_created=DEFAULT):191 build_channels, processors=None, date_created=DEFAULT):
154 """See `ISnapBaseSet`."""192 """See `ISnapBaseSet`."""
155 store = IMasterStore(SnapBase)193 store = IMasterStore(SnapBase)
156 snap_base = SnapBase(194 snap_base = SnapBase(
157 registrant, name, display_name, distro_series, build_channels,195 registrant, name, display_name, distro_series, build_channels,
158 date_created=date_created)196 date_created=date_created)
159 store.add(snap_base)197 store.add(snap_base)
198 if processors is None:
199 processors = [
200 das.processor for das in distro_series.enabled_architectures]
201 snap_base.setProcessors(processors)
160 return snap_base202 return snap_base
161203
162 def __iter__(self):204 def __iter__(self):
diff --git a/lib/lp/snappy/tests/test_snap.py b/lib/lp/snappy/tests/test_snap.py
index be338d7..50d1269 100644
--- a/lib/lp/snappy/tests/test_snap.py
+++ b/lib/lp/snappy/tests/test_snap.py
@@ -300,7 +300,8 @@ class TestSnap(TestCaseWithFactory):
300 snap = self.factory.makeSnap(300 snap = self.factory.makeSnap(
301 distroseries=distroarchseries.distroseries, processors=[processor])301 distroseries=distroarchseries.distroseries, processors=[processor])
302 with admin_logged_in():302 with admin_logged_in():
303 snap_base = self.factory.makeSnapBase()303 snap_base = self.factory.makeSnapBase(
304 distro_series=distroarchseries.distroseries)
304 build = snap.requestBuild(305 build = snap.requestBuild(
305 snap.owner, snap.distro_series.main_archive, distroarchseries,306 snap.owner, snap.distro_series.main_archive, distroarchseries,
306 PackagePublishingPocket.UPDATES, snap_base=snap_base)307 PackagePublishingPocket.UPDATES, snap_base=snap_base)
@@ -745,20 +746,21 @@ class TestSnap(TestCaseWithFactory):
745 # base, requestBuildsFromJob requests builds for the appropriate746 # base, requestBuildsFromJob requests builds for the appropriate
746 # distroseries for the base.747 # distroseries for the base.
747 self.useFixture(GitHostingFixture(blob="base: test-base\n"))748 self.useFixture(GitHostingFixture(blob="base: test-base\n"))
748 with admin_logged_in():749 distroseries = self.factory.makeDistroSeries()
749 snap_base = self.factory.makeSnapBase(
750 name="test-base",
751 build_channels={"snapcraft": "stable/launchpad-buildd"})
752 self.factory.makeSnapBase()
753 for arch_tag in ("mips64el", "riscv64"):750 for arch_tag in ("mips64el", "riscv64"):
754 self.makeBuildableDistroArchSeries(751 self.makeBuildableDistroArchSeries(
755 distroseries=snap_base.distro_series, architecturetag=arch_tag,752 distroseries=distroseries, architecturetag=arch_tag,
756 processor=self.factory.makeProcessor(753 processor=self.factory.makeProcessor(
757 name=arch_tag, supports_virtualized=True))754 name=arch_tag, supports_virtualized=True))
755 with admin_logged_in():
756 snap_base = self.factory.makeSnapBase(
757 name="test-base", distro_series=distroseries,
758 build_channels={"snapcraft": "stable/launchpad-buildd"})
759 self.factory.makeSnapBase()
758 snap = self.factory.makeSnap(760 snap = self.factory.makeSnap(
759 distroseries=None, git_ref=self.factory.makeGitRefs()[0])761 distroseries=None, git_ref=self.factory.makeGitRefs()[0])
760 job = getUtility(ISnapRequestBuildsJobSource).create(762 job = getUtility(ISnapRequestBuildsJobSource).create(
761 snap, snap.owner.teamowner, snap_base.distro_series.main_archive,763 snap, snap.owner.teamowner, distroseries.main_archive,
762 PackagePublishingPocket.RELEASE, None)764 PackagePublishingPocket.RELEASE, None)
763 self.assertEqual(765 self.assertEqual(
764 get_transaction_timestamp(IStore(snap)), job.date_created)766 get_transaction_timestamp(IStore(snap)), job.date_created)
@@ -769,27 +771,29 @@ class TestSnap(TestCaseWithFactory):
769 build_request=job.build_request)771 build_request=job.build_request)
770 self.assertRequestedBuildsMatch(772 self.assertRequestedBuildsMatch(
771 builds, job, ["mips64el", "riscv64"], snap_base,773 builds, job, ["mips64el", "riscv64"], snap_base,
772 snap_base.build_channels, distro_series=snap_base.distro_series)774 snap_base.build_channels, distro_series=distroseries)
773775
774 def test_requestBuildsFromJob_no_distroseries_no_explicit_base(self):776 def test_requestBuildsFromJob_no_distroseries_no_explicit_base(self):
775 # If the snap doesn't specify a distroseries and has no explicit777 # If the snap doesn't specify a distroseries and has no explicit
776 # base, requestBuildsFromJob requests builds for the appropriate778 # base, requestBuildsFromJob requests builds for the appropriate
777 # distroseries for the default base.779 # distroseries for the default base.
778 self.useFixture(GitHostingFixture(blob="name: foo\n"))780 self.useFixture(GitHostingFixture(blob="name: foo\n"))
781 distroseries = self.factory.makeDistroSeries()
782 for arch_tag in ("mips64el", "riscv64"):
783 self.makeBuildableDistroArchSeries(
784 distroseries=distroseries, architecturetag=arch_tag,
785 processor=self.factory.makeProcessor(
786 name=arch_tag, supports_virtualized=True))
779 with admin_logged_in():787 with admin_logged_in():
780 snap_base = self.factory.makeSnapBase(788 snap_base = self.factory.makeSnapBase(
789 distro_series=distroseries,
781 build_channels={"snapcraft": "stable/launchpad-buildd"})790 build_channels={"snapcraft": "stable/launchpad-buildd"})
782 getUtility(ISnapBaseSet).setDefault(snap_base)791 getUtility(ISnapBaseSet).setDefault(snap_base)
783 self.factory.makeSnapBase()792 self.factory.makeSnapBase()
784 for arch_tag in ("mips64el", "riscv64"):
785 self.makeBuildableDistroArchSeries(
786 distroseries=snap_base.distro_series, architecturetag=arch_tag,
787 processor=self.factory.makeProcessor(
788 name=arch_tag, supports_virtualized=True))
789 snap = self.factory.makeSnap(793 snap = self.factory.makeSnap(
790 distroseries=None, git_ref=self.factory.makeGitRefs()[0])794 distroseries=None, git_ref=self.factory.makeGitRefs()[0])
791 job = getUtility(ISnapRequestBuildsJobSource).create(795 job = getUtility(ISnapRequestBuildsJobSource).create(
792 snap, snap.owner.teamowner, snap_base.distro_series.main_archive,796 snap, snap.owner.teamowner, distroseries.main_archive,
793 PackagePublishingPocket.RELEASE, None)797 PackagePublishingPocket.RELEASE, None)
794 self.assertEqual(798 self.assertEqual(
795 get_transaction_timestamp(IStore(snap)), job.date_created)799 get_transaction_timestamp(IStore(snap)), job.date_created)
@@ -800,7 +804,7 @@ class TestSnap(TestCaseWithFactory):
800 build_request=job.build_request)804 build_request=job.build_request)
801 self.assertRequestedBuildsMatch(805 self.assertRequestedBuildsMatch(
802 builds, job, ["mips64el", "riscv64"], snap_base,806 builds, job, ["mips64el", "riscv64"], snap_base,
803 snap_base.build_channels, distro_series=snap_base.distro_series)807 snap_base.build_channels, distro_series=distroseries)
804808
805 def test_requestBuildsFromJob_no_distroseries_no_default_base(self):809 def test_requestBuildsFromJob_no_distroseries_no_default_base(self):
806 # If the snap doesn't specify a distroseries and has an explicit810 # If the snap doesn't specify a distroseries and has an explicit
@@ -821,6 +825,40 @@ class TestSnap(TestCaseWithFactory):
821 job.requester, job.archive, job.pocket,825 job.requester, job.archive, job.pocket,
822 build_request=job.build_request)826 build_request=job.build_request)
823827
828 def test_requestBuildsFromJob_snap_base_architectures(self):
829 # requestBuildsFromJob intersects the architectures supported by the
830 # snap base with any other constraints.
831 self.useFixture(GitHostingFixture(blob="base: test-base\n"))
832 processors = [
833 self.factory.makeProcessor(supports_virtualized=True)
834 for _ in range(3)]
835 distroseries = self.factory.makeDistroSeries()
836 for processor in processors:
837 self.makeBuildableDistroArchSeries(
838 distroseries=distroseries, architecturetag=processor.name,
839 processor=processor)
840 with admin_logged_in():
841 snap_base = self.factory.makeSnapBase(
842 name="test-base", distro_series=distroseries,
843 build_channels={"snapcraft": "stable/launchpad-buildd"},
844 processors=processors[:2])
845 snap = self.factory.makeSnap(
846 distroseries=None, git_ref=self.factory.makeGitRefs()[0])
847 job = getUtility(ISnapRequestBuildsJobSource).create(
848 snap, snap.owner.teamowner, snap_base.distro_series.main_archive,
849 PackagePublishingPocket.RELEASE, None)
850 self.assertEqual(
851 get_transaction_timestamp(IStore(snap)), job.date_created)
852 transaction.commit()
853 with person_logged_in(job.requester):
854 builds = snap.requestBuildsFromJob(
855 job.requester, job.archive, job.pocket,
856 build_request=job.build_request)
857 self.assertRequestedBuildsMatch(
858 builds, job, [processor.name for processor in processors[:2]],
859 snap_base, snap_base.build_channels,
860 distro_series=snap_base.distro_series)
861
824 def test_requestBuildsFromJob_unsupported_remote(self):862 def test_requestBuildsFromJob_unsupported_remote(self):
825 # If the snap is based on an external Git repository from which we863 # If the snap is based on an external Git repository from which we
826 # don't support fetching blobs, requestBuildsFromJob falls back to864 # don't support fetching blobs, requestBuildsFromJob falls back to
@@ -2544,12 +2582,14 @@ class TestSnapSet(TestCaseWithFactory):
2544 def test_makeAutoBuilds_infers_distroseries(self):2582 def test_makeAutoBuilds_infers_distroseries(self):
2545 # ISnapSet.makeAutoBuilds can infer the series of a snap from the base2583 # ISnapSet.makeAutoBuilds can infer the series of a snap from the base
2546 # specified in its snapcraft.yaml.2584 # specified in its snapcraft.yaml.
2547 with admin_logged_in():2585 distroseries = self.factory.makeDistroSeries()
2548 snap_base = self.factory.makeSnapBase(name="core20")
2549 das = self.makeBuildableDistroArchSeries(2586 das = self.makeBuildableDistroArchSeries(
2550 distroseries=snap_base.distro_series, architecturetag='riscv64',2587 distroseries=distroseries, architecturetag='riscv64',
2551 processor=self.factory.makeProcessor(2588 processor=self.factory.makeProcessor(
2552 name='riscv64', supports_virtualized=True))2589 name='riscv64', supports_virtualized=True))
2590 with admin_logged_in():
2591 snap_base = self.factory.makeSnapBase(
2592 name="core20", distro_series=distroseries)
2553 [git_ref] = self.factory.makeGitRefs()2593 [git_ref] = self.factory.makeGitRefs()
2554 owner = self.factory.makePerson()2594 owner = self.factory.makePerson()
2555 snap = self.factory.makeSnap(2595 snap = self.factory.makeSnap(
diff --git a/lib/lp/snappy/tests/test_snapbase.py b/lib/lp/snappy/tests/test_snapbase.py
index 67754d2..c528f40 100644
--- a/lib/lp/snappy/tests/test_snapbase.py
+++ b/lib/lp/snappy/tests/test_snapbase.py
@@ -31,6 +31,7 @@ from lp.snappy.interfaces.snapbase import (
31 )31 )
32from lp.soyuz.interfaces.component import IComponentSet32from lp.soyuz.interfaces.component import IComponentSet
33from lp.testing import (33from lp.testing import (
34 admin_logged_in,
34 api_url,35 api_url,
35 celebrity_logged_in,36 celebrity_logged_in,
36 logout,37 logout,
@@ -78,6 +79,56 @@ class TestSnapBase(TestCaseWithFactory):
78 self.assertRaises(CannotDeleteSnapBase, snap_base.destroySelf)79 self.assertRaises(CannotDeleteSnapBase, snap_base.destroySelf)
7980
8081
82class TestSnapBaseProcessors(TestCaseWithFactory):
83
84 layer = ZopelessDatabaseLayer
85
86 def setUp(self):
87 super(TestSnapBaseProcessors, self).setUp(user="foo.bar@canonical.com")
88 self.unrestricted_procs = [
89 self.factory.makeProcessor() for _ in range(3)]
90 self.restricted_procs = [
91 self.factory.makeProcessor(restricted=True, build_by_default=False)
92 for _ in range(2)]
93 self.procs = self.unrestricted_procs + self.restricted_procs
94 self.factory.makeProcessor()
95 self.distroseries = self.factory.makeDistroSeries()
96 for processor in self.procs:
97 self.factory.makeDistroArchSeries(
98 distroseries=self.distroseries, architecturetag=processor.name,
99 processor=processor)
100
101 def test_new_default_processors(self):
102 # SnapBaseSet.new creates a SnapBaseArch for each available
103 # Processor for the corresponding series.
104 snap_base = getUtility(ISnapBaseSet).new(
105 registrant=self.factory.makePerson(),
106 name=self.factory.getUniqueUnicode(),
107 display_name=self.factory.getUniqueUnicode(),
108 distro_series=self.distroseries, build_channels={})
109 self.assertContentEqual(self.procs, snap_base.processors)
110
111 def test_new_override_processors(self):
112 # SnapBaseSet.new can be given a custom set of processors.
113 snap_base = getUtility(ISnapBaseSet).new(
114 registrant=self.factory.makePerson(),
115 name=self.factory.getUniqueUnicode(),
116 display_name=self.factory.getUniqueUnicode(),
117 distro_series=self.distroseries, build_channels={},
118 processors=self.procs[:2])
119 self.assertContentEqual(self.procs[:2], snap_base.processors)
120
121 def test_set(self):
122 # The property remembers its value correctly.
123 snap_base = self.factory.makeSnapBase()
124 snap_base.setProcessors(self.restricted_procs)
125 self.assertContentEqual(self.restricted_procs, snap_base.processors)
126 snap_base.setProcessors(self.procs)
127 self.assertContentEqual(self.procs, snap_base.processors)
128 snap_base.setProcessors([])
129 self.assertContentEqual([], snap_base.processors)
130
131
81class TestSnapBaseSet(TestCaseWithFactory):132class TestSnapBaseSet(TestCaseWithFactory):
82133
83 layer = ZopelessDatabaseLayer134 layer = ZopelessDatabaseLayer
@@ -368,6 +419,71 @@ class TestSnapBaseWebservice(TestCaseWithFactory):
368 with person_logged_in(person):419 with person_logged_in(person):
369 self.assertEqual([], list(snap_base.dependencies))420 self.assertEqual([], list(snap_base.dependencies))
370421
422 def setUpProcessors(self):
423 self.unrestricted_procs = [
424 self.factory.makeProcessor() for _ in range(3)]
425 self.unrestricted_proc_names = [
426 processor.name for processor in self.unrestricted_procs]
427 self.restricted_procs = [
428 self.factory.makeProcessor(restricted=True, build_by_default=False)
429 for _ in range(2)]
430 self.restricted_proc_names = [
431 processor.name for processor in self.restricted_procs]
432 self.procs = self.unrestricted_procs + self.restricted_procs
433 self.factory.makeProcessor()
434 self.distroseries = self.factory.makeDistroSeries()
435 for processor in self.procs:
436 self.factory.makeDistroArchSeries(
437 distroseries=self.distroseries, architecturetag=processor.name,
438 processor=processor)
439
440 def setProcessors(self, user, snap_base_url, names):
441 ws = webservice_for_person(
442 user, permission=OAuthPermission.WRITE_PUBLIC)
443 return ws.named_post(
444 snap_base_url, "setProcessors",
445 processors=["/+processors/%s" % name for name in names],
446 api_version="devel")
447
448 def assertProcessors(self, user, snap_base_url, names):
449 body = webservice_for_person(user).get(
450 snap_base_url + "/processors", api_version="devel").jsonBody()
451 self.assertContentEqual(
452 names, [entry["name"] for entry in body["entries"]])
453
454 def test_setProcessors_admin(self):
455 """An admin can change the supported processor set."""
456 self.setUpProcessors()
457 with admin_logged_in():
458 snap_base = self.factory.makeSnapBase(
459 distro_series=self.distroseries,
460 processors=self.unrestricted_procs)
461 snap_base_url = api_url(snap_base)
462 admin = self.factory.makeAdministrator()
463 self.assertProcessors(
464 admin, snap_base_url, self.unrestricted_proc_names)
465
466 response = self.setProcessors(
467 admin, snap_base_url,
468 [self.unrestricted_proc_names[0], self.restricted_proc_names[0]])
469 self.assertEqual(200, response.status)
470 self.assertProcessors(
471 admin, snap_base_url,
472 [self.unrestricted_proc_names[0], self.restricted_proc_names[0]])
473
474 def test_setProcessors_non_admin_forbidden(self):
475 """Only admins and registry experts can call setProcessors."""
476 self.setUpProcessors()
477 with admin_logged_in():
478 snap_base = self.factory.makeSnapBase(
479 distro_series=self.distroseries)
480 snap_base_url = api_url(snap_base)
481 person = self.factory.makePerson()
482
483 response = self.setProcessors(
484 person, snap_base_url, [self.unrestricted_proc_names[0]])
485 self.assertEqual(401, response.status)
486
371 def test_collection(self):487 def test_collection(self):
372 # lp.snap_bases is a collection of all SnapBases.488 # lp.snap_bases is a collection of all SnapBases.
373 person = self.factory.makePerson()489 person = self.factory.makePerson()
diff --git a/lib/lp/testing/factory.py b/lib/lp/testing/factory.py
index e95e83d..e65a596 100644
--- a/lib/lp/testing/factory.py
+++ b/lib/lp/testing/factory.py
@@ -4911,7 +4911,7 @@ class BareLaunchpadObjectFactory(ObjectFactory):
4911 return snappy_series4911 return snappy_series
49124912
4913 def makeSnapBase(self, registrant=None, name=None, display_name=None,4913 def makeSnapBase(self, registrant=None, name=None, display_name=None,
4914 distro_series=None, build_channels=None,4914 distro_series=None, build_channels=None, processors=None,
4915 date_created=DEFAULT):4915 date_created=DEFAULT):
4916 """Make a new SnapBase."""4916 """Make a new SnapBase."""
4917 if registrant is None:4917 if registrant is None:
@@ -4927,7 +4927,7 @@ class BareLaunchpadObjectFactory(ObjectFactory):
4927 build_channels = {u"snapcraft": u"stable"}4927 build_channels = {u"snapcraft": u"stable"}
4928 return getUtility(ISnapBaseSet).new(4928 return getUtility(ISnapBaseSet).new(
4929 registrant, name, display_name, distro_series, build_channels,4929 registrant, name, display_name, distro_series, build_channels,
4930 date_created=date_created)4930 processors=processors, date_created=date_created)
49314931
4932 def makeOCIProjectName(self, name=None):4932 def makeOCIProjectName(self, name=None):
4933 if name is None:4933 if name is None:

Subscribers

People subscribed via source and target branches

to status/vote changes: