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

Proposed by Colin Watson
Status: Merged
Approved by: j.c.sackett
Approved revision: no longer in the source branch.
Merged at revision: 15575
Proposed branch: lp:~cjwatson/launchpad/queue-api-overrides
Merge into: lp:launchpad
Diff against target: 518 lines (+241/-20)
5 files modified
lib/lp/soyuz/browser/queue.py (+5/-2)
lib/lp/soyuz/doc/distroseriesqueue.txt (+5/-5)
lib/lp/soyuz/interfaces/queue.py (+37/-2)
lib/lp/soyuz/model/queue.py (+43/-7)
lib/lp/soyuz/tests/test_packageupload.py (+151/-4)
To merge this branch: bzr merge lp:~cjwatson/launchpad/queue-api-overrides
Reviewer Review Type Date Requested Status
j.c.sackett (community) Approve
Review via email: mp+113437@code.launchpad.net

Commit message

Export PackageUpload.overrideSource and PackageUpload.overrideBinaries.

Description of the change

== Summary ==

Replace the queue tool in Launchpad with an API client (part five).

== Proposed fix ==

This branch exports the override methods on PackageUpload. With this in addition to what's already landed, the client should be feature-complete, and the remaining work should consist of cleanups.

== 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.
 * 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. In a previous branch (currently QAed and awaiting deployment) I amended Archive.overrideBinaries to take a similar list of dicts as a "changes" parameter, allowing many override changes to be made in a single request; this exports that and improves test coverage.

I extracted this branch from https://code.launchpad.net/~cjwatson/launchpad/queue-api/+merge/108967 at Benji's request.

== Implementation details ==

The only thing I think is notable here is that I copied the new-component-requires-new-archive check from lib/lp/soyuz/scripts/queue.py into PackageUpload.overrideBinaries. This should be acceptable temporary duplication since: (a) it really belongs in the model anyway; (b) lp.soyuz.scripts.queue calls BPR.override directly rather than going through PackageUpload.overrideBinaries, so the check doesn't happen twice at run-time; (c) I will be removing lp.soyuz.scripts.queue soon so there's no point bothering to refactor it.

== LOC Rationale ==

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

== Tests ==

bin/test -vvct test_packageupload

== Demo and Q/A ==

http://paste.ubuntu.com/1072964/ is the current version of my client. With this branch, it should be possible to override sources and (individual) binaries in queue items.

To post a comment you must log in.
Revision history for this message
j.c.sackett (jcsackett) wrote :

This looks good, thanks for breaking it out into smaller branches.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/soyuz/browser/queue.py'
--- lib/lp/soyuz/browser/queue.py 2012-07-05 09:49:55 +0000
+++ lib/lp/soyuz/browser/queue.py 2012-07-06 16:20:24 +0000
@@ -45,6 +45,7 @@
45from lp.soyuz.interfaces.queue import (45from lp.soyuz.interfaces.queue import (
46 IPackageUpload,46 IPackageUpload,
47 IPackageUploadSet,47 IPackageUploadSet,
48 QueueAdminUnauthorizedError,
48 QueueInconsistentStateError,49 QueueInconsistentStateError,
49 )50 )
50from lp.soyuz.interfaces.section import ISectionSet51from lp.soyuz.interfaces.section import ISectionSet
@@ -388,7 +389,8 @@
388 }]389 }]
389 binary_overridden = queue_item.overrideBinaries(390 binary_overridden = queue_item.overrideBinaries(
390 binary_changes, allowed_components)391 binary_changes, allowed_components)
391 except QueueInconsistentStateError as info:392 except (QueueAdminUnauthorizedError,
393 QueueInconsistentStateError) as info:
392 failure.append("FAILED: %s (%s)" %394 failure.append("FAILED: %s (%s)" %
393 (queue_item.displayname, info))395 (queue_item.displayname, info))
394 continue396 continue
@@ -409,7 +411,8 @@
409411
410 try:412 try:
411 getattr(self, 'queue_action_' + action)(queue_item)413 getattr(self, 'queue_action_' + action)(queue_item)
412 except QueueInconsistentStateError as info:414 except (QueueAdminUnauthorizedError,
415 QueueInconsistentStateError) as info:
413 failure.append('FAILED: %s (%s)' %416 failure.append('FAILED: %s (%s)' %
414 (queue_item.displayname, info))417 (queue_item.displayname, info))
415 else:418 else:
416419
=== modified file 'lib/lp/soyuz/doc/distroseriesqueue.txt'
--- lib/lp/soyuz/doc/distroseriesqueue.txt 2012-07-03 09:29:35 +0000
+++ lib/lp/soyuz/doc/distroseriesqueue.txt 2012-07-06 16:20:24 +0000
@@ -648,7 +648,7 @@
648In addition to these parameters, you must also supply648In addition to these parameters, you must also supply
649"allowed_components", which is a sequence of IComponent. Any overrides649"allowed_components", which is a sequence of IComponent. Any overrides
650must have the existing and new component in this sequence otherwise650must have the existing and new component in this sequence otherwise
651QueueInconsistentStateError is raised.651QueueAdminUnauthorizedError is raised.
652652
653The alsa-utils source is already in the queue with component "main"653The alsa-utils source is already in the queue with component "main"
654and section "base".654and section "base".
@@ -673,7 +673,7 @@
673 ... allowed_components=(universe,))673 ... allowed_components=(universe,))
674 Traceback (most recent call last):674 Traceback (most recent call last):
675 ...675 ...
676 QueueInconsistentStateError: No rights to override to restricted676 QueueAdminUnauthorizedError: No rights to override to restricted
677677
678Allowing "restricted" still won't work because the original component678Allowing "restricted" still won't work because the original component
679is "main":679is "main":
@@ -683,7 +683,7 @@
683 ... allowed_components=(restricted,))683 ... allowed_components=(restricted,))
684 Traceback (most recent call last):684 Traceback (most recent call last):
685 ...685 ...
686 QueueInconsistentStateError: No rights to override from main686 QueueAdminUnauthorizedError: No rights to override from main
687687
688Specifying both main and restricted allows the override to restricted/web.688Specifying both main and restricted allows the override to restricted/web.
689overrideSource() returns True if it completed the task.689overrideSource() returns True if it completed the task.
@@ -719,13 +719,13 @@
719 ... binary_changes, allowed_components=(universe,))719 ... binary_changes, allowed_components=(universe,))
720 Traceback (most recent call last):720 Traceback (most recent call last):
721 ...721 ...
722 QueueInconsistentStateError: No rights to override to restricted722 QueueAdminUnauthorizedError: No rights to override to restricted
723723
724 >>> print item.overrideBinaries(724 >>> print item.overrideBinaries(
725 ... binary_changes, allowed_components=(restricted,))725 ... binary_changes, allowed_components=(restricted,))
726 Traceback (most recent call last):726 Traceback (most recent call last):
727 ...727 ...
728 QueueInconsistentStateError: No rights to override from main728 QueueAdminUnauthorizedError: No rights to override from main
729729
730 >>> print item.overrideBinaries(730 >>> print item.overrideBinaries(
731 ... binary_changes, allowed_components=(main,restricted))731 ... binary_changes, allowed_components=(main,restricted))
732732
=== modified file 'lib/lp/soyuz/interfaces/queue.py'
--- lib/lp/soyuz/interfaces/queue.py 2012-07-04 13:02:32 +0000
+++ lib/lp/soyuz/interfaces/queue.py 2012-07-06 16:20:24 +0000
@@ -16,6 +16,7 @@
16 'IPackageUploadCustom',16 'IPackageUploadCustom',
17 'IPackageUploadSet',17 'IPackageUploadSet',
18 'NonBuildableSourceUploadError',18 'NonBuildableSourceUploadError',
19 'QueueAdminUnauthorizedError',
19 'QueueBuildAcceptError',20 'QueueBuildAcceptError',
20 'QueueInconsistentStateError',21 'QueueInconsistentStateError',
21 'QueueSourceAcceptError',22 'QueueSourceAcceptError',
@@ -26,12 +27,15 @@
2627
27from lazr.enum import DBEnumeratedType28from lazr.enum import DBEnumeratedType
28from lazr.restful.declarations import (29from lazr.restful.declarations import (
30 call_with,
29 error_status,31 error_status,
30 export_as_webservice_entry,32 export_as_webservice_entry,
31 export_read_operation,33 export_read_operation,
32 export_write_operation,34 export_write_operation,
33 exported,35 exported,
34 operation_for_version,36 operation_for_version,
37 operation_parameters,
38 REQUEST_USER,
35 )39 )
36from lazr.restful.fields import Reference40from lazr.restful.fields import Reference
37from zope.interface import (41from zope.interface import (
@@ -42,10 +46,12 @@
42 Bool,46 Bool,
43 Choice,47 Choice,
44 Datetime,48 Datetime,
49 Dict,
45 Int,50 Int,
46 List,51 List,
47 TextLine,52 TextLine,
48 )53 )
54from zope.security.interfaces import Unauthorized
4955
50from lp import _56from lp import _
51from lp.soyuz.enums import PackageUploadStatus57from lp.soyuz.enums import PackageUploadStatus
@@ -69,6 +75,10 @@
69 """75 """
7076
7177
78class QueueAdminUnauthorizedError(Unauthorized):
79 """User not permitted to perform a queue administration operation."""
80
81
72class NonBuildableSourceUploadError(QueueInconsistentStateError):82class NonBuildableSourceUploadError(QueueInconsistentStateError):
73 """Source upload will not result in any build record.83 """Source upload will not result in any build record.
7484
@@ -394,7 +404,14 @@
394 :param logger: Specify a logger object if required. Mainly for tests.404 :param logger: Specify a logger object if required. Mainly for tests.
395 """405 """
396406
397 def overrideSource(new_component, new_section, allowed_components):407 @operation_parameters(
408 new_component=TextLine(title=u"The new component name."),
409 new_section=TextLine(title=u"The new section name."))
410 @call_with(allowed_components=None, user=REQUEST_USER)
411 @export_write_operation()
412 @operation_for_version('devel')
413 def overrideSource(new_component=None, new_section=None,
414 allowed_components=None, user=None):
398 """Override the source package contained in this queue item.415 """Override the source package contained in this queue item.
399416
400 :param new_component: An IComponent to replace the existing one417 :param new_component: An IComponent to replace the existing one
@@ -403,6 +420,8 @@
403 in the upload's source.420 in the upload's source.
404 :param allowed_components: A sequence of components that the421 :param allowed_components: A sequence of components that the
405 callsite is allowed to override from and to.422 callsite is allowed to override from and to.
423 :param user: The user requesting the override change, used if
424 allowed_components is None.
406425
407 :raises QueueInconsistentStateError: if either the existing426 :raises QueueInconsistentStateError: if either the existing
408 or the new_component are not in the allowed_components427 or the new_component are not in the allowed_components
@@ -414,7 +433,21 @@
414 :return: True if the source was overridden.433 :return: True if the source was overridden.
415 """434 """
416435
417 def overrideBinaries(changes, allowed_components):436 @operation_parameters(
437 changes=List(
438 title=u"A sequence of changes to apply.",
439 description=(
440 u"Each item may have a 'name' item which specifies the binary "
441 "package name to override; otherwise, the change applies to "
442 "all binaries in the upload. It may also have 'component', "
443 "'section', and 'priority' items which replace the "
444 "corresponding existing one in the upload's overridden "
445 "binaries."),
446 value_type=Dict(key_type=TextLine())))
447 @call_with(allowed_components=None, user=REQUEST_USER)
448 @export_write_operation()
449 @operation_for_version('devel')
450 def overrideBinaries(changes, allowed_components=None, user=None):
418 """Override binary packages in a binary queue item.451 """Override binary packages in a binary queue item.
419452
420 :param changes: A sequence of mappings of changes to apply. Each453 :param changes: A sequence of mappings of changes to apply. Each
@@ -426,6 +459,8 @@
426 left unchanged.459 left unchanged.
427 :param allowed_components: A sequence of components that the460 :param allowed_components: A sequence of components that the
428 callsite is allowed to override from and to.461 callsite is allowed to override from and to.
462 :param user: The user requesting the override change, used if
463 allowed_components is None.
429464
430 :raises QueueInconsistentStateError: if either the existing465 :raises QueueInconsistentStateError: if either the existing
431 or the new_component are not in the allowed_components466 or the new_component are not in the allowed_components
432467
=== modified file 'lib/lp/soyuz/model/queue.py'
--- lib/lp/soyuz/model/queue.py 2012-07-06 13:51:34 +0000
+++ lib/lp/soyuz/model/queue.py 2012-07-06 16:20:24 +0000
@@ -84,6 +84,7 @@
84 PriorityNotFound,84 PriorityNotFound,
85 SectionNotFound,85 SectionNotFound,
86 )86 )
87from lp.soyuz.interfaces.archivepermission import IArchivePermissionSet
87from lp.soyuz.interfaces.component import IComponentSet88from lp.soyuz.interfaces.component import IComponentSet
88from lp.soyuz.interfaces.packagecopyjob import IPackageCopyJobSource89from lp.soyuz.interfaces.packagecopyjob import IPackageCopyJobSource
89from lp.soyuz.interfaces.publishing import (90from lp.soyuz.interfaces.publishing import (
@@ -99,6 +100,7 @@
99 IPackageUploadSet,100 IPackageUploadSet,
100 IPackageUploadSource,101 IPackageUploadSource,
101 NonBuildableSourceUploadError,102 NonBuildableSourceUploadError,
103 QueueAdminUnauthorizedError,
102 QueueBuildAcceptError,104 QueueBuildAcceptError,
103 QueueInconsistentStateError,105 QueueInconsistentStateError,
104 QueueSourceAcceptError,106 QueueSourceAcceptError,
@@ -1038,7 +1040,7 @@
1038 allowed_component_names = [1040 allowed_component_names = [
1039 component.name for component in allowed_components]1041 component.name for component in allowed_components]
1040 if copy_job.component_name not in allowed_component_names:1042 if copy_job.component_name not in allowed_component_names:
1041 raise QueueInconsistentStateError(1043 raise QueueAdminUnauthorizedError(
1042 "No rights to override from %s" % copy_job.component_name)1044 "No rights to override from %s" % copy_job.component_name)
1043 copy_job.addSourceOverride(SourceOverride(1045 copy_job.addSourceOverride(SourceOverride(
1044 copy_job.package_name, new_component, new_section))1046 copy_job.package_name, new_component, new_section))
@@ -1055,7 +1057,7 @@
1055 if old_component not in allowed_components:1057 if old_component not in allowed_components:
1056 # The old component is not in the list of allowed components1058 # The old component is not in the list of allowed components
1057 # to override.1059 # to override.
1058 raise QueueInconsistentStateError(1060 raise QueueAdminUnauthorizedError(
1059 "No rights to override from %s" % old_component.name)1061 "No rights to override from %s" % old_component.name)
1060 source.sourcepackagerelease.override(1062 source.sourcepackagerelease.override(
1061 component=new_component, section=new_section)1063 component=new_component, section=new_section)
@@ -1068,7 +1070,8 @@
10681070
1069 return made_changes1071 return made_changes
10701072
1071 def overrideSource(self, new_component, new_section, allowed_components):1073 def overrideSource(self, new_component=None, new_section=None,
1074 allowed_components=None, user=None):
1072 """See `IPackageUpload`."""1075 """See `IPackageUpload`."""
1073 if new_component is None and new_section is None:1076 if new_component is None and new_section is None:
1074 # Nothing needs overriding, bail out.1077 # Nothing needs overriding, bail out.
@@ -1077,8 +1080,19 @@
1077 new_component = self._nameToComponent(new_component)1080 new_component = self._nameToComponent(new_component)
1078 new_section = self._nameToSection(new_section)1081 new_section = self._nameToSection(new_section)
10791082
1083 if allowed_components is None and user is not None:
1084 # Get a list of components for which the user has rights to
1085 # override to or from.
1086 permission_set = getUtility(IArchivePermissionSet)
1087 permissions = permission_set.componentsForQueueAdmin(
1088 self.distroseries.main_archive, user)
1089 allowed_components = set(
1090 permission.component for permission in permissions)
1091 assert allowed_components is not None, (
1092 "Must provide allowed_components for non-webservice calls.")
1093
1080 if new_component not in list(allowed_components) + [None]:1094 if new_component not in list(allowed_components) + [None]:
1081 raise QueueInconsistentStateError(1095 raise QueueAdminUnauthorizedError(
1082 "No rights to override to %s" % new_component.name)1096 "No rights to override to %s" % new_component.name)
10831097
1084 return (1098 return (
@@ -1113,7 +1127,7 @@
11131127
1114 return changes_by_name, changes_for_all1128 return changes_by_name, changes_for_all
11151129
1116 def overrideBinaries(self, changes, allowed_components):1130 def overrideBinaries(self, changes, allowed_components=None, user=None):
1117 """See `IPackageUpload`."""1131 """See `IPackageUpload`."""
1118 if not self.contains_build:1132 if not self.contains_build:
1119 return False1133 return False
@@ -1122,6 +1136,17 @@
1122 # Nothing needs overriding, bail out.1136 # Nothing needs overriding, bail out.
1123 return False1137 return False
11241138
1139 if allowed_components is None and user is not None:
1140 # Get a list of components for which the user has rights to
1141 # override to or from.
1142 permission_set = getUtility(IArchivePermissionSet)
1143 permissions = permission_set.componentsForQueueAdmin(
1144 self.distroseries.main_archive, user)
1145 allowed_components = set(
1146 permission.component for permission in permissions)
1147 assert allowed_components is not None, (
1148 "Must provide allowed_components for non-webservice calls.")
1149
1125 changes_by_name, changes_for_all = self._filterBinaryChanges(changes)1150 changes_by_name, changes_for_all = self._filterBinaryChanges(changes)
11261151
1127 new_components = set()1152 new_components = set()
@@ -1135,12 +1160,23 @@
1135 component.name1160 component.name
1136 for component in new_components.difference(allowed_components))1161 for component in new_components.difference(allowed_components))
1137 if disallowed_components:1162 if disallowed_components:
1138 raise QueueInconsistentStateError(1163 raise QueueAdminUnauthorizedError(
1139 "No rights to override to %s" %1164 "No rights to override to %s" %
1140 ", ".join(disallowed_components))1165 ", ".join(disallowed_components))
11411166
1142 made_changes = False1167 made_changes = False
1143 for build in self.builds:1168 for build in self.builds:
1169 # See if the new component requires a new archive on the build.
1170 for component in new_components:
1171 distroarchseries = build.build.distro_arch_series
1172 distribution = distroarchseries.distroseries.distribution
1173 new_archive = distribution.getArchiveByComponent(
1174 component.name)
1175 if new_archive != build.build.archive:
1176 raise QueueInconsistentStateError(
1177 "Overriding component to '%s' failed because it "
1178 "would require a new archive." % component.name)
1179
1144 for binarypackage in build.build.binarypackages:1180 for binarypackage in build.build.binarypackages:
1145 change = changes_by_name.get(1181 change = changes_by_name.get(
1146 binarypackage.name, changes_for_all)1182 binarypackage.name, changes_for_all)
@@ -1148,7 +1184,7 @@
1148 if binarypackage.component not in allowed_components:1184 if binarypackage.component not in allowed_components:
1149 # The old component is not in the list of allowed1185 # The old component is not in the list of allowed
1150 # components to override.1186 # components to override.
1151 raise QueueInconsistentStateError(1187 raise QueueAdminUnauthorizedError(
1152 "No rights to override from %s" %1188 "No rights to override from %s" %
1153 binarypackage.component.name)1189 binarypackage.component.name)
1154 binarypackage.override(**change)1190 binarypackage.override(**change)
11551191
=== modified file 'lib/lp/soyuz/tests/test_packageupload.py'
--- lib/lp/soyuz/tests/test_packageupload.py 2012-07-05 13:01:48 +0000
+++ lib/lp/soyuz/tests/test_packageupload.py 2012-07-06 16:20:24 +0000
@@ -42,6 +42,7 @@
42from lp.soyuz.interfaces.queue import (42from lp.soyuz.interfaces.queue import (
43 IPackageUpload,43 IPackageUpload,
44 IPackageUploadSet,44 IPackageUploadSet,
45 QueueAdminUnauthorizedError,
45 QueueInconsistentStateError,46 QueueInconsistentStateError,
46 )47 )
47from lp.soyuz.interfaces.section import ISectionSet48from lp.soyuz.interfaces.section import ISectionSet
@@ -444,8 +445,7 @@
444 only_allowed_component = self.factory.makeComponent()445 only_allowed_component = self.factory.makeComponent()
445 section = self.factory.makeSection()446 section = self.factory.makeSection()
446 self.assertRaises(447 self.assertRaises(
447 QueueInconsistentStateError,448 QueueAdminUnauthorizedError, pu.overrideSource,
448 pu.overrideSource,
449 only_allowed_component, section, [only_allowed_component])449 only_allowed_component, section, [only_allowed_component])
450450
451 def test_overrideSource_checks_permission_for_new_component(self):451 def test_overrideSource_checks_permission_for_new_component(self):
@@ -454,8 +454,7 @@
454 disallowed_component = self.factory.makeComponent()454 disallowed_component = self.factory.makeComponent()
455 section = self.factory.makeSection()455 section = self.factory.makeSection()
456 self.assertRaises(456 self.assertRaises(
457 QueueInconsistentStateError,457 QueueAdminUnauthorizedError, pu.overrideSource,
458 pu.overrideSource,
459 disallowed_component, section, [current_component])458 disallowed_component, section, [current_component])
460459
461 def test_overrideSource_ignores_None_component_change(self):460 def test_overrideSource_ignores_None_component_change(self):
@@ -971,6 +970,9 @@
971 def test_edit_permissions(self):970 def test_edit_permissions(self):
972 self.assertRequiresEdit("acceptFromQueue")971 self.assertRequiresEdit("acceptFromQueue")
973 self.assertRequiresEdit("rejectFromQueue")972 self.assertRequiresEdit("rejectFromQueue")
973 self.assertRequiresEdit("overrideSource", new_component="main")
974 self.assertRequiresEdit(
975 "overrideBinaries", changes=[{"component": "main"}])
974976
975 def test_acceptFromQueue_archive_admin(self):977 def test_acceptFromQueue_archive_admin(self):
976 # acceptFromQueue as an archive admin accepts the upload.978 # acceptFromQueue as an archive admin accepts the upload.
@@ -1034,6 +1036,45 @@
1034 for file in upload.sourcepackagerelease.files]1036 for file in upload.sourcepackagerelease.files]
1035 self.assertContentEqual(source_file_urls, ws_source_file_urls)1037 self.assertContentEqual(source_file_urls, ws_source_file_urls)
10361038
1039 def test_overrideSource_limited_component_permissions(self):
1040 # Overriding between two components requires queue admin of both.
1041 person = self.makeQueueAdmin([self.universe])
1042 upload, ws_upload = self.makeSourcePackageUpload(
1043 person, component=self.universe)
1044
1045 self.assertEqual("New", ws_upload.status)
1046 self.assertEqual("universe", ws_upload.component_name)
1047 self.assertRaises(Unauthorized, ws_upload.overrideSource,
1048 new_component="main")
1049
1050 with admin_logged_in():
1051 upload.overrideSource(
1052 new_component=self.main,
1053 allowed_components=[self.main, self.universe])
1054 transaction.commit()
1055 self.assertEqual("main", upload.component_name)
1056 self.assertRaises(Unauthorized, ws_upload.overrideSource,
1057 new_component="universe")
1058
1059 def test_overrideSource_changes_properties(self):
1060 # Running overrideSource changes the corresponding properties.
1061 person = self.makeQueueAdmin([self.main, self.universe])
1062 upload, ws_upload = self.makeSourcePackageUpload(
1063 person, component=self.universe)
1064 with person_logged_in(person):
1065 new_section = self.factory.makeSection()
1066 transaction.commit()
1067
1068 self.assertEqual("New", ws_upload.status)
1069 self.assertEqual("universe", ws_upload.component_name)
1070 self.assertNotEqual(new_section.name, ws_upload.section_name)
1071 ws_upload.overrideSource(
1072 new_component="main", new_section=new_section.name)
1073 self.assertEqual("main", ws_upload.component_name)
1074 self.assertEqual(new_section.name, ws_upload.section_name)
1075 ws_upload.overrideSource(new_component="universe")
1076 self.assertEqual("universe", ws_upload.component_name)
1077
1037 def assertBinaryPropertiesMatch(self, arch, bpr, ws_binary):1078 def assertBinaryPropertiesMatch(self, arch, bpr, ws_binary):
1038 expected_binary = {1079 expected_binary = {
1039 "is_new": True,1080 "is_new": True,
@@ -1078,6 +1119,112 @@
1078 for file in bpr.files]1119 for file in bpr.files]
1079 self.assertContentEqual(binary_file_urls, ws_binary_file_urls)1120 self.assertContentEqual(binary_file_urls, ws_binary_file_urls)
10801121
1122 def test_overrideBinaries_limited_component_permissions(self):
1123 # Overriding between two components requires queue admin of both.
1124 person = self.makeQueueAdmin([self.universe])
1125 upload, ws_upload = self.makeBinaryPackageUpload(
1126 person, binarypackagename="hello", component=self.universe)
1127
1128 self.assertEqual("New", ws_upload.status)
1129 self.assertEqual(
1130 set(["universe"]),
1131 set(binary["component"]
1132 for binary in ws_upload.getBinaryProperties()))
1133 self.assertRaises(
1134 Unauthorized, ws_upload.overrideBinaries,
1135 changes=[{"component": "main"}])
1136
1137 with admin_logged_in():
1138 upload.overrideBinaries(
1139 [{"component": self.main}],
1140 allowed_components=[self.main, self.universe])
1141 transaction.commit()
1142
1143 self.assertEqual(
1144 set(["main"]),
1145 set(binary["component"]
1146 for binary in ws_upload.getBinaryProperties()))
1147 self.assertRaises(
1148 Unauthorized, ws_upload.overrideBinaries,
1149 changes=[{"component": "universe"}])
1150
1151 def test_overrideBinaries_disallows_new_archive(self):
1152 # overrideBinaries refuses to override the component to something
1153 # that requires a different archive.
1154 partner = self.factory.makeComponent("partner")
1155 self.factory.makeComponentSelection(
1156 distroseries=self.distroseries, component=partner)
1157 person = self.makeQueueAdmin([self.universe, partner])
1158 upload, ws_upload = self.makeBinaryPackageUpload(
1159 person, component=self.universe)
1160
1161 self.assertEqual(
1162 "universe", ws_upload.getBinaryProperties()[0]["component"])
1163 self.assertRaises(
1164 BadRequest, ws_upload.overrideBinaries,
1165 changes=[{"component": "partner"}])
1166
1167 def test_overrideBinaries_without_name_changes_all_properties(self):
1168 # Running overrideBinaries with a change entry containing no "name"
1169 # field changes the corresponding properties of all binaries.
1170 person = self.makeQueueAdmin([self.main, self.universe])
1171 upload, ws_upload = self.makeBinaryPackageUpload(
1172 person, component=self.universe)
1173 with person_logged_in(person):
1174 new_section = self.factory.makeSection()
1175 transaction.commit()
1176
1177 self.assertEqual("New", ws_upload.status)
1178 for binary in ws_upload.getBinaryProperties():
1179 self.assertEqual("universe", binary["component"])
1180 self.assertNotEqual(new_section.name, binary["section"])
1181 self.assertEqual("OPTIONAL", binary["priority"])
1182 changes = [{
1183 "component": "main",
1184 "section": new_section.name,
1185 "priority": "extra",
1186 }]
1187 ws_upload.overrideBinaries(changes=changes)
1188 for binary in ws_upload.getBinaryProperties():
1189 self.assertEqual("main", binary["component"])
1190 self.assertEqual(new_section.name, binary["section"])
1191 self.assertEqual("EXTRA", binary["priority"])
1192
1193 def test_overrideBinaries_with_name_changes_selected_properties(self):
1194 # Running overrideBinaries with change entries containing "name"
1195 # fields changes the corresponding properties of only the selected
1196 # binaries.
1197 person = self.makeQueueAdmin([self.main, self.universe])
1198 upload, ws_upload = self.makeBinaryPackageUpload(
1199 person, component=self.universe)
1200 with person_logged_in(person):
1201 new_section = self.factory.makeSection()
1202 transaction.commit()
1203
1204 self.assertEqual("New", ws_upload.status)
1205 ws_binaries = ws_upload.getBinaryProperties()
1206 for binary in ws_binaries:
1207 self.assertEqual("universe", binary["component"])
1208 self.assertNotEqual(new_section.name, binary["section"])
1209 self.assertEqual("OPTIONAL", binary["priority"])
1210 change_one = {
1211 "name": ws_binaries[0]["name"],
1212 "component": "main",
1213 "priority": "standard",
1214 }
1215 change_two = {
1216 "name": ws_binaries[1]["name"],
1217 "section": new_section.name,
1218 }
1219 ws_upload.overrideBinaries(changes=[change_one, change_two])
1220 ws_binaries = ws_upload.getBinaryProperties()
1221 self.assertEqual("main", ws_binaries[0]["component"])
1222 self.assertNotEqual(new_section.name, ws_binaries[0]["section"])
1223 self.assertEqual("STANDARD", ws_binaries[0]["priority"])
1224 self.assertEqual("universe", ws_binaries[1]["component"])
1225 self.assertEqual(new_section.name, ws_binaries[1]["section"])
1226 self.assertEqual("OPTIONAL", ws_binaries[1]["priority"])
1227
1081 def test_custom_info(self):1228 def test_custom_info(self):
1082 # API clients can inspect properties of custom uploads.1229 # API clients can inspect properties of custom uploads.
1083 person = self.makeQueueAdmin([self.universe])1230 person = self.makeQueueAdmin([self.universe])