Merge lp:~cjwatson/launchpad/queue-api into lp:launchpad
- queue-api
- Merge into devel
Status: | Rejected | ||||
---|---|---|---|---|---|
Rejected by: | Colin Watson | ||||
Proposed branch: | lp:~cjwatson/launchpad/queue-api | ||||
Merge into: | lp:launchpad | ||||
Diff against target: |
1647 lines (+802/-187) 16 files modified
lib/lp/archiveuploader/tests/nascentupload-ddebs.txt (+2/-1) lib/lp/registry/interfaces/distroseries.py (+7/-0) lib/lp/soyuz/browser/queue.py (+7/-3) lib/lp/soyuz/configure.zcml (+6/-0) lib/lp/soyuz/doc/distroseriesqueue.txt (+22/-26) lib/lp/soyuz/interfaces/archive.py (+13/-1) lib/lp/soyuz/interfaces/binarypackagerelease.py (+8/-1) lib/lp/soyuz/interfaces/queue.py (+133/-31) lib/lp/soyuz/model/binarypackagerelease.py (+13/-2) lib/lp/soyuz/model/queue.py (+241/-36) lib/lp/soyuz/scripts/queue.py (+3/-3) lib/lp/soyuz/stories/webservice/xx-packageupload.txt (+13/-0) lib/lp/soyuz/tests/test_distroseriesqueue_ddtp_tarball.py (+0/-28) lib/lp/soyuz/tests/test_distroseriesqueue_dist_upgrader.py (+4/-27) lib/lp/soyuz/tests/test_packageupload.py (+327/-26) lib/lp/testing/factory.py (+3/-2) |
||||
To merge this branch: | bzr merge lp:~cjwatson/launchpad/queue-api | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Launchpad code reviewers | Pending | ||
Review via email: mp+108967@code.launchpad.net |
Commit message
Export enough of PackageUpload and DistroSeries.
Description of the change
== Summary ==
Implement the next stage of https:/
== Proposed fix ==
Export enough of PackageUpload and DistroSeries.
== Pre-implementation notes ==
I've gone round a few times with various people, particularly William Grant, on the exact way to export all of this stuff, because I gather that we want to avoid exposing the current data model in order that it can be rearranged in the future. This has led to the following design choices:
* Everything is on devel. The only clients for this should be tools such as those in lp:ubuntu-archive-tools, which can be kept up to date if there's a need to change these interfaces.
* Even though some of the underlying methods are on other objects, all the new exported methods are on PackageUpload rather than exporting anything else.
* 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 arranged for properties (including overrides) of all binaries in an upload to come back as a list of dicts in a single JSON response, and I've amended Archive.
== LOC Rationale ==
+615, on top of a previous branch that was +91. I think this is valid because this is part of an arc of work (resourced by Ubuntu Engineering) that will culminate in removing lib/lp/
== Tests ==
bin/test -vvct nascentupload-
== Demo and Q/A ==
http://
Benji York (benji) wrote : | # |
Colin Watson (cjwatson) wrote : | # |
You're probably right. I shall get splitting ...
Colin Watson (cjwatson) wrote : | # |
This has now all been merged in smaller pieces.
Preview Diff
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-06-08 14:30:37 +0000 | |||
4 | @@ -89,7 +89,8 @@ | |||
5 | 89 | 89 | ||
6 | 90 | >>> switch_dbuser('launchpad') | 90 | >>> switch_dbuser('launchpad') |
7 | 91 | 91 | ||
9 | 92 | >>> bin.queue_root.overrideBinaries(main, devel, None, [main, universe]) | 92 | >>> bin.queue_root.overrideBinaries( |
10 | 93 | ... [{"component": main, "section": devel}], [main, universe]) | ||
11 | 93 | True | 94 | True |
12 | 94 | >>> bin.queue_root.acceptFromQueue() | 95 | >>> bin.queue_root.acceptFromQueue() |
13 | 95 | 96 | ||
14 | 96 | 97 | ||
15 | === modified file 'lib/lp/registry/interfaces/distroseries.py' | |||
16 | --- lib/lp/registry/interfaces/distroseries.py 2012-01-10 09:55:24 +0000 | |||
17 | +++ lib/lp/registry/interfaces/distroseries.py 2012-06-08 14:30:37 +0000 | |||
18 | @@ -547,6 +547,13 @@ | |||
19 | 547 | description=_("Return only items with custom files of this " | 547 | description=_("Return only items with custom files of this " |
20 | 548 | "type."), | 548 | "type."), |
21 | 549 | required=False), | 549 | required=False), |
22 | 550 | name=TextLine(title=_("Package or file name"), required=False), | ||
23 | 551 | version=TextLine(title=_("Package version"), required=False), | ||
24 | 552 | exact_match=Bool( | ||
25 | 553 | title=_("Exact match"), | ||
26 | 554 | description=_("Whether to filter name and version by exact " | ||
27 | 555 | "matching."), | ||
28 | 556 | required=False), | ||
29 | 550 | ) | 557 | ) |
30 | 551 | # Really IPackageUpload, patched in _schema_circular_imports.py | 558 | # Really IPackageUpload, patched in _schema_circular_imports.py |
31 | 552 | @operation_returns_collection_of(Interface) | 559 | @operation_returns_collection_of(Interface) |
32 | 553 | 560 | ||
33 | === modified file 'lib/lp/soyuz/browser/queue.py' | |||
34 | --- lib/lp/soyuz/browser/queue.py 2012-01-01 02:58:52 +0000 | |||
35 | +++ lib/lp/soyuz/browser/queue.py 2012-06-08 14:30:37 +0000 | |||
36 | @@ -1,4 +1,4 @@ | |||
38 | 1 | # Copyright 2009-2011 Canonical Ltd. This software is licensed under the | 1 | # Copyright 2009-2012 Canonical Ltd. This software is licensed under the |
39 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). |
40 | 3 | 3 | ||
41 | 4 | """Browser views for package queue.""" | 4 | """Browser views for package queue.""" |
42 | @@ -385,9 +385,13 @@ | |||
43 | 385 | try: | 385 | try: |
44 | 386 | source_overridden = queue_item.overrideSource( | 386 | source_overridden = queue_item.overrideSource( |
45 | 387 | new_component, new_section, allowed_components) | 387 | new_component, new_section, allowed_components) |
46 | 388 | binary_changes = [{ | ||
47 | 389 | "component": new_component, | ||
48 | 390 | "section": new_section, | ||
49 | 391 | "priority": new_priority, | ||
50 | 392 | }] | ||
51 | 388 | binary_overridden = queue_item.overrideBinaries( | 393 | binary_overridden = queue_item.overrideBinaries( |
54 | 389 | new_component, new_section, new_priority, | 394 | binary_changes, allowed_components) |
53 | 390 | allowed_components) | ||
55 | 391 | except QueueInconsistentStateError, info: | 395 | except QueueInconsistentStateError, info: |
56 | 392 | failure.append("FAILED: %s (%s)" % | 396 | failure.append("FAILED: %s (%s)" % |
57 | 393 | (queue_item.displayname, info)) | 397 | (queue_item.displayname, info)) |
58 | 394 | 398 | ||
59 | === modified file 'lib/lp/soyuz/configure.zcml' | |||
60 | --- lib/lp/soyuz/configure.zcml 2012-06-07 20:35:53 +0000 | |||
61 | +++ lib/lp/soyuz/configure.zcml 2012-06-08 14:30:37 +0000 | |||
62 | @@ -157,18 +157,24 @@ | |||
63 | 157 | distroseries | 157 | distroseries |
64 | 158 | 158 | ||
65 | 159 | changesfile | 159 | changesfile |
66 | 160 | changes_file_url | ||
67 | 160 | signing_key | 161 | signing_key |
68 | 161 | archive | 162 | archive |
69 | 162 | sources | 163 | sources |
70 | 164 | sourceFileUrls | ||
71 | 163 | builds | 165 | builds |
72 | 166 | binaryFileUrls | ||
73 | 164 | customfiles | 167 | customfiles |
74 | 165 | custom_file_urls | 168 | custom_file_urls |
75 | 169 | customFileUrls | ||
76 | 170 | getBinaryProperties | ||
77 | 166 | date_created | 171 | date_created |
78 | 167 | sourcepackagerelease | 172 | sourcepackagerelease |
79 | 168 | component_name | 173 | component_name |
80 | 169 | concrete_package_copy_job | 174 | concrete_package_copy_job |
81 | 170 | contains_source | 175 | contains_source |
82 | 171 | contains_build | 176 | contains_build |
83 | 177 | contains_copy | ||
84 | 172 | contains_translation | 178 | contains_translation |
85 | 173 | contains_installer | 179 | contains_installer |
86 | 174 | contains_upgrader | 180 | contains_upgrader |
87 | 175 | 181 | ||
88 | === modified file 'lib/lp/soyuz/doc/distroseriesqueue.txt' | |||
89 | --- lib/lp/soyuz/doc/distroseriesqueue.txt 2012-01-06 11:08:30 +0000 | |||
90 | +++ lib/lp/soyuz/doc/distroseriesqueue.txt 2012-06-08 14:30:37 +0000 | |||
91 | @@ -648,7 +648,7 @@ | |||
92 | 648 | In addition to these parameters, you must also supply | 648 | In addition to these parameters, you must also supply |
93 | 649 | "allowed_components", which is a sequence of IComponent. Any overrides | 649 | "allowed_components", which is a sequence of IComponent. Any overrides |
94 | 650 | must have the existing and new component in this sequence otherwise | 650 | must have the existing and new component in this sequence otherwise |
96 | 651 | QueueInconsistentStateError is raised. | 651 | QueueAdminUnauthorizedError is raised. |
97 | 652 | 652 | ||
98 | 653 | The alsa-utils source is already in the queue with component "main" | 653 | The alsa-utils source is already in the queue with component "main" |
99 | 654 | and section "base". | 654 | and section "base". |
100 | @@ -673,7 +673,7 @@ | |||
101 | 673 | ... allowed_components=(universe,)) | 673 | ... allowed_components=(universe,)) |
102 | 674 | Traceback (most recent call last): | 674 | Traceback (most recent call last): |
103 | 675 | ... | 675 | ... |
105 | 676 | QueueInconsistentStateError: No rights to override to restricted | 676 | QueueAdminUnauthorizedError: No rights to override to restricted |
106 | 677 | 677 | ||
107 | 678 | Allowing "restricted" still won't work because the original component | 678 | Allowing "restricted" still won't work because the original component |
108 | 679 | is "main": | 679 | is "main": |
109 | @@ -683,7 +683,7 @@ | |||
110 | 683 | ... allowed_components=(restricted,)) | 683 | ... allowed_components=(restricted,)) |
111 | 684 | Traceback (most recent call last): | 684 | Traceback (most recent call last): |
112 | 685 | ... | 685 | ... |
114 | 686 | QueueInconsistentStateError: No rights to override from main | 686 | QueueAdminUnauthorizedError: No rights to override from main |
115 | 687 | 687 | ||
116 | 688 | Specifying both main and restricted allows the override to restricted/web. | 688 | Specifying both main and restricted allows the override to restricted/web. |
117 | 689 | overrideSource() returns True if it completed the task. | 689 | overrideSource() returns True if it completed the task. |
118 | @@ -710,29 +710,25 @@ | |||
119 | 710 | main/base/Important | 710 | main/base/Important |
120 | 711 | 711 | ||
121 | 712 | >>> from lp.soyuz.enums import PackagePublishingPriority | 712 | >>> from lp.soyuz.enums import PackagePublishingPriority |
145 | 713 | >>> print item.overrideBinaries( | 713 | >>> binary_changes = [{ |
146 | 714 | ... new_component=restricted, | 714 | ... "component": restricted, |
147 | 715 | ... new_section=web, | 715 | ... "section": web, |
148 | 716 | ... new_priority=PackagePublishingPriority.EXTRA, | 716 | ... "priority": PackagePublishingPriority.EXTRA, |
149 | 717 | ... allowed_components=(universe,)) | 717 | ... }] |
150 | 718 | Traceback (most recent call last): | 718 | >>> print item.overrideBinaries( |
151 | 719 | ... | 719 | ... binary_changes, allowed_components=(universe,)) |
152 | 720 | QueueInconsistentStateError: No rights to override to restricted | 720 | Traceback (most recent call last): |
153 | 721 | 721 | ... | |
154 | 722 | >>> print item.overrideBinaries( | 722 | QueueAdminUnauthorizedError: No rights to override to restricted |
155 | 723 | ... new_component=restricted, | 723 | |
156 | 724 | ... new_section=web, | 724 | >>> print item.overrideBinaries( |
157 | 725 | ... new_priority=PackagePublishingPriority.EXTRA, | 725 | ... binary_changes, allowed_components=(restricted,)) |
158 | 726 | ... allowed_components=(restricted,)) | 726 | Traceback (most recent call last): |
159 | 727 | Traceback (most recent call last): | 727 | ... |
160 | 728 | ... | 728 | QueueAdminUnauthorizedError: No rights to override from main |
161 | 729 | QueueInconsistentStateError: No rights to override from main | 729 | |
162 | 730 | 730 | >>> print item.overrideBinaries( | |
163 | 731 | >>> print item.overrideBinaries( | 731 | ... binary_changes, allowed_components=(main,restricted)) |
141 | 732 | ... new_component=restricted, | ||
142 | 733 | ... new_section=web, | ||
143 | 734 | ... new_priority=PackagePublishingPriority.EXTRA, | ||
144 | 735 | ... allowed_components=(main,restricted)) | ||
164 | 736 | True | 732 | True |
165 | 737 | >>> print "%s/%s/%s" % ( | 733 | >>> print "%s/%s/%s" % ( |
166 | 738 | ... binary_package.component.name, | 734 | ... binary_package.component.name, |
167 | 739 | 735 | ||
168 | === modified file 'lib/lp/soyuz/interfaces/archive.py' | |||
169 | --- lib/lp/soyuz/interfaces/archive.py 2012-06-06 21:24:57 +0000 | |||
170 | +++ lib/lp/soyuz/interfaces/archive.py 2012-06-08 14:30:37 +0000 | |||
171 | @@ -43,6 +43,8 @@ | |||
172 | 43 | 'NoSuchPPA', | 43 | 'NoSuchPPA', |
173 | 44 | 'NoTokensForTeams', | 44 | 'NoTokensForTeams', |
174 | 45 | 'PocketNotFound', | 45 | 'PocketNotFound', |
175 | 46 | 'PriorityNotFound', | ||
176 | 47 | 'SectionNotFound', | ||
177 | 46 | 'VersionRequiresName', | 48 | 'VersionRequiresName', |
178 | 47 | 'default_name_by_purpose', | 49 | 'default_name_by_purpose', |
179 | 48 | 'validate_external_dependencies', | 50 | 'validate_external_dependencies', |
180 | @@ -156,7 +158,7 @@ | |||
181 | 156 | 158 | ||
182 | 157 | 159 | ||
183 | 158 | class ComponentNotFound(NameLookupFailed): | 160 | class ComponentNotFound(NameLookupFailed): |
185 | 159 | """Invalid source name.""" | 161 | """Invalid component name.""" |
186 | 160 | _message_prefix = 'No such component' | 162 | _message_prefix = 'No such component' |
187 | 161 | 163 | ||
188 | 162 | 164 | ||
189 | @@ -165,6 +167,16 @@ | |||
190 | 165 | """Invalid component name.""" | 167 | """Invalid component name.""" |
191 | 166 | 168 | ||
192 | 167 | 169 | ||
193 | 170 | class SectionNotFound(NameLookupFailed): | ||
194 | 171 | """Invalid section name.""" | ||
195 | 172 | _message_prefix = "No such section" | ||
196 | 173 | |||
197 | 174 | |||
198 | 175 | class PriorityNotFound(NameLookupFailed): | ||
199 | 176 | """Invalid priority name.""" | ||
200 | 177 | _message_prefix = "No such priority" | ||
201 | 178 | |||
202 | 179 | |||
203 | 168 | class NoSuchPPA(NameLookupFailed): | 180 | class NoSuchPPA(NameLookupFailed): |
204 | 169 | """Raised when we try to look up an PPA that doesn't exist.""" | 181 | """Raised when we try to look up an PPA that doesn't exist.""" |
205 | 170 | _message_prefix = "No such ppa" | 182 | _message_prefix = "No such ppa" |
206 | 171 | 183 | ||
207 | === modified file 'lib/lp/soyuz/interfaces/binarypackagerelease.py' | |||
208 | --- lib/lp/soyuz/interfaces/binarypackagerelease.py 2011-12-24 16:54:44 +0000 | |||
209 | +++ lib/lp/soyuz/interfaces/binarypackagerelease.py 2012-06-08 14:30:37 +0000 | |||
210 | @@ -1,4 +1,4 @@ | |||
212 | 1 | # Copyright 2009 Canonical Ltd. This software is licensed under the | 1 | # Copyright 2009-2012 Canonical Ltd. This software is licensed under the |
213 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). |
214 | 3 | 3 | ||
215 | 4 | # pylint: disable-msg=E0211,E0213 | 4 | # pylint: disable-msg=E0211,E0213 |
216 | @@ -98,6 +98,13 @@ | |||
217 | 98 | description=_("True if there binary version was never published for " | 98 | description=_("True if there binary version was never published for " |
218 | 99 | "the architeture it was built for. False otherwise.")) | 99 | "the architeture it was built for. False otherwise.")) |
219 | 100 | 100 | ||
220 | 101 | def properties(): | ||
221 | 102 | """Returns the properties of this binary. | ||
222 | 103 | |||
223 | 104 | For fast retrieval over the webservice, this is returned as a | ||
224 | 105 | dictionary. | ||
225 | 106 | """ | ||
226 | 107 | |||
227 | 101 | def addFile(file): | 108 | def addFile(file): |
228 | 102 | """Create a BinaryPackageFile record referencing this build | 109 | """Create a BinaryPackageFile record referencing this build |
229 | 103 | and attach the provided library file alias (file). | 110 | and attach the provided library file alias (file). |
230 | 104 | 111 | ||
231 | === modified file 'lib/lp/soyuz/interfaces/queue.py' | |||
232 | --- lib/lp/soyuz/interfaces/queue.py 2012-05-30 08:50:50 +0000 | |||
233 | +++ lib/lp/soyuz/interfaces/queue.py 2012-06-08 14:30:37 +0000 | |||
234 | @@ -16,6 +16,7 @@ | |||
235 | 16 | 'IPackageUploadCustom', | 16 | 'IPackageUploadCustom', |
236 | 17 | 'IPackageUploadSet', | 17 | 'IPackageUploadSet', |
237 | 18 | 'NonBuildableSourceUploadError', | 18 | 'NonBuildableSourceUploadError', |
238 | 19 | 'QueueAdminUnauthorizedError', | ||
239 | 19 | 'QueueBuildAcceptError', | 20 | 'QueueBuildAcceptError', |
240 | 20 | 'QueueInconsistentStateError', | 21 | 'QueueInconsistentStateError', |
241 | 21 | 'QueueSourceAcceptError', | 22 | 'QueueSourceAcceptError', |
242 | @@ -26,11 +27,15 @@ | |||
243 | 26 | 27 | ||
244 | 27 | from lazr.enum import DBEnumeratedType | 28 | from lazr.enum import DBEnumeratedType |
245 | 28 | from lazr.restful.declarations import ( | 29 | from lazr.restful.declarations import ( |
246 | 30 | call_with, | ||
247 | 29 | error_status, | 31 | error_status, |
248 | 30 | export_as_webservice_entry, | 32 | export_as_webservice_entry, |
249 | 33 | export_read_operation, | ||
250 | 31 | export_write_operation, | 34 | export_write_operation, |
251 | 32 | exported, | 35 | exported, |
252 | 33 | operation_for_version, | 36 | operation_for_version, |
253 | 37 | operation_parameters, | ||
254 | 38 | REQUEST_USER, | ||
255 | 34 | ) | 39 | ) |
256 | 35 | from lazr.restful.fields import Reference | 40 | from lazr.restful.fields import Reference |
257 | 36 | from zope.interface import ( | 41 | from zope.interface import ( |
258 | @@ -38,12 +43,15 @@ | |||
259 | 38 | Interface, | 43 | Interface, |
260 | 39 | ) | 44 | ) |
261 | 40 | from zope.schema import ( | 45 | from zope.schema import ( |
262 | 46 | Bool, | ||
263 | 41 | Choice, | 47 | Choice, |
264 | 42 | Datetime, | 48 | Datetime, |
265 | 49 | Dict, | ||
266 | 43 | Int, | 50 | Int, |
267 | 44 | List, | 51 | List, |
268 | 45 | TextLine, | 52 | TextLine, |
269 | 46 | ) | 53 | ) |
270 | 54 | from zope.security.interfaces import Unauthorized | ||
271 | 47 | 55 | ||
272 | 48 | from lp import _ | 56 | from lp import _ |
273 | 49 | from lp.soyuz.enums import PackageUploadStatus | 57 | from lp.soyuz.enums import PackageUploadStatus |
274 | @@ -67,6 +75,10 @@ | |||
275 | 67 | """ | 75 | """ |
276 | 68 | 76 | ||
277 | 69 | 77 | ||
278 | 78 | class QueueAdminUnauthorizedError(Unauthorized): | ||
279 | 79 | """User not permitted to perform a queue administration operation.""" | ||
280 | 80 | |||
281 | 81 | |||
282 | 70 | class NonBuildableSourceUploadError(QueueInconsistentStateError): | 82 | class NonBuildableSourceUploadError(QueueInconsistentStateError): |
283 | 71 | """Source upload will not result in any build record. | 83 | """Source upload will not result in any build record. |
284 | 72 | 84 | ||
285 | @@ -141,6 +153,14 @@ | |||
286 | 141 | 153 | ||
287 | 142 | changesfile = Attribute("The librarian alias for the changes file " | 154 | changesfile = Attribute("The librarian alias for the changes file " |
288 | 143 | "associated with this upload") | 155 | "associated with this upload") |
289 | 156 | changes_file_url = exported( | ||
290 | 157 | TextLine( | ||
291 | 158 | title=_("Changes file URL"), | ||
292 | 159 | description=_("Librarian URL for the changes file associated with " | ||
293 | 160 | "this upload. Will be None if the upload was copied " | ||
294 | 161 | "from another series."), | ||
295 | 162 | required=False, readonly=True), | ||
296 | 163 | as_of="devel") | ||
297 | 144 | 164 | ||
298 | 145 | signing_key = Attribute("Changesfile Signing Key.") | 165 | signing_key = Attribute("Changesfile Signing Key.") |
299 | 146 | 166 | ||
300 | @@ -162,17 +182,18 @@ | |||
301 | 162 | title=_("Archive"), required=True, readonly=True)) | 182 | title=_("Archive"), required=True, readonly=True)) |
302 | 163 | sources = Attribute("The queue sources associated with this queue item") | 183 | sources = Attribute("The queue sources associated with this queue item") |
303 | 164 | builds = Attribute("The queue builds associated with the queue item") | 184 | builds = Attribute("The queue builds associated with the queue item") |
304 | 185 | |||
305 | 165 | customfiles = Attribute("Custom upload files associated with this " | 186 | customfiles = Attribute("Custom upload files associated with this " |
306 | 166 | "queue item") | 187 | "queue item") |
307 | 167 | |||
308 | 168 | custom_file_urls = exported( | 188 | custom_file_urls = exported( |
309 | 169 | List( | 189 | List( |
311 | 170 | title=_("Custom File URLs"), | 190 | title=_("Custom file URLs"), |
312 | 171 | description=_("Librarian URLs for all the custom files attached " | 191 | description=_("Librarian URLs for all the custom files attached " |
313 | 172 | "to this upload."), | 192 | "to this upload."), |
314 | 173 | value_type=TextLine(), | 193 | value_type=TextLine(), |
315 | 174 | required=False, | 194 | required=False, |
317 | 175 | readonly=True)) | 195 | readonly=True), |
318 | 196 | ("devel", dict(exported=False)), exported=True) | ||
319 | 176 | 197 | ||
320 | 177 | displayname = exported( | 198 | displayname = exported( |
321 | 178 | TextLine( | 199 | TextLine( |
322 | @@ -191,17 +212,39 @@ | |||
323 | 191 | sourcepackagerelease = Attribute( | 212 | sourcepackagerelease = Attribute( |
324 | 192 | "The source package release for this item") | 213 | "The source package release for this item") |
325 | 193 | 214 | ||
337 | 194 | package_name = TextLine( | 215 | package_name = exported( |
338 | 195 | title=_("Name of the uploaded source package"), readonly=True) | 216 | TextLine( |
339 | 196 | 217 | title=_("Name of the uploaded source package"), readonly=True), | |
340 | 197 | package_version = TextLine( | 218 | as_of="devel") |
341 | 198 | title=_("Source package version"), readonly=True) | 219 | |
342 | 199 | 220 | package_version = exported( | |
343 | 200 | component_name = TextLine( | 221 | TextLine(title=_("Source package version"), readonly=True), |
344 | 201 | title=_("Source package component name"), readonly=True) | 222 | as_of="devel") |
345 | 202 | 223 | ||
346 | 203 | contains_source = Attribute("whether or not this upload contains sources") | 224 | component_name = exported( |
347 | 204 | contains_build = Attribute("whether or not this upload contains binaries") | 225 | TextLine(title=_("Source package component name"), readonly=True), |
348 | 226 | as_of="devel") | ||
349 | 227 | |||
350 | 228 | section_name = exported( | ||
351 | 229 | TextLine(title=_("Source package section name"), readonly=True), | ||
352 | 230 | as_of="devel") | ||
353 | 231 | |||
354 | 232 | contains_source = exported( | ||
355 | 233 | Bool( | ||
356 | 234 | title=_("Whether or not this upload contains sources"), | ||
357 | 235 | readonly=True), | ||
358 | 236 | as_of="devel") | ||
359 | 237 | contains_build = exported( | ||
360 | 238 | Bool( | ||
361 | 239 | title=_("Whether or not this upload contains binaries"), | ||
362 | 240 | readonly=True), | ||
363 | 241 | as_of="devel") | ||
364 | 242 | contains_copy = exported( | ||
365 | 243 | Bool( | ||
366 | 244 | title=_("Whether or not this upload contains a copy from another " | ||
367 | 245 | "series."), | ||
368 | 246 | readonly=True), | ||
369 | 247 | as_of="devel") | ||
370 | 205 | contains_installer = Attribute( | 248 | contains_installer = Attribute( |
371 | 206 | "whether or not this upload contains installers images") | 249 | "whether or not this upload contains installers images") |
372 | 207 | contains_translation = Attribute( | 250 | contains_translation = Attribute( |
373 | @@ -223,8 +266,38 @@ | |||
374 | 223 | on all the binarypackagerelease records arising from the build. | 266 | on all the binarypackagerelease records arising from the build. |
375 | 224 | """) | 267 | """) |
376 | 225 | 268 | ||
379 | 226 | section_name = TextLine( | 269 | @export_read_operation() |
380 | 227 | title=_("Source package sectio name"), readonly=True) | 270 | @operation_for_version("devel") |
381 | 271 | def sourceFileUrls(): | ||
382 | 272 | """URLs for all the source files attached to this upload. | ||
383 | 273 | |||
384 | 274 | :return: A collection of URLs for this upload. | ||
385 | 275 | """ | ||
386 | 276 | |||
387 | 277 | @export_read_operation() | ||
388 | 278 | @operation_for_version("devel") | ||
389 | 279 | def binaryFileUrls(): | ||
390 | 280 | """URLs for all the binary files attached to this upload. | ||
391 | 281 | |||
392 | 282 | :return: A collection of URLs for this upload. | ||
393 | 283 | """ | ||
394 | 284 | |||
395 | 285 | @export_read_operation() | ||
396 | 286 | @operation_for_version("devel") | ||
397 | 287 | def customFileUrls(): | ||
398 | 288 | """URLs for all the custom files attached to this upload. | ||
399 | 289 | |||
400 | 290 | :return: A collection of URLs for this upload. | ||
401 | 291 | """ | ||
402 | 292 | |||
403 | 293 | @export_read_operation() | ||
404 | 294 | @operation_for_version("devel") | ||
405 | 295 | def getBinaryProperties(): | ||
406 | 296 | """The properties of the binaries associated with this queue item. | ||
407 | 297 | |||
408 | 298 | :return: A list of dictionaries, each containing the properties of a | ||
409 | 299 | single binary. | ||
410 | 300 | """ | ||
411 | 228 | 301 | ||
412 | 229 | def setNew(): | 302 | def setNew(): |
413 | 230 | """Set queue state to NEW.""" | 303 | """Set queue state to NEW.""" |
414 | @@ -329,7 +402,14 @@ | |||
415 | 329 | :param logger: Specify a logger object if required. Mainly for tests. | 402 | :param logger: Specify a logger object if required. Mainly for tests. |
416 | 330 | """ | 403 | """ |
417 | 331 | 404 | ||
419 | 332 | def overrideSource(new_component, new_section, allowed_components): | 405 | @operation_parameters( |
420 | 406 | new_component=TextLine(title=u"The new component name."), | ||
421 | 407 | new_section=TextLine(title=u"The new section name.")) | ||
422 | 408 | @call_with(allowed_components=None, user=REQUEST_USER) | ||
423 | 409 | @export_write_operation() | ||
424 | 410 | @operation_for_version('devel') | ||
425 | 411 | def overrideSource(new_component=None, new_section=None, | ||
426 | 412 | allowed_components=None, user=None): | ||
427 | 333 | """Override the source package contained in this queue item. | 413 | """Override the source package contained in this queue item. |
428 | 334 | 414 | ||
429 | 335 | :param new_component: An IComponent to replace the existing one | 415 | :param new_component: An IComponent to replace the existing one |
430 | @@ -338,6 +418,8 @@ | |||
431 | 338 | in the upload's source. | 418 | in the upload's source. |
432 | 339 | :param allowed_components: A sequence of components that the | 419 | :param allowed_components: A sequence of components that the |
433 | 340 | callsite is allowed to override from and to. | 420 | callsite is allowed to override from and to. |
434 | 421 | :param user: The user requesting the override change, used if | ||
435 | 422 | allowed_components is None. | ||
436 | 341 | 423 | ||
437 | 342 | :raises QueueInconsistentStateError: if either the existing | 424 | :raises QueueInconsistentStateError: if either the existing |
438 | 343 | or the new_component are not in the allowed_components | 425 | or the new_component are not in the allowed_components |
439 | @@ -349,27 +431,40 @@ | |||
440 | 349 | :return: True if the source was overridden. | 431 | :return: True if the source was overridden. |
441 | 350 | """ | 432 | """ |
442 | 351 | 433 | ||
446 | 352 | def overrideBinaries(new_component, new_section, new_priority, | 434 | @operation_parameters( |
447 | 353 | allowed_components): | 435 | changes=List( |
448 | 354 | """Override all the binaries in a binary queue item. | 436 | title=u"A sequence of changes to apply.", |
449 | 437 | description=( | ||
450 | 438 | u"Each item may have a 'name' item which specifies the binary " | ||
451 | 439 | "package name to override; otherwise, the change applies to " | ||
452 | 440 | "all binaries in the upload. It may also have 'component', " | ||
453 | 441 | "'section', and 'priority' items which replace the " | ||
454 | 442 | "corresponding existing one in the upload's overridden " | ||
455 | 443 | "binaries."), | ||
456 | 444 | value_type=Dict(key_type=TextLine()))) | ||
457 | 445 | @call_with(allowed_components=None, user=REQUEST_USER) | ||
458 | 446 | @export_write_operation() | ||
459 | 447 | @operation_for_version('devel') | ||
460 | 448 | def overrideBinaries(changes, allowed_components=None, user=None): | ||
461 | 449 | """Override binary packages in a binary queue item. | ||
462 | 355 | 450 | ||
469 | 356 | :param new_component: An IComponent to replace the existing one | 451 | :param changes: A sequence of mappings of changes to apply. Each |
470 | 357 | in the upload's source. | 452 | change mapping may have a "name" item which specifies the binary |
471 | 358 | :param new_section: An ISection to replace the existing one | 453 | package name to override; otherwise, the change applies to all |
472 | 359 | in the upload's source. | 454 | binaries in the upload. It may also have "component", "section", |
473 | 360 | :param new_priority: A valid PackagePublishingPriority to replace | 455 | and "priority" items which replace the corresponding existing |
474 | 361 | the existing one in the upload's binaries. | 456 | one in the upload's overridden binaries. Any missing items are |
475 | 457 | left unchanged. | ||
476 | 362 | :param allowed_components: A sequence of components that the | 458 | :param allowed_components: A sequence of components that the |
477 | 363 | callsite is allowed to override from and to. | 459 | callsite is allowed to override from and to. |
478 | 460 | :param user: The user requesting the override change, used if | ||
479 | 461 | allowed_components is None. | ||
480 | 364 | 462 | ||
481 | 365 | :raises QueueInconsistentStateError: if either the existing | 463 | :raises QueueInconsistentStateError: if either the existing |
482 | 366 | or the new_component are not in the allowed_components | 464 | or the new_component are not in the allowed_components |
483 | 367 | sequence. | 465 | sequence. |
484 | 368 | 466 | ||
489 | 369 | The override values may be None, in which case they are not | 467 | :return: True if any binaries were overridden. |
486 | 370 | changed. | ||
487 | 371 | |||
488 | 372 | :return: True if the binaries were overridden. | ||
490 | 373 | """ | 468 | """ |
491 | 374 | 469 | ||
492 | 375 | 470 | ||
493 | @@ -382,13 +477,20 @@ | |||
494 | 382 | 477 | ||
495 | 383 | packageupload = Int( | 478 | packageupload = Int( |
496 | 384 | title=_("PackageUpload"), required=True, | 479 | title=_("PackageUpload"), required=True, |
498 | 385 | readonly=False, | 480 | readonly=True, |
499 | 386 | ) | 481 | ) |
500 | 387 | 482 | ||
501 | 388 | build = Int( | 483 | build = Int( |
502 | 389 | title=_("The related build"), required=True, readonly=False, | 484 | title=_("The related build"), required=True, readonly=False, |
503 | 390 | ) | 485 | ) |
504 | 391 | 486 | ||
505 | 487 | def binaries(): | ||
506 | 488 | """Returns the properties of the binaries in this build. | ||
507 | 489 | |||
508 | 490 | For fast retrieval over the webservice, these are returned as a list | ||
509 | 491 | of dictionaries, one per binary. | ||
510 | 492 | """ | ||
511 | 493 | |||
512 | 392 | def publish(logger=None): | 494 | def publish(logger=None): |
513 | 393 | """Publish this queued source in the distroseries referred to by | 495 | """Publish this queued source in the distroseries referred to by |
514 | 394 | the parent queue item. | 496 | the parent queue item. |
515 | 395 | 497 | ||
516 | === modified file 'lib/lp/soyuz/model/binarypackagerelease.py' | |||
517 | --- lib/lp/soyuz/model/binarypackagerelease.py 2012-04-16 23:02:44 +0000 | |||
518 | +++ lib/lp/soyuz/model/binarypackagerelease.py 2012-06-08 14:30:37 +0000 | |||
519 | @@ -1,4 +1,4 @@ | |||
521 | 1 | # Copyright 2009 Canonical Ltd. This software is licensed under the | 1 | # Copyright 2009-2012 Canonical Ltd. This software is licensed under the |
522 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). |
523 | 3 | 3 | ||
524 | 4 | # pylint: disable-msg=E0611,W0212 | 4 | # pylint: disable-msg=E0611,W0212 |
525 | @@ -131,6 +131,18 @@ | |||
526 | 131 | self.binarypackagename) | 131 | self.binarypackagename) |
527 | 132 | return distroarchseries_binary_package.currentrelease is None | 132 | return distroarchseries_binary_package.currentrelease is None |
528 | 133 | 133 | ||
529 | 134 | @property | ||
530 | 135 | def properties(self): | ||
531 | 136 | return { | ||
532 | 137 | "name": self.name, | ||
533 | 138 | "version": self.version, | ||
534 | 139 | "is_new": self.is_new, | ||
535 | 140 | "architecture": self.build.arch_tag, | ||
536 | 141 | "component": self.component.name, | ||
537 | 142 | "section": self.section.name, | ||
538 | 143 | "priority": self.priority.name, | ||
539 | 144 | } | ||
540 | 145 | |||
541 | 134 | @cachedproperty | 146 | @cachedproperty |
542 | 135 | def files(self): | 147 | def files(self): |
543 | 136 | return list( | 148 | return list( |
544 | @@ -201,4 +213,3 @@ | |||
545 | 201 | def binary_package_version(self): | 213 | def binary_package_version(self): |
546 | 202 | """See `IBinaryPackageReleaseDownloadCount`.""" | 214 | """See `IBinaryPackageReleaseDownloadCount`.""" |
547 | 203 | return self.binary_package_release.version | 215 | return self.binary_package_release.version |
548 | 204 | |||
549 | 205 | 216 | ||
550 | === modified file 'lib/lp/soyuz/model/queue.py' | |||
551 | --- lib/lp/soyuz/model/queue.py 2012-05-25 15:31:50 +0000 | |||
552 | +++ lib/lp/soyuz/model/queue.py 2012-06-08 14:30:37 +0000 | |||
553 | @@ -13,6 +13,7 @@ | |||
554 | 13 | 'PackageUploadSet', | 13 | 'PackageUploadSet', |
555 | 14 | ] | 14 | ] |
556 | 15 | 15 | ||
557 | 16 | from itertools import chain | ||
558 | 16 | import os | 17 | import os |
559 | 17 | import shutil | 18 | import shutil |
560 | 18 | import StringIO | 19 | import StringIO |
561 | @@ -50,8 +51,10 @@ | |||
562 | 50 | from lp.registry.interfaces.pocket import PackagePublishingPocket | 51 | from lp.registry.interfaces.pocket import PackagePublishingPocket |
563 | 51 | from lp.registry.model.sourcepackagename import SourcePackageName | 52 | from lp.registry.model.sourcepackagename import SourcePackageName |
564 | 52 | from lp.services.config import config | 53 | from lp.services.config import config |
565 | 54 | from lp.services.database.bulk import load_referencing | ||
566 | 53 | from lp.services.database.constants import UTC_NOW | 55 | from lp.services.database.constants import UTC_NOW |
567 | 54 | from lp.services.database.datetimecol import UtcDateTimeCol | 56 | from lp.services.database.datetimecol import UtcDateTimeCol |
568 | 57 | from lp.services.database.decoratedresultset import DecoratedResultSet | ||
569 | 55 | from lp.services.database.enumcol import EnumCol | 58 | from lp.services.database.enumcol import EnumCol |
570 | 56 | from lp.services.database.lpstorm import ( | 59 | from lp.services.database.lpstorm import ( |
571 | 57 | IMasterStore, | 60 | IMasterStore, |
572 | @@ -61,22 +64,34 @@ | |||
573 | 61 | SQLBase, | 64 | SQLBase, |
574 | 62 | sqlvalues, | 65 | sqlvalues, |
575 | 63 | ) | 66 | ) |
576 | 67 | from lp.services.librarian.browser import ProxiedLibraryFileAlias | ||
577 | 64 | from lp.services.librarian.interfaces.client import DownloadFailed | 68 | from lp.services.librarian.interfaces.client import DownloadFailed |
578 | 65 | from lp.services.librarian.model import LibraryFileAlias | 69 | from lp.services.librarian.model import LibraryFileAlias |
579 | 66 | from lp.services.librarian.utils import copy_and_close | 70 | from lp.services.librarian.utils import copy_and_close |
580 | 67 | from lp.services.mail.signedmessage import strip_pgp_signature | 71 | from lp.services.mail.signedmessage import strip_pgp_signature |
582 | 68 | from lp.services.propertycache import cachedproperty | 72 | from lp.services.propertycache import ( |
583 | 73 | cachedproperty, | ||
584 | 74 | get_property_cache, | ||
585 | 75 | ) | ||
586 | 69 | from lp.soyuz.adapters.notification import notify | 76 | from lp.soyuz.adapters.notification import notify |
587 | 70 | from lp.soyuz.adapters.overrides import SourceOverride | 77 | from lp.soyuz.adapters.overrides import SourceOverride |
588 | 71 | from lp.soyuz.enums import ( | 78 | from lp.soyuz.enums import ( |
589 | 72 | PackageUploadCustomFormat, | 79 | PackageUploadCustomFormat, |
590 | 73 | PackageUploadStatus, | 80 | PackageUploadStatus, |
591 | 74 | ) | 81 | ) |
593 | 75 | from lp.soyuz.interfaces.archive import MAIN_ARCHIVE_PURPOSES | 82 | from lp.soyuz.interfaces.archive import ( |
594 | 83 | ComponentNotFound, | ||
595 | 84 | MAIN_ARCHIVE_PURPOSES, | ||
596 | 85 | PriorityNotFound, | ||
597 | 86 | SectionNotFound, | ||
598 | 87 | ) | ||
599 | 88 | from lp.soyuz.interfaces.archivepermission import IArchivePermissionSet | ||
600 | 89 | from lp.soyuz.interfaces.component import IComponentSet | ||
601 | 76 | from lp.soyuz.interfaces.packagecopyjob import IPackageCopyJobSource | 90 | from lp.soyuz.interfaces.packagecopyjob import IPackageCopyJobSource |
602 | 77 | from lp.soyuz.interfaces.publishing import ( | 91 | from lp.soyuz.interfaces.publishing import ( |
603 | 78 | IPublishingSet, | 92 | IPublishingSet, |
604 | 79 | ISourcePackagePublishingHistory, | 93 | ISourcePackagePublishingHistory, |
605 | 94 | name_priority_map, | ||
606 | 80 | ) | 95 | ) |
607 | 81 | from lp.soyuz.interfaces.queue import ( | 96 | from lp.soyuz.interfaces.queue import ( |
608 | 82 | IPackageUpload, | 97 | IPackageUpload, |
609 | @@ -86,11 +101,13 @@ | |||
610 | 86 | IPackageUploadSet, | 101 | IPackageUploadSet, |
611 | 87 | IPackageUploadSource, | 102 | IPackageUploadSource, |
612 | 88 | NonBuildableSourceUploadError, | 103 | NonBuildableSourceUploadError, |
613 | 104 | QueueAdminUnauthorizedError, | ||
614 | 89 | QueueBuildAcceptError, | 105 | QueueBuildAcceptError, |
615 | 90 | QueueInconsistentStateError, | 106 | QueueInconsistentStateError, |
616 | 91 | QueueSourceAcceptError, | 107 | QueueSourceAcceptError, |
617 | 92 | QueueStateWriteProtectedError, | 108 | QueueStateWriteProtectedError, |
618 | 93 | ) | 109 | ) |
619 | 110 | from lp.soyuz.interfaces.section import ISectionSet | ||
620 | 94 | from lp.soyuz.model.binarypackagename import BinaryPackageName | 111 | from lp.soyuz.model.binarypackagename import BinaryPackageName |
621 | 95 | from lp.soyuz.model.binarypackagerelease import BinaryPackageRelease | 112 | from lp.soyuz.model.binarypackagerelease import BinaryPackageRelease |
622 | 96 | from lp.soyuz.pas import BuildDaemonPackagesArchSpecific | 113 | from lp.soyuz.pas import BuildDaemonPackagesArchSpecific |
623 | @@ -230,11 +247,44 @@ | |||
624 | 230 | 247 | ||
625 | 231 | # Join this table to the PackageUploadBuild and the | 248 | # Join this table to the PackageUploadBuild and the |
626 | 232 | # PackageUploadSource objects which are related. | 249 | # PackageUploadSource objects which are related. |
628 | 233 | sources = SQLMultipleJoin('PackageUploadSource', | 250 | _sources = SQLMultipleJoin('PackageUploadSource', |
629 | 251 | joinColumn='packageupload') | ||
630 | 252 | # Does not include source builds. | ||
631 | 253 | _builds = SQLMultipleJoin('PackageUploadBuild', | ||
632 | 234 | joinColumn='packageupload') | 254 | joinColumn='packageupload') |
636 | 235 | # Does not include source builds. | 255 | |
637 | 236 | builds = SQLMultipleJoin('PackageUploadBuild', | 256 | @cachedproperty |
638 | 237 | joinColumn='packageupload') | 257 | def sources(self): |
639 | 258 | return list(self._sources) | ||
640 | 259 | |||
641 | 260 | def sourceFileUrls(self): | ||
642 | 261 | """See `IPackageUpload`.""" | ||
643 | 262 | if self.contains_source: | ||
644 | 263 | return [ | ||
645 | 264 | ProxiedLibraryFileAlias( | ||
646 | 265 | file.libraryfile, self.archive).http_url | ||
647 | 266 | for file in self.sourcepackagerelease.files] | ||
648 | 267 | else: | ||
649 | 268 | return [] | ||
650 | 269 | |||
651 | 270 | @cachedproperty | ||
652 | 271 | def builds(self): | ||
653 | 272 | return list(self._builds) | ||
654 | 273 | |||
655 | 274 | def binaryFileUrls(self): | ||
656 | 275 | """See `IPackageUpload`.""" | ||
657 | 276 | return [ | ||
658 | 277 | ProxiedLibraryFileAlias(file.libraryfile, self.archive).http_url | ||
659 | 278 | for build in self.builds | ||
660 | 279 | for bpr in build.build.binarypackages | ||
661 | 280 | for file in bpr.files] | ||
662 | 281 | |||
663 | 282 | @property | ||
664 | 283 | def changes_file_url(self): | ||
665 | 284 | if self.changesfile is not None: | ||
666 | 285 | return self.changesfile.getURL() | ||
667 | 286 | else: | ||
668 | 287 | return None | ||
669 | 238 | 288 | ||
670 | 239 | def getSourceBuild(self): | 289 | def getSourceBuild(self): |
671 | 240 | #avoid circular import | 290 | #avoid circular import |
672 | @@ -250,8 +300,12 @@ | |||
673 | 250 | PackageUploadSource.packageupload == self.id).one() | 300 | PackageUploadSource.packageupload == self.id).one() |
674 | 251 | 301 | ||
675 | 252 | # Also the custom files associated with the build. | 302 | # Also the custom files associated with the build. |
678 | 253 | customfiles = SQLMultipleJoin('PackageUploadCustom', | 303 | _customfiles = SQLMultipleJoin('PackageUploadCustom', |
679 | 254 | joinColumn='packageupload') | 304 | joinColumn='packageupload') |
680 | 305 | |||
681 | 306 | @cachedproperty | ||
682 | 307 | def customfiles(self): | ||
683 | 308 | return list(self._customfiles) | ||
684 | 255 | 309 | ||
685 | 256 | @property | 310 | @property |
686 | 257 | def custom_file_urls(self): | 311 | def custom_file_urls(self): |
687 | @@ -259,6 +313,18 @@ | |||
688 | 259 | return tuple( | 313 | return tuple( |
689 | 260 | file.libraryfilealias.getURL() for file in self.customfiles) | 314 | file.libraryfilealias.getURL() for file in self.customfiles) |
690 | 261 | 315 | ||
691 | 316 | def customFileUrls(self): | ||
692 | 317 | """See `IPackageUpload`.""" | ||
693 | 318 | return [ | ||
694 | 319 | ProxiedLibraryFileAlias( | ||
695 | 320 | file.libraryfilealias, self.archive).http_url | ||
696 | 321 | for file in self.customfiles] | ||
697 | 322 | |||
698 | 323 | def getBinaryProperties(self): | ||
699 | 324 | """See `IPackageUpload`.""" | ||
700 | 325 | return list(chain.from_iterable( | ||
701 | 326 | build.binaries for build in self.builds)) | ||
702 | 327 | |||
703 | 262 | def setNew(self): | 328 | def setNew(self): |
704 | 263 | """See `IPackageUpload`.""" | 329 | """See `IPackageUpload`.""" |
705 | 264 | if self.status == PackageUploadStatus.NEW: | 330 | if self.status == PackageUploadStatus.NEW: |
706 | @@ -544,7 +610,7 @@ | |||
707 | 544 | def acceptFromCopy(self): | 610 | def acceptFromCopy(self): |
708 | 545 | """See `IPackageUpload`.""" | 611 | """See `IPackageUpload`.""" |
709 | 546 | assert self.is_delayed_copy, 'Can only process delayed-copies.' | 612 | assert self.is_delayed_copy, 'Can only process delayed-copies.' |
711 | 547 | assert self.sources.count() == 1, ( | 613 | assert self._sources.count() == 1, ( |
712 | 548 | 'Source is mandatory for delayed copies.') | 614 | 'Source is mandatory for delayed copies.') |
713 | 549 | self.setAccepted() | 615 | self.setAccepted() |
714 | 550 | 616 | ||
715 | @@ -581,7 +647,7 @@ | |||
716 | 581 | 647 | ||
717 | 582 | def _isSingleSourceUpload(self): | 648 | def _isSingleSourceUpload(self): |
718 | 583 | """Return True if this upload contains only a single source.""" | 649 | """Return True if this upload contains only a single source.""" |
720 | 584 | return ((self.sources.count() == 1) and | 650 | return ((self._sources.count() == 1) and |
721 | 585 | (not bool(self.builds)) and | 651 | (not bool(self.builds)) and |
722 | 586 | (not bool(self.customfiles))) | 652 | (not bool(self.customfiles))) |
723 | 587 | 653 | ||
724 | @@ -590,12 +656,17 @@ | |||
725 | 590 | @cachedproperty | 656 | @cachedproperty |
726 | 591 | def contains_source(self): | 657 | def contains_source(self): |
727 | 592 | """See `IPackageUpload`.""" | 658 | """See `IPackageUpload`.""" |
729 | 593 | return self.sources | 659 | return bool(self.sources) |
730 | 594 | 660 | ||
731 | 595 | @cachedproperty | 661 | @cachedproperty |
732 | 596 | def contains_build(self): | 662 | def contains_build(self): |
733 | 597 | """See `IPackageUpload`.""" | 663 | """See `IPackageUpload`.""" |
735 | 598 | return self.builds | 664 | return bool(self.builds) |
736 | 665 | |||
737 | 666 | @cachedproperty | ||
738 | 667 | def contains_copy(self): | ||
739 | 668 | """See `IPackageUpload`.""" | ||
740 | 669 | return self.package_copy_job_id is not None | ||
741 | 599 | 670 | ||
742 | 600 | @cachedproperty | 671 | @cachedproperty |
743 | 601 | def from_build(self): | 672 | def from_build(self): |
744 | @@ -804,18 +875,21 @@ | |||
745 | 804 | 875 | ||
746 | 805 | def addSource(self, spr): | 876 | def addSource(self, spr): |
747 | 806 | """See `IPackageUpload`.""" | 877 | """See `IPackageUpload`.""" |
748 | 878 | del get_property_cache(self).sources | ||
749 | 807 | return PackageUploadSource( | 879 | return PackageUploadSource( |
750 | 808 | packageupload=self, | 880 | packageupload=self, |
751 | 809 | sourcepackagerelease=spr.id) | 881 | sourcepackagerelease=spr.id) |
752 | 810 | 882 | ||
753 | 811 | def addBuild(self, build): | 883 | def addBuild(self, build): |
754 | 812 | """See `IPackageUpload`.""" | 884 | """See `IPackageUpload`.""" |
755 | 885 | del get_property_cache(self).builds | ||
756 | 813 | return PackageUploadBuild( | 886 | return PackageUploadBuild( |
757 | 814 | packageupload=self, | 887 | packageupload=self, |
758 | 815 | build=build.id) | 888 | build=build.id) |
759 | 816 | 889 | ||
760 | 817 | def addCustom(self, library_file, custom_type): | 890 | def addCustom(self, library_file, custom_type): |
761 | 818 | """See `IPackageUpload`.""" | 891 | """See `IPackageUpload`.""" |
762 | 892 | del get_property_cache(self).customfiles | ||
763 | 819 | return PackageUploadCustom( | 893 | return PackageUploadCustom( |
764 | 820 | packageupload=self, | 894 | packageupload=self, |
765 | 821 | libraryfilealias=library_file.id, | 895 | libraryfilealias=library_file.id, |
766 | @@ -915,6 +989,33 @@ | |||
767 | 915 | """See `IPackageUpload`.""" | 989 | """See `IPackageUpload`.""" |
768 | 916 | return getUtility(IPackageCopyJobSource).wrap(self.package_copy_job) | 990 | return getUtility(IPackageCopyJobSource).wrap(self.package_copy_job) |
769 | 917 | 991 | ||
770 | 992 | def _nameToComponent(self, component): | ||
771 | 993 | """Helper to convert a possible string component to IComponent.""" | ||
772 | 994 | try: | ||
773 | 995 | if isinstance(component, basestring): | ||
774 | 996 | component = getUtility(IComponentSet)[component] | ||
775 | 997 | return component | ||
776 | 998 | except NotFoundError: | ||
777 | 999 | raise ComponentNotFound(component) | ||
778 | 1000 | |||
779 | 1001 | def _nameToSection(self, section): | ||
780 | 1002 | """Helper to convert a possible string section to ISection.""" | ||
781 | 1003 | try: | ||
782 | 1004 | if isinstance(section, basestring): | ||
783 | 1005 | section = getUtility(ISectionSet)[section] | ||
784 | 1006 | return section | ||
785 | 1007 | except NotFoundError: | ||
786 | 1008 | raise SectionNotFound(section) | ||
787 | 1009 | |||
788 | 1010 | def _nameToPriority(self, priority): | ||
789 | 1011 | """Helper to convert a possible string priority to its enum.""" | ||
790 | 1012 | try: | ||
791 | 1013 | if isinstance(priority, basestring): | ||
792 | 1014 | priority = name_priority_map[priority] | ||
793 | 1015 | return priority | ||
794 | 1016 | except KeyError: | ||
795 | 1017 | raise PriorityNotFound(priority) | ||
796 | 1018 | |||
797 | 918 | def _overrideSyncSource(self, new_component, new_section, | 1019 | def _overrideSyncSource(self, new_component, new_section, |
798 | 919 | allowed_components): | 1020 | allowed_components): |
799 | 920 | """Override source on the upload's `PackageCopyJob`, if any.""" | 1021 | """Override source on the upload's `PackageCopyJob`, if any.""" |
800 | @@ -925,7 +1026,7 @@ | |||
801 | 925 | allowed_component_names = [ | 1026 | allowed_component_names = [ |
802 | 926 | component.name for component in allowed_components] | 1027 | component.name for component in allowed_components] |
803 | 927 | if copy_job.component_name not in allowed_component_names: | 1028 | if copy_job.component_name not in allowed_component_names: |
805 | 928 | raise QueueInconsistentStateError( | 1029 | raise QueueAdminUnauthorizedError( |
806 | 929 | "No rights to override from %s" % copy_job.component_name) | 1030 | "No rights to override from %s" % copy_job.component_name) |
807 | 930 | copy_job.addSourceOverride(SourceOverride( | 1031 | copy_job.addSourceOverride(SourceOverride( |
808 | 931 | copy_job.package_name, new_component, new_section)) | 1032 | copy_job.package_name, new_component, new_section)) |
809 | @@ -942,7 +1043,7 @@ | |||
810 | 942 | if old_component not in allowed_components: | 1043 | if old_component not in allowed_components: |
811 | 943 | # The old component is not in the list of allowed components | 1044 | # The old component is not in the list of allowed components |
812 | 944 | # to override. | 1045 | # to override. |
814 | 945 | raise QueueInconsistentStateError( | 1046 | raise QueueAdminUnauthorizedError( |
815 | 946 | "No rights to override from %s" % old_component.name) | 1047 | "No rights to override from %s" % old_component.name) |
816 | 947 | source.sourcepackagerelease.override( | 1048 | source.sourcepackagerelease.override( |
817 | 948 | component=new_component, section=new_section) | 1049 | component=new_component, section=new_section) |
818 | @@ -955,14 +1056,29 @@ | |||
819 | 955 | 1056 | ||
820 | 956 | return made_changes | 1057 | return made_changes |
821 | 957 | 1058 | ||
823 | 958 | def overrideSource(self, new_component, new_section, allowed_components): | 1059 | def overrideSource(self, new_component=None, new_section=None, |
824 | 1060 | allowed_components=None, user=None): | ||
825 | 959 | """See `IPackageUpload`.""" | 1061 | """See `IPackageUpload`.""" |
826 | 960 | if new_component is None and new_section is None: | 1062 | if new_component is None and new_section is None: |
827 | 961 | # Nothing needs overriding, bail out. | 1063 | # Nothing needs overriding, bail out. |
828 | 962 | return False | 1064 | return False |
829 | 963 | 1065 | ||
830 | 1066 | new_component = self._nameToComponent(new_component) | ||
831 | 1067 | new_section = self._nameToSection(new_section) | ||
832 | 1068 | |||
833 | 1069 | if allowed_components is None and user is not None: | ||
834 | 1070 | # Get a list of components for which the user has rights to | ||
835 | 1071 | # override to or from. | ||
836 | 1072 | permission_set = getUtility(IArchivePermissionSet) | ||
837 | 1073 | permissions = permission_set.componentsForQueueAdmin( | ||
838 | 1074 | self.distroseries.main_archive, user) | ||
839 | 1075 | allowed_components = set( | ||
840 | 1076 | permission.component for permission in permissions) | ||
841 | 1077 | assert allowed_components is not None, ( | ||
842 | 1078 | "Must provide allowed_components for non-webservice calls.") | ||
843 | 1079 | |||
844 | 964 | if new_component not in list(allowed_components) + [None]: | 1080 | if new_component not in list(allowed_components) + [None]: |
846 | 965 | raise QueueInconsistentStateError( | 1081 | raise QueueAdminUnauthorizedError( |
847 | 966 | "No rights to override to %s" % new_component.name) | 1082 | "No rights to override to %s" % new_component.name) |
848 | 967 | 1083 | ||
849 | 968 | return ( | 1084 | return ( |
850 | @@ -971,35 +1087,95 @@ | |||
851 | 971 | self._overrideNonSyncSource( | 1087 | self._overrideNonSyncSource( |
852 | 972 | new_component, new_section, allowed_components)) | 1088 | new_component, new_section, allowed_components)) |
853 | 973 | 1089 | ||
856 | 974 | def overrideBinaries(self, new_component, new_section, new_priority, | 1090 | def _filterBinaryChanges(self, changes): |
857 | 975 | allowed_components): | 1091 | """Process a binary changes mapping into a more convenient form.""" |
858 | 1092 | changes_by_name = {} | ||
859 | 1093 | changes_for_all = None | ||
860 | 1094 | |||
861 | 1095 | for change in changes: | ||
862 | 1096 | filtered_change = {} | ||
863 | 1097 | if "component" in change: | ||
864 | 1098 | filtered_change["component"] = self._nameToComponent( | ||
865 | 1099 | change["component"]) | ||
866 | 1100 | if "section" in change: | ||
867 | 1101 | filtered_change["section"] = self._nameToSection( | ||
868 | 1102 | change["section"]) | ||
869 | 1103 | if "priority" in change: | ||
870 | 1104 | filtered_change["priority"] = self._nameToPriority( | ||
871 | 1105 | change["priority"]) | ||
872 | 1106 | |||
873 | 1107 | if "name" in change: | ||
874 | 1108 | changes_by_name[change["name"]] = filtered_change | ||
875 | 1109 | else: | ||
876 | 1110 | # Changes with no "name" item provide a default for all | ||
877 | 1111 | # binaries. | ||
878 | 1112 | changes_for_all = filtered_change | ||
879 | 1113 | |||
880 | 1114 | return changes_by_name, changes_for_all | ||
881 | 1115 | |||
882 | 1116 | def overrideBinaries(self, changes, allowed_components=None, user=None): | ||
883 | 976 | """See `IPackageUpload`.""" | 1117 | """See `IPackageUpload`.""" |
884 | 977 | if not self.contains_build: | 1118 | if not self.contains_build: |
885 | 978 | return False | 1119 | return False |
886 | 979 | 1120 | ||
889 | 980 | if (new_component is None and new_section is None and | 1121 | if not changes: |
888 | 981 | new_priority is None): | ||
890 | 982 | # Nothing needs overriding, bail out. | 1122 | # Nothing needs overriding, bail out. |
891 | 983 | return False | 1123 | return False |
892 | 984 | 1124 | ||
897 | 985 | if new_component not in allowed_components: | 1125 | if allowed_components is None and user is not None: |
898 | 986 | raise QueueInconsistentStateError( | 1126 | # Get a list of components for which the user has rights to |
899 | 987 | "No rights to override to %s" % new_component.name) | 1127 | # override to or from. |
900 | 988 | 1128 | permission_set = getUtility(IArchivePermissionSet) | |
901 | 1129 | permissions = permission_set.componentsForQueueAdmin( | ||
902 | 1130 | self.distroseries.main_archive, user) | ||
903 | 1131 | allowed_components = set( | ||
904 | 1132 | permission.component for permission in permissions) | ||
905 | 1133 | assert allowed_components is not None, ( | ||
906 | 1134 | "Must provide allowed_components for non-webservice calls.") | ||
907 | 1135 | |||
908 | 1136 | changes_by_name, changes_for_all = self._filterBinaryChanges(changes) | ||
909 | 1137 | |||
910 | 1138 | new_components = set() | ||
911 | 1139 | for change in changes_by_name.values(): | ||
912 | 1140 | if "component" in change: | ||
913 | 1141 | new_components.add(change["component"]) | ||
914 | 1142 | if changes_for_all is not None and "component" in changes_for_all: | ||
915 | 1143 | new_components.add(changes_for_all["component"]) | ||
916 | 1144 | disallowed_components = sorted( | ||
917 | 1145 | component.name | ||
918 | 1146 | for component in new_components.difference(allowed_components)) | ||
919 | 1147 | if disallowed_components: | ||
920 | 1148 | raise QueueAdminUnauthorizedError( | ||
921 | 1149 | "No rights to override to %s" % | ||
922 | 1150 | ", ".join(disallowed_components)) | ||
923 | 1151 | |||
924 | 1152 | made_changes = False | ||
925 | 989 | for build in self.builds: | 1153 | for build in self.builds: |
926 | 1154 | # See if the new component requires a new archive on the build. | ||
927 | 1155 | for component in new_components: | ||
928 | 1156 | distroarchseries = build.build.distro_arch_series | ||
929 | 1157 | distribution = distroarchseries.distroseries.distribution | ||
930 | 1158 | new_archive = distribution.getArchiveByComponent( | ||
931 | 1159 | component.name) | ||
932 | 1160 | if new_archive != build.build.archive: | ||
933 | 1161 | raise QueueInconsistentStateError( | ||
934 | 1162 | "Overriding component to '%s' failed because it " | ||
935 | 1163 | "would require a new archive." % component.name) | ||
936 | 1164 | |||
937 | 990 | for binarypackage in build.build.binarypackages: | 1165 | for binarypackage in build.build.binarypackages: |
948 | 991 | if binarypackage.component not in allowed_components: | 1166 | change = changes_by_name.get( |
949 | 992 | # The old or the new component is not in the list of | 1167 | binarypackage.name, changes_for_all) |
950 | 993 | # allowed components to override. | 1168 | if change is not None: |
951 | 994 | raise QueueInconsistentStateError( | 1169 | if binarypackage.component not in allowed_components: |
952 | 995 | "No rights to override from %s" % ( | 1170 | # The old component is not in the list of allowed |
953 | 996 | binarypackage.component.name)) | 1171 | # components to override. |
954 | 997 | binarypackage.override( | 1172 | raise QueueAdminUnauthorizedError( |
955 | 998 | component=new_component, | 1173 | "No rights to override from %s" % ( |
956 | 999 | section=new_section, | 1174 | binarypackage.component.name)) |
957 | 1000 | priority=new_priority) | 1175 | binarypackage.override(**change) |
958 | 1176 | made_changes = True | ||
959 | 1001 | 1177 | ||
961 | 1002 | return bool(self.builds) | 1178 | return made_changes |
962 | 1003 | 1179 | ||
963 | 1004 | 1180 | ||
964 | 1005 | class PackageUploadBuild(SQLBase): | 1181 | class PackageUploadBuild(SQLBase): |
965 | @@ -1014,6 +1190,12 @@ | |||
966 | 1014 | 1190 | ||
967 | 1015 | build = ForeignKey(dbName='build', foreignKey='BinaryPackageBuild') | 1191 | build = ForeignKey(dbName='build', foreignKey='BinaryPackageBuild') |
968 | 1016 | 1192 | ||
969 | 1193 | @property | ||
970 | 1194 | def binaries(self): | ||
971 | 1195 | """See `IPackageUploadBuild`.""" | ||
972 | 1196 | for binary in self.build.binarypackages: | ||
973 | 1197 | yield binary.properties | ||
974 | 1198 | |||
975 | 1017 | def checkComponentAndSection(self): | 1199 | def checkComponentAndSection(self): |
976 | 1018 | """See `IPackageUploadBuild`.""" | 1200 | """See `IPackageUploadBuild`.""" |
977 | 1019 | distroseries = self.packageupload.distroseries | 1201 | distroseries = self.packageupload.distroseries |
978 | @@ -1622,7 +1804,30 @@ | |||
979 | 1622 | PackageUpload.distroseries == distroseries, | 1804 | PackageUpload.distroseries == distroseries, |
980 | 1623 | *conditions) | 1805 | *conditions) |
981 | 1624 | query = query.order_by(Desc(PackageUpload.id)) | 1806 | query = query.order_by(Desc(PackageUpload.id)) |
983 | 1625 | return query.config(distinct=True) | 1807 | query = query.config(distinct=True) |
984 | 1808 | |||
985 | 1809 | def preload_hook(rows): | ||
986 | 1810 | puses = load_referencing( | ||
987 | 1811 | PackageUploadSource, rows, ["packageuploadID"]) | ||
988 | 1812 | pubs = load_referencing( | ||
989 | 1813 | PackageUploadBuild, rows, ["packageuploadID"]) | ||
990 | 1814 | pucs = load_referencing( | ||
991 | 1815 | PackageUploadCustom, rows, ["packageuploadID"]) | ||
992 | 1816 | |||
993 | 1817 | for pu in rows: | ||
994 | 1818 | cache = get_property_cache(pu) | ||
995 | 1819 | cache.sources = [] | ||
996 | 1820 | cache.builds = [] | ||
997 | 1821 | cache.customfiles = [] | ||
998 | 1822 | |||
999 | 1823 | for pus in puses: | ||
1000 | 1824 | get_property_cache(pus.packageupload).sources.append(pus) | ||
1001 | 1825 | for pub in pubs: | ||
1002 | 1826 | get_property_cache(pub.packageupload).builds.append(pub) | ||
1003 | 1827 | for puc in pucs: | ||
1004 | 1828 | get_property_cache(puc.packageupload).customfiles.append(puc) | ||
1005 | 1829 | |||
1006 | 1830 | return DecoratedResultSet(query, pre_iter_hook=preload_hook) | ||
1007 | 1626 | 1831 | ||
1008 | 1627 | def getBuildByBuildIDs(self, build_ids): | 1832 | def getBuildByBuildIDs(self, build_ids): |
1009 | 1628 | """See `IPackageUploadSet`.""" | 1833 | """See `IPackageUploadSet`.""" |
1010 | 1629 | 1834 | ||
1011 | === modified file 'lib/lp/soyuz/scripts/queue.py' | |||
1012 | --- lib/lp/soyuz/scripts/queue.py 2012-02-10 10:50:03 +0000 | |||
1013 | +++ lib/lp/soyuz/scripts/queue.py 2012-06-08 14:30:37 +0000 | |||
1014 | @@ -1,4 +1,4 @@ | |||
1016 | 1 | # Copyright 2009-2011 Canonical Ltd. This software is licensed under the | 1 | # Copyright 2009-2012 Canonical Ltd. This software is licensed under the |
1017 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). |
1018 | 3 | 3 | ||
1019 | 4 | # pylint: disable-msg=W0231 | 4 | # pylint: disable-msg=W0231 |
1020 | @@ -260,8 +260,8 @@ | |||
1021 | 260 | False: '-', | 260 | False: '-', |
1022 | 261 | } | 261 | } |
1023 | 262 | return ( | 262 | return ( |
1026 | 263 | source_tag[bool(queue_item.contains_source)] + | 263 | source_tag[queue_item.contains_source] + |
1027 | 264 | binary_tag[bool(queue_item.contains_build)]) | 264 | binary_tag[queue_item.contains_build]) |
1028 | 265 | 265 | ||
1029 | 266 | def displayItem(self, queue_item): | 266 | def displayItem(self, queue_item): |
1030 | 267 | """Display one line summary of the queue item provided.""" | 267 | """Display one line summary of the queue item provided.""" |
1031 | 268 | 268 | ||
1032 | === modified file 'lib/lp/soyuz/stories/webservice/xx-packageupload.txt' | |||
1033 | --- lib/lp/soyuz/stories/webservice/xx-packageupload.txt 2012-05-30 14:12:44 +0000 | |||
1034 | +++ lib/lp/soyuz/stories/webservice/xx-packageupload.txt 2012-06-08 14:30:37 +0000 | |||
1035 | @@ -31,6 +31,19 @@ | |||
1036 | 31 | self_link: u'http://.../ubuntu/warty/+upload/11' | 31 | self_link: u'http://.../ubuntu/warty/+upload/11' |
1037 | 32 | status: u'Done' | 32 | status: u'Done' |
1038 | 33 | 33 | ||
1039 | 34 | getPackageUploads can filter on package names. | ||
1040 | 35 | |||
1041 | 36 | >>> uploads = webservice.named_get( | ||
1042 | 37 | ... warty['self_link'], 'getPackageUploads', | ||
1043 | 38 | ... name='mozilla').jsonBody() | ||
1044 | 39 | >>> len(uploads['entries']) | ||
1045 | 40 | 1 | ||
1046 | 41 | >>> uploads = webservice.named_get( | ||
1047 | 42 | ... warty['self_link'], 'getPackageUploads', | ||
1048 | 43 | ... name='missing').jsonBody() | ||
1049 | 44 | >>> len(uploads['entries']) | ||
1050 | 45 | 0 | ||
1051 | 46 | |||
1052 | 34 | 47 | ||
1053 | 35 | Retrieving Static Translation Files | 48 | Retrieving Static Translation Files |
1054 | 36 | =================================== | 49 | =================================== |
1055 | 37 | 50 | ||
1056 | === modified file 'lib/lp/soyuz/tests/test_distroseriesqueue_ddtp_tarball.py' | |||
1057 | --- lib/lp/soyuz/tests/test_distroseriesqueue_ddtp_tarball.py 2012-05-25 13:28:31 +0000 | |||
1058 | +++ lib/lp/soyuz/tests/test_distroseriesqueue_ddtp_tarball.py 2012-06-08 14:30:37 +0000 | |||
1059 | @@ -27,10 +27,6 @@ | |||
1060 | 27 | getPolicy, | 27 | getPolicy, |
1061 | 28 | ) | 28 | ) |
1062 | 29 | from lp.services.log.logger import DevNullLogger | 29 | from lp.services.log.logger import DevNullLogger |
1063 | 30 | from lp.soyuz.scripts.queue import ( | ||
1064 | 31 | CommandRunner, | ||
1065 | 32 | name_queue_map, | ||
1066 | 33 | ) | ||
1067 | 34 | from lp.soyuz.tests.test_publishing import TestNativePublishingBase | 30 | from lp.soyuz.tests.test_publishing import TestNativePublishingBase |
1068 | 35 | from lp.testing.gpgkeys import import_public_test_keys | 31 | from lp.testing.gpgkeys import import_public_test_keys |
1069 | 36 | 32 | ||
1070 | @@ -68,30 +64,6 @@ | |||
1071 | 68 | def test_accepts_correct_upload(self): | 64 | def test_accepts_correct_upload(self): |
1072 | 69 | self.uploadTestData("20060728") | 65 | self.uploadTestData("20060728") |
1073 | 70 | 66 | ||
1074 | 71 | def runQueueCommand(self, queue_name, args): | ||
1075 | 72 | def null_display(text): | ||
1076 | 73 | pass | ||
1077 | 74 | |||
1078 | 75 | queue = name_queue_map[queue_name] | ||
1079 | 76 | runner = CommandRunner( | ||
1080 | 77 | queue, "ubuntutest", "breezy-autotest", True, None, None, None, | ||
1081 | 78 | display=null_display) | ||
1082 | 79 | runner.execute(args) | ||
1083 | 80 | |||
1084 | 81 | def test_queue_tool_behaviour(self): | ||
1085 | 82 | # The queue tool can fetch ddtp-tarball uploads. | ||
1086 | 83 | self.uploadTestData("20060728") | ||
1087 | 84 | # Make sure that we can use the librarian files. | ||
1088 | 85 | transaction.commit() | ||
1089 | 86 | # Fetch upload into a temporary directory. | ||
1090 | 87 | self.useTempDir() | ||
1091 | 88 | self.runQueueCommand("accepted", ["fetch", "trans"]) | ||
1092 | 89 | expected_entries = [ | ||
1093 | 90 | "translations-main_20060728_all.changes", | ||
1094 | 91 | "translations_main_20060728.tar.gz", | ||
1095 | 92 | ] | ||
1096 | 93 | self.assertContentEqual(expected_entries, os.listdir(".")) | ||
1097 | 94 | |||
1098 | 95 | def test_publish(self): | 67 | def test_publish(self): |
1099 | 96 | upload = self.uploadTestData("20060728") | 68 | upload = self.uploadTestData("20060728") |
1100 | 97 | transaction.commit() | 69 | transaction.commit() |
1101 | 98 | 70 | ||
1102 | === modified file 'lib/lp/soyuz/tests/test_distroseriesqueue_dist_upgrader.py' | |||
1103 | --- lib/lp/soyuz/tests/test_distroseriesqueue_dist_upgrader.py 2012-05-25 13:27:41 +0000 | |||
1104 | +++ lib/lp/soyuz/tests/test_distroseriesqueue_dist_upgrader.py 2012-06-08 14:30:37 +0000 | |||
1105 | @@ -23,10 +23,6 @@ | |||
1106 | 23 | ) | 23 | ) |
1107 | 24 | from lp.services.config import config | 24 | from lp.services.config import config |
1108 | 25 | from lp.services.log.logger import DevNullLogger | 25 | from lp.services.log.logger import DevNullLogger |
1109 | 26 | from lp.soyuz.scripts.queue import ( | ||
1110 | 27 | CommandRunner, | ||
1111 | 28 | name_queue_map, | ||
1112 | 29 | ) | ||
1113 | 30 | from lp.soyuz.tests.test_publishing import TestNativePublishingBase | 26 | from lp.soyuz.tests.test_publishing import TestNativePublishingBase |
1114 | 31 | from lp.testing.gpgkeys import import_public_test_keys | 27 | from lp.testing.gpgkeys import import_public_test_keys |
1115 | 32 | 28 | ||
1116 | @@ -69,37 +65,18 @@ | |||
1117 | 69 | def test_accepts_correct_upload(self): | 65 | def test_accepts_correct_upload(self): |
1118 | 70 | self.uploadTestData("20060302.0120") | 66 | self.uploadTestData("20060302.0120") |
1119 | 71 | 67 | ||
1133 | 72 | def runQueueCommand(self, queue_name, args): | 68 | def test_accept_reject(self): |
1134 | 73 | def null_display(text): | 69 | # We can accept and reject dist-upgrader uploads. |
1122 | 74 | pass | ||
1123 | 75 | |||
1124 | 76 | queue = name_queue_map[queue_name] | ||
1125 | 77 | runner = CommandRunner( | ||
1126 | 78 | queue, "ubuntutest", "breezy-autotest", True, None, None, None, | ||
1127 | 79 | display=null_display) | ||
1128 | 80 | runner.execute(args) | ||
1129 | 81 | |||
1130 | 82 | def test_queue_tool_behaviour(self): | ||
1131 | 83 | # The queue tool can accept, reject, and fetch dist-upgrader | ||
1132 | 84 | # uploads. See bug #54649. | ||
1135 | 85 | upload = self.uploadTestData("20060302.0120") | 70 | upload = self.uploadTestData("20060302.0120") |
1136 | 86 | # Make sure that we can use the librarian files. | 71 | # Make sure that we can use the librarian files. |
1137 | 87 | transaction.commit() | 72 | transaction.commit() |
1138 | 88 | # Reject from accepted queue (unlikely, would normally be from | 73 | # Reject from accepted queue (unlikely, would normally be from |
1139 | 89 | # unapproved or new). | 74 | # unapproved or new). |
1141 | 90 | self.runQueueCommand("accepted", ["reject", "dist"]) | 75 | upload.queue_root.rejectFromQueue(logger=self.logger) |
1142 | 91 | self.assertEqual("REJECTED", upload.queue_root.status.name) | 76 | self.assertEqual("REJECTED", upload.queue_root.status.name) |
1143 | 92 | # Accept from rejected queue (also unlikely, but only for testing). | 77 | # Accept from rejected queue (also unlikely, but only for testing). |
1145 | 93 | self.runQueueCommand("rejected", ["accept", "dist"]) | 78 | upload.queue_root.acceptFromQueue(logger=self.logger) |
1146 | 94 | self.assertEqual("ACCEPTED", upload.queue_root.status.name) | 79 | self.assertEqual("ACCEPTED", upload.queue_root.status.name) |
1147 | 95 | # Fetch upload into a temporary directory. | ||
1148 | 96 | self.useTempDir() | ||
1149 | 97 | self.runQueueCommand("accepted", ["fetch", "dist"]) | ||
1150 | 98 | expected_entries = [ | ||
1151 | 99 | "dist-upgrader_20060302.0120_all.changes", | ||
1152 | 100 | "dist-upgrader_20060302.0120_all.tar.gz", | ||
1153 | 101 | ] | ||
1154 | 102 | self.assertContentEqual(expected_entries, os.listdir(".")) | ||
1155 | 103 | 80 | ||
1156 | 104 | def test_bad_upload_remains_in_accepted(self): | 81 | def test_bad_upload_remains_in_accepted(self): |
1157 | 105 | # Bad dist-upgrader uploads remain in ACCEPTED. | 82 | # Bad dist-upgrader uploads remain in ACCEPTED. |
1158 | 106 | 83 | ||
1159 | === modified file 'lib/lp/soyuz/tests/test_packageupload.py' | |||
1160 | --- lib/lp/soyuz/tests/test_packageupload.py 2012-05-30 08:50:50 +0000 | |||
1161 | +++ lib/lp/soyuz/tests/test_packageupload.py 2012-06-08 14:30:37 +0000 | |||
1162 | @@ -12,9 +12,11 @@ | |||
1163 | 12 | BadRequest, | 12 | BadRequest, |
1164 | 13 | Unauthorized, | 13 | Unauthorized, |
1165 | 14 | ) | 14 | ) |
1166 | 15 | from testtools.matchers import Equals | ||
1167 | 15 | import transaction | 16 | import transaction |
1168 | 16 | from zope.component import getUtility | 17 | from zope.component import getUtility |
1169 | 17 | from zope.security.proxy import removeSecurityProxy | 18 | from zope.security.proxy import removeSecurityProxy |
1170 | 19 | from zope.schema import getFields | ||
1171 | 18 | 20 | ||
1172 | 19 | from lp.archivepublisher.interfaces.publisherconfig import IPublisherConfigSet | 21 | from lp.archivepublisher.interfaces.publisherconfig import IPublisherConfigSet |
1173 | 20 | from lp.archiveuploader.tests import datadir | 22 | from lp.archiveuploader.tests import datadir |
1174 | @@ -25,6 +27,7 @@ | |||
1175 | 25 | from lp.services.config import config | 27 | from lp.services.config import config |
1176 | 26 | from lp.services.database.lpstorm import IStore | 28 | from lp.services.database.lpstorm import IStore |
1177 | 27 | from lp.services.job.interfaces.job import JobStatus | 29 | from lp.services.job.interfaces.job import JobStatus |
1178 | 30 | from lp.services.librarian.browser import ProxiedLibraryFileAlias | ||
1179 | 28 | from lp.services.log.logger import BufferLogger | 31 | from lp.services.log.logger import BufferLogger |
1180 | 29 | from lp.services.mail import stub | 32 | from lp.services.mail import stub |
1181 | 30 | from lp.soyuz.adapters.overrides import SourceOverride | 33 | from lp.soyuz.adapters.overrides import SourceOverride |
1182 | @@ -37,7 +40,9 @@ | |||
1183 | 37 | from lp.soyuz.interfaces.archivepermission import IArchivePermissionSet | 40 | from lp.soyuz.interfaces.archivepermission import IArchivePermissionSet |
1184 | 38 | from lp.soyuz.interfaces.component import IComponentSet | 41 | from lp.soyuz.interfaces.component import IComponentSet |
1185 | 39 | from lp.soyuz.interfaces.queue import ( | 42 | from lp.soyuz.interfaces.queue import ( |
1186 | 43 | IPackageUpload, | ||
1187 | 40 | IPackageUploadSet, | 44 | IPackageUploadSet, |
1188 | 45 | QueueAdminUnauthorizedError, | ||
1189 | 41 | QueueInconsistentStateError, | 46 | QueueInconsistentStateError, |
1190 | 42 | ) | 47 | ) |
1191 | 43 | from lp.soyuz.interfaces.section import ISectionSet | 48 | from lp.soyuz.interfaces.section import ISectionSet |
1192 | @@ -48,6 +53,7 @@ | |||
1193 | 48 | api_url, | 53 | api_url, |
1194 | 49 | launchpadlib_for, | 54 | launchpadlib_for, |
1195 | 50 | person_logged_in, | 55 | person_logged_in, |
1196 | 56 | StormStatementRecorder, | ||
1197 | 51 | TestCaseWithFactory, | 57 | TestCaseWithFactory, |
1198 | 52 | ) | 58 | ) |
1199 | 53 | from lp.testing.dbuser import switch_dbuser | 59 | from lp.testing.dbuser import switch_dbuser |
1200 | @@ -55,7 +61,10 @@ | |||
1201 | 55 | LaunchpadFunctionalLayer, | 61 | LaunchpadFunctionalLayer, |
1202 | 56 | LaunchpadZopelessLayer, | 62 | LaunchpadZopelessLayer, |
1203 | 57 | ) | 63 | ) |
1205 | 58 | from lp.testing.matchers import Provides | 64 | from lp.testing.matchers import ( |
1206 | 65 | HasQueryCount, | ||
1207 | 66 | Provides, | ||
1208 | 67 | ) | ||
1209 | 59 | 68 | ||
1210 | 60 | 69 | ||
1211 | 61 | class PackageUploadTestCase(TestCaseWithFactory): | 70 | class PackageUploadTestCase(TestCaseWithFactory): |
1212 | @@ -440,8 +449,7 @@ | |||
1213 | 440 | only_allowed_component = self.factory.makeComponent() | 449 | only_allowed_component = self.factory.makeComponent() |
1214 | 441 | section = self.factory.makeSection() | 450 | section = self.factory.makeSection() |
1215 | 442 | self.assertRaises( | 451 | self.assertRaises( |
1218 | 443 | QueueInconsistentStateError, | 452 | QueueAdminUnauthorizedError, pu.overrideSource, |
1217 | 444 | pu.overrideSource, | ||
1219 | 445 | only_allowed_component, section, [only_allowed_component]) | 453 | only_allowed_component, section, [only_allowed_component]) |
1220 | 446 | 454 | ||
1221 | 447 | def test_overrideSource_checks_permission_for_new_component(self): | 455 | def test_overrideSource_checks_permission_for_new_component(self): |
1222 | @@ -450,8 +458,7 @@ | |||
1223 | 450 | disallowed_component = self.factory.makeComponent() | 458 | disallowed_component = self.factory.makeComponent() |
1224 | 451 | section = self.factory.makeSection() | 459 | section = self.factory.makeSection() |
1225 | 452 | self.assertRaises( | 460 | self.assertRaises( |
1228 | 453 | QueueInconsistentStateError, | 461 | QueueAdminUnauthorizedError, pu.overrideSource, |
1227 | 454 | pu.overrideSource, | ||
1229 | 455 | disallowed_component, section, [current_component]) | 462 | disallowed_component, section, [current_component]) |
1230 | 456 | 463 | ||
1231 | 457 | def test_overrideSource_ignores_None_component_change(self): | 464 | def test_overrideSource_ignores_None_component_change(self): |
1232 | @@ -864,6 +871,20 @@ | |||
1233 | 864 | list(reversed(ordered_uploads)), | 871 | list(reversed(ordered_uploads)), |
1234 | 865 | list(getUtility(IPackageUploadSet).getAll(series))) | 872 | list(getUtility(IPackageUploadSet).getAll(series))) |
1235 | 866 | 873 | ||
1236 | 874 | def test_getAll_can_preload_exported_properties(self): | ||
1237 | 875 | # getAll preloads everything exported on the webservice. | ||
1238 | 876 | distroseries = self.factory.makeDistroSeries() | ||
1239 | 877 | self.factory.makeSourcePackageUpload(distroseries=distroseries) | ||
1240 | 878 | self.factory.makeBuildPackageUpload(distroseries=distroseries) | ||
1241 | 879 | self.factory.makeCustomPackageUpload(distroseries=distroseries) | ||
1242 | 880 | uploads = list(getUtility(IPackageUploadSet).getAll(distroseries)) | ||
1243 | 881 | with StormStatementRecorder() as recorder: | ||
1244 | 882 | for name, field in getFields(IPackageUpload).items(): | ||
1245 | 883 | if field.queryTaggedValue("lazr.restful.exported") is not None: | ||
1246 | 884 | for upload in uploads: | ||
1247 | 885 | getattr(upload, name) | ||
1248 | 886 | self.assertThat(recorder, HasQueryCount(Equals(0))) | ||
1249 | 887 | |||
1250 | 867 | def test_rejectFromQueue_no_changes_file(self): | 888 | def test_rejectFromQueue_no_changes_file(self): |
1251 | 868 | # If the PackageUpload has no changesfile, we can still reject it. | 889 | # If the PackageUpload has no changesfile, we can still reject it. |
1252 | 869 | pu = self.factory.makePackageUpload() | 890 | pu = self.factory.makePackageUpload() |
1253 | @@ -880,12 +901,13 @@ | |||
1254 | 880 | def setUp(self): | 901 | def setUp(self): |
1255 | 881 | super(TestPackageUploadWebservice, self).setUp() | 902 | super(TestPackageUploadWebservice, self).setUp() |
1256 | 882 | self.webservice = None | 903 | self.webservice = None |
1257 | 883 | |||
1258 | 884 | def makeDistroSeries(self): | ||
1259 | 885 | self.distroseries = self.factory.makeDistroSeries() | 904 | self.distroseries = self.factory.makeDistroSeries() |
1260 | 886 | self.main = self.factory.makeComponent("main") | 905 | self.main = self.factory.makeComponent("main") |
1261 | 887 | self.factory.makeComponentSelection( | 906 | self.factory.makeComponentSelection( |
1262 | 888 | distroseries=self.distroseries, component=self.main) | 907 | distroseries=self.distroseries, component=self.main) |
1263 | 908 | self.universe = self.factory.makeComponent("universe") | ||
1264 | 909 | self.factory.makeComponentSelection( | ||
1265 | 910 | distroseries=self.distroseries, component=self.universe) | ||
1266 | 889 | 911 | ||
1267 | 890 | def makeQueueAdmin(self, components): | 912 | def makeQueueAdmin(self, components): |
1268 | 891 | person = self.factory.makePerson() | 913 | person = self.factory.makePerson() |
1269 | @@ -902,57 +924,336 @@ | |||
1270 | 902 | self.webservice = launchpadlib_for("testing", person) | 924 | self.webservice = launchpadlib_for("testing", person) |
1271 | 903 | return self.webservice.load(api_url(obj)) | 925 | return self.webservice.load(api_url(obj)) |
1272 | 904 | 926 | ||
1273 | 927 | def makeSourcePackageUpload(self, person, **kwargs): | ||
1274 | 928 | with person_logged_in(person): | ||
1275 | 929 | upload = self.factory.makeSourcePackageUpload( | ||
1276 | 930 | distroseries=self.distroseries, **kwargs) | ||
1277 | 931 | transaction.commit() | ||
1278 | 932 | spr = upload.sourcepackagerelease | ||
1279 | 933 | for extension in ("dsc", "tar.gz"): | ||
1280 | 934 | filename = "%s_%s.%s" % (spr.name, spr.version, extension) | ||
1281 | 935 | lfa = self.factory.makeLibraryFileAlias(filename=filename) | ||
1282 | 936 | spr.addFile(lfa) | ||
1283 | 937 | transaction.commit() | ||
1284 | 938 | return upload, self.load(upload, person) | ||
1285 | 939 | |||
1286 | 940 | def makeBinaryPackageUpload(self, person, binarypackagename=None, | ||
1287 | 941 | component=None): | ||
1288 | 942 | with person_logged_in(person): | ||
1289 | 943 | upload = self.factory.makeBuildPackageUpload( | ||
1290 | 944 | distroseries=self.distroseries, | ||
1291 | 945 | binarypackagename=binarypackagename, component=component) | ||
1292 | 946 | self.factory.makeBinaryPackageRelease( | ||
1293 | 947 | build=upload.builds[0].build, component=component) | ||
1294 | 948 | transaction.commit() | ||
1295 | 949 | for build in upload.builds: | ||
1296 | 950 | for bpr in build.build.binarypackages: | ||
1297 | 951 | filename = "%s_%s_%s.deb" % ( | ||
1298 | 952 | bpr.name, bpr.version, bpr.build.arch_tag) | ||
1299 | 953 | lfa = self.factory.makeLibraryFileAlias(filename=filename) | ||
1300 | 954 | bpr.addFile(lfa) | ||
1301 | 955 | transaction.commit() | ||
1302 | 956 | return upload, self.load(upload, person) | ||
1303 | 957 | |||
1304 | 958 | def makeCustomPackageUpload(self, person, **kwargs): | ||
1305 | 959 | with person_logged_in(person): | ||
1306 | 960 | upload = self.factory.makeCustomPackageUpload( | ||
1307 | 961 | distroseries=self.distroseries, **kwargs) | ||
1308 | 962 | transaction.commit() | ||
1309 | 963 | return upload, self.load(upload, person) | ||
1310 | 964 | |||
1311 | 905 | def assertRequiresEdit(self, method_name, **kwargs): | 965 | def assertRequiresEdit(self, method_name, **kwargs): |
1312 | 906 | """Test that a web service queue method requires launchpad.Edit.""" | 966 | """Test that a web service queue method requires launchpad.Edit.""" |
1313 | 907 | with admin_logged_in(): | 967 | with admin_logged_in(): |
1314 | 908 | upload = self.factory.makeSourcePackageUpload() | 968 | upload = self.factory.makeSourcePackageUpload() |
1315 | 909 | transaction.commit() | 969 | transaction.commit() |
1316 | 910 | ws_upload = self.load(upload) | 970 | ws_upload = self.load(upload) |
1319 | 911 | self.assertRaises(Unauthorized, getattr(ws_upload, method_name), | 971 | self.assertRaises( |
1320 | 912 | **kwargs) | 972 | Unauthorized, getattr(ws_upload, method_name), **kwargs) |
1321 | 913 | 973 | ||
1322 | 914 | def test_edit_permissions(self): | 974 | def test_edit_permissions(self): |
1323 | 915 | self.assertRequiresEdit("acceptFromQueue") | 975 | self.assertRequiresEdit("acceptFromQueue") |
1324 | 916 | self.assertRequiresEdit("rejectFromQueue") | 976 | self.assertRequiresEdit("rejectFromQueue") |
1325 | 977 | self.assertRequiresEdit("overrideSource", new_component="main") | ||
1326 | 978 | self.assertRequiresEdit( | ||
1327 | 979 | "overrideBinaries", changes=[{"component": "main"}]) | ||
1328 | 917 | 980 | ||
1329 | 918 | def test_acceptFromQueue_archive_admin(self): | 981 | def test_acceptFromQueue_archive_admin(self): |
1330 | 919 | # acceptFromQueue as an archive admin accepts the upload. | 982 | # acceptFromQueue as an archive admin accepts the upload. |
1331 | 920 | self.makeDistroSeries() | ||
1332 | 921 | person = self.makeQueueAdmin([self.main]) | 983 | person = self.makeQueueAdmin([self.main]) |
1337 | 922 | with person_logged_in(person): | 984 | upload, ws_upload = self.makeSourcePackageUpload( |
1338 | 923 | upload = self.factory.makeSourcePackageUpload( | 985 | person, component=self.main) |
1335 | 924 | distroseries=self.distroseries, component=self.main) | ||
1336 | 925 | transaction.commit() | ||
1339 | 926 | 986 | ||
1340 | 927 | ws_upload = self.load(upload, person) | ||
1341 | 928 | self.assertEqual("New", ws_upload.status) | 987 | self.assertEqual("New", ws_upload.status) |
1342 | 929 | ws_upload.acceptFromQueue() | 988 | ws_upload.acceptFromQueue() |
1343 | 930 | self.assertEqual("Done", ws_upload.status) | 989 | self.assertEqual("Done", ws_upload.status) |
1344 | 931 | 990 | ||
1345 | 932 | def test_double_accept_raises_BadRequest(self): | 991 | def test_double_accept_raises_BadRequest(self): |
1346 | 933 | # Trying to accept an upload twice returns 400 instead of OOPSing. | 992 | # Trying to accept an upload twice returns 400 instead of OOPSing. |
1347 | 934 | self.makeDistroSeries() | ||
1348 | 935 | person = self.makeQueueAdmin([self.main]) | 993 | person = self.makeQueueAdmin([self.main]) |
1349 | 994 | upload, _ = self.makeSourcePackageUpload(person, component=self.main) | ||
1350 | 995 | |||
1351 | 936 | with person_logged_in(person): | 996 | with person_logged_in(person): |
1352 | 937 | upload = self.factory.makeSourcePackageUpload( | ||
1353 | 938 | distroseries=self.distroseries, component=self.main) | ||
1354 | 939 | upload.setAccepted() | 997 | upload.setAccepted() |
1355 | 940 | transaction.commit() | ||
1356 | 941 | |||
1357 | 942 | ws_upload = self.load(upload, person) | 998 | ws_upload = self.load(upload, person) |
1358 | 943 | self.assertEqual("Accepted", ws_upload.status) | 999 | self.assertEqual("Accepted", ws_upload.status) |
1359 | 944 | self.assertRaises(BadRequest, ws_upload.acceptFromQueue) | 1000 | self.assertRaises(BadRequest, ws_upload.acceptFromQueue) |
1360 | 945 | 1001 | ||
1361 | 946 | def test_rejectFromQueue_archive_admin(self): | 1002 | def test_rejectFromQueue_archive_admin(self): |
1362 | 947 | # rejectFromQueue as an archive admin rejects the upload. | 1003 | # rejectFromQueue as an archive admin rejects the upload. |
1363 | 948 | self.makeDistroSeries() | ||
1364 | 949 | person = self.makeQueueAdmin([self.main]) | 1004 | person = self.makeQueueAdmin([self.main]) |
1369 | 950 | with person_logged_in(person): | 1005 | upload, ws_upload = self.makeSourcePackageUpload( |
1370 | 951 | upload = self.factory.makeSourcePackageUpload( | 1006 | person, component=self.main) |
1367 | 952 | distroseries=self.distroseries, component=self.main) | ||
1368 | 953 | transaction.commit() | ||
1371 | 954 | 1007 | ||
1372 | 955 | ws_upload = self.load(upload, person) | ||
1373 | 956 | self.assertEqual("New", ws_upload.status) | 1008 | self.assertEqual("New", ws_upload.status) |
1374 | 957 | ws_upload.rejectFromQueue() | 1009 | ws_upload.rejectFromQueue() |
1375 | 958 | self.assertEqual("Rejected", ws_upload.status) | 1010 | self.assertEqual("Rejected", ws_upload.status) |
1376 | 1011 | |||
1377 | 1012 | def test_source_info(self): | ||
1378 | 1013 | # API clients can inspect properties of source uploads. | ||
1379 | 1014 | person = self.makeQueueAdmin([self.universe]) | ||
1380 | 1015 | upload, ws_upload = self.makeSourcePackageUpload( | ||
1381 | 1016 | person, sourcepackagename="hello", component=self.universe) | ||
1382 | 1017 | |||
1383 | 1018 | self.assertTrue(ws_upload.contains_source) | ||
1384 | 1019 | self.assertFalse(ws_upload.contains_build) | ||
1385 | 1020 | self.assertFalse(ws_upload.contains_copy) | ||
1386 | 1021 | self.assertEqual("hello", ws_upload.display_name) | ||
1387 | 1022 | self.assertEqual(upload.package_version, ws_upload.display_version) | ||
1388 | 1023 | self.assertEqual("source", ws_upload.display_arches) | ||
1389 | 1024 | self.assertEqual("hello", ws_upload.package_name) | ||
1390 | 1025 | self.assertEqual(upload.package_version, ws_upload.package_version) | ||
1391 | 1026 | self.assertEqual("universe", ws_upload.component_name) | ||
1392 | 1027 | self.assertEqual(upload.section_name, ws_upload.section_name) | ||
1393 | 1028 | |||
1394 | 1029 | def test_source_fetch(self): | ||
1395 | 1030 | # API clients can fetch files attached to source uploads. | ||
1396 | 1031 | person = self.makeQueueAdmin([self.universe]) | ||
1397 | 1032 | upload, ws_upload = self.makeSourcePackageUpload( | ||
1398 | 1033 | person, component=self.universe) | ||
1399 | 1034 | ws_source_file_urls = ws_upload.sourceFileUrls() | ||
1400 | 1035 | self.assertNotEqual(0, len(ws_source_file_urls)) | ||
1401 | 1036 | with person_logged_in(person): | ||
1402 | 1037 | source_file_urls = [ | ||
1403 | 1038 | ProxiedLibraryFileAlias( | ||
1404 | 1039 | file.libraryfile, upload.archive).http_url | ||
1405 | 1040 | for file in upload.sourcepackagerelease.files] | ||
1406 | 1041 | self.assertContentEqual(source_file_urls, ws_source_file_urls) | ||
1407 | 1042 | |||
1408 | 1043 | def test_overrideSource_limited_component_permissions(self): | ||
1409 | 1044 | # Overriding between two components requires queue admin of both. | ||
1410 | 1045 | person = self.makeQueueAdmin([self.universe]) | ||
1411 | 1046 | upload, ws_upload = self.makeSourcePackageUpload( | ||
1412 | 1047 | person, component=self.universe) | ||
1413 | 1048 | |||
1414 | 1049 | self.assertEqual("New", ws_upload.status) | ||
1415 | 1050 | self.assertEqual("universe", ws_upload.component_name) | ||
1416 | 1051 | self.assertRaises(Unauthorized, ws_upload.overrideSource, | ||
1417 | 1052 | new_component="main") | ||
1418 | 1053 | |||
1419 | 1054 | with admin_logged_in(): | ||
1420 | 1055 | upload.overrideSource( | ||
1421 | 1056 | new_component=self.main, | ||
1422 | 1057 | allowed_components=[self.main, self.universe]) | ||
1423 | 1058 | transaction.commit() | ||
1424 | 1059 | self.assertEqual("main", upload.component_name) | ||
1425 | 1060 | self.assertRaises(Unauthorized, ws_upload.overrideSource, | ||
1426 | 1061 | new_component="universe") | ||
1427 | 1062 | |||
1428 | 1063 | def test_overrideSource_changes_properties(self): | ||
1429 | 1064 | # Running overrideSource changes the corresponding properties. | ||
1430 | 1065 | person = self.makeQueueAdmin([self.main, self.universe]) | ||
1431 | 1066 | upload, ws_upload = self.makeSourcePackageUpload( | ||
1432 | 1067 | person, component=self.universe) | ||
1433 | 1068 | with person_logged_in(person): | ||
1434 | 1069 | new_section = self.factory.makeSection() | ||
1435 | 1070 | transaction.commit() | ||
1436 | 1071 | |||
1437 | 1072 | self.assertEqual("New", ws_upload.status) | ||
1438 | 1073 | self.assertEqual("universe", ws_upload.component_name) | ||
1439 | 1074 | self.assertNotEqual(new_section.name, ws_upload.section_name) | ||
1440 | 1075 | ws_upload.overrideSource( | ||
1441 | 1076 | new_component="main", new_section=new_section.name) | ||
1442 | 1077 | self.assertEqual("main", ws_upload.component_name) | ||
1443 | 1078 | self.assertEqual(new_section.name, ws_upload.section_name) | ||
1444 | 1079 | ws_upload.overrideSource(new_component="universe") | ||
1445 | 1080 | self.assertEqual("universe", ws_upload.component_name) | ||
1446 | 1081 | |||
1447 | 1082 | def test_binary_info(self): | ||
1448 | 1083 | # API clients can inspect properties of binary uploads. | ||
1449 | 1084 | person = self.makeQueueAdmin([self.universe]) | ||
1450 | 1085 | upload, ws_upload = self.makeBinaryPackageUpload( | ||
1451 | 1086 | person, component=self.universe) | ||
1452 | 1087 | with person_logged_in(person): | ||
1453 | 1088 | arch = upload.builds[0].build.arch_tag | ||
1454 | 1089 | bprs = upload.builds[0].build.binarypackages | ||
1455 | 1090 | |||
1456 | 1091 | self.assertFalse(ws_upload.contains_source) | ||
1457 | 1092 | self.assertTrue(ws_upload.contains_build) | ||
1458 | 1093 | ws_binaries = ws_upload.getBinaryProperties() | ||
1459 | 1094 | self.assertEqual(len(list(bprs)), len(ws_binaries)) | ||
1460 | 1095 | for bpr, binary in zip(bprs, ws_binaries): | ||
1461 | 1096 | expected_binary = { | ||
1462 | 1097 | "is_new": True, | ||
1463 | 1098 | "name": bpr.name, | ||
1464 | 1099 | "version": bpr.version, | ||
1465 | 1100 | "architecture": arch, | ||
1466 | 1101 | "component": "universe", | ||
1467 | 1102 | "section": bpr.section.name, | ||
1468 | 1103 | "priority": bpr.priority.name, | ||
1469 | 1104 | } | ||
1470 | 1105 | self.assertContentEqual(expected_binary.keys(), binary.keys()) | ||
1471 | 1106 | for key, value in expected_binary.items(): | ||
1472 | 1107 | self.assertEqual(value, binary[key]) | ||
1473 | 1108 | |||
1474 | 1109 | def test_binary_fetch(self): | ||
1475 | 1110 | # API clients can fetch files attached to binary uploads. | ||
1476 | 1111 | person = self.makeQueueAdmin([self.universe]) | ||
1477 | 1112 | upload, ws_upload = self.makeBinaryPackageUpload( | ||
1478 | 1113 | person, component=self.universe) | ||
1479 | 1114 | |||
1480 | 1115 | ws_binary_file_urls = ws_upload.binaryFileUrls() | ||
1481 | 1116 | self.assertNotEqual(0, len(ws_binary_file_urls)) | ||
1482 | 1117 | with person_logged_in(person): | ||
1483 | 1118 | binary_file_urls = [ | ||
1484 | 1119 | ProxiedLibraryFileAlias( | ||
1485 | 1120 | file.libraryfile, upload.archive).http_url | ||
1486 | 1121 | for bpr in upload.builds[0].build.binarypackages | ||
1487 | 1122 | for file in bpr.files] | ||
1488 | 1123 | self.assertContentEqual(binary_file_urls, ws_binary_file_urls) | ||
1489 | 1124 | |||
1490 | 1125 | def test_overrideBinaries_limited_component_permissions(self): | ||
1491 | 1126 | # Overriding between two components requires queue admin of both. | ||
1492 | 1127 | person = self.makeQueueAdmin([self.universe]) | ||
1493 | 1128 | upload, ws_upload = self.makeBinaryPackageUpload( | ||
1494 | 1129 | person, binarypackagename="hello", component=self.universe) | ||
1495 | 1130 | |||
1496 | 1131 | self.assertEqual("New", ws_upload.status) | ||
1497 | 1132 | self.assertEqual( | ||
1498 | 1133 | set(["universe"]), | ||
1499 | 1134 | set(binary["component"] | ||
1500 | 1135 | for binary in ws_upload.getBinaryProperties())) | ||
1501 | 1136 | self.assertRaises( | ||
1502 | 1137 | Unauthorized, ws_upload.overrideBinaries, | ||
1503 | 1138 | changes=[{"component": "main"}]) | ||
1504 | 1139 | |||
1505 | 1140 | with admin_logged_in(): | ||
1506 | 1141 | upload.overrideBinaries( | ||
1507 | 1142 | [{"component": self.main}], | ||
1508 | 1143 | allowed_components=[self.main, self.universe]) | ||
1509 | 1144 | transaction.commit() | ||
1510 | 1145 | |||
1511 | 1146 | self.assertEqual( | ||
1512 | 1147 | set(["main"]), | ||
1513 | 1148 | set(binary["component"] | ||
1514 | 1149 | for binary in ws_upload.getBinaryProperties())) | ||
1515 | 1150 | self.assertRaises( | ||
1516 | 1151 | Unauthorized, ws_upload.overrideBinaries, | ||
1517 | 1152 | changes=[{"component": "universe"}]) | ||
1518 | 1153 | |||
1519 | 1154 | def test_overrideBinaries_disallows_new_archive(self): | ||
1520 | 1155 | # overrideBinaries refuses to override the component to something | ||
1521 | 1156 | # that requires a different archive. | ||
1522 | 1157 | partner = self.factory.makeComponent("partner") | ||
1523 | 1158 | self.factory.makeComponentSelection( | ||
1524 | 1159 | distroseries=self.distroseries, component=partner) | ||
1525 | 1160 | person = self.makeQueueAdmin([self.universe, partner]) | ||
1526 | 1161 | upload, ws_upload = self.makeBinaryPackageUpload( | ||
1527 | 1162 | person, component=self.universe) | ||
1528 | 1163 | |||
1529 | 1164 | self.assertEqual( | ||
1530 | 1165 | "universe", ws_upload.getBinaryProperties()[0]["component"]) | ||
1531 | 1166 | self.assertRaises( | ||
1532 | 1167 | BadRequest, ws_upload.overrideBinaries, | ||
1533 | 1168 | changes=[{"component": "partner"}]) | ||
1534 | 1169 | |||
1535 | 1170 | def test_overrideBinaries_without_name_changes_all_properties(self): | ||
1536 | 1171 | # Running overrideBinaries with a change entry containing no "name" | ||
1537 | 1172 | # field changes the corresponding properties of all binaries. | ||
1538 | 1173 | person = self.makeQueueAdmin([self.main, self.universe]) | ||
1539 | 1174 | upload, ws_upload = self.makeBinaryPackageUpload( | ||
1540 | 1175 | person, component=self.universe) | ||
1541 | 1176 | with person_logged_in(person): | ||
1542 | 1177 | new_section = self.factory.makeSection() | ||
1543 | 1178 | transaction.commit() | ||
1544 | 1179 | |||
1545 | 1180 | self.assertEqual("New", ws_upload.status) | ||
1546 | 1181 | for binary in ws_upload.getBinaryProperties(): | ||
1547 | 1182 | self.assertEqual("universe", binary["component"]) | ||
1548 | 1183 | self.assertNotEqual(new_section.name, binary["section"]) | ||
1549 | 1184 | self.assertEqual("OPTIONAL", binary["priority"]) | ||
1550 | 1185 | changes = [{ | ||
1551 | 1186 | "component": "main", | ||
1552 | 1187 | "section": new_section.name, | ||
1553 | 1188 | "priority": "extra", | ||
1554 | 1189 | }] | ||
1555 | 1190 | ws_upload.overrideBinaries(changes=changes) | ||
1556 | 1191 | for binary in ws_upload.getBinaryProperties(): | ||
1557 | 1192 | self.assertEqual("main", binary["component"]) | ||
1558 | 1193 | self.assertEqual(new_section.name, binary["section"]) | ||
1559 | 1194 | self.assertEqual("EXTRA", binary["priority"]) | ||
1560 | 1195 | |||
1561 | 1196 | def test_overrideBinaries_with_name_changes_selected_properties(self): | ||
1562 | 1197 | # Running overrideBinaries with change entries containing "name" | ||
1563 | 1198 | # fields changes the corresponding properties of only the selected | ||
1564 | 1199 | # binaries. | ||
1565 | 1200 | person = self.makeQueueAdmin([self.main, self.universe]) | ||
1566 | 1201 | upload, ws_upload = self.makeBinaryPackageUpload( | ||
1567 | 1202 | person, component=self.universe) | ||
1568 | 1203 | with person_logged_in(person): | ||
1569 | 1204 | new_section = self.factory.makeSection() | ||
1570 | 1205 | transaction.commit() | ||
1571 | 1206 | |||
1572 | 1207 | self.assertEqual("New", ws_upload.status) | ||
1573 | 1208 | ws_binaries = ws_upload.getBinaryProperties() | ||
1574 | 1209 | for binary in ws_binaries: | ||
1575 | 1210 | self.assertEqual("universe", binary["component"]) | ||
1576 | 1211 | self.assertNotEqual(new_section.name, binary["section"]) | ||
1577 | 1212 | self.assertEqual("OPTIONAL", binary["priority"]) | ||
1578 | 1213 | change_one = { | ||
1579 | 1214 | "name": ws_binaries[0]["name"], | ||
1580 | 1215 | "component": "main", | ||
1581 | 1216 | "priority": "standard", | ||
1582 | 1217 | } | ||
1583 | 1218 | change_two = { | ||
1584 | 1219 | "name": ws_binaries[1]["name"], | ||
1585 | 1220 | "section": new_section.name, | ||
1586 | 1221 | } | ||
1587 | 1222 | ws_upload.overrideBinaries(changes=[change_one, change_two]) | ||
1588 | 1223 | ws_binaries = ws_upload.getBinaryProperties() | ||
1589 | 1224 | self.assertEqual("main", ws_binaries[0]["component"]) | ||
1590 | 1225 | self.assertNotEqual(new_section.name, ws_binaries[0]["section"]) | ||
1591 | 1226 | self.assertEqual("STANDARD", ws_binaries[0]["priority"]) | ||
1592 | 1227 | self.assertEqual("universe", ws_binaries[1]["component"]) | ||
1593 | 1228 | self.assertEqual(new_section.name, ws_binaries[1]["section"]) | ||
1594 | 1229 | self.assertEqual("OPTIONAL", ws_binaries[1]["priority"]) | ||
1595 | 1230 | |||
1596 | 1231 | def test_custom_info(self): | ||
1597 | 1232 | # API clients can inspect properties of custom uploads. | ||
1598 | 1233 | person = self.makeQueueAdmin([self.universe]) | ||
1599 | 1234 | upload, ws_upload = self.makeCustomPackageUpload( | ||
1600 | 1235 | person, custom_type=PackageUploadCustomFormat.DEBIAN_INSTALLER, | ||
1601 | 1236 | filename="debian-installer-images_1.tar.gz") | ||
1602 | 1237 | |||
1603 | 1238 | self.assertFalse(ws_upload.contains_source) | ||
1604 | 1239 | self.assertFalse(ws_upload.contains_build) | ||
1605 | 1240 | self.assertFalse(ws_upload.contains_copy) | ||
1606 | 1241 | self.assertEqual( | ||
1607 | 1242 | "debian-installer-images_1.tar.gz", ws_upload.display_name) | ||
1608 | 1243 | self.assertEqual("-", ws_upload.display_version) | ||
1609 | 1244 | self.assertEqual("raw-installer", ws_upload.display_arches) | ||
1610 | 1245 | |||
1611 | 1246 | def test_custom_fetch(self): | ||
1612 | 1247 | # API clients can fetch files attached to custom uploads. | ||
1613 | 1248 | person = self.makeQueueAdmin([self.universe]) | ||
1614 | 1249 | upload, ws_upload = self.makeCustomPackageUpload( | ||
1615 | 1250 | person, custom_type=PackageUploadCustomFormat.DEBIAN_INSTALLER, | ||
1616 | 1251 | filename="debian-installer-images_1.tar.gz") | ||
1617 | 1252 | ws_custom_file_urls = ws_upload.customFileUrls() | ||
1618 | 1253 | self.assertNotEqual(0, len(ws_custom_file_urls)) | ||
1619 | 1254 | with person_logged_in(person): | ||
1620 | 1255 | custom_file_urls = [ | ||
1621 | 1256 | ProxiedLibraryFileAlias( | ||
1622 | 1257 | file.libraryfilealias, upload.archive).http_url | ||
1623 | 1258 | for file in upload.customfiles] | ||
1624 | 1259 | self.assertContentEqual(custom_file_urls, ws_custom_file_urls) | ||
1625 | 959 | 1260 | ||
1626 | === modified file 'lib/lp/testing/factory.py' | |||
1627 | --- lib/lp/testing/factory.py 2012-06-08 06:01:50 +0000 | |||
1628 | +++ lib/lp/testing/factory.py 2012-06-08 14:30:37 +0000 | |||
1629 | @@ -3494,7 +3494,7 @@ | |||
1630 | 3494 | return upload | 3494 | return upload |
1631 | 3495 | 3495 | ||
1632 | 3496 | def makeBuildPackageUpload(self, distroseries=None, | 3496 | def makeBuildPackageUpload(self, distroseries=None, |
1634 | 3497 | binarypackagename=None): | 3497 | binarypackagename=None, component=None): |
1635 | 3498 | """Make a `PackageUpload` with a `PackageUploadBuild` attached.""" | 3498 | """Make a `PackageUpload` with a `PackageUploadBuild` attached.""" |
1636 | 3499 | if distroseries is None: | 3499 | if distroseries is None: |
1637 | 3500 | distroseries = self.makeDistroSeries() | 3500 | distroseries = self.makeDistroSeries() |
1638 | @@ -3503,7 +3503,8 @@ | |||
1639 | 3503 | build = self.makeBinaryPackageBuild() | 3503 | build = self.makeBinaryPackageBuild() |
1640 | 3504 | upload.addBuild(build) | 3504 | upload.addBuild(build) |
1641 | 3505 | self.makeBinaryPackageRelease( | 3505 | self.makeBinaryPackageRelease( |
1643 | 3506 | binarypackagename=binarypackagename, build=build) | 3506 | binarypackagename=binarypackagename, build=build, |
1644 | 3507 | component=component) | ||
1645 | 3507 | return upload | 3508 | return upload |
1646 | 3508 | 3509 | ||
1647 | 3509 | def makeCustomPackageUpload(self, distroseries=None, custom_type=None, | 3510 | def makeCustomPackageUpload(self, distroseries=None, custom_type=None, |
It is tough to give an appropriate amount of attention to a branch this big. I suggest breaking it into two or more branches that can get closer to our target of an 800 line diff (https:/ /dev.launchpad. net/PreMergeRev iews#line- 38).
Perhaps one branch that does the refactoring needed for the interface and another that does the exposing, or maybe separate branches for exposing different subsets of the API.
Thanks.