Merge lp:~cjwatson/launchpad/queue-api-refactor-overrides into lp:launchpad

Proposed by Colin Watson
Status: Merged
Approved by: Colin Watson
Approved revision: no longer in the source branch.
Merged at revision: 15545
Proposed branch: lp:~cjwatson/launchpad/queue-api-refactor-overrides
Merge into: lp:launchpad
Diff against target: 330 lines (+135/-48)
6 files modified
lib/lp/archiveuploader/tests/nascentupload-ddebs.txt (+2/-1)
lib/lp/soyuz/browser/queue.py (+7/-3)
lib/lp/soyuz/doc/distroseriesqueue.txt (+8/-12)
lib/lp/soyuz/interfaces/archive.py (+13/-1)
lib/lp/soyuz/interfaces/queue.py (+10/-13)
lib/lp/soyuz/model/queue.py (+95/-18)
To merge this branch: bzr merge lp:~cjwatson/launchpad/queue-api-refactor-overrides
Reviewer Review Type Date Requested Status
Benji York (community) code Approve
Review via email: mp+113074@code.launchpad.net

Commit message

Refactor PackageUpload.overrideSource and PackageUpload.overrideBinaries interfaces in preparation for exporting them on the webservice.

Description of the change

== Summary ==

The PackageUpload.overrideBinaries interface will be unacceptably slow when exported on the API.

The PackageUpload.overrideSource and PackageUpload.overrideBinaries interfaces will need to accept strings as well as component/section/priority objects once exported on the API.

== Proposed fix ==

There are source packages with lots of binaries that sometimes need to be overridden individually (e.g. linux) and API requests aren't especially fast. I've therefore amended Archive.overrideBinaries to take a similar list of dicts as a "changes" parameter, allowing many override changes to be made in a single request.

== Pre-implementation notes ==

Override design based on conversations with William Grant.

This is split out of a larger branch, https://code.launchpad.net/~cjwatson/launchpad/queue-api/+merge/108967, at Benji's suggestion in an attempt to make it more easily reviewable.

== LOC Rationale ==

+86. As noted in https://code.launchpad.net/~cjwatson/launchpad/queue-api/+merge/108967, this arc will be LoC-negative.

== Tests ==

bin/test -vvct nascentupload-ddebs.txt -t distroseriesqueue.txt

Test coverage should be sufficient to avoid regressions, but will nevertheless be improved by a follow-up branch to export things on the webservice.

To post a comment you must log in.
Revision history for this message
Benji York (benji) wrote :

This looks good. I like the way the data for the changes is
packaged/unpacked.

The only thing I spotted were some extra parens:

    raise QueueInconsistentStateError(
        "No rights to override from %s" % (
            binarypackage.component.name))

Since the % expression is already in parenthesis, those around
"binarypackage.component.name" are unnecessary.

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 2012-01-20 16:11:11 +0000
3+++ lib/lp/archiveuploader/tests/nascentupload-ddebs.txt 2012-07-03 09:34:24 +0000
4@@ -89,7 +89,8 @@
5
6 >>> switch_dbuser('launchpad')
7
8- >>> bin.queue_root.overrideBinaries(main, devel, None, [main, universe])
9+ >>> bin.queue_root.overrideBinaries(
10+ ... [{"component": main, "section": devel}], [main, universe])
11 True
12 >>> bin.queue_root.acceptFromQueue()
13
14
15=== modified file 'lib/lp/soyuz/browser/queue.py'
16--- lib/lp/soyuz/browser/queue.py 2012-06-29 08:40:05 +0000
17+++ lib/lp/soyuz/browser/queue.py 2012-07-03 09:34:24 +0000
18@@ -1,4 +1,4 @@
19-# Copyright 2009-2011 Canonical Ltd. This software is licensed under the
20+# Copyright 2009-2012 Canonical Ltd. This software is licensed under the
21 # GNU Affero General Public License version 3 (see the file LICENSE).
22
23 """Browser views for package queue."""
24@@ -385,9 +385,13 @@
25 try:
26 source_overridden = queue_item.overrideSource(
27 new_component, new_section, allowed_components)
28+ binary_changes = [{
29+ "component": new_component,
30+ "section": new_section,
31+ "priority": new_priority,
32+ }]
33 binary_overridden = queue_item.overrideBinaries(
34- new_component, new_section, new_priority,
35- allowed_components)
36+ binary_changes, allowed_components)
37 except QueueInconsistentStateError as info:
38 failure.append("FAILED: %s (%s)" %
39 (queue_item.displayname, info))
40
41=== modified file 'lib/lp/soyuz/doc/distroseriesqueue.txt'
42--- lib/lp/soyuz/doc/distroseriesqueue.txt 2012-06-29 08:40:05 +0000
43+++ lib/lp/soyuz/doc/distroseriesqueue.txt 2012-07-03 09:34:24 +0000
44@@ -710,29 +710,25 @@
45 main/base/Important
46
47 >>> from lp.soyuz.enums import PackagePublishingPriority
48+ >>> binary_changes = [{
49+ ... "component": restricted,
50+ ... "section": web,
51+ ... "priority": PackagePublishingPriority.EXTRA,
52+ ... }]
53 >>> print item.overrideBinaries(
54- ... new_component=restricted,
55- ... new_section=web,
56- ... new_priority=PackagePublishingPriority.EXTRA,
57- ... allowed_components=(universe,))
58+ ... binary_changes, allowed_components=(universe,))
59 Traceback (most recent call last):
60 ...
61 QueueInconsistentStateError: No rights to override to restricted
62
63 >>> print item.overrideBinaries(
64- ... new_component=restricted,
65- ... new_section=web,
66- ... new_priority=PackagePublishingPriority.EXTRA,
67- ... allowed_components=(restricted,))
68+ ... binary_changes, allowed_components=(restricted,))
69 Traceback (most recent call last):
70 ...
71 QueueInconsistentStateError: No rights to override from main
72
73 >>> print item.overrideBinaries(
74- ... new_component=restricted,
75- ... new_section=web,
76- ... new_priority=PackagePublishingPriority.EXTRA,
77- ... allowed_components=(main,restricted))
78+ ... binary_changes, allowed_components=(main,restricted))
79 True
80 >>> print "%s/%s/%s" % (
81 ... binary_package.component.name,
82
83=== modified file 'lib/lp/soyuz/interfaces/archive.py'
84--- lib/lp/soyuz/interfaces/archive.py 2012-06-19 23:49:20 +0000
85+++ lib/lp/soyuz/interfaces/archive.py 2012-07-03 09:34:24 +0000
86@@ -43,6 +43,8 @@
87 'NoSuchPPA',
88 'NoTokensForTeams',
89 'PocketNotFound',
90+ 'PriorityNotFound',
91+ 'SectionNotFound',
92 'VersionRequiresName',
93 'default_name_by_purpose',
94 'validate_external_dependencies',
95@@ -156,7 +158,7 @@
96
97
98 class ComponentNotFound(NameLookupFailed):
99- """Invalid source name."""
100+ """Invalid component name."""
101 _message_prefix = 'No such component'
102
103
104@@ -165,6 +167,16 @@
105 """Invalid component name."""
106
107
108+class SectionNotFound(NameLookupFailed):
109+ """Invalid section name."""
110+ _message_prefix = "No such section"
111+
112+
113+class PriorityNotFound(NameLookupFailed):
114+ """Invalid priority name."""
115+ _message_prefix = "No such priority"
116+
117+
118 class NoSuchPPA(NameLookupFailed):
119 """Raised when we try to look up an PPA that doesn't exist."""
120 _message_prefix = "No such ppa"
121
122=== modified file 'lib/lp/soyuz/interfaces/queue.py'
123--- lib/lp/soyuz/interfaces/queue.py 2012-06-30 17:41:37 +0000
124+++ lib/lp/soyuz/interfaces/queue.py 2012-07-03 09:34:24 +0000
125@@ -349,16 +349,16 @@
126 :return: True if the source was overridden.
127 """
128
129- def overrideBinaries(new_component, new_section, new_priority,
130- allowed_components):
131- """Override all the binaries in a binary queue item.
132+ def overrideBinaries(changes, allowed_components):
133+ """Override binary packages in a binary queue item.
134
135- :param new_component: An IComponent to replace the existing one
136- in the upload's source.
137- :param new_section: An ISection to replace the existing one
138- in the upload's source.
139- :param new_priority: A valid PackagePublishingPriority to replace
140- the existing one in the upload's binaries.
141+ :param changes: A sequence of mappings of changes to apply. Each
142+ change mapping may have a "name" item which specifies the binary
143+ package name to override; otherwise, the change applies to all
144+ binaries in the upload. It may also have "component", "section",
145+ and "priority" items which replace the corresponding existing
146+ one in the upload's overridden binaries. Any missing items are
147+ left unchanged.
148 :param allowed_components: A sequence of components that the
149 callsite is allowed to override from and to.
150
151@@ -366,10 +366,7 @@
152 or the new_component are not in the allowed_components
153 sequence.
154
155- The override values may be None, in which case they are not
156- changed.
157-
158- :return: True if the binaries were overridden.
159+ :return: True if any binaries were overridden.
160 """
161
162
163
164=== modified file 'lib/lp/soyuz/model/queue.py'
165--- lib/lp/soyuz/model/queue.py 2012-07-03 08:04:35 +0000
166+++ lib/lp/soyuz/model/queue.py 2012-07-03 09:34:24 +0000
167@@ -72,11 +72,18 @@
168 PackageUploadCustomFormat,
169 PackageUploadStatus,
170 )
171-from lp.soyuz.interfaces.archive import MAIN_ARCHIVE_PURPOSES
172+from lp.soyuz.interfaces.archive import (
173+ ComponentNotFound,
174+ MAIN_ARCHIVE_PURPOSES,
175+ PriorityNotFound,
176+ SectionNotFound,
177+ )
178+from lp.soyuz.interfaces.component import IComponentSet
179 from lp.soyuz.interfaces.packagecopyjob import IPackageCopyJobSource
180 from lp.soyuz.interfaces.publishing import (
181 IPublishingSet,
182 ISourcePackagePublishingHistory,
183+ name_priority_map,
184 )
185 from lp.soyuz.interfaces.queue import (
186 IPackageUpload,
187@@ -91,6 +98,7 @@
188 QueueSourceAcceptError,
189 QueueStateWriteProtectedError,
190 )
191+from lp.soyuz.interfaces.section import ISectionSet
192 from lp.soyuz.model.binarypackagename import BinaryPackageName
193 from lp.soyuz.model.binarypackagerelease import BinaryPackageRelease
194 from lp.soyuz.pas import BuildDaemonPackagesArchSpecific
195@@ -913,6 +921,33 @@
196 """See `IPackageUpload`."""
197 return getUtility(IPackageCopyJobSource).wrap(self.package_copy_job)
198
199+ def _nameToComponent(self, component):
200+ """Helper to convert a possible string component to IComponent."""
201+ try:
202+ if isinstance(component, basestring):
203+ component = getUtility(IComponentSet)[component]
204+ return component
205+ except NotFoundError:
206+ raise ComponentNotFound(component)
207+
208+ def _nameToSection(self, section):
209+ """Helper to convert a possible string section to ISection."""
210+ try:
211+ if isinstance(section, basestring):
212+ section = getUtility(ISectionSet)[section]
213+ return section
214+ except NotFoundError:
215+ raise SectionNotFound(section)
216+
217+ def _nameToPriority(self, priority):
218+ """Helper to convert a possible string priority to its enum."""
219+ try:
220+ if isinstance(priority, basestring):
221+ priority = name_priority_map[priority]
222+ return priority
223+ except KeyError:
224+ raise PriorityNotFound(priority)
225+
226 def _overrideSyncSource(self, new_component, new_section,
227 allowed_components):
228 """Override source on the upload's `PackageCopyJob`, if any."""
229@@ -959,6 +994,9 @@
230 # Nothing needs overriding, bail out.
231 return False
232
233+ new_component = self._nameToComponent(new_component)
234+ new_section = self._nameToSection(new_section)
235+
236 if new_component not in list(allowed_components) + [None]:
237 raise QueueInconsistentStateError(
238 "No rights to override to %s" % new_component.name)
239@@ -969,35 +1007,74 @@
240 self._overrideNonSyncSource(
241 new_component, new_section, allowed_components))
242
243- def overrideBinaries(self, new_component, new_section, new_priority,
244- allowed_components):
245+ def _filterBinaryChanges(self, changes):
246+ """Process a binary changes mapping into a more convenient form."""
247+ changes_by_name = {}
248+ changes_for_all = None
249+
250+ for change in changes:
251+ filtered_change = {}
252+ if change.get("component") is not None:
253+ filtered_change["component"] = self._nameToComponent(
254+ change.get("component"))
255+ if change.get("section") is not None:
256+ filtered_change["section"] = self._nameToSection(
257+ change.get("section"))
258+ if change.get("priority") is not None:
259+ filtered_change["priority"] = self._nameToPriority(
260+ change.get("priority"))
261+
262+ if "name" in change:
263+ changes_by_name[change["name"]] = filtered_change
264+ else:
265+ # Changes with no "name" item provide a default for all
266+ # binaries.
267+ changes_for_all = filtered_change
268+
269+ return changes_by_name, changes_for_all
270+
271+ def overrideBinaries(self, changes, allowed_components):
272 """See `IPackageUpload`."""
273 if not self.contains_build:
274 return False
275
276- if (new_component is None and new_section is None and
277- new_priority is None):
278+ if not changes:
279 # Nothing needs overriding, bail out.
280 return False
281
282- if new_component not in allowed_components:
283+ changes_by_name, changes_for_all = self._filterBinaryChanges(changes)
284+
285+ new_components = set()
286+ for change in changes_by_name.values():
287+ if "component" in change:
288+ new_components.add(change["component"])
289+ if changes_for_all is not None and "component" in changes_for_all:
290+ new_components.add(changes_for_all["component"])
291+ new_components.discard(None)
292+ disallowed_components = sorted(
293+ component.name
294+ for component in new_components.difference(allowed_components))
295+ if disallowed_components:
296 raise QueueInconsistentStateError(
297- "No rights to override to %s" % new_component.name)
298+ "No rights to override to %s" %
299+ ", ".join(disallowed_components))
300
301+ made_changes = False
302 for build in self.builds:
303 for binarypackage in build.build.binarypackages:
304- if binarypackage.component not in allowed_components:
305- # The old or the new component is not in the list of
306- # allowed components to override.
307- raise QueueInconsistentStateError(
308- "No rights to override from %s" % (
309- binarypackage.component.name))
310- binarypackage.override(
311- component=new_component,
312- section=new_section,
313- priority=new_priority)
314+ change = changes_by_name.get(
315+ binarypackage.name, changes_for_all)
316+ if change:
317+ if binarypackage.component not in allowed_components:
318+ # The old component is not in the list of allowed
319+ # components to override.
320+ raise QueueInconsistentStateError(
321+ "No rights to override from %s" %
322+ binarypackage.component.name)
323+ binarypackage.override(**change)
324+ made_changes = True
325
326- return bool(self.builds)
327+ return made_changes
328
329
330 class PackageUploadBuild(SQLBase):