Merge lp:~cjwatson/launchpad/snap-allow-network into lp:launchpad

Proposed by Colin Watson
Status: Merged
Merged at revision: 18615
Proposed branch: lp:~cjwatson/launchpad/snap-allow-network
Merge into: lp:launchpad
Diff against target: 240 lines (+46/-19)
8 files modified
lib/lp/snappy/browser/snap.py (+3/-2)
lib/lp/snappy/browser/tests/test_snap.py (+5/-2)
lib/lp/snappy/interfaces/snap.py (+7/-0)
lib/lp/snappy/model/snap.py (+12/-8)
lib/lp/snappy/model/snapbuildbehaviour.py (+1/-1)
lib/lp/snappy/tests/test_snap.py (+4/-0)
lib/lp/snappy/tests/test_snapbuildbehaviour.py (+8/-0)
lib/lp/testing/factory.py (+6/-6)
To merge this branch: bzr merge lp:~cjwatson/launchpad/snap-allow-network
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+336924@code.launchpad.net

Commit message

Add Snap.allow_internet: if false, do not dispatch a proxy token to builds of that snap.

Description of the change

This will allow snaps that are intended to be delivered with Ubuntu images to be restricted to build from only resources on Launchpad, and thus be reproducible, supportable, etc.

Requires https://code.launchpad.net/~cjwatson/launchpad/db-snap-allow-network/+merge/336923.

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
1=== modified file 'lib/lp/snappy/browser/snap.py'
2--- lib/lp/snappy/browser/snap.py 2017-03-27 19:28:36 +0000
3+++ lib/lp/snappy/browser/snap.py 2018-04-04 14:15:46 +0000
4@@ -1,4 +1,4 @@
5-# Copyright 2015-2017 Canonical Ltd. This software is licensed under the
6+# Copyright 2015-2018 Canonical Ltd. This software is licensed under the
7 # GNU Affero General Public License version 3 (see the file LICENSE).
8
9 """Snap views."""
10@@ -319,6 +319,7 @@
11 'name',
12 'private',
13 'require_virtualized',
14+ 'allow_internet',
15 'auto_build',
16 'store_upload',
17 ])
18@@ -648,7 +649,7 @@
19
20 page_title = 'Administer'
21
22- field_names = ['private', 'require_virtualized']
23+ field_names = ['private', 'require_virtualized', 'allow_internet']
24
25 def validate(self, data):
26 super(SnapAdminView, self).validate(data)
27
28=== modified file 'lib/lp/snappy/browser/tests/test_snap.py'
29--- lib/lp/snappy/browser/tests/test_snap.py 2017-12-19 17:16:02 +0000
30+++ lib/lp/snappy/browser/tests/test_snap.py 2018-04-04 14:15:46 +0000
31@@ -1,4 +1,4 @@
32-# Copyright 2015-2017 Canonical Ltd. This software is licensed under the
33+# Copyright 2015-2018 Canonical Ltd. This software is licensed under the
34 # GNU Affero General Public License version 3 (see the file LICENSE).
35
36 """Test snap package views."""
37@@ -573,7 +573,7 @@
38 user=self.person)
39
40 def test_admin_snap(self):
41- # Admins can change require_virtualized and privacy.
42+ # Admins can change require_virtualized, privacy, and allow_internet.
43 login("admin@canonical.com")
44 commercial_admin = self.factory.makePerson(
45 member_of=[getUtility(ILaunchpadCelebrities).commercial_admin])
46@@ -581,16 +581,19 @@
47 snap = self.factory.makeSnap(registrant=self.person)
48 self.assertTrue(snap.require_virtualized)
49 self.assertFalse(snap.private)
50+ self.assertTrue(snap.allow_internet)
51
52 browser = self.getViewBrowser(snap, user=commercial_admin)
53 browser.getLink("Administer snap package").click()
54 browser.getControl("Require virtualized builders").selected = False
55 browser.getControl("Private").selected = True
56+ browser.getControl("Allow external network access").selected = False
57 browser.getControl("Update snap package").click()
58
59 login_person(self.person)
60 self.assertFalse(snap.require_virtualized)
61 self.assertTrue(snap.private)
62+ self.assertFalse(snap.allow_internet)
63
64 def test_admin_snap_privacy_mismatch(self):
65 # Cannot make snap public if it still contains private information.
66
67=== modified file 'lib/lp/snappy/interfaces/snap.py'
68--- lib/lp/snappy/interfaces/snap.py 2018-03-22 14:56:41 +0000
69+++ lib/lp/snappy/interfaces/snap.py 2018-04-04 14:15:46 +0000
70@@ -600,6 +600,13 @@
71 value_type=Reference(schema=IProcessor),
72 readonly=False))
73
74+ allow_internet = exported(Bool(
75+ title=_("Allow external network access"),
76+ required=True, readonly=False,
77+ description=_(
78+ "Allow access to external network resources via a proxy. "
79+ "Resources hosted on Launchpad itself are always allowed.")))
80+
81
82 class ISnap(
83 ISnapView, ISnapEdit, ISnapEditableAttributes, ISnapAdminAttributes,
84
85=== modified file 'lib/lp/snappy/model/snap.py'
86--- lib/lp/snappy/model/snap.py 2018-03-22 15:33:25 +0000
87+++ lib/lp/snappy/model/snap.py 2018-04-04 14:15:46 +0000
88@@ -198,6 +198,8 @@
89
90 private = Bool(name='private')
91
92+ allow_internet = Bool(name='allow_internet', allow_none=False)
93+
94 store_upload = Bool(name='store_upload', allow_none=False)
95
96 store_series_id = Int(name='store_series', allow_none=True)
97@@ -213,9 +215,9 @@
98 description=None, branch=None, git_ref=None, auto_build=False,
99 auto_build_archive=None, auto_build_pocket=None,
100 auto_build_channels=None, require_virtualized=True,
101- date_created=DEFAULT, private=False, store_upload=False,
102- store_series=None, store_name=None, store_secrets=None,
103- store_channels=None):
104+ date_created=DEFAULT, private=False, allow_internet=True,
105+ store_upload=False, store_series=None, store_name=None,
106+ store_secrets=None, store_channels=None):
107 """Construct a `Snap`."""
108 super(Snap, self).__init__()
109 self.registrant = registrant
110@@ -233,6 +235,7 @@
111 self.date_created = date_created
112 self.date_last_modified = date_created
113 self.private = private
114+ self.allow_internet = allow_internet
115 self.store_upload = store_upload
116 self.store_series = store_series
117 self.store_name = store_name
118@@ -669,8 +672,8 @@
119 auto_build_archive=None, auto_build_pocket=None,
120 auto_build_channels=None, require_virtualized=True,
121 processors=None, date_created=DEFAULT, private=False,
122- store_upload=False, store_series=None, store_name=None,
123- store_secrets=None, store_channels=None):
124+ allow_internet=True, store_upload=False, store_series=None,
125+ store_name=None, store_secrets=None, store_channels=None):
126 """See `ISnapSet`."""
127 if not registrant.inTeam(owner):
128 if owner.is_team:
129@@ -713,9 +716,10 @@
130 auto_build_pocket=auto_build_pocket,
131 auto_build_channels=auto_build_channels,
132 require_virtualized=require_virtualized, date_created=date_created,
133- private=private, store_upload=store_upload,
134- store_series=store_series, store_name=store_name,
135- store_secrets=store_secrets, store_channels=store_channels)
136+ private=private, allow_internet=allow_internet,
137+ store_upload=store_upload, store_series=store_series,
138+ store_name=store_name, store_secrets=store_secrets,
139+ store_channels=store_channels)
140 store.add(snap)
141
142 if processors is None:
143
144=== modified file 'lib/lp/snappy/model/snapbuildbehaviour.py'
145--- lib/lp/snappy/model/snapbuildbehaviour.py 2018-03-23 13:26:09 +0000
146+++ lib/lp/snappy/model/snapbuildbehaviour.py 2018-04-04 14:15:46 +0000
147@@ -85,7 +85,7 @@
148 """
149 build = self.build
150 args = {}
151- if config.snappy.builder_proxy_host:
152+ if config.snappy.builder_proxy_host and build.snap.allow_internet:
153 token = yield self._requestProxyToken()
154 args["proxy_url"] = (
155 "http://{username}:{password}@{host}:{port}".format(
156
157=== modified file 'lib/lp/snappy/tests/test_snap.py'
158--- lib/lp/snappy/tests/test_snap.py 2018-02-08 12:47:44 +0000
159+++ lib/lp/snappy/tests/test_snap.py 2018-04-04 14:15:46 +0000
160@@ -653,6 +653,7 @@
161 self.assertIsNone(snap.auto_build_channels)
162 self.assertTrue(snap.require_virtualized)
163 self.assertFalse(snap.private)
164+ self.assertTrue(snap.allow_internet)
165
166 def test_creation_git(self):
167 # The metadata entries supplied when a Snap is created for a Git
168@@ -674,6 +675,7 @@
169 self.assertIsNone(snap.auto_build_channels)
170 self.assertTrue(snap.require_virtualized)
171 self.assertFalse(snap.private)
172+ self.assertTrue(snap.allow_internet)
173
174 def test_creation_git_url(self):
175 # A Snap can be backed directly by a URL for an external Git
176@@ -1378,6 +1380,7 @@
177 self.assertIsNone(snap["git_path"])
178 self.assertIsNone(snap["git_ref_link"])
179 self.assertTrue(snap["require_virtualized"])
180+ self.assertTrue(snap["allow_internet"])
181
182 def test_new_git(self):
183 # Ensure Snap creation based on a Git branch works.
184@@ -1398,6 +1401,7 @@
185 self.assertEqual(ref.path, snap["git_path"])
186 self.assertEqual(self.getURL(ref), snap["git_ref_link"])
187 self.assertTrue(snap["require_virtualized"])
188+ self.assertTrue(snap["allow_internet"])
189
190 def test_new_private(self):
191 # Ensure private Snap creation works.
192
193=== modified file 'lib/lp/snappy/tests/test_snapbuildbehaviour.py'
194--- lib/lp/snappy/tests/test_snapbuildbehaviour.py 2018-03-23 13:26:09 +0000
195+++ lib/lp/snappy/tests/test_snapbuildbehaviour.py 2018-04-04 14:15:46 +0000
196@@ -414,6 +414,14 @@
197 self.assertFalse(isProxy(args["channels"]))
198 self.assertEqual({"snapcraft": "edge"}, args["channels"])
199
200+ def test_extraBuildArgs_disallow_internet(self):
201+ # If external network access is not allowed for the snap,
202+ # _extraBuildArgs does not dispatch a proxy token.
203+ job = self.makeJob(allow_internet=False)
204+ args = yield job._extraBuildArgs()
205+ self.assertNotIn("proxy_url", args)
206+ self.assertNotIn("revocation_endpoint", args)
207+
208 @defer.inlineCallbacks
209 def test_composeBuildRequest_proxy_url_set(self):
210 job = self.makeJob()
211
212=== modified file 'lib/lp/testing/factory.py'
213--- lib/lp/testing/factory.py 2018-03-22 16:48:16 +0000
214+++ lib/lp/testing/factory.py 2018-04-04 14:15:46 +0000
215@@ -4672,9 +4672,9 @@
216 auto_build_archive=None, auto_build_pocket=None,
217 auto_build_channels=None, is_stale=None,
218 require_virtualized=True, processors=None,
219- date_created=DEFAULT, private=False, store_upload=False,
220- store_series=None, store_name=None, store_secrets=None,
221- store_channels=None):
222+ date_created=DEFAULT, private=False, allow_internet=True,
223+ store_upload=False, store_series=None, store_name=None,
224+ store_secrets=None, store_channels=None):
225 """Make a new Snap."""
226 if registrant is None:
227 registrant = self.makePerson()
228@@ -4699,9 +4699,9 @@
229 auto_build=auto_build, auto_build_archive=auto_build_archive,
230 auto_build_pocket=auto_build_pocket,
231 auto_build_channels=auto_build_channels, private=private,
232- store_upload=store_upload, store_series=store_series,
233- store_name=store_name, store_secrets=store_secrets,
234- store_channels=store_channels)
235+ allow_internet=allow_internet, store_upload=store_upload,
236+ store_series=store_series, store_name=store_name,
237+ store_secrets=store_secrets, store_channels=store_channels)
238 if is_stale is not None:
239 removeSecurityProxy(snap).is_stale = is_stale
240 IStore(snap).flush()