Merge ~cjwatson/launchpad:refactor-build-retry-cancel-rescore into launchpad:master
- Git
- lp:~cjwatson/launchpad
- refactor-build-retry-cancel-rescore
- Merge into master
Status: | Merged |
---|---|
Approved by: | Colin Watson |
Approved revision: | 618b6d0313a33166db0d82bb11bb52ececfee337 |
Merge reported by: | Otto Co-Pilot |
Merged at revision: | not available |
Proposed branch: | ~cjwatson/launchpad:refactor-build-retry-cancel-rescore |
Merge into: | launchpad:master |
Diff against target: |
1627 lines (+316/-627) 24 files modified
lib/lp/_schema_circular_imports.py (+0/-2) lib/lp/buildmaster/interfaces/buildfarmjob.py (+93/-3) lib/lp/buildmaster/interfaces/packagebuild.py (+11/-3) lib/lp/buildmaster/interfaces/webservice.py (+7/-1) lib/lp/buildmaster/model/buildfarmjob.py (+77/-0) lib/lp/buildmaster/tests/test_buildfarmjob.py (+3/-1) lib/lp/buildmaster/tests/test_packagebuild.py (+1/-0) lib/lp/charms/interfaces/charmrecipebuild.py (+14/-53) lib/lp/charms/model/charmrecipebuild.py (+2/-60) lib/lp/code/configure.zcml (+3/-0) lib/lp/code/interfaces/sourcepackagerecipebuild.py (+21/-39) lib/lp/code/model/sourcepackagerecipebuild.py (+1/-25) lib/lp/oci/interfaces/ocirecipebuild.py (+13/-53) lib/lp/oci/model/ocirecipebuild.py (+2/-60) lib/lp/security.py (+0/-12) lib/lp/snappy/interfaces/snapbuild.py (+14/-53) lib/lp/snappy/model/snapbuild.py (+2/-60) lib/lp/soyuz/interfaces/binarypackagebuild.py (+26/-57) lib/lp/soyuz/interfaces/livefsbuild.py (+14/-40) lib/lp/soyuz/interfaces/webservice.py (+1/-5) lib/lp/soyuz/model/binarypackagebuild.py (+5/-60) lib/lp/soyuz/model/livefsbuild.py (+1/-32) lib/lp/soyuz/stories/webservice/xx-builds.txt (+3/-4) lib/lp/soyuz/tests/test_build.py (+2/-4) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jürgen Gmach | Approve | ||
Review via email: mp+413831@code.launchpad.net |
Commit message
Push retry/cancel/
Description of the change
Previously, each build type implemented retry/cancel/
I had to split `IBuildFarmJob` into `IBuildFarmJob{
Source package recipe builds gain a `rescore` API operation, and live filesystem and source package recipe builds both gain `can_be_retried` and `retry` methods (although `can_be_retried` is currently always False for those build types, and `retry` will always fail, but that might change in future).
Attempting to retry or rescore a build that cannot be retried or rescored respectively now consistently returns HTTP 400 rather than returning HTTP 500 in some cases.
Colin Watson (cjwatson) : | # |
Preview Diff
1 | diff --git a/lib/lp/_schema_circular_imports.py b/lib/lp/_schema_circular_imports.py |
2 | index 09fe6e4..af12d9a 100644 |
3 | --- a/lib/lp/_schema_circular_imports.py |
4 | +++ b/lib/lp/_schema_circular_imports.py |
5 | @@ -742,8 +742,6 @@ patch_entry_explicit_version(IArchiveSubscriber, 'beta') |
6 | |
7 | # IBinaryPackageBuild |
8 | patch_entry_explicit_version(IBinaryPackageBuild, 'beta') |
9 | -patch_operations_explicit_version( |
10 | - IBinaryPackageBuild, 'beta', "rescore", "retry") |
11 | |
12 | # IBinaryPackagePublishingHistory |
13 | patch_entry_explicit_version(IBinaryPackagePublishingHistory, 'beta') |
14 | diff --git a/lib/lp/buildmaster/interfaces/buildfarmjob.py b/lib/lp/buildmaster/interfaces/buildfarmjob.py |
15 | index 86d62e5..9e8d461 100644 |
16 | --- a/lib/lp/buildmaster/interfaces/buildfarmjob.py |
17 | +++ b/lib/lp/buildmaster/interfaces/buildfarmjob.py |
18 | @@ -4,17 +4,28 @@ |
19 | """Interface for Soyuz build farm jobs.""" |
20 | |
21 | __all__ = [ |
22 | + 'CannotBeRescored', |
23 | + 'CannotBeRetried', |
24 | 'IBuildFarmJob', |
25 | + 'IBuildFarmJobAdmin', |
26 | 'IBuildFarmJobDB', |
27 | + 'IBuildFarmJobEdit', |
28 | 'IBuildFarmJobSet', |
29 | 'IBuildFarmJobSource', |
30 | + 'IBuildFarmJobView', |
31 | 'InconsistentBuildFarmJobError', |
32 | 'ISpecificBuildFarmJobSource', |
33 | ] |
34 | |
35 | +import http.client |
36 | + |
37 | from lazr.restful.declarations import ( |
38 | + error_status, |
39 | + export_write_operation, |
40 | exported, |
41 | exported_as_webservice_entry, |
42 | + operation_for_version, |
43 | + operation_parameters, |
44 | ) |
45 | from lazr.restful.fields import Reference |
46 | from zope.interface import ( |
47 | @@ -49,6 +60,22 @@ class InconsistentBuildFarmJobError(Exception): |
48 | """ |
49 | |
50 | |
51 | +@error_status(http.client.BAD_REQUEST) |
52 | +class CannotBeRetried(Exception): |
53 | + """Raised when retrying a build that cannot be retried.""" |
54 | + |
55 | + def __init__(self, build_id): |
56 | + super().__init__("Build %s cannot be retried." % build_id) |
57 | + |
58 | + |
59 | +@error_status(http.client.BAD_REQUEST) |
60 | +class CannotBeRescored(Exception): |
61 | + """Raised when rescoring a build that cannot be rescored.""" |
62 | + |
63 | + def __init__(self, build_id): |
64 | + super().__init__("Build %s cannot be rescored." % build_id) |
65 | + |
66 | + |
67 | class IBuildFarmJobDB(Interface): |
68 | """Operations on a `BuildFarmJob` DB row. |
69 | |
70 | @@ -63,9 +90,8 @@ class IBuildFarmJobDB(Interface): |
71 | description=_("The specific type of job.")) |
72 | |
73 | |
74 | -@exported_as_webservice_entry(as_of='beta') |
75 | -class IBuildFarmJob(Interface): |
76 | - """Operations that jobs for the build farm must implement.""" |
77 | +class IBuildFarmJobView(Interface): |
78 | + """`IBuildFarmJob` attributes that require launchpad.View.""" |
79 | |
80 | id = Attribute('The build farm job ID.') |
81 | |
82 | @@ -238,6 +264,70 @@ class IBuildFarmJob(Interface): |
83 | "Newline-separated list of repositories to be used to retrieve any " |
84 | "external build-dependencies when performing this build.") |
85 | |
86 | + can_be_rescored = exported(Bool( |
87 | + title=_("Can be rescored"), required=True, readonly=True, |
88 | + description=_( |
89 | + "Whether this build record can be rescored manually."))) |
90 | + |
91 | + can_be_retried = exported(Bool( |
92 | + title=_("Can be retried"), required=True, readonly=True, |
93 | + description=_("Whether this build record can be retried."))) |
94 | + |
95 | + can_be_cancelled = exported(Bool( |
96 | + title=_("Can be cancelled"), required=True, readonly=True, |
97 | + description=_("Whether this build record can be cancelled."))) |
98 | + |
99 | + |
100 | +class IBuildFarmJobEdit(Interface): |
101 | + """`IBuildFarmJob` methods that require launchpad.Edit.""" |
102 | + |
103 | + def resetBuild(): |
104 | + """Reset this build record to a clean state. |
105 | + |
106 | + This method should only be called by `BuildFarmJobMixin.retry`, but |
107 | + subclasses may override it to reset additional state. |
108 | + """ |
109 | + |
110 | + @export_write_operation() |
111 | + @operation_for_version("devel") |
112 | + def retry(): |
113 | + """Restore the build record to its initial state. |
114 | + |
115 | + Build record loses its history, is moved to NEEDSBUILD and a new |
116 | + non-scored BuildQueue entry is created for it. |
117 | + """ |
118 | + |
119 | + @export_write_operation() |
120 | + @operation_for_version("devel") |
121 | + def cancel(): |
122 | + """Cancel the build if it is either pending or in progress. |
123 | + |
124 | + Check the can_be_cancelled property prior to calling this method to |
125 | + find out if cancelling the build is possible. |
126 | + |
127 | + If the build is in progress, it is marked as CANCELLING until the |
128 | + buildd manager terminates the build and marks it CANCELLED. If the |
129 | + build is not in progress, it is marked CANCELLED immediately and is |
130 | + removed from the build queue. |
131 | + |
132 | + If the build is not in a cancellable state, this method is a no-op. |
133 | + """ |
134 | + |
135 | + |
136 | +class IBuildFarmJobAdmin(Interface): |
137 | + """`IBuildFarmJob` methods that require launchpad.Admin.""" |
138 | + |
139 | + @operation_parameters(score=Int(title=_("Score"), required=True)) |
140 | + @export_write_operation() |
141 | + @operation_for_version("devel") |
142 | + def rescore(score): |
143 | + """Change the build's score.""" |
144 | + |
145 | + |
146 | +@exported_as_webservice_entry(as_of='beta') |
147 | +class IBuildFarmJob(IBuildFarmJobView, IBuildFarmJobEdit, IBuildFarmJobAdmin): |
148 | + """Operations that jobs for the build farm must implement.""" |
149 | + |
150 | |
151 | class ISpecificBuildFarmJobSource(Interface): |
152 | """A utility for retrieving objects of a specific IBuildFarmJob type. |
153 | diff --git a/lib/lp/buildmaster/interfaces/packagebuild.py b/lib/lp/buildmaster/interfaces/packagebuild.py |
154 | index 8d6b836..3af81f7 100644 |
155 | --- a/lib/lp/buildmaster/interfaces/packagebuild.py |
156 | +++ b/lib/lp/buildmaster/interfaces/packagebuild.py |
157 | @@ -5,6 +5,7 @@ |
158 | |
159 | __all__ = [ |
160 | 'IPackageBuild', |
161 | + 'IPackageBuildView', |
162 | ] |
163 | |
164 | |
165 | @@ -18,7 +19,10 @@ from zope.schema import ( |
166 | ) |
167 | |
168 | from lp import _ |
169 | -from lp.buildmaster.interfaces.buildfarmjob import IBuildFarmJob |
170 | +from lp.buildmaster.interfaces.buildfarmjob import ( |
171 | + IBuildFarmJob, |
172 | + IBuildFarmJobView, |
173 | + ) |
174 | from lp.registry.interfaces.distribution import IDistribution |
175 | from lp.registry.interfaces.distroseries import IDistroSeries |
176 | from lp.registry.interfaces.pocket import PackagePublishingPocket |
177 | @@ -26,8 +30,8 @@ from lp.services.librarian.interfaces import ILibraryFileAlias |
178 | from lp.soyuz.interfaces.archive import IArchive |
179 | |
180 | |
181 | -class IPackageBuild(IBuildFarmJob): |
182 | - """Attributes and operations specific to package build jobs.""" |
183 | +class IPackageBuildView(IBuildFarmJobView): |
184 | + """`IPackageBuild` methods that require launchpad.View.""" |
185 | |
186 | archive = exported( |
187 | Reference( |
188 | @@ -95,3 +99,7 @@ class IPackageBuild(IBuildFarmJob): |
189 | |
190 | :param changes: Changes file from the upload. |
191 | """ |
192 | + |
193 | + |
194 | +class IPackageBuild(IPackageBuildView, IBuildFarmJob): |
195 | + """Attributes and operations specific to package build jobs.""" |
196 | diff --git a/lib/lp/buildmaster/interfaces/webservice.py b/lib/lp/buildmaster/interfaces/webservice.py |
197 | index b899a8f..e6ca23e 100644 |
198 | --- a/lib/lp/buildmaster/interfaces/webservice.py |
199 | +++ b/lib/lp/buildmaster/interfaces/webservice.py |
200 | @@ -10,6 +10,8 @@ which tells `lazr.restful` that it should look for webservice exports here. |
201 | """ |
202 | |
203 | __all__ = [ |
204 | + 'CannotBeRescored', |
205 | + 'CannotBeRetried', |
206 | 'IBuilder', |
207 | 'IBuilderSet', |
208 | 'IBuildFarmJob', |
209 | @@ -21,7 +23,11 @@ from lp.buildmaster.interfaces.builder import ( |
210 | IBuilder, |
211 | IBuilderSet, |
212 | ) |
213 | -from lp.buildmaster.interfaces.buildfarmjob import IBuildFarmJob |
214 | +from lp.buildmaster.interfaces.buildfarmjob import ( |
215 | + CannotBeRescored, |
216 | + CannotBeRetried, |
217 | + IBuildFarmJob, |
218 | + ) |
219 | from lp.buildmaster.interfaces.processor import ( |
220 | IProcessor, |
221 | IProcessorSet, |
222 | diff --git a/lib/lp/buildmaster/model/buildfarmjob.py b/lib/lp/buildmaster/model/buildfarmjob.py |
223 | index 876546d..181be99 100644 |
224 | --- a/lib/lp/buildmaster/model/buildfarmjob.py |
225 | +++ b/lib/lp/buildmaster/model/buildfarmjob.py |
226 | @@ -34,6 +34,8 @@ from lp.buildmaster.enums import ( |
227 | BuildStatus, |
228 | ) |
229 | from lp.buildmaster.interfaces.buildfarmjob import ( |
230 | + CannotBeRescored, |
231 | + CannotBeRetried, |
232 | IBuildFarmJob, |
233 | IBuildFarmJobDB, |
234 | IBuildFarmJobSet, |
235 | @@ -262,6 +264,81 @@ class BuildFarmJobMixin: |
236 | del get_property_cache(self).buildqueue_record |
237 | return queue_entry |
238 | |
239 | + @property |
240 | + def can_be_retried(self): |
241 | + """See `IBuildFarmJob`. |
242 | + |
243 | + Implementations should override this method to first check whether |
244 | + their associated build behaviour would accept the build if it |
245 | + succeeded. |
246 | + """ |
247 | + failed_statuses = [ |
248 | + BuildStatus.FAILEDTOBUILD, |
249 | + BuildStatus.MANUALDEPWAIT, |
250 | + BuildStatus.CHROOTWAIT, |
251 | + BuildStatus.FAILEDTOUPLOAD, |
252 | + BuildStatus.CANCELLED, |
253 | + BuildStatus.SUPERSEDED, |
254 | + ] |
255 | + |
256 | + # If the build is currently in any of the failed states, |
257 | + # it may be retried. |
258 | + return self.status in failed_statuses |
259 | + |
260 | + @property |
261 | + def can_be_rescored(self): |
262 | + """See `IBuildFarmJob`.""" |
263 | + return ( |
264 | + self.buildqueue_record is not None and |
265 | + self.status is BuildStatus.NEEDSBUILD) |
266 | + |
267 | + @property |
268 | + def can_be_cancelled(self): |
269 | + """See `IBuildFarmJob`.""" |
270 | + if not self.buildqueue_record: |
271 | + return False |
272 | + |
273 | + cancellable_statuses = [ |
274 | + BuildStatus.BUILDING, |
275 | + BuildStatus.NEEDSBUILD, |
276 | + ] |
277 | + return self.status in cancellable_statuses |
278 | + |
279 | + def resetBuild(self): |
280 | + """See `IBuildFarmJob`.""" |
281 | + self.build_farm_job.status = self.status = BuildStatus.NEEDSBUILD |
282 | + self.build_farm_job.date_finished = self.date_finished = None |
283 | + self.date_started = None |
284 | + self.build_farm_job.builder = self.builder = None |
285 | + self.log = None |
286 | + self.upload_log = None |
287 | + self.dependencies = None |
288 | + self.failure_count = 0 |
289 | + |
290 | + def retry(self): |
291 | + """See `IBuildFarmJob`.""" |
292 | + if not self.can_be_retried: |
293 | + raise CannotBeRetried(self.id) |
294 | + |
295 | + self.resetBuild() |
296 | + self.queueBuild() |
297 | + |
298 | + def rescore(self, score): |
299 | + """See `IBuildFarmJob`.""" |
300 | + if not self.can_be_rescored: |
301 | + raise CannotBeRescored(self.id) |
302 | + |
303 | + self.buildqueue_record.manualScore(score) |
304 | + |
305 | + def cancel(self): |
306 | + """See `IBuildFarmJob`.""" |
307 | + if not self.can_be_cancelled: |
308 | + return |
309 | + # BuildQueue.cancel() will decide whether to go straight to |
310 | + # CANCELLED, or go through CANCELLING to let buildd-manager clean up |
311 | + # the builder. |
312 | + self.buildqueue_record.cancel() |
313 | + |
314 | |
315 | class SpecificBuildFarmJobSourceMixin: |
316 | |
317 | diff --git a/lib/lp/buildmaster/tests/test_buildfarmjob.py b/lib/lp/buildmaster/tests/test_buildfarmjob.py |
318 | index a10dfb0..911582f 100644 |
319 | --- a/lib/lp/buildmaster/tests/test_buildfarmjob.py |
320 | +++ b/lib/lp/buildmaster/tests/test_buildfarmjob.py |
321 | @@ -21,6 +21,7 @@ from lp.buildmaster.enums import ( |
322 | BuildStatus, |
323 | ) |
324 | from lp.buildmaster.interfaces.buildfarmjob import ( |
325 | + CannotBeRetried, |
326 | IBuildFarmJob, |
327 | IBuildFarmJobSet, |
328 | IBuildFarmJobSource, |
329 | @@ -119,6 +120,7 @@ class TestBuildFarmJobMixin(TestCaseWithFactory): |
330 | |
331 | def test_providesInterface(self): |
332 | # BuildFarmJobMixin derivatives provide IBuildFarmJob |
333 | + login('admin@canonical.com') |
334 | self.assertProvides(self.build_farm_job, IBuildFarmJob) |
335 | |
336 | def test_duration_none(self): |
337 | @@ -149,7 +151,7 @@ class TestBuildFarmJobMixin(TestCaseWithFactory): |
338 | def test_edit_build_farm_job(self): |
339 | # Users with edit access can update attributes. |
340 | login('admin@canonical.com') |
341 | - self.assertRaises(AssertionError, self.build_farm_job.retry) |
342 | + self.assertRaises(CannotBeRetried, self.build_farm_job.retry) |
343 | |
344 | def test_updateStatus_sets_status(self): |
345 | # updateStatus always sets status. |
346 | diff --git a/lib/lp/buildmaster/tests/test_packagebuild.py b/lib/lp/buildmaster/tests/test_packagebuild.py |
347 | index 90d5adb..8a1b51c 100644 |
348 | --- a/lib/lp/buildmaster/tests/test_packagebuild.py |
349 | +++ b/lib/lp/buildmaster/tests/test_packagebuild.py |
350 | @@ -35,6 +35,7 @@ class TestPackageBuildMixin(TestCaseWithFactory): |
351 | |
352 | def test_providesInterface(self): |
353 | # PackageBuild provides IPackageBuild |
354 | + login('admin@canonical.com') |
355 | self.assertProvides(self.package_build, IPackageBuild) |
356 | |
357 | def test_updateStatus_MANUALDEPWAIT_sets_dependencies(self): |
358 | diff --git a/lib/lp/charms/interfaces/charmrecipebuild.py b/lib/lp/charms/interfaces/charmrecipebuild.py |
359 | index 4a33e19..2bbfe04 100644 |
360 | --- a/lib/lp/charms/interfaces/charmrecipebuild.py |
361 | +++ b/lib/lp/charms/interfaces/charmrecipebuild.py |
362 | @@ -23,7 +23,6 @@ from lazr.restful.declarations import ( |
363 | exported, |
364 | exported_as_webservice_entry, |
365 | operation_for_version, |
366 | - operation_parameters, |
367 | ) |
368 | from lazr.restful.fields import ( |
369 | CollectionField, |
370 | @@ -43,8 +42,15 @@ from zope.schema import ( |
371 | ) |
372 | |
373 | from lp import _ |
374 | -from lp.buildmaster.interfaces.buildfarmjob import ISpecificBuildFarmJobSource |
375 | -from lp.buildmaster.interfaces.packagebuild import IPackageBuild |
376 | +from lp.buildmaster.interfaces.buildfarmjob import ( |
377 | + IBuildFarmJobAdmin, |
378 | + IBuildFarmJobEdit, |
379 | + ISpecificBuildFarmJobSource, |
380 | + ) |
381 | +from lp.buildmaster.interfaces.packagebuild import ( |
382 | + IPackageBuild, |
383 | + IPackageBuildView, |
384 | + ) |
385 | from lp.charms.interfaces.charmrecipe import ( |
386 | ICharmRecipe, |
387 | ICharmRecipeBuildRequest, |
388 | @@ -100,7 +106,7 @@ class CharmRecipeBuildStoreUploadStatus(EnumeratedType): |
389 | """) |
390 | |
391 | |
392 | -class ICharmRecipeBuildView(IPackageBuild): |
393 | +class ICharmRecipeBuildView(IPackageBuildView): |
394 | """`ICharmRecipeBuild` attributes that require launchpad.View.""" |
395 | |
396 | build_request = Reference( |
397 | @@ -141,21 +147,6 @@ class ICharmRecipeBuildView(IPackageBuild): |
398 | title=_("Score of the related build farm job (if any)."), |
399 | required=False, readonly=True)) |
400 | |
401 | - can_be_rescored = exported(Bool( |
402 | - title=_("Can be rescored"), |
403 | - required=True, readonly=True, |
404 | - description=_("Whether this build record can be rescored manually."))) |
405 | - |
406 | - can_be_retried = exported(Bool( |
407 | - title=_("Can be retried"), |
408 | - required=False, readonly=True, |
409 | - description=_("Whether this build record can be retried."))) |
410 | - |
411 | - can_be_cancelled = exported(Bool( |
412 | - title=_("Can be cancelled"), |
413 | - required=True, readonly=True, |
414 | - description=_("Whether this build record can be cancelled."))) |
415 | - |
416 | eta = Datetime( |
417 | title=_("The datetime when the build job is estimated to complete."), |
418 | readonly=True) |
419 | @@ -229,7 +220,7 @@ class ICharmRecipeBuildView(IPackageBuild): |
420 | """ |
421 | |
422 | |
423 | -class ICharmRecipeBuildEdit(Interface): |
424 | +class ICharmRecipeBuildEdit(IBuildFarmJobEdit): |
425 | """`ICharmRecipeBuild` methods that require launchpad.Edit.""" |
426 | |
427 | def addFile(lfa): |
428 | @@ -248,48 +239,18 @@ class ICharmRecipeBuildEdit(Interface): |
429 | where an upload can be scheduled. |
430 | """ |
431 | |
432 | - @export_write_operation() |
433 | - @operation_for_version("devel") |
434 | - def retry(): |
435 | - """Restore the build record to its initial state. |
436 | - |
437 | - Build record loses its history, is moved to NEEDSBUILD and a new |
438 | - non-scored BuildQueue entry is created for it. |
439 | - """ |
440 | - |
441 | - @export_write_operation() |
442 | - @operation_for_version("devel") |
443 | - def cancel(): |
444 | - """Cancel the build if it is either pending or in progress. |
445 | - |
446 | - Check the can_be_cancelled property prior to calling this method to |
447 | - find out if cancelling the build is possible. |
448 | - |
449 | - If the build is in progress, it is marked as CANCELLING until the |
450 | - buildd manager terminates the build and marks it CANCELLED. If the |
451 | - build is not in progress, it is marked CANCELLED immediately and is |
452 | - removed from the build queue. |
453 | - |
454 | - If the build is not in a cancellable state, this method is a no-op. |
455 | - """ |
456 | - |
457 | |
458 | -class ICharmRecipeBuildAdmin(Interface): |
459 | +class ICharmRecipeBuildAdmin(IBuildFarmJobAdmin): |
460 | """`ICharmRecipeBuild` methods that require launchpad.Admin.""" |
461 | |
462 | - @operation_parameters(score=Int(title=_("Score"), required=True)) |
463 | - @export_write_operation() |
464 | - @operation_for_version("devel") |
465 | - def rescore(score): |
466 | - """Change the build's score.""" |
467 | - |
468 | |
469 | # XXX cjwatson 2021-09-15 bug=760849: "beta" is a lie to get WADL |
470 | # generation working. Individual attributes must set their version to |
471 | # "devel". |
472 | @exported_as_webservice_entry(as_of="beta") |
473 | class ICharmRecipeBuild( |
474 | - ICharmRecipeBuildView, ICharmRecipeBuildEdit, ICharmRecipeBuildAdmin): |
475 | + ICharmRecipeBuildView, ICharmRecipeBuildEdit, ICharmRecipeBuildAdmin, |
476 | + IPackageBuild): |
477 | """A build record for a charm recipe.""" |
478 | |
479 | |
480 | diff --git a/lib/lp/charms/model/charmrecipebuild.py b/lib/lp/charms/model/charmrecipebuild.py |
481 | index cbcdd9e..2374924 100644 |
482 | --- a/lib/lp/charms/model/charmrecipebuild.py |
483 | +++ b/lib/lp/charms/model/charmrecipebuild.py |
484 | @@ -220,70 +220,12 @@ class CharmRecipeBuild(PackageBuildMixin, StormBase): |
485 | |
486 | @property |
487 | def can_be_retried(self): |
488 | - """See `ICharmRecipeBuild`.""" |
489 | + """See `IBuildFarmJob`.""" |
490 | # First check that the behaviour would accept the build if it |
491 | # succeeded. |
492 | if self.distro_series.status == SeriesStatus.OBSOLETE: |
493 | return False |
494 | - |
495 | - failed_statuses = [ |
496 | - BuildStatus.FAILEDTOBUILD, |
497 | - BuildStatus.MANUALDEPWAIT, |
498 | - BuildStatus.CHROOTWAIT, |
499 | - BuildStatus.FAILEDTOUPLOAD, |
500 | - BuildStatus.CANCELLED, |
501 | - BuildStatus.SUPERSEDED, |
502 | - ] |
503 | - |
504 | - # If the build is currently in any of the failed states, |
505 | - # it may be retried. |
506 | - return self.status in failed_statuses |
507 | - |
508 | - @property |
509 | - def can_be_rescored(self): |
510 | - """See `ICharmRecipeBuild`.""" |
511 | - return ( |
512 | - self.buildqueue_record is not None and |
513 | - self.status is BuildStatus.NEEDSBUILD) |
514 | - |
515 | - @property |
516 | - def can_be_cancelled(self): |
517 | - """See `ICharmRecipeBuild`.""" |
518 | - if not self.buildqueue_record: |
519 | - return False |
520 | - |
521 | - cancellable_statuses = [ |
522 | - BuildStatus.BUILDING, |
523 | - BuildStatus.NEEDSBUILD, |
524 | - ] |
525 | - return self.status in cancellable_statuses |
526 | - |
527 | - def retry(self): |
528 | - """See `ICharmRecipeBuild`.""" |
529 | - assert self.can_be_retried, "Build %s cannot be retried" % self.id |
530 | - self.build_farm_job.status = self.status = BuildStatus.NEEDSBUILD |
531 | - self.build_farm_job.date_finished = self.date_finished = None |
532 | - self.date_started = None |
533 | - self.build_farm_job.builder = self.builder = None |
534 | - self.log = None |
535 | - self.upload_log = None |
536 | - self.dependencies = None |
537 | - self.failure_count = 0 |
538 | - self.queueBuild() |
539 | - |
540 | - def rescore(self, score): |
541 | - """See `ICharmRecipeBuild`.""" |
542 | - assert self.can_be_rescored, "Build %s cannot be rescored" % self.id |
543 | - self.buildqueue_record.manualScore(score) |
544 | - |
545 | - def cancel(self): |
546 | - """See `ICharmRecipeBuild`.""" |
547 | - if not self.can_be_cancelled: |
548 | - return |
549 | - # BuildQueue.cancel() will decide whether to go straight to |
550 | - # CANCELLED, or go through CANCELLING to let buildd-manager clean up |
551 | - # the slave. |
552 | - self.buildqueue_record.cancel() |
553 | + return super().can_be_retried |
554 | |
555 | def calculateScore(self): |
556 | """See `IBuildFarmJob`.""" |
557 | diff --git a/lib/lp/code/configure.zcml b/lib/lp/code/configure.zcml |
558 | index fb8e0d5..b5399af 100644 |
559 | --- a/lib/lp/code/configure.zcml |
560 | +++ b/lib/lp/code/configure.zcml |
561 | @@ -1196,6 +1196,9 @@ |
562 | <require |
563 | permission="launchpad.Edit" |
564 | interface="lp.code.interfaces.sourcepackagerecipebuild.ISourcePackageRecipeBuildEdit"/> |
565 | + <require |
566 | + permission="launchpad.Admin" |
567 | + interface="lp.code.interfaces.sourcepackagerecipebuild.ISourcePackageRecipeBuildAdmin"/> |
568 | </class> |
569 | |
570 | <securedutility |
571 | diff --git a/lib/lp/code/interfaces/sourcepackagerecipebuild.py b/lib/lp/code/interfaces/sourcepackagerecipebuild.py |
572 | index 82375f7..12fe659 100644 |
573 | --- a/lib/lp/code/interfaces/sourcepackagerecipebuild.py |
574 | +++ b/lib/lp/code/interfaces/sourcepackagerecipebuild.py |
575 | @@ -8,26 +8,26 @@ __all__ = [ |
576 | 'ISourcePackageRecipeBuildSource', |
577 | ] |
578 | |
579 | -from lazr.restful.declarations import ( |
580 | - export_write_operation, |
581 | - exported, |
582 | - exported_as_webservice_entry, |
583 | - operation_for_version, |
584 | - ) |
585 | +from lazr.restful.declarations import exported_as_webservice_entry |
586 | from lazr.restful.fields import ( |
587 | CollectionField, |
588 | Reference, |
589 | ) |
590 | -from zope.interface import Interface |
591 | from zope.schema import ( |
592 | - Bool, |
593 | Int, |
594 | Object, |
595 | ) |
596 | |
597 | from lp import _ |
598 | -from lp.buildmaster.interfaces.buildfarmjob import ISpecificBuildFarmJobSource |
599 | -from lp.buildmaster.interfaces.packagebuild import IPackageBuild |
600 | +from lp.buildmaster.interfaces.buildfarmjob import ( |
601 | + IBuildFarmJobAdmin, |
602 | + IBuildFarmJobEdit, |
603 | + ISpecificBuildFarmJobSource, |
604 | + ) |
605 | +from lp.buildmaster.interfaces.packagebuild import ( |
606 | + IPackageBuild, |
607 | + IPackageBuildView, |
608 | + ) |
609 | from lp.code.interfaces.sourcepackagerecipe import ( |
610 | ISourcePackageRecipe, |
611 | ISourcePackageRecipeData, |
612 | @@ -38,7 +38,8 @@ from lp.soyuz.interfaces.binarypackagebuild import IBinaryPackageBuild |
613 | from lp.soyuz.interfaces.sourcepackagerelease import ISourcePackageRelease |
614 | |
615 | |
616 | -class ISourcePackageRecipeBuildView(IPackageBuild): |
617 | +class ISourcePackageRecipeBuildView(IPackageBuildView): |
618 | + """`ISourcePackageRecipeBuild` attributes that require launchpad.View.""" |
619 | |
620 | id = Int(title=_("Identifier for this build.")) |
621 | |
622 | @@ -57,16 +58,6 @@ class ISourcePackageRecipeBuildView(IPackageBuild): |
623 | recipe = Object( |
624 | schema=ISourcePackageRecipe, title=_("The recipe being built.")) |
625 | |
626 | - can_be_rescored = exported(Bool( |
627 | - title=_("Can be rescored"), |
628 | - required=True, readonly=True, |
629 | - description=_("Whether this build record can be rescored manually."))) |
630 | - |
631 | - can_be_cancelled = exported(Bool( |
632 | - title=_("Can be cancelled"), |
633 | - required=True, readonly=True, |
634 | - description=_("Whether this build record can be cancelled."))) |
635 | - |
636 | manifest = Object( |
637 | schema=ISourcePackageRecipeData, title=_( |
638 | 'A snapshot of the recipe for this build.')) |
639 | @@ -82,31 +73,22 @@ class ISourcePackageRecipeBuildView(IPackageBuild): |
640 | """Return the file under +files with specified name.""" |
641 | |
642 | |
643 | -class ISourcePackageRecipeBuildEdit(Interface): |
644 | - |
645 | - @export_write_operation() |
646 | - @operation_for_version("devel") |
647 | - def cancel(): |
648 | - """Cancel the build if it is either pending or in progress. |
649 | - |
650 | - Check the can_be_cancelled property prior to calling this method to |
651 | - find out if cancelling the build is possible. |
652 | - |
653 | - If the build is in progress, it is marked as CANCELLING until the |
654 | - buildd manager terminates the build and marks it CANCELLED. If the |
655 | - build is not in progress, it is marked CANCELLED immediately and is |
656 | - removed from the build queue. |
657 | - |
658 | - If the build is not in a cancellable state, this method is a no-op. |
659 | - """ |
660 | +class ISourcePackageRecipeBuildEdit(IBuildFarmJobEdit): |
661 | + """`ISourcePackageRecipeBuild` attributes that require launchpad.Edit.""" |
662 | |
663 | def destroySelf(): |
664 | """Delete the build itself.""" |
665 | |
666 | |
667 | +class ISourcePackageRecipeBuildAdmin(IBuildFarmJobAdmin): |
668 | + """`ISourcePackageRecipeBuild` attributes that require launchpad.Admin.""" |
669 | + |
670 | + |
671 | @exported_as_webservice_entry() |
672 | class ISourcePackageRecipeBuild(ISourcePackageRecipeBuildView, |
673 | - ISourcePackageRecipeBuildEdit): |
674 | + ISourcePackageRecipeBuildEdit, |
675 | + ISourcePackageRecipeBuildAdmin, |
676 | + IPackageBuild): |
677 | """A build of a source package.""" |
678 | |
679 | |
680 | diff --git a/lib/lp/code/model/sourcepackagerecipebuild.py b/lib/lp/code/model/sourcepackagerecipebuild.py |
681 | index 7be41ee..273d606 100644 |
682 | --- a/lib/lp/code/model/sourcepackagerecipebuild.py |
683 | +++ b/lib/lp/code/model/sourcepackagerecipebuild.py |
684 | @@ -263,31 +263,7 @@ class SourcePackageRecipeBuild(SpecificBuildFarmJobSourceMixin, |
685 | builds.append(build) |
686 | return builds |
687 | |
688 | - @property |
689 | - def can_be_rescored(self): |
690 | - """See `IBuild`.""" |
691 | - return self.status is BuildStatus.NEEDSBUILD |
692 | - |
693 | - @property |
694 | - def can_be_cancelled(self): |
695 | - """See `ISourcePackageRecipeBuild`.""" |
696 | - if not self.buildqueue_record: |
697 | - return False |
698 | - |
699 | - cancellable_statuses = [ |
700 | - BuildStatus.BUILDING, |
701 | - BuildStatus.NEEDSBUILD, |
702 | - ] |
703 | - return self.status in cancellable_statuses |
704 | - |
705 | - def cancel(self): |
706 | - """See `ISourcePackageRecipeBuild`.""" |
707 | - if not self.can_be_cancelled: |
708 | - return |
709 | - # BuildQueue.cancel() will decide whether to go straight to |
710 | - # CANCELLED, or go through CANCELLING to let buildd-manager |
711 | - # clean up the slave. |
712 | - self.buildqueue_record.cancel() |
713 | + can_be_retried = False |
714 | |
715 | def destroySelf(self): |
716 | if self.buildqueue_record is not None: |
717 | diff --git a/lib/lp/oci/interfaces/ocirecipebuild.py b/lib/lp/oci/interfaces/ocirecipebuild.py |
718 | index 32aabe0..b366f87 100644 |
719 | --- a/lib/lp/oci/interfaces/ocirecipebuild.py |
720 | +++ b/lib/lp/oci/interfaces/ocirecipebuild.py |
721 | @@ -25,7 +25,6 @@ from lazr.restful.declarations import ( |
722 | exported, |
723 | exported_as_webservice_entry, |
724 | operation_for_version, |
725 | - operation_parameters, |
726 | ) |
727 | from lazr.restful.fields import ( |
728 | CollectionField, |
729 | @@ -47,8 +46,15 @@ from zope.schema import ( |
730 | |
731 | from lp import _ |
732 | from lp.app.interfaces.launchpad import IPrivacy |
733 | -from lp.buildmaster.interfaces.buildfarmjob import ISpecificBuildFarmJobSource |
734 | -from lp.buildmaster.interfaces.packagebuild import IPackageBuild |
735 | +from lp.buildmaster.interfaces.buildfarmjob import ( |
736 | + IBuildFarmJobAdmin, |
737 | + IBuildFarmJobEdit, |
738 | + ISpecificBuildFarmJobSource, |
739 | + ) |
740 | +from lp.buildmaster.interfaces.packagebuild import ( |
741 | + IPackageBuild, |
742 | + IPackageBuildView, |
743 | + ) |
744 | from lp.oci.interfaces.ocirecipe import ( |
745 | IOCIRecipe, |
746 | IOCIRecipeBuildRequest, |
747 | @@ -141,7 +147,7 @@ class OCIRecipeBuildSetRegistryUploadStatus(EnumeratedType): |
748 | """) |
749 | |
750 | |
751 | -class IOCIRecipeBuildView(IPackageBuild, IPrivacy): |
752 | +class IOCIRecipeBuildView(IPackageBuildView, IPrivacy): |
753 | """`IOCIRecipeBuild` attributes that require launchpad.View permission.""" |
754 | |
755 | build_request = Reference( |
756 | @@ -207,21 +213,6 @@ class IOCIRecipeBuildView(IPackageBuild, IPrivacy): |
757 | title=_("Score of the related build farm job (if any)."), |
758 | required=False, readonly=True)) |
759 | |
760 | - can_be_rescored = exported(Bool( |
761 | - title=_("Can be rescored"), |
762 | - required=True, readonly=True, |
763 | - description=_("Whether this build record can be rescored manually."))) |
764 | - |
765 | - can_be_retried = exported(Bool( |
766 | - title=_("Can be retried"), |
767 | - required=True, readonly=True, |
768 | - description=_("Whether this build record can be retried."))) |
769 | - |
770 | - can_be_cancelled = exported(Bool( |
771 | - title=_("Can be cancelled"), |
772 | - required=True, readonly=True, |
773 | - description=_("Whether this build record can be cancelled."))) |
774 | - |
775 | manifest = Attribute(_("The manifest of the image.")) |
776 | |
777 | digests = Attribute(_("File containing the image digests.")) |
778 | @@ -266,7 +257,7 @@ class IOCIRecipeBuildView(IPackageBuild, IPrivacy): |
779 | """ |
780 | |
781 | |
782 | -class IOCIRecipeBuildEdit(Interface): |
783 | +class IOCIRecipeBuildEdit(IBuildFarmJobEdit): |
784 | """`IOCIRecipeBuild` attributes that require launchpad.Edit permission.""" |
785 | |
786 | def addFile(lfa, layer_file_digest): |
787 | @@ -286,46 +277,15 @@ class IOCIRecipeBuildEdit(Interface): |
788 | where an upload can be scheduled. |
789 | """ |
790 | |
791 | - @export_write_operation() |
792 | - @operation_for_version("devel") |
793 | - def retry(): |
794 | - """Restore the build record to its initial state. |
795 | - |
796 | - Build record loses its history, is moved to NEEDSBUILD and a new |
797 | - non-scored BuildQueue entry is created for it. |
798 | - """ |
799 | - |
800 | - @export_write_operation() |
801 | - @operation_for_version("devel") |
802 | - def cancel(): |
803 | - """Cancel the build if it is either pending or in progress. |
804 | - |
805 | - Check the can_be_cancelled property prior to calling this method to |
806 | - find out if cancelling the build is possible. |
807 | - |
808 | - If the build is in progress, it is marked as CANCELLING until the |
809 | - buildd manager terminates the build and marks it CANCELLED. If the |
810 | - build is not in progress, it is marked CANCELLED immediately and is |
811 | - removed from the build queue. |
812 | - |
813 | - If the build is not in a cancellable state, this method is a no-op. |
814 | - """ |
815 | - |
816 | |
817 | -class IOCIRecipeBuildAdmin(Interface): |
818 | +class IOCIRecipeBuildAdmin(IBuildFarmJobAdmin): |
819 | """`IOCIRecipeBuild` attributes that require launchpad.Admin permission.""" |
820 | |
821 | - @operation_parameters(score=Int(title=_("Score"), required=True)) |
822 | - @export_write_operation() |
823 | - @operation_for_version("devel") |
824 | - def rescore(score): |
825 | - """Change the build's score.""" |
826 | - |
827 | |
828 | @exported_as_webservice_entry( |
829 | publish_web_link=True, as_of="devel", singular_name="oci_recipe_build") |
830 | class IOCIRecipeBuild(IOCIRecipeBuildAdmin, IOCIRecipeBuildEdit, |
831 | - IOCIRecipeBuildView): |
832 | + IOCIRecipeBuildView, IPackageBuild): |
833 | """A build record for an OCI recipe.""" |
834 | |
835 | |
836 | diff --git a/lib/lp/oci/model/ocirecipebuild.py b/lib/lp/oci/model/ocirecipebuild.py |
837 | index fc4e3e2..13cc08a 100644 |
838 | --- a/lib/lp/oci/model/ocirecipebuild.py |
839 | +++ b/lib/lp/oci/model/ocirecipebuild.py |
840 | @@ -214,43 +214,12 @@ class OCIRecipeBuild(PackageBuildMixin, StormBase): |
841 | |
842 | @property |
843 | def can_be_retried(self): |
844 | - """See `IOCIRecipeBuild`.""" |
845 | + """See `IBuildFarmJob`.""" |
846 | # First check that the behaviour would accept the build if it |
847 | # succeeded. |
848 | if self.distro_series.status == SeriesStatus.OBSOLETE: |
849 | return False |
850 | - |
851 | - failed_statuses = [ |
852 | - BuildStatus.FAILEDTOBUILD, |
853 | - BuildStatus.MANUALDEPWAIT, |
854 | - BuildStatus.CHROOTWAIT, |
855 | - BuildStatus.FAILEDTOUPLOAD, |
856 | - BuildStatus.CANCELLED, |
857 | - BuildStatus.SUPERSEDED, |
858 | - ] |
859 | - |
860 | - # If the build is currently in any of the failed states, |
861 | - # it may be retried. |
862 | - return self.status in failed_statuses |
863 | - |
864 | - @property |
865 | - def can_be_rescored(self): |
866 | - """See `IOCIRecipeBuild`.""" |
867 | - return ( |
868 | - self.buildqueue_record is not None and |
869 | - self.status is BuildStatus.NEEDSBUILD) |
870 | - |
871 | - @property |
872 | - def can_be_cancelled(self): |
873 | - """See `IOCIRecipeBuild`.""" |
874 | - if not self.buildqueue_record: |
875 | - return False |
876 | - |
877 | - cancellable_statuses = [ |
878 | - BuildStatus.BUILDING, |
879 | - BuildStatus.NEEDSBUILD, |
880 | - ] |
881 | - return self.status in cancellable_statuses |
882 | + return super().can_be_retried |
883 | |
884 | @property |
885 | def is_private(self): |
886 | @@ -271,33 +240,6 @@ class OCIRecipeBuild(PackageBuildMixin, StormBase): |
887 | |
888 | private = is_private |
889 | |
890 | - def retry(self): |
891 | - """See `IOCIRecipeBuild`.""" |
892 | - assert self.can_be_retried, "Build %s cannot be retried" % self.id |
893 | - self.build_farm_job.status = self.status = BuildStatus.NEEDSBUILD |
894 | - self.build_farm_job.date_finished = self.date_finished = None |
895 | - self.date_started = None |
896 | - self.build_farm_job.builder = self.builder = None |
897 | - self.log = None |
898 | - self.upload_log = None |
899 | - self.dependencies = None |
900 | - self.failure_count = 0 |
901 | - self.queueBuild() |
902 | - |
903 | - def rescore(self, score): |
904 | - """See `IOCIRecipeBuild`.""" |
905 | - assert self.can_be_rescored, "Build %s cannot be rescored" % self.id |
906 | - self.buildqueue_record.manualScore(score) |
907 | - |
908 | - def cancel(self): |
909 | - """See `IOCIRecipeBuild`.""" |
910 | - if not self.can_be_cancelled: |
911 | - return |
912 | - # BuildQueue.cancel() will decide whether to go straight to |
913 | - # CANCELLED, or go through CANCELLING to let buildd-manager clean up |
914 | - # the slave. |
915 | - self.buildqueue_record.cancel() |
916 | - |
917 | def calculateScore(self): |
918 | # XXX twom 2020-02-11 - This might need an addition? |
919 | return 2510 |
920 | diff --git a/lib/lp/security.py b/lib/lp/security.py |
921 | index 9f6690e..03c580f 100644 |
922 | --- a/lib/lp/security.py |
923 | +++ b/lib/lp/security.py |
924 | @@ -1666,18 +1666,6 @@ class EditCodeImportMachine(OnlyBazaarExpertsAndAdmins): |
925 | usedfor = ICodeImportMachine |
926 | |
927 | |
928 | -class AdminSourcePackageRecipeBuilds(AuthorizationBase): |
929 | - """Control who can edit SourcePackageRecipeBuilds. |
930 | - |
931 | - Access is restricted to Buildd Admins. |
932 | - """ |
933 | - permission = 'launchpad.Admin' |
934 | - usedfor = ISourcePackageRecipeBuild |
935 | - |
936 | - def checkAuthenticated(self, user): |
937 | - return user.in_buildd_admin |
938 | - |
939 | - |
940 | class AdminDistributionTranslations(AuthorizationBase): |
941 | """Class for deciding who can administer distribution translations. |
942 | |
943 | diff --git a/lib/lp/snappy/interfaces/snapbuild.py b/lib/lp/snappy/interfaces/snapbuild.py |
944 | index 6d41fbe..4012391 100644 |
945 | --- a/lib/lp/snappy/interfaces/snapbuild.py |
946 | +++ b/lib/lp/snappy/interfaces/snapbuild.py |
947 | @@ -25,7 +25,6 @@ from lazr.restful.declarations import ( |
948 | exported, |
949 | exported_as_webservice_entry, |
950 | operation_for_version, |
951 | - operation_parameters, |
952 | ) |
953 | from lazr.restful.fields import ( |
954 | CollectionField, |
955 | @@ -48,8 +47,15 @@ from zope.schema import ( |
956 | |
957 | from lp import _ |
958 | from lp.app.interfaces.launchpad import IPrivacy |
959 | -from lp.buildmaster.interfaces.buildfarmjob import ISpecificBuildFarmJobSource |
960 | -from lp.buildmaster.interfaces.packagebuild import IPackageBuild |
961 | +from lp.buildmaster.interfaces.buildfarmjob import ( |
962 | + IBuildFarmJobAdmin, |
963 | + IBuildFarmJobEdit, |
964 | + ISpecificBuildFarmJobSource, |
965 | + ) |
966 | +from lp.buildmaster.interfaces.packagebuild import ( |
967 | + IPackageBuild, |
968 | + IPackageBuildView, |
969 | + ) |
970 | from lp.registry.interfaces.person import IPerson |
971 | from lp.registry.interfaces.pocket import PackagePublishingPocket |
972 | from lp.services.database.constants import DEFAULT |
973 | @@ -128,7 +134,7 @@ class SnapBuildStoreUploadStatus(EnumeratedType): |
974 | """) |
975 | |
976 | |
977 | -class ISnapBuildView(IPackageBuild, IPrivacy): |
978 | +class ISnapBuildView(IPackageBuildView, IPrivacy): |
979 | """`ISnapBuild` attributes that require launchpad.View permission.""" |
980 | |
981 | build_request = Reference( |
982 | @@ -189,21 +195,6 @@ class ISnapBuildView(IPackageBuild, IPrivacy): |
983 | title=_("Score of the related build farm job (if any)."), |
984 | required=False, readonly=True)) |
985 | |
986 | - can_be_rescored = exported(Bool( |
987 | - title=_("Can be rescored"), |
988 | - required=True, readonly=True, |
989 | - description=_("Whether this build record can be rescored manually."))) |
990 | - |
991 | - can_be_retried = exported(Bool( |
992 | - title=_("Can be retried"), |
993 | - required=False, readonly=True, |
994 | - description=_("Whether this build record can be retried."))) |
995 | - |
996 | - can_be_cancelled = exported(Bool( |
997 | - title=_("Can be cancelled"), |
998 | - required=True, readonly=True, |
999 | - description=_("Whether this build record can be cancelled."))) |
1000 | - |
1001 | eta = Datetime( |
1002 | title=_("The datetime when the build job is estimated to complete."), |
1003 | readonly=True) |
1004 | @@ -298,7 +289,7 @@ class ISnapBuildView(IPackageBuild, IPrivacy): |
1005 | :return: A collection of URLs for this build.""" |
1006 | |
1007 | |
1008 | -class ISnapBuildEdit(Interface): |
1009 | +class ISnapBuildEdit(IBuildFarmJobEdit): |
1010 | """`ISnapBuild` attributes that require launchpad.Edit.""" |
1011 | |
1012 | def addFile(lfa): |
1013 | @@ -317,47 +308,17 @@ class ISnapBuildEdit(Interface): |
1014 | where an upload can be scheduled. |
1015 | """ |
1016 | |
1017 | - @export_write_operation() |
1018 | - @operation_for_version("devel") |
1019 | - def retry(): |
1020 | - """Restore the build record to its initial state. |
1021 | - |
1022 | - Build record loses its history, is moved to NEEDSBUILD and a new |
1023 | - non-scored BuildQueue entry is created for it. |
1024 | - """ |
1025 | - |
1026 | - @export_write_operation() |
1027 | - @operation_for_version("devel") |
1028 | - def cancel(): |
1029 | - """Cancel the build if it is either pending or in progress. |
1030 | - |
1031 | - Check the can_be_cancelled property prior to calling this method to |
1032 | - find out if cancelling the build is possible. |
1033 | - |
1034 | - If the build is in progress, it is marked as CANCELLING until the |
1035 | - buildd manager terminates the build and marks it CANCELLED. If the |
1036 | - build is not in progress, it is marked CANCELLED immediately and is |
1037 | - removed from the build queue. |
1038 | - |
1039 | - If the build is not in a cancellable state, this method is a no-op. |
1040 | - """ |
1041 | - |
1042 | |
1043 | -class ISnapBuildAdmin(Interface): |
1044 | +class ISnapBuildAdmin(IBuildFarmJobAdmin): |
1045 | """`ISnapBuild` attributes that require launchpad.Admin.""" |
1046 | |
1047 | - @operation_parameters(score=Int(title=_("Score"), required=True)) |
1048 | - @export_write_operation() |
1049 | - @operation_for_version("devel") |
1050 | - def rescore(score): |
1051 | - """Change the build's score.""" |
1052 | - |
1053 | |
1054 | # XXX cjwatson 2014-05-06 bug=760849: "beta" is a lie to get WADL |
1055 | # generation working. Individual attributes must set their version to |
1056 | # "devel". |
1057 | @exported_as_webservice_entry(as_of="beta") |
1058 | -class ISnapBuild(ISnapBuildView, ISnapBuildEdit, ISnapBuildAdmin): |
1059 | +class ISnapBuild( |
1060 | + ISnapBuildView, ISnapBuildEdit, ISnapBuildAdmin, IPackageBuild): |
1061 | """Build information for snap package builds.""" |
1062 | |
1063 | |
1064 | diff --git a/lib/lp/snappy/model/snapbuild.py b/lib/lp/snappy/model/snapbuild.py |
1065 | index a4405de..24fa63f 100644 |
1066 | --- a/lib/lp/snappy/model/snapbuild.py |
1067 | +++ b/lib/lp/snappy/model/snapbuild.py |
1068 | @@ -284,70 +284,12 @@ class SnapBuild(PackageBuildMixin, Storm): |
1069 | |
1070 | @property |
1071 | def can_be_retried(self): |
1072 | - """See `ISnapBuild`.""" |
1073 | + """See `IBuildFarmJob`.""" |
1074 | # First check that the behaviour would accept the build if it |
1075 | # succeeded. |
1076 | if self.distro_series.status == SeriesStatus.OBSOLETE: |
1077 | return False |
1078 | - |
1079 | - failed_statuses = [ |
1080 | - BuildStatus.FAILEDTOBUILD, |
1081 | - BuildStatus.MANUALDEPWAIT, |
1082 | - BuildStatus.CHROOTWAIT, |
1083 | - BuildStatus.FAILEDTOUPLOAD, |
1084 | - BuildStatus.CANCELLED, |
1085 | - BuildStatus.SUPERSEDED, |
1086 | - ] |
1087 | - |
1088 | - # If the build is currently in any of the failed states, |
1089 | - # it may be retried. |
1090 | - return self.status in failed_statuses |
1091 | - |
1092 | - @property |
1093 | - def can_be_rescored(self): |
1094 | - """See `ISnapBuild`.""" |
1095 | - return ( |
1096 | - self.buildqueue_record is not None and |
1097 | - self.status is BuildStatus.NEEDSBUILD) |
1098 | - |
1099 | - @property |
1100 | - def can_be_cancelled(self): |
1101 | - """See `ISnapBuild`.""" |
1102 | - if not self.buildqueue_record: |
1103 | - return False |
1104 | - |
1105 | - cancellable_statuses = [ |
1106 | - BuildStatus.BUILDING, |
1107 | - BuildStatus.NEEDSBUILD, |
1108 | - ] |
1109 | - return self.status in cancellable_statuses |
1110 | - |
1111 | - def retry(self): |
1112 | - """See `ISnapBuild`.""" |
1113 | - assert self.can_be_retried, "Build %s cannot be retried" % self.id |
1114 | - self.build_farm_job.status = self.status = BuildStatus.NEEDSBUILD |
1115 | - self.build_farm_job.date_finished = self.date_finished = None |
1116 | - self.date_started = None |
1117 | - self.build_farm_job.builder = self.builder = None |
1118 | - self.log = None |
1119 | - self.upload_log = None |
1120 | - self.dependencies = None |
1121 | - self.failure_count = 0 |
1122 | - self.queueBuild() |
1123 | - |
1124 | - def rescore(self, score): |
1125 | - """See `ISnapBuild`.""" |
1126 | - assert self.can_be_rescored, "Build %s cannot be rescored" % self.id |
1127 | - self.buildqueue_record.manualScore(score) |
1128 | - |
1129 | - def cancel(self): |
1130 | - """See `ISnapBuild`.""" |
1131 | - if not self.can_be_cancelled: |
1132 | - return |
1133 | - # BuildQueue.cancel() will decide whether to go straight to |
1134 | - # CANCELLED, or go through CANCELLING to let buildd-manager clean up |
1135 | - # the slave. |
1136 | - self.buildqueue_record.cancel() |
1137 | + return super().can_be_retried |
1138 | |
1139 | def calculateScore(self): |
1140 | return 2510 + self.archive.relative_build_score |
1141 | diff --git a/lib/lp/soyuz/interfaces/binarypackagebuild.py b/lib/lp/soyuz/interfaces/binarypackagebuild.py |
1142 | index 3771af8..edc245f 100644 |
1143 | --- a/lib/lp/soyuz/interfaces/binarypackagebuild.py |
1144 | +++ b/lib/lp/soyuz/interfaces/binarypackagebuild.py |
1145 | @@ -5,21 +5,17 @@ |
1146 | |
1147 | __all__ = [ |
1148 | 'BuildSetStatus', |
1149 | - 'CannotBeRescored', |
1150 | 'IBinaryPackageBuild', |
1151 | 'IBuildRescoreForm', |
1152 | 'IBinaryPackageBuildSet', |
1153 | 'UnparsableDependencies', |
1154 | ] |
1155 | |
1156 | -import http.client |
1157 | - |
1158 | from lazr.enum import ( |
1159 | EnumeratedType, |
1160 | Item, |
1161 | ) |
1162 | from lazr.restful.declarations import ( |
1163 | - error_status, |
1164 | export_read_operation, |
1165 | export_write_operation, |
1166 | exported, |
1167 | @@ -42,24 +38,25 @@ from zope.schema import ( |
1168 | |
1169 | from lp import _ |
1170 | from lp.buildmaster.enums import BuildStatus |
1171 | -from lp.buildmaster.interfaces.buildfarmjob import ISpecificBuildFarmJobSource |
1172 | -from lp.buildmaster.interfaces.packagebuild import IPackageBuild |
1173 | +from lp.buildmaster.interfaces.buildfarmjob import ( |
1174 | + IBuildFarmJobAdmin, |
1175 | + IBuildFarmJobEdit, |
1176 | + ISpecificBuildFarmJobSource, |
1177 | + ) |
1178 | +from lp.buildmaster.interfaces.packagebuild import ( |
1179 | + IPackageBuild, |
1180 | + IPackageBuildView, |
1181 | + ) |
1182 | from lp.buildmaster.interfaces.processor import IProcessor |
1183 | from lp.soyuz.interfaces.publishing import ISourcePackagePublishingHistory |
1184 | from lp.soyuz.interfaces.sourcepackagerelease import ISourcePackageRelease |
1185 | |
1186 | |
1187 | -@error_status(http.client.BAD_REQUEST) |
1188 | -class CannotBeRescored(Exception): |
1189 | - """Raised when rescoring a build that cannot be rescored.""" |
1190 | - _message_prefix = "Cannot rescore build" |
1191 | - |
1192 | - |
1193 | class UnparsableDependencies(Exception): |
1194 | """Raised when parsing invalid dependencies on a binary package.""" |
1195 | |
1196 | |
1197 | -class IBinaryPackageBuildView(IPackageBuild): |
1198 | +class IBinaryPackageBuildView(IPackageBuildView): |
1199 | """A Build interface for items requiring launchpad.View.""" |
1200 | id = Int(title=_('ID'), required=True, readonly=True) |
1201 | |
1202 | @@ -116,25 +113,6 @@ class IBinaryPackageBuildView(IPackageBuild): |
1203 | "A list of distroarchseriesbinarypackages that resulted from this" |
1204 | "build, ordered by name.") |
1205 | |
1206 | - can_be_rescored = exported( |
1207 | - Bool( |
1208 | - title=_("Can Be Rescored"), required=False, readonly=True, |
1209 | - description=_( |
1210 | - "Whether or not this build record can be rescored " |
1211 | - "manually."))) |
1212 | - |
1213 | - can_be_retried = exported( |
1214 | - Bool( |
1215 | - title=_("Can Be Retried"), required=False, readonly=True, |
1216 | - description=_( |
1217 | - "Whether or not this build record can be retried."))) |
1218 | - |
1219 | - can_be_cancelled = exported( |
1220 | - Bool( |
1221 | - title=_("Can Be Cancelled"), required=False, readonly=True, |
1222 | - description=_( |
1223 | - "Whether or not this build record can be cancelled."))) |
1224 | - |
1225 | upload_changesfile = Attribute( |
1226 | "The `LibraryFileAlias` object containing the changes file which " |
1227 | "was originally uploaded with the results of this build. It's " |
1228 | @@ -243,10 +221,19 @@ class IBinaryPackageBuildView(IPackageBuild): |
1229 | """ |
1230 | |
1231 | |
1232 | -class IBinaryPackageBuildEdit(Interface): |
1233 | +class IBinaryPackageBuildEdit(IBuildFarmJobEdit): |
1234 | """A Build interface for items requiring launchpad.Edit.""" |
1235 | |
1236 | + def addBuildInfo(buildinfo): |
1237 | + """Add a buildinfo file to this build. |
1238 | + |
1239 | + :param buildinfo: An `ILibraryFileAlias`. |
1240 | + """ |
1241 | + |
1242 | + # Redeclaring from IBuildFarmJobEdit.retry since this was available in |
1243 | + # the beta version. |
1244 | @export_write_operation() |
1245 | + @operation_for_version("beta") |
1246 | def retry(): |
1247 | """Restore the build record to its initial state. |
1248 | |
1249 | @@ -254,28 +241,6 @@ class IBinaryPackageBuildEdit(Interface): |
1250 | non-scored BuildQueue entry is created for it. |
1251 | """ |
1252 | |
1253 | - @export_write_operation() |
1254 | - @operation_for_version("devel") |
1255 | - def cancel(): |
1256 | - """Cancel the build if it is either pending or in progress. |
1257 | - |
1258 | - Check the can_be_cancelled property prior to calling this method to |
1259 | - find out if cancelling the build is possible. |
1260 | - |
1261 | - If the build is in progress, it is marked as CANCELLING until the |
1262 | - buildd manager terminates the build and marks it CANCELLED. If the |
1263 | - build is not in progress, it is marked CANCELLED immediately and is |
1264 | - removed from the build queue. |
1265 | - |
1266 | - If the build is not in a cancellable state, this method is a no-op. |
1267 | - """ |
1268 | - |
1269 | - def addBuildInfo(buildinfo): |
1270 | - """Add a buildinfo file to this build. |
1271 | - |
1272 | - :param buildinfo: An `ILibraryFileAlias`. |
1273 | - """ |
1274 | - |
1275 | |
1276 | class IBinaryPackageBuildRestricted(Interface): |
1277 | """Restricted `IBinaryPackageBuild` attributes. |
1278 | @@ -297,11 +262,14 @@ class IBinaryPackageBuildRestricted(Interface): |
1279 | exported_as="external_dependencies") |
1280 | |
1281 | |
1282 | -class IBinaryPackageBuildAdmin(Interface): |
1283 | +class IBinaryPackageBuildAdmin(IBuildFarmJobAdmin): |
1284 | """A Build interface for items requiring launchpad.Admin.""" |
1285 | |
1286 | + # Redeclaring from IBuildFarmJobEdit.rescore since this was available in |
1287 | + # the beta version. |
1288 | @operation_parameters(score=Int(title=_("Score"), required=True)) |
1289 | @export_write_operation() |
1290 | + @operation_for_version("beta") |
1291 | def rescore(score): |
1292 | """Change the build's score.""" |
1293 | |
1294 | @@ -309,7 +277,8 @@ class IBinaryPackageBuildAdmin(Interface): |
1295 | @exported_as_webservice_entry(singular_name='build', plural_name='builds') |
1296 | class IBinaryPackageBuild( |
1297 | IBinaryPackageBuildView, IBinaryPackageBuildEdit, |
1298 | - IBinaryPackageBuildRestricted, IBinaryPackageBuildAdmin): |
1299 | + IBinaryPackageBuildRestricted, IBinaryPackageBuildAdmin, |
1300 | + IPackageBuild): |
1301 | """A Build interface""" |
1302 | |
1303 | |
1304 | diff --git a/lib/lp/soyuz/interfaces/livefsbuild.py b/lib/lp/soyuz/interfaces/livefsbuild.py |
1305 | index cbba840..5ed7098 100644 |
1306 | --- a/lib/lp/soyuz/interfaces/livefsbuild.py |
1307 | +++ b/lib/lp/soyuz/interfaces/livefsbuild.py |
1308 | @@ -11,11 +11,9 @@ __all__ = [ |
1309 | |
1310 | from lazr.restful.declarations import ( |
1311 | export_read_operation, |
1312 | - export_write_operation, |
1313 | exported, |
1314 | exported_as_webservice_entry, |
1315 | operation_for_version, |
1316 | - operation_parameters, |
1317 | ) |
1318 | from lazr.restful.fields import Reference |
1319 | from zope.interface import Interface |
1320 | @@ -29,8 +27,15 @@ from zope.schema import ( |
1321 | |
1322 | from lp import _ |
1323 | from lp.app.interfaces.launchpad import IPrivacy |
1324 | -from lp.buildmaster.interfaces.buildfarmjob import ISpecificBuildFarmJobSource |
1325 | -from lp.buildmaster.interfaces.packagebuild import IPackageBuild |
1326 | +from lp.buildmaster.interfaces.buildfarmjob import ( |
1327 | + IBuildFarmJobAdmin, |
1328 | + IBuildFarmJobEdit, |
1329 | + ISpecificBuildFarmJobSource, |
1330 | + ) |
1331 | +from lp.buildmaster.interfaces.packagebuild import ( |
1332 | + IPackageBuild, |
1333 | + IPackageBuildView, |
1334 | + ) |
1335 | from lp.registry.interfaces.person import IPerson |
1336 | from lp.registry.interfaces.pocket import PackagePublishingPocket |
1337 | from lp.services.database.constants import DEFAULT |
1338 | @@ -53,7 +58,7 @@ class ILiveFSFile(Interface): |
1339 | required=True, readonly=True) |
1340 | |
1341 | |
1342 | -class ILiveFSBuildView(IPackageBuild, IPrivacy): |
1343 | +class ILiveFSBuildView(IPackageBuildView, IPrivacy): |
1344 | """`ILiveFSBuild` attributes that require launchpad.View permission.""" |
1345 | |
1346 | requester = exported(Reference( |
1347 | @@ -103,16 +108,6 @@ class ILiveFSBuildView(IPackageBuild, IPrivacy): |
1348 | title=_("Score of the related build farm job (if any)."), |
1349 | required=False, readonly=True)) |
1350 | |
1351 | - can_be_rescored = exported(Bool( |
1352 | - title=_("Can be rescored"), |
1353 | - required=True, readonly=True, |
1354 | - description=_("Whether this build record can be rescored manually."))) |
1355 | - |
1356 | - can_be_cancelled = exported(Bool( |
1357 | - title=_("Can be cancelled"), |
1358 | - required=True, readonly=True, |
1359 | - description=_("Whether this build record can be cancelled."))) |
1360 | - |
1361 | def getFiles(): |
1362 | """Retrieve the build's `ILiveFSFile` records. |
1363 | |
1364 | @@ -144,7 +139,7 @@ class ILiveFSBuildView(IPackageBuild, IPrivacy): |
1365 | :return: A collection of URLs for this build.""" |
1366 | |
1367 | |
1368 | -class ILiveFSBuildEdit(Interface): |
1369 | +class ILiveFSBuildEdit(IBuildFarmJobEdit): |
1370 | """`ILiveFSBuild` attributes that require launchpad.Edit.""" |
1371 | |
1372 | def addFile(lfa): |
1373 | @@ -154,38 +149,17 @@ class ILiveFSBuildEdit(Interface): |
1374 | :return: An `ILiveFSFile`. |
1375 | """ |
1376 | |
1377 | - @export_write_operation() |
1378 | - @operation_for_version("devel") |
1379 | - def cancel(): |
1380 | - """Cancel the build if it is either pending or in progress. |
1381 | - |
1382 | - Check the can_be_cancelled property prior to calling this method to |
1383 | - find out if cancelling the build is possible. |
1384 | - |
1385 | - If the build is in progress, it is marked as CANCELLING until the |
1386 | - buildd manager terminates the build and marks it CANCELLED. If the |
1387 | - build is not in progress, it is marked CANCELLED immediately and is |
1388 | - removed from the build queue. |
1389 | - |
1390 | - If the build is not in a cancellable state, this method is a no-op. |
1391 | - """ |
1392 | - |
1393 | |
1394 | -class ILiveFSBuildAdmin(Interface): |
1395 | +class ILiveFSBuildAdmin(IBuildFarmJobAdmin): |
1396 | """`ILiveFSBuild` attributes that require launchpad.Admin.""" |
1397 | |
1398 | - @operation_parameters(score=Int(title=_("Score"), required=True)) |
1399 | - @export_write_operation() |
1400 | - @operation_for_version("devel") |
1401 | - def rescore(score): |
1402 | - """Change the build's score.""" |
1403 | - |
1404 | |
1405 | # XXX cjwatson 2014-05-06 bug=760849: "beta" is a lie to get WADL |
1406 | # generation working. Individual attributes must set their version to |
1407 | # "devel". |
1408 | @exported_as_webservice_entry(singular_name="livefs_build", as_of="beta") |
1409 | -class ILiveFSBuild(ILiveFSBuildView, ILiveFSBuildEdit, ILiveFSBuildAdmin): |
1410 | +class ILiveFSBuild( |
1411 | + ILiveFSBuildView, ILiveFSBuildEdit, ILiveFSBuildAdmin, IPackageBuild): |
1412 | """Build information for live filesystem builds.""" |
1413 | |
1414 | |
1415 | diff --git a/lib/lp/soyuz/interfaces/webservice.py b/lib/lp/soyuz/interfaces/webservice.py |
1416 | index 90ef0e1..b8ecec2 100644 |
1417 | --- a/lib/lp/soyuz/interfaces/webservice.py |
1418 | +++ b/lib/lp/soyuz/interfaces/webservice.py |
1419 | @@ -13,7 +13,6 @@ __all__ = [ |
1420 | 'AlreadySubscribed', |
1421 | 'ArchiveDisabled', |
1422 | 'ArchiveNotPrivate', |
1423 | - 'CannotBeRescored', |
1424 | 'CannotCopy', |
1425 | 'CannotSwitchPrivacy', |
1426 | 'CannotUploadToArchive', |
1427 | @@ -81,10 +80,7 @@ from lp.soyuz.interfaces.archive import ( |
1428 | from lp.soyuz.interfaces.archivedependency import IArchiveDependency |
1429 | from lp.soyuz.interfaces.archivepermission import IArchivePermission |
1430 | from lp.soyuz.interfaces.archivesubscriber import IArchiveSubscriber |
1431 | -from lp.soyuz.interfaces.binarypackagebuild import ( |
1432 | - CannotBeRescored, |
1433 | - IBinaryPackageBuild, |
1434 | - ) |
1435 | +from lp.soyuz.interfaces.binarypackagebuild import IBinaryPackageBuild |
1436 | from lp.soyuz.interfaces.binarypackagerelease import ( |
1437 | IBinaryPackageReleaseDownloadCount, |
1438 | ) |
1439 | diff --git a/lib/lp/soyuz/model/binarypackagebuild.py b/lib/lp/soyuz/model/binarypackagebuild.py |
1440 | index 511c975..f51f565 100644 |
1441 | --- a/lib/lp/soyuz/model/binarypackagebuild.py |
1442 | +++ b/lib/lp/soyuz/model/binarypackagebuild.py |
1443 | @@ -101,7 +101,6 @@ from lp.soyuz.interfaces.archive import ( |
1444 | ) |
1445 | from lp.soyuz.interfaces.binarypackagebuild import ( |
1446 | BuildSetStatus, |
1447 | - CannotBeRescored, |
1448 | IBinaryPackageBuild, |
1449 | IBinaryPackageBuildSet, |
1450 | UnparsableDependencies, |
1451 | @@ -475,64 +474,19 @@ class BinaryPackageBuild(PackageBuildMixin, SQLBase): |
1452 | |
1453 | @property |
1454 | def can_be_retried(self): |
1455 | - """See `IBuild`.""" |
1456 | + """See `IBuildFarmJob`.""" |
1457 | # First check that the slave scanner would pick up the build record |
1458 | # if we reset it. |
1459 | if not self.archive.canModifySuite(self.distro_series, self.pocket): |
1460 | # The slave scanner would not pick this up, so it cannot be |
1461 | # re-tried. |
1462 | return False |
1463 | + return super().can_be_retried |
1464 | |
1465 | - failed_statuses = [ |
1466 | - BuildStatus.FAILEDTOBUILD, |
1467 | - BuildStatus.MANUALDEPWAIT, |
1468 | - BuildStatus.CHROOTWAIT, |
1469 | - BuildStatus.FAILEDTOUPLOAD, |
1470 | - BuildStatus.CANCELLED, |
1471 | - BuildStatus.SUPERSEDED, |
1472 | - ] |
1473 | - |
1474 | - # If the build is currently in any of the failed states, |
1475 | - # it may be retried. |
1476 | - return self.status in failed_statuses |
1477 | - |
1478 | - @property |
1479 | - def can_be_rescored(self): |
1480 | - """See `IBuild`.""" |
1481 | - return self.status is BuildStatus.NEEDSBUILD |
1482 | - |
1483 | - @property |
1484 | - def can_be_cancelled(self): |
1485 | - """See `IBuild`.""" |
1486 | - if not self.buildqueue_record: |
1487 | - return False |
1488 | - |
1489 | - cancellable_statuses = [ |
1490 | - BuildStatus.BUILDING, |
1491 | - BuildStatus.NEEDSBUILD, |
1492 | - ] |
1493 | - return self.status in cancellable_statuses |
1494 | - |
1495 | - def retry(self): |
1496 | - """See `IBuild`.""" |
1497 | - assert self.can_be_retried, "Build %s cannot be retried" % self.id |
1498 | - self.build_farm_job.status = self.status = BuildStatus.NEEDSBUILD |
1499 | - self.build_farm_job.date_finished = self.date_finished = None |
1500 | - self.date_started = None |
1501 | - self.build_farm_job.builder = self.builder = None |
1502 | - self.log = None |
1503 | - self.upload_log = None |
1504 | - self.dependencies = None |
1505 | - self.failure_count = 0 |
1506 | + def resetBuild(self): |
1507 | + """See `IBuildFarmJob`.""" |
1508 | + super().resetBuild() |
1509 | self.virtualized = is_build_virtualized(self.archive, self.processor) |
1510 | - self.queueBuild() |
1511 | - |
1512 | - def rescore(self, score): |
1513 | - """See `IBuild`.""" |
1514 | - if not self.can_be_rescored: |
1515 | - raise CannotBeRescored("Build cannot be rescored.") |
1516 | - |
1517 | - self.buildqueue_record.manualScore(score) |
1518 | |
1519 | @property |
1520 | def api_score(self): |
1521 | @@ -543,15 +497,6 @@ class BinaryPackageBuild(PackageBuildMixin, SQLBase): |
1522 | else: |
1523 | return self.buildqueue_record.lastscore |
1524 | |
1525 | - def cancel(self): |
1526 | - """See `IBinaryPackageBuild`.""" |
1527 | - if not self.can_be_cancelled: |
1528 | - return |
1529 | - # BuildQueue.cancel() will decide whether to go straight to |
1530 | - # CANCELLED, or go through CANCELLING to let buildd-manager |
1531 | - # clean up the slave. |
1532 | - self.buildqueue_record.cancel() |
1533 | - |
1534 | def _parseDependencyToken(self, token): |
1535 | """Parse the given token. |
1536 | |
1537 | diff --git a/lib/lp/soyuz/model/livefsbuild.py b/lib/lp/soyuz/model/livefsbuild.py |
1538 | index f8312c2..7ec3e87 100644 |
1539 | --- a/lib/lp/soyuz/model/livefsbuild.py |
1540 | +++ b/lib/lp/soyuz/model/livefsbuild.py |
1541 | @@ -230,38 +230,7 @@ class LiveFSBuild(PackageBuildMixin, Storm): |
1542 | else: |
1543 | return self.buildqueue_record.lastscore |
1544 | |
1545 | - @property |
1546 | - def can_be_rescored(self): |
1547 | - """See `ILiveFSBuild`.""" |
1548 | - return ( |
1549 | - self.buildqueue_record is not None and |
1550 | - self.status is BuildStatus.NEEDSBUILD) |
1551 | - |
1552 | - @property |
1553 | - def can_be_cancelled(self): |
1554 | - """See `ILiveFSBuild`.""" |
1555 | - if not self.buildqueue_record: |
1556 | - return False |
1557 | - |
1558 | - cancellable_statuses = [ |
1559 | - BuildStatus.BUILDING, |
1560 | - BuildStatus.NEEDSBUILD, |
1561 | - ] |
1562 | - return self.status in cancellable_statuses |
1563 | - |
1564 | - def rescore(self, score): |
1565 | - """See `ILiveFSBuild`.""" |
1566 | - assert self.can_be_rescored, "Build %s cannot be rescored" % self.id |
1567 | - self.buildqueue_record.manualScore(score) |
1568 | - |
1569 | - def cancel(self): |
1570 | - """See `ILiveFSBuild`.""" |
1571 | - if not self.can_be_cancelled: |
1572 | - return |
1573 | - # BuildQueue.cancel() will decide whether to go straight to |
1574 | - # CANCELLED, or go through CANCELLING to let buildd-manager clean up |
1575 | - # the slave. |
1576 | - self.buildqueue_record.cancel() |
1577 | + can_be_retried = False |
1578 | |
1579 | def calculateScore(self): |
1580 | return ( |
1581 | diff --git a/lib/lp/soyuz/stories/webservice/xx-builds.txt b/lib/lp/soyuz/stories/webservice/xx-builds.txt |
1582 | index 1e7a44a..5148042 100644 |
1583 | --- a/lib/lp/soyuz/stories/webservice/xx-builds.txt |
1584 | +++ b/lib/lp/soyuz/stories/webservice/xx-builds.txt |
1585 | @@ -205,10 +205,9 @@ As can cprov who owns the PPA for the build: |
1586 | ... cprov, permission=OAuthPermission.WRITE_PUBLIC) |
1587 | >>> print(cprov_webservice.named_post( |
1588 | ... a_build['self_link'], 'retry')) |
1589 | - HTTP/1.1 500 Internal Server Error |
1590 | + HTTP/1.1 400 Bad Request |
1591 | ... |
1592 | - AssertionError: Build ... cannot be retried |
1593 | - <BLANKLINE> |
1594 | + Build ... cannot be retried. |
1595 | |
1596 | but in this case, although he has permission to retry the build, it |
1597 | failed because it was already retried by an admin. This is reflected in the |
1598 | @@ -265,4 +264,4 @@ alter the buildstate to one that cannot be retried: |
1599 | ... a_build['self_link'], 'rescore', score=1000)) |
1600 | HTTP/1.1 400 Bad Request |
1601 | ... |
1602 | - Build cannot be rescored. |
1603 | + Build ... cannot be rescored. |
1604 | diff --git a/lib/lp/soyuz/tests/test_build.py b/lib/lp/soyuz/tests/test_build.py |
1605 | index 6e83cde..8181a2d 100644 |
1606 | --- a/lib/lp/soyuz/tests/test_build.py |
1607 | +++ b/lib/lp/soyuz/tests/test_build.py |
1608 | @@ -11,6 +11,7 @@ import transaction |
1609 | from zope.component import getUtility |
1610 | |
1611 | from lp.buildmaster.enums import BuildStatus |
1612 | +from lp.buildmaster.interfaces.buildfarmjob import CannotBeRescored |
1613 | from lp.registry.interfaces.person import IPersonSet |
1614 | from lp.registry.interfaces.pocket import PackagePublishingPocket |
1615 | from lp.registry.interfaces.series import SeriesStatus |
1616 | @@ -20,10 +21,7 @@ from lp.soyuz.enums import ( |
1617 | PackagePublishingPriority, |
1618 | PackageUploadStatus, |
1619 | ) |
1620 | -from lp.soyuz.interfaces.binarypackagebuild import ( |
1621 | - CannotBeRescored, |
1622 | - IBinaryPackageBuildSet, |
1623 | - ) |
1624 | +from lp.soyuz.interfaces.binarypackagebuild import IBinaryPackageBuildSet |
1625 | from lp.soyuz.interfaces.component import IComponentSet |
1626 | from lp.soyuz.interfaces.publishing import PackagePublishingStatus |
1627 | from lp.soyuz.tests.test_publishing import SoyuzTestPublisher |
LGTM + tests pass