Merge lp:~salgado/launchpad/upload-policy-utility into lp:launchpad
- upload-policy-utility
- Merge into devel
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 | ||||||||
Related bugs: |
|
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/
they can be accessed externally and don't need to be hard-coded in multiple
places.
Julian Edwards (julian-edwards) wrote : | # |
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.
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 findPolicyByOpt
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:/
Julian Edwards (julian-edwards) wrote : | # |
Well I guess I meant both :)
So considering:
- policy = findPolicyByNam
+ policy = getUtility(
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?
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 = findPolicyByNam
> + policy = getUtility(
>
> 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 findPolicyByOpt
any reason for doing that as it just uses options.context to lookup a
policy by name -- I'd rather have the callsites use
findPolicyByNam
--
Guilherme Salgado <https:/
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 = findPolicyByNam
> > + policy = getUtility(
> >
> > 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 findPolicyByOpt
> any reason for doing that as it just uses options.context to lookup a
> policy by name -- I'd rather have the callsites use
> findPolicyByNam
Ah yes of course, that's good.
Land it! Thanks for the improvement to Soyuz.
Guilherme Salgado (salgado) wrote : | # |
Julian, I'd like to include some extra (related) changes on this branch; just moving test-specific stuff to lp/archiveuploa
Guilherme Salgado (salgado) wrote : | # |
http://
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/archiveuploa
> 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.
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
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) |
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?