Merge lp:~rvb/maas/bug-1384001-redux-2 into lp:~maas-committers/maas/trunk

Proposed by Raphaël Badin
Status: Merged
Approved by: Raphaël Badin
Approved revision: no longer in the source branch.
Merged at revision: 3305
Proposed branch: lp:~rvb/maas/bug-1384001-redux-2
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 49 lines (+20/-1)
2 files modified
src/maasserver/middleware.py (+7/-1)
src/maasserver/tests/test_middleware.py (+13/-0)
To merge this branch: bzr merge lp:~rvb/maas/bug-1384001-redux-2
Reviewer Review Type Date Requested Status
Gavin Panella (community) Approve
Christian Reis (community) Approve
Graham Binns (community) Approve
Review via email: mp+239855@code.launchpad.net

Commit message

When node.wait_for_power_commands raises only one exception: re-raise this exception instead of MultipleFailures. This allows the proper response (e.g. a 503 response) to be returned by the API instead of the default 500 response.

Description of the change

This is a minimal fix that I'm doing so that a 503 response is returned when a power action is already in progress. Turns out node.wait_for_power_commands is only called for one node at a time and thus the MultipleFailures exception isn't really useful.

I'm planning to backport this to 1.7 and this is why this change is minimal: a more thorough cleanup is in progress (to completely get rid of MutlipleFailures) but we will land it only on trunk.

Additionally, this raises the default waiting time to 10 seconds: since this is a default we apply to any operation, it's probably best to be on the safe side; when this done on a pre-exception (and thus per-power type) basis, we will use something more fine-grained.

To post a comment you must log in.
Revision history for this message
Graham Binns (gmb) :
review: Approve
Revision history for this message
Christian Reis (kiko) wrote :

Approved for 1.7 and trunk.

review: Approve
Revision history for this message
Gavin Panella (allenap) wrote :

I'm on the fence; see my one comment.

review: Needs Information
Revision history for this message
Gavin Panella (allenap) wrote :

Raphaël has said that he'll do this in the middleware instead. I think that's slightly less risky, so +1 on that basis so he's not blocked on me. Someone should still give the code a once-over before landing.

MultipleFailures is also planned for removal in trunk.

review: Approve
Revision history for this message
Gavin Panella (allenap) wrote :

Nice.

review: Approve
Revision history for this message
Julian Edwards (julian-edwards) wrote :

On Tuesday 28 Oct 2014 17:21:59 you wrote:
> I know it doesn't fit into Python's exception model very well, but then
> Python's exception model doesn't allow for concurrent exceptions in a
> single thread. It's a sign that we ought to move error-handling code closer
> to the call-site. The division between synchronous and asynchronous code
> is, I think, partially responsible.

When Raph and I discussed this, for trunk we agreed that we'd remove
MultipleFailures entirely (I believe you added it because of the multiple node
start-up stuff) and handle errors declared in the AMP message at the callsites
themselves. General errors should still get handled in the middleware.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/maasserver/middleware.py'
--- src/maasserver/middleware.py 2014-10-27 13:22:08 +0000
+++ src/maasserver/middleware.py 2014-10-28 20:55:59 +0000
@@ -357,7 +357,7 @@
357357
358 # Default 'Retry-After' header sent for httplib.SERVICE_UNAVAILABLE358 # Default 'Retry-After' header sent for httplib.SERVICE_UNAVAILABLE
359 # responses.359 # responses.
360 RETRY_AFTER_SERVICE_UNAVAILABLE = 5360 RETRY_AFTER_SERVICE_UNAVAILABLE = 10
361361
362 def process_exception(self, request, exception):362 def process_exception(self, request, exception):
363 path_matcher = re.compile(settings.API_URL_REGEXP)363 path_matcher = re.compile(settings.API_URL_REGEXP)
@@ -374,6 +374,12 @@
374374
375 status = self.handled_exceptions[exception.__class__]375 status = self.handled_exceptions[exception.__class__]
376 if isinstance(exception, MultipleFailures):376 if isinstance(exception, MultipleFailures):
377 # If only one exception has been raised, process this exception:
378 # this allows MAAS to convert this exception into the proper
379 # type of response (e.g. 503) instead of the 500 response that
380 # MultipleFailures is transformed into.
381 if len(exception.args) == 1:
382 return self.process_exception(request, exception.args[0].value)
377 for failure in exception.args:383 for failure in exception.args:
378 logging.exception(exception)384 logging.exception(exception)
379 error_message = "\n".join(385 error_message = "\n".join(
380386
=== modified file 'src/maasserver/tests/test_middleware.py'
--- src/maasserver/tests/test_middleware.py 2014-10-27 10:38:45 +0000
+++ src/maasserver/tests/test_middleware.py 2014-10-28 20:55:59 +0000
@@ -494,6 +494,19 @@
494 (httplib.INTERNAL_SERVER_ERROR, expected_error_message),494 (httplib.INTERNAL_SERVER_ERROR, expected_error_message),
495 (response.status_code, response.content))495 (response.status_code, response.content))
496496
497 def test_multiple_failures_with_one_exception(self):
498 middleware = APIRPCErrorsMiddleware()
499 request = factory.make_fake_request(
500 "/api/1.0/" + factory.make_string(), 'POST')
501 expected_error_message = factory.make_name("error")
502 unique_exception = PowerActionAlreadyInProgress(expected_error_message)
503 exception = MultipleFailures(Failure(unique_exception))
504 response = middleware.process_exception(request, exception)
505
506 self.assertEqual(
507 (httplib.SERVICE_UNAVAILABLE, expected_error_message),
508 (response.status_code, response.content))
509
497 def test_handles_TimeoutError(self):510 def test_handles_TimeoutError(self):
498 middleware = APIRPCErrorsMiddleware()511 middleware = APIRPCErrorsMiddleware()
499 request = factory.make_fake_request(512 request = factory.make_fake_request(