Merge lp:~cjwatson/launchpad/snap-build-channels into lp:launchpad

Proposed by Colin Watson
Status: Merged
Merged at revision: 18579
Proposed branch: lp:~cjwatson/launchpad/snap-build-channels
Merge into: lp:launchpad
Diff against target: 512 lines (+177/-25)
9 files modified
lib/lp/snappy/interfaces/snap.py (+22/-3)
lib/lp/snappy/interfaces/snapbuild.py (+9/-1)
lib/lp/snappy/model/snap.py (+22/-10)
lib/lp/snappy/model/snapbuild.py (+7/-3)
lib/lp/snappy/model/snapbuildbehaviour.py (+3/-1)
lib/lp/snappy/tests/test_snap.py (+93/-1)
lib/lp/snappy/tests/test_snapbuild.py (+1/-0)
lib/lp/snappy/tests/test_snapbuildbehaviour.py (+12/-1)
lib/lp/testing/factory.py (+8/-5)
To merge this branch: bzr merge lp:~cjwatson/launchpad/snap-build-channels
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+337360@code.launchpad.net

Commit message

Add the ability to select source channels when building snaps.

Description of the change

This will let us run snap builds where snapcraft is installed as a snap. It depends on https://code.launchpad.net/~cjwatson/launchpad-buildd/snapception/+merge/337126 and https://code.launchpad.net/~cjwatson/launchpad/db-snap-build-channels/+merge/337361.

We'll probably also want a feature flag to change the default, but that can be added a little later, once the basic mechanism works.

To post a comment you must log in.
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/snappy/interfaces/snap.py'
--- lib/lp/snappy/interfaces/snap.py 2017-08-22 11:36:30 +0000
+++ lib/lp/snappy/interfaces/snap.py 2018-03-22 16:49:25 +0000
@@ -1,4 +1,4 @@
1# Copyright 2015-2017 Canonical Ltd. This software is licensed under the1# Copyright 2015-2018 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"""Snap package interfaces."""4"""Snap package interfaces."""
@@ -63,6 +63,7 @@
63 Bool,63 Bool,
64 Choice,64 Choice,
65 Datetime,65 Datetime,
66 Dict,
66 Int,67 Int,
67 List,68 List,
68 Text,69 Text,
@@ -281,17 +282,27 @@
281 @operation_parameters(282 @operation_parameters(
282 archive=Reference(schema=IArchive),283 archive=Reference(schema=IArchive),
283 distro_arch_series=Reference(schema=IDistroArchSeries),284 distro_arch_series=Reference(schema=IDistroArchSeries),
284 pocket=Choice(vocabulary=PackagePublishingPocket))285 pocket=Choice(vocabulary=PackagePublishingPocket),
286 channels=Dict(
287 title=_("Source snap channels to use for this build."),
288 description=_(
289 "A dictionary mapping snap names to channels to use for this "
290 "build. Currently only 'core' and 'snapcraft' keys are "
291 "supported."),
292 key_type=TextLine(), required=False))
285 # Really ISnapBuild, patched in lp.snappy.interfaces.webservice.293 # Really ISnapBuild, patched in lp.snappy.interfaces.webservice.
286 @export_factory_operation(Interface, [])294 @export_factory_operation(Interface, [])
287 @operation_for_version("devel")295 @operation_for_version("devel")
288 def requestBuild(requester, archive, distro_arch_series, pocket):296 def requestBuild(requester, archive, distro_arch_series, pocket,
297 channels=None):
289 """Request that the snap package be built.298 """Request that the snap package be built.
290299
291 :param requester: The person requesting the build.300 :param requester: The person requesting the build.
292 :param archive: The IArchive to associate the build with.301 :param archive: The IArchive to associate the build with.
293 :param distro_arch_series: The architecture to build for.302 :param distro_arch_series: The architecture to build for.
294 :param pocket: The pocket that should be targeted.303 :param pocket: The pocket that should be targeted.
304 :param channels: A dictionary mapping snap names to channels to use
305 for this build.
295 :return: `ISnapBuild`.306 :return: `ISnapBuild`.
296 """307 """
297308
@@ -509,6 +520,14 @@
509 "The package stream within the source distribution series to use "520 "The package stream within the source distribution series to use "
510 "when building the snap package.")))521 "when building the snap package.")))
511522
523 auto_build_channels = exported(Dict(
524 title=_("Source snap channels for automatic builds"),
525 key_type=TextLine(), required=False, readonly=False,
526 description=_(
527 "A dictionary mapping snap names to channels to use when building "
528 "this snap package. Currently only 'core' and 'snapcraft' keys "
529 "are supported.")))
530
512 is_stale = Bool(531 is_stale = Bool(
513 title=_("Snap package is stale and is due to be rebuilt."),532 title=_("Snap package is stale and is due to be rebuilt."),
514 required=True, readonly=False)533 required=True, readonly=False)
515534
=== modified file 'lib/lp/snappy/interfaces/snapbuild.py'
--- lib/lp/snappy/interfaces/snapbuild.py 2018-02-16 21:48:03 +0000
+++ lib/lp/snappy/interfaces/snapbuild.py 2018-03-22 16:49:25 +0000
@@ -1,4 +1,4 @@
1# Copyright 2015-2017 Canonical Ltd. This software is licensed under the1# Copyright 2015-2018 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"""Snap package build interfaces."""4"""Snap package build interfaces."""
@@ -149,6 +149,14 @@
149 title=_("The pocket for which to build."),149 title=_("The pocket for which to build."),
150 vocabulary=PackagePublishingPocket, required=True, readonly=True))150 vocabulary=PackagePublishingPocket, required=True, readonly=True))
151151
152 channels = exported(Dict(
153 title=_("Source snap channels to use for this build."),
154 description=_(
155 "A dictionary mapping snap names to channels to use for this "
156 "build. Currently only 'core' and 'snapcraft' keys are "
157 "supported."),
158 key_type=TextLine()))
159
152 virtualized = Bool(160 virtualized = Bool(
153 title=_("If True, this build is virtualized."), readonly=True)161 title=_("If True, this build is virtualized."), readonly=True)
154162
155163
=== modified file 'lib/lp/snappy/model/snap.py'
--- lib/lp/snappy/model/snap.py 2017-11-10 11:23:27 +0000
+++ lib/lp/snappy/model/snap.py 2018-03-22 16:49:25 +0000
@@ -1,4 +1,4 @@
1# Copyright 2015-2017 Canonical Ltd. This software is licensed under the1# Copyright 2015-2018 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
@@ -94,6 +94,7 @@
94 )94 )
95from lp.services.database.stormexpr import (95from lp.services.database.stormexpr import (
96 Greatest,96 Greatest,
97 IsDistinctFrom,
97 NullsLast,98 NullsLast,
98 )99 )
99from lp.services.features import getFeatureFlag100from lp.services.features import getFeatureFlag
@@ -189,6 +190,8 @@
189190
190 auto_build_pocket = DBEnum(enum=PackagePublishingPocket, allow_none=True)191 auto_build_pocket = DBEnum(enum=PackagePublishingPocket, allow_none=True)
191192
193 auto_build_channels = JSON('auto_build_channels', allow_none=True)
194
192 is_stale = Bool(name='is_stale', allow_none=False)195 is_stale = Bool(name='is_stale', allow_none=False)
193196
194 require_virtualized = Bool(name='require_virtualized')197 require_virtualized = Bool(name='require_virtualized')
@@ -209,9 +212,10 @@
209 def __init__(self, registrant, owner, distro_series, name,212 def __init__(self, registrant, owner, distro_series, name,
210 description=None, branch=None, git_ref=None, auto_build=False,213 description=None, branch=None, git_ref=None, auto_build=False,
211 auto_build_archive=None, auto_build_pocket=None,214 auto_build_archive=None, auto_build_pocket=None,
212 require_virtualized=True, date_created=DEFAULT,215 auto_build_channels=None, require_virtualized=True,
213 private=False, store_upload=False, store_series=None,216 date_created=DEFAULT, private=False, store_upload=False,
214 store_name=None, store_secrets=None, store_channels=None):217 store_series=None, store_name=None, store_secrets=None,
218 store_channels=None):
215 """Construct a `Snap`."""219 """Construct a `Snap`."""
216 super(Snap, self).__init__()220 super(Snap, self).__init__()
217 self.registrant = registrant221 self.registrant = registrant
@@ -224,6 +228,7 @@
224 self.auto_build = auto_build228 self.auto_build = auto_build
225 self.auto_build_archive = auto_build_archive229 self.auto_build_archive = auto_build_archive
226 self.auto_build_pocket = auto_build_pocket230 self.auto_build_pocket = auto_build_pocket
231 self.auto_build_channels = auto_build_channels
227 self.require_virtualized = require_virtualized232 self.require_virtualized = require_virtualized
228 self.date_created = date_created233 self.date_created = date_created
229 self.date_last_modified = date_created234 self.date_last_modified = date_created
@@ -439,7 +444,8 @@
439 return False444 return False
440 return True445 return True
441446
442 def requestBuild(self, requester, archive, distro_arch_series, pocket):447 def requestBuild(self, requester, archive, distro_arch_series, pocket,
448 channels=None):
443 """See `ISnap`."""449 """See `ISnap`."""
444 if not requester.inTeam(self.owner):450 if not requester.inTeam(self.owner):
445 raise SnapNotOwner(451 raise SnapNotOwner(
@@ -459,12 +465,14 @@
459 SnapBuild.archive_id == archive.id,465 SnapBuild.archive_id == archive.id,
460 SnapBuild.distro_arch_series_id == distro_arch_series.id,466 SnapBuild.distro_arch_series_id == distro_arch_series.id,
461 SnapBuild.pocket == pocket,467 SnapBuild.pocket == pocket,
468 SnapBuild.channels == channels,
462 SnapBuild.status == BuildStatus.NEEDSBUILD)469 SnapBuild.status == BuildStatus.NEEDSBUILD)
463 if pending.any() is not None:470 if pending.any() is not None:
464 raise SnapBuildAlreadyPending471 raise SnapBuildAlreadyPending
465472
466 build = getUtility(ISnapBuildSet).new(473 build = getUtility(ISnapBuildSet).new(
467 requester, self, archive, distro_arch_series, pocket)474 requester, self, archive, distro_arch_series, pocket,
475 channels=channels)
468 build.queueBuild()476 build.queueBuild()
469 return build477 return build
470478
@@ -484,7 +492,7 @@
484 try:492 try:
485 build = self.requestBuild(493 build = self.requestBuild(
486 self.owner, self.auto_build_archive, arch,494 self.owner, self.auto_build_archive, arch,
487 self.auto_build_pocket)495 self.auto_build_pocket, self.auto_build_channels)
488 if logger is not None:496 if logger is not None:
489 logger.debug(497 logger.debug(
490 " - %s/%s/%s: Build requested.",498 " - %s/%s/%s: Build requested.",
@@ -659,9 +667,10 @@
659 branch=None, git_repository=None, git_repository_url=None,667 branch=None, git_repository=None, git_repository_url=None,
660 git_path=None, git_ref=None, auto_build=False,668 git_path=None, git_ref=None, auto_build=False,
661 auto_build_archive=None, auto_build_pocket=None,669 auto_build_archive=None, auto_build_pocket=None,
662 require_virtualized=True, processors=None, date_created=DEFAULT,670 auto_build_channels=None, require_virtualized=True,
663 private=False, store_upload=False, store_series=None,671 processors=None, date_created=DEFAULT, private=False,
664 store_name=None, store_secrets=None, store_channels=None):672 store_upload=False, store_series=None, store_name=None,
673 store_secrets=None, store_channels=None):
665 """See `ISnapSet`."""674 """See `ISnapSet`."""
666 if not registrant.inTeam(owner):675 if not registrant.inTeam(owner):
667 if owner.is_team:676 if owner.is_team:
@@ -702,6 +711,7 @@
702 branch=branch, git_ref=git_ref, auto_build=auto_build,711 branch=branch, git_ref=git_ref, auto_build=auto_build,
703 auto_build_archive=auto_build_archive,712 auto_build_archive=auto_build_archive,
704 auto_build_pocket=auto_build_pocket,713 auto_build_pocket=auto_build_pocket,
714 auto_build_channels=auto_build_channels,
705 require_virtualized=require_virtualized, date_created=date_created,715 require_virtualized=require_virtualized, date_created=date_created,
706 private=private, store_upload=store_upload,716 private=private, store_upload=store_upload,
707 store_series=store_series, store_name=store_name,717 store_series=store_series, store_name=store_name,
@@ -917,6 +927,8 @@
917 SnapBuild.snap_id == Snap.id,927 SnapBuild.snap_id == Snap.id,
918 SnapBuild.archive_id == Snap.auto_build_archive_id,928 SnapBuild.archive_id == Snap.auto_build_archive_id,
919 SnapBuild.pocket == Snap.auto_build_pocket,929 SnapBuild.pocket == Snap.auto_build_pocket,
930 Not(IsDistinctFrom(
931 SnapBuild.channels, Snap.auto_build_channels)),
920 # We only want Snaps that haven't had an automatic932 # We only want Snaps that haven't had an automatic
921 # SnapBuild dispatched for them recently.933 # SnapBuild dispatched for them recently.
922 SnapBuild.date_created >= threshold_date)),934 SnapBuild.date_created >= threshold_date)),
923935
=== modified file 'lib/lp/snappy/model/snapbuild.py'
--- lib/lp/snappy/model/snapbuild.py 2018-02-16 21:48:03 +0000
+++ lib/lp/snappy/model/snapbuild.py 2018-03-22 16:49:25 +0000
@@ -22,6 +22,7 @@
22 DateTime,22 DateTime,
23 Desc,23 Desc,
24 Int,24 Int,
25 JSON,
25 Reference,26 Reference,
26 Select,27 Select,
27 SQL,28 SQL,
@@ -142,6 +143,8 @@
142143
143 pocket = DBEnum(enum=PackagePublishingPocket, allow_none=False)144 pocket = DBEnum(enum=PackagePublishingPocket, allow_none=False)
144145
146 channels = JSON('channels', allow_none=True)
147
145 processor_id = Int(name='processor', allow_none=False)148 processor_id = Int(name='processor', allow_none=False)
146 processor = Reference(processor_id, 'Processor.id')149 processor = Reference(processor_id, 'Processor.id')
147 virtualized = Bool(name='virtualized')150 virtualized = Bool(name='virtualized')
@@ -171,7 +174,7 @@
171 failure_count = Int(name='failure_count', allow_none=False)174 failure_count = Int(name='failure_count', allow_none=False)
172175
173 def __init__(self, build_farm_job, requester, snap, archive,176 def __init__(self, build_farm_job, requester, snap, archive,
174 distro_arch_series, pocket, processor, virtualized,177 distro_arch_series, pocket, channels, processor, virtualized,
175 date_created):178 date_created):
176 """Construct a `SnapBuild`."""179 """Construct a `SnapBuild`."""
177 super(SnapBuild, self).__init__()180 super(SnapBuild, self).__init__()
@@ -181,6 +184,7 @@
181 self.archive = archive184 self.archive = archive
182 self.distro_arch_series = distro_arch_series185 self.distro_arch_series = distro_arch_series
183 self.pocket = pocket186 self.pocket = pocket
187 self.channels = channels
184 self.processor = processor188 self.processor = processor
185 self.virtualized = virtualized189 self.virtualized = virtualized
186 self.date_created = date_created190 self.date_created = date_created
@@ -484,7 +488,7 @@
484class SnapBuildSet(SpecificBuildFarmJobSourceMixin):488class SnapBuildSet(SpecificBuildFarmJobSourceMixin):
485489
486 def new(self, requester, snap, archive, distro_arch_series, pocket,490 def new(self, requester, snap, archive, distro_arch_series, pocket,
487 date_created=DEFAULT):491 channels=None, date_created=DEFAULT):
488 """See `ISnapBuildSet`."""492 """See `ISnapBuildSet`."""
489 store = IMasterStore(SnapBuild)493 store = IMasterStore(SnapBuild)
490 build_farm_job = getUtility(IBuildFarmJobSource).new(494 build_farm_job = getUtility(IBuildFarmJobSource).new(
@@ -492,7 +496,7 @@
492 archive)496 archive)
493 snapbuild = SnapBuild(497 snapbuild = SnapBuild(
494 build_farm_job, requester, snap, archive, distro_arch_series,498 build_farm_job, requester, snap, archive, distro_arch_series,
495 pocket, distro_arch_series.processor,499 pocket, channels, distro_arch_series.processor,
496 not distro_arch_series.processor.supports_nonvirtualized500 not distro_arch_series.processor.supports_nonvirtualized
497 or snap.require_virtualized or archive.require_virtualized,501 or snap.require_virtualized or archive.require_virtualized,
498 date_created)502 date_created)
499503
=== modified file 'lib/lp/snappy/model/snapbuildbehaviour.py'
--- lib/lp/snappy/model/snapbuildbehaviour.py 2018-03-01 17:36:31 +0000
+++ lib/lp/snappy/model/snapbuildbehaviour.py 2018-03-22 16:49:25 +0000
@@ -1,4 +1,4 @@
1# Copyright 2015-2017 Canonical Ltd. This software is licensed under the1# Copyright 2015-2018 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"""An `IBuildFarmJobBehaviour` for `SnapBuild`.4"""An `IBuildFarmJobBehaviour` for `SnapBuild`.
@@ -109,6 +109,8 @@
109 logger=logger))109 logger=logger))
110 args["archive_private"] = build.archive.private110 args["archive_private"] = build.archive.private
111 args["build_url"] = canonical_url(build)111 args["build_url"] = canonical_url(build)
112 if build.channels is not None:
113 args["channels"] = build.channels
112 if build.snap.branch is not None:114 if build.snap.branch is not None:
113 args["branch"] = build.snap.branch.bzr_identity115 args["branch"] = build.snap.branch.bzr_identity
114 elif build.snap.git_ref is not None:116 elif build.snap.git_ref is not None:
115117
=== modified file 'lib/lp/snappy/tests/test_snap.py'
--- lib/lp/snappy/tests/test_snap.py 2018-01-23 18:54:30 +0000
+++ lib/lp/snappy/tests/test_snap.py 2018-03-22 16:49:25 +0000
@@ -54,6 +54,7 @@
54 ONE_DAY_AGO,54 ONE_DAY_AGO,
55 UTC_NOW,55 UTC_NOW,
56 )56 )
57from lp.services.database.interfaces import IStore
57from lp.services.database.sqlbase import flush_database_caches58from lp.services.database.sqlbase import flush_database_caches
58from lp.services.features.testing import (59from lp.services.features.testing import (
59 FeatureFixture,60 FeatureFixture,
@@ -188,6 +189,7 @@
188 self.assertEqual(snap.distro_series.main_archive, build.archive)189 self.assertEqual(snap.distro_series.main_archive, build.archive)
189 self.assertEqual(distroarchseries, build.distro_arch_series)190 self.assertEqual(distroarchseries, build.distro_arch_series)
190 self.assertEqual(PackagePublishingPocket.UPDATES, build.pocket)191 self.assertEqual(PackagePublishingPocket.UPDATES, build.pocket)
192 self.assertIsNone(build.channels)
191 self.assertEqual(BuildStatus.NEEDSBUILD, build.status)193 self.assertEqual(BuildStatus.NEEDSBUILD, build.status)
192 store = Store.of(build)194 store = Store.of(build)
193 store.flush()195 store.flush()
@@ -232,6 +234,18 @@
232 queue_record.score()234 queue_record.score()
233 self.assertEqual(2610, queue_record.lastscore)235 self.assertEqual(2610, queue_record.lastscore)
234236
237 def test_requestBuild_channels(self):
238 # requestBuild can select non-default channels.
239 processor = self.factory.makeProcessor(supports_virtualized=True)
240 distroarchseries = self.makeBuildableDistroArchSeries(
241 processor=processor)
242 snap = self.factory.makeSnap(
243 distroseries=distroarchseries.distroseries, processors=[processor])
244 build = snap.requestBuild(
245 snap.owner, snap.distro_series.main_archive, distroarchseries,
246 PackagePublishingPocket.UPDATES, channels={"snapcraft": "edge"})
247 self.assertEqual({"snapcraft": "edge"}, build.channels)
248
235 def test_requestBuild_rejects_repeats(self):249 def test_requestBuild_rejects_repeats(self):
236 # requestBuild refuses if there is already a pending build.250 # requestBuild refuses if there is already a pending build.
237 distroseries = self.factory.makeDistroSeries()251 distroseries = self.factory.makeDistroSeries()
@@ -355,10 +369,36 @@
355 with person_logged_in(snap.owner):369 with person_logged_in(snap.owner):
356 builds = snap.requestAutoBuilds()370 builds = snap.requestAutoBuilds()
357 self.assertThat(builds, MatchesSetwise(371 self.assertThat(builds, MatchesSetwise(
372 *(MatchesStructure(
373 requester=Equals(snap.owner), snap=Equals(snap),
374 archive=Equals(archive), distro_arch_series=Equals(das),
375 pocket=Equals(PackagePublishingPocket.PROPOSED),
376 channels=Is(None))
377 for das in dases[:2])))
378
379 def test_requestAutoBuilds_channels(self):
380 # requestAutoBuilds honours Snap.auto_build_channels.
381 distroseries = self.factory.makeDistroSeries()
382 dases = []
383 for _ in range(3):
384 processor = self.factory.makeProcessor(supports_virtualized=True)
385 dases.append(self.makeBuildableDistroArchSeries(
386 distroseries=distroseries, processor=processor))
387 archive = self.factory.makeArchive()
388 snap = self.factory.makeSnap(
389 distroseries=distroseries,
390 processors=[das.processor for das in dases[:2]],
391 auto_build_archive=archive,
392 auto_build_pocket=PackagePublishingPocket.PROPOSED,
393 auto_build_channels={"snapcraft": "edge"})
394 with person_logged_in(snap.owner):
395 builds = snap.requestAutoBuilds()
396 self.assertThat(builds, MatchesSetwise(
358 *(MatchesStructure.byEquality(397 *(MatchesStructure.byEquality(
359 requester=snap.owner, snap=snap, archive=archive,398 requester=snap.owner, snap=snap, archive=archive,
360 distro_arch_series=das,399 distro_arch_series=das,
361 pocket=PackagePublishingPocket.PROPOSED)400 pocket=PackagePublishingPocket.PROPOSED,
401 channels={"snapcraft": "edge"})
362 for das in dases[:2])))402 for das in dases[:2])))
363403
364 def test_getBuilds(self):404 def test_getBuilds(self):
@@ -610,6 +650,7 @@
610 self.assertFalse(snap.auto_build)650 self.assertFalse(snap.auto_build)
611 self.assertIsNone(snap.auto_build_archive)651 self.assertIsNone(snap.auto_build_archive)
612 self.assertIsNone(snap.auto_build_pocket)652 self.assertIsNone(snap.auto_build_pocket)
653 self.assertIsNone(snap.auto_build_channels)
613 self.assertTrue(snap.require_virtualized)654 self.assertTrue(snap.require_virtualized)
614 self.assertFalse(snap.private)655 self.assertFalse(snap.private)
615656
@@ -630,6 +671,7 @@
630 self.assertFalse(snap.auto_build)671 self.assertFalse(snap.auto_build)
631 self.assertIsNone(snap.auto_build_archive)672 self.assertIsNone(snap.auto_build_archive)
632 self.assertIsNone(snap.auto_build_pocket)673 self.assertIsNone(snap.auto_build_pocket)
674 self.assertIsNone(snap.auto_build_channels)
633 self.assertTrue(snap.require_virtualized)675 self.assertTrue(snap.require_virtualized)
634 self.assertFalse(snap.private)676 self.assertFalse(snap.private)
635677
@@ -1008,6 +1050,56 @@
1008 self.assertEqual([], builds)1050 self.assertEqual([], builds)
1009 self.assertEqual([], logger.getLogBuffer().splitlines())1051 self.assertEqual([], logger.getLogBuffer().splitlines())
10101052
1053 def test_makeAutoBuilds_skips_if_built_recently_matching_channels(self):
1054 # ISnapSet.makeAutoBuilds only considers recently-requested builds
1055 # to match a snap if they match its auto_build_channels.
1056 das1, snap1 = self.makeAutoBuildableSnap(is_stale=True)
1057 das2, snap2 = self.makeAutoBuildableSnap(
1058 is_stale=True, auto_build_channels={"snapcraft": "edge"})
1059 # Create some builds with mismatched channels.
1060 self.factory.makeSnapBuild(
1061 requester=snap1.owner, snap=snap1,
1062 archive=snap1.auto_build_archive, distroarchseries=das1,
1063 channels={"snapcraft": "edge"})
1064 self.factory.makeSnapBuild(
1065 requester=snap2.owner, snap=snap2,
1066 archive=snap2.auto_build_archive, distroarchseries=das2,
1067 channels={"snapcraft": "stable"})
1068
1069 logger = BufferLogger()
1070 builds = getUtility(ISnapSet).makeAutoBuilds(logger=logger)
1071 self.assertThat(builds, MatchesSetwise(
1072 MatchesStructure(
1073 requester=Equals(snap1.owner), snap=Equals(snap1),
1074 distro_arch_series=Equals(das1), channels=Is(None),
1075 status=Equals(BuildStatus.NEEDSBUILD)),
1076 MatchesStructure.byEquality(
1077 requester=snap2.owner, snap=snap2, distro_arch_series=das2,
1078 channels={"snapcraft": "edge"}, status=BuildStatus.NEEDSBUILD),
1079 ))
1080 log_entries = logger.getLogBuffer().splitlines()
1081 self.assertEqual(4, len(log_entries))
1082 for das, snap in (das1, snap1), (das2, snap2):
1083 self.assertIn(
1084 "DEBUG Scheduling builds of snap package %s/%s" % (
1085 snap.owner.name, snap.name),
1086 log_entries)
1087 self.assertIn(
1088 "DEBUG - %s/%s/%s: Build requested." % (
1089 snap.owner.name, snap.name, das.architecturetag),
1090 log_entries)
1091 self.assertFalse(snap.is_stale)
1092
1093 # Mark the two snaps stale and try again. There are now matching
1094 # builds so we don't try to request more.
1095 for snap in snap1, snap2:
1096 removeSecurityProxy(snap).is_stale = True
1097 IStore(snap).flush()
1098 logger = BufferLogger()
1099 builds = getUtility(ISnapSet).makeAutoBuilds(logger=logger)
1100 self.assertEqual([], builds)
1101 self.assertEqual([], logger.getLogBuffer().splitlines())
1102
1011 def test_makeAutoBuilds_skips_non_stale_snaps(self):1103 def test_makeAutoBuilds_skips_non_stale_snaps(self):
1012 # ISnapSet.makeAutoBuilds skips snap packages that are not stale.1104 # ISnapSet.makeAutoBuilds skips snap packages that are not stale.
1013 das, snap = self.makeAutoBuildableSnap(is_stale=False)1105 das, snap = self.makeAutoBuildableSnap(is_stale=False)
10141106
=== modified file 'lib/lp/snappy/tests/test_snapbuild.py'
--- lib/lp/snappy/tests/test_snapbuild.py 2018-01-23 10:59:44 +0000
+++ lib/lp/snappy/tests/test_snapbuild.py 2018-03-22 16:49:25 +0000
@@ -597,6 +597,7 @@
597 self.assertEqual(597 self.assertEqual(
598 db_build.distro_arch_series.architecturetag, build["arch_tag"])598 db_build.distro_arch_series.architecturetag, build["arch_tag"])
599 self.assertEqual("Updates", build["pocket"])599 self.assertEqual("Updates", build["pocket"])
600 self.assertIsNone(build["channels"])
600 self.assertIsNone(build["score"])601 self.assertIsNone(build["score"])
601 self.assertFalse(build["can_be_rescored"])602 self.assertFalse(build["can_be_rescored"])
602 self.assertFalse(build["can_be_cancelled"])603 self.assertFalse(build["can_be_cancelled"])
603604
=== modified file 'lib/lp/snappy/tests/test_snapbuildbehaviour.py'
--- lib/lp/snappy/tests/test_snapbuildbehaviour.py 2018-03-01 17:36:31 +0000
+++ lib/lp/snappy/tests/test_snapbuildbehaviour.py 2018-03-22 16:49:25 +0000
@@ -1,4 +1,4 @@
1# Copyright 2015-2017 Canonical Ltd. This software is licensed under the1# Copyright 2015-2018 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 snap package build behaviour."""4"""Test snap package build behaviour."""
@@ -402,6 +402,17 @@
402 ]))402 ]))
403403
404 @defer.inlineCallbacks404 @defer.inlineCallbacks
405 def test_extraBuildArgs_channels(self):
406 # If the build needs particular channels, _extraBuildArgs sends
407 # them.
408 job = self.makeJob(channels={"snapcraft": "edge"})
409 expected_archives, expected_trusted_keys = (
410 yield get_sources_list_for_building(
411 job.build, job.build.distro_arch_series, None))
412 args = yield job._extraBuildArgs()
413 self.assertEqual({"snapcraft": "edge"}, args["channels"])
414
415 @defer.inlineCallbacks
405 def test_composeBuildRequest_proxy_url_set(self):416 def test_composeBuildRequest_proxy_url_set(self):
406 job = self.makeJob()417 job = self.makeJob()
407 build_request = yield job.composeBuildRequest(None)418 build_request = yield job.composeBuildRequest(None)
408419
=== modified file 'lib/lp/testing/factory.py'
--- lib/lp/testing/factory.py 2018-02-24 09:11:39 +0000
+++ lib/lp/testing/factory.py 2018-03-22 16:49:25 +0000
@@ -4670,7 +4670,8 @@
4670 def makeSnap(self, registrant=None, owner=None, distroseries=None,4670 def makeSnap(self, registrant=None, owner=None, distroseries=None,
4671 name=None, branch=None, git_ref=None, auto_build=False,4671 name=None, branch=None, git_ref=None, auto_build=False,
4672 auto_build_archive=None, auto_build_pocket=None,4672 auto_build_archive=None, auto_build_pocket=None,
4673 is_stale=None, require_virtualized=True, processors=None,4673 auto_build_channels=None, is_stale=None,
4674 require_virtualized=True, processors=None,
4674 date_created=DEFAULT, private=False, store_upload=False,4675 date_created=DEFAULT, private=False, store_upload=False,
4675 store_series=None, store_name=None, store_secrets=None,4676 store_series=None, store_name=None, store_secrets=None,
4676 store_channels=None):4677 store_channels=None):
@@ -4696,7 +4697,8 @@
4696 require_virtualized=require_virtualized, processors=processors,4697 require_virtualized=require_virtualized, processors=processors,
4697 date_created=date_created, branch=branch, git_ref=git_ref,4698 date_created=date_created, branch=branch, git_ref=git_ref,
4698 auto_build=auto_build, auto_build_archive=auto_build_archive,4699 auto_build=auto_build, auto_build_archive=auto_build_archive,
4699 auto_build_pocket=auto_build_pocket, private=private,4700 auto_build_pocket=auto_build_pocket,
4701 auto_build_channels=auto_build_channels, private=private,
4700 store_upload=store_upload, store_series=store_series,4702 store_upload=store_upload, store_series=store_series,
4701 store_name=store_name, store_secrets=store_secrets,4703 store_name=store_name, store_secrets=store_secrets,
4702 store_channels=store_channels)4704 store_channels=store_channels)
@@ -4707,8 +4709,9 @@
47074709
4708 def makeSnapBuild(self, requester=None, registrant=None, snap=None,4710 def makeSnapBuild(self, requester=None, registrant=None, snap=None,
4709 archive=None, distroarchseries=None, pocket=None,4711 archive=None, distroarchseries=None, pocket=None,
4710 date_created=DEFAULT, status=BuildStatus.NEEDSBUILD,4712 channels=None, date_created=DEFAULT,
4711 builder=None, duration=None, **kwargs):4713 status=BuildStatus.NEEDSBUILD, builder=None,
4714 duration=None, **kwargs):
4712 """Make a new SnapBuild."""4715 """Make a new SnapBuild."""
4713 if requester is None:4716 if requester is None:
4714 requester = self.makePerson()4717 requester = self.makePerson()
@@ -4736,7 +4739,7 @@
4736 pocket = PackagePublishingPocket.UPDATES4739 pocket = PackagePublishingPocket.UPDATES
4737 snapbuild = getUtility(ISnapBuildSet).new(4740 snapbuild = getUtility(ISnapBuildSet).new(
4738 requester, snap, archive, distroarchseries, pocket,4741 requester, snap, archive, distroarchseries, pocket,
4739 date_created=date_created)4742 channels=channels, date_created=date_created)
4740 if duration is not None:4743 if duration is not None:
4741 removeSecurityProxy(snapbuild).updateStatus(4744 removeSecurityProxy(snapbuild).updateStatus(
4742 BuildStatus.BUILDING, builder=builder,4745 BuildStatus.BUILDING, builder=builder,