Merge lp:~cjwatson/launchpad/uefi-ppa-no-unapproved into lp:launchpad

Proposed by Colin Watson on 2012-08-14
Status: Merged
Approved by: Colin Watson on 2012-08-16
Approved revision: no longer in the source branch.
Merged at revision: 15823
Proposed branch: lp:~cjwatson/launchpad/uefi-ppa-no-unapproved
Merge into: lp:launchpad
Diff against target: 387 lines (+166/-171)
3 files modified
lib/lp/archiveuploader/tests/test_uploadpolicy.py (+164/-22)
lib/lp/archiveuploader/tests/uploadpolicy.txt (+0/-149)
lib/lp/archiveuploader/uploadpolicy.py (+2/-0)
To merge this branch: bzr merge lp:~cjwatson/launchpad/uefi-ppa-no-unapproved
Reviewer Review Type Date Requested Status
Steve Kowalik (community) code 2012-08-14 Approve on 2012-08-14
Review via email: mp+119542@code.launchpad.net

Commit Message

Always auto-approve buildd uploads to PPAs, even if they contain UEFI custom files.

Description of the Change

== Summary ==

UEFI custom uploads to PPAs land in the UNAPPROVED queue, even though PPAs aren't supposed to have an approval workflow and have no good way to handle the approval. (See bug 1036599 for a more complete exploration of the rationale.)

== Proposed fix ==

Ensure that BuildDaemonUploadPolicy.autoApprove always returns True for uploads to PPAs.

== Implementation details ==

I turned uploadpolicy.txt into unit tests to recover LoC.

== Tests ==

bin/test -vvc lp.archiveuploader.tests.test_uploadpolicy

== Demo and Q/A ==

Upload a new revision of efilinux to a dogfood PPA (based on what's currently in quantal there), wait for it to build, and check that it is automatically accepted by process-upload rather than landing in the UNAPPROVED queue.

To post a comment you must log in.
Steve Kowalik (stevenk) wrote :

Looks good to me. And I approve of the -5. :-)

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/archiveuploader/tests/test_uploadpolicy.py'
2--- lib/lp/archiveuploader/tests/test_uploadpolicy.py 2012-01-01 02:58:52 +0000
3+++ lib/lp/archiveuploader/tests/test_uploadpolicy.py 2012-08-14 14:08:16 +0000
4@@ -1,20 +1,64 @@
5 #!/usr/bin/python
6 #
7-# Copyright 2010 Canonical Ltd. This software is licensed under the
8+# Copyright 2010-2012 Canonical Ltd. This software is licensed under the
9 # GNU Affero General Public License version 3 (see the file LICENSE).
10
11+from zope.component import getUtility
12+from zope.component.interfaces import ComponentLookupError
13+
14 from lp.app.errors import NotFoundError
15+from lp.archiveuploader.nascentuploadfile import CustomUploadFile
16 from lp.archiveuploader.uploadpolicy import (
17 AbstractUploadPolicy,
18 ArchiveUploadType,
19+ findPolicyByName,
20+ IArchiveUploadPolicy,
21+ InsecureUploadPolicy,
22 )
23+from lp.registry.interfaces.distribution import IDistributionSet
24+from lp.registry.interfaces.series import SeriesStatus
25+from lp.services.database.sqlbase import flush_database_updates
26 from lp.testing import (
27+ celebrity_logged_in,
28 TestCase,
29 TestCaseWithFactory,
30 )
31 from lp.testing.layers import DatabaseFunctionalLayer
32
33
34+class FakeNascentUpload:
35+
36+ def __init__(self, sourceful, binaryful, is_ppa):
37+ self.sourceful = sourceful
38+ self.binaryful = binaryful
39+ self.is_ppa = is_ppa
40+ self.rejections = []
41+
42+ def reject(self, msg):
43+ self.rejections.append(msg)
44+
45+
46+def make_fake_upload(sourceful=False, binaryful=False, is_ppa=False):
47+ return FakeNascentUpload(sourceful, binaryful, is_ppa)
48+
49+
50+def make_policy(accepted_type):
51+ policy = AbstractUploadPolicy()
52+ policy.accepted_type = accepted_type
53+ return policy
54+
55+
56+class FakeOptions:
57+ def __init__(self, distroseries=None):
58+ self.distro = "ubuntu"
59+ self.distroseries = distroseries
60+
61+
62+class FakeChangesFile:
63+ def __init__(self, custom_files=[]):
64+ self.custom_files = custom_files
65+
66+
67 class TestUploadPolicy_validateUploadType(TestCase):
68 """Test what kind (sourceful/binaryful/mixed) of uploads are accepted."""
69
70@@ -99,6 +143,49 @@
71
72 layer = DatabaseFunctionalLayer
73
74+ def test_getUtility_returns_class(self):
75+ # Since upload policies need to be changed according to
76+ # user-specified arguments, the utility for looking up policies
77+ # returns the class itself rather than an instance of it.
78+ policy_cls = getUtility(IArchiveUploadPolicy, "insecure")
79+ self.assertIs(InsecureUploadPolicy, policy_cls)
80+
81+ def test_findPolicyByName_returns_instance(self):
82+ # There is a helper function that returns an instance of the policy
83+ # with the given name. It is preferred over using getUtility()
84+ # directly.
85+ insecure_policy = findPolicyByName("insecure")
86+ self.assertIsInstance(insecure_policy, InsecureUploadPolicy)
87+
88+ def test_policy_names(self):
89+ self.assertEqual("insecure", findPolicyByName("insecure").name)
90+ self.assertEqual("buildd", findPolicyByName("buildd").name)
91+
92+ def test_cannot_look_up_abstract_policy(self):
93+ self.assertRaises(ComponentLookupError, findPolicyByName, "abstract")
94+
95+ def test_policy_attributes(self):
96+ # Some attributes are expected to exist but vary between policies.
97+ insecure_policy = findPolicyByName("insecure")
98+ buildd_policy = findPolicyByName("buildd")
99+ self.assertFalse(insecure_policy.unsigned_changes_ok)
100+ self.assertTrue(buildd_policy.unsigned_changes_ok)
101+ self.assertFalse(insecure_policy.unsigned_dsc_ok)
102+ self.assertTrue(buildd_policy.unsigned_dsc_ok)
103+
104+ def test_setOptions_distro_name(self):
105+ # Policies pick up the distribution name from options.
106+ for policy_name in "insecure", "buildd":
107+ policy = findPolicyByName(policy_name)
108+ policy.setOptions(FakeOptions())
109+ self.assertEqual("ubuntu", policy.distro.name)
110+
111+ def test_setOptions_distroseries_name(self):
112+ # If a distroseries name is set, the policy picks it up from option.
113+ buildd_policy = findPolicyByName("buildd")
114+ buildd_policy.setOptions(FakeOptions(distroseries="hoary"))
115+ self.assertEqual("hoary", buildd_policy.distroseries.name)
116+
117 def test_setDistroSeriesAndPocket_distro_not_found(self):
118 policy = AbstractUploadPolicy()
119 policy.distro = self.factory.makeDistribution()
120@@ -106,24 +193,79 @@
121 NotFoundError, policy.setDistroSeriesAndPocket,
122 'nonexistent_security')
123
124-
125-class FakeNascentUpload:
126-
127- def __init__(self, sourceful, binaryful):
128- self.sourceful = sourceful
129- self.binaryful = binaryful
130- self.is_ppa = False
131- self.rejections = []
132-
133- def reject(self, msg):
134- self.rejections.append(msg)
135-
136-
137-def make_fake_upload(sourceful=False, binaryful=False):
138- return FakeNascentUpload(sourceful, binaryful)
139-
140-
141-def make_policy(accepted_type):
142- policy = AbstractUploadPolicy()
143- policy.accepted_type = accepted_type
144- return policy
145+ def setHoaryStatus(self, status):
146+ ubuntu = getUtility(IDistributionSet)["ubuntu"]
147+ with celebrity_logged_in("admin"):
148+ ubuntu["hoary"].status = status
149+ flush_database_updates()
150+
151+ def test_insecure_approves_release(self):
152+ # Uploads to the RELEASE pocket of non-FROZEN distroseries are
153+ # approved by the insecure policy.
154+ insecure_policy = findPolicyByName("insecure")
155+ insecure_policy.setOptions(FakeOptions(distroseries="hoary"))
156+ self.assertEqual(
157+ SeriesStatus.DEVELOPMENT, insecure_policy.distroseries.status)
158+ self.assertTrue(insecure_policy.autoApprove(make_fake_upload()))
159+ self.assertTrue(insecure_policy.autoApprove(
160+ make_fake_upload(is_ppa=True)))
161+
162+ def test_insecure_approves_proposed(self):
163+ # Uploads to the PROPOSED pocket of non-FROZEN distroseries are
164+ # approved by the insecure policy.
165+ insecure_policy = findPolicyByName("insecure")
166+ insecure_policy.setOptions(FakeOptions(distroseries="hoary-proposed"))
167+ self.assertEqual(
168+ SeriesStatus.DEVELOPMENT, insecure_policy.distroseries.status)
169+ self.assertTrue(insecure_policy.autoApprove(make_fake_upload()))
170+
171+ def test_insecure_does_not_approve_proposed_post_release(self):
172+ # Uploads to the PROPOSED pocket are not auto-approved after
173+ # release.
174+ self.setHoaryStatus(SeriesStatus.CURRENT)
175+ insecure_policy = findPolicyByName("insecure")
176+ insecure_policy.setOptions(FakeOptions(distroseries="hoary-proposed"))
177+ self.assertFalse(insecure_policy.autoApprove(make_fake_upload()))
178+
179+ def test_insecure_does_not_approve_frozen(self):
180+ # When the distroseries is FROZEN, uploads to the primary archive
181+ # wait in the UNAPPROVED queue, but PPA uploads are still approved.
182+ self.setHoaryStatus(SeriesStatus.FROZEN)
183+ insecure_policy = findPolicyByName("insecure")
184+ insecure_policy.setOptions(FakeOptions(distroseries="hoary-proposed"))
185+ self.assertFalse(insecure_policy.autoApprove(make_fake_upload()))
186+ self.assertTrue(insecure_policy.autoApprove(
187+ make_fake_upload(is_ppa=True)))
188+
189+ def test_insecure_does_not_approve_updates(self):
190+ # Uploads to the UPDATES pocket are not auto-approved by the
191+ # insecure policy. Despite not being allowed yet (see
192+ # UploadPolicy.checkUpload), PPA uploads to post-release pockets
193+ # would still be auto-approved.
194+ self.setHoaryStatus(SeriesStatus.CURRENT)
195+ insecure_policy = findPolicyByName("insecure")
196+ insecure_policy.setOptions(FakeOptions(distroseries="hoary-updates"))
197+ self.assertFalse(insecure_policy.autoApprove(make_fake_upload()))
198+ self.assertTrue(insecure_policy.autoApprove(
199+ make_fake_upload(is_ppa=True)))
200+
201+ def test_buildd_does_not_approve_uefi(self):
202+ # Uploads to the primary archive containing UEFI custom files are
203+ # not approved.
204+ buildd_policy = findPolicyByName("buildd")
205+ uploadfile = CustomUploadFile(
206+ "uefi.tar.gz", None, 0, "main/raw-uefi", "extra", buildd_policy,
207+ None)
208+ upload = make_fake_upload(binaryful=True)
209+ upload.changes = FakeChangesFile(custom_files=[uploadfile])
210+ self.assertFalse(buildd_policy.autoApprove(upload))
211+
212+ def test_buildd_approves_uefi_ppa(self):
213+ # Uploads to PPAs containing UEFI custom files are auto-approved.
214+ buildd_policy = findPolicyByName("buildd")
215+ uploadfile = CustomUploadFile(
216+ "uefi.tar.gz", None, 0, "main/raw-uefi", "extra", buildd_policy,
217+ None)
218+ upload = make_fake_upload(binaryful=True, is_ppa=True)
219+ upload.changes = FakeChangesFile(custom_files=[uploadfile])
220+ self.assertTrue(buildd_policy.autoApprove(upload))
221
222=== removed file 'lib/lp/archiveuploader/tests/uploadpolicy.txt'
223--- lib/lp/archiveuploader/tests/uploadpolicy.txt 2012-03-28 16:07:40 +0000
224+++ lib/lp/archiveuploader/tests/uploadpolicy.txt 1970-01-01 00:00:00 +0000
225@@ -1,149 +0,0 @@
226-The uploader policies
227----------------------
228-
229-When the uploader is invoked, it is given a policy to work in. This governs
230-such things as what tests get run at what stages of the upload, and whether or
231-not there is a build to be created, or one in existence to be used. These
232-policies are in the lp.archiveuploader package, in the uploadpolicy module,
233-and implement IArchiveUploadPolicy. They are registered as named utilities,
234-but since the policy themselves need to be changed according to user-specified
235-arguments, we register the classes as the component of the utilities, which
236-means a call to getUtility(IArchiveUploadPolicy, name) will return the class
237-itself rather than an instance of it.
238-
239- >>> from lp.archiveuploader.uploadpolicy import (
240- ... IArchiveUploadPolicy, findPolicyByName)
241- >>> policy_cls = getUtility(IArchiveUploadPolicy, 'insecure')
242- >>> policy_cls
243- <class '...InsecureUploadPolicy'>
244-
245-There's a helper function which returns an instance of the policy with the
246-given name, though, and it's preferred over using getUtility() directly.
247-
248- >>> insecure_policy = findPolicyByName('insecure')
249- >>> insecure_policy
250- <lp...InsecureUploadPolicy object...
251-
252-Two of the policies defined so far are the insecure and buildd policies.
253-
254- >>> insecure_policy.name
255- 'insecure'
256- >>> buildd_policy = findPolicyByName('buildd')
257- >>> buildd_policy.name
258- 'buildd'
259- >>> abstract_policy = findPolicyByName('abstract')
260- Traceback (most recent call last):
261- ...
262- ComponentLookupError:...
263-
264-There is a bunch of attributes which we expect to have and which can vary
265-from policy to policy.
266-
267- >>> insecure_policy.unsigned_changes_ok
268- False
269- >>> buildd_policy.unsigned_changes_ok
270- True
271- >>> insecure_policy.unsigned_dsc_ok
272- False
273- >>> buildd_policy.unsigned_dsc_ok
274- True
275-
276-The policies require certain values to be present in the options at times...
277-
278- >>> class MockAbstractOptions:
279- ... distro = 'ubuntu'
280- ... distroseries = None
281- >>> class MockOptions(MockAbstractOptions):
282- ... builds = True
283-
284- >>> ab_opts = MockAbstractOptions()
285- >>> bd_opts = MockOptions()
286-
287- >>> insecure_policy.setOptions(ab_opts)
288- >>> insecure_policy.distro.name
289- u'ubuntu'
290- >>> buildd_policy.setOptions(ab_opts)
291- >>> buildd_policy.setOptions(bd_opts)
292- >>> buildd_policy.distro.name
293- u'ubuntu'
294-
295-Policies can think about distroseriess...
296-
297- >>> buildd_policy.setDistroSeriesAndPocket("hoary")
298- >>> print buildd_policy.distroseries.name
299- hoary
300-
301-Policies can make decisions based on whether or not they want to
302-approve an upload automatically (I.E. move it straight to ACCEPTED
303-instead of UNAPPROVED)
304-
305- >>> from lp.registry.interfaces.distribution import IDistributionSet
306- >>> from lp.registry.interfaces.series import SeriesStatus
307- >>> ubuntu = getUtility(IDistributionSet)['ubuntu']
308- >>> hoary = ubuntu['hoary']
309-
310- >>> class FakeUpload:
311- ... def __init__(self, ppa=False):
312- ... self.is_ppa = ppa
313-
314- >>> print hoary.status.name
315- DEVELOPMENT
316-
317-Uploads to the RELEASE pocket of not FROZEN distroseries are approved
318-by the insecure policy:
319-
320- >>> insecure_policy.setDistroSeriesAndPocket('hoary')
321- >>> insecure_policy.autoApprove(FakeUpload())
322- True
323-
324- >>> insecure_policy.autoApprove(FakeUpload(ppa=True))
325- True
326-
327-So are uploads to the PROPOSED pocket:
328-
329- >>> insecure_policy.setDistroSeriesAndPocket('hoary-proposed')
330- >>> insecure_policy.autoApprove(FakeUpload())
331- True
332-
333-When the distroseries is FROZEN the uploads should wait in UNAPPROVED queue:
334-
335- >>> login('foo.bar@canonical.com')
336- >>> hoary.status = SeriesStatus.FROZEN
337- >>> from lp.services.database.sqlbase import flush_database_updates
338- >>> flush_database_updates()
339-
340- >>> insecure_policy.autoApprove(FakeUpload())
341- False
342-
343-PPA uploads continue to be auto-approved:
344-
345- >>> insecure_policy.autoApprove(FakeUpload(ppa=True))
346- True
347-
348-Reset the policy so that we can try again...
349-
350- >>> insecure_policy.policy = None
351- >>> insecure_policy.distroseries = None
352-
353-Uploads to the UPDATES pocket are not auto-approved by the insecure policy
354-
355- >>> insecure_policy.setDistroSeriesAndPocket('hoary-updates')
356- >>> insecure_policy.autoApprove(FakeUpload())
357- False
358-
359-Despite of not being allowed yet (see UploadPolicy.checkUpload) PPA
360-uploads to post-release pockets would also be auto-approved:
361-
362- >>> insecure_policy.autoApprove(FakeUpload(ppa=True))
363- True
364-
365-Uploads to the PROPOSED pocket are also not auto-approved by the insecure
366-policy, either before or after release:
367-
368- >>> insecure_policy.setDistroSeriesAndPocket('hoary-proposed')
369- >>> insecure_policy.autoApprove(FakeUpload())
370- False
371- >>> hoary.status = SeriesStatus.CURRENT
372- >>> flush_database_updates()
373- >>> insecure_policy.autoApprove(FakeUpload())
374- False
375
376=== modified file 'lib/lp/archiveuploader/uploadpolicy.py'
377--- lib/lp/archiveuploader/uploadpolicy.py 2012-07-20 08:29:06 +0000
378+++ lib/lp/archiveuploader/uploadpolicy.py 2012-08-14 14:08:16 +0000
379@@ -322,6 +322,8 @@
380
381 def autoApprove(self, upload):
382 """Check that all custom files in this upload can be auto-approved."""
383+ if upload.is_ppa:
384+ return True
385 if upload.binaryful:
386 for custom_file in upload.changes.custom_files:
387 if not custom_file.autoApprove():