Merge lp:~cjwatson/launchpad/copy-set-phase into lp:launchpad

Proposed by Colin Watson
Status: Merged
Approved by: Colin Watson
Approved revision: no longer in the source branch.
Merged at revision: 16697
Proposed branch: lp:~cjwatson/launchpad/copy-set-phase
Merge into: lp:launchpad
Diff against target: 721 lines (+191/-53)
17 files modified
lib/lp/archiveuploader/tests/nascentupload-ddebs.txt (+5/-3)
lib/lp/soyuz/adapters/overrides.py (+25/-6)
lib/lp/soyuz/adapters/tests/test_overrides.py (+46/-5)
lib/lp/soyuz/interfaces/archive.py (+11/-2)
lib/lp/soyuz/interfaces/packagecopyjob.py (+8/-2)
lib/lp/soyuz/interfaces/publishing.py (+1/-1)
lib/lp/soyuz/model/archive.py (+14/-4)
lib/lp/soyuz/model/packagecopyjob.py (+10/-3)
lib/lp/soyuz/model/publishing.py (+16/-11)
lib/lp/soyuz/model/queue.py (+2/-1)
lib/lp/soyuz/scripts/packagecopier.py (+12/-4)
lib/lp/soyuz/scripts/tests/test_copypackage.py (+21/-0)
lib/lp/soyuz/stories/soyuz/xx-person-packages.txt (+8/-5)
lib/lp/soyuz/tests/test_archive.py (+4/-2)
lib/lp/soyuz/tests/test_packagecopyjob.py (+3/-1)
lib/lp/soyuz/tests/test_publishing.py (+4/-2)
lib/lp/testing/factory.py (+1/-1)
To merge this branch: bzr merge lp:~cjwatson/launchpad/copy-set-phase
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+170775@code.launchpad.net

Commit message

Allow setting the phased-update-percentage for binary publications created by copies.

Description of the change

== Summary ==

Copying packages to the -updates pocket but gradually phasing them in using the phased_update_percentage mechanism effectively requires the PUP to be set to 0 after the copy. However, doing this with BPPH.changeOverride is problematic: it requires archive owner privileges rather than merely the ability to copy, and it's racy because you have to wait for the async copy to complete before you can change its overrides and you have to make sure you squeeze the override change in before the next publisher run. It would be better to be able to specify an initial PUP for the copied binaries. Bug 1192286 has more details.

== Proposed fix ==

Add a phased_update_percentage argument to Archive.copyPackage and propagate it all the way down. The only fiddly bit is choosing exactly how to pass it through PublishingSet.copyBinaries; I opted for making it a parameter you can tweak when creating an override policy, which the policy then remembers and applies as appropriate.

== LOC Rationale ==

+133. This is making an existing feature properly usable so I think it's reasonable; and I have a good track record at removing LoC from LP ...

== Tests ==

bin/test -vvct lp.soyuz.adapters.tests.test_overrides -t lp.soyuz.tests.test_archive -t lp.soyuz.tests.test_packagecopyjob -t lp.soyuz.scripts.tests.test_copypackage -t lp.soyuz.tests.test_publishing -t lp.soyuz.tests.test_packageupload

== Demo and Q/A ==

''Explain how to demonstrate the fix and how to carry out QA on it.''

On DF, pick two packages in PROPOSED pockets but not elsewhere and copy them to UPDATES, one with phased_update_percentage=0 and one with that argument unspecified. Run the publisher. The first should be published with "Phased-Update-Percentage: 0" in Packages, and the second should have that field missing.

To post a comment you must log in.
Revision history for this message
William Grant (wgrant) wrote :

Oops, this slipped under the radar. Looks fine to me, thanks.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/archiveuploader/tests/nascentupload-ddebs.txt'
2--- lib/lp/archiveuploader/tests/nascentupload-ddebs.txt 2013-05-24 03:05:34 +0000
3+++ lib/lp/archiveuploader/tests/nascentupload-ddebs.txt 2013-06-21 17:17:27 +0000
4@@ -81,6 +81,7 @@
5
6 We override the binary to main/devel, and accept it into the archive.
7
8+ >>> from operator import attrgetter
9 >>> from lp.soyuz.interfaces.component import IComponentSet
10 >>> from lp.soyuz.interfaces.section import ISectionSet
11 >>> main = getUtility(IComponentSet)['main']
12@@ -99,7 +100,8 @@
13
14 >>> switch_dbuser(config.uploadqueue.dbuser)
15
16- >>> bin_pubs = bin.queue_root.realiseUpload()
17+ >>> bin_pubs = sorted(
18+ ... bin.queue_root.realiseUpload(), key=attrgetter('displayname'))
19
20 >>> switch_dbuser('uploader')
21
22@@ -109,8 +111,8 @@
23 ... print '%s %s %s %s' % (
24 ... bin_pub.displayname, bin_pub.status.name,
25 ... bin_pub.component.name, bin_pub.section.name)
26+ debug-bin 1.0-1 in hoary i386 PENDING main devel
27 debug-bin-dbgsym 1.0-1 in hoary i386 PENDING main devel
28- debug-bin 1.0-1 in hoary i386 PENDING main devel
29
30 DEBs and DDEBs are uploaded to separate archives, because the size
31 impact of uploading them to a single archive on mirrors would be
32@@ -118,7 +120,7 @@
33
34 The DDEB is stored appropriately in the database.
35
36- >>> [ddeb_pub, deb_pub] = bin_pubs
37+ >>> [deb_pub, ddeb_pub] = bin_pubs
38 >>> ddeb = ddeb_pub.binarypackagerelease
39
40 >>> print ddeb.title
41
42=== modified file 'lib/lp/soyuz/adapters/overrides.py'
43--- lib/lp/soyuz/adapters/overrides.py 2013-06-20 05:50:00 +0000
44+++ lib/lp/soyuz/adapters/overrides.py 2013-06-21 17:17:27 +0000
45@@ -70,6 +70,8 @@
46 "The IDistroArchSeries for the publication")
47 priority = Attribute(
48 "The PackagePublishingPriority that's being overridden")
49+ phased_update_percentage = Attribute(
50+ "The phased update percentage that's being overridden")
51
52
53 class Override:
54@@ -109,11 +111,12 @@
55 implements(IBinaryOverride)
56
57 def __init__(self, binary_package_name, distro_arch_series, component,
58- section, priority):
59+ section, priority, phased_update_percentage):
60 super(BinaryOverride, self).__init__(component, section)
61 self.binary_package_name = binary_package_name
62 self.distro_arch_series = distro_arch_series
63 self.priority = priority
64+ self.phased_update_percentage = phased_update_percentage
65
66 def __eq__(self, other):
67 return (
68@@ -121,13 +124,16 @@
69 self.distro_arch_series == other.distro_arch_series and
70 self.component == other.component and
71 self.section == other.section and
72- self.priority == other.priority)
73+ self.priority == other.priority and
74+ self.phased_update_percentage == other.phased_update_percentage)
75
76 def __repr__(self):
77 return ("<BinaryOverride at %x component=%r section=%r "
78- "binary_package_name=%r distro_arch_series=%r priority=%r>" %
79+ "binary_package_name=%r distro_arch_series=%r priority=%r "
80+ "phased_update_percentage=%r>" %
81 (id(self), self.component, self.section, self.binary_package_name,
82- self.distro_arch_series, self.priority))
83+ self.distro_arch_series, self.priority,
84+ self.phased_update_percentage))
85
86
87 class IOverridePolicy(Interface):
88@@ -140,6 +146,9 @@
89 keep the same component and section as their ancestor publications.
90 """
91
92+ phased_update_percentage = Attribute(
93+ "The phased update percentage to apply to binary publications.")
94+
95 def calculateSourceOverrides(archive, distroseries, pocket, sources,
96 source_component=None):
97 """Calculate source overrides.
98@@ -173,6 +182,10 @@
99
100 implements(IOverridePolicy)
101
102+ def __init__(self, phased_update_percentage=None):
103+ super(BaseOverridePolicy, self).__init__()
104+ self.phased_update_percentage = phased_update_percentage
105+
106 def calculateSourceOverrides(self, archive, distroseries, pocket,
107 sources, source_component=None):
108 raise NotImplementedError()
109@@ -245,6 +258,8 @@
110 for bpn, das in expanded if das is not None]
111 if len(candidates) == 0:
112 return []
113+ # Do not copy phased_update_percentage from existing publications;
114+ # it is too context-dependent to copy.
115 already_published = DecoratedResultSet(
116 store.find(
117 (BinaryPackagePublishingHistory.binarypackagenameID,
118@@ -269,7 +284,9 @@
119 None)),
120 pre_iter_hook=eager_load)
121 return [
122- BinaryOverride(name, das, component, section, priority)
123+ BinaryOverride(
124+ name, das, component, section, priority,
125+ self.phased_update_percentage)
126 for name, das, component, section, priority in already_published]
127
128
129@@ -324,7 +341,9 @@
130 default_component = archive.default_component or getUtility(
131 IComponentSet)['universe']
132 return [
133- BinaryOverride(binary, das, default_component, None, None)
134+ BinaryOverride(
135+ binary, das, default_component, None, None,
136+ self.phased_update_percentage)
137 for binary, das in calculate_target_das(distroseries, binaries)]
138
139
140
141=== modified file 'lib/lp/soyuz/adapters/tests/test_overrides.py'
142--- lib/lp/soyuz/adapters/tests/test_overrides.py 2013-05-23 07:06:42 +0000
143+++ lib/lp/soyuz/adapters/tests/test_overrides.py 2013-06-21 17:17:27 +0000
144@@ -1,4 +1,4 @@
145-# Copyright 2011 Canonical Ltd. This software is licensed under the
146+# Copyright 2011-2013 Canonical Ltd. This software is licensed under the
147 # GNU Affero General Public License version 3 (see the file LICENSE).
148
149 """Test generic override policy classes."""
150@@ -124,7 +124,7 @@
151 BinaryOverride(
152 bpph.binarypackagerelease.binarypackagename,
153 bpph.distroarchseries, bpph.component, bpph.section,
154- bpph.priority)]
155+ bpph.priority, None)]
156 self.assertEqual(expected, overrides)
157
158 def test_binary_overrides_constant_query_count(self):
159@@ -203,7 +203,7 @@
160 expected = [
161 BinaryOverride(
162 bpph.binarypackagerelease.binarypackagename,
163- bpph.distroarchseries, universe, None, None)]
164+ bpph.distroarchseries, universe, None, None, None)]
165 self.assertEqual(expected, overrides)
166
167 def test_ubuntu_override_policy_sources(self):
168@@ -259,13 +259,14 @@
169 expected.append(
170 BinaryOverride(
171 bpn, distroarchseries, bpph.component, bpph.section,
172- bpph.priority))
173+ bpph.priority, None))
174 for i in xrange(2):
175 distroarchseries = self.factory.makeDistroArchSeries(
176 distroseries=distroseries)
177 bpns.append((bpn, distroarchseries.architecturetag))
178 expected.append(
179- BinaryOverride(bpn, distroarchseries, universe, None, None))
180+ BinaryOverride(
181+ bpn, distroarchseries, universe, None, None, None))
182 distroseries.nominatedarchindep = distroarchseries
183 policy = UbuntuOverridePolicy()
184 overrides = policy.calculateBinaryOverrides(
185@@ -294,3 +295,43 @@
186 distroseries.main_archive, distroseries, pocket, ((bpn, 'i386'),))
187
188 self.assertEqual([], overrides)
189+
190+ def test_phased_update_percentage(self):
191+ # A policy with a phased_update_percentage applies it to new binary
192+ # overrides.
193+ universe = getUtility(IComponentSet)['universe']
194+ distroseries = self.factory.makeDistroSeries()
195+ pocket = self.factory.getAnyPocket()
196+ bpn = self.factory.makeBinaryPackageName()
197+ bpns = []
198+ expected = []
199+ distroarchseries = self.factory.makeDistroArchSeries(
200+ distroseries=distroseries)
201+ bpb = self.factory.makeBinaryPackageBuild(
202+ distroarchseries=distroarchseries)
203+ bpr = self.factory.makeBinaryPackageRelease(
204+ build=bpb, binarypackagename=bpn, architecturespecific=True)
205+ bpph = self.factory.makeBinaryPackagePublishingHistory(
206+ binarypackagerelease=bpr, distroarchseries=distroarchseries,
207+ archive=distroseries.main_archive, pocket=pocket)
208+ bpns.append((bpn, distroarchseries.architecturetag))
209+ expected.append(
210+ BinaryOverride(
211+ bpn, distroarchseries, bpph.component, bpph.section,
212+ bpph.priority, 50))
213+ distroarchseries = self.factory.makeDistroArchSeries(
214+ distroseries=distroseries)
215+ bpns.append((bpn, distroarchseries.architecturetag))
216+ expected.append(
217+ BinaryOverride(bpn, distroarchseries, universe, None, None, 50))
218+ distroseries.nominatedarchindep = distroarchseries
219+ policy = UbuntuOverridePolicy(phased_update_percentage=50)
220+ overrides = policy.calculateBinaryOverrides(
221+ distroseries.main_archive, distroseries, pocket, bpns)
222+ self.assertEqual(2, len(overrides))
223+ key = attrgetter("binary_package_name.name",
224+ "distro_arch_series.architecturetag",
225+ "component.name")
226+ sorted_expected = sorted(expected, key=key)
227+ sorted_overrides = sorted(overrides, key=key)
228+ self.assertEqual(sorted_expected, sorted_overrides)
229
230=== modified file 'lib/lp/soyuz/interfaces/archive.py'
231--- lib/lp/soyuz/interfaces/archive.py 2013-05-24 04:49:14 +0000
232+++ lib/lp/soyuz/interfaces/archive.py 2013-06-21 17:17:27 +0000
233@@ -1042,7 +1042,7 @@
234 def getPockets():
235 """Return iterable containing valid pocket names for this archive."""
236
237- def getOverridePolicy():
238+ def getOverridePolicy(phased_update_percentage=None):
239 """Returns an instantiated `IOverridePolicy` for the archive."""
240
241 buildd_secret = TextLine(
242@@ -1356,13 +1356,20 @@
243 from_pocket=TextLine(title=_("Source pocket name"), required=False),
244 from_series=TextLine(
245 title=_("Source distroseries name"), required=False),
246+ phased_update_percentage=Int(
247+ title=_("Phased update percentage"),
248+ description=_("The percentage of users for whom this package"
249+ " should be recommended, or None to publish the"
250+ " update for everyone."),
251+ required=False),
252 )
253 @export_write_operation()
254 @operation_for_version('devel')
255 def copyPackage(source_name, version, from_archive, to_pocket,
256 person, to_series=None, include_binaries=False,
257 sponsored=None, unembargo=False, auto_approve=False,
258- from_pocket=None, from_series=None):
259+ from_pocket=None, from_series=None,
260+ phased_update_percentage=None):
261 """Copy a single named source into this archive.
262
263 Asynchronously copy a specific version of a named source to the
264@@ -1393,6 +1400,8 @@
265 copy from any pocket with a matching version.
266 :param from_series: the source distroseries (as a string). If
267 omitted, copy from any series with a matching version.
268+ :param phased_update_percentage: the phased update percentage to
269+ apply to the copied publication.
270
271 :raises NoSuchSourcePackageName: if the source name is invalid
272 :raises PocketNotFound: if the pocket name is invalid
273
274=== modified file 'lib/lp/soyuz/interfaces/packagecopyjob.py'
275--- lib/lp/soyuz/interfaces/packagecopyjob.py 2012-10-23 12:36:50 +0000
276+++ lib/lp/soyuz/interfaces/packagecopyjob.py 2013-06-21 17:17:27 +0000
277@@ -1,4 +1,4 @@
278-# Copyright 2010-2012 Canonical Ltd. This software is licensed under the
279+# Copyright 2010-2013 Canonical Ltd. This software is licensed under the
280 # GNU Affero General Public License version 3 (see the file LICENSE).
281
282 __metaclass__ = type
283@@ -129,7 +129,8 @@
284 include_binaries=False, package_version=None,
285 copy_policy=PackageCopyPolicy.INSECURE, requester=None,
286 sponsored=None, unembargo=False, auto_approve=False,
287- source_distroseries=None, source_pocket=None):
288+ source_distroseries=None, source_pocket=None,
289+ phased_update_percentage=None):
290 """Create a new `IPlainPackageCopyJob`.
291
292 :param package_name: The name of the source package to copy.
293@@ -157,6 +158,8 @@
294 :param source_pocket: The pocket from which to copy the packages.
295 Must be a member of `PackagePublishingPocket`. If omitted, copy
296 from any pocket with a matching version.
297+ :param phased_update_percentage: The phased update percentage to
298+ apply to the copied publication.
299 """
300
301 def createMultiple(target_distroseries, copy_tasks, requester,
302@@ -242,6 +245,9 @@
303 title=_("Source package publishing pocket"), required=False,
304 readonly=True)
305
306+ phased_update_percentage = Int(
307+ title=_("Phased update percentage"), required=False, readonly=True)
308+
309 def addSourceOverride(override):
310 """Add an `ISourceOverride` to the metadata."""
311
312
313=== modified file 'lib/lp/soyuz/interfaces/publishing.py'
314--- lib/lp/soyuz/interfaces/publishing.py 2013-05-31 04:00:53 +0000
315+++ lib/lp/soyuz/interfaces/publishing.py 2013-06-21 17:17:27 +0000
316@@ -991,7 +991,7 @@
317 :param pocket: The target `PackagePublishingPocket`.
318 :param binaries: A dict mapping `BinaryPackageReleases` to their
319 desired overrides as (`Component`, `Section`,
320- `PackagePublishingPriority`) tuples.
321+ `PackagePublishingPriority`, `phased_update_percentage`) tuples.
322
323 :return: A list of new `IBinaryPackagePublishingHistory` records.
324 """
325
326=== modified file 'lib/lp/soyuz/model/archive.py'
327--- lib/lp/soyuz/model/archive.py 2013-06-20 05:50:00 +0000
328+++ lib/lp/soyuz/model/archive.py 2013-06-21 17:17:27 +0000
329@@ -1635,9 +1635,17 @@
330 def copyPackage(self, source_name, version, from_archive, to_pocket,
331 person, to_series=None, include_binaries=False,
332 sponsored=None, unembargo=False, auto_approve=False,
333- from_pocket=None, from_series=None):
334+ from_pocket=None, from_series=None,
335+ phased_update_percentage=None):
336 """See `IArchive`."""
337 # Asynchronously copy a package using the job system.
338+ if phased_update_percentage is not None:
339+ if phased_update_percentage < 0 or phased_update_percentage > 100:
340+ raise ValueError(
341+ "phased_update_percentage must be between 0 and 100 "
342+ "(inclusive).")
343+ elif phased_update_percentage == 100:
344+ phased_update_percentage = None
345 pocket = self._text_to_pocket(to_pocket)
346 series = self._text_to_series(to_series)
347 if from_pocket:
348@@ -1663,7 +1671,8 @@
349 copy_policy=PackageCopyPolicy.INSECURE, requester=person,
350 sponsored=sponsored, unembargo=unembargo,
351 auto_approve=auto_approve, source_distroseries=from_series,
352- source_pocket=from_pocket)
353+ source_pocket=from_pocket,
354+ phased_update_percentage=phased_update_percentage)
355
356 def copyPackages(self, source_names, from_archive, to_pocket,
357 person, to_series=None, from_series=None,
358@@ -1996,14 +2005,15 @@
359 # understandiung EnumItems.
360 return list(PackagePublishingPocket.items)
361
362- def getOverridePolicy(self):
363+ def getOverridePolicy(self, phased_update_percentage=None):
364 """See `IArchive`."""
365 # Circular imports.
366 from lp.soyuz.adapters.overrides import UbuntuOverridePolicy
367 # XXX StevenK: bug=785004 2011-05-19 Return PPAOverridePolicy() for
368 # a PPA that overrides the component/pocket to main/RELEASE.
369 if self.purpose in MAIN_ARCHIVE_PURPOSES:
370- return UbuntuOverridePolicy()
371+ return UbuntuOverridePolicy(
372+ phased_update_percentage=phased_update_percentage)
373 return None
374
375 def removeCopyNotification(self, job_id):
376
377=== modified file 'lib/lp/soyuz/model/packagecopyjob.py'
378--- lib/lp/soyuz/model/packagecopyjob.py 2013-06-20 05:50:00 +0000
379+++ lib/lp/soyuz/model/packagecopyjob.py 2013-06-21 17:17:27 +0000
380@@ -272,7 +272,7 @@
381 def _makeMetadata(cls, target_pocket, package_version,
382 include_binaries, sponsored=None, unembargo=False,
383 auto_approve=False, source_distroseries=None,
384- source_pocket=None):
385+ source_pocket=None, phased_update_percentage=None):
386 """Produce a metadata dict for this job."""
387 return {
388 'target_pocket': target_pocket.value,
389@@ -284,6 +284,7 @@
390 'source_distroseries':
391 source_distroseries.name if source_distroseries else None,
392 'source_pocket': source_pocket.value if source_pocket else None,
393+ 'phased_update_percentage': phased_update_percentage,
394 }
395
396 @classmethod
397@@ -292,13 +293,15 @@
398 include_binaries=False, package_version=None,
399 copy_policy=PackageCopyPolicy.INSECURE, requester=None,
400 sponsored=None, unembargo=False, auto_approve=False,
401- source_distroseries=None, source_pocket=None):
402+ source_distroseries=None, source_pocket=None,
403+ phased_update_percentage=None):
404 """See `IPlainPackageCopyJobSource`."""
405 assert package_version is not None, "No package version specified."
406 assert requester is not None, "No requester specified."
407 metadata = cls._makeMetadata(
408 target_pocket, package_version, include_binaries, sponsored,
409- unembargo, auto_approve, source_distroseries, source_pocket)
410+ unembargo, auto_approve, source_distroseries, source_pocket,
411+ phased_update_percentage)
412 job = PackageCopyJob(
413 job_type=cls.class_job_type,
414 source_archive=source_archive,
415@@ -451,6 +454,10 @@
416 return None
417 return PackagePublishingPocket.items[name]
418
419+ @property
420+ def phased_update_percentage(self):
421+ return self.metadata.get('phased_update_percentage')
422+
423 def _createPackageUpload(self, unapproved=False):
424 pu = self.target_distroseries.createQueueEntry(
425 pocket=self.target_pocket, archive=self.target_archive,
426
427=== modified file 'lib/lp/soyuz/model/publishing.py'
428--- lib/lp/soyuz/model/publishing.py 2013-06-21 02:49:33 +0000
429+++ lib/lp/soyuz/model/publishing.py 2013-06-21 17:17:27 +0000
430@@ -1445,20 +1445,18 @@
431 return []
432
433 BPPH = BinaryPackagePublishingHistory
434- # XXX cjwatson 2013-02-13: We do not currently set the
435- # phased_update_percentage here. However, it might be useful in
436- # future to be able to copy a package from PROPOSED to UPDATES and
437- # immediately set its phased_update_percentage to zero; this should
438- # perhaps be an option.
439 return bulk.create(
440 (BPPH.archive, BPPH.distroarchseries, BPPH.pocket,
441 BPPH.binarypackagerelease, BPPH.binarypackagename,
442- BPPH.component, BPPH.section, BPPH.priority, BPPH.status,
443- BPPH.datecreated),
444+ BPPH.component, BPPH.section, BPPH.priority,
445+ BPPH.phased_update_percentage, BPPH.status, BPPH.datecreated),
446 [(archive, das, pocket, bpr, bpr.binarypackagename,
447 get_component(archive, das.distroseries, component),
448- section, priority, PackagePublishingStatus.PENDING, UTC_NOW)
449- for (das, bpr, (component, section, priority)) in needed],
450+ section, priority, phased_update_percentage,
451+ PackagePublishingStatus.PENDING, UTC_NOW)
452+ for (das, bpr,
453+ (component, section, priority,
454+ phased_update_percentage)) in needed],
455 get_objects=True)
456
457 def copyBinaries(self, archive, distroseries, pocket, bpphs, policy=None):
458@@ -1500,7 +1498,14 @@
459 new_component = override.component or bpph.component
460 new_section = override.section or bpph.section
461 new_priority = override.priority or bpph.priority
462- calculated = (new_component, new_section, new_priority)
463+ # No "or bpph.phased_update_percentage" here; if the
464+ # override doesn't specify one then we leave it at None
465+ # (a.k.a. 100% of users).
466+ new_phased_update_percentage = (
467+ override.phased_update_percentage)
468+ calculated = (
469+ new_component, new_section, new_priority,
470+ new_phased_update_percentage)
471 with_overrides[bpph.binarypackagerelease] = calculated
472
473 # If there is a corresponding DDEB then give it our
474@@ -1514,7 +1519,7 @@
475 else:
476 with_overrides = dict(
477 (bpph.binarypackagerelease, (bpph.component, bpph.section,
478- bpph.priority)) for bpph in bpphs)
479+ bpph.priority, None)) for bpph in bpphs)
480 if not with_overrides:
481 return list()
482 return self.publishBinaries(
483
484=== modified file 'lib/lp/soyuz/model/queue.py'
485--- lib/lp/soyuz/model/queue.py 2013-06-20 05:50:00 +0000
486+++ lib/lp/soyuz/model/queue.py 2013-06-21 17:17:27 +0000
487@@ -1207,7 +1207,8 @@
488 binary.version,
489 'Specific' if binary.architecturespecific else 'Independent',
490 ))
491- bins[binary] = (binary.component, binary.section, binary.priority)
492+ bins[binary] = (
493+ binary.component, binary.section, binary.priority, None)
494 return getUtility(IPublishingSet).publishBinaries(
495 self.packageupload.archive, distroseries,
496 self.packageupload.pocket, bins)
497
498=== modified file 'lib/lp/soyuz/scripts/packagecopier.py'
499--- lib/lp/soyuz/scripts/packagecopier.py 2013-06-21 02:39:33 +0000
500+++ lib/lp/soyuz/scripts/packagecopier.py 2013-06-21 17:17:27 +0000
501@@ -506,7 +506,8 @@
502 person=None, check_permissions=True, overrides=None,
503 send_email=False, strict_binaries=True, close_bugs=True,
504 create_dsd_job=True, announce_from_person=None, sponsored=None,
505- packageupload=None, unembargo=False, logger=None):
506+ packageupload=None, unembargo=False, phased_update_percentage=None,
507+ logger=None):
508 """Perform the complete copy of the given sources incrementally.
509
510 Verifies if each copy can be performed using `CopyChecker` and
511@@ -553,6 +554,8 @@
512 :param unembargo: If True, allow copying restricted files from a private
513 archive to a public archive, and unrestrict their library files when
514 doing so.
515+ :param phased_update_percentage: The phased update percentage to apply
516+ to the copied publication.
517 :param logger: An optional logger.
518
519 :raise CannotCopy when one or more copies were not allowed. The error
520@@ -628,7 +631,8 @@
521 source, archive, destination_series, pocket, include_binaries,
522 override, close_bugs=close_bugs, create_dsd_job=create_dsd_job,
523 close_bugs_since_version=old_version, creator=creator,
524- sponsor=sponsor, packageupload=packageupload, logger=logger)
525+ sponsor=sponsor, packageupload=packageupload,
526+ phased_update_percentage=phased_update_percentage, logger=logger)
527 if send_email:
528 notify(
529 person, source.sourcepackagerelease, [], [], archive,
530@@ -655,7 +659,8 @@
531 def _do_direct_copy(source, archive, series, pocket, include_binaries,
532 override=None, close_bugs=True, create_dsd_job=True,
533 close_bugs_since_version=None, creator=None,
534- sponsor=None, packageupload=None, logger=None):
535+ sponsor=None, packageupload=None,
536+ phased_update_percentage=None, logger=None):
537 """Copy publishing records to another location.
538
539 Copy each item of the given list of `SourcePackagePublishingHistory`
540@@ -685,6 +690,8 @@
541 :param sponsor: the sponsor `IPerson`, if this copy is being sponsored.
542 :param packageupload: The `IPackageUpload` that caused this publication
543 to be created.
544+ :param phased_update_percentage: The phased update percentage to apply
545+ to the copied publication.
546 :param logger: An optional logger.
547
548 :return: a list of `ISourcePackagePublishingHistory` and
549@@ -703,7 +710,8 @@
550 version=source.sourcepackagerelease.version,
551 status=active_publishing_status,
552 distroseries=series, pocket=pocket)
553- policy = archive.getOverridePolicy()
554+ policy = archive.getOverridePolicy(
555+ phased_update_percentage=phased_update_percentage)
556 if source_in_destination.is_empty():
557 # If no manual overrides were specified and the archive has an
558 # override policy then use that policy to get overrides.
559
560=== modified file 'lib/lp/soyuz/scripts/tests/test_copypackage.py'
561--- lib/lp/soyuz/scripts/tests/test_copypackage.py 2013-05-29 07:24:13 +0000
562+++ lib/lp/soyuz/scripts/tests/test_copypackage.py 2013-06-21 17:17:27 +0000
563@@ -1380,6 +1380,27 @@
564 section=override.section)
565 self.assertThat(copied_source, matcher)
566
567+ def test_copy_with_phased_update_percentage(self):
568+ # Test the phased_update_percentage parameter for do_copy.
569+ nobby, archive, source = self._setup_archive(
570+ use_nobby=True, pocket=PackagePublishingPocket.PROPOSED)
571+ [bin_i386, bin_hppa] = self.test_publisher.getPubBinaries(
572+ pub_source=source, distroseries=nobby,
573+ pocket=PackagePublishingPocket.PROPOSED)
574+ transaction.commit()
575+ switch_dbuser('archivepublisher')
576+ [copied_source, copied_bin_i386, copied_bin_hppa] = do_copy(
577+ [source], archive, nobby, PackagePublishingPocket.UPDATES,
578+ include_binaries=True, check_permissions=False)
579+ self.assertIsNone(copied_bin_i386.phased_update_percentage)
580+ self.assertIsNone(copied_bin_hppa.phased_update_percentage)
581+ [copied_source, copied_bin_i386, copied_bin_hppa] = do_copy(
582+ [source], archive, nobby, PackagePublishingPocket.BACKPORTS,
583+ include_binaries=True, check_permissions=False,
584+ phased_update_percentage=50)
585+ self.assertEqual(50, copied_bin_i386.phased_update_percentage)
586+ self.assertEqual(50, copied_bin_hppa.phased_update_percentage)
587+
588 def test_copy_ppa_generates_notification(self):
589 # When a copy into a PPA is performed, a notification is sent.
590 nobby, archive, source = self._setup_archive()
591
592=== modified file 'lib/lp/soyuz/stories/soyuz/xx-person-packages.txt'
593--- lib/lp/soyuz/stories/soyuz/xx-person-packages.txt 2013-06-20 05:50:00 +0000
594+++ lib/lp/soyuz/stories/soyuz/xx-person-packages.txt 2013-06-21 17:17:27 +0000
595@@ -189,9 +189,11 @@
596 Create some new source packages, source1 and source2, both created by cprov
597 so that they appear in his +packages page.
598
599+ >>> from storm.expr import SQL
600 >>> from zope.component import getUtility
601 >>> from lp.registry.interfaces.distribution import IDistributionSet
602 >>> from lp.registry.interfaces.person import IPersonSet
603+ >>> from lp.services.database.constants import UTC_NOW
604 >>> from lp.soyuz.tests.test_publishing import SoyuzTestPublisher
605 >>> from lp.soyuz.enums import PackagePublishingStatus
606
607@@ -207,7 +209,8 @@
608 >>> source1 = test_publisher.getPubSource(
609 ... status=PackagePublishingStatus.PUBLISHED,
610 ... sourcename='source1',
611- ... archive=cprov.archive)
612+ ... archive=cprov.archive,
613+ ... date_uploaded=UTC_NOW - SQL("INTERVAL '1 second'"))
614 >>> source1.sourcepackagerelease.creator=cprov
615 >>> source1_mark = source1.copyTo(
616 ... source1.distroseries, source1.pocket, mark.archive)
617@@ -260,17 +263,17 @@
618 >>> nopriv_browser = setupBrowser(auth="Basic no-priv@canonical.com:test")
619 >>> nopriv_browser.open("http://launchpad.dev/~cprov/+related-packages")
620 >>> print_ppa_rows(nopriv_browser)
621+ source2 PPA named p3a for No Priv... Ubuntutest Breezy-autotest 666
622+ ...ago None
623 source1 PPA for Celso Providelo - Ubuntutest Breezy-autotest 666
624 ...ago None
625- source2 PPA named p3a for No Priv... Ubuntutest Breezy-autotest 666
626- ...ago None
627
628 >>> admin_browser.open("http://launchpad.dev/~cprov/+related-packages")
629 >>> print_ppa_rows(admin_browser)
630+ source2 PPA named p3a for No Priv... Ubuntutest Breezy-autotest 666
631+ ...ago None
632 source1 PPA for Celso Providelo - Ubuntutest Breezy-autotest 666
633 ...ago None
634- source2 PPA named p3a for No Priv... Ubuntutest Breezy-autotest 666
635- ...ago None
636
637 Let's move the publication of source1 from mark's public archive to his
638 private one and the view the page again.
639
640=== modified file 'lib/lp/soyuz/tests/test_archive.py'
641--- lib/lp/soyuz/tests/test_archive.py 2013-06-20 05:50:00 +0000
642+++ lib/lp/soyuz/tests/test_archive.py 2013-06-21 17:17:27 +0000
643@@ -2154,7 +2154,8 @@
644 target_archive.copyPackage(
645 source_name, version, source_archive, to_pocket.name,
646 to_series=to_series.name, include_binaries=False,
647- person=target_archive.owner, sponsored=sponsored)
648+ person=target_archive.owner, sponsored=sponsored,
649+ phased_update_percentage=30)
650
651 # The source should not be published yet in the target_archive.
652 published = target_archive.getPublishedSources(
653@@ -2175,7 +2176,8 @@
654 target_pocket=to_pocket,
655 include_binaries=False,
656 sponsored=sponsored,
657- copy_policy=PackageCopyPolicy.INSECURE))
658+ copy_policy=PackageCopyPolicy.INSECURE,
659+ phased_update_percentage=30))
660
661 def test_copyPackage_disallows_non_primary_archive_uploaders(self):
662 # If copying to a primary archive and you're not an uploader for
663
664=== modified file 'lib/lp/soyuz/tests/test_packagecopyjob.py'
665--- lib/lp/soyuz/tests/test_packagecopyjob.py 2013-06-20 05:50:00 +0000
666+++ lib/lp/soyuz/tests/test_packagecopyjob.py 2013-06-21 17:17:27 +0000
667@@ -206,7 +206,8 @@
668 target_pocket=PackagePublishingPocket.RELEASE,
669 package_version="1.0-1", include_binaries=False,
670 copy_policy=PackageCopyPolicy.MASS_SYNC,
671- requester=requester, sponsored=sponsored)
672+ requester=requester, sponsored=sponsored,
673+ phased_update_percentage=20)
674 self.assertProvides(job, IPackageCopyJob)
675 self.assertEqual(archive1.id, job.source_archive_id)
676 self.assertEqual(archive1, job.source_archive)
677@@ -220,6 +221,7 @@
678 self.assertEqual(PackageCopyPolicy.MASS_SYNC, job.copy_policy)
679 self.assertEqual(requester, job.requester)
680 self.assertEqual(sponsored, job.sponsored)
681+ self.assertEqual(20, job.phased_update_percentage)
682
683 def test_createMultiple_creates_one_job_per_copy(self):
684 mother = self.factory.makeDistroSeriesParent()
685
686=== modified file 'lib/lp/soyuz/tests/test_publishing.py'
687--- lib/lp/soyuz/tests/test_publishing.py 2013-05-17 02:40:30 +0000
688+++ lib/lp/soyuz/tests/test_publishing.py 2013-06-21 17:17:27 +0000
689@@ -1422,7 +1422,7 @@
690 'binaries': dict(
691 (bpr, (self.factory.makeComponent(),
692 self.factory.makeSection(),
693- PackagePublishingPriority.REQUIRED)) for bpr in bprs),
694+ PackagePublishingPriority.REQUIRED, 50)) for bpr in bprs),
695 }
696
697 def test_architecture_dependent(self):
698@@ -1445,7 +1445,9 @@
699 (args['archive'], target_das, args['pocket']),
700 (bpph.archive, bpph.distroarchseries, bpph.pocket))
701 self.assertEqual(
702- overrides, (bpph.component, bpph.section, bpph.priority))
703+ overrides,
704+ (bpph.component, bpph.section, bpph.priority,
705+ bpph.phased_update_percentage))
706 self.assertEqual(PackagePublishingStatus.PENDING, bpph.status)
707
708 def test_architecture_independent(self):
709
710=== modified file 'lib/lp/testing/factory.py'
711--- lib/lp/testing/factory.py 2013-06-20 05:50:00 +0000
712+++ lib/lp/testing/factory.py 2013-06-21 17:17:27 +0000
713@@ -3809,7 +3809,7 @@
714 archive, distroarchseries.distroseries, pocket,
715 {binarypackagerelease: (
716 binarypackagerelease.component, binarypackagerelease.section,
717- priority)})
718+ priority, None)})
719 for bpph in bpphs:
720 naked_bpph = removeSecurityProxy(bpph)
721 naked_bpph.status = status