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

Proposed by Colin Watson
Status: Merged
Merged at revision: 18766
Proposed branch: lp:~cjwatson/launchpad/snap-build-channels-feature-flag
Merge into: lp:launchpad
Prerequisite: lp:~cjwatson/launchpad/snap-build-channels-ui
Diff against target: 241 lines (+105/-15)
6 files modified
lib/lp/snappy/browser/snap.py (+0/-7)
lib/lp/snappy/browser/widgets/snapbuildchannels.py (+21/-0)
lib/lp/snappy/browser/widgets/tests/test_snapbuildchannelswidget.py (+39/-3)
lib/lp/snappy/interfaces/snap.py (+2/-0)
lib/lp/snappy/model/snapbuildbehaviour.py (+11/-4)
lib/lp/snappy/tests/test_snapbuildbehaviour.py (+32/-1)
To merge this branch: bzr merge lp:~cjwatson/launchpad/snap-build-channels-feature-flag
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+344868@code.launchpad.net

Commit message

Add a feature flag to control the installation source for snapcraft.

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/snappy/browser/snap.py'
--- lib/lp/snappy/browser/snap.py 2018-07-13 16:42:08 +0000
+++ lib/lp/snappy/browser/snap.py 2018-08-30 16:16:36 +0000
@@ -340,13 +340,6 @@
340 'auto_build_channels',340 'auto_build_channels',
341 'store_upload',341 'store_upload',
342 ])342 ])
343 auto_build_channels = copy_field(
344 ISnap['auto_build_channels'],
345 description=(
346 u'The channels to use for build tools when building the snap '
347 u'package. If unset, or if the channel for snapcraft is set to '
348 u'"apt", the default behaviour is to install snapcraft from the '
349 u'source archive using apt.'))
350 store_distro_series = Choice(343 store_distro_series = Choice(
351 vocabulary='BuildableSnappyDistroSeries', required=True,344 vocabulary='BuildableSnappyDistroSeries', required=True,
352 title=u'Series')345 title=u'Series')
353346
=== modified file 'lib/lp/snappy/browser/widgets/snapbuildchannels.py'
--- lib/lp/snappy/browser/widgets/snapbuildchannels.py 2018-07-13 15:56:52 +0000
+++ lib/lp/snappy/browser/widgets/snapbuildchannels.py 2018-08-30 16:16:36 +0000
@@ -23,10 +23,12 @@
23from zope.security.proxy import isinstance as zope_isinstance23from zope.security.proxy import isinstance as zope_isinstance
2424
25from lp.app.errors import UnexpectedFormData25from lp.app.errors import UnexpectedFormData
26from lp.services.features import getFeatureFlag
26from lp.services.webapp.interfaces import (27from lp.services.webapp.interfaces import (
27 IAlwaysSubmittedWidget,28 IAlwaysSubmittedWidget,
28 ISingleLineWidgetLayout,29 ISingleLineWidgetLayout,
29 )30 )
31from lp.snappy.interfaces.snap import SNAP_SNAPCRAFT_CHANNEL_FEATURE_FLAG
3032
3133
32@implementer(ISingleLineWidgetLayout, IAlwaysSubmittedWidget, IInputWidget)34@implementer(ISingleLineWidgetLayout, IAlwaysSubmittedWidget, IInputWidget)
@@ -37,6 +39,25 @@
37 snap_names = ["core", "snapcraft"]39 snap_names = ["core", "snapcraft"]
38 _widgets_set_up = False40 _widgets_set_up = False
3941
42 def __init__(self, context, request):
43 super(SnapBuildChannelsWidget, self).__init__(context, request)
44 self.hint = (
45 'The channels to use for build tools when building the snap '
46 'package.\n')
47 default_snapcraft_channel = (
48 getFeatureFlag(SNAP_SNAPCRAFT_CHANNEL_FEATURE_FLAG) or "apt")
49 if default_snapcraft_channel == "apt":
50 self.hint += (
51 'If unset, or if the channel for snapcraft is set to "apt", '
52 'the default is to install snapcraft from the source archive '
53 'using apt.')
54 else:
55 self.hint += (
56 'If unset, the default is to install snapcraft from the "%s" '
57 'channel. Setting the channel for snapcraft to "apt" causes '
58 'snapcraft to be installed from the source archive using '
59 'apt.' % default_snapcraft_channel)
60
40 def setUpSubWidgets(self):61 def setUpSubWidgets(self):
41 if self._widgets_set_up:62 if self._widgets_set_up:
42 return63 return
4364
=== modified file 'lib/lp/snappy/browser/widgets/tests/test_snapbuildchannelswidget.py'
--- lib/lp/snappy/browser/widgets/tests/test_snapbuildchannelswidget.py 2018-04-30 16:48:47 +0000
+++ lib/lp/snappy/browser/widgets/tests/test_snapbuildchannelswidget.py 2018-08-30 16:16:36 +0000
@@ -14,10 +14,12 @@
14from zope.schema import Dict14from zope.schema import Dict
1515
16from lp.services.beautifulsoup import BeautifulSoup16from lp.services.beautifulsoup import BeautifulSoup
17from lp.services.features.testing import FeatureFixture
17from lp.services.webapp.servers import LaunchpadTestRequest18from lp.services.webapp.servers import LaunchpadTestRequest
18from lp.snappy.browser.widgets.snapbuildchannels import (19from lp.snappy.browser.widgets.snapbuildchannels import (
19 SnapBuildChannelsWidget,20 SnapBuildChannelsWidget,
20 )21 )
22from lp.snappy.interfaces.snap import SNAP_SNAPCRAFT_CHANNEL_FEATURE_FLAG
21from lp.testing import (23from lp.testing import (
22 TestCaseWithFactory,24 TestCaseWithFactory,
23 verifyObject,25 verifyObject,
@@ -35,9 +37,9 @@
35 __name__="auto_build_channels",37 __name__="auto_build_channels",
36 title="Source snap channels for automatic builds")38 title="Source snap channels for automatic builds")
37 self.context = self.factory.makeSnap()39 self.context = self.factory.makeSnap()
38 field = field.bind(self.context)40 self.field = field.bind(self.context)
39 request = LaunchpadTestRequest()41 self.request = LaunchpadTestRequest()
40 self.widget = SnapBuildChannelsWidget(field, request)42 self.widget = SnapBuildChannelsWidget(self.field, self.request)
4143
42 def test_implements(self):44 def test_implements(self):
43 self.assertTrue(verifyObject(IBrowserWidget, self.widget))45 self.assertTrue(verifyObject(IBrowserWidget, self.widget))
@@ -48,6 +50,40 @@
48 self.widget.template.filename.endswith("snapbuildchannels.pt"),50 self.widget.template.filename.endswith("snapbuildchannels.pt"),
49 "Template was not set up.")51 "Template was not set up.")
5052
53 def test_hint_no_feature_flag(self):
54 self.assertEqual(
55 'The channels to use for build tools when building the snap '
56 'package.\n'
57 'If unset, or if the channel for snapcraft is set to "apt", '
58 'the default is to install snapcraft from the source archive '
59 'using apt.',
60 self.widget.hint)
61
62 def test_hint_feature_flag_apt(self):
63 self.useFixture(
64 FeatureFixture({SNAP_SNAPCRAFT_CHANNEL_FEATURE_FLAG: "apt"}))
65 widget = SnapBuildChannelsWidget(self.field, self.request)
66 self.assertEqual(
67 'The channels to use for build tools when building the snap '
68 'package.\n'
69 'If unset, or if the channel for snapcraft is set to "apt", '
70 'the default is to install snapcraft from the source archive '
71 'using apt.',
72 widget.hint)
73
74 def test_hint_feature_flag_real_channel(self):
75 self.useFixture(
76 FeatureFixture({SNAP_SNAPCRAFT_CHANNEL_FEATURE_FLAG: "stable"}))
77 widget = SnapBuildChannelsWidget(self.field, self.request)
78 self.assertEqual(
79 'The channels to use for build tools when building the snap '
80 'package.\n'
81 'If unset, the default is to install snapcraft from the "stable" '
82 'channel. Setting the channel for snapcraft to "apt" causes '
83 'snapcraft to be installed from the source archive using '
84 'apt.',
85 widget.hint)
86
51 def test_setUpSubWidgets_first_call(self):87 def test_setUpSubWidgets_first_call(self):
52 # The subwidgets are set up and a flag is set.88 # The subwidgets are set up and a flag is set.
53 self.widget.setUpSubWidgets()89 self.widget.setUpSubWidgets()
5490
=== modified file 'lib/lp/snappy/interfaces/snap.py'
--- lib/lp/snappy/interfaces/snap.py 2018-08-06 14:42:30 +0000
+++ lib/lp/snappy/interfaces/snap.py 2018-08-30 16:16:36 +0000
@@ -22,6 +22,7 @@
22 'NoSourceForSnap',22 'NoSourceForSnap',
23 'NoSuchSnap',23 'NoSuchSnap',
24 'SNAP_PRIVATE_FEATURE_FLAG',24 'SNAP_PRIVATE_FEATURE_FLAG',
25 'SNAP_SNAPCRAFT_CHANNEL_FEATURE_FLAG',
25 'SNAP_TESTING_FLAGS',26 'SNAP_TESTING_FLAGS',
26 'SNAP_WEBHOOKS_FEATURE_FLAG',27 'SNAP_WEBHOOKS_FEATURE_FLAG',
27 'SnapAuthorizationBadMacaroon',28 'SnapAuthorizationBadMacaroon',
@@ -110,6 +111,7 @@
110111
111112
112SNAP_PRIVATE_FEATURE_FLAG = u"snap.allow_private"113SNAP_PRIVATE_FEATURE_FLAG = u"snap.allow_private"
114SNAP_SNAPCRAFT_CHANNEL_FEATURE_FLAG = u"snap.channels.snapcraft"
113SNAP_WEBHOOKS_FEATURE_FLAG = u"snap.webhooks.enabled"115SNAP_WEBHOOKS_FEATURE_FLAG = u"snap.webhooks.enabled"
114116
115117
116118
=== modified file 'lib/lp/snappy/model/snapbuildbehaviour.py'
--- lib/lp/snappy/model/snapbuildbehaviour.py 2018-07-13 16:42:08 +0000
+++ lib/lp/snappy/model/snapbuildbehaviour.py 2018-08-30 16:16:36 +0000
@@ -29,8 +29,12 @@
29 )29 )
30from lp.registry.interfaces.series import SeriesStatus30from lp.registry.interfaces.series import SeriesStatus
31from lp.services.config import config31from lp.services.config import config
32from lp.services.features import getFeatureFlag
32from lp.services.twistedsupport.treq import check_status33from lp.services.twistedsupport.treq import check_status
33from lp.snappy.interfaces.snap import SnapBuildArchiveOwnerMismatch34from lp.snappy.interfaces.snap import (
35 SNAP_SNAPCRAFT_CHANNEL_FEATURE_FLAG,
36 SnapBuildArchiveOwnerMismatch,
37 )
34from lp.snappy.interfaces.snapbuild import ISnapBuild38from lp.snappy.interfaces.snapbuild import ISnapBuild
35from lp.soyuz.adapters.archivedependencies import (39from lp.soyuz.adapters.archivedependencies import (
36 get_sources_list_for_building,40 get_sources_list_for_building,
@@ -108,12 +112,15 @@
108 tools_source=config.snappy.tools_source,112 tools_source=config.snappy.tools_source,
109 tools_fingerprint=config.snappy.tools_fingerprint,113 tools_fingerprint=config.snappy.tools_fingerprint,
110 logger=logger))114 logger=logger))
111 if (build.channels is not None and115 channels = build.channels or {}
112 build.channels.get("snapcraft") != "apt"):116 if "snapcraft" not in channels:
117 channels["snapcraft"] = (
118 getFeatureFlag(SNAP_SNAPCRAFT_CHANNEL_FEATURE_FLAG) or "apt")
119 if channels.get("snapcraft") != "apt":
113 # We have to remove the security proxy that Zope applies to this120 # We have to remove the security proxy that Zope applies to this
114 # dict, since otherwise we'll be unable to serialise it to121 # dict, since otherwise we'll be unable to serialise it to
115 # XML-RPC.122 # XML-RPC.
116 args["channels"] = removeSecurityProxy(build.channels)123 args["channels"] = removeSecurityProxy(channels)
117 if build.snap.branch is not None:124 if build.snap.branch is not None:
118 args["branch"] = build.snap.branch.bzr_identity125 args["branch"] = build.snap.branch.bzr_identity
119 elif build.snap.git_ref is not None:126 elif build.snap.git_ref is not None:
120127
=== modified file 'lib/lp/snappy/tests/test_snapbuildbehaviour.py'
--- lib/lp/snappy/tests/test_snapbuildbehaviour.py 2018-07-13 16:42:08 +0000
+++ lib/lp/snappy/tests/test_snapbuildbehaviour.py 2018-08-30 16:16:36 +0000
@@ -72,9 +72,13 @@
72from lp.registry.interfaces.pocket import PackagePublishingPocket72from lp.registry.interfaces.pocket import PackagePublishingPocket
73from lp.registry.interfaces.series import SeriesStatus73from lp.registry.interfaces.series import SeriesStatus
74from lp.services.config import config74from lp.services.config import config
75from lp.services.features.testing import FeatureFixture
75from lp.services.log.logger import BufferLogger76from lp.services.log.logger import BufferLogger
76from lp.services.webapp import canonical_url77from lp.services.webapp import canonical_url
77from lp.snappy.interfaces.snap import SnapBuildArchiveOwnerMismatch78from lp.snappy.interfaces.snap import (
79 SNAP_SNAPCRAFT_CHANNEL_FEATURE_FLAG,
80 SnapBuildArchiveOwnerMismatch,
81 )
78from lp.snappy.model.snapbuildbehaviour import SnapBuildBehaviour82from lp.snappy.model.snapbuildbehaviour import SnapBuildBehaviour
79from lp.soyuz.adapters.archivedependencies import (83from lp.soyuz.adapters.archivedependencies import (
80 get_sources_list_for_building,84 get_sources_list_for_building,
@@ -541,6 +545,33 @@
541 self.assertNotIn("channels", args)545 self.assertNotIn("channels", args)
542546
543 @defer.inlineCallbacks547 @defer.inlineCallbacks
548 def test_extraBuildArgs_channels_feature_flag_real_channel(self):
549 # If the snap.channels.snapcraft feature flag is set, it identifies
550 # the default channel to be used for snapcraft.
551 self.useFixture(
552 FeatureFixture({SNAP_SNAPCRAFT_CHANNEL_FEATURE_FLAG: "stable"}))
553 job = self.makeJob()
554 expected_archives, expected_trusted_keys = (
555 yield get_sources_list_for_building(
556 job.build, job.build.distro_arch_series, None))
557 args = yield job.extraBuildArgs()
558 self.assertFalse(isProxy(args["channels"]))
559 self.assertEqual({"snapcraft": "stable"}, args["channels"])
560
561 @defer.inlineCallbacks
562 def test_extraBuildArgs_channels_feature_flag_overridden(self):
563 # The snap.channels.snapcraft feature flag can be overridden by
564 # explicit configuration.
565 self.useFixture(
566 FeatureFixture({SNAP_SNAPCRAFT_CHANNEL_FEATURE_FLAG: "stable"}))
567 job = self.makeJob(channels={"snapcraft": "apt"})
568 expected_archives, expected_trusted_keys = (
569 yield get_sources_list_for_building(
570 job.build, job.build.distro_arch_series, None))
571 args = yield job.extraBuildArgs()
572 self.assertNotIn("channels", args)
573
574 @defer.inlineCallbacks
544 def test_extraBuildArgs_disallow_internet(self):575 def test_extraBuildArgs_disallow_internet(self):
545 # If external network access is not allowed for the snap,576 # If external network access is not allowed for the snap,
546 # extraBuildArgs does not dispatch a proxy token.577 # extraBuildArgs does not dispatch a proxy token.