Merge lp:~salgado/launchpad/upload-policy-utility into lp:launchpad

Proposed by Guilherme Salgado
Status: Merged
Approved by: Julian Edwards
Approved revision: no longer in the source branch.
Merged at revision: 11388
Proposed branch: lp:~salgado/launchpad/upload-policy-utility
Merge into: lp:launchpad
Diff against target: 699 lines (+251/-150)
12 files modified
lib/canonical/launchpad/webapp/meta.zcml (+7/-0)
lib/canonical/launchpad/webapp/metazcml.py (+16/-0)
lib/canonical/launchpad/webapp/tests/test_metazcml.py (+30/-0)
lib/lp/archiveuploader/tests/__init__.py (+53/-1)
lib/lp/archiveuploader/tests/test_uploadprocessor.py (+13/-9)
lib/lp/archiveuploader/tests/uploadpolicy.txt (+28/-20)
lib/lp/archiveuploader/uploadpolicy.py (+48/-109)
lib/lp/archiveuploader/uploadprocessor.py (+7/-7)
lib/lp/code/configure.zcml (+5/-0)
lib/lp/code/model/sourcepackagerecipebuild.py (+29/-2)
lib/lp/soyuz/configure.zcml (+11/-0)
lib/lp/soyuz/scripts/soyuz_process_upload.py (+4/-2)
To merge this branch: bzr merge lp:~salgado/launchpad/upload-policy-utility
Reviewer Review Type Date Requested Status
Julian Edwards (community) Approve
Review via email: mp+32911@code.launchpad.net

Commit message

Use named utilities for archive upload policies instead of implementing our own global registry for that.

Description of the change

Use named utilities for archive upload policies instead of implementing our
own global registry for that.

Also define the name for these utilities as constants/class-variables so that
they can be accessed externally and don't need to be hard-coded in multiple
places.

To post a comment you must log in.
Revision history for this message
Julian Edwards (julian-edwards) wrote :

Really nice branch! I have a couple of comments:

> def callable():
> global called
> called += 1

Can this be lp.testing.fakemethod.FakeMethod instead?

Also, you've un-factored findPolicyByName(). Why didn't you fix it in one place for all the existing call sites to use instead of changing all the call sites?

review: Needs Information (code)
Revision history for this message
Guilherme Salgado (salgado) wrote :

On Wed, 2010-08-18 at 08:30 +0000, Julian Edwards wrote:
> Review: Needs Information code
> Really nice branch! I have a couple of comments:
>
> > def callable():
> > global called
> > called += 1
>
> Can this be lp.testing.fakemethod.FakeMethod instead?

Good catch; I keep forgetting about these fake* things that Jeroen
wrote.

>
> Also, you've un-factored findPolicyByName(). Why didn't you fix it in one place for all the existing call sites to use instead of changing all the call sites?

I guess you mean findPolicyByOptions()? I un-factored that because its
new implementation would be just two lines, but more importantly because
one of that method's side effects was setting the options (through
setOptions()) on the retrieved policy (which was in fact retrieved by
name and not by any of its options), and I since explicit is better than
implicit<tm> I thought it was a good idea to have the call sites call
setOptions() themselves.

--
Guilherme Salgado <https://launchpad.net/~salgado>

Revision history for this message
Julian Edwards (julian-edwards) wrote :

Well I guess I meant both :)

So considering:

- policy = findPolicyByName(name)
+ policy = getUtility(IArchiveUploadPolicy, name)()

I find the former far more readable - it actually tells me what it's doing *and* more importantly it's abstracting out *how* it works so we can tweak it more easily later if we want. The fact that it's the same line count doesn't really matter - in fact your diff would have been much smaller :)

As for moving out setOptions(), I think that's OK, but given my reasoning above, would you agree it makes sense at least to keep the old names?

review: Needs Information
Revision history for this message
Guilherme Salgado (salgado) wrote :

On Wed, 2010-08-18 at 13:13 +0000, Julian Edwards wrote:
> Review: Needs Information
> Well I guess I meant both :)
>
> So considering:
>
> - policy = findPolicyByName(name)
> + policy = getUtility(IArchiveUploadPolicy, name)()
>
> I find the former far more readable - it actually tells me what it's
> doing *and* more importantly it's abstracting out *how* it works so we
> can tweak it more easily later if we want. The fact that it's the
> same line count doesn't really matter - in fact your diff would have
> been much smaller :)

I see; that sounds ok to me. I'll do that change.

>
> As for moving out setOptions(), I think that's OK, but given my
> reasoning above, would you agree it makes sense at least to keep the
> old names?

names? Do you mean to keep findPolicyByOptions() as well? I don't see
any reason for doing that as it just uses options.context to lookup a
policy by name -- I'd rather have the callsites use
findPolicyByName(options.context) instead. How does that sound to you?

--
Guilherme Salgado <https://launchpad.net/~salgado>

Revision history for this message
Julian Edwards (julian-edwards) wrote :

 merge approve
 review approve

On Wednesday 18 August 2010 14:25:58 you wrote:
> On Wed, 2010-08-18 at 13:13 +0000, Julian Edwards wrote:
> > Review: Needs Information
> > Well I guess I meant both :)
> >
> > So considering:
> >
> > - policy = findPolicyByName(name)
> > + policy = getUtility(IArchiveUploadPolicy, name)()
> >
> > I find the former far more readable - it actually tells me what it's
> > doing *and* more importantly it's abstracting out *how* it works so we
> > can tweak it more easily later if we want. The fact that it's the
> > same line count doesn't really matter - in fact your diff would have
> > been much smaller :)
>
> I see; that sounds ok to me. I'll do that change.

Great!

>
> > As for moving out setOptions(), I think that's OK, but given my
> > reasoning above, would you agree it makes sense at least to keep the
> > old names?
>
> names? Do you mean to keep findPolicyByOptions() as well? I don't see
> any reason for doing that as it just uses options.context to lookup a
> policy by name -- I'd rather have the callsites use
> findPolicyByName(options.context) instead. How does that sound to you?

Ah yes of course, that's good.

Land it! Thanks for the improvement to Soyuz.

review: Approve
Revision history for this message
Guilherme Salgado (salgado) wrote :

Julian, I'd like to include some extra (related) changes on this branch; just moving test-specific stuff to lp/archiveuploader/tests. Ideally I'd have these utilities registered only when running tests, but that's not possible for the Scripts/Zopeless layer, so we have to register them all the time.

Revision history for this message
Guilherme Salgado (salgado) wrote :
Revision history for this message
Julian Edwards (julian-edwards) wrote :

On Wednesday 18 August 2010 16:02:38 you wrote:
> Julian, I'd like to include some extra (related) changes on this branch;
> just moving test-specific stuff to lp/archiveuploader/tests. Ideally I'd
> have these utilities registered only when running tests, but that's not
> possible for the Scripts/Zopeless layer, so we have to register them all
> the time.

I use those policies on the command line when I am doing testing on dogfood,
so I'd like to see them left where they are please.

Revision history for this message
Guilherme Salgado (salgado) wrote :

FTR:
<salgado> bigjools, you'll still be able to use them; they'll just live closer to the tests now. although I'd have to change that XXX into a comment explaining why we want the policies registered all the time
<bigjools> salgado: ok that's fine if we can still use them from the command line

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/canonical/launchpad/webapp/meta.zcml'
2--- lib/canonical/launchpad/webapp/meta.zcml 2010-07-22 02:10:27 +0000
3+++ lib/canonical/launchpad/webapp/meta.zcml 2010-08-18 16:15:57 +0000
4@@ -26,6 +26,13 @@
5 />
6
7 <directive
8+ namespace="http://namespaces.zope.org/zope"
9+ name="call"
10+ schema="canonical.launchpad.webapp.metazcml.ICallDirective"
11+ handler="canonical.launchpad.webapp.metazcml.call"
12+ />
13+
14+ <directive
15 namespace="http://namespaces.zope.org/browser"
16 name="feeds"
17 schema="canonical.launchpad.webapp.metazcml.IFeedsDirective"
18
19=== modified file 'lib/canonical/launchpad/webapp/metazcml.py'
20--- lib/canonical/launchpad/webapp/metazcml.py 2010-08-13 15:26:43 +0000
21+++ lib/canonical/launchpad/webapp/metazcml.py 2010-08-18 16:15:57 +0000
22@@ -618,6 +618,22 @@
23 self)
24
25
26+class ICallDirective(Interface):
27+ """Call the given callable.
28+
29+ This is useful when you have something that you want to call at startup
30+ but don't want it tied to a specific zope event. Or when you need to
31+ register utilities in python at the time the zcml is processed.
32+ """
33+
34+ callable = GlobalObject(
35+ title=u"The thing that will be called.", required=True)
36+
37+
38+def call(_context, callable):
39+ callable()
40+
41+
42 class IDefineLaunchpadPermissionDirective(IPermissionDirective):
43
44 access_level = TextLine(
45
46=== added file 'lib/canonical/launchpad/webapp/tests/test_metazcml.py'
47--- lib/canonical/launchpad/webapp/tests/test_metazcml.py 1970-01-01 00:00:00 +0000
48+++ lib/canonical/launchpad/webapp/tests/test_metazcml.py 2010-08-18 16:15:57 +0000
49@@ -0,0 +1,30 @@
50+# Copyright 2010 Canonical Ltd. This software is licensed under the
51+# GNU Affero General Public License version 3 (see the file LICENSE).
52+
53+__metaclass__ = type
54+
55+from zope.configuration import xmlconfig
56+
57+
58+from lp.testing import TestCase
59+from lp.testing.fakemethod import FakeMethod
60+
61+
62+class TestCallDirective(TestCase):
63+
64+ def test_call(self):
65+ directive = """
66+ <call callable="%(this)s.callable" />
67+ """ % dict(this=this)
68+ xmlconfig.string(zcml_configure % directive)
69+ self.assertEqual(1, callable.call_count)
70+
71+
72+callable = FakeMethod()
73+this = "canonical.launchpad.webapp.tests.test_metazcml"
74+zcml_configure = """
75+ <configure xmlns="http://namespaces.zope.org/zope">
76+ <include package="canonical.launchpad.webapp" file="meta.zcml" />
77+ %s
78+ </configure>
79+ """
80
81=== modified file 'lib/lp/archiveuploader/tests/__init__.py'
82--- lib/lp/archiveuploader/tests/__init__.py 2010-08-06 13:52:46 +0000
83+++ lib/lp/archiveuploader/tests/__init__.py 2010-08-18 16:15:57 +0000
84@@ -15,7 +15,10 @@
85 import sys
86 import traceback
87
88-from lp.archiveuploader.uploadpolicy import findPolicyByName
89+from zope.component import getGlobalSiteManager
90+
91+from lp.archiveuploader.uploadpolicy import (
92+ AbstractUploadPolicy, findPolicyByName, IArchiveUploadPolicy)
93 from lp.soyuz.model.queue import PackageUploadSet
94 from canonical.librarian.ftests.harness import fillLibrarianFile
95
96@@ -98,6 +101,55 @@
97 self.print_traceback(exc_info)
98
99
100+class AnythingGoesUploadPolicy(AbstractUploadPolicy):
101+ """This policy is invoked when processing uploads from the test process.
102+
103+ We require a signed changes file but that's it.
104+ """
105+
106+ name = 'anything'
107+
108+ def __init__(self):
109+ AbstractUploadPolicy.__init__(self)
110+ # We require the changes to be signed but not the dsc
111+ self.unsigned_dsc_ok = True
112+
113+ def policySpecificChecks(self, upload):
114+ """Nothing, let it go."""
115+ pass
116+
117+ def rejectPPAUploads(self, upload):
118+ """We allow PPA uploads."""
119+ return False
120+
121+
122+class AbsolutelyAnythingGoesUploadPolicy(AnythingGoesUploadPolicy):
123+ """This policy is invoked when processing uploads from the test process.
124+
125+ Absolutely everything is allowed, for when you don't want the hassle
126+ of dealing with inappropriate checks in tests.
127+ """
128+
129+ name = 'absolutely-anything'
130+
131+ def __init__(self):
132+ AnythingGoesUploadPolicy.__init__(self)
133+ self.unsigned_changes_ok = True
134+
135+ def policySpecificChecks(self, upload):
136+ """Nothing, let it go."""
137+ pass
138+
139+
140+def register_archive_upload_policy_adapters():
141+ policies = [
142+ AnythingGoesUploadPolicy, AbsolutelyAnythingGoesUploadPolicy]
143+ sm = getGlobalSiteManager()
144+ for policy in policies:
145+ sm.registerUtility(
146+ component=policy, provided=IArchiveUploadPolicy, name=policy.name)
147+
148+
149 mock_options = MockUploadOptions()
150 mock_logger = MockUploadLogger()
151 mock_logger_quiet = MockUploadLogger(verbose=False)
152
153=== modified file 'lib/lp/archiveuploader/tests/test_uploadprocessor.py'
154--- lib/lp/archiveuploader/tests/test_uploadprocessor.py 2010-08-13 06:08:36 +0000
155+++ lib/lp/archiveuploader/tests/test_uploadprocessor.py 2010-08-18 16:15:57 +0000
156@@ -19,14 +19,13 @@
157
158 from email import message_from_string
159
160-from zope.component import getUtility
161+from zope.component import getGlobalSiteManager, getUtility
162 from zope.security.proxy import removeSecurityProxy
163
164 from lp.app.errors import NotFoundError
165-from lp.archiveuploader.uploadpolicy import (AbstractUploadPolicy,
166- findPolicyByOptions)
167+from lp.archiveuploader.uploadpolicy import (
168+ AbstractUploadPolicy, IArchiveUploadPolicy, findPolicyByName)
169 from lp.archiveuploader.uploadprocessor import UploadProcessor
170-from lp.buildmaster.interfaces.buildbase import BuildStatus
171 from canonical.config import config
172 from canonical.database.constants import UTC_NOW
173 from lp.soyuz.model.archivepermission import ArchivePermission
174@@ -62,7 +61,7 @@
175 ISourcePackageNameSet)
176 from lp.services.mail import stub
177 from canonical.launchpad.testing.fakepackager import FakePackager
178-from lp.testing import TestCase, TestCaseWithFactory
179+from lp.testing import TestCaseWithFactory
180 from lp.testing.mail_helpers import pop_notifications
181 from canonical.launchpad.webapp.errorlog import ErrorReportingUtility
182 from canonical.testing import LaunchpadZopelessLayer
183@@ -146,7 +145,9 @@
184 def getUploadProcessor(self, txn):
185 def getPolicy(distro):
186 self.options.distro = distro.name
187- return findPolicyByOptions(self.options)
188+ policy = findPolicyByName(self.options.context)
189+ policy.setOptions(self.options)
190+ return policy
191 return UploadProcessor(
192 self.options.base_fsroot, self.options.dryrun,
193 self.options.nomails,
194@@ -533,9 +534,12 @@
195
196 See bug 35965.
197 """
198- # Register our broken upload policy
199- AbstractUploadPolicy._registerPolicy(BrokenUploadPolicy)
200- self.options.context = 'broken'
201+ self.options.context = u'broken'
202+ # Register our broken upload policy.
203+ getGlobalSiteManager().registerUtility(
204+ component=BrokenUploadPolicy, provided=IArchiveUploadPolicy,
205+ name=self.options.context)
206+
207 uploadprocessor = self.getUploadProcessor(self.layer.txn)
208
209 # Upload a package to Breezy.
210
211=== modified file 'lib/lp/archiveuploader/tests/uploadpolicy.txt'
212--- lib/lp/archiveuploader/tests/uploadpolicy.txt 2010-07-24 09:12:37 +0000
213+++ lib/lp/archiveuploader/tests/uploadpolicy.txt 2010-08-18 16:15:57 +0000
214@@ -1,31 +1,39 @@
215 == The uploader policies ==
216
217-When the uploader is invoked, it is given a policy to work in. This
218-governs such things as what tests get run at what stages of the
219-upload, and whether or not there is a build to be created, or one in
220-existence to be used. These policies are in the
221-lp.archiveuploader package, in the uploadpolicy module. They
222-are accessed by calling findPolicyByName which will either return a
223-policy instance or else raise a KeyError.
224-
225-XXX: dsilvers: 20051019: Get rid of this registration/lookup system
226-when we've fixed bug 3137
227-
228- >>> from lp.archiveuploader.uploadpolicy import findPolicyByName
229-
230-There are two policies defined so far. They are the insecure and
231-buildd policies.
232+When the uploader is invoked, it is given a policy to work in. This governs
233+such things as what tests get run at what stages of the upload, and whether or
234+not there is a build to be created, or one in existence to be used. These
235+policies are in the lp.archiveuploader package, in the uploadpolicy module,
236+and implement IArchiveUploadPolicy. They are registered as named utilities,
237+but since the policy themselves need to be changed according to user-specified
238+arguments, we register the classes as the component of the utilities, which
239+means a call to getUtility(IArchiveUploadPolicy, name) will return the class
240+itself rather than an instance of it.
241+
242+ >>> from lp.archiveuploader.uploadpolicy import (
243+ ... IArchiveUploadPolicy, findPolicyByName)
244+ >>> policy_cls = getUtility(IArchiveUploadPolicy, 'insecure')
245+ >>> policy_cls
246+ <class '...InsecureUploadPolicy'>
247+
248+There's a helper function which returns an instance of the policy with the
249+given name, though, and it's preferred over using getUtility() directly.
250
251 >>> insecure_policy = findPolicyByName('insecure')
252- >>> insecure_policy.name == 'insecure'
253- True
254+ >>> insecure_policy
255+ <lp...InsecureUploadPolicy object...
256+
257+Two of the policies defined so far are the insecure and buildd policies.
258+
259+ >>> insecure_policy.name
260+ 'insecure'
261 >>> buildd_policy = findPolicyByName('buildd')
262- >>> buildd_policy.name == 'buildd'
263- True
264+ >>> buildd_policy.name
265+ 'buildd'
266 >>> abstract_policy = findPolicyByName('abstract')
267 Traceback (most recent call last):
268 ...
269- KeyError: 'abstract'
270+ ComponentLookupError:...
271
272 There is a bunch of attributes which we expect to have and which can vary
273 from policy to policy.
274
275=== modified file 'lib/lp/archiveuploader/uploadpolicy.py'
276--- lib/lp/archiveuploader/uploadpolicy.py 2010-06-23 15:54:45 +0000
277+++ lib/lp/archiveuploader/uploadpolicy.py 2010-08-18 16:15:57 +0000
278@@ -6,21 +6,25 @@
279 __metaclass__ = type
280
281 __all__ = [
282+ "AbstractUploadPolicy",
283+ "BuildDaemonUploadPolicy",
284 "findPolicyByName",
285- "findPolicyByOptions",
286+ "IArchiveUploadPolicy",
287+ "SOURCE_PACKAGE_RECIPE_UPLOAD_POLICY_NAME",
288 "UploadPolicyError",
289 ]
290
291-from zope.component import getUtility
292+from zope.component import getGlobalSiteManager, getUtility
293+from zope.interface import implements, Interface
294
295 from canonical.launchpad.interfaces import ILaunchpadCelebrities
296-from lp.code.interfaces.sourcepackagerecipebuild import (
297- ISourcePackageRecipeBuildSource)
298 from lp.registry.interfaces.distribution import IDistributionSet
299 from lp.registry.interfaces.series import SeriesStatus
300 from lp.registry.interfaces.pocket import PackagePublishingPocket
301
302
303+# Defined here so that uploadpolicy.py doesn't depend on lp.code.
304+SOURCE_PACKAGE_RECIPE_UPLOAD_POLICY_NAME = 'recipe'
305 # Number of seconds in an hour (used later)
306 HOURS = 3600
307
308@@ -29,6 +33,20 @@
309 """Raised when a specific policy violation occurs."""
310
311
312+class IArchiveUploadPolicy(Interface):
313+ """The policy of an upload to a Launchpad archive.
314+
315+ This is, in practice, just a marker interface for us to look up upload
316+ policies by name.
317+
318+ If registered as a utility, any classes implementing this must be given as
319+ the 'component' argument of the <utility> directive so that a getUtility()
320+ call returns the class itself rather than an instance. This is needed
321+ because the callsites usually change the policies (based on user-specified
322+ arguments).
323+ """
324+
325+
326 class AbstractUploadPolicy:
327 """Encapsulate the policy of an upload to a launchpad archive.
328
329@@ -38,13 +56,13 @@
330 tests themselves and they operate on NascentUpload instances in order
331 to verify them.
332 """
333+ implements(IArchiveUploadPolicy)
334
335- policies = {}
336 options = None
337+ name = 'abstract'
338
339 def __init__(self):
340 """Prepare a policy..."""
341- self.name = 'abstract'
342 self.distro = None
343 self.distroseries = None
344 self.pocket = None
345@@ -143,39 +161,14 @@
346 """Return whether the NEW upload should be automatically approved."""
347 return False
348
349- @classmethod
350- def _registerPolicy(cls, policy_type):
351- """Register the given policy type as belonging to its given name."""
352- # XXX: JonathanLange 2010-01-15 bug=510892: This shouldn't instantiate
353- # policy types. They should instead have names as class variables.
354- policy_name = policy_type().name
355- cls.policies[policy_name] = policy_type
356-
357- @classmethod
358- def findPolicyByName(cls, policy_name):
359- """Return a new policy instance for the given policy name."""
360- return cls.policies[policy_name]()
361-
362- @classmethod
363- def findPolicyByOptions(cls, options):
364- """Return a new policy instance given the options dictionary."""
365- policy = cls.policies[options.context]()
366- policy.setOptions(options)
367- return policy
368-
369-# XXX: dsilvers 2005-10-19 bug=3373: use the component architecture for
370-# these instead of reinventing the registration/finder again?
371-# Nice shiny top-level policy finder
372-findPolicyByName = AbstractUploadPolicy.findPolicyByName
373-findPolicyByOptions = AbstractUploadPolicy.findPolicyByOptions
374-
375
376 class InsecureUploadPolicy(AbstractUploadPolicy):
377 """The insecure upload policy is used by the poppy interface."""
378
379+ name = 'insecure'
380+
381 def __init__(self):
382 AbstractUploadPolicy.__init__(self)
383- self.name = 'insecure'
384 self.can_upload_binaries = False
385 self.can_upload_mixed = False
386
387@@ -281,15 +274,13 @@
388 return False
389
390
391-AbstractUploadPolicy._registerPolicy(InsecureUploadPolicy)
392-
393-
394 class BuildDaemonUploadPolicy(AbstractUploadPolicy):
395 """The build daemon upload policy is invoked by the slave scanner."""
396
397+ name = 'buildd'
398+
399 def __init__(self):
400 super(BuildDaemonUploadPolicy, self).__init__()
401- self.name = 'buildd'
402 # We permit unsigned uploads because we trust our build daemons
403 self.unsigned_changes_ok = True
404 self.unsigned_dsc_ok = True
405@@ -313,37 +304,13 @@
406 return False
407
408
409-AbstractUploadPolicy._registerPolicy(BuildDaemonUploadPolicy)
410-
411-
412-class SourcePackageRecipeUploadPolicy(BuildDaemonUploadPolicy):
413- """Policy for uploading the results of a source package recipe build."""
414-
415- def __init__(self):
416- super(SourcePackageRecipeUploadPolicy, self).__init__()
417- # XXX: JonathanLange 2010-01-15 bug=510894: This has to be exactly the
418- # same string as the one in SourcePackageRecipeBuild.policy_name.
419- # Factor out a shared constant.
420- self.name = 'recipe'
421- self.can_upload_source = True
422- self.can_upload_binaries = False
423-
424- def getUploader(self, changes):
425- """Return the person doing the upload."""
426- build_id = int(getattr(self.options, 'buildid'))
427- sprb = getUtility(ISourcePackageRecipeBuildSource).getById(build_id)
428- return sprb.requester
429-
430-
431-AbstractUploadPolicy._registerPolicy(SourcePackageRecipeUploadPolicy)
432-
433-
434 class SyncUploadPolicy(AbstractUploadPolicy):
435 """This policy is invoked when processing sync uploads."""
436
437+ name = 'sync'
438+
439 def __init__(self):
440 AbstractUploadPolicy.__init__(self)
441- self.name = "sync"
442 # We don't require changes or dsc to be signed for syncs
443 self.unsigned_changes_ok = True
444 self.unsigned_dsc_ok = True
445@@ -357,50 +324,6 @@
446 # Implement this to check the sync
447 pass
448
449-AbstractUploadPolicy._registerPolicy(SyncUploadPolicy)
450-
451-
452-class AnythingGoesUploadPolicy(AbstractUploadPolicy):
453- """This policy is invoked when processing uploads from the test process.
454-
455- We require a signed changes file but that's it.
456- """
457-
458- def __init__(self):
459- AbstractUploadPolicy.__init__(self)
460- self.name = "anything"
461- # We require the changes to be signed but not the dsc
462- self.unsigned_dsc_ok = True
463-
464- def policySpecificChecks(self, upload):
465- """Nothing, let it go."""
466- pass
467-
468- def rejectPPAUploads(self, upload):
469- """We allow PPA uploads."""
470- return False
471-
472-AbstractUploadPolicy._registerPolicy(AnythingGoesUploadPolicy)
473-
474-
475-class AbsolutelyAnythingGoesUploadPolicy(AnythingGoesUploadPolicy):
476- """This policy is invoked when processing uploads from the test process.
477-
478- Absolutely everything is allowed, for when you don't want the hassle
479- of dealing with inappropriate checks in tests.
480- """
481-
482- def __init__(self):
483- AnythingGoesUploadPolicy.__init__(self)
484- self.name = "absolutely-anything"
485- self.unsigned_changes_ok = True
486-
487- def policySpecificChecks(self, upload):
488- """Nothing, let it go."""
489- pass
490-
491-AbstractUploadPolicy._registerPolicy(AbsolutelyAnythingGoesUploadPolicy)
492-
493
494 class SecurityUploadPolicy(AbstractUploadPolicy):
495 """The security-upload policy.
496@@ -408,9 +331,10 @@
497 It allows unsigned changes and binary uploads.
498 """
499
500+ name = 'security'
501+
502 def __init__(self):
503 AbstractUploadPolicy.__init__(self)
504- self.name = "security"
505 self.unsigned_dsc_ok = True
506 self.unsigned_changes_ok = True
507 self.can_upload_mixed = True
508@@ -422,4 +346,19 @@
509 upload.reject(
510 "Not permitted to do security upload to non SECURITY pocket")
511
512-AbstractUploadPolicy._registerPolicy(SecurityUploadPolicy)
513+
514+def findPolicyByName(policy_name):
515+ """Return a new policy instance for the given policy name."""
516+ return getUtility(IArchiveUploadPolicy, policy_name)()
517+
518+
519+def register_archive_upload_policy_adapters():
520+ policies = [
521+ BuildDaemonUploadPolicy,
522+ InsecureUploadPolicy,
523+ SyncUploadPolicy,
524+ SecurityUploadPolicy]
525+ sm = getGlobalSiteManager()
526+ for policy in policies:
527+ sm.registerUtility(
528+ component=policy, provided=IArchiveUploadPolicy, name=policy.name)
529
530=== modified file 'lib/lp/archiveuploader/uploadprocessor.py'
531--- lib/lp/archiveuploader/uploadprocessor.py 2010-08-12 17:54:00 +0000
532+++ lib/lp/archiveuploader/uploadprocessor.py 2010-08-18 16:15:57 +0000
533@@ -60,6 +60,8 @@
534 from lp.archiveuploader.nascentupload import (
535 NascentUpload, FatalUploadError, EarlyReturnUploadError)
536 from lp.archiveuploader.uploadpolicy import (
537+ BuildDaemonUploadPolicy,
538+ SOURCE_PACKAGE_RECIPE_UPLOAD_POLICY_NAME,
539 UploadPolicyError)
540 from lp.soyuz.interfaces.archive import IArchiveSet, NoSuchPPA
541 from lp.registry.interfaces.distribution import IDistributionSet
542@@ -350,10 +352,10 @@
543
544 # Reject source upload to buildd upload paths.
545 first_path = relative_path.split(os.path.sep)[0]
546- # XXX: JonathanLange 2010-01-15 bug=510894: We should not be re-using
547- # magical string literals. Zombie Dijkstra will come and kill us in
548- # our sleep.
549- if first_path.isdigit() and policy.name not in ('buildd', 'recipe'):
550+ is_not_buildd_nor_recipe_policy = policy.name not in [
551+ SOURCE_PACKAGE_RECIPE_UPLOAD_POLICY_NAME,
552+ BuildDaemonUploadPolicy.name]
553+ if first_path.isdigit() and is_not_buildd_nor_recipe_policy:
554 error_message = (
555 "Invalid upload path (%s) for this policy (%s)" %
556 (relative_path, policy.name))
557@@ -448,8 +450,6 @@
558 self.log.debug("Keeping contents untouched")
559 return
560
561- pathname = os.path.basename(upload)
562-
563 self.log.debug("Removing upload directory %s", upload)
564 shutil.rmtree(upload)
565
566@@ -547,7 +547,7 @@
567
568 suite_name = parts[1]
569 try:
570- suite = distribution.getDistroSeriesAndPocket(suite_name)
571+ distribution.getDistroSeriesAndPocket(suite_name)
572 except NotFoundError:
573 raise exc_type("Could not find suite '%s'." % suite_name)
574
575
576=== modified file 'lib/lp/code/configure.zcml'
577--- lib/lp/code/configure.zcml 2010-08-13 04:01:57 +0000
578+++ lib/lp/code/configure.zcml 2010-08-18 16:15:57 +0000
579@@ -999,6 +999,11 @@
580 <utility component="lp.code.model.sourcepackagerecipebuild.SourcePackageRecipeBuildJob"
581 name="RECIPEBRANCHBUILD"
582 provides="lp.buildmaster.interfaces.buildfarmjob.IBuildFarmJob"/>
583+
584+ <call
585+ callable="lp.code.model.sourcepackagerecipebuild.register_archive_upload_policy_adapter"
586+ />
587+
588 <webservice:register module="lp.code.interfaces.webservice" />
589 <adapter
590 provides="lp.buildmaster.interfaces.buildfarmjob.ISpecificBuildFarmJob"
591
592=== modified file 'lib/lp/code/model/sourcepackagerecipebuild.py'
593--- lib/lp/code/model/sourcepackagerecipebuild.py 2010-08-09 20:37:40 +0000
594+++ lib/lp/code/model/sourcepackagerecipebuild.py 2010-08-18 16:15:57 +0000
595@@ -24,12 +24,15 @@
596 from storm.locals import Int, Reference, Storm
597 from storm.store import Store
598
599-from zope.component import getUtility
600+from zope.component import getGlobalSiteManager, getUtility
601 from zope.interface import classProvides, implements
602 from zope.security.proxy import ProxyFactory
603
604 from canonical.launchpad.webapp import errorlog
605 from lp.app.errors import NotFoundError
606+from lp.archiveuploader.uploadpolicy import (
607+ BuildDaemonUploadPolicy, IArchiveUploadPolicy,
608+ SOURCE_PACKAGE_RECIPE_UPLOAD_POLICY_NAME)
609 from lp.buildmaster.model.packagebuild import (
610 PackageBuild, PackageBuildDerived)
611 from lp.buildmaster.interfaces.buildbase import BuildStatus
612@@ -54,11 +57,28 @@
613 from lp.soyuz.model.sourcepackagerelease import SourcePackageRelease
614
615
616+class SourcePackageRecipeUploadPolicy(BuildDaemonUploadPolicy):
617+ """Policy for uploading the results of a source package recipe build."""
618+
619+ name = SOURCE_PACKAGE_RECIPE_UPLOAD_POLICY_NAME
620+
621+ def __init__(self):
622+ super(SourcePackageRecipeUploadPolicy, self).__init__()
623+ self.can_upload_source = True
624+ self.can_upload_binaries = False
625+
626+ def getUploader(self, changes):
627+ """Return the person doing the upload."""
628+ build_id = int(getattr(self.options, 'buildid'))
629+ sprb = getUtility(ISourcePackageRecipeBuildSource).getById(build_id)
630+ return sprb.requester
631+
632+
633 class SourcePackageRecipeBuild(PackageBuildDerived, Storm):
634
635 __storm_table__ = 'SourcePackageRecipeBuild'
636
637- policy_name = 'recipe'
638+ policy_name = SourcePackageRecipeUploadPolicy.name
639
640 implements(ISourcePackageRecipeBuild)
641 classProvides(ISourcePackageRecipeBuildSource)
642@@ -348,6 +368,13 @@
643 return 2405 + self.build.archive.relative_build_score
644
645
646+def register_archive_upload_policy_adapter():
647+ getGlobalSiteManager().registerUtility(
648+ component=SourcePackageRecipeUploadPolicy,
649+ provided=IArchiveUploadPolicy,
650+ name=SourcePackageRecipeUploadPolicy.name)
651+
652+
653 def get_recipe_build_for_build_farm_job(build_farm_job):
654 """Return the SourcePackageRecipeBuild associated with a BuildFarmJob."""
655 store = Store.of(build_farm_job)
656
657=== modified file 'lib/lp/soyuz/configure.zcml'
658--- lib/lp/soyuz/configure.zcml 2010-08-16 21:34:11 +0000
659+++ lib/lp/soyuz/configure.zcml 2010-08-18 16:15:57 +0000
660@@ -855,4 +855,15 @@
661 interface="lp.soyuz.interfaces.binarypackagerelease.IBinaryPackageReleaseDownloadCount"/>
662 </class>
663
664+ <call
665+ callable="lp.archiveuploader.uploadpolicy.register_archive_upload_policy_adapters"
666+ />
667+
668+ <!-- Although the policies defined in lp.archiveuploader.tests are only
669+ required by tests, they're used when testing scripts manually on
670+ dogfood/staging, so they're registered here. -->
671+ <call
672+ callable="lp.archiveuploader.tests.register_archive_upload_policy_adapters"
673+ />
674+
675 </configure>
676
677=== modified file 'lib/lp/soyuz/scripts/soyuz_process_upload.py'
678--- lib/lp/soyuz/scripts/soyuz_process_upload.py 2010-08-06 13:52:46 +0000
679+++ lib/lp/soyuz/scripts/soyuz_process_upload.py 2010-08-18 16:15:57 +0000
680@@ -8,7 +8,7 @@
681
682 import os
683
684-from lp.archiveuploader.uploadpolicy import findPolicyByOptions
685+from lp.archiveuploader.uploadpolicy import findPolicyByName
686 from lp.archiveuploader.uploadprocessor import UploadProcessor
687 from lp.services.scripts.base import (
688 LaunchpadCronScript, LaunchpadScriptFailure)
689@@ -77,7 +77,9 @@
690 self.logger.debug("Initialising connection.")
691 def getPolicy(distro):
692 self.options.distro = distro.name
693- return findPolicyByOptions(self.options)
694+ policy = findPolicyByName(self.options.context)
695+ policy.setOptions(self.options)
696+ return policy
697 processor = UploadProcessor(self.options.base_fsroot,
698 self.options.dryrun, self.options.nomails, self.options.keep,
699 getPolicy, self.txn, self.logger)