Merge lp:~cjwatson/launchpad/change-override-forbid-release-stable into lp:launchpad

Proposed by Colin Watson on 2012-06-19
Status: Merged
Approved by: William Grant on 2012-06-19
Approved revision: no longer in the source branch.
Merged at revision: 15441
Proposed branch: lp:~cjwatson/launchpad/change-override-forbid-release-stable
Merge into: lp:launchpad
Diff against target: 665 lines (+193/-145)
15 files modified
lib/lp/archiveuploader/uploadpolicy.py (+4/-4)
lib/lp/buildmaster/model/packagebuild.py (+5/-6)
lib/lp/registry/doc/distroseries.txt (+26/-16)
lib/lp/registry/interfaces/distroseries.py (+0/-20)
lib/lp/registry/model/distroseries.py (+6/-55)
lib/lp/soyuz/adapters/copypolicy.py (+1/-2)
lib/lp/soyuz/doc/buildd-queuebuilder-lookup.txt (+3/-3)
lib/lp/soyuz/doc/publishing.txt (+2/-0)
lib/lp/soyuz/interfaces/archive.py (+22/-0)
lib/lp/soyuz/model/archive.py (+33/-1)
lib/lp/soyuz/model/binarypackagebuild.py (+3/-4)
lib/lp/soyuz/model/publishing.py (+30/-23)
lib/lp/soyuz/model/queue.py (+8/-10)
lib/lp/soyuz/scripts/tests/test_changeoverride.py (+4/-0)
lib/lp/soyuz/tests/test_publishing.py (+46/-1)
To merge this branch: bzr merge lp:~cjwatson/launchpad/change-override-forbid-release-stable
Reviewer Review Type Date Requested Status
William Grant code 2012-06-19 Approve on 2012-06-19
Review via email: mp+110942@code.launchpad.net

Commit Message

Prevent changeOverride creating publications in RELEASE pockets that will never be published.

Description of the Change

== Summary ==

Bug 530020: override changes can result in stranded publications that will never be published, since the RELEASE pockets of released DSes in the primary Ubuntu archive are immutable.

== Proposed fix ==

Move the cannotModifySuite method from the publisher down to DistroSeries so that changeOverride can use it (there are enough copies of this logic already without adding more!), and raise an exception in changeOverride if that indicates that the newly-created publication would be stranded.

== Implementation details ==

I cleaned up some useless "current = self" code in changeOverride. This made sense before the removal of secure publication records in r7659.7.1, but now it's just more typing.

== LOC Rationale ==

+42. I'm doing this so that I can land https://code.launchpad.net/~cjwatson/launchpad/export-change-override/+merge/109549, which is looking like it should come out at least 76 lines in credit once the old script is removed, so I think this will be OK.

== Tests ==

bin/test -vvct test_publishing.TestChangeOverride

== Demo and Q/A ==

On dogfood, attempt to run 'change-override.py -s precise -p extra base-files' and 'change-override.py -s quantal -p extra base-files'; these should respectively fail and succeed.

To post a comment you must log in.
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/archiveuploader/uploadpolicy.py'
2--- lib/lp/archiveuploader/uploadpolicy.py 2012-03-28 13:27:58 +0000
3+++ lib/lp/archiveuploader/uploadpolicy.py 2012-06-19 10:11:30 +0000
4@@ -263,10 +263,10 @@
5 PPA uploads are always auto-approved.
6 RELEASE and PROPOSED pocket uploads (to main archives) are only
7 auto-approved if the distroseries is in a non-FROZEN state
8- pre-release. (We already performed the
9- IDistroSeries.canUploadToPocket check in the checkUpload base
10- method, which will deny RELEASE uploads post-release, but it doesn't
11- hurt to repeat this for that case.)
12+ pre-release. (We already performed the IArchive.canModifySuite
13+ check in the checkUpload base method, which will deny RELEASE
14+ uploads post-release, but it doesn't hurt to repeat this for that
15+ case.)
16 """
17 if upload.is_ppa:
18 return True
19
20=== modified file 'lib/lp/buildmaster/model/packagebuild.py'
21--- lib/lp/buildmaster/model/packagebuild.py 2012-01-11 08:52:22 +0000
22+++ lib/lp/buildmaster/model/packagebuild.py 2012-06-19 10:11:30 +0000
23@@ -1,4 +1,4 @@
24-# Copyright 2010 Canonical Ltd. This software is licensed under the
25+# Copyright 2010-2012 Canonical Ltd. This software is licensed under the
26 # GNU Affero General Public License version 3 (see the file LICENSE).
27
28 __metaclass__ = type
29@@ -330,11 +330,10 @@
30 return self._release_builder_and_remove_queue_item()
31
32 # Explode before collect a binary that is denied in this
33- # distroseries/pocket
34- if not self.archive.allowUpdatesToReleasePocket():
35- assert self.distro_series.canUploadToPocket(self.pocket), (
36- "%s (%s) can not be built for pocket %s: illegal status"
37- % (self.title, self.id, self.pocket.name))
38+ # distroseries/pocket/archive
39+ assert self.archive.canModifySuite(self.distro_series, self.pocket), (
40+ "%s (%s) can not be built for pocket %s in %s: illegal status"
41+ % (self.title, self.id, self.pocket.name, self.archive))
42
43 # Ensure we have the correct build root as:
44 # <BUILDMASTER_ROOT>/incoming/<UPLOAD_LEAF>/<TARGET_PATH>/[FILES]
45
46=== modified file 'lib/lp/registry/doc/distroseries.txt'
47--- lib/lp/registry/doc/distroseries.txt 2012-04-23 22:18:46 +0000
48+++ lib/lp/registry/doc/distroseries.txt 2012-06-19 10:11:30 +0000
49@@ -166,10 +166,11 @@
50
51 See distroseries-publishing-lookups.txt for more information.
52
53-canUploadToPocket method helps us to decide if an upload is allowed or
54-not, according to the distroseries status and the upload target pocket.
55+canModifySuite method helps us to decide if an upload is allowed or not,
56+according to the distroseries status and the upload target pocket.
57
58 >>> ubuntu = getUtility(IDistributionSet)['ubuntu']
59+ >>> archive = ubuntu.main_archive
60 >>> breezy_autotest = ubuntu['breezy-autotest']
61 >>> hoary = ubuntu['hoary']
62
63@@ -178,16 +179,18 @@
64
65 >>> warty.status.name
66 'CURRENT'
67- >>> warty.canUploadToPocket(PackagePublishingPocket.RELEASE)
68+ >>> archive.canModifySuite(warty, PackagePublishingPocket.RELEASE)
69 False
70- >>> warty.canUploadToPocket(PackagePublishingPocket.SECURITY)
71+ >>> archive.canModifySuite(warty, PackagePublishingPocket.SECURITY)
72 True
73
74 >>> breezy_autotest.status.name
75 'EXPERIMENTAL'
76- >>> breezy_autotest.canUploadToPocket(PackagePublishingPocket.RELEASE)
77+ >>> archive.canModifySuite(
78+ ... breezy_autotest, PackagePublishingPocket.RELEASE)
79 True
80- >>> breezy_autotest.canUploadToPocket(PackagePublishingPocket.SECURITY)
81+ >>> archive.canModifySuite(
82+ ... breezy_autotest, PackagePublishingPocket.SECURITY)
83 False
84
85 The FROZEN status is special. Uploads are allowed for all pockets as
86@@ -198,20 +201,21 @@
87
88 >>> hoary.status.name
89 'FROZEN'
90- >>> hoary.canUploadToPocket(PackagePublishingPocket.RELEASE)
91+ >>> archive.canModifySuite(hoary, PackagePublishingPocket.RELEASE)
92 True
93- >>> hoary.canUploadToPocket(PackagePublishingPocket.SECURITY)
94+ >>> archive.canModifySuite(hoary, PackagePublishingPocket.SECURITY)
95 True
96
97 The PROPOSED pocket is also special. Pre-release, it may be used for
98 staging uploads on their way into the RELEASE pocket; post-release, it may
99 be used for staging uploads on their way into the UPDATES pocket.
100
101- >>> warty.canUploadToPocket(PackagePublishingPocket.PROPOSED)
102- True
103- >>> breezy_autotest.canUploadToPocket(PackagePublishingPocket.PROPOSED)
104- True
105- >>> hoary.canUploadToPocket(PackagePublishingPocket.PROPOSED)
106+ >>> archive.canModifySuite(warty, PackagePublishingPocket.PROPOSED)
107+ True
108+ >>> archive.canModifySuite(
109+ ... breezy_autotest, PackagePublishingPocket.PROPOSED)
110+ True
111+ >>> archive.canModifySuite(hoary, PackagePublishingPocket.PROPOSED)
112 True
113
114 Package searching
115@@ -517,8 +521,12 @@
116 ------------------------------------------------
117
118 >>> from lp.registry.interfaces.sourcepackage import ISourcePackage
119- >>> from lp.soyuz.interfaces.distroseriesbinarypackage import IDistroSeriesBinaryPackage
120- >>> from lp.soyuz.interfaces.distroseriessourcepackagerelease import IDistroSeriesSourcePackageRelease
121+ >>> from lp.soyuz.interfaces.distroseriesbinarypackage import (
122+ ... IDistroSeriesBinaryPackage,
123+ ... )
124+ >>> from lp.soyuz.interfaces.distroseriessourcepackagerelease import (
125+ ... IDistroSeriesSourcePackageRelease,
126+ ... )
127
128 >>> pmount_src_name = SourcePackageName.byName('pmount')
129 >>> pmount_source = hoary.getSourcePackage(pmount_src_name)
130@@ -722,7 +730,9 @@
131 4
132 >>> warty_mozilla_pub_bin = warty_mozilla_pub_binaries[0]
133
134- >>> from lp.soyuz.interfaces.publishing import IBinaryPackagePublishingHistory
135+ >>> from lp.soyuz.interfaces.publishing import (
136+ ... IBinaryPackagePublishingHistory,
137+ ... )
138 >>> verifyObject(IBinaryPackagePublishingHistory, warty_mozilla_pub_bin)
139 True
140
141
142=== modified file 'lib/lp/registry/interfaces/distroseries.py'
143--- lib/lp/registry/interfaces/distroseries.py 2012-01-10 09:55:24 +0000
144+++ lib/lp/registry/interfaces/distroseries.py 2012-06-19 10:11:30 +0000
145@@ -402,26 +402,6 @@
146 development moves on to the other pockets.
147 """
148
149- def canUploadToPocket(pocket):
150- """Decides whether or not allow uploads for a given pocket.
151-
152- Only allow uploads for RELEASE pocket in unreleased
153- distroseries and the opposite, only allow uploads for
154- non-RELEASE pockets in released distroseries.
155- For instance, in edgy time :
156-
157- warty -> DENY
158- edgy -> ALLOW
159- warty-updates -> ALLOW
160- edgy-security -> DENY
161-
162- Note that FROZEN is not considered either 'stable' or 'unstable'
163- state. Uploads to a FROZEN distroseries will end up in the
164- UNAPPROVED queue.
165-
166- Return True if the upload is allowed and False if denied.
167- """
168-
169 def getLatestUploads():
170 """Return the latest five source uploads for this DistroSeries.
171
172
173=== modified file 'lib/lp/registry/model/distroseries.py'
174--- lib/lp/registry/model/distroseries.py 2012-05-14 03:07:32 +0000
175+++ lib/lp/registry/model/distroseries.py 2012-06-19 10:11:30 +0000
176@@ -709,33 +709,6 @@
177 """See `HasMilestonesMixin`."""
178 return (Milestone.distroseries == self)
179
180- def canUploadToPocket(self, pocket):
181- """See `IDistroSeries`."""
182- # Allow everything for distroseries in FROZEN state.
183- if self.status == SeriesStatus.FROZEN:
184- return True
185-
186- # Define stable/released states.
187- stable_states = (SeriesStatus.SUPPORTED,
188- SeriesStatus.CURRENT)
189-
190- # Deny uploads for RELEASE pocket in stable states.
191- if (pocket == PackagePublishingPocket.RELEASE and
192- self.status in stable_states):
193- return False
194-
195- # Deny uploads for post-release-only pockets in unstable states.
196- pre_release_pockets = (
197- PackagePublishingPocket.RELEASE,
198- PackagePublishingPocket.PROPOSED,
199- )
200- if (pocket not in pre_release_pockets and
201- self.status not in stable_states):
202- return False
203-
204- # Allow anything else.
205- return True
206-
207 def updatePackageCount(self):
208 """See `IDistroSeries`."""
209 self.sourcecount = IStore(SourcePackagePublishingHistory).find(
210@@ -1610,34 +1583,12 @@
211 if is_careful:
212 return True
213
214- # PPA and PARTNER allow everything.
215- if publication.archive.allowUpdatesToReleasePocket():
216- return True
217-
218- # FROZEN state also allow all pockets to be published.
219- if self.status == SeriesStatus.FROZEN:
220- return True
221-
222- # If we're not republishing, we want to make sure that
223- # we're not publishing packages into the wrong pocket.
224- # Unfortunately for careful mode that can't hold true
225- # because we indeed need to republish everything.
226- pre_release_pockets = (
227- PackagePublishingPocket.RELEASE,
228- PackagePublishingPocket.PROPOSED,
229- )
230- if self.isUnstable() and publication.pocket not in pre_release_pockets:
231- log.error("Tried to publish %s (%s) into a non-release "
232- "pocket on unstable series %s, skipping"
233- % (publication.displayname, publication.id,
234- self.displayname))
235- return False
236- if (not self.isUnstable() and
237- publication.pocket == PackagePublishingPocket.RELEASE):
238- log.error("Tried to publish %s (%s) into release pocket "
239- "on stable series %s, skipping"
240- % (publication.displayname, publication.id,
241- self.displayname))
242+ if not publication.archive.canModifySuite(self, publication.pocket):
243+ log.error(
244+ "Tried to publish %s (%s) into the %s pocket on series %s "
245+ "(%s), skipping" % (
246+ publication.displayname, publication.id,
247+ publication.pocket, self.displayname, self.status.name))
248 return False
249
250 return True
251
252=== modified file 'lib/lp/soyuz/adapters/copypolicy.py'
253--- lib/lp/soyuz/adapters/copypolicy.py 2012-03-28 13:27:58 +0000
254+++ lib/lp/soyuz/adapters/copypolicy.py 2012-06-19 10:11:30 +0000
255@@ -45,8 +45,7 @@
256 # If the pocket is RELEASE or PROPOSED and we're not frozen then you
257 # can upload to it. Any other states mean the upload is unapproved.
258 #
259- # This check is orthogonal to the
260- # IDistroSeries.canUploadToPocket check.
261+ # This check is orthogonal to the IArchive.canModifySuite check.
262 auto_approve_pockets = (
263 PackagePublishingPocket.RELEASE,
264 PackagePublishingPocket.PROPOSED,
265
266=== modified file 'lib/lp/soyuz/doc/buildd-queuebuilder-lookup.txt'
267--- lib/lp/soyuz/doc/buildd-queuebuilder-lookup.txt 2011-12-30 06:14:56 +0000
268+++ lib/lp/soyuz/doc/buildd-queuebuilder-lookup.txt 2012-06-19 10:11:30 +0000
269@@ -174,9 +174,9 @@
270
271 Note that, also the publications targeted to post-release pockets are
272 returned, but they won't be dispatched until they can be accepted (see
273-IDistroSeries.canUploadToPocket). This nuance will be more explored
274-when fixing #67790 when we will start publishing and building
275-post-release pocket in FROZEN state.
276+IArchive.canModifySuite). This nuance will be more explored when fixing
277+#67790 when we will start publishing and building post-release pocket in
278+FROZEN state.
279
280 Summing up, all the 8 test publications will be returned from the lookup:
281
282
283=== modified file 'lib/lp/soyuz/doc/publishing.txt'
284--- lib/lp/soyuz/doc/publishing.txt 2011-12-30 06:14:56 +0000
285+++ lib/lp/soyuz/doc/publishing.txt 2012-06-19 10:11:30 +0000
286@@ -475,7 +475,9 @@
287
288 'distroseries' and 'archive' will be constant.
289
290+ >>> from lp.registry.interfaces.series import SeriesStatus
291 >>> distroseries = source.distroseries
292+ >>> distroseries.status = SeriesStatus.CURRENT
293 >>> archive = source.archive
294
295 'pocket' will be UPDATES.
296
297=== modified file 'lib/lp/soyuz/interfaces/archive.py'
298--- lib/lp/soyuz/interfaces/archive.py 2012-06-12 04:05:44 +0000
299+++ lib/lp/soyuz/interfaces/archive.py 2012-06-19 10:11:30 +0000
300@@ -576,6 +576,28 @@
301 :return: True if they can, False if they cannot.
302 """
303
304+ def canModifySuite(distroseries, pocket):
305+ """Decides whether or not to allow uploads for a given DS/pocket.
306+
307+ Some archive types (e.g. PPAs) allow uploads to the RELEASE pocket
308+ regardless of the distroseries state. For others (principally
309+ primary archives), only allow uploads for RELEASE pocket in
310+ unreleased distroseries, and conversely only allow uploads for
311+ non-RELEASE pockets in released distroseries.
312+ For instance, in edgy time :
313+
314+ warty -> DENY
315+ edgy -> ALLOW
316+ warty-updates -> ALLOW
317+ edgy-security -> DENY
318+
319+ Note that FROZEN is not considered either 'stable' or 'unstable'
320+ state. Uploads to a FROZEN distroseries will end up in the
321+ UNAPPROVED queue.
322+
323+ Return True if the upload is allowed and False if denied.
324+ """
325+
326 def checkUploadToPocket(distroseries, pocket):
327 """Check if an upload to a particular archive and pocket is possible.
328
329
330=== modified file 'lib/lp/soyuz/model/archive.py'
331--- lib/lp/soyuz/model/archive.py 2012-06-15 10:21:34 +0000
332+++ lib/lp/soyuz/model/archive.py 2012-06-19 10:11:30 +0000
333@@ -67,6 +67,7 @@
334 )
335 from lp.registry.interfaces.pocket import PackagePublishingPocket
336 from lp.registry.interfaces.role import IHasOwner
337+from lp.registry.interfaces.series import SeriesStatus
338 from lp.registry.interfaces.sourcepackagename import ISourcePackageNameSet
339 from lp.registry.model.sourcepackagename import SourcePackageName
340 from lp.registry.model.teammembership import TeamParticipation
341@@ -1227,6 +1228,37 @@
342 strict_component=True)
343 return reason is None
344
345+ def canModifySuite(self, distroseries, pocket):
346+ """See `IArchive`."""
347+ # PPA and PARTNER allow everything.
348+ if self.allowUpdatesToReleasePocket():
349+ return True
350+
351+ # Allow everything for distroseries in FROZEN state.
352+ if distroseries.status == SeriesStatus.FROZEN:
353+ return True
354+
355+ # Define stable/released states.
356+ stable_states = (SeriesStatus.SUPPORTED,
357+ SeriesStatus.CURRENT)
358+
359+ # Deny uploads for RELEASE pocket in stable states.
360+ if (pocket == PackagePublishingPocket.RELEASE and
361+ distroseries.status in stable_states):
362+ return False
363+
364+ # Deny uploads for post-release-only pockets in unstable states.
365+ pre_release_pockets = (
366+ PackagePublishingPocket.RELEASE,
367+ PackagePublishingPocket.PROPOSED,
368+ )
369+ if (pocket not in pre_release_pockets and
370+ distroseries.status not in stable_states):
371+ return False
372+
373+ # Allow anything else.
374+ return True
375+
376 def checkUploadToPocket(self, distroseries, pocket):
377 """See `IArchive`."""
378 if self.is_partner:
379@@ -1247,7 +1279,7 @@
380 # state.
381 # XXX julian 2005-05-29 bug=117557:
382 # This is a greasy hack until bug #117557 is fixed.
383- if not distroseries.canUploadToPocket(pocket):
384+ if not self.canModifySuite(distroseries, pocket):
385 return CannotUploadToPocket(distroseries, pocket)
386
387 def _checkUpload(self, person, distroseries, sourcepackagename, component,
388
389=== modified file 'lib/lp/soyuz/model/binarypackagebuild.py'
390--- lib/lp/soyuz/model/binarypackagebuild.py 2012-02-13 04:20:08 +0000
391+++ lib/lp/soyuz/model/binarypackagebuild.py 2012-06-19 10:11:30 +0000
392@@ -1,4 +1,4 @@
393-# Copyright 2009-2011 Canonical Ltd. This software is licensed under the
394+# Copyright 2009-2012 Canonical Ltd. This software is licensed under the
395 # GNU Affero General Public License version 3 (see the file LICENSE).
396
397 # pylint: disable-msg=E0611,W0212
398@@ -327,9 +327,8 @@
399 def can_be_retried(self):
400 """See `IBuild`."""
401 # First check that the slave scanner would pick up the build record
402- # if we reset it. PPA and Partner builds are always ok.
403- if (self.archive.purpose == ArchivePurpose.PRIMARY and
404- not self.distro_series.canUploadToPocket(self.pocket)):
405+ # if we reset it.
406+ if not self.archive.canModifySuite(self.distro_series, self.pocket):
407 # The slave scanner would not pick this up, so it cannot be
408 # re-tried.
409 return False
410
411=== modified file 'lib/lp/soyuz/model/publishing.py'
412--- lib/lp/soyuz/model/publishing.py 2012-05-21 07:34:15 +0000
413+++ lib/lp/soyuz/model/publishing.py 2012-06-19 10:11:30 +0000
414@@ -787,17 +787,13 @@
415 raise AssertionError("changeOverride must be passed either a"
416 " new component or new section")
417
418- # Retrieve current publishing info
419- current = self
420-
421 # Check there is a change to make
422 if new_component is None:
423- new_component = current.component
424+ new_component = self.component
425 if new_section is None:
426- new_section = current.section
427+ new_section = self.section
428
429- if (new_component == current.component and
430- new_section == current.section):
431+ if new_component == self.component and new_section == self.section:
432 return
433
434 # See if the archive has changed by virtue of the component
435@@ -805,18 +801,25 @@
436 distribution = self.distroseries.distribution
437 new_archive = distribution.getArchiveByComponent(
438 new_component.name)
439- if new_archive != None and new_archive != current.archive:
440+ if new_archive != None and new_archive != self.archive:
441 raise ArchiveOverriderError(
442 "Overriding component to '%s' failed because it would "
443 "require a new archive." % new_component.name)
444
445+ # Refuse to create new publication records that will never be
446+ # published.
447+ if not self.archive.canModifySuite(self.distroseries, self.pocket):
448+ raise ArchiveOverriderError(
449+ "Cannot change overrides in suite '%s'" %
450+ self.distroseries.getSuite(self.pocket))
451+
452 return getUtility(IPublishingSet).newSourcePublication(
453- distroseries=current.distroseries,
454- sourcepackagerelease=current.sourcepackagerelease,
455- pocket=current.pocket,
456+ distroseries=self.distroseries,
457+ sourcepackagerelease=self.sourcepackagerelease,
458+ pocket=self.pocket,
459 component=new_component,
460 section=new_section,
461- archive=current.archive)
462+ archive=self.archive)
463
464 def copyTo(self, distroseries, pocket, archive, override=None,
465 create_dsd_job=True, creator=None, sponsor=None,
466@@ -1202,20 +1205,17 @@
467 raise AssertionError("changeOverride must be passed a new"
468 "component, section and/or priority.")
469
470- # Retrieve current publishing info
471- current = self
472-
473 # Check there is a change to make
474 if new_component is None:
475- new_component = current.component
476+ new_component = self.component
477 if new_section is None:
478- new_section = current.section
479+ new_section = self.section
480 if new_priority is None:
481- new_priority = current.priority
482+ new_priority = self.priority
483
484- if (new_component == current.component and
485- new_section == current.section and
486- new_priority == current.priority):
487+ if (new_component == self.component and
488+ new_section == self.section and
489+ new_priority == self.priority):
490 return
491
492 # See if the archive has changed by virtue of the component changing:
493@@ -1227,6 +1227,13 @@
494 "Overriding component to '%s' failed because it would "
495 "require a new archive." % new_component.name)
496
497+ # Refuse to create new publication records that will never be
498+ # published.
499+ if not self.archive.canModifySuite(self.distroseries, self.pocket):
500+ raise ArchiveOverriderError(
501+ "Cannot change overrides in suite '%s'" %
502+ self.distroseries.getSuite(self.pocket))
503+
504 # Append the modified package publishing entry
505 return BinaryPackagePublishingHistory(
506 binarypackagename=self.binarypackagerelease.binarypackagename,
507@@ -1234,11 +1241,11 @@
508 distroarchseries=self.distroarchseries,
509 status=PackagePublishingStatus.PENDING,
510 datecreated=UTC_NOW,
511- pocket=current.pocket,
512+ pocket=self.pocket,
513 component=new_component,
514 section=new_section,
515 priority=new_priority,
516- archive=current.archive)
517+ archive=self.archive)
518
519 def copyTo(self, distroseries, pocket, archive):
520 """See `BinaryPackagePublishingHistory`."""
521
522=== modified file 'lib/lp/soyuz/model/queue.py'
523--- lib/lp/soyuz/model/queue.py 2012-05-25 15:31:50 +0000
524+++ lib/lp/soyuz/model/queue.py 2012-06-19 10:11:30 +0000
525@@ -277,11 +277,10 @@
526 """See `IPackageUpload`."""
527 # Explode if something wrong like warty/RELEASE pass through
528 # NascentUpload/UploadPolicies checks for 'ubuntu' main distro.
529- if not self.archive.allowUpdatesToReleasePocket():
530- assert self.distroseries.canUploadToPocket(self.pocket), (
531- "Not permitted acceptance in the %s pocket in a "
532- "series in the '%s' state." % (
533- self.pocket.name, self.distroseries.status.name))
534+ assert self.archive.canModifySuite(self.distroseries, self.pocket), (
535+ "Not permitted acceptance in the %s pocket in a "
536+ "series in the '%s' state." % (
537+ self.pocket.name, self.distroseries.status.name))
538
539 if self.status == PackageUploadStatus.ACCEPTED:
540 raise QueueInconsistentStateError(
541@@ -738,11 +737,10 @@
542 "Can not publish a non-ACCEPTED queue record (%s)" % self.id)
543 # Explode if something wrong like warty/RELEASE pass through
544 # NascentUpload/UploadPolicies checks
545- if not self.archive.allowUpdatesToReleasePocket():
546- assert self.distroseries.canUploadToPocket(self.pocket), (
547- "Not permitted to publish to the %s pocket in a "
548- "series in the '%s' state." % (
549- self.pocket.name, self.distroseries.status.name))
550+ assert self.archive.canModifySuite(self.distroseries, self.pocket), (
551+ "Not permitted to publish to the %s pocket in a "
552+ "series in the '%s' state." % (
553+ self.pocket.name, self.distroseries.status.name))
554
555 publishing_records = []
556 # In realising an upload we first load all the sources into
557
558=== modified file 'lib/lp/soyuz/scripts/tests/test_changeoverride.py'
559--- lib/lp/soyuz/scripts/tests/test_changeoverride.py 2012-01-01 02:58:52 +0000
560+++ lib/lp/soyuz/scripts/tests/test_changeoverride.py 2012-06-19 10:11:30 +0000
561@@ -12,6 +12,7 @@
562 from lp.registry.interfaces.distribution import IDistributionSet
563 from lp.registry.interfaces.person import IPersonSet
564 from lp.registry.interfaces.pocket import PackagePublishingPocket
565+from lp.registry.interfaces.series import SeriesStatus
566 from lp.services.librarian.interfaces import ILibraryFileAliasSet
567 from lp.services.log.logger import BufferLogger
568 from lp.soyuz.enums import PackagePublishingPriority
569@@ -263,6 +264,7 @@
570 This test checks the expected behaviour for each of them.
571 """
572 self._setupOverridePublishingContext()
573+ self.warty.status = SeriesStatus.DEVELOPMENT
574
575 changer = self.getChanger(
576 component="universe", section="web", priority='extra')
577@@ -364,6 +366,7 @@
578 This behaviour is inherited from `SoyuzScript`.
579 """
580 self._setupOverridePublishingContext()
581+ self.warty.status = SeriesStatus.DEVELOPMENT
582 changer = self.getChanger(
583 component="universe", section="web", priority='extra',
584 package_version='0.9')
585@@ -398,6 +401,7 @@
586 This behaviour is inherited from `SoyuzScript`.
587 """
588 self._setupOverridePublishingContext()
589+ self.warty.status = SeriesStatus.DEVELOPMENT
590 changer = self.getChanger(
591 component="universe", section="web", priority='extra',
592 arch_tag='i386')
593
594=== modified file 'lib/lp/soyuz/tests/test_publishing.py'
595--- lib/lp/soyuz/tests/test_publishing.py 2012-01-20 15:42:44 +0000
596+++ lib/lp/soyuz/tests/test_publishing.py 2012-06-19 10:11:30 +0000
597@@ -1,4 +1,4 @@
598-# Copyright 2009-2011 Canonical Ltd. This software is licensed under the
599+# Copyright 2009-2012 Canonical Ltd. This software is licensed under the
600 # GNU Affero General Public License version 3 (see the file LICENSE).
601
602 """Test native publication workflow for Soyuz. """
603@@ -24,6 +24,7 @@
604 from lp.registry.interfaces.distribution import IDistributionSet
605 from lp.registry.interfaces.person import IPersonSet
606 from lp.registry.interfaces.pocket import PackagePublishingPocket
607+from lp.registry.interfaces.series import SeriesStatus
608 from lp.registry.interfaces.sourcepackage import SourcePackageUrgency
609 from lp.registry.interfaces.sourcepackagename import ISourcePackageNameSet
610 from lp.services.config import config
611@@ -58,6 +59,7 @@
612 BinaryPackagePublishingHistory,
613 SourcePackagePublishingHistory,
614 )
615+from lp.soyuz.scripts.changeoverride import ArchiveOverriderError
616 from lp.testing import (
617 StormStatementRecorder,
618 TestCaseWithFactory,
619@@ -1645,3 +1647,46 @@
620 # archive too.
621 self.assertContentEqual(
622 [], getUtility(IPublishingSet).publishBinaries(**args))
623+
624+
625+class TestChangeOverride(TestNativePublishingBase):
626+ """Test that changing overrides works."""
627+
628+ def setUpOverride(self, status, pocket=PackagePublishingPocket.RELEASE,
629+ binary=False):
630+ self.distroseries.status = status
631+ if binary:
632+ pub = self.getPubBinaries(pocket=pocket)[0]
633+ else:
634+ pub = self.getPubSource(pocket=pocket)
635+ universe = getUtility(IComponentSet)["universe"]
636+ return pub.changeOverride(new_component=universe)
637+
638+ def assertCanOverride(self, *args, **kwargs):
639+ new_pub = self.setUpOverride(*args, **kwargs)
640+ self.assertEqual("universe", new_pub.component.name)
641+
642+ def assertCannotOverride(self, *args, **kwargs):
643+ self.assertRaises(
644+ ArchiveOverriderError, self.setUpOverride, *args, **kwargs)
645+
646+ def test_changeOverride_forbids_stable_RELEASE(self):
647+ # changeOverride is not allowed in the RELEASE pocket of a stable
648+ # distroseries.
649+ self.assertCannotOverride(SeriesStatus.CURRENT)
650+ self.assertCannotOverride(SeriesStatus.CURRENT, binary=True)
651+
652+ def test_changeOverride_allows_development_RELEASE(self):
653+ # changeOverride is allowed in the RELEASE pocket of a development
654+ # distroseries.
655+ self.assertCanOverride(SeriesStatus.DEVELOPMENT)
656+ self.assertCanOverride(SeriesStatus.DEVELOPMENT, binary=True)
657+
658+ def test_changeOverride_allows_stable_PROPOSED(self):
659+ # changeOverride is allowed in the PROPOSED pocket of a stable
660+ # distroseries.
661+ self.assertCanOverride(
662+ SeriesStatus.CURRENT, pocket=PackagePublishingPocket.PROPOSED)
663+ self.assertCanOverride(
664+ SeriesStatus.CURRENT, pocket=PackagePublishingPocket.PROPOSED,
665+ binary=True)